diff --git a/android/KMEA/app/src/main/assets/android-host.js b/android/KMEA/app/src/main/assets/android-host.js
index ad440d493a2..48672c05728 100644
--- a/android/KMEA/app/src/main/assets/android-host.js
+++ b/android/KMEA/app/src/main/assets/android-host.js
@@ -247,7 +247,7 @@ function deregisterModel(modelID) {
}
function enableSuggestions(model, suggestionType) {
- // Set the options first so that KMW's ModelManager can properly handle model enablement states
+ // Set the options first so that KMW's ModelCache can properly handle model enablement states
// the moment we actually register the new model.
// Use console_debug
console_debug('enableSuggestions(model, maySuggest='+suggestionType+')');
diff --git a/android/KMEA/build.sh b/android/KMEA/build.sh
index f4d77058155..28d9877285c 100755
--- a/android/KMEA/build.sh
+++ b/android/KMEA/build.sh
@@ -23,7 +23,7 @@ JUNIT_RESULTS="##teamcity[importData type='junit' path='keyman\android\KMEA\app\
builder_describe "Builds Keyman Engine for Android." \
"@/web/src/app/webview" \
- "@/web/src/engine/sentry-manager" \
+ "@/common/web/sentry-manager" \
"clean" \
"configure" \
"build" \
@@ -81,7 +81,7 @@ if builder_start_action build:engine; then
cp "$KEYMAN_WEB_ROOT/build/app/resources/osk/kmwosk.css" "$ENGINE_ASSETS/kmwosk.css"
cp "$KEYMAN_WEB_ROOT/build/app/resources/osk/globe-hint.css" "$ENGINE_ASSETS/globe-hint.css"
cp "$KEYMAN_WEB_ROOT/build/app/resources/osk/keymanweb-osk.ttf" "$ENGINE_ASSETS/keymanweb-osk.ttf"
- cp "$KEYMAN_ROOT/web/src/engine/sentry-manager/build/lib/index.js" "$ENGINE_ASSETS/keyman-sentry.js"
+ cp "$KEYMAN_ROOT/common/web/sentry-manager/build/lib/index.js" "$ENGINE_ASSETS/keyman-sentry.js"
echo "Copying es6-shim polyfill"
cp "$KEYMAN_ROOT/node_modules/es6-shim/es6-shim.min.js" "$ENGINE_ASSETS/es6-shim.min.js"
diff --git a/android/Tests/KeyboardHarness/app/src/main/assets/keyboardharness.kpj b/android/Tests/KeyboardHarness/app/src/main/assets/keyboardharness.kpj
index 80a3e1568a2..5df60525f84 100644
--- a/android/Tests/KeyboardHarness/app/src/main/assets/keyboardharness.kpj
+++ b/android/Tests/KeyboardHarness/app/src/main/assets/keyboardharness.kpj
@@ -7,6 +7,6 @@
True
True
True
- lexicalmodel
+ keyboard
diff --git a/common/include/km_types.h b/common/include/km_types.h
index 05bc0aa432b..cd0ca7f8d07 100644
--- a/common/include/km_types.h
+++ b/common/include/km_types.h
@@ -11,12 +11,20 @@
#if defined(__LP64__) || defined(_LP64)
/* 64-bit, g++ */
-#define KMX_64BIT
+#define KMX_REQUIRES_REALIGNMENT
#endif
#if defined(_WIN64) && !defined(USE_64)
/* 64-bit, Windows */
-#define KMX_64BIT
+#define KMX_REQUIRES_REALIGNMENT
+#endif
+
+#if defined(__EMSCRIPTEN__)
+// Emscripten/WASM. Emscripten even though it uses 32-bit (and not 64-bit
+// pointers like the 64-bit architectures above) requires 32-bit alignment
+// for pointers which we don't always have in the KMX data from file
+// (see #12844).
+#define KMX_REQUIRES_REALIGNMENT
#endif
typedef uint32_t KMX_DWORD;
diff --git a/common/include/kmx_file.h b/common/include/kmx_file.h
index 748ba32d920..d0be382048d 100644
--- a/common/include/kmx_file.h
+++ b/common/include/kmx_file.h
@@ -5,7 +5,7 @@
#pragma once
-#include
+#include "km_types.h"
#ifdef KM_CORE_LIBRARY
// TODO: move this to a common namespace keyman::common::kmx_file or similar in the future
@@ -52,8 +52,10 @@ namespace kmx {
#define VERSION_160 0x00001000
#define VERSION_170 0x00001100
+#define VERSION_190 0x00001300
+
#define VERSION_MIN VERSION_50
-#define VERSION_MAX VERSION_170
+#define VERSION_MAX VERSION_190
//
// Backspace types
@@ -62,6 +64,9 @@ namespace kmx {
#define BK_DEFAULT 0
#define BK_DEADKEY 1
+// Next character to delete is a Unicode surrogate pair
+#define BK_SURROGATE 4
+
// Different begin types
#define BEGIN_ANSI 0
#define BEGIN_UNICODE 1
@@ -267,14 +272,24 @@ namespace kmx {
#define C_CODE_IFSYSTEMSTORE(store, val1, val2) U_UC_SENTINEL U_CODE_IFSYSTEMSTORE store val1 val2
#define C_CODE_SETSYSTEMSTORE(store, val) U_UC_SENTINEL U_CODE_SETSYSTEMSTORE store val
+//
+// COMP_KEYBOARD.dwFlags bitfield
+//
+
#define KF_SHIFTFREESCAPS 0x0001
#define KF_CAPSONONLY 0x0002
#define KF_CAPSALWAYSOFF 0x0004
#define KF_LOGICALLAYOUT 0x0008
#define KF_AUTOMATICVERSION 0x0010
-// 16.0: Support for LDML Keyboards in KMXPlus file format
-#define KF_KMXPLUS 0x0020
+/** 16.0+: A `COMP_KEYBOARD_KMXPLUSINFO` structure is present immediately after `COMP_KEYBOARD` */
+#define KF_KMXPLUS 0x0020
+
+/**
+ * 19.0+: The `COMP_KEYBOARD_KMXPLUSINFO` structure contains a v19 embedded OSK;
+ * may be used with or without KF_KMXPLUS.
+ */
+#define KF_KMXPLUSOSK 0x0040
#define HK_ALT 0x00010000
#define HK_CTRL 0x00020000
@@ -362,17 +377,17 @@ struct COMP_KEYBOARD_KMXPLUSINFO {
};
/**
- * Only valid if comp_keyboard.dwFlags&KF_KMXPLUS
+ * Only valid if comp_keyboard.dwFlags&(KF_KMXPLUS|KF_KMXPLUSOSK)
*/
struct COMP_KEYBOARD_EX {
- COMP_KEYBOARD header; // 0000 see COMP_KEYBOARD
- COMP_KEYBOARD_KMXPLUSINFO kmxplus; // 0040 see COMP_KEYBOARD_EXTRA
+ struct COMP_KEYBOARD header; // 0000 see COMP_KEYBOARD
+ struct COMP_KEYBOARD_KMXPLUSINFO kmxplus; // 0040 see COMP_KEYBOARD_EXTRA
};
-typedef COMP_KEYBOARD *PCOMP_KEYBOARD;
-typedef COMP_STORE *PCOMP_STORE;
-typedef COMP_KEY *PCOMP_KEY;
-typedef COMP_GROUP *PCOMP_GROUP;
+typedef struct COMP_KEYBOARD *PCOMP_KEYBOARD;
+typedef struct COMP_STORE *PCOMP_STORE;
+typedef struct COMP_KEY *PCOMP_KEY;
+typedef struct COMP_GROUP *PCOMP_GROUP;
extern const int CODE__SIZE[];
#define CODE__SIZE_MAX 5
@@ -382,10 +397,10 @@ extern const int CODE__SIZE[];
#define KEYBOARDFILEGROUP_SIZE 24
#define KEYBOARDFILEKEY_SIZE 20
-static_assert(sizeof(COMP_STORE) == KEYBOARDFILESTORE_SIZE, "COMP_STORE must be KEYBOARDFILESTORE_SIZE bytes");
-static_assert(sizeof(COMP_KEY) == KEYBOARDFILEKEY_SIZE, "COMP_KEY must be KEYBOARDFILEKEY_SIZE bytes");
-static_assert(sizeof(COMP_GROUP) == KEYBOARDFILEGROUP_SIZE, "COMP_GROUP must be KEYBOARDFILEGROUP_SIZE bytes");
-static_assert(sizeof(COMP_KEYBOARD) == KEYBOARDFILEHEADER_SIZE, "COMP_KEYBOARD must be KEYBOARDFILEHEADER_SIZE bytes");
+static_assert(sizeof(struct COMP_STORE) == KEYBOARDFILESTORE_SIZE, "COMP_STORE must be KEYBOARDFILESTORE_SIZE bytes");
+static_assert(sizeof(struct COMP_KEY) == KEYBOARDFILEKEY_SIZE, "COMP_KEY must be KEYBOARDFILEKEY_SIZE bytes");
+static_assert(sizeof(struct COMP_GROUP) == KEYBOARDFILEGROUP_SIZE, "COMP_GROUP must be KEYBOARDFILEGROUP_SIZE bytes");
+static_assert(sizeof(struct COMP_KEYBOARD) == KEYBOARDFILEHEADER_SIZE, "COMP_KEYBOARD must be KEYBOARDFILEHEADER_SIZE bytes");
#ifdef KM_CORE_LIBRARY
} // namespace kmx
diff --git a/common/test/keyboards/baseline/README.md b/common/test/keyboards/baseline/README.md
index cede1070851..28106b6f1db 100644
--- a/common/test/keyboards/baseline/README.md
+++ b/common/test/keyboards/baseline/README.md
@@ -4,11 +4,13 @@ This folder contains a set of keyboards that are used by multiple projects to
verify behaviour, both for compilation, and for runtime tests.
The following projects are known to use these keyboards:
+
* core -- .kmn files only, compiled with kmc during tests
* developer/kmcmplib -- .kmn files, compiled for tests, and .kmx as reference
* linux -- .kmn files for test steps only, .kmx files
+* web -- .kmn files for test steps, .kmx and .js files
-The .kmx files were built with kmcomp, not kmc, in order to ensure that
+The .kmx and .js files were built with kmcomp, not kmc, in order to ensure that
developer/kmcmplib gets a valid baseline reference.
Once kmc is stable, it is possible that we will be able to use the kmc npm
@@ -16,9 +18,34 @@ module to build .kmx for all projects that need them.
## Build parameters
-Keyboards were built with debug information and no compiler version embedded,
-with kmcomp 16.0.138:
+The keyboards can be built with:
-```bat
-for %d in (*.kmn) do kmcomp -no-compiler-version -d %d
+```bash
+./build.sh build
```
+
+This builds the keyboards with debug information and no compiler version
+embedded.
+
+## Grouping of the test fixtures
+
+Fixtures that test similar functionality are roughly grouped together.
+There is some overlap between different groups, so this was done
+mainly by test name.
+
+| Name | Test group |
+|----------|------------------------------------------|
+| k_00xx_* | Tests that didn't fit in any other group |
+| k_01xx_* | Basic rules |
+| k_02xx_* | RALT |
+| k_03xx_* | deadkeys |
+| k_04xx_* | Using multiple groups |
+| k_05xx_* | Options |
+| k_06xx_* | System stores |
+| k_07xx_* | Caps related tests |
+| k_08xx_* | Context related |
+
+## Description of file format
+
+See [README.md](/core/tests/unit/kmx/README.md) in core for a description
+of the file format.
diff --git a/common/test/keyboards/baseline/baseline.kpj b/common/test/keyboards/baseline/baseline.kpj
index 3ce7e9b83c1..5e7c461aac6 100644
--- a/common/test/keyboards/baseline/baseline.kpj
+++ b/common/test/keyboards/baseline/baseline.kpj
@@ -9,324 +9,324 @@
- id_k_000___null_keyboard
- k_000___null_keyboard.kmn
- k_000___null_keyboard.kmn
+ id_k_0000___null_keyboard
+ k_0000___null_keyboard.kmn
+ k_0000___null_keyboard.kmn
1.0
.kmn
- id_k_001___basic_input_unicodei
- k_001___basic_input_unicodei.kmn
- k_001___basic_input_unicodei.kmn
+ id_k_0100___basic_input_unicodei
+ k_0100___basic_input_unicodei.kmn
+ k_0100___basic_input_unicodei.kmn
1.0
.kmn
- id_k_002___basic_input_unicode
- k_002___basic_input_unicode.kmn
- k_002___basic_input_unicode.kmn
+ id_k_0101___basic_input_unicode
+ k_0101___basic_input_unicode.kmn
+ k_0101___basic_input_unicode.kmn
1.0
.kmn
- id_k_003___nul
- k_003___nul.kmn
- k_003___nul.kmn
+ id_k_0001___nul
+ k_0001___nul.kmn
+ k_0001___nul.kmn
1.0
.kmn
- id_k_004___basic_input__shift_2_
- k_004___basic_input__shift_2_.kmn
- k_004___basic_input__shift_2_.kmn
+ id_k_0102___basic_input__shift_2_
+ k_0102___basic_input__shift_2_.kmn
+ k_0102___basic_input__shift_2_.kmn
1.0
.kmn
- id_k_005___nul_with_initial_context
- k_005___nul_with_initial_context.kmn
- k_005___nul_with_initial_context.kmn
+ id_k_0002___nul_with_initial_context
+ k_0002___nul_with_initial_context.kmn
+ k_0002___nul_with_initial_context.kmn
1.0
.kmn
- id_k_006___vkey_input__shift_ctrl_
- k_006___vkey_input__shift_ctrl_.kmn
- k_006___vkey_input__shift_ctrl_.kmn
+ id_k_0103___vkey_input__shift_ctrl_
+ k_0103___vkey_input__shift_ctrl_.kmn
+ k_0103___vkey_input__shift_ctrl_.kmn
1.0
.kmn
- id_k_007___vkey_input__ctrl_alt_
- k_007___vkey_input__ctrl_alt_.kmn
- k_007___vkey_input__ctrl_alt_.kmn
+ id_k_0104___vkey_input__ctrl_alt_
+ k_0104___vkey_input__ctrl_alt_.kmn
+ k_0104___vkey_input__ctrl_alt_.kmn
1.0
.kmn
- id_k_008___vkey_input__ctrl_alt_2_
- k_008___vkey_input__ctrl_alt_2_.kmn
- k_008___vkey_input__ctrl_alt_2_.kmn
+ id_k_0105___vkey_input__ctrl_alt_2_
+ k_0105___vkey_input__ctrl_alt_2_.kmn
+ k_0105___vkey_input__ctrl_alt_2_.kmn
1.0
.kmn
- id_k_012___ralt
- k_012___ralt.kmn
- k_012___ralt.kmn
+ id_k_0200___ralt
+ k_0200___ralt.kmn
+ k_0200___ralt.kmn
1.0
.kmn
- id_k_013___deadkeys
- k_013___deadkeys.kmn
- k_013___deadkeys.kmn
+ id_k_0300___deadkeys
+ k_0300___deadkeys.kmn
+ k_0300___deadkeys.kmn
1.0
.kmn
- id_k_014___groups_and_virtual_keys
- k_014___groups_and_virtual_keys.kmn
- k_014___groups_and_virtual_keys.kmn
+ id_k_0400___groups_and_virtual_keys
+ k_0400___groups_and_virtual_keys.kmn
+ k_0400___groups_and_virtual_keys.kmn
1.0
.kmn
- id_k_015___ralt_2
- k_015___ralt_2.kmn
- k_015___ralt_2.kmn
+ id_k_0201___ralt_2
+ k_0201___ralt_2.kmn
+ k_0201___ralt_2.kmn
1.0
.kmn
- id_k_017___space_mnemonic_kbd
- k_017___space_mnemonic_kbd.kmn
- k_017___space_mnemonic_kbd.kmn
+ id_k_0004___space_mnemonic_kbd
+ k_0004___space_mnemonic_kbd.kmn
+ k_0004___space_mnemonic_kbd.kmn
1.0
.kmn
- id_k_018___nul_testing
- k_018___nul_testing.kmn
- k_018___nul_testing.kmn
+ id_k_0003___nul_testing
+ k_0003___nul_testing.kmn
+ k_0003___nul_testing.kmn
1.0
.kmn
- id_k_019___multiple_deadkeys
- k_019___multiple_deadkeys.kmn
- k_019___multiple_deadkeys.kmn
+ id_k_0301___multiple_deadkeys
+ k_0301___multiple_deadkeys.kmn
+ k_0301___multiple_deadkeys.kmn
1.0
.kmn
- id_k_020___deadkeys_and_backspace
- k_020___deadkeys_and_backspace.kmn
- k_020___deadkeys_and_backspace.kmn
+ id_k_0302___deadkeys_and_backspace
+ k_0302___deadkeys_and_backspace.kmn
+ k_0302___deadkeys_and_backspace.kmn
1.0
.kmn
- id_k_021___options
- k_021___options.kmn
- k_021___options.kmn
+ id_k_0500___options
+ k_0500___options.kmn
+ k_0500___options.kmn
1.0
.kmn
- id_k_022___options_with_preset
- k_022___options_with_preset.kmn
- k_022___options_with_preset.kmn
+ id_k_0501___options_with_preset
+ k_0501___options_with_preset.kmn
+ k_0501___options_with_preset.kmn
1.0
.kmn
- id_k_023___options_with_save
- k_023___options_with_save.kmn
- k_023___options_with_save.kmn
+ id_k_0502___options_with_save
+ k_0502___options_with_save.kmn
+ k_0502___options_with_save.kmn
1.0
.kmn
- id_k_024___options_with_save_and_preset
- k_024___options_with_save_and_preset.kmn
- k_024___options_with_save_and_preset.kmn
+ id_k_0503___options_with_save_and_preset
+ k_0503___options_with_save_and_preset.kmn
+ k_0503___options_with_save_and_preset.kmn
1.0
.kmn
- id_k_025___options_with_reset
- k_025___options_with_reset.kmn
- k_025___options_with_reset.kmn
+ id_k_0504___options_with_reset
+ k_0504___options_with_reset.kmn
+ k_0504___options_with_reset.kmn
1.0
.kmn
- id_k_026___system_stores
- k_026___system_stores.kmn
- k_026___system_stores.kmn
+ id_k_0600___system_stores
+ k_0600___system_stores.kmn
+ k_0600___system_stores.kmn
1.0
.kmn
- id_k_027___system_stores_2
- k_027___system_stores_2.kmn
- k_027___system_stores_2.kmn
+ id_k_0601___system_stores_2
+ k_0601___system_stores_2.kmn
+ k_0601___system_stores_2.kmn
1.0
.kmn
- id_k_028___smp
- k_028___smp.kmn
- k_028___smp.kmn
+ id_k_0106___smp
+ k_0106___smp.kmn
+ k_0106___smp.kmn
1.0
.kmn
- id_k_029___beep
- k_029___beep.kmn
- k_029___beep.kmn
+ id_k_0005___beep
+ k_0005___beep.kmn
+ k_0005___beep.kmn
1.0
.kmn
- id_k_030___multiple_groups
- k_030___multiple_groups.kmn
- k_030___multiple_groups.kmn
+ id_k_0401___multiple_groups
+ k_0401___multiple_groups.kmn
+ k_0401___multiple_groups.kmn
1.0
.kmn
- id_k_031___caps_lock
- k_031___caps_lock.kmn
- k_031___caps_lock.kmn
+ id_k_0700___caps_lock
+ k_0700___caps_lock.kmn
+ k_0700___caps_lock.kmn
1.0
.kmn
- id_k_032___caps_control
- k_032___caps_control.kmn
- k_032___caps_control.kmn
+ id_k_0701___caps_control
+ k_0701___caps_control.kmn
+ k_0701___caps_control.kmn
1.0
.kmn
- id_k_033___caps_always_off
- k_033___caps_always_off.kmn
- k_033___caps_always_off.kmn
+ id_k_0702___caps_always_off
+ k_0702___caps_always_off.kmn
+ k_0702___caps_always_off.kmn
1.0
.kmn
- id_k_034___options_double_set_reset
- k_034___options_double_set_reset.kmn
- k_034___options_double_set_reset.kmn
+ id_k_0505___options_double_set_reset
+ k_0505___options_double_set_reset.kmn
+ k_0505___options_double_set_reset.kmn
1.0
.kmn
- id_k_035___options_double_set_staged
- k_035___options_double_set_staged.kmn
- k_035___options_double_set_staged.kmn
+ id_k_0506___options_double_set_staged
+ k_0506___options_double_set_staged.kmn
+ k_0506___options_double_set_staged.kmn
1.0
.kmn
- id_k_036___options___double_reset_staged
- k_036___options___double_reset_staged.kmn
- k_036___options___double_reset_staged.kmn
+ id_k_0507___options___double_reset_staged
+ k_0507___options___double_reset_staged.kmn
+ k_0507___options___double_reset_staged.kmn
1.0
.kmn
- id_k_037___options___double_reset
- k_037___options___double_reset.kmn
- k_037___options___double_reset.kmn
+ id_k_0508___options___double_reset
+ k_0508___options___double_reset.kmn
+ k_0508___options___double_reset.kmn
1.0
.kmn
- id_k_038___punctkeys
- k_038___punctkeys.kmn
- k_038___punctkeys.kmn
+ id_k_0107___punctkeys
+ k_0107___punctkeys.kmn
+ k_0107___punctkeys.kmn
1.0
.kmn
- id_k_039___generic_ctrlalt
- k_039___generic_ctrlalt.kmn
- k_039___generic_ctrlalt.kmn
+ id_k_0203___generic_ctrlalt
+ k_0203___generic_ctrlalt.kmn
+ k_0203___generic_ctrlalt.kmn
1.0
.kmn
- id_k_040___long_context
- k_040___long_context.kmn
- k_040___long_context.kmn
+ id_k_0800___long_context
+ k_0800___long_context.kmn
+ k_0800___long_context.kmn
1.0
.kmn
- id_k_041___long_context_and_deadkeys
- k_041___long_context_and_deadkeys.kmn
- k_041___long_context_and_deadkeys.kmn
+ id_k_0801___long_context_and_deadkeys
+ k_0801___long_context_and_deadkeys.kmn
+ k_0801___long_context_and_deadkeys.kmn
1.0
.kmn
- id_k_042___long_context_and_split_deadkeys
- k_042___long_context_and_split_deadkeys.kmn
- k_042___long_context_and_split_deadkeys.kmn
+ id_k_0802___long_context_and_split_deadkeys
+ k_0802___long_context_and_split_deadkeys.kmn
+ k_0802___long_context_and_split_deadkeys.kmn
1.0
.kmn
- id_k_043___output_and_keystroke
- k_043___output_and_keystroke.kmn
- k_043___output_and_keystroke.kmn
+ id_k_0402___output_and_keystroke
+ k_0402___output_and_keystroke.kmn
+ k_0402___output_and_keystroke.kmn
1.0
.kmn
- id_k_044___if_and_context
- k_044___if_and_context.kmn
- k_044___if_and_context.kmn
+ id_k_0803___if_and_context
+ k_0803___if_and_context.kmn
+ k_0803___if_and_context.kmn
1.0
.kmn
- id_k_045___deadkey_and_context
- k_045___deadkey_and_context.kmn
- k_045___deadkey_and_context.kmn
+ id_k_0804___deadkey_and_context
+ k_0804___deadkey_and_context.kmn
+ k_0804___deadkey_and_context.kmn
1.0
.kmn
- id_k_046___deadkey_and_contextex
- k_046___deadkey_and_contextex.kmn
- k_046___deadkey_and_contextex.kmn
+ id_k_0805___deadkey_and_contextex
+ k_0805___deadkey_and_contextex.kmn
+ k_0805___deadkey_and_contextex.kmn
1.0
.kmn
- id_k_047___caps_always_off_initially_on
- k_047___caps_always_off_initially_on.kmn
- k_047___caps_always_off_initially_on.kmn
+ id_k_0703___caps_always_off_initially_on
+ k_0703___caps_always_off_initially_on.kmn
+ k_0703___caps_always_off_initially_on.kmn
1.0
.kmn
- id_k_048___modifier_keys_keep_context
- k_048___modifier_keys_keep_context.kmn
- k_048___modifier_keys_keep_context.kmn
+ id_k_0806___modifier_keys_keep_context
+ k_0806___modifier_keys_keep_context.kmn
+ k_0806___modifier_keys_keep_context.kmn
1.0
.kmn
- id_k_049___enter_invalidates_context
- k_049___enter_invalidates_context.kmn
- k_049___enter_invalidates_context.kmn
+ id_k_0807___enter_invalidates_context
+ k_0807___enter_invalidates_context.kmn
+ k_0807___enter_invalidates_context.kmn
1.0
.kmn
diff --git a/common/test/keyboards/baseline/build.sh b/common/test/keyboards/baseline/build.sh
index 1428b5e0c48..3a1e8d9f1ec 100755
--- a/common/test/keyboards/baseline/build.sh
+++ b/common/test/keyboards/baseline/build.sh
@@ -17,7 +17,7 @@ builder_parse "$@"
builder_describe_outputs \
configure kmcomp/kmcomp.exe \
- build k_000___null_keyboard.kmx
+ build k_0000___null_keyboard.kmx
do_configure() {
mkdir -p kmcomp
@@ -25,9 +25,15 @@ do_configure() {
}
do_build() {
- local name
+ local name jsname
for name in *.kmn; do
- ./kmcomp/kmcomp.exe -no-compiler-version -d "$name"
+ jsname="$(basename "${name%.kmn}").js"
+ ./kmcomp/kmcomp.exe -no-compiler-version -d "${name}"
+ if [[ "${name}" != "k_0812___nul_and_contextex.kmn" ]]; then
+ # k_0812___nul_and_contextex.kmn fails to compile because contextex
+ # is not currently supported in context() match, so we skip it
+ ./kmcomp/kmcomp.exe -no-compiler-version -d "${name}" "${jsname}"
+ fi
done
}
diff --git a/common/test/keyboards/baseline/k_0000___null_keyboard.js b/common/test/keyboards/baseline/k_0000___null_keyboard.js
new file mode 100644
index 00000000000..076ec597c48
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0000___null_keyboard.js
@@ -0,0 +1,28 @@
+
+KeymanWeb.KR(new Keyboard_k_0000___null_keyboard());
+
+function Keyboard_k_0000___null_keyboard()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0000___null_keyboard";
+ this.KN="0000 - null keyboard";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_000___null_keyboard.kmn b/common/test/keyboards/baseline/k_0000___null_keyboard.kmn
similarity index 84%
rename from common/test/keyboards/baseline/k_000___null_keyboard.kmn
rename to common/test/keyboards/baseline/k_0000___null_keyboard.kmn
index ecae49acde0..57321e6d63d 100644
--- a/common/test/keyboards/baseline/k_000___null_keyboard.kmn
+++ b/common/test/keyboards/baseline/k_0000___null_keyboard.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '000 - null keyboard'
+store(&NAME) '0000 - null keyboard'
c Description: Tests null keyboard
c keys: [K_A][RALT K_B][SHIFT K_C]
c expected: aC
diff --git a/common/test/keyboards/baseline/k_0000___null_keyboard.kmx b/common/test/keyboards/baseline/k_0000___null_keyboard.kmx
new file mode 100644
index 00000000000..dfc85b5e2a7
Binary files /dev/null and b/common/test/keyboards/baseline/k_0000___null_keyboard.kmx differ
diff --git a/common/test/keyboards/baseline/k_0001___nul.js b/common/test/keyboards/baseline/k_0001___nul.js
new file mode 100644
index 00000000000..d33ee337cf4
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0001___nul.js
@@ -0,0 +1,42 @@
+
+KeymanWeb.KR(new Keyboard_k_0001___nul());
+
+function Keyboard_k_0001___nul()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0001___nul";
+ this.KN="0001 - nul";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x41)) {
+ if(k.KN(0,t)){
+ r=m=1; // Line 13
+ k.KO(0,t,"b");
+ }
+ else if(k.KCM(1,t,"b",1)){
+ r=m=1; // Line 14
+ k.KO(1,t,"c");
+ }
+ else if(1){
+ r=m=1; // Line 15
+ k.KO(0,t,"d");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_003___nul.kmn b/common/test/keyboards/baseline/k_0001___nul.kmn
similarity index 83%
rename from common/test/keyboards/baseline/k_003___nul.kmn
rename to common/test/keyboards/baseline/k_0001___nul.kmn
index 32f6e84839a..44d92b628c7 100644
--- a/common/test/keyboards/baseline/k_003___nul.kmn
+++ b/common/test/keyboards/baseline/k_0001___nul.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '003 - nul'
+store(&NAME) '0001 - nul'
c Description: Tests nul context
c keys: [K_A][K_A][K_A]
c expected: cd
-c context:
+c context:
store(&version) '6.0'
diff --git a/common/test/keyboards/baseline/k_0001___nul.kmx b/common/test/keyboards/baseline/k_0001___nul.kmx
new file mode 100644
index 00000000000..6795c349f5c
Binary files /dev/null and b/common/test/keyboards/baseline/k_0001___nul.kmx differ
diff --git a/common/test/keyboards/baseline/k_0002___nul_with_initial_context.js b/common/test/keyboards/baseline/k_0002___nul_with_initial_context.js
new file mode 100644
index 00000000000..ca42f2a1f3e
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0002___nul_with_initial_context.js
@@ -0,0 +1,42 @@
+
+KeymanWeb.KR(new Keyboard_k_0002___nul_with_initial_context());
+
+function Keyboard_k_0002___nul_with_initial_context()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0002___nul_with_initial_context";
+ this.KN="0002 - nul with initial context";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x41)) {
+ if(k.KN(0,t)){
+ r=m=1; // Line 13
+ k.KO(0,t,"b");
+ }
+ else if(k.KCM(1,t,"b",1)){
+ r=m=1; // Line 14
+ k.KO(1,t,"c");
+ }
+ else if(1){
+ r=m=1; // Line 15
+ k.KO(0,t,"d");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_005___nul_with_initial_context.kmn b/common/test/keyboards/baseline/k_0002___nul_with_initial_context.kmn
similarity index 83%
rename from common/test/keyboards/baseline/k_005___nul_with_initial_context.kmn
rename to common/test/keyboards/baseline/k_0002___nul_with_initial_context.kmn
index f59016cbe18..4ae05e6ba4a 100644
--- a/common/test/keyboards/baseline/k_005___nul_with_initial_context.kmn
+++ b/common/test/keyboards/baseline/k_0002___nul_with_initial_context.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '005 - nul with initial context'
+store(&NAME) '0002 - nul with initial context'
c Description: Tests nul context with an initial context supplied
c keys: [K_A][K_A]
c expected: xdd
diff --git a/common/test/keyboards/baseline/k_0002___nul_with_initial_context.kmx b/common/test/keyboards/baseline/k_0002___nul_with_initial_context.kmx
new file mode 100644
index 00000000000..3d89b790f5c
Binary files /dev/null and b/common/test/keyboards/baseline/k_0002___nul_with_initial_context.kmx differ
diff --git a/common/test/keyboards/baseline/k_0003___nul_testing.js b/common/test/keyboards/baseline/k_0003___nul_testing.js
new file mode 100644
index 00000000000..330a614dba6
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0003___nul_testing.js
@@ -0,0 +1,34 @@
+
+KeymanWeb.KR(new Keyboard_k_0003___nul_testing());
+
+function Keyboard_k_0003___nul_testing()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0003___nul_testing";
+ this.KN="0003 - nul testing";
+ this.KMINVER="9.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x41)) {
+ if(k.KN(0,t)){
+ r=m=1; // Line 12
+ k.KO(0,t,"OK");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_018___nul_testing.kmn b/common/test/keyboards/baseline/k_0003___nul_testing.kmn
similarity index 87%
rename from common/test/keyboards/baseline/k_018___nul_testing.kmn
rename to common/test/keyboards/baseline/k_0003___nul_testing.kmn
index 20da4d4af70..87fc78f86fe 100644
--- a/common/test/keyboards/baseline/k_018___nul_testing.kmn
+++ b/common/test/keyboards/baseline/k_0003___nul_testing.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '018 - nul testing'
+store(&NAME) '0003 - nul testing'
c Description: Tests the processing of nul in LHS of rules
c keys: [K_A][K_A]
c expected: OKa
diff --git a/common/test/keyboards/baseline/k_0003___nul_testing.kmx b/common/test/keyboards/baseline/k_0003___nul_testing.kmx
new file mode 100644
index 00000000000..e30ca2494d2
Binary files /dev/null and b/common/test/keyboards/baseline/k_0003___nul_testing.kmx differ
diff --git a/common/test/keyboards/baseline/k_0004___space_mnemonic_kbd.js b/common/test/keyboards/baseline/k_0004___space_mnemonic_kbd.js
new file mode 100644
index 00000000000..a4ee094cb5c
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0004___space_mnemonic_kbd.js
@@ -0,0 +1,46 @@
+
+KeymanWeb.KR(new Keyboard_k_0004___space_mnemonic_kbd());
+
+function Keyboard_k_0004___space_mnemonic_kbd()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0004___space_mnemonic_kbd";
+ this.KN="0004 - space mnemonic kbd";
+ this.KMINVER="9.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=1;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.g_main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x20)) {
+ if(k.KCM(2,t,"ab",2)){
+ r=m=1; // Line 14
+ k.KO(2,t,"X");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x64)) {
+ if(k.KCM(2,t,"c ",2)){
+ r=m=1; // Line 15
+ k.KO(2,t,"Y");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x65)) {
+ if(k.KCM(2,t," d",2)){
+ r=m=1; // Line 16
+ k.KO(2,t,"Z");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmn b/common/test/keyboards/baseline/k_0004___space_mnemonic_kbd.kmn
similarity index 90%
rename from common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmn
rename to common/test/keyboards/baseline/k_0004___space_mnemonic_kbd.kmn
index 07e37ee681b..2660d114d4e 100644
--- a/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmn
+++ b/common/test/keyboards/baseline/k_0004___space_mnemonic_kbd.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '017 - space mnemonic kbd'
+store(&NAME) '0004 - space mnemonic kbd'
c Description: Tests Space handling in mnemonic keyboards (failed with Win 98)
c keys: [K_A][K_B][K_SPACE][K_C][K_SPACE][K_D][K_SPACE][K_D][K_E]
c expected: XYZ
diff --git a/common/test/keyboards/baseline/k_0004___space_mnemonic_kbd.kmx b/common/test/keyboards/baseline/k_0004___space_mnemonic_kbd.kmx
new file mode 100644
index 00000000000..b256ace5368
Binary files /dev/null and b/common/test/keyboards/baseline/k_0004___space_mnemonic_kbd.kmx differ
diff --git a/common/test/keyboards/baseline/k_0005___beep.js b/common/test/keyboards/baseline/k_0005___beep.js
new file mode 100644
index 00000000000..e0d19f0651e
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0005___beep.js
@@ -0,0 +1,40 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0005___beep());
+}
+function Keyboard_k_0005___beep()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0005___beep";
+ this.KN="0005 - beep";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KDC(0,t);
+ k.KB(t);
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_029___beep.kmn b/common/test/keyboards/baseline/k_0005___beep.kmn
similarity index 79%
rename from common/test/keyboards/baseline/k_029___beep.kmn
rename to common/test/keyboards/baseline/k_0005___beep.kmn
index 65e650ee44b..b9b9fcab8cb 100644
--- a/common/test/keyboards/baseline/k_029___beep.kmn
+++ b/common/test/keyboards/baseline/k_0005___beep.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '029 - beep'
+store(&NAME) '0005 - beep'
c Description: Tests beep
c keys: [K_1]
c expected: \b
-c context:
+c context:
store(&version) '10.0'
diff --git a/common/test/keyboards/baseline/k_0005___beep.kmx b/common/test/keyboards/baseline/k_0005___beep.kmx
new file mode 100644
index 00000000000..c491183f26c
Binary files /dev/null and b/common/test/keyboards/baseline/k_0005___beep.kmx differ
diff --git a/common/test/keyboards/baseline/k_000___null_keyboard.kmx b/common/test/keyboards/baseline/k_000___null_keyboard.kmx
deleted file mode 100644
index 2ac1a0f65f7..00000000000
Binary files a/common/test/keyboards/baseline/k_000___null_keyboard.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_001___basic_input_unicodei.kmx b/common/test/keyboards/baseline/k_001___basic_input_unicodei.kmx
deleted file mode 100644
index 82f60cd001d..00000000000
Binary files a/common/test/keyboards/baseline/k_001___basic_input_unicodei.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_002___basic_input_unicode.kmx b/common/test/keyboards/baseline/k_002___basic_input_unicode.kmx
deleted file mode 100644
index 5f1497a29e0..00000000000
Binary files a/common/test/keyboards/baseline/k_002___basic_input_unicode.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_003___nul.kmx b/common/test/keyboards/baseline/k_003___nul.kmx
deleted file mode 100644
index 9a5e835e04a..00000000000
Binary files a/common/test/keyboards/baseline/k_003___nul.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmx b/common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmx
deleted file mode 100644
index c465d596d5f..00000000000
Binary files a/common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_005___nul_with_initial_context.kmx b/common/test/keyboards/baseline/k_005___nul_with_initial_context.kmx
deleted file mode 100644
index 53f9889b32f..00000000000
Binary files a/common/test/keyboards/baseline/k_005___nul_with_initial_context.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmx b/common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmx
deleted file mode 100644
index 6651a91b196..00000000000
Binary files a/common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmx b/common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmx
deleted file mode 100644
index 3000af8a802..00000000000
Binary files a/common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmx b/common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmx
deleted file mode 100644
index b12a0f15c85..00000000000
Binary files a/common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_0100___basic_input_unicodei.js b/common/test/keyboards/baseline/k_0100___basic_input_unicodei.js
new file mode 100644
index 00000000000..86e960301a9
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0100___basic_input_unicodei.js
@@ -0,0 +1,52 @@
+
+KeymanWeb.KR(new Keyboard_k_0100___basic_input_unicodei());
+
+function Keyboard_k_0100___basic_input_unicodei()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0100___basic_input_unicodei";
+ this.KN="0100 - basic input UnicodeI";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0010;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4010, 0x41)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KO(0,t,"a");
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x42)) {
+ if(1){
+ r=m=1; // Line 14
+ k.KO(0,t,"b");
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x43)) {
+ if(1){
+ r=m=1; // Line 15
+ k.KO(0,t,"c");
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x46)) {
+ if(k.KCM(2,t,"DE",2)){
+ r=m=1; // Line 17
+ k.KO(2,t,"def");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_001___basic_input_unicodei.kmn b/common/test/keyboards/baseline/k_0100___basic_input_unicodei.kmn
similarity index 85%
rename from common/test/keyboards/baseline/k_001___basic_input_unicodei.kmn
rename to common/test/keyboards/baseline/k_0100___basic_input_unicodei.kmn
index 446c0e8f1d9..da80e98ca71 100644
--- a/common/test/keyboards/baseline/k_001___basic_input_unicodei.kmn
+++ b/common/test/keyboards/baseline/k_0100___basic_input_unicodei.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '001 - basic input UnicodeI'
+store(&NAME) '0100 - basic input UnicodeI'
c Description: Tests basic input with simple context rules, ANSI characters used only
c keys: [SHIFT K_A][SHIFT K_B][SHIFT K_C][SHIFT K_SPACE][SHIFT K_D][SHIFT K_E][SHIFT K_F]
c expected: abc def
-c context:
+c context:
store(&version) '6.0'
diff --git a/common/test/keyboards/baseline/k_0100___basic_input_unicodei.kmx b/common/test/keyboards/baseline/k_0100___basic_input_unicodei.kmx
new file mode 100644
index 00000000000..b31daddd07f
Binary files /dev/null and b/common/test/keyboards/baseline/k_0100___basic_input_unicodei.kmx differ
diff --git a/common/test/keyboards/baseline/k_0101___basic_input_unicode.js b/common/test/keyboards/baseline/k_0101___basic_input_unicode.js
new file mode 100644
index 00000000000..74dc5e3ddb7
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0101___basic_input_unicode.js
@@ -0,0 +1,52 @@
+
+KeymanWeb.KR(new Keyboard_k_0101___basic_input_unicode());
+
+function Keyboard_k_0101___basic_input_unicode()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0101___basic_input_unicode";
+ this.KN="0101 - basic input Unicode";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0010;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4010, 0x41)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KO(0,t,"ก");
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x42)) {
+ if(1){
+ r=m=1; // Line 14
+ k.KO(0,t,"ข");
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x43)) {
+ if(1){
+ r=m=1; // Line 15
+ k.KO(0,t,"ฃ");
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x46)) {
+ if(k.KCM(2,t,"DE",2)){
+ r=m=1; // Line 17
+ k.KO(2,t,"คฅฆ");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_002___basic_input_unicode.kmn b/common/test/keyboards/baseline/k_0101___basic_input_unicode.kmn
similarity index 90%
rename from common/test/keyboards/baseline/k_002___basic_input_unicode.kmn
rename to common/test/keyboards/baseline/k_0101___basic_input_unicode.kmn
index 04c6df9f4a6..a14821c8b6e 100644
--- a/common/test/keyboards/baseline/k_002___basic_input_unicode.kmn
+++ b/common/test/keyboards/baseline/k_0101___basic_input_unicode.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '002 - basic input Unicode'
+store(&NAME) '0101 - basic input Unicode'
c Description: Tests basic input with simple context rules (Unicode)
c keys: [SHIFT K_A][SHIFT K_B][SHIFT K_C][SHIFT K_SPACE][SHIFT K_D][SHIFT K_E][SHIFT K_F]
c expected: \u0E01\u0E02\u0E03\u0020\u0E04\u0E05\u0E06
diff --git a/common/test/keyboards/baseline/k_0101___basic_input_unicode.kmx b/common/test/keyboards/baseline/k_0101___basic_input_unicode.kmx
new file mode 100644
index 00000000000..acf956d6b4b
Binary files /dev/null and b/common/test/keyboards/baseline/k_0101___basic_input_unicode.kmx differ
diff --git a/common/test/keyboards/baseline/k_0102___basic_input__shift_2_.js b/common/test/keyboards/baseline/k_0102___basic_input__shift_2_.js
new file mode 100644
index 00000000000..a7ce73ed872
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0102___basic_input__shift_2_.js
@@ -0,0 +1,52 @@
+
+KeymanWeb.KR(new Keyboard_k_0102___basic_input__shift_2_());
+
+function Keyboard_k_0102___basic_input__shift_2_()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0102___basic_input__shift_2_";
+ this.KN="0102 - basic input (shift 2)";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0010;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4010, 0x41)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KO(0,t,"ก");
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x42)) {
+ if(1){
+ r=m=1; // Line 14
+ k.KO(0,t,"ข");
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x43)) {
+ if(1){
+ r=m=1; // Line 15
+ k.KO(0,t,"ฃ");
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x46)) {
+ if(k.KCM(2,t,"DE",2)){
+ r=m=1; // Line 17
+ k.KO(2,t,"คฅฆ");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmn b/common/test/keyboards/baseline/k_0102___basic_input__shift_2_.kmn
similarity index 85%
rename from common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmn
rename to common/test/keyboards/baseline/k_0102___basic_input__shift_2_.kmn
index d8782144273..0b072799ac5 100644
--- a/common/test/keyboards/baseline/k_004___basic_input__shift_2_.kmn
+++ b/common/test/keyboards/baseline/k_0102___basic_input__shift_2_.kmn
@@ -1,13 +1,13 @@
-store(&NAME) '004 - basic input (shift 2)'
+store(&NAME) '0102 - basic input (shift 2)'
c Description: Tests basic vkey input with shift only (Unicode)
c keys: [SHIFT K_A][SHIFT K_B][SHIFT K_C][SHIFT K_SPACE][SHIFT K_D][SHIFT K_E][SHIFT K_F]
c expected: \u0E01\u0E02\u0E03\u0020\u0E04\u0E05\u0E06
-c context:
+c context:
store(&version) '6.0'
begin Unicode > use(Main)
-
+
group(Main) using keys
+ [SHIFT K_A] > U+0E01
diff --git a/common/test/keyboards/baseline/k_0102___basic_input__shift_2_.kmx b/common/test/keyboards/baseline/k_0102___basic_input__shift_2_.kmx
new file mode 100644
index 00000000000..2e431a12ef9
Binary files /dev/null and b/common/test/keyboards/baseline/k_0102___basic_input__shift_2_.kmx differ
diff --git a/common/test/keyboards/baseline/k_0103___vkey_input__shift_ctrl_.js b/common/test/keyboards/baseline/k_0103___vkey_input__shift_ctrl_.js
new file mode 100644
index 00000000000..0775346e75f
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0103___vkey_input__shift_ctrl_.js
@@ -0,0 +1,52 @@
+
+KeymanWeb.KR(new Keyboard_k_0103___vkey_input__shift_ctrl_());
+
+function Keyboard_k_0103___vkey_input__shift_ctrl_()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0103___vkey_input__shift_ctrl_";
+ this.KN="0103 - vkey input (shift ctrl)";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0030;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4030, 0x41)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KO(0,t,"ก");
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x42)) {
+ if(1){
+ r=m=1; // Line 14
+ k.KO(0,t,"ข");
+ }
+ }
+ else if(k.KKM(e, 0x4030, 0x43)) {
+ if(1){
+ r=m=1; // Line 15
+ k.KO(0,t,"ฃ");
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x46)) {
+ if(k.KCM(2,t,"DE",2)){
+ r=m=1; // Line 17
+ k.KO(2,t,"คฅฆ");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmn b/common/test/keyboards/baseline/k_0103___vkey_input__shift_ctrl_.kmn
similarity index 86%
rename from common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmn
rename to common/test/keyboards/baseline/k_0103___vkey_input__shift_ctrl_.kmn
index 9a966aa651d..c9947cfcc54 100644
--- a/common/test/keyboards/baseline/k_006___vkey_input__shift_ctrl_.kmn
+++ b/common/test/keyboards/baseline/k_0103___vkey_input__shift_ctrl_.kmn
@@ -1,13 +1,13 @@
-store(&NAME) '006 - vkey input (shift ctrl)'
+store(&NAME) '0103 - vkey input (shift ctrl)'
c Description: Tests basic vkey input with shift and control (Unicode)
c keys: [LCTRL SHIFT K_A][SHIFT K_B][RCTRL SHIFT K_C][K_SPACE][SHIFT K_D][SHIFT K_E][SHIFT K_F]
c expected: \u0E01\u0E02\u0E03\u0020\u0E04\u0E05\u0E06
-c context:
+c context:
store(&version) '6.0'
begin Unicode > use(Main)
-
+
group(Main) using keys
+ [SHIFT CTRL K_A] > U+0E01
diff --git a/common/test/keyboards/baseline/k_0103___vkey_input__shift_ctrl_.kmx b/common/test/keyboards/baseline/k_0103___vkey_input__shift_ctrl_.kmx
new file mode 100644
index 00000000000..9a9747c33db
Binary files /dev/null and b/common/test/keyboards/baseline/k_0103___vkey_input__shift_ctrl_.kmx differ
diff --git a/common/test/keyboards/baseline/k_0104___vkey_input__ctrl_alt_.js b/common/test/keyboards/baseline/k_0104___vkey_input__ctrl_alt_.js
new file mode 100644
index 00000000000..ee6e25596f3
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0104___vkey_input__ctrl_alt_.js
@@ -0,0 +1,52 @@
+
+KeymanWeb.KR(new Keyboard_k_0104___vkey_input__ctrl_alt_());
+
+function Keyboard_k_0104___vkey_input__ctrl_alt_()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0104___vkey_input__ctrl_alt_";
+ this.KN="0104 - vkey input (ctrl alt)";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0060;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4060, 0x41)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KO(0,t,"ก");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x42)) {
+ if(1){
+ r=m=1; // Line 14
+ k.KO(0,t,"ข");
+ }
+ }
+ else if(k.KKM(e, 0x4060, 0x43)) {
+ if(1){
+ r=m=1; // Line 15
+ k.KO(0,t,"ฃ");
+ }
+ }
+ else if(k.KKM(e, 0x4060, 0x46)) {
+ if(k.KCM(2,t,"de",2)){
+ r=m=1; // Line 17
+ k.KO(2,t,"คฅฆ");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmn b/common/test/keyboards/baseline/k_0104___vkey_input__ctrl_alt_.kmn
similarity index 85%
rename from common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmn
rename to common/test/keyboards/baseline/k_0104___vkey_input__ctrl_alt_.kmn
index 3c2eb42bfe0..a7e7b608105 100644
--- a/common/test/keyboards/baseline/k_007___vkey_input__ctrl_alt_.kmn
+++ b/common/test/keyboards/baseline/k_0104___vkey_input__ctrl_alt_.kmn
@@ -1,15 +1,15 @@
-store(&NAME) '007 - vkey input (ctrl alt)'
+store(&NAME) '0104 - vkey input (ctrl alt)'
c Description: Tests basic vkey input with control and alt (Unicode)
c keys: [LCTRL LALT K_A][K_B][RCTRL RALT K_C][K_SPACE][K_D][K_E][LCTRL LALT K_F]
c expected: \u0E01\u0E02\u0E03\u0020\u0E04\u0E05\u0E06
-c context:
+c context:
store(&version) '6.0'
begin Unicode > use(Main)
group(Main) using keys
-
+
+ [CTRL ALT K_A] > U+0E01
+ [K_B] > U+0E02
+ [CTRL ALT K_C] > U+0E03
diff --git a/common/test/keyboards/baseline/k_0104___vkey_input__ctrl_alt_.kmx b/common/test/keyboards/baseline/k_0104___vkey_input__ctrl_alt_.kmx
new file mode 100644
index 00000000000..ae53a86547b
Binary files /dev/null and b/common/test/keyboards/baseline/k_0104___vkey_input__ctrl_alt_.kmx differ
diff --git a/common/test/keyboards/baseline/k_0105___vkey_input__ctrl_alt_2_.js b/common/test/keyboards/baseline/k_0105___vkey_input__ctrl_alt_2_.js
new file mode 100644
index 00000000000..2a6c8c0d029
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0105___vkey_input__ctrl_alt_2_.js
@@ -0,0 +1,40 @@
+
+KeymanWeb.KR(new Keyboard_k_0105___vkey_input__ctrl_alt_2_());
+
+function Keyboard_k_0105___vkey_input__ctrl_alt_2_()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0105___vkey_input__ctrl_alt_2_";
+ this.KN="0105 - vkey input (ctrl alt 2)";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0060;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4060, 0x41)) {
+ if(1){
+ r=m=1; // Line 14
+ k.KO(0,t,"ก");
+ }
+ }
+ else if(k.KKM(e, 0x4060, 0x43)) {
+ if(1){
+ r=m=1; // Line 15
+ k.KO(0,t,"ฃ");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmn b/common/test/keyboards/baseline/k_0105___vkey_input__ctrl_alt_2_.kmn
similarity index 87%
rename from common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmn
rename to common/test/keyboards/baseline/k_0105___vkey_input__ctrl_alt_2_.kmn
index d1660c40f26..6baa092327a 100644
--- a/common/test/keyboards/baseline/k_008___vkey_input__ctrl_alt_2_.kmn
+++ b/common/test/keyboards/baseline/k_0105___vkey_input__ctrl_alt_2_.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '008 - vkey input (ctrl alt 2)'
+store(&NAME) '0105 - vkey input (ctrl alt 2)'
c Description: Tests basic vkey input with control and alt (Unicode)
c keys: [LCTRL LALT K_A][LCTRL LALT K_B][LCTRL LALT K_C]
c expected: \u0E01\u0E03
diff --git a/common/test/keyboards/baseline/k_0105___vkey_input__ctrl_alt_2_.kmx b/common/test/keyboards/baseline/k_0105___vkey_input__ctrl_alt_2_.kmx
new file mode 100644
index 00000000000..6f88e253897
Binary files /dev/null and b/common/test/keyboards/baseline/k_0105___vkey_input__ctrl_alt_2_.kmx differ
diff --git a/common/test/keyboards/baseline/k_0106___smp.js b/common/test/keyboards/baseline/k_0106___smp.js
new file mode 100644
index 00000000000..e7c75e32d7d
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0106___smp.js
@@ -0,0 +1,80 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0106___smp());
+}
+function Keyboard_k_0106___smp()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0106___smp";
+ this.KN="0106 - smp";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.KS=1;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 15
+ k.KDC(0,t);
+ k.KO(-1,t,"🙂");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_2 /* 0x32 */)) {
+ if(k.KFCM(1,t,['🙂'])){
+ r=m=1; // Line 16
+ k.KDC(1,t);
+ k.KO(-1,t,"🙂hi😀");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_3 /* 0x33 */)) {
+ if(k.KFCM(1,t,['😀'])){
+ r=m=1; // Line 17
+ k.KDC(1,t);
+ k.KO(-1,t,"x");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_4 /* 0x34 */)) {
+ if(k.KFCM(4,t,['🙂','h','i','x'])){
+ r=m=1; // Line 18
+ k.KDC(4,t);
+ k.KO(-1,t,"😁y");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_5 /* 0x35 */)) {
+ if(1){
+ r=m=1; // Line 22
+ k.KDC(0,t);
+ k.KO(-1,t,"𐌳");
+ r=this.g_second_1(t,e);
+ m=2;
+ }
+ }
+ return r;
+ };
+ this.g_second_1=function(t,e) {
+ var k=KeymanWeb,r=1,m=0;
+ if(k.KFCM(1,t,['𐌳'])){
+ m=1; // Line 26
+ k.KDC(1,t);
+ k.KO(-1,t,"x");
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_028___smp.kmn b/common/test/keyboards/baseline/k_0106___smp.kmn
similarity index 94%
rename from common/test/keyboards/baseline/k_028___smp.kmn
rename to common/test/keyboards/baseline/k_0106___smp.kmn
index 7c1eaaba960..3ce026ca4c7 100644
--- a/common/test/keyboards/baseline/k_028___smp.kmn
+++ b/common/test/keyboards/baseline/k_0106___smp.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '028 - smp'
+store(&NAME) '0106 - smp'
c Description: Tests SMP characters; see #4361 [1-4], #5561 [5]
c keys: [K_1][K_2][K_3][K_4][K_5]
c expected: \U0001F601yx
diff --git a/common/test/keyboards/baseline/k_0106___smp.kmx b/common/test/keyboards/baseline/k_0106___smp.kmx
new file mode 100644
index 00000000000..cfbb429d0b7
Binary files /dev/null and b/common/test/keyboards/baseline/k_0106___smp.kmx differ
diff --git a/common/test/keyboards/baseline/k_0107___punctkeys.js b/common/test/keyboards/baseline/k_0107___punctkeys.js
new file mode 100644
index 00000000000..a5455156e99
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0107___punctkeys.js
@@ -0,0 +1,100 @@
+
+KeymanWeb.KR(new Keyboard_k_0107___punctkeys());
+
+function Keyboard_k_0107___punctkeys()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0107___punctkeys";
+ this.KN="0107 - punctkeys";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0xE2)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 24
+ k.KO(1,t,"p");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0xDE)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 23
+ k.KO(1,t,"o");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0xBC)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 15
+ k.KO(1,t,"g");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0xBD)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 16
+ k.KO(1,t,"h");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0xBE)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 17
+ k.KO(1,t,"i");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0xBF)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 18
+ k.KO(1,t,"j");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0xBA)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 13
+ k.KO(1,t,"e");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0xBB)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 14
+ k.KO(1,t,"f");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0xDB)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 20
+ k.KO(1,t,"l");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0xDC)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 21
+ k.KO(1,t,"m");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0xDD)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 22
+ k.KO(1,t,"n");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0xC0)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 19
+ k.KO(1,t,"k");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_038___punctkeys.kmn b/common/test/keyboards/baseline/k_0107___punctkeys.kmn
similarity index 92%
rename from common/test/keyboards/baseline/k_038___punctkeys.kmn
rename to common/test/keyboards/baseline/k_0107___punctkeys.kmn
index 33f9c052973..86edfb9dc65 100644
--- a/common/test/keyboards/baseline/k_038___punctkeys.kmn
+++ b/common/test/keyboards/baseline/k_0107___punctkeys.kmn
@@ -1,13 +1,13 @@
-store(&NAME) '038 - punctkeys'
+store(&NAME) '0107 - punctkeys'
c Description: Tests punctuation keys (Unicode)
c keys: [K_A][K_COLON][K_A][K_EQUAL][K_A][K_COMMA][K_A][K_HYPHEN][K_A][K_PERIOD][K_A][K_SLASH][K_A][K_BKQUOTE][K_A][K_LBRKT][K_A][K_BKSLASH][K_A][K_RBRKT][K_A][K_QUOTE][K_A][K_oE2]
c expected: efghijklmnop
-c context:
+c context:
store(&version) '6.0'
begin Unicode > use(Main)
-
+
group(Main) using keys
'a' + [K_COLON] > 'e'
diff --git a/common/test/keyboards/baseline/k_0107___punctkeys.kmx b/common/test/keyboards/baseline/k_0107___punctkeys.kmx
new file mode 100644
index 00000000000..d55d4d2c972
Binary files /dev/null and b/common/test/keyboards/baseline/k_0107___punctkeys.kmx differ
diff --git a/common/test/keyboards/baseline/k_012___ralt.kmx b/common/test/keyboards/baseline/k_012___ralt.kmx
deleted file mode 100644
index 3ac57c165b5..00000000000
Binary files a/common/test/keyboards/baseline/k_012___ralt.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_013___deadkeys.kmx b/common/test/keyboards/baseline/k_013___deadkeys.kmx
deleted file mode 100644
index 7f93d7d9ae9..00000000000
Binary files a/common/test/keyboards/baseline/k_013___deadkeys.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmx b/common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmx
deleted file mode 100644
index 03bb1508a55..00000000000
Binary files a/common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_015___ralt_2.kmx b/common/test/keyboards/baseline/k_015___ralt_2.kmx
deleted file mode 100644
index 0a5e1bbf434..00000000000
Binary files a/common/test/keyboards/baseline/k_015___ralt_2.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmx b/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmx
deleted file mode 100644
index ffb5e1ac098..00000000000
Binary files a/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_018___nul_testing.kmx b/common/test/keyboards/baseline/k_018___nul_testing.kmx
deleted file mode 100644
index 7c54781918f..00000000000
Binary files a/common/test/keyboards/baseline/k_018___nul_testing.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_019___multiple_deadkeys.kmx b/common/test/keyboards/baseline/k_019___multiple_deadkeys.kmx
deleted file mode 100644
index fe22a2819a5..00000000000
Binary files a/common/test/keyboards/baseline/k_019___multiple_deadkeys.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_0200___ralt.js b/common/test/keyboards/baseline/k_0200___ralt.js
new file mode 100644
index 00000000000..1e4672f5685
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0200___ralt.js
@@ -0,0 +1,52 @@
+
+KeymanWeb.KR(new Keyboard_k_0200___ralt());
+
+function Keyboard_k_0200___ralt()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0200___ralt";
+ this.KN="0200 - ralt";
+ this.KMINVER="9.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0060;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.g_main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x41)) {
+ if(1){
+ r=m=1; // Line 14
+ k.KO(0,t,"ז");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x4F)) {
+ if(1){
+ r=m=1; // Line 15
+ k.KO(0,t,"ר");
+ }
+ }
+ else if(k.KKM(e, 0x4060, 0x41)) {
+ if(1){
+ r=m=1; // Line 16
+ k.KO(0,t,"b");
+ }
+ }
+ else if(k.KKM(e, 0x4060, 0x4F)) {
+ if(1){
+ r=m=1; // Line 17
+ k.KO(0,t,"c");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_012___ralt.kmn b/common/test/keyboards/baseline/k_0200___ralt.kmn
similarity index 78%
rename from common/test/keyboards/baseline/k_012___ralt.kmn
rename to common/test/keyboards/baseline/k_0200___ralt.kmn
index 8e041ad99e0..5dd8e09ab1a 100644
--- a/common/test/keyboards/baseline/k_012___ralt.kmn
+++ b/common/test/keyboards/baseline/k_0200___ralt.kmn
@@ -1,9 +1,9 @@
-store(&NAME) '012 - ralt'
+store(&NAME) '0200 - ralt'
c Description: Tests Right Alt processing: requires Ctrl+Alt simulation off.
c env.simulate_altgr: 0
c keys: [RALT K_A][RALT K_O][LCTRL LALT K_A][LCTRL LALT K_O]
c expected: \u05d6\u05e8\u0062\u0063
-c context:
+c context:
store(&VERSION) '9.0'
@@ -11,7 +11,7 @@ begin Unicode > use(main)
group(main) using keys
-+ [RAlt K_A] > U+05D6
++ [RAlt K_A] > U+05D6
+ [RAlt K_O] > U+05E8
-+ [Ctrl Alt K_A] > "b"
++ [Ctrl Alt K_A] > "b"
+ [Ctrl Alt K_O] > "c"
diff --git a/common/test/keyboards/baseline/k_0200___ralt.kmx b/common/test/keyboards/baseline/k_0200___ralt.kmx
new file mode 100644
index 00000000000..6fd03a1b6cb
Binary files /dev/null and b/common/test/keyboards/baseline/k_0200___ralt.kmx differ
diff --git a/common/test/keyboards/baseline/k_0201___ralt_2.js b/common/test/keyboards/baseline/k_0201___ralt_2.js
new file mode 100644
index 00000000000..b4504c909d0
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0201___ralt_2.js
@@ -0,0 +1,34 @@
+
+KeymanWeb.KR(new Keyboard_k_0201___ralt_2());
+
+function Keyboard_k_0201___ralt_2()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0201___ralt_2";
+ this.KN="0201 - ralt 2";
+ this.KMINVER="9.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.g_main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x41)) {
+ if(k.KCM(1,t,"c",1)){
+ r=m=1; // Line 13
+ k.KO(1,t,"cd");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_015___ralt_2.kmn b/common/test/keyboards/baseline/k_0201___ralt_2.kmn
similarity index 55%
rename from common/test/keyboards/baseline/k_015___ralt_2.kmn
rename to common/test/keyboards/baseline/k_0201___ralt_2.kmn
index 631bea8d8d8..32e2a81e36a 100644
--- a/common/test/keyboards/baseline/k_015___ralt_2.kmn
+++ b/common/test/keyboards/baseline/k_0201___ralt_2.kmn
@@ -1,9 +1,10 @@
-store(&NAME) '015 - ralt 2'
+store(&VERSION) '9.0'
+store(&NAME) '0201 - ralt 2'
c Description: Tests Right Alt processing with non-US kbds.
-c keys: [K_A][K_B][K_C][RALT K_A]
-c expected: abcd
+c keys: [K_A][K_B][K_C][RALT K_A][K_A][K_B][K_C][K_A]
+c expected: abcdabca
-store(&VERSION) '9.0'
+store(&TARGETS) 'any'
begin Unicode > use(main)
diff --git a/common/test/keyboards/baseline/k_0201___ralt_2.kmx b/common/test/keyboards/baseline/k_0201___ralt_2.kmx
new file mode 100644
index 00000000000..79ead9aea3c
Binary files /dev/null and b/common/test/keyboards/baseline/k_0201___ralt_2.kmx differ
diff --git a/common/test/keyboards/baseline/k_0202___alt.js b/common/test/keyboards/baseline/k_0202___alt.js
new file mode 100644
index 00000000000..c0643cfca44
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0202___alt.js
@@ -0,0 +1,34 @@
+
+KeymanWeb.KR(new Keyboard_k_0202___alt());
+
+function Keyboard_k_0202___alt()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0202___alt";
+ this.KN="0202 - alt";
+ this.KMINVER="9.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0040;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.g_main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4040, 0x41)) {
+ if(k.KCM(1,t,"c",1)){
+ r=m=1; // Line 13
+ k.KO(1,t,"cd");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_0202___alt.kmn b/common/test/keyboards/baseline/k_0202___alt.kmn
new file mode 100755
index 00000000000..ea8d575bd7f
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0202___alt.kmn
@@ -0,0 +1,13 @@
+store(&VERSION) '9.0'
+store(&NAME) '0202 - alt'
+c Description: Tests Alt processing with non-US kbds.
+c keys: [K_A][K_B][K_C][RALT K_A][K_A][K_B][K_C][K_A]
+c expected: abcdabca
+
+store(&TARGETS) 'any'
+
+begin Unicode > use(main)
+
+group(main) using keys
+
+'c' + [Alt K_A] > "cd"
diff --git a/common/test/keyboards/baseline/k_0202___alt.kmx b/common/test/keyboards/baseline/k_0202___alt.kmx
new file mode 100644
index 00000000000..14a61fc01b4
Binary files /dev/null and b/common/test/keyboards/baseline/k_0202___alt.kmx differ
diff --git a/common/test/keyboards/baseline/k_0203___generic_ctrlalt.js b/common/test/keyboards/baseline/k_0203___generic_ctrlalt.js
new file mode 100644
index 00000000000..de496127b2d
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0203___generic_ctrlalt.js
@@ -0,0 +1,40 @@
+
+KeymanWeb.KR(new Keyboard_k_0203___generic_ctrlalt());
+
+function Keyboard_k_0203___generic_ctrlalt()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0203___generic_ctrlalt";
+ this.KN="0203 - generic ctrlalt";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0060;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4020, 0x42)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 13
+ k.KO(1,t,"b");
+ }
+ }
+ else if(k.KKM(e, 0x4040, 0x43)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 14
+ k.KO(1,t,"c");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_039___generic_ctrlalt.kmn b/common/test/keyboards/baseline/k_0203___generic_ctrlalt.kmn
similarity index 84%
rename from common/test/keyboards/baseline/k_039___generic_ctrlalt.kmn
rename to common/test/keyboards/baseline/k_0203___generic_ctrlalt.kmn
index 1bbb1de6611..8221e7d62c0 100644
--- a/common/test/keyboards/baseline/k_039___generic_ctrlalt.kmn
+++ b/common/test/keyboards/baseline/k_0203___generic_ctrlalt.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '039 - generic ctrlalt'
+store(&NAME) '0203 - generic ctrlalt'
c Description: Tests generic alt and control (Unicode)
c keys: [K_A][K_A][LCTRL K_B][K_A][LALT K_C][K_A][RCTRL K_B][K_A][RALT K_C]
c expected: abcbc
-c context:
+c context:
store(&version) '6.0'
diff --git a/common/test/keyboards/baseline/k_0203___generic_ctrlalt.kmx b/common/test/keyboards/baseline/k_0203___generic_ctrlalt.kmx
new file mode 100644
index 00000000000..c2ac5c2f2f2
Binary files /dev/null and b/common/test/keyboards/baseline/k_0203___generic_ctrlalt.kmx differ
diff --git a/common/test/keyboards/baseline/k_021___options.kmx b/common/test/keyboards/baseline/k_021___options.kmx
deleted file mode 100644
index f35da34878a..00000000000
Binary files a/common/test/keyboards/baseline/k_021___options.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_022___options_with_preset.kmx b/common/test/keyboards/baseline/k_022___options_with_preset.kmx
deleted file mode 100644
index e681df83394..00000000000
Binary files a/common/test/keyboards/baseline/k_022___options_with_preset.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_023___options_with_save.kmx b/common/test/keyboards/baseline/k_023___options_with_save.kmx
deleted file mode 100644
index 3668b7a066e..00000000000
Binary files a/common/test/keyboards/baseline/k_023___options_with_save.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmx b/common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmx
deleted file mode 100644
index 1fecdfac213..00000000000
Binary files a/common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_025___options_with_reset.kmx b/common/test/keyboards/baseline/k_025___options_with_reset.kmx
deleted file mode 100644
index 028f81cb5fe..00000000000
Binary files a/common/test/keyboards/baseline/k_025___options_with_reset.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_026___system_stores.kmx b/common/test/keyboards/baseline/k_026___system_stores.kmx
deleted file mode 100644
index bb918cf1429..00000000000
Binary files a/common/test/keyboards/baseline/k_026___system_stores.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_027___system_stores_2.kmx b/common/test/keyboards/baseline/k_027___system_stores_2.kmx
deleted file mode 100644
index dac90ab985b..00000000000
Binary files a/common/test/keyboards/baseline/k_027___system_stores_2.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_028___smp.kmx b/common/test/keyboards/baseline/k_028___smp.kmx
deleted file mode 100644
index f343f969e9b..00000000000
Binary files a/common/test/keyboards/baseline/k_028___smp.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_029___beep.kmx b/common/test/keyboards/baseline/k_029___beep.kmx
deleted file mode 100644
index abf8b899836..00000000000
Binary files a/common/test/keyboards/baseline/k_029___beep.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_0300___deadkeys.js b/common/test/keyboards/baseline/k_0300___deadkeys.js
new file mode 100644
index 00000000000..0ceda698246
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0300___deadkeys.js
@@ -0,0 +1,40 @@
+
+KeymanWeb.KR(new Keyboard_k_0300___deadkeys());
+
+function Keyboard_k_0300___deadkeys()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0300___deadkeys";
+ this.KN="0300 - deadkeys";
+ this.KMINVER="9.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0010;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.g_main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4010, 0x36)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KDO(0,t,0);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x41)) {
+ if(k.KDM(0,t,0)){
+ r=m=1; // Line 15
+ k.KO(0,t,"â");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_013___deadkeys.kmn b/common/test/keyboards/baseline/k_0300___deadkeys.kmn
similarity index 87%
rename from common/test/keyboards/baseline/k_013___deadkeys.kmn
rename to common/test/keyboards/baseline/k_0300___deadkeys.kmn
index d0f4876455e..528a0929a01 100644
--- a/common/test/keyboards/baseline/k_013___deadkeys.kmn
+++ b/common/test/keyboards/baseline/k_0300___deadkeys.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '013 - deadkeys'
+store(&NAME) '0300 - deadkeys'
c Description: Tests a simple deadkey rule in Unicode
c keys: [SHIFT K_6][K_A]
c expected: \u00E2
diff --git a/common/test/keyboards/baseline/k_0300___deadkeys.kmx b/common/test/keyboards/baseline/k_0300___deadkeys.kmx
new file mode 100644
index 00000000000..a575f19954b
Binary files /dev/null and b/common/test/keyboards/baseline/k_0300___deadkeys.kmx differ
diff --git a/common/test/keyboards/baseline/k_0301___multiple_deadkeys.js b/common/test/keyboards/baseline/k_0301___multiple_deadkeys.js
new file mode 100644
index 00000000000..67b3f4987c4
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0301___multiple_deadkeys.js
@@ -0,0 +1,176 @@
+
+KeymanWeb.KR(new Keyboard_k_0301___multiple_deadkeys());
+
+function Keyboard_k_0301___multiple_deadkeys()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0301___multiple_deadkeys";
+ this.KN="0301 - multiple deadkeys";
+ this.KMINVER="9.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.g_main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x31)) {
+ if(1){
+ r=m=1; // Line 22
+ k.KDO(0,t,0);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x32)) {
+ if(1){
+ r=m=1; // Line 25
+ k.KO(0,t,"a");
+ k.KDO(-1,t,1);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x33)) {
+ if(1){
+ r=m=1; // Line 28
+ k.KDO(0,t,2);
+ k.KO(-1,t,"a");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x34)) {
+ if(1){
+ r=m=1; // Line 31
+ k.KDO(0,t,3);
+ k.KDO(-1,t,4);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x35)) {
+ if(1){
+ r=m=1; // Line 34
+ k.KO(0,t,"a");
+ k.KDO(-1,t,5);
+ k.KDO(-1,t,6);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x36)) {
+ if(1){
+ r=m=1; // Line 37
+ k.KO(0,t,"a");
+ k.KDO(-1,t,7);
+ k.KDO(-1,t,8);
+ k.KO(-1,t,"b");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x37)) {
+ if(1){
+ r=m=1; // Line 40
+ k.KDO(0,t,9);
+ k.KDO(-1,t,10);
+ k.KO(-1,t,"a");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x38)) {
+ if(1){
+ r=m=1; // Line 45
+ k.KDO(0,t,11);
+ k.KDO(-1,t,12);
+ k.KDO(-1,t,13);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x39)) {
+ if(1){
+ r=m=1; // Line 56
+ k.KO(0,t,"{");
+ k.KDO(-1,t,14);
+ k.KDO(-1,t,15);
+ k.KO(-1,t,"}");
+ k.KDO(-1,t,16);
+ k.KDO(-1,t,17);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x58)) {
+ if(k.KCM(2,t,"{",1)&&k.KDM(1,t,14)&&k.KDM(1,t,18)&&k.KCM(1,t,"}",1)&&k.KDM(0,t,19)&&k.KDM(0,t,20)){
+ r=m=1; // Line 59
+ k.KO(2,t,"9=OK");
+ }
+ else if(k.KCM(2,t,"a",1)&&k.KDM(1,t,7)&&k.KDM(1,t,8)&&k.KCM(1,t,"b",1)){
+ r=m=1; // Line 38
+ k.KO(2,t,"6=OK ");
+ }
+ else if(k.KDM(1,t,15)&&k.KCM(1,t,"}",1)&&k.KDM(0,t,16)&&k.KDM(0,t,17)){
+ r=m=1; // Line 58
+ k.KDO(1,t,18);
+ k.KO(-1,t,"}");
+ k.KDO(-1,t,19);
+ k.KDO(-1,t,20);
+ }
+ else if(k.KCM(1,t,"a",1)&&k.KDM(0,t,5)&&k.KDM(0,t,6)){
+ r=m=1; // Line 35
+ k.KO(1,t,"5=OK ");
+ }
+ else if(k.KDM(1,t,9)&&k.KDM(1,t,10)&&k.KCM(1,t,"a",1)){
+ r=m=1; // Line 41
+ k.KO(1,t,"7=OK ");
+ }
+ else if(k.KDM(0,t,11)&&k.KDM(0,t,12)&&k.KDM(0,t,13)){
+ r=m=1; // Line 46
+ k.KO(0,t,"8=OK ");
+ }
+ else if(k.KCM(1,t,"a",1)&&k.KDM(0,t,1)){
+ r=m=1; // Line 26
+ k.KO(1,t,"2=OK ");
+ }
+ else if(k.KDM(1,t,2)&&k.KCM(1,t,"a",1)){
+ r=m=1; // Line 29
+ k.KO(1,t,"3=OK ");
+ }
+ else if(k.KDM(0,t,3)&&k.KDM(0,t,4)){
+ r=m=1; // Line 32
+ k.KO(0,t,"4=OK ");
+ }
+ else if(k.KDM(1,t,9)&&k.KCM(1,t,"a",1)){
+ r=m=1; // Line 42
+ k.KO(1,t,"7=Fail1 ");
+ }
+ else if(k.KDM(1,t,10)&&k.KCM(1,t,"a",1)){
+ r=m=1; // Line 43
+ k.KO(1,t,"7=Fail2 ");
+ }
+ else if(k.KDM(0,t,11)&&k.KDM(0,t,12)){
+ r=m=1; // Line 50
+ k.KO(0,t,"8=Fail4 ");
+ }
+ else if(k.KDM(0,t,11)&&k.KDM(0,t,13)){
+ r=m=1; // Line 51
+ k.KO(0,t,"8=Fail5 ");
+ }
+ else if(k.KDM(0,t,12)&&k.KDM(0,t,13)){
+ r=m=1; // Line 52
+ k.KO(0,t,"8=Fail6 ");
+ }
+ else if(k.KDM(0,t,0)){
+ r=m=1; // Line 23
+ k.KO(0,t,"1=OK ");
+ }
+ else if(k.KDM(0,t,11)){
+ r=m=1; // Line 47
+ k.KO(0,t,"8=Fail1 ");
+ }
+ else if(k.KDM(0,t,12)){
+ r=m=1; // Line 48
+ k.KO(0,t,"8=Fail2 ");
+ }
+ else if(k.KDM(0,t,13)){
+ r=m=1; // Line 49
+ k.KO(0,t,"8=Fail3 ");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_019___multiple_deadkeys.kmn b/common/test/keyboards/baseline/k_0301___multiple_deadkeys.kmn
similarity index 97%
rename from common/test/keyboards/baseline/k_019___multiple_deadkeys.kmn
rename to common/test/keyboards/baseline/k_0301___multiple_deadkeys.kmn
index e70b30f08c5..ea7b8535816 100644
--- a/common/test/keyboards/baseline/k_019___multiple_deadkeys.kmn
+++ b/common/test/keyboards/baseline/k_0301___multiple_deadkeys.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '019 - multiple deadkeys'
+store(&NAME) '0301 - multiple deadkeys'
c Description: Tests deadkey scenarios
c 1. One deadkey in context dk(1) + '1'
c 2. One char and one deadkey in context 'a' dk(2) + '2'
diff --git a/common/test/keyboards/baseline/k_0301___multiple_deadkeys.kmx b/common/test/keyboards/baseline/k_0301___multiple_deadkeys.kmx
new file mode 100644
index 00000000000..68804c91dd9
Binary files /dev/null and b/common/test/keyboards/baseline/k_0301___multiple_deadkeys.kmx differ
diff --git a/common/test/keyboards/baseline/k_0302___deadkeys_and_backspace.js b/common/test/keyboards/baseline/k_0302___deadkeys_and_backspace.js
new file mode 100644
index 00000000000..2322275bdde
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0302___deadkeys_and_backspace.js
@@ -0,0 +1,93 @@
+
+KeymanWeb.KR(new Keyboard_k_0302___deadkeys_and_backspace());
+
+function Keyboard_k_0302___deadkeys_and_backspace()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0302___deadkeys_and_backspace";
+ this.KN="0302- deadkeys and backspace";
+ this.KMINVER="9.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.g_main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x08)) {
+ if(k.KCM(2,t,"c",1)&&k.KDM(1,t,11)&&k.KCM(1,t,"d",1)){
+ r=m=1; // Line 32
+ k.KO(2,t," ok");
+ }
+ else if(k.KCM(2,t,"cd",2)){
+ r=m=1; // Line 31
+ k.KO(2,t," fail");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x31)) {
+ if(1){
+ r=m=1; // Line 24
+ k.KDO(0,t,0);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x32)) {
+ if(1){
+ r=m=1; // Line 25
+ k.KO(0,t,"a");
+ k.KDO(-1,t,1);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x33)) {
+ if(1){
+ r=m=1; // Line 26
+ k.KDO(0,t,2);
+ k.KO(-1,t,"a");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x34)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDO(0,t,3);
+ k.KDO(-1,t,4);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x35)) {
+ if(1){
+ r=m=1; // Line 28
+ k.KO(0,t,"a");
+ k.KDO(-1,t,5);
+ k.KDO(-1,t,6);
+ k.KO(-1,t,"b");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x36)) {
+ if(1){
+ r=m=1; // Line 29
+ k.KO(0,t,"a");
+ k.KDO(-1,t,7);
+ k.KDO(-1,t,8);
+ k.KO(-1,t,"b");
+ k.KDO(-1,t,9);
+ k.KDO(-1,t,10);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x37)) {
+ if(1){
+ r=m=1; // Line 30
+ k.KO(0,t,"c");
+ k.KDO(-1,t,11);
+ k.KO(-1,t,"de");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmn b/common/test/keyboards/baseline/k_0302___deadkeys_and_backspace.kmn
similarity index 96%
rename from common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmn
rename to common/test/keyboards/baseline/k_0302___deadkeys_and_backspace.kmn
index af7c80f21d9..8049e44a86c 100644
--- a/common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmn
+++ b/common/test/keyboards/baseline/k_0302___deadkeys_and_backspace.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '020 - deadkeys and backspace'
+store(&NAME) '0302- deadkeys and backspace'
c Description: Tests deadkey backspacing
c 1. One deadkey in context dk(1) + BKSP = nul
c 2. One char and one deadkey in context 'a' dk(2) + BKSP = nul
diff --git a/common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmx b/common/test/keyboards/baseline/k_0302___deadkeys_and_backspace.kmx
similarity index 87%
rename from common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmx
rename to common/test/keyboards/baseline/k_0302___deadkeys_and_backspace.kmx
index 68271160736..71e4f50f3ed 100644
Binary files a/common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmx and b/common/test/keyboards/baseline/k_0302___deadkeys_and_backspace.kmx differ
diff --git a/common/test/keyboards/baseline/k_031___caps_lock.kmx b/common/test/keyboards/baseline/k_031___caps_lock.kmx
deleted file mode 100644
index 5fe3e4fbd74..00000000000
Binary files a/common/test/keyboards/baseline/k_031___caps_lock.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_032___caps_control.kmx b/common/test/keyboards/baseline/k_032___caps_control.kmx
deleted file mode 100644
index 1914d1954bb..00000000000
Binary files a/common/test/keyboards/baseline/k_032___caps_control.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_033___caps_always_off.kmx b/common/test/keyboards/baseline/k_033___caps_always_off.kmx
deleted file mode 100644
index cb9a71a0460..00000000000
Binary files a/common/test/keyboards/baseline/k_033___caps_always_off.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_034___options_double_set_reset.kmx b/common/test/keyboards/baseline/k_034___options_double_set_reset.kmx
deleted file mode 100644
index df8eb8062a5..00000000000
Binary files a/common/test/keyboards/baseline/k_034___options_double_set_reset.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_035___options_double_set_staged.kmx b/common/test/keyboards/baseline/k_035___options_double_set_staged.kmx
deleted file mode 100644
index e112a0740f5..00000000000
Binary files a/common/test/keyboards/baseline/k_035___options_double_set_staged.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_037___options___double_reset.kmx b/common/test/keyboards/baseline/k_037___options___double_reset.kmx
deleted file mode 100644
index eed688349c9..00000000000
Binary files a/common/test/keyboards/baseline/k_037___options___double_reset.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_038___punctkeys.kmx b/common/test/keyboards/baseline/k_038___punctkeys.kmx
deleted file mode 100644
index 8166409f5f3..00000000000
Binary files a/common/test/keyboards/baseline/k_038___punctkeys.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_039___generic_ctrlalt.kmx b/common/test/keyboards/baseline/k_039___generic_ctrlalt.kmx
deleted file mode 100644
index 1bf180a31c8..00000000000
Binary files a/common/test/keyboards/baseline/k_039___generic_ctrlalt.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_0400___groups_and_virtual_keys.js b/common/test/keyboards/baseline/k_0400___groups_and_virtual_keys.js
new file mode 100644
index 00000000000..32aebaea242
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0400___groups_and_virtual_keys.js
@@ -0,0 +1,69 @@
+
+KeymanWeb.KR(new Keyboard_k_0400___groups_and_virtual_keys());
+
+function Keyboard_k_0400___groups_and_virtual_keys()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0400___groups_and_virtual_keys";
+ this.KN="0400 - groups and virtual keys";
+ this.KMINVER="9.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0070;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_UMain_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_UMain_0(t,e);
+ };
+ this.g_UMain_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4020, 0x32)) {
+ if(1){
+ r=m=1; // Line 16
+ k.KDO(0,t,0);
+ }
+ }
+ else if(k.KKM(e, 0x4040, 0x32)) {
+ if(1){
+ r=m=1; // Line 17
+ k.KDO(0,t,0);
+ }
+ }
+ else if(k.KKM(e, 0x4010, 0x32)) {
+ if(1){
+ r=m=1; // Line 18
+ k.KDO(0,t,1);
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x41)) {
+ if(1){
+ r=m=1; // Line 15
+ k.KO(0,t,"α");
+ }
+ }
+ if(m==1) {
+
+ r=this.g_DK1_1(t,e);
+ m=2;
+ }
+ return r;
+ };
+ this.g_DK1_1=function(t,e) {
+ var k=KeymanWeb,r=1,m=0;
+ if(k.KDM(1,t,0)&&k.KCM(1,t,"α",1)){
+ m=1; // Line 25
+ k.KO(1,t,"ᾱ");
+ }
+ else if(k.KDM(1,t,1)&&k.KCM(1,t,"α",1)){
+ m=1; // Line 26
+ k.KO(1,t,"ᾰ");
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmn b/common/test/keyboards/baseline/k_0400___groups_and_virtual_keys.kmn
similarity index 92%
rename from common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmn
rename to common/test/keyboards/baseline/k_0400___groups_and_virtual_keys.kmn
index 0e32c432eb6..67e63340a1c 100644
--- a/common/test/keyboards/baseline/k_014___groups_and_virtual_keys.kmn
+++ b/common/test/keyboards/baseline/k_0400___groups_and_virtual_keys.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '014 - groups and virtual keys'
+store(&NAME) '0400 - groups and virtual keys'
c Description: Tests multiple groups and virtual keys with mnemonic layout
c keys: [K_A][SHIFT K_2][K_A][LCTRL K_2][K_A]
c expected: \u03B1\u1FB0\u1FB1
diff --git a/common/test/keyboards/baseline/k_0400___groups_and_virtual_keys.kmx b/common/test/keyboards/baseline/k_0400___groups_and_virtual_keys.kmx
new file mode 100644
index 00000000000..c0ab17f6577
Binary files /dev/null and b/common/test/keyboards/baseline/k_0400___groups_and_virtual_keys.kmx differ
diff --git a/common/test/keyboards/baseline/k_0401___multiple_groups.js b/common/test/keyboards/baseline/k_0401___multiple_groups.js
new file mode 100644
index 00000000000..12bc00f9e61
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0401___multiple_groups.js
@@ -0,0 +1,73 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0401___multiple_groups());
+}
+function Keyboard_k_0401___multiple_groups()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0401___multiple_groups";
+ this.KN="401 - multiple groups";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KDC(0,t);
+ k.KO(-1,t,"a");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_2 /* 0x32 */)) {
+ if(k.KFCM(1,t,['b'])){
+ r=m=1; // Line 14
+ k.KDC(1,t);
+ k.KO(-1,t,"b");
+ r=this.g_b_2(t,e);
+ m=2;
+ }
+ }
+ if(m==1) {
+
+ k.KDC(-1,t);
+ r=this.g_a_1(t,e);
+ m=2;
+ }
+ return r;
+ };
+ this.g_a_1=function(t,e) {
+ var k=KeymanWeb,r=1,m=0;
+ if(k.KFCM(1,t,['a'])){
+ m=1; // Line 20
+ k.KDC(1,t);
+ k.KO(-1,t,"b");
+ }
+ return r;
+ };
+ this.g_b_2=function(t,e) {
+ var k=KeymanWeb,r=1,m=0;
+ if(k.KFCM(1,t,['b'])){
+ m=1; // Line 24
+ k.KDC(1,t);
+ k.KO(-1,t,"abc");
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_030___multiple_groups.kmn b/common/test/keyboards/baseline/k_0401___multiple_groups.kmn
similarity index 90%
rename from common/test/keyboards/baseline/k_030___multiple_groups.kmn
rename to common/test/keyboards/baseline/k_0401___multiple_groups.kmn
index b9148827bc8..87097387aa2 100644
--- a/common/test/keyboards/baseline/k_030___multiple_groups.kmn
+++ b/common/test/keyboards/baseline/k_0401___multiple_groups.kmn
@@ -1,4 +1,4 @@
-store(&NAME) "030 - multiple groups"
+store(&NAME) "401 - multiple groups"
c Description: Tests multiple groups. Purpose here is to interleave output and backspaces from kmx processor to consumer
c keys: [K_1][K_2]
c expected: abc
diff --git a/common/test/keyboards/baseline/k_030___multiple_groups.kmx b/common/test/keyboards/baseline/k_0401___multiple_groups.kmx
similarity index 67%
rename from common/test/keyboards/baseline/k_030___multiple_groups.kmx
rename to common/test/keyboards/baseline/k_0401___multiple_groups.kmx
index e27ee4dd08c..415a932ccb7 100644
Binary files a/common/test/keyboards/baseline/k_030___multiple_groups.kmx and b/common/test/keyboards/baseline/k_0401___multiple_groups.kmx differ
diff --git a/common/test/keyboards/baseline/k_0402___output_and_keystroke.js b/common/test/keyboards/baseline/k_0402___output_and_keystroke.js
new file mode 100644
index 00000000000..aa1e8903298
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0402___output_and_keystroke.js
@@ -0,0 +1,87 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0402___output_and_keystroke());
+}
+function Keyboard_k_0402___output_and_keystroke()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0402___output_and_keystroke";
+ this.KN="0402 - output and keystroke";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KDC(0,t);
+ k.KO(-1,t,"a");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_2 /* 0x32 */)) {
+ if(k.KFCM(1,t,['b'])){
+ r=m=1; // Line 14
+ k.KDC(1,t);
+ k.KO(-1,t,"b");
+ r=this.g_b_2(t,e);
+ m=2;
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_3 /* 0x33 */)) {
+ if(k.KFCM(3,t,['a','b','c'])){
+ r=m=1; // Line 15
+ k.KDC(3,t);
+ k.KO(-1,t,"a");
+ k.KO(-1,t,"bd");
+ r=this.g_emit_3(t,e);
+ m=2;
+ }
+ }
+ if(m==1) {
+
+ k.KDC(-1,t);
+ r=this.g_a_1(t,e);
+ m=2;
+ }
+ return r;
+ };
+ this.g_a_1=function(t,e) {
+ var k=KeymanWeb,r=1,m=0;
+ if(k.KFCM(1,t,['a'])){
+ m=1; // Line 21
+ k.KDC(1,t);
+ k.KO(-1,t,"b");
+ }
+ return r;
+ };
+ this.g_b_2=function(t,e) {
+ var k=KeymanWeb,r=1,m=0;
+ if(k.KFCM(1,t,['b'])){
+ m=1; // Line 25
+ k.KDC(1,t);
+ k.KO(-1,t,"abc");
+ }
+ return r;
+ };
+ this.g_emit_3=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_043___output_and_keystroke.kmn b/common/test/keyboards/baseline/k_0402___output_and_keystroke.kmn
similarity index 90%
rename from common/test/keyboards/baseline/k_043___output_and_keystroke.kmn
rename to common/test/keyboards/baseline/k_0402___output_and_keystroke.kmn
index 4c6409c1c71..dd8df421841 100644
--- a/common/test/keyboards/baseline/k_043___output_and_keystroke.kmn
+++ b/common/test/keyboards/baseline/k_0402___output_and_keystroke.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '043 - output and keystroke'
+store(&NAME) '0402 - output and keystroke'
c Description: Tests the 'emit default key' function by having an empty 'using keys' final group
c keys: [K_1][K_2][K_3]
c expected: abd3
diff --git a/common/test/keyboards/baseline/k_0402___output_and_keystroke.kmx b/common/test/keyboards/baseline/k_0402___output_and_keystroke.kmx
new file mode 100644
index 00000000000..a6a883d2fd2
Binary files /dev/null and b/common/test/keyboards/baseline/k_0402___output_and_keystroke.kmx differ
diff --git a/common/test/keyboards/baseline/k_040___long_context.kmx b/common/test/keyboards/baseline/k_040___long_context.kmx
deleted file mode 100644
index 55a88a77abb..00000000000
Binary files a/common/test/keyboards/baseline/k_040___long_context.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmx b/common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmx
deleted file mode 100644
index de2b1d5020c..00000000000
Binary files a/common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmx b/common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmx
deleted file mode 100644
index fbe552d53b0..00000000000
Binary files a/common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_043___output_and_keystroke.kmx b/common/test/keyboards/baseline/k_043___output_and_keystroke.kmx
deleted file mode 100644
index a25aae26a64..00000000000
Binary files a/common/test/keyboards/baseline/k_043___output_and_keystroke.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_044___if_and_context.kmx b/common/test/keyboards/baseline/k_044___if_and_context.kmx
deleted file mode 100644
index 60c0c6300a6..00000000000
Binary files a/common/test/keyboards/baseline/k_044___if_and_context.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_045___deadkey_and_context.kmx b/common/test/keyboards/baseline/k_045___deadkey_and_context.kmx
deleted file mode 100644
index 4b1e3bfb2aa..00000000000
Binary files a/common/test/keyboards/baseline/k_045___deadkey_and_context.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmx b/common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmx
deleted file mode 100644
index 618eefbf27f..00000000000
Binary files a/common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmx b/common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmx
deleted file mode 100644
index 301fcddc046..00000000000
Binary files a/common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmx b/common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmx
deleted file mode 100644
index 98ca5a6dbe3..00000000000
Binary files a/common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_049___enter_invalidates_context.kmx b/common/test/keyboards/baseline/k_049___enter_invalidates_context.kmx
deleted file mode 100644
index 916a4a46625..00000000000
Binary files a/common/test/keyboards/baseline/k_049___enter_invalidates_context.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_0500___options.js b/common/test/keyboards/baseline/k_0500___options.js
new file mode 100644
index 00000000000..a9eb29cc71d
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0500___options.js
@@ -0,0 +1,64 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0500___options());
+}
+function Keyboard_k_0500___options()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0500___options";
+ this.KN="0500 - options";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_foo_4=KeymanWeb.KLOAD(this.KI,"foo","0");
+ this.s7="1";
+ this.s8="0";
+ this.s9="1";
+ this.s10="0";
+ this.KVS=['s_foo_4'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_0 /* 0x30 */)) {
+ if(1){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ this.s_foo_4=this.s10;
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 17
+ k.KDC(0,t);
+ this.s_foo_4=this.s9;
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(this.s_foo_4===this.s7){
+ r=m=1; // Line 15
+ k.KDC(0,t);
+ k.KO(-1,t,"foo.");
+ }
+ else if(this.s_foo_4===this.s8){
+ r=m=1; // Line 16
+ k.KDC(0,t);
+ k.KO(-1,t,"no foo.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_021___options.kmn b/common/test/keyboards/baseline/k_0500___options.kmn
similarity index 88%
rename from common/test/keyboards/baseline/k_021___options.kmn
rename to common/test/keyboards/baseline/k_0500___options.kmn
index 3851896b84e..428168cadbc 100644
--- a/common/test/keyboards/baseline/k_021___options.kmn
+++ b/common/test/keyboards/baseline/k_0500___options.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '021 - options'
+store(&NAME) '0500 - options'
c Description: Tests basic option rules
c keys: [K_A][K_1][K_A][K_0][K_A]
c expected: no foo.foo.no foo.
-c context:
+c context:
store(&version) '10.0'
diff --git a/common/test/keyboards/baseline/k_0500___options.kmx b/common/test/keyboards/baseline/k_0500___options.kmx
new file mode 100644
index 00000000000..df17de841b1
Binary files /dev/null and b/common/test/keyboards/baseline/k_0500___options.kmx differ
diff --git a/common/test/keyboards/baseline/k_0501___options_with_preset.js b/common/test/keyboards/baseline/k_0501___options_with_preset.js
new file mode 100644
index 00000000000..bf48622b987
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0501___options_with_preset.js
@@ -0,0 +1,66 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0501___options_with_preset());
+}
+function Keyboard_k_0501___options_with_preset()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0501___options_with_preset";
+ this.KN="0501 - options with preset";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_foo_4=KeymanWeb.KLOAD(this.KI,"foo","0");
+ this.s7="1";
+ this.s8="0";
+ this.s9="1";
+ this.s10="0";
+ this.KVS=['s_foo_4'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_0 /* 0x30 */)) {
+ if(1){
+ r=m=1; // Line 21
+ k.KDC(0,t);
+ this.s_foo_4=this.s10;
+ k.KSAVE("foo",this.s_foo_4);
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ this.s_foo_4=this.s9;
+ k.KSAVE("foo",this.s_foo_4);
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(this.s_foo_4===this.s7){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ k.KO(-1,t,"foo.");
+ }
+ else if(this.s_foo_4===this.s8){
+ r=m=1; // Line 19
+ k.KDC(0,t);
+ k.KO(-1,t,"no foo.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_022___options_with_preset.kmn b/common/test/keyboards/baseline/k_0501___options_with_preset.kmn
similarity index 88%
rename from common/test/keyboards/baseline/k_022___options_with_preset.kmn
rename to common/test/keyboards/baseline/k_0501___options_with_preset.kmn
index dbaad6e63c8..98184fca342 100644
--- a/common/test/keyboards/baseline/k_022___options_with_preset.kmn
+++ b/common/test/keyboards/baseline/k_0501___options_with_preset.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '022 - options with preset'
+store(&NAME) '0501 - options with preset'
c Description: Tests basic option rules with a preset supplied
c keys: [K_A][K_1][K_A][K_0][K_A]
c expected: foo.foo.no foo.
-c context:
+c context:
c option: foo=1
c expected option: foo=0
c saved option: foo=0
diff --git a/common/test/keyboards/baseline/k_0501___options_with_preset.kmx b/common/test/keyboards/baseline/k_0501___options_with_preset.kmx
new file mode 100644
index 00000000000..0ed8c43f5e5
Binary files /dev/null and b/common/test/keyboards/baseline/k_0501___options_with_preset.kmx differ
diff --git a/common/test/keyboards/baseline/k_0502___options_with_save.js b/common/test/keyboards/baseline/k_0502___options_with_save.js
new file mode 100644
index 00000000000..26c4fa041bd
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0502___options_with_save.js
@@ -0,0 +1,71 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0502___options_with_save());
+}
+function Keyboard_k_0502___options_with_save()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0502___options_with_save";
+ this.KN="0502 - options with save";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_foo_4=KeymanWeb.KLOAD(this.KI,"foo","0");
+ this.s7="1";
+ this.s8="0";
+ this.s9="1";
+ this.s10="0";
+ this.KVS=['s_foo_4'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_0 /* 0x30 */)) {
+ if(1){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ this.s_foo_4=this.s10;
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 19
+ k.KDC(0,t);
+ this.s_foo_4=this.s9;
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_2 /* 0x32 */)) {
+ if(1){
+ r=m=1; // Line 21
+ k.KDC(0,t);
+ k.KSAVE("foo",this.s_foo_4);
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(this.s_foo_4===this.s7){
+ r=m=1; // Line 17
+ k.KDC(0,t);
+ k.KO(-1,t,"foo.");
+ }
+ else if(this.s_foo_4===this.s8){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ k.KO(-1,t,"no foo.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_023___options_with_save.kmn b/common/test/keyboards/baseline/k_0502___options_with_save.kmn
similarity index 88%
rename from common/test/keyboards/baseline/k_023___options_with_save.kmn
rename to common/test/keyboards/baseline/k_0502___options_with_save.kmn
index 913bea076f3..987b30055c0 100644
--- a/common/test/keyboards/baseline/k_023___options_with_save.kmn
+++ b/common/test/keyboards/baseline/k_0502___options_with_save.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '023 - options with save'
+store(&NAME) '0502 - options with save'
c Description: Tests basic option rules with save
c keys: [K_A][K_1][K_A][K_0][K_A][K_2]
c expected: no foo.foo.no foo.
-c context:
+c context:
c expected option: foo=0
c saved option: foo=0
diff --git a/common/test/keyboards/baseline/k_0502___options_with_save.kmx b/common/test/keyboards/baseline/k_0502___options_with_save.kmx
new file mode 100644
index 00000000000..5277a9c6e31
Binary files /dev/null and b/common/test/keyboards/baseline/k_0502___options_with_save.kmx differ
diff --git a/common/test/keyboards/baseline/k_0503___options_with_save_and_preset.js b/common/test/keyboards/baseline/k_0503___options_with_save_and_preset.js
new file mode 100644
index 00000000000..0ce89bd875a
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0503___options_with_save_and_preset.js
@@ -0,0 +1,72 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0503___options_with_save_and_preset());
+}
+function Keyboard_k_0503___options_with_save_and_preset()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0503___options_with_save_and_preset";
+ this.KN="0503 - options with save and preset";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_foo_4=KeymanWeb.KLOAD(this.KI,"foo","0");
+ this.s7="1";
+ this.s8="0";
+ this.s9="1";
+ this.s10="0";
+ this.s11="0";
+ this.KVS=['s_foo_4'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_0 /* 0x30 */)) {
+ if(1){
+ r=m=1; // Line 21
+ k.KDC(0,t);
+ this.s_foo_4=this.s10;
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ this.s_foo_4=this.s9;
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_2 /* 0x32 */)) {
+ if(this.s_foo_4===this.s11){
+ r=m=1; // Line 22
+ k.KDC(0,t);
+ k.KSAVE("foo",this.s_foo_4);
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(this.s_foo_4===this.s7){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ k.KO(-1,t,"foo.");
+ }
+ else if(this.s_foo_4===this.s8){
+ r=m=1; // Line 19
+ k.KDC(0,t);
+ k.KO(-1,t,"no foo.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmn b/common/test/keyboards/baseline/k_0503___options_with_save_and_preset.kmn
similarity index 86%
rename from common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmn
rename to common/test/keyboards/baseline/k_0503___options_with_save_and_preset.kmn
index 0c3004c3e32..9688ed8e616 100644
--- a/common/test/keyboards/baseline/k_024___options_with_save_and_preset.kmn
+++ b/common/test/keyboards/baseline/k_0503___options_with_save_and_preset.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '024 - options with save and preset'
+store(&NAME) '0503 - options with save and preset'
c Description: Tests basic option rules with save
c keys: [K_A][K_1][K_A][K_0][K_A][K_2]
c expected: foo.foo.no foo.
-c context:
+c context:
c option: foo=1
c expected option: foo=0
c saved option: foo=0
diff --git a/common/test/keyboards/baseline/k_0503___options_with_save_and_preset.kmx b/common/test/keyboards/baseline/k_0503___options_with_save_and_preset.kmx
new file mode 100644
index 00000000000..49b55208b61
Binary files /dev/null and b/common/test/keyboards/baseline/k_0503___options_with_save_and_preset.kmx differ
diff --git a/common/test/keyboards/baseline/k_0504___options_with_reset.js b/common/test/keyboards/baseline/k_0504___options_with_reset.js
new file mode 100644
index 00000000000..5cb7dc19aeb
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0504___options_with_reset.js
@@ -0,0 +1,71 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0504___options_with_reset());
+}
+function Keyboard_k_0504___options_with_reset()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0504___options_with_reset";
+ this.KN="0504 - options with reset";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_foo_4=KeymanWeb.KLOAD(this.KI,"foo","0");
+ this.s7="1";
+ this.s8="0";
+ this.s9="1";
+ this.s10="0";
+ this.KVS=['s_foo_4'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_0 /* 0x30 */)) {
+ if(1){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ this.s_foo_4=this.s10;
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 19
+ k.KDC(0,t);
+ this.s_foo_4=this.s9;
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_3 /* 0x33 */)) {
+ if(1){
+ r=m=1; // Line 21
+ k.KDC(0,t);
+ this.s_foo_4=k.KLOAD(this.KI,"foo","0");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(this.s_foo_4===this.s7){
+ r=m=1; // Line 17
+ k.KDC(0,t);
+ k.KO(-1,t,"foo.");
+ }
+ else if(this.s_foo_4===this.s8){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ k.KO(-1,t,"no foo.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_025___options_with_reset.kmn b/common/test/keyboards/baseline/k_0504___options_with_reset.kmn
similarity index 87%
rename from common/test/keyboards/baseline/k_025___options_with_reset.kmn
rename to common/test/keyboards/baseline/k_0504___options_with_reset.kmn
index 13a0d692837..caeb29c25d0 100644
--- a/common/test/keyboards/baseline/k_025___options_with_reset.kmn
+++ b/common/test/keyboards/baseline/k_0504___options_with_reset.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '025 - options with reset'
+store(&NAME) '0504 - options with reset'
c Description: Tests basic option rules with reset
c keys: [K_A][K_1][K_A][K_0][K_A][K_3]
c expected: foo.foo.no foo.
-c context:
+c context:
c option: foo=1
c expected option: foo=1
diff --git a/common/test/keyboards/baseline/k_0504___options_with_reset.kmx b/common/test/keyboards/baseline/k_0504___options_with_reset.kmx
new file mode 100644
index 00000000000..413060deca8
Binary files /dev/null and b/common/test/keyboards/baseline/k_0504___options_with_reset.kmx differ
diff --git a/common/test/keyboards/baseline/k_0505___options_double_set_reset.js b/common/test/keyboards/baseline/k_0505___options_double_set_reset.js
new file mode 100644
index 00000000000..7e89315cdaa
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0505___options_double_set_reset.js
@@ -0,0 +1,66 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0505___options_double_set_reset());
+}
+function Keyboard_k_0505___options_double_set_reset()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0505___options_double_set_reset";
+ this.KN="0505 - options double set reset";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_foo_4=KeymanWeb.KLOAD(this.KI,"foo","0");
+ this.s7="1";
+ this.s8="2";
+ this.s9="3";
+ this.s10="2";
+ this.s11="3";
+ this.s12="0";
+ this.KVS=['s_foo_4'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(this.s_foo_4===this.s7){
+ r=m=1; // Line 17
+ k.KDC(0,t);
+ k.KO(-1,t,"foo.");
+ this.s_foo_4=this.s8;
+ this.s_foo_4=k.KLOAD(this.KI,"foo","0");
+ this.s_foo_4=this.s9;
+ this.s_foo_4=k.KLOAD(this.KI,"foo","0");
+ }
+ else if(this.s_foo_4===this.s10){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ k.KO(-1,t,"bar.");
+ }
+ else if(this.s_foo_4===this.s11){
+ r=m=1; // Line 19
+ k.KDC(0,t);
+ k.KO(-1,t,"baz.");
+ }
+ else if(this.s_foo_4===this.s12){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ k.KO(-1,t,"no foo.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_034___options_double_set_reset.kmn b/common/test/keyboards/baseline/k_0505___options_double_set_reset.kmn
similarity index 87%
rename from common/test/keyboards/baseline/k_034___options_double_set_reset.kmn
rename to common/test/keyboards/baseline/k_0505___options_double_set_reset.kmn
index 3fe9225ecad..e91f95e26b7 100644
--- a/common/test/keyboards/baseline/k_034___options_double_set_reset.kmn
+++ b/common/test/keyboards/baseline/k_0505___options_double_set_reset.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '034 - options double set reset'
+store(&NAME) '0505 - options double set reset'
c Description: Tests basic option rules with save reset+set+reset
c keys: [K_A][K_A]
c expected: foo.foo.
-c context:
+c context:
c option: foo=1
c expected option: foo=1
diff --git a/common/test/keyboards/baseline/k_0505___options_double_set_reset.kmx b/common/test/keyboards/baseline/k_0505___options_double_set_reset.kmx
new file mode 100644
index 00000000000..f44ef699c0f
Binary files /dev/null and b/common/test/keyboards/baseline/k_0505___options_double_set_reset.kmx differ
diff --git a/common/test/keyboards/baseline/k_0506___options_double_set_staged.js b/common/test/keyboards/baseline/k_0506___options_double_set_staged.js
new file mode 100644
index 00000000000..d0e5bb58ff6
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0506___options_double_set_staged.js
@@ -0,0 +1,77 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0506___options_double_set_staged());
+}
+function Keyboard_k_0506___options_double_set_staged()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0506___options_double_set_staged";
+ this.KN="0506 - options double set staged";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_foo_4=KeymanWeb.KLOAD(this.KI,"foo","0");
+ this.s7="1";
+ this.s8="2";
+ this.s9="3";
+ this.s10="2";
+ this.s11="3";
+ this.s12="0";
+ this.KVS=['s_foo_4'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(this.s_foo_4===this.s7){
+ r=m=1; // Line 17
+ k.KDC(0,t);
+ k.KO(-1,t,"foo.");
+ this.s_foo_4=this.s8;
+ }
+ else if(this.s_foo_4===this.s10){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ k.KO(-1,t,"bar.");
+ }
+ else if(this.s_foo_4===this.s11){
+ r=m=1; // Line 21
+ k.KDC(0,t);
+ k.KO(-1,t,"baz.");
+ }
+ else if(this.s_foo_4===this.s12){
+ r=m=1; // Line 22
+ k.KDC(0,t);
+ k.KO(-1,t,"no foo.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_B /* 0x42 */)) {
+ if(1){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ this.s_foo_4=k.KLOAD(this.KI,"foo","0");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_C /* 0x43 */)) {
+ if(1){
+ r=m=1; // Line 19
+ k.KDC(0,t);
+ this.s_foo_4=this.s9;
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_035___options_double_set_staged.kmn b/common/test/keyboards/baseline/k_0506___options_double_set_staged.kmn
similarity index 90%
rename from common/test/keyboards/baseline/k_035___options_double_set_staged.kmn
rename to common/test/keyboards/baseline/k_0506___options_double_set_staged.kmn
index 75d1b81b89b..c78bee2fcf7 100644
--- a/common/test/keyboards/baseline/k_035___options_double_set_staged.kmn
+++ b/common/test/keyboards/baseline/k_0506___options_double_set_staged.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '035 - options double set staged'
+store(&NAME) '0506 - options double set staged'
c Description: Tests basic option rules with save reset+set+reset
c keys: [K_A][K_B][K_C][K_B][K_A]
c expected: foo.foo.
diff --git a/common/test/keyboards/baseline/k_0506___options_double_set_staged.kmx b/common/test/keyboards/baseline/k_0506___options_double_set_staged.kmx
new file mode 100644
index 00000000000..c6f03efa7cd
Binary files /dev/null and b/common/test/keyboards/baseline/k_0506___options_double_set_staged.kmx differ
diff --git a/common/test/keyboards/baseline/k_0507___options___double_reset_staged.js b/common/test/keyboards/baseline/k_0507___options___double_reset_staged.js
new file mode 100644
index 00000000000..754fd8ae658
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0507___options___double_reset_staged.js
@@ -0,0 +1,63 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0507___options___double_reset_staged());
+}
+function Keyboard_k_0507___options___double_reset_staged()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0507___options___double_reset_staged";
+ this.KN="0507- options - double reset staged";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_foo_4=KeymanWeb.KLOAD(this.KI,"foo","0");
+ this.s7="1";
+ this.s8="2";
+ this.s9="2";
+ this.s10="0";
+ this.KVS=['s_foo_4'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(this.s_foo_4===this.s7){
+ r=m=1; // Line 17
+ k.KDC(0,t);
+ k.KO(-1,t,"foo.");
+ this.s_foo_4=this.s8;
+ }
+ else if(this.s_foo_4===this.s9){
+ r=m=1; // Line 19
+ k.KDC(0,t);
+ k.KO(-1,t,"bar.");
+ }
+ else if(this.s_foo_4===this.s10){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ k.KO(-1,t,"no foo.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_B /* 0x42 */)) {
+ if(1){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ this.s_foo_4=k.KLOAD(this.KI,"foo","0");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_036___options___double_reset_staged.kmn b/common/test/keyboards/baseline/k_0507___options___double_reset_staged.kmn
similarity index 88%
rename from common/test/keyboards/baseline/k_036___options___double_reset_staged.kmn
rename to common/test/keyboards/baseline/k_0507___options___double_reset_staged.kmn
index 53157b487d3..e250695af1d 100644
--- a/common/test/keyboards/baseline/k_036___options___double_reset_staged.kmn
+++ b/common/test/keyboards/baseline/k_0507___options___double_reset_staged.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '036 - options - double reset staged'
+store(&NAME) '0507- options - double reset staged'
c Description: Tests basic option rules with save reset+reset
c keys: [K_A][K_B][K_B][K_A]
c expected: foo.foo.
diff --git a/common/test/keyboards/baseline/k_036___options___double_reset_staged.kmx b/common/test/keyboards/baseline/k_0507___options___double_reset_staged.kmx
similarity index 79%
rename from common/test/keyboards/baseline/k_036___options___double_reset_staged.kmx
rename to common/test/keyboards/baseline/k_0507___options___double_reset_staged.kmx
index ccb05e04c1f..2aba8f9cdbc 100644
Binary files a/common/test/keyboards/baseline/k_036___options___double_reset_staged.kmx and b/common/test/keyboards/baseline/k_0507___options___double_reset_staged.kmx differ
diff --git a/common/test/keyboards/baseline/k_0508___options___double_reset.js b/common/test/keyboards/baseline/k_0508___options___double_reset.js
new file mode 100644
index 00000000000..9f208eaaafb
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0508___options___double_reset.js
@@ -0,0 +1,58 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0508___options___double_reset());
+}
+function Keyboard_k_0508___options___double_reset()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0508___options___double_reset";
+ this.KN="0508 - options - double reset";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_foo_4=KeymanWeb.KLOAD(this.KI,"foo","0");
+ this.s7="1";
+ this.s8="2";
+ this.s9="2";
+ this.s10="0";
+ this.KVS=['s_foo_4'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(this.s_foo_4===this.s7){
+ r=m=1; // Line 17
+ k.KDC(0,t);
+ k.KO(-1,t,"foo.");
+ this.s_foo_4=this.s8;
+ this.s_foo_4=k.KLOAD(this.KI,"foo","0");
+ this.s_foo_4=k.KLOAD(this.KI,"foo","0");
+ }
+ else if(this.s_foo_4===this.s9){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ k.KO(-1,t,"bar.");
+ }
+ else if(this.s_foo_4===this.s10){
+ r=m=1; // Line 19
+ k.KDC(0,t);
+ k.KO(-1,t,"no foo.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_037___options___double_reset.kmn b/common/test/keyboards/baseline/k_0508___options___double_reset.kmn
similarity index 86%
rename from common/test/keyboards/baseline/k_037___options___double_reset.kmn
rename to common/test/keyboards/baseline/k_0508___options___double_reset.kmn
index 05e5fe7b024..766cf5f87c9 100644
--- a/common/test/keyboards/baseline/k_037___options___double_reset.kmn
+++ b/common/test/keyboards/baseline/k_0508___options___double_reset.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '037 - options - double reset'
+store(&NAME) '0508 - options - double reset'
c Description: Tests basic option rules with save reset+reset
c keys: [K_A][K_A]
c expected: foo.foo.
-c context:
+c context:
c option: foo=1
c expected option: foo=1
diff --git a/common/test/keyboards/baseline/k_0508___options___double_reset.kmx b/common/test/keyboards/baseline/k_0508___options___double_reset.kmx
new file mode 100644
index 00000000000..f34e800c022
Binary files /dev/null and b/common/test/keyboards/baseline/k_0508___options___double_reset.kmx differ
diff --git a/common/test/keyboards/baseline/k_050___nul_and_context.kmx b/common/test/keyboards/baseline/k_050___nul_and_context.kmx
deleted file mode 100644
index a777f48e2c8..00000000000
Binary files a/common/test/keyboards/baseline/k_050___nul_and_context.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_051___if_and_context.kmx b/common/test/keyboards/baseline/k_051___if_and_context.kmx
deleted file mode 100644
index facfe918590..00000000000
Binary files a/common/test/keyboards/baseline/k_051___if_and_context.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_052___nul_and_index.kmx b/common/test/keyboards/baseline/k_052___nul_and_index.kmx
deleted file mode 100644
index 7e533db7912..00000000000
Binary files a/common/test/keyboards/baseline/k_052___nul_and_index.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_053___if_and_index.kmx b/common/test/keyboards/baseline/k_053___if_and_index.kmx
deleted file mode 100644
index ee426130348..00000000000
Binary files a/common/test/keyboards/baseline/k_053___if_and_index.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_054___nul_and_contextex.kmx b/common/test/keyboards/baseline/k_054___nul_and_contextex.kmx
deleted file mode 100644
index 3c75d606f44..00000000000
Binary files a/common/test/keyboards/baseline/k_054___nul_and_contextex.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_055___deadkey_cancelled_by_arrow.kmx b/common/test/keyboards/baseline/k_055___deadkey_cancelled_by_arrow.kmx
deleted file mode 100644
index 551a2639209..00000000000
Binary files a/common/test/keyboards/baseline/k_055___deadkey_cancelled_by_arrow.kmx and /dev/null differ
diff --git a/common/test/keyboards/baseline/k_0600___system_stores.js b/common/test/keyboards/baseline/k_0600___system_stores.js
new file mode 100644
index 00000000000..b72c56cc1cd
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0600___system_stores.js
@@ -0,0 +1,211 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0600___system_stores());
+}
+function Keyboard_k_0600___system_stores()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0600___system_stores";
+ this.KN="0600 - system stores";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_digit_4="1234567890";
+ this.s_letter_5="abcdefghij";
+ this.s8="windows";
+ this.s9="desktop";
+ this.s10="hardware";
+ this.s11="native";
+ this.s12="en-US";
+ this.s13="kbdus.dll";
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_0 /* 0x30 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) {
+ if(k.KIFS(31,this.s8,t)){
+ r=m=1; // Line 19
+ k.KDC(0,t);
+ k.KO(-1,t,"windows.");
+ }
+ else if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_2 /* 0x32 */)) {
+ if(k.KIFS(31,this.s9,t)){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ k.KO(-1,t,"desktop.");
+ }
+ else if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_3 /* 0x33 */)) {
+ if(k.KIFS(31,this.s10,t)){
+ r=m=1; // Line 21
+ k.KDC(0,t);
+ k.KO(-1,t,"hardware.");
+ }
+ else if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_4 /* 0x34 */)) {
+ if(k.KIFS(31,this.s11,t)){
+ r=m=1; // Line 22
+ k.KDC(0,t);
+ k.KO(-1,t,"native.");
+ }
+ else if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_5 /* 0x35 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_6 /* 0x36 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_7 /* 0x37 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_8 /* 0x38 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_9 /* 0x39 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(k.KIFS(32,this.s12,t)){
+ r=m=1; // Line 25
+ k.KDC(0,t);
+ k.KO(-1,t,"en-US.");
+ }
+ else if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_B /* 0x42 */)) {
+ if(k.KIFS(32,this.s13,t)){
+ r=m=1; // Line 26
+ k.KDC(0,t);
+ k.KO(-1,t,"kbdus.dll.");
+ }
+ else if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_C /* 0x43 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_D /* 0x44 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_E /* 0x45 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_F /* 0x46 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_G /* 0x47 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_H /* 0x48 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_I /* 0x49 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_J /* 0x4A */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_026___system_stores.kmn b/common/test/keyboards/baseline/k_0600___system_stores.kmn
similarity index 94%
rename from common/test/keyboards/baseline/k_026___system_stores.kmn
rename to common/test/keyboards/baseline/k_0600___system_stores.kmn
index 0538c51364b..9b63ffb576c 100644
--- a/common/test/keyboards/baseline/k_026___system_stores.kmn
+++ b/common/test/keyboards/baseline/k_0600___system_stores.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '026 - system stores'
+store(&NAME) '0600 - system stores'
c Description: Tests platform and baselayout system store rules
c keys: [K_1][K_2][K_3][K_4][K_A][K_B]
c expected: windows.desktop.hardware.native.en-US.kbdus.dll.
-c context:
+c context:
c (default) option: &platform=windows desktop hardware native <-- ignored by test
c (default) option: &baseLayout=kbdus.dll <-- ignored by test
c (default) option: &baseLayoutAlt=en-US <-- ignored by test
diff --git a/common/test/keyboards/baseline/k_0600___system_stores.kmx b/common/test/keyboards/baseline/k_0600___system_stores.kmx
new file mode 100644
index 00000000000..54c86383bc3
Binary files /dev/null and b/common/test/keyboards/baseline/k_0600___system_stores.kmx differ
diff --git a/common/test/keyboards/baseline/k_0601___system_stores_2.js b/common/test/keyboards/baseline/k_0601___system_stores_2.js
new file mode 100644
index 00000000000..2a2d14135ea
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0601___system_stores_2.js
@@ -0,0 +1,211 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0601___system_stores_2());
+}
+function Keyboard_k_0601___system_stores_2()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0601___system_stores_2";
+ this.KN="0601 - system stores 2";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_digit_4="1234567890";
+ this.s_letter_5="abcdefghij";
+ this.s8="linux";
+ this.s9="desktop";
+ this.s10="hardware";
+ this.s11="native";
+ this.s12="fr-FR";
+ this.s13="kbdfr.dll";
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_0 /* 0x30 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) {
+ if(k.KIFS(31,this.s8,t)){
+ r=m=1; // Line 19
+ k.KDC(0,t);
+ k.KO(-1,t,"linux.");
+ }
+ else if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_2 /* 0x32 */)) {
+ if(k.KIFS(31,this.s9,t)){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ k.KO(-1,t,"desktop.");
+ }
+ else if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_3 /* 0x33 */)) {
+ if(k.KIFS(31,this.s10,t)){
+ r=m=1; // Line 21
+ k.KDC(0,t);
+ k.KO(-1,t,"hardware.");
+ }
+ else if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_4 /* 0x34 */)) {
+ if(k.KIFS(31,this.s11,t)){
+ r=m=1; // Line 22
+ k.KDC(0,t);
+ k.KO(-1,t,"native.");
+ }
+ else if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_5 /* 0x35 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_6 /* 0x36 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_7 /* 0x37 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_8 /* 0x38 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_9 /* 0x39 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"platform-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(k.KIFS(32,this.s12,t)){
+ r=m=1; // Line 25
+ k.KDC(0,t);
+ k.KO(-1,t,"fr-FR.");
+ }
+ else if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_B /* 0x42 */)) {
+ if(k.KIFS(32,this.s13,t)){
+ r=m=1; // Line 26
+ k.KDC(0,t);
+ k.KO(-1,t,"kbdfr.dll.");
+ }
+ else if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_C /* 0x43 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_D /* 0x44 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_E /* 0x45 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_F /* 0x46 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_G /* 0x47 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_H /* 0x48 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_I /* 0x49 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_J /* 0x4A */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"baselayout-fail.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_027___system_stores_2.kmn b/common/test/keyboards/baseline/k_0601___system_stores_2.kmn
similarity index 93%
rename from common/test/keyboards/baseline/k_027___system_stores_2.kmn
rename to common/test/keyboards/baseline/k_0601___system_stores_2.kmn
index 95457c84639..3e30cbf5e5d 100644
--- a/common/test/keyboards/baseline/k_027___system_stores_2.kmn
+++ b/common/test/keyboards/baseline/k_0601___system_stores_2.kmn
@@ -1,8 +1,8 @@
-store(&NAME) '027 - system stores 2'
+store(&NAME) '0601 - system stores 2'
c Description: Tests platform and baselayout system store rules
c keys: [K_1][K_2][K_3][K_4][K_A][K_B]
c expected: linux.desktop.hardware.native.fr-FR.kbdfr.dll.
-c context:
+c context:
c option: &platform=linux desktop hardware native
c option: &baseLayout=kbdfr.dll
c option: &baseLayoutAlt=fr-FR
diff --git a/common/test/keyboards/baseline/k_0601___system_stores_2.kmx b/common/test/keyboards/baseline/k_0601___system_stores_2.kmx
new file mode 100644
index 00000000000..f19063be7d9
Binary files /dev/null and b/common/test/keyboards/baseline/k_0601___system_stores_2.kmx differ
diff --git a/common/test/keyboards/baseline/k_0700___caps_lock.js b/common/test/keyboards/baseline/k_0700___caps_lock.js
new file mode 100644
index 00000000000..a89cef36e44
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0700___caps_lock.js
@@ -0,0 +1,103 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0700___caps_lock());
+}
+function Keyboard_k_0700___caps_lock()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0700___caps_lock";
+ this.KN="0700 - caps lock";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=modCodes.SHIFT | modCodes.CAPS | modCodes.NO_CAPS /* 0x0310 */;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_3 /* 0x33 */)) {
+ if(1){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 14
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 15
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_3 /* 0x33 */)) {
+ if(1){
+ r=m=1; // Line 21
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.SHIFT | modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4210 */, keyCodes.K_2 /* 0x32 */)) {
+ if(1){
+ r=m=1; // Line 17
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.SHIFT | modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4110 */, keyCodes.K_2 /* 0x32 */)) {
+ if(1){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_A /* 0x41 */)) {
+ if(1){
+ r=m=1; // Line 24
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_B /* 0x42 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_B /* 0x42 */)) {
+ if(1){
+ r=m=1; // Line 26
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_031___caps_lock.kmn b/common/test/keyboards/baseline/k_0700___caps_lock.kmn
similarity index 93%
rename from common/test/keyboards/baseline/k_031___caps_lock.kmn
rename to common/test/keyboards/baseline/k_0700___caps_lock.kmn
index d14b8ad56ba..27fc5b6b739 100644
--- a/common/test/keyboards/baseline/k_031___caps_lock.kmn
+++ b/common/test/keyboards/baseline/k_0700___caps_lock.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '031 - caps lock'
+store(&NAME) '0700 - caps lock'
c Description: Tests Caps Lock
c keys: [K_1][SHIFT K_2][SHIFT K_3][K_A][SHIFT K_B]
c expected: pass.pass.pass.pass.pass.
diff --git a/common/test/keyboards/baseline/k_0700___caps_lock.kmx b/common/test/keyboards/baseline/k_0700___caps_lock.kmx
new file mode 100644
index 00000000000..072520eb51e
Binary files /dev/null and b/common/test/keyboards/baseline/k_0700___caps_lock.kmx differ
diff --git a/common/test/keyboards/baseline/k_0701___caps_control.js b/common/test/keyboards/baseline/k_0701___caps_control.js
new file mode 100644
index 00000000000..0b4ee57e6d9
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0701___caps_control.js
@@ -0,0 +1,131 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0701___caps_control());
+}
+function Keyboard_k_0701___caps_control()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0701___caps_control";
+ this.KN="0701 - caps control";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=modCodes.SHIFT | modCodes.CAPS | modCodes.NO_CAPS /* 0x0310 */;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.SHIFT | modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4210 */, keyCodes.K_3 /* 0x33 */)) {
+ if(1){
+ r=m=1; // Line 24
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.SHIFT | modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4110 */, keyCodes.K_3 /* 0x33 */)) {
+ if(1){
+ r=m=1; // Line 25
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 17
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_2 /* 0x32 */)) {
+ if(1){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_2 /* 0x32 */)) {
+ if(1){
+ r=m=1; // Line 21
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_4 /* 0x34 */)) {
+ if(1){
+ r=m=1; // Line 27
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_4 /* 0x34 */)) {
+ if(1){
+ r=m=1; // Line 28
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_5 /* 0x35 */)) {
+ if(1){
+ r=m=1; // Line 30
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_5 /* 0x35 */)) {
+ if(1){
+ r=m=1; // Line 31
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_6 /* 0x36 */)) {
+ if(1){
+ r=m=1; // Line 33
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_6 /* 0x36 */)) {
+ if(1){
+ r=m=1; // Line 34
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_7 /* 0x37 */)) {
+ if(1){
+ r=m=1; // Line 37
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_7 /* 0x37 */)) {
+ if(1){
+ r=m=1; // Line 38
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_032___caps_control.kmn b/common/test/keyboards/baseline/k_0701___caps_control.kmn
similarity index 96%
rename from common/test/keyboards/baseline/k_032___caps_control.kmn
rename to common/test/keyboards/baseline/k_0701___caps_control.kmn
index 884fb41a6d5..13c9aa29a05 100644
--- a/common/test/keyboards/baseline/k_032___caps_control.kmn
+++ b/common/test/keyboards/baseline/k_0701___caps_control.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '032 - caps control'
+store(&NAME) '0701 - caps control'
c Description: Tests Caps Lock env set
c keys: [K_1][K_CAPS][K_2][SHIFT K_3][K_4][K_CAPS][K_5][K_CAPS][K_6][K_SHIFT][K_7]
c expected: pass.pass.pass.pass.pass.pass.pass.
diff --git a/common/test/keyboards/baseline/k_0701___caps_control.kmx b/common/test/keyboards/baseline/k_0701___caps_control.kmx
new file mode 100644
index 00000000000..c9f04435d20
Binary files /dev/null and b/common/test/keyboards/baseline/k_0701___caps_control.kmx differ
diff --git a/common/test/keyboards/baseline/k_0702___caps_always_off.js b/common/test/keyboards/baseline/k_0702___caps_always_off.js
new file mode 100644
index 00000000000..48455fb1a99
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0702___caps_always_off.js
@@ -0,0 +1,75 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0702___caps_always_off());
+}
+function Keyboard_k_0702___caps_always_off()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0702___caps_always_off";
+ this.KN="0702 - caps always off";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=modCodes.CAPS | modCodes.NO_CAPS /* 0x0300 */;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 16
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 17
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_2 /* 0x32 */)) {
+ if(1){
+ r=m=1; // Line 19
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_2 /* 0x32 */)) {
+ if(1){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_3 /* 0x33 */)) {
+ if(1){
+ r=m=1; // Line 22
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_3 /* 0x33 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_033___caps_always_off.kmn b/common/test/keyboards/baseline/k_0702___caps_always_off.kmn
similarity index 90%
rename from common/test/keyboards/baseline/k_033___caps_always_off.kmn
rename to common/test/keyboards/baseline/k_0702___caps_always_off.kmn
index 912f2c5ac0d..fb2099bbe9b 100644
--- a/common/test/keyboards/baseline/k_033___caps_always_off.kmn
+++ b/common/test/keyboards/baseline/k_0702___caps_always_off.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '033 - caps always off'
+store(&NAME) '0702 - caps always off'
c Description: Tests Caps Lock always off
c keys: [K_1][K_CAPS][K_2][K_CAPS][K_3]
c expected: pass.pass.pass.
diff --git a/common/test/keyboards/baseline/k_0702___caps_always_off.kmx b/common/test/keyboards/baseline/k_0702___caps_always_off.kmx
new file mode 100644
index 00000000000..f8d6c0c9df5
Binary files /dev/null and b/common/test/keyboards/baseline/k_0702___caps_always_off.kmx differ
diff --git a/common/test/keyboards/baseline/k_0703___caps_always_off_initially_on.js b/common/test/keyboards/baseline/k_0703___caps_always_off_initially_on.js
new file mode 100644
index 00000000000..a482844beca
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0703___caps_always_off_initially_on.js
@@ -0,0 +1,75 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0703___caps_always_off_initially_on());
+}
+function Keyboard_k_0703___caps_always_off_initially_on()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0703___caps_always_off_initially_on";
+ this.KN="0703 - caps always off initially on";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=modCodes.CAPS | modCodes.NO_CAPS /* 0x0300 */;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 17
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_1 /* 0x31 */)) {
+ if(1){
+ r=m=1; // Line 18
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_2 /* 0x32 */)) {
+ if(1){
+ r=m=1; // Line 20
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_2 /* 0x32 */)) {
+ if(1){
+ r=m=1; // Line 21
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ else if(k.KKM(e, modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4100 */, keyCodes.K_3 /* 0x33 */)) {
+ if(1){
+ r=m=1; // Line 23
+ k.KDC(0,t);
+ k.KO(-1,t,"fail.");
+ }
+ }
+ else if(k.KKM(e, modCodes.NO_CAPS | modCodes.VIRTUAL_KEY /* 0x4200 */, keyCodes.K_3 /* 0x33 */)) {
+ if(1){
+ r=m=1; // Line 24
+ k.KDC(0,t);
+ k.KO(-1,t,"pass.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmn b/common/test/keyboards/baseline/k_0703___caps_always_off_initially_on.kmn
similarity index 90%
rename from common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmn
rename to common/test/keyboards/baseline/k_0703___caps_always_off_initially_on.kmn
index 4c0b6913658..140a3167e00 100644
--- a/common/test/keyboards/baseline/k_047___caps_always_off_initially_on.kmn
+++ b/common/test/keyboards/baseline/k_0703___caps_always_off_initially_on.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '047 - caps always off initially on'
+store(&NAME) '0703 - caps always off initially on'
c Description: Tests Caps Lock always off, entry to the test has Caps Lock
c on to test that it is then switched off on first keystroke event.
c keys: [K_1][K_CAPS][K_2][K_CAPS][K_3]
diff --git a/common/test/keyboards/baseline/k_0703___caps_always_off_initially_on.kmx b/common/test/keyboards/baseline/k_0703___caps_always_off_initially_on.kmx
new file mode 100644
index 00000000000..bd9c41c9dc5
Binary files /dev/null and b/common/test/keyboards/baseline/k_0703___caps_always_off_initially_on.kmx differ
diff --git a/common/test/keyboards/baseline/k_0800___long_context.js b/common/test/keyboards/baseline/k_0800___long_context.js
new file mode 100644
index 00000000000..9870e97456e
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0800___long_context.js
@@ -0,0 +1,40 @@
+
+KeymanWeb.KR(new Keyboard_k_0800___long_context());
+
+function Keyboard_k_0800___long_context()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0800___long_context";
+ this.KN="0800 - long context";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x20)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KO(0,t," ");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x46)) {
+ if(k.KCM(1,t," ",1)){
+ r=m=1; // Line 16
+ k.KO(1,t," ");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_040___long_context.kmn b/common/test/keyboards/baseline/k_0800___long_context.kmn
similarity index 91%
rename from common/test/keyboards/baseline/k_040___long_context.kmn
rename to common/test/keyboards/baseline/k_0800___long_context.kmn
index f570d257108..b14373ae3d0 100644
--- a/common/test/keyboards/baseline/k_040___long_context.kmn
+++ b/common/test/keyboards/baseline/k_0800___long_context.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '040 - long context'
+store(&NAME) '0800 - long context'
c Description: Tests context longer than 64 chars
c keys: [K_SPACE][K_F]
c expected: 012345678901234567890123456789012345678901234567890123456789012\u0020
diff --git a/common/test/keyboards/baseline/k_0800___long_context.kmx b/common/test/keyboards/baseline/k_0800___long_context.kmx
new file mode 100644
index 00000000000..7ff3b9dc82e
Binary files /dev/null and b/common/test/keyboards/baseline/k_0800___long_context.kmx differ
diff --git a/common/test/keyboards/baseline/k_0801___long_context_and_deadkeys.js b/common/test/keyboards/baseline/k_0801___long_context_and_deadkeys.js
new file mode 100644
index 00000000000..01f5f0f0d20
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0801___long_context_and_deadkeys.js
@@ -0,0 +1,75 @@
+
+KeymanWeb.KR(new Keyboard_k_0801___long_context_and_deadkeys());
+
+function Keyboard_k_0801___long_context_and_deadkeys()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0801___long_context_and_deadkeys";
+ this.KN="0801 - long context and deadkeys";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x31)) {
+ if(k.KDM(60,t,0)&&k.KCM(60,t,"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgh",60)){
+ r=m=1; // Line 18
+ k.KO(-1,t,"1");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x32)) {
+ if(k.KCM(63,t,"\x08\x01abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgh1",63)){
+ r=m=1; // Line 22
+ k.KO(63,t,"FAIL TEST 2");
+ }
+ else if(k.KCM(62,t,"\x01abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgh1",62)){
+ r=m=1; // Line 23
+ k.KO(62,t,"FAIL TEST 2");
+ }
+ else if(k.KCM(61,t,"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgh1",61)){
+ r=m=1; // Line 24
+ k.KO(-1,t,"2");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x33)) {
+ if(k.KCM(64,t,"\x08\x01abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgh12",64)){
+ r=m=1; // Line 28
+ k.KO(64,t,"FAIL TEST 3");
+ }
+ else if(k.KCM(63,t,"\x01abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgh12",63)){
+ r=m=1; // Line 29
+ k.KO(63,t,"FAIL TEST 3");
+ }
+ else if(k.KCM(62,t,"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgh12",62)){
+ r=m=1; // Line 30
+ k.KO(62,t,"abcdefghijklmnopqrstuvwxyz123");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x34)) {
+ if(k.KDM(29,t,0)&&k.KCM(29,t,"abcdefghijklmnopqrstuvwxyz123",29)){
+ r=m=1; // Line 33
+ k.KO(29,t,"PASS");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x5A)) {
+ if(1){
+ r=m=1; // Line 15
+ k.KDO(0,t,0);
+ k.KO(-1,t,"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgh");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmn b/common/test/keyboards/baseline/k_0801___long_context_and_deadkeys.kmn
similarity index 96%
rename from common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmn
rename to common/test/keyboards/baseline/k_0801___long_context_and_deadkeys.kmn
index 455fa1c41cb..29fea905903 100644
--- a/common/test/keyboards/baseline/k_041___long_context_and_deadkeys.kmn
+++ b/common/test/keyboards/baseline/k_0801___long_context_and_deadkeys.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '041 - long context and deadkeys'
+store(&NAME) '0801 - long context and deadkeys'
c Description: Tests that we don't split a dk in context:set. Note that we start with a context
c with length MAXCONTEXT-1 (abc...fgh is 60 characters/bytes long + 3 bytes for dk(1)).
c keys: [K_Z][K_1][K_2][K_3][K_4]
diff --git a/common/test/keyboards/baseline/k_0801___long_context_and_deadkeys.kmx b/common/test/keyboards/baseline/k_0801___long_context_and_deadkeys.kmx
new file mode 100644
index 00000000000..ff8c7a55ba2
Binary files /dev/null and b/common/test/keyboards/baseline/k_0801___long_context_and_deadkeys.kmx differ
diff --git a/common/test/keyboards/baseline/k_0802___long_context_and_split_deadkeys.js b/common/test/keyboards/baseline/k_0802___long_context_and_split_deadkeys.js
new file mode 100644
index 00000000000..467eb874724
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0802___long_context_and_split_deadkeys.js
@@ -0,0 +1,57 @@
+
+KeymanWeb.KR(new Keyboard_k_0802___long_context_and_split_deadkeys());
+
+function Keyboard_k_0802___long_context_and_split_deadkeys()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0802___long_context_and_split_deadkeys";
+ this.KN="0802 - long context and split deadkeys";
+ this.KMINVER="6.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x20)) {
+ if(k.KCM(64,t,"\x01abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk",64)){
+ r=m=1; // Line 18
+ k.KO(64,t,"FAIL");
+ }
+ else if(k.KDM(63,t,0)&&k.KCM(63,t,"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk",63)){
+ r=m=1; // Line 19
+ k.KO(63,t,"UNEXPECTED");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x31)) {
+ if(k.KCM(61,t,"defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk ",61)){
+ r=m=1; // Line 22
+ k.KO(61,t,"def");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x32)) {
+ if(k.KDM(6,t,0)&&k.KCM(6,t,"abcdef",6)){
+ r=m=1; // Line 23
+ k.KO(6,t,"PASS");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x5A)) {
+ if(1){
+ r=m=1; // Line 14
+ k.KDO(0,t,0);
+ k.KO(-1,t,"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmn b/common/test/keyboards/baseline/k_0802___long_context_and_split_deadkeys.kmn
similarity index 93%
rename from common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmn
rename to common/test/keyboards/baseline/k_0802___long_context_and_split_deadkeys.kmn
index ce31d18e282..a6d883886a1 100644
--- a/common/test/keyboards/baseline/k_042___long_context_and_split_deadkeys.kmn
+++ b/common/test/keyboards/baseline/k_0802___long_context_and_split_deadkeys.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '042 - long context and split deadkeys'
+store(&NAME) '0802 - long context and split deadkeys'
c Description: Tests that we don't split a dk in context. Note that we start with a context with length MAXCONTEXT.
c keys: [K_Z][K_SPACE][K_1][K_2]
c context:
diff --git a/common/test/keyboards/baseline/k_0802___long_context_and_split_deadkeys.kmx b/common/test/keyboards/baseline/k_0802___long_context_and_split_deadkeys.kmx
new file mode 100644
index 00000000000..f26e1e43866
Binary files /dev/null and b/common/test/keyboards/baseline/k_0802___long_context_and_split_deadkeys.kmx differ
diff --git a/common/test/keyboards/baseline/k_0803___if_and_context.js b/common/test/keyboards/baseline/k_0803___if_and_context.js
new file mode 100644
index 00000000000..68ac3f6dcfc
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0803___if_and_context.js
@@ -0,0 +1,51 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0803___if_and_context());
+}
+function Keyboard_k_0803___if_and_context()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0803___if_and_context";
+ this.KN="0803 - if and context";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_nfc_5=KeymanWeb.KLOAD(this.KI,"nfc","0");
+ this.s_diaeresisBase_6="ae";
+ this.s8="0";
+ this.KVS=['s_nfc_5'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(1){
+ r=m=1; // Line 16
+ k.KDC(0,t);
+ k.KO(-1,t,"exay");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_B /* 0x42 */)) {
+ if(k.KFCM(4,t,[{t:'a',a:this.s_diaeresisBase_6},'x',{t:'a',a:this.s_diaeresisBase_6},'y'])&&this.s_nfc_5===this.s8){
+ r=m=1; // Line 20
+ k.KDC(4,t);
+ k.KIO(-1,this.s_diaeresisBase_6,1,t);
+ k.KO(-1,t,"x");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_044___if_and_context.kmn b/common/test/keyboards/baseline/k_0803___if_and_context.kmn
similarity index 91%
rename from common/test/keyboards/baseline/k_044___if_and_context.kmn
rename to common/test/keyboards/baseline/k_0803___if_and_context.kmn
index 352490325cc..e19a96c17a1 100644
--- a/common/test/keyboards/baseline/k_044___if_and_context.kmn
+++ b/common/test/keyboards/baseline/k_0803___if_and_context.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '044 - if and context'
+store(&NAME) '0803 - if and context'
c Description: Tests that context(x) takes if() statements into account. See #4275.
c keys: [K_A][K_B]
c expected: ex
diff --git a/common/test/keyboards/baseline/k_0803___if_and_context.kmx b/common/test/keyboards/baseline/k_0803___if_and_context.kmx
new file mode 100644
index 00000000000..17e648142b5
Binary files /dev/null and b/common/test/keyboards/baseline/k_0803___if_and_context.kmx differ
diff --git a/common/test/keyboards/baseline/k_0804___deadkey_and_context.js b/common/test/keyboards/baseline/k_0804___deadkey_and_context.js
new file mode 100644
index 00000000000..0b233bdc24d
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0804___deadkey_and_context.js
@@ -0,0 +1,64 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0804___deadkey_and_context());
+}
+function Keyboard_k_0804___deadkey_and_context()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0804___deadkey_and_context";
+ this.KN="0804 - deadkey and context";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=modCodes.SHIFT /* 0x0010 */;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_SLASH /* 0xBF */)) {
+ if(k.KFCM(7,t,['<',{t:'d',d:0},'a',{t:'d',d:1},'b',{t:'d',d:2},'>'])){
+ r=m=1; // Line 18
+ k.KDC(7,t);
+ k.KO(-1,t,"correct");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_Y /* 0x59 */)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KDC(0,t);
+ k.KDO(-1,t,0);
+ k.KO(-1,t,"a");
+ k.KDO(-1,t,1);
+ k.KO(-1,t,"b");
+ k.KDO(-1,t,2);
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_Z /* 0x5A */)) {
+ if(k.KFCM(5,t,[{t:'d',d:0},'a',{t:'d',d:1},'b',{t:'d',d:2}])){
+ r=m=1; // Line 17
+ k.KDC(5,t);
+ k.KO(-1,t,"<");
+ k.KDO(-1,t,0);
+ k.KO(-1,t,"a");
+ k.KDO(-1,t,1);
+ k.KO(-1,t,"b");
+ k.KDO(-1,t,2);
+ k.KO(-1,t,">");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_045___deadkey_and_context.kmn b/common/test/keyboards/baseline/k_0804___deadkey_and_context.kmn
similarity index 90%
rename from common/test/keyboards/baseline/k_045___deadkey_and_context.kmn
rename to common/test/keyboards/baseline/k_0804___deadkey_and_context.kmn
index c79953a05b8..3c6522d4704 100644
--- a/common/test/keyboards/baseline/k_045___deadkey_and_context.kmn
+++ b/common/test/keyboards/baseline/k_0804___deadkey_and_context.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '045 - deadkey and context'
+store(&NAME) '0804 - deadkey and context'
c Description: Tests that context emits deadkeys correctly. See #4275.
c keys: [K_Y][K_Z][SHIFT K_SLASH]
c expected: correct
diff --git a/common/test/keyboards/baseline/k_0804___deadkey_and_context.kmx b/common/test/keyboards/baseline/k_0804___deadkey_and_context.kmx
new file mode 100644
index 00000000000..c0a8199fa5d
Binary files /dev/null and b/common/test/keyboards/baseline/k_0804___deadkey_and_context.kmx differ
diff --git a/common/test/keyboards/baseline/k_0805___deadkey_and_contextex.js b/common/test/keyboards/baseline/k_0805___deadkey_and_contextex.js
new file mode 100644
index 00000000000..b58f22a2230
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0805___deadkey_and_contextex.js
@@ -0,0 +1,60 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0805___deadkey_and_contextex());
+}
+function Keyboard_k_0805___deadkey_and_contextex()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0805___deadkey_and_contextex";
+ this.KN="0805 - deadkey and contextex";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=modCodes.SHIFT /* 0x0010 */;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_SLASH /* 0xBF */)) {
+ if(k.KFCM(3,t,['<',{t:'d',d:0},'>'])){
+ r=m=1; // Line 18
+ k.KDC(3,t);
+ k.KO(-1,t,"correct");
+ }
+ }
+ else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_M /* 0x4D */)) {
+ if(k.KFCM(5,t,[{t:'d',d:0},'a',{t:'d',d:1},'b',{t:'d',d:2}])){
+ r=m=1; // Line 17
+ k.KDC(5,t);
+ k.KO(-1,t,"<");
+ k.KDO(-1,t,0);
+ k.KO(-1,t,">");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_Y /* 0x59 */)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KDC(0,t);
+ k.KDO(-1,t,0);
+ k.KO(-1,t,"a");
+ k.KDO(-1,t,1);
+ k.KO(-1,t,"b");
+ k.KDO(-1,t,2);
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmn b/common/test/keyboards/baseline/k_0805___deadkey_and_contextex.kmn
similarity index 89%
rename from common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmn
rename to common/test/keyboards/baseline/k_0805___deadkey_and_contextex.kmn
index 48ad796c3e5..f120a982520 100644
--- a/common/test/keyboards/baseline/k_046___deadkey_and_contextex.kmn
+++ b/common/test/keyboards/baseline/k_0805___deadkey_and_contextex.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '046 - deadkey and contextex'
+store(&NAME) '0805 - deadkey and contextex'
c Description: Tests that context() emits deadkeys correctly. See #4275.
c keys: [K_Y][SHIFT K_M][SHIFT K_SLASH]
c expected: correct
diff --git a/common/test/keyboards/baseline/k_0805___deadkey_and_contextex.kmx b/common/test/keyboards/baseline/k_0805___deadkey_and_contextex.kmx
new file mode 100644
index 00000000000..73811a388ad
Binary files /dev/null and b/common/test/keyboards/baseline/k_0805___deadkey_and_contextex.kmx differ
diff --git a/common/test/keyboards/baseline/k_0806___modifier_keys_keep_context.js b/common/test/keyboards/baseline/k_0806___modifier_keys_keep_context.js
new file mode 100644
index 00000000000..afb8164deec
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0806___modifier_keys_keep_context.js
@@ -0,0 +1,40 @@
+
+KeymanWeb.KR(new Keyboard_k_0806___modifier_keys_keep_context());
+
+function Keyboard_k_0806___modifier_keys_keep_context()
+{
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0806___modifier_keys_keep_context";
+ this.KN="0806 - modifier keys keep context";
+ this.KMINVER="9.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0x0000;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_main_0(t,e);
+ };
+ this.g_main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, 0x4000, 0x41)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KO(0,t,"a");
+ }
+ }
+ else if(k.KKM(e, 0x4000, 0x42)) {
+ if(k.KCM(1,t,"a",1)){
+ r=m=1; // Line 15
+ k.KO(1,t,"pass.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmn b/common/test/keyboards/baseline/k_0806___modifier_keys_keep_context.kmn
similarity index 85%
rename from common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmn
rename to common/test/keyboards/baseline/k_0806___modifier_keys_keep_context.kmn
index bb0cfc4df5d..97cce24a6bc 100644
--- a/common/test/keyboards/baseline/k_048___modifier_keys_keep_context.kmn
+++ b/common/test/keyboards/baseline/k_0806___modifier_keys_keep_context.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '048 - modifier keys keep context'
+store(&NAME) '0806 - modifier keys keep context'
c Description: Tests that modifier keys don't reset context (#5591)
c keys: [K_A][K_SHIFT][K_B][K_A][K_ALT][K_B][K_A][K_CAPS][K_B]
c expected: pass.pass.pass.
diff --git a/common/test/keyboards/baseline/k_0806___modifier_keys_keep_context.kmx b/common/test/keyboards/baseline/k_0806___modifier_keys_keep_context.kmx
new file mode 100644
index 00000000000..fcf3ca65c5b
Binary files /dev/null and b/common/test/keyboards/baseline/k_0806___modifier_keys_keep_context.kmx differ
diff --git a/common/test/keyboards/baseline/k_0807___enter_invalidates_context.js b/common/test/keyboards/baseline/k_0807___enter_invalidates_context.js
new file mode 100644
index 00000000000..90948d0648e
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0807___enter_invalidates_context.js
@@ -0,0 +1,40 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0807___enter_invalidates_context());
+}
+function Keyboard_k_0807___enter_invalidates_context()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0807___enter_invalidates_context";
+ this.KN="0807 - enter_invalidates_context";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_C /* 0x43 */)) {
+ if(k.KFCM(2,t,['a','b'])){
+ r=m=1; // Line 17
+ k.KDC(2,t);
+ k.KO(-1,t,"abd");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_049___enter_invalidates_context.kmn b/common/test/keyboards/baseline/k_0807___enter_invalidates_context.kmn
similarity index 89%
rename from common/test/keyboards/baseline/k_049___enter_invalidates_context.kmn
rename to common/test/keyboards/baseline/k_0807___enter_invalidates_context.kmn
index 78363865ef3..c66f2fccf03 100644
--- a/common/test/keyboards/baseline/k_049___enter_invalidates_context.kmn
+++ b/common/test/keyboards/baseline/k_0807___enter_invalidates_context.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '049 - enter_invalidates_context'
+store(&NAME) '0807 - enter_invalidates_context'
c Description: Tests that core context is cleared when kb processor
c determines its invalid. eg Enter key See #10182.
c keys: [K_A][K_B][K_ENTER][K_C]
diff --git a/common/test/keyboards/baseline/k_0807___enter_invalidates_context.kmx b/common/test/keyboards/baseline/k_0807___enter_invalidates_context.kmx
new file mode 100644
index 00000000000..863379d98c8
Binary files /dev/null and b/common/test/keyboards/baseline/k_0807___enter_invalidates_context.kmx differ
diff --git a/common/test/keyboards/baseline/k_0808___nul_and_context.js b/common/test/keyboards/baseline/k_0808___nul_and_context.js
new file mode 100644
index 00000000000..cca0be5e355
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0808___nul_and_context.js
@@ -0,0 +1,43 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0808___nul_and_context());
+}
+function Keyboard_k_0808___nul_and_context()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0808___nul_and_context";
+ this.KN="0808 - nul_and_context";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_cons_4="mnpqrstv";
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_B /* 0x42 */)) {
+ if(k.KFCM(2,t,[{t:'n'},{t:'a',a:this.s_cons_4}])){
+ r=m=1; // Line 15
+ k.KDC(1,t);
+ k.KO(-1,t,"2");
+ k.KIO(-1,this.s_cons_4,1,t);
+ k.KO(-1,t,"3");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_050___nul_and_context.kmn b/common/test/keyboards/baseline/k_0808___nul_and_context.kmn
similarity index 87%
rename from common/test/keyboards/baseline/k_050___nul_and_context.kmn
rename to common/test/keyboards/baseline/k_0808___nul_and_context.kmn
index 206777b7f06..fabac4155c6 100644
--- a/common/test/keyboards/baseline/k_050___nul_and_context.kmn
+++ b/common/test/keyboards/baseline/k_0808___nul_and_context.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '050 - nul_and_context'
+store(&NAME) '0808 - nul_and_context'
c Description: Tests that context has the right offset when used with nul
c keys: [K_T][K_B]
c expected: 2t3
diff --git a/common/test/keyboards/baseline/k_0808___nul_and_context.kmx b/common/test/keyboards/baseline/k_0808___nul_and_context.kmx
new file mode 100644
index 00000000000..2304b23fe76
Binary files /dev/null and b/common/test/keyboards/baseline/k_0808___nul_and_context.kmx differ
diff --git a/common/test/keyboards/baseline/k_0809___if_and_context.js b/common/test/keyboards/baseline/k_0809___if_and_context.js
new file mode 100644
index 00000000000..a2e3ecd0356
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0809___if_and_context.js
@@ -0,0 +1,45 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0809___if_and_context());
+}
+function Keyboard_k_0809___if_and_context()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0809___if_and_context";
+ this.KN="0809 - if_and_context";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_cons_4="mnpqrstv";
+ this.s_ifx_5=KeymanWeb.KLOAD(this.KI,"ifx","1");
+ this.s8="1";
+ this.KVS=['s_ifx_5'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_D /* 0x44 */)) {
+ if(k.KFCM(1,t,[{t:'a',a:this.s_cons_4}])&&this.s_ifx_5===this.s8){
+ r=m=1; // Line 16
+ k.KDC(1,t);
+ k.KO(-1,t,"4");
+ k.KIO(-1,this.s_cons_4,1,t);
+ k.KO(-1,t,"5");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_051___if_and_context.kmn b/common/test/keyboards/baseline/k_0809___if_and_context.kmn
similarity index 88%
rename from common/test/keyboards/baseline/k_051___if_and_context.kmn
rename to common/test/keyboards/baseline/k_0809___if_and_context.kmn
index 7a6a3e3f63f..29c7c48ddb8 100644
--- a/common/test/keyboards/baseline/k_051___if_and_context.kmn
+++ b/common/test/keyboards/baseline/k_0809___if_and_context.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '051 - if_and_context'
+store(&NAME) '0809 - if_and_context'
c Description: Tests that context has the right offset when used with if
c keys: [K_T][K_D]
c expected: 4t5
diff --git a/common/test/keyboards/baseline/k_0809___if_and_context.kmx b/common/test/keyboards/baseline/k_0809___if_and_context.kmx
new file mode 100644
index 00000000000..ac1e467c9c2
Binary files /dev/null and b/common/test/keyboards/baseline/k_0809___if_and_context.kmx differ
diff --git a/common/test/keyboards/baseline/k_0810___nul_and_index.js b/common/test/keyboards/baseline/k_0810___nul_and_index.js
new file mode 100644
index 00000000000..1f86fe5ac04
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0810___nul_and_index.js
@@ -0,0 +1,44 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0810___nul_and_index());
+}
+function Keyboard_k_0810___nul_and_index()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0810___nul_and_index";
+ this.KN="0810 - nul_and_index";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_cons_4="mnpqrstv";
+ this.s_outs_5="MNPQRSTV";
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_B /* 0x42 */)) {
+ if(k.KFCM(2,t,[{t:'n'},{t:'a',a:this.s_cons_4}])){
+ r=m=1; // Line 16
+ k.KDC(1,t);
+ k.KO(-1,t,"2");
+ k.KIO(-1,this.s_outs_5,1,t);
+ k.KO(-1,t,"3");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_052___nul_and_index.kmn b/common/test/keyboards/baseline/k_0810___nul_and_index.kmn
similarity index 88%
rename from common/test/keyboards/baseline/k_052___nul_and_index.kmn
rename to common/test/keyboards/baseline/k_0810___nul_and_index.kmn
index c56c411c076..91940d089c8 100644
--- a/common/test/keyboards/baseline/k_052___nul_and_index.kmn
+++ b/common/test/keyboards/baseline/k_0810___nul_and_index.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '052 - nul_and_index'
+store(&NAME) '0810 - nul_and_index'
c Description: Tests that context has the right offset when used with nul
c keys: [K_T][K_B]
c expected: 2T3
diff --git a/common/test/keyboards/baseline/k_0810___nul_and_index.kmx b/common/test/keyboards/baseline/k_0810___nul_and_index.kmx
new file mode 100644
index 00000000000..ea1a20064e4
Binary files /dev/null and b/common/test/keyboards/baseline/k_0810___nul_and_index.kmx differ
diff --git a/common/test/keyboards/baseline/k_0811___if_and_index.js b/common/test/keyboards/baseline/k_0811___if_and_index.js
new file mode 100644
index 00000000000..68205a31e72
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0811___if_and_index.js
@@ -0,0 +1,46 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0811___if_and_index());
+}
+function Keyboard_k_0811___if_and_index()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0811___if_and_index";
+ this.KN="0811 - if_and_index";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.s_cons_4="mnpqrstv";
+ this.s_outs_5="MNPQRSTV";
+ this.s_ifx_6=KeymanWeb.KLOAD(this.KI,"ifx","1");
+ this.s9="1";
+ this.KVS=['s_ifx_6'];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_D /* 0x44 */)) {
+ if(k.KFCM(1,t,[{t:'a',a:this.s_cons_4}])&&this.s_ifx_6===this.s9){
+ r=m=1; // Line 17
+ k.KDC(1,t);
+ k.KO(-1,t,"4");
+ k.KIO(-1,this.s_outs_5,1,t);
+ k.KO(-1,t,"5");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_053___if_and_index.kmn b/common/test/keyboards/baseline/k_0811___if_and_index.kmn
similarity index 89%
rename from common/test/keyboards/baseline/k_053___if_and_index.kmn
rename to common/test/keyboards/baseline/k_0811___if_and_index.kmn
index 86479a8e88e..b019ffcc2b2 100644
--- a/common/test/keyboards/baseline/k_053___if_and_index.kmn
+++ b/common/test/keyboards/baseline/k_0811___if_and_index.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '053 - if_and_index'
+store(&NAME) '0811 - if_and_index'
c Description: Tests that index() has the right offset when used with if
c keys: [K_T][K_D]
c expected: 4T5
diff --git a/common/test/keyboards/baseline/k_0811___if_and_index.kmx b/common/test/keyboards/baseline/k_0811___if_and_index.kmx
new file mode 100644
index 00000000000..3313e890f9c
Binary files /dev/null and b/common/test/keyboards/baseline/k_0811___if_and_index.kmx differ
diff --git a/common/test/keyboards/baseline/k_054___nul_and_contextex.kmn b/common/test/keyboards/baseline/k_0812___nul_and_contextex.kmn
similarity index 88%
rename from common/test/keyboards/baseline/k_054___nul_and_contextex.kmn
rename to common/test/keyboards/baseline/k_0812___nul_and_contextex.kmn
index 39ef0d4534d..ae57598e1c9 100644
--- a/common/test/keyboards/baseline/k_054___nul_and_contextex.kmn
+++ b/common/test/keyboards/baseline/k_0812___nul_and_contextex.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '054 - nul_and_contextex'
+store(&NAME) '0812 - nul_and_contextex'
c Description: Tests that context in context part of rule has the right offset when used with nul
c keys: [K_T][K_T][K_B]
c expected: 2tt3
diff --git a/common/test/keyboards/baseline/k_0812___nul_and_contextex.kmx b/common/test/keyboards/baseline/k_0812___nul_and_contextex.kmx
new file mode 100644
index 00000000000..ae1dd8d8f84
Binary files /dev/null and b/common/test/keyboards/baseline/k_0812___nul_and_contextex.kmx differ
diff --git a/common/test/keyboards/baseline/k_0813___deadkey_cancelled_by_arrow.js b/common/test/keyboards/baseline/k_0813___deadkey_cancelled_by_arrow.js
new file mode 100644
index 00000000000..3991bda76c8
--- /dev/null
+++ b/common/test/keyboards/baseline/k_0813___deadkey_cancelled_by_arrow.js
@@ -0,0 +1,52 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_k_0813___deadkey_cancelled_by_arrow());
+}
+function Keyboard_k_0813___deadkey_cancelled_by_arrow()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_k_0813___deadkey_cancelled_by_arrow";
+ this.KN="0813 - deadkey cancelled by arrow";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=modCodes.SHIFT /* 0x0010 */;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_6 /* 0x36 */)) {
+ if(1){
+ r=m=1; // Line 13
+ k.KDC(0,t);
+ k.KDO(-1,t,0);
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_E /* 0x45 */)) {
+ if(k.KFCM(1,t,[{t:'d',d:0}])){
+ r=m=1; // Line 14
+ k.KDC(1,t);
+ k.KO(-1,t,"FAIL.");
+ }
+ else if(1){
+ r=m=1; // Line 15
+ k.KDC(0,t);
+ k.KO(-1,t,"PASS.");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/keyboards/baseline/k_055___deadkey_cancelled_by_arrow.kmn b/common/test/keyboards/baseline/k_0813___deadkey_cancelled_by_arrow.kmn
similarity index 88%
rename from common/test/keyboards/baseline/k_055___deadkey_cancelled_by_arrow.kmn
rename to common/test/keyboards/baseline/k_0813___deadkey_cancelled_by_arrow.kmn
index b84e9a4ab72..f52c747f362 100644
--- a/common/test/keyboards/baseline/k_055___deadkey_cancelled_by_arrow.kmn
+++ b/common/test/keyboards/baseline/k_0813___deadkey_cancelled_by_arrow.kmn
@@ -1,4 +1,4 @@
-store(&NAME) '050 - deadkey cancelled by arrow'
+store(&NAME) '0813 - deadkey cancelled by arrow'
c Description: Tests that context gets reset if we encounter a right-arrow
c after a deadkey (#12968)
c keys: [SHIFT K_6][K_RIGHT][K_E][SHIFT K_6][K_LEFT][K_E][SHIFT K_6][K_UP][K_E][SHIFT K_6][K_DOWN][K_E]
diff --git a/common/test/keyboards/baseline/k_0813___deadkey_cancelled_by_arrow.kmx b/common/test/keyboards/baseline/k_0813___deadkey_cancelled_by_arrow.kmx
new file mode 100644
index 00000000000..526c9300b56
Binary files /dev/null and b/common/test/keyboards/baseline/k_0813___deadkey_cancelled_by_arrow.kmx differ
diff --git a/common/test/keyboards/embed_osk/.gitignore b/common/test/keyboards/embed_osk/.gitignore
new file mode 100644
index 00000000000..d16386367f7
--- /dev/null
+++ b/common/test/keyboards/embed_osk/.gitignore
@@ -0,0 +1 @@
+build/
\ No newline at end of file
diff --git a/common/test/keyboards/embed_osk/embed_osk.kpj b/common/test/keyboards/embed_osk/embed_osk.kpj
new file mode 100644
index 00000000000..2b5513fc525
--- /dev/null
+++ b/common/test/keyboards/embed_osk/embed_osk.kpj
@@ -0,0 +1,12 @@
+
+
+
+ $PROJECTPATH\build
+ $PROJECTPATH\source
+ True
+ True
+ False
+ keyboard
+ 2.0
+
+
diff --git a/common/test/keyboards/embed_osk/source/embed_osk.kps b/common/test/keyboards/embed_osk/source/embed_osk.kps
new file mode 100644
index 00000000000..5ae723b1327
--- /dev/null
+++ b/common/test/keyboards/embed_osk/source/embed_osk.kps
@@ -0,0 +1,33 @@
+
+
+
+ 19.0.0.0
+ 7.0
+
+
+
+
+
+
+ test_v19_kmxplus
+ test_v19_kmxplus
+
+
+
+ ..\build\test_v19_kmxplus.kmx
+ 0
+ .kmx
+
+
+
+
+ test_v19_kmxplus
+ test_v19_kmxplus
+ 1.0
+
+ English
+
+
+
+
+
diff --git a/common/test/keyboards/embed_osk/source/test_v19_kmxplus.kmn b/common/test/keyboards/embed_osk/source/test_v19_kmxplus.kmn
new file mode 100644
index 00000000000..7a4f6cad131
--- /dev/null
+++ b/common/test/keyboards/embed_osk/source/test_v19_kmxplus.kmn
@@ -0,0 +1,7 @@
+store(&VERSION) '19.0'
+store(&NAME) 'Test KMX+ space reserved in header'
+store(&TARGETS) 'desktop'
+
+begin Unicode > use(main)
+
+group(main) using keys
diff --git a/developer/src/kmc-ldml/test/fixtures/basic.txt b/common/test/keyboards/kmx-plus/basic-17.txt
similarity index 95%
rename from developer/src/kmc-ldml/test/fixtures/basic.txt
rename to common/test/keyboards/kmx-plus/basic-17.txt
index 81748b400e3..99159142b77 100644
--- a/developer/src/kmc-ldml/test/fixtures/basic.txt
+++ b/common/test/keyboards/kmx-plus/basic-17.txt
@@ -1,25 +1,12 @@
#
# Keyman is copyright (C) SIL International. MIT License.
#
-# basic.txt describes the expected output of running kmc against basic.xml. It is used in
-# the end-to-end test test-compiler-e2e.ts.
+# basic-17.txt describes the expected output of running kmc against basic.xml
+# with v17 schema and v17 target version. It is used in the end-to-end test
+# test-compiler-e2e.ts.
#
-# Any changes to the compiler or basic.xml will likely result in changes to the compiled file.
-# While structural differences should be updated manually in this file to ensure that we are
-# getting the expected result for the e2e test, the checksum can be safely retrieved from the
-# updated compilation result. The following may be helpful for working with this file when
-# updating the binary format:
+# See README.md for more information.
#
-# cd developer/src/kmc
-# ./build.sh configure build # if needed
-# ./build.sh build-fixtures
-#
-# This will compile both the .xml and the .txt to build/test/fixtures and also emit the
-# checksum for basic-xml.kmx so you can patch that into this file.
-#
-# For the format of this file, see “hextobin.ts”
-#
-# P.S. surprisingly, the Dart language highlighter in VSCode does a helpful job on this file.
block(kmxheader) # struct COMP_KEYBOARD {
4b 58 54 53 # KMX_DWORD dwIdentifier; // 0000 Keyman compiled keyboard id
@@ -96,7 +83,7 @@ block(store_vk_path_string)
62 00 61 00 73 00 69 00 63 00 2d 00 78 00 6d 00 6c 00 2e 00 6b 00 76 00 6b 00 00 00 # 'basic-xml.kvk'
block(sect) # struct COMP_KMXPLUS_SECT {
- 73 65 63 74 # KMX_DWORD header.ident; // 0000 Section name
+ 73 65 63 74 # KMX_DWORD header.ident; // 0000 Section name 'sect'
diff(sect,endsect) # KMX_DWORD header.size; // 0004 Section length
diff(sect,eof) # KMX_DWORD total; // 0008 KMXPlus entire length
sizeof(sectitems,8) # KMX_DWORD count; // 000C number of section headers
@@ -167,6 +154,7 @@ block(bksp)
index(strNull,strNull,2) # KMX_DWORD str mapFrom 0
index(strNull,strNull,2) # KMX_DWORD str mapTo 0
+block(endbksp)
# ----------------------------------------------------------------------------------------------------
# disp
@@ -238,12 +226,12 @@ block(elemOrdrFrom)
00 00 00 00 # TODO-LDML: uset #0 # KMX_DWORD element; [uset] // str: output string or UTF-32LE codepoint
02 00 37 00 # KMX_DWORD flags; // flag and order values - unicodeset
45 1A 00 00 # KMX_DWORD element; 'ᩅ' // str: output string or UTF-32LE codepoint
- 00 00 0A 00 # KMX_DWORD flags; // flag and order values - unicodeset
+ 00 00 0A 00 # KMX_DWORD flags; // flag and order values - cp
block(elemOrdrBefore)
# before="\u{1A6B}"
6b 1a 00 00 # KMX_DWORD element; 'ᩫ' // str: output string or UTF-32LE codepoint
- 00 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset
+ 00 00 00 00 # KMX_DWORD flags; // flag and order values - cp
block(endelem)
@@ -393,6 +381,8 @@ block(keys) # struct COMP_KMXPLUS_KEYS {
dd 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
de 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+block(endkeys)
+
# ----------------------------------------------------------------------------------------------------
# layr
# ----------------------------------------------------------------------------------------------------
@@ -427,7 +417,7 @@ block(layr) # struct COMP_KMXPLUS_LAYR {
# TODO-LDML: lots of comment-out ahead. Need to revisit.
-block(list) # struct COMP_KMXPLUS_LAYR_LIST {
+block(list) # struct COMP_KMXPLUS_LIST {
6c 69 73 74 # KMX_DWORD header.ident; // 0000 Section name - list
diff(list,endList) # KMX_DWORD header.size; // 0004 Section length
03 00 00 00 # KMX_DWORD listCount (should be 2)
@@ -572,10 +562,10 @@ block(endstrs) # end of strs block
block(tran) # struct COMP_KMXPLUS_TRAN {
74 72 61 6e # KMX_DWORD header.ident; // 0000 Section name - tran
- diff(tran,tranEnd) # KMX_DWORD header.size; // 0004 Section length
+ diff(tran,endtran) # KMX_DWORD header.size; // 0004 Section length
diff(tranGroupStart,tranTransformStart,12) # KMX_DWORD groupCount;
diff(tranTransformStart,tranReorderStart,16) # KMX_DWORD transformCount;
- diff(tranReorderStart,tranEnd,8) # KMX_DWORD reorderCount;
+ diff(tranReorderStart,endtran,8) # KMX_DWORD reorderCount;
block(tranGroupStart) # COMP_KMXPLUS_TRAN_GROUP
# group 0
@@ -619,11 +609,11 @@ block(tran) # struct COMP_KMXPLUS_TRAN {
index(elemNull,elemOrdrFrom) # KMXPLUS_ELEM elements;
index(elemNull,elemOrdrBefore) # KMXPLUS_ELEM before;
-block(tranEnd)
+block(endtran)
block(uset)
75 73 65 74 # KMX_DWORD header.ident; // 0000 Section name - uset
- diff(uset,usetEnd) # KMX_DWORD header.size; // 0004 Section length
+ diff(uset,enduset) # KMX_DWORD header.size; // 0004 Section length
01 00 00 00 # lists
01 00 00 00 # elements
# lists
@@ -634,17 +624,17 @@ block(uset)
# range @0
75 1A 00 00 # start
79 1A 00 00 # end
- block(usetEnd)
+ block(enduset)
block(vars) # struct COMP_KMXPLUS_VARS {
76 61 72 73 # KMX_DWORD header.ident; // 0000 Section name - vars
- diff(vars,varsEnd) # KMX_DWORD header.size; // 0004 Section length
+ diff(vars,endvars) # KMX_DWORD header.size; // 0004 Section length
01 00 00 00 # KMX_DWORD markers - list 1 ['a']
- diff(varsBegin,varsEnd,16) # KMX_DWORD varCount
+ diff(varsBegin,endvars,16) # KMX_DWORD varCount
block(varsBegin)
# var 0
- 00 00 00 00 # KMX_DWORD type = str
+ 00 00 00 00 # KMX_DWORD type = string
index(strNull,strA,2) # KMXPLUS_STR id 'a'
index(strNull,strAmarker,2) # KMXPLUS_STR value '\m{a}'
00 00 00 00 # KMXPLUS_ELEM
@@ -662,10 +652,10 @@ block(vars) # struct COMP_KMXPLUS_VARS {
00 00 00 00 # KMXPLUS_ELEM elem
# var 3
- 02 00 00 00 # KMX_DWORD type = string
+ 02 00 00 00 # KMX_DWORD type = uset
index(strNull,strVus,2) # KMXPLUS_STR id 'vus'
index(strNull,strUSet,2) # KMXPLUS_STR value '[abc]'
- 00 00 00 00 # KMXPLUS_ELEM elem
-block(varsEnd)
+ 00 00 00 00 # KMXPLUS_ELEM elem == uset.list[0]
+block(endvars)
block(eof) # end of file
diff --git a/common/test/keyboards/kmx-plus/basic-19.txt b/common/test/keyboards/kmx-plus/basic-19.txt
new file mode 100644
index 00000000000..b46803f565c
--- /dev/null
+++ b/common/test/keyboards/kmx-plus/basic-19.txt
@@ -0,0 +1,684 @@
+#
+# Keyman is copyright (C) SIL International. MIT License.
+#
+# basic-19.txt describes the expected output of running kmc against basic.xml
+# with v19 schema and v19 target version. It is used in the end-to-end test
+# test-compiler-e2e.ts.
+#
+# Differences from v17:
+# * KMX: COMP_KEYBOARD.dwFileVersion
+# * KMX+: `sec2` section vs `sect` section
+# * KMX+: each section has an additional version header field
+# * KMX+: `disp` section is v19
+# * KMX+: `layr` section is v19
+#
+# See README.md for more information.
+#
+
+block(kmxheader) # struct COMP_KEYBOARD {
+ 4b 58 54 53 # KMX_DWORD dwIdentifier; // 0000 Keyman compiled keyboard id
+
+ 00 13 00 00 # KMX_DWORD dwFileVersion; // 0004 Version of the file - Keyman 4.0 is 0x0400
+
+ 00 00 00 00 # KMX_DWORD dwCheckSum; // 0008 deprecated in 16.0, always 0
+ 00 00 00 00 # KMX_DWORD KeyboardID; // 000C as stored in HKEY_LOCAL_MACHINE//system//currentcontrolset//control//keyboard layouts
+ 01 00 00 00 # KMX_DWORD IsRegistered; // 0010
+ 00 00 00 00 # KMX_DWORD version; // 0014 keyboard version
+
+ sizeof(stores,12) # KMX_DWORD cxStoreArray; // 0018 in array entries
+ 00 00 00 00 # KMX_DWORD cxGroupArray; // 001C in array entries
+
+ offset(stores) # KMX_DWORD dpStoreArray; // 0020 [LPSTORE] address of first item in store array
+ 00 00 00 00 # KMX_DWORD dpGroupArray; // 0024 [LPGROUP] address of first item in group array
+
+ ff ff ff ff # KMX_DWORD StartGroup[2]; // 0028 index of starting groups [2 of them]
+ ff ff ff ff #
+
+ 20 00 00 00 # KMX_DWORD dwFlags; // 0030 Flags for the keyboard file
+
+ 00 00 00 00 # KMX_DWORD dwHotKey; // 0034 standard windows hotkey (hiword=shift/ctrl/alt stuff, loword=vkey)
+
+ 00 00 00 00 # KMX_DWORD dpBitmapOffset; // 0038 offset of the bitmaps in the file
+ 00 00 00 00 # KMX_DWORD dwBitmapSize; // 003C size in bytes of the bitmaps
+ # };
+
+block(kmxplusinfo) # struct COMP_KEYBOARD_KMXPLUSINFO {
+ offset(sec2) # KMX_DWORD dpKMXPlus; // 0040 offset of KMXPlus data from BOF, header is first
+ diff(sec2,eof) # KMX_DWORD dwKMXPlusSize; // 0044 size in bytes of entire KMXPlus data
+ # };
+
+block(stores) # struct COMP_STORE {
+ 07 00 00 00 # KMX_DWORD dwSystemID; - TSS_NAME
+ offset(store_name_name) # KMX_DWORD dpName;
+ offset(store_name_string) # KMX_DWORD dpString;
+ # };
+ # excluding this so we don’t have compiled version changes
+ # 14 00 00 00 # KMX_DWORD dwSystemID; - TSS_COMPILEDVERSION
+ # offset(store_compiledversion_name) # KMX_DWORD dpName;
+ # offset(store_compiledversion_string) # KMX_DWORD dpString;
+ # };
+ 24 00 00 00 # KMX_DWORD dwSystemID; - TSS_KEYBOARDVERSION
+ offset(store_keyboardversion_name) # KMX_DWORD dpName;
+ offset(store_keyboardversion_string) # KMX_DWORD dpString;
+ # };
+ 26 00 00 00 # KMX_DWORD dwSystemID; - TSS_TARGETS
+ offset(store_targets_name) # KMX_DWORD dpName;
+ offset(store_targets_string) # KMX_DWORD dpString;
+ # };
+
+ 18 00 00 00 # TSS_VISUALKEYBOARD
+ 00 00 00 00 # KMX_DWORD dpName;
+ offset(store_vk_path_string) # KMX_DWORD dpString;
+
+block(store_name_name)
+ 26 00 4e 00 41 00 4d 00 45 00 00 00 # '&NAME'
+block(store_name_string)
+ 54 00 65 00 73 00 74 00 4b 00 62 00 64 00 00 00 # 'TestKbd'
+
+block(store_keyboardversion_name)
+ 26 00 4b 00 45 00 59 00 42 00 4f 00 41 00 52 00
+ 44 00 56 00 45 00 52 00 53 00 49 00 4f 00 4e 00
+ 00 00 # '&KEYBOARDVERSION'
+block(store_keyboardversion_string)
+ 31 00 2e 00 30 00 2e 00 30 00 00 00 # '1.0.0'
+
+block(store_targets_name)
+ 26 00 54 00 41 00 52 00 47 00 45 00 54 00 53 00 00 00 # '&TARGETS'
+block(store_targets_string)
+ 64 00 65 00 73 00 6b 00 74 00 6f 00 70 00 00 00 # 'desktop'
+block(store_vk_path_string)
+ 62 00 61 00 73 00 69 00 63 00 2d 00 78 00 6d 00 6c 00 2e 00 6b 00 76 00 6b 00 00 00 # 'basic-xml.kvk'
+
+block(sec2) # struct COMP_KMXPLUS_SEC2 {
+ 73 65 63 32 # KMX_DWORD header.ident; // 0000 Section name 'sec2'
+ diff(sec2,endsec2) # KMX_DWORD header.size; // 0004 Section length
+ 00 13 00 00 # KMX_DWORD header.version; // 0008 Section version, 19 = 0x13
+ diff(sec2,eof) # KMX_DWORD total; // 000C KMXPlus entire length
+ sizeof(sectitems,8) # KMX_DWORD count; // 0010 number of section headers
+ # };
+ # Next sections are sec2 entries
+ # KMX_DWORD sec2; // 0010+ Section identity
+ # KMX_DWORD offset; // 0014+ Section offset relative to dpKMXPlus of section
+
+block(sectitems)
+ 62 6b 73 70
+ diff(sec2,bksp)
+
+ 64 69 73 70
+ diff(sec2,disp)
+
+ 65 6c 65 6d
+ diff(sec2,elem)
+
+ 6b 65 79 73
+ diff(sec2,keys)
+
+ 6c 61 79 72
+ diff(sec2,layr)
+
+ 6c 69 73 74
+ diff(sec2,list)
+
+ 6c 6f 63 61
+ diff(sec2,loca)
+
+ 6d 65 74 61
+ diff(sec2,meta)
+
+ 73 74 72 73
+ diff(sec2,strs)
+
+ 74 72 61 6e
+ diff(sec2,tran)
+
+ 75 73 65 74
+ diff(sec2,uset)
+
+ 76 61 72 73
+ diff(sec2,vars)
+
+block(endsec2)
+
+# ----------------------------------------------------------------------------------------------------
+# bksp
+# ----------------------------------------------------------------------------------------------------
+
+block(bksp)
+ # TODO-LDML: see 'tran' for a more programmatic way to calculate the fields
+ 62 6b 73 70
+ sizeof(bksp)
+ 00 11 00 00 # KMX_DWORD header.version; // 0008 Section version, 17 = 0x11
+ 01 00 00 00 # KMX_DWORD groupCount
+ 01 00 00 00 # KMX_DWORD transformCount
+ 00 00 00 00 # KMX_DWORD reorderCount
+
+ # group 0
+ 00 00 00 00 # KMX_DWORD type = transform
+ 01 00 00 00 # KMX_DWORD count = 1
+ 00 00 00 00 # KMX_DWORD index = 0
+
+ # transforms 0
+ index(strNull,strElemTranFrom1b,2) # KMX_DWORD str from ^e ## << ??
+ index(strNull,strNull,2) # KMX_DWORD str to 0
+ index(strNull,strNull,2) # KMX_DWORD str mapFrom 0
+ index(strNull,strNull,2) # KMX_DWORD str mapTo 0
+
+block(endbksp)
+
+# ----------------------------------------------------------------------------------------------------
+# disp (v19)
+# ----------------------------------------------------------------------------------------------------
+
+block(disp) # struct COMP_KMXPLUS_DISP {
+ 64 69 73 70 # KMX_DWORD header.ident; // 0000 Section name - disp
+ sizeof(disp) # KMX_DWORD header.size; // 0004 Section length
+ 00 13 00 00 # KMX_DWORD header.version; // 0008 Section version, 19 = 0x13
+ 02 00 00 00 # KMX_DWORD count; // 000C number of entries
+ index(strNull,strElemBkspFrom2,2) # KMX_DWORD baseCharacter // 0010 baseCharacter = 'e'
+ # };
+
+ # entry 0
+ index(strNull,strA,2) # KMX_DWORD toId // 0000 baseCharacter = 'a'
+ index(strNull,strElemTranFrom1,2) # KMX_DWORD display // 0004 display = '^'
+ 00 00 00 00 # KMX_DWORD flags // 0008 isId=0
+
+ # entry 1
+ index(strNull,strElemBkspFrom2,2) # KMX_DWORD toId // 0000 id = 'e'
+ index(strNull,strElemTranFrom1b,2) # KMX_DWORD display // 0004 '^e'
+ 01 00 00 00 # KMX_DWORD flags // 0008 isId=1
+
+
+
+# ----------------------------------------------------------------------------------------------------
+# elem
+# ----------------------------------------------------------------------------------------------------
+
+block(elem) # struct COMP_KMXPLUS_ELEM {
+ 65 6c 65 6d # KMX_DWORD header.ident; // 0000 Section name - elem
+ diff(elem,endelem) # KMX_DWORD header.size; // 0004 Section length
+ 00 11 00 00 # KMX_DWORD header.version; // 0008 Section version, 17 = 0x11
+ index(elemNull,endelem) # KMX_DWORD count; // 000C number of entries
+ # };
+
+#strings
+ #elem #0 null element
+ 00 00 00 00 # KMX_DWORD offset; // 0010+ offset from this blob
+ sizeof(elemNull,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units)
+
+ #elem #1 count=03
+ #elem #2 count=03
+ #elem #3 count=01
+
+
+ diff(elem,elemSet) # KMX_DWORD offset; // 0010+ offset from this blob
+ sizeof(elemSet,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units)
+
+ diff(elem,elemOrdrFrom) # KMX_DWORD offset; // 0010+ offset from this blob
+ sizeof(elemOrdrFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units)
+
+ diff(elem,elemOrdrBefore) # KMX_DWORD offset; // 0010+ offset from this blob
+ sizeof(elemOrdrBefore,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units)
+
+# now the elements
+block(elemNull)
+
+block(elemSet)
+ 61 00 00 00 # KMX_DWORD element; 'a'
+ 00 00 00 00 # KMX_DWORD flags;
+ 62 00 00 00 # KMX_DWORD element; 'b'
+ 00 00 00 00 # KMX_DWORD flags;
+ 63 00 00 00 # KMX_DWORD element; 'c'
+ 00 00 00 00 # KMX_DWORD flags;
+
+block(elemOrdrFrom)
+ # from="\u{1A60}[\u1A75-\u1A79]\u{1A45}" order="10 55 10"
+ 60 1a 00 00 # KMX_DWORD element; '᩠' // str: output string or UTF-32LE codepoint
+ 00 00 0A 00 # KMX_DWORD flags; // flag and order values - cp
+ 00 00 00 00 # TODO-LDML: uset #0 # KMX_DWORD element; [uset] // str: output string or UTF-32LE codepoint
+ 02 00 37 00 # KMX_DWORD flags; // flag and order values - unicodeset
+ 45 1A 00 00 # KMX_DWORD element; 'ᩅ' // str: output string or UTF-32LE codepoint
+ 00 00 0A 00 # KMX_DWORD flags; // flag and order values - cp
+
+block(elemOrdrBefore)
+ # before="\u{1A6B}"
+ 6b 1a 00 00 # KMX_DWORD element; 'ᩫ' // str: output string or UTF-32LE codepoint
+ 00 00 00 00 # KMX_DWORD flags; // flag and order values - cp
+
+block(endelem)
+
+# ----------------------------------------------------------------------------------------------------
+# keys
+# ----------------------------------------------------------------------------------------------------
+
+block(keys) # struct COMP_KMXPLUS_KEYS {
+ 6b 65 79 73 # KMX_DWORD header.ident; // 0000 Section name - keys
+ sizeof(keys) # KMX_DWORD header.size; // 0004 Section length
+ 00 11 00 00 # KMX_DWORD header.version; // 0008 Section version, 17 = 0x11
+ 05 00 00 00 # KMX_DWORD keyCount
+ 01 00 00 00 # KMX_DWORD flicksCount
+ 00 00 00 00 # KMX_DWORD flickCount
+ 30 00 00 00 # KMX_DWORD kmapCount; // 0008 number of kmap - #48, one per key
+ # keys
+ # (#0000) a
+ 61 00 00 00 # 'a'
+ 00 00 00 00 # KMX_DWORD flags = 0
+ index(strNull,strA,2) # KMXPLUS_STR 'a' (key id)
+ 00 00 00 00 # KMXPLUS_STR switch
+ 0A 00 00 00 # KMX_DWORD width*10
+ 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST longPress
+ 00 00 00 00 # STR longPressDefault
+ 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST multiTap
+ 00 00 00 00 # flicks
+ # (#0001) e
+ 65 00 00 00 # 'e'
+ 00 00 00 00 # KMX_DWORD flags = 0
+ index(strNull,strElemBkspFrom2,2) # KMXPLUS_STR 'e' (key id)
+ 00 00 00 00 # KMXPLUS_STR switch
+ 0A 00 00 00 # KMX_DWORD width*10
+ 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST longPress
+ 00 00 00 00 # STR longPressDefault
+ 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST multiTap
+ 00 00 00 00 # flicks
+ # (#0002) gap (reserved)
+ 00 00 00 00 # nothing
+ 03 00 00 00 # KMX_DWORD flags = extend|gap
+ index(strNull,strGapReserved,2) # KMXPLUS_STR 'gap (reserved)' (key id)
+ 00 00 00 00 # KMXPLUS_STR switch
+ 0A 00 00 00 # KMX_DWORD width*10 (full width)
+ 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST longPress
+ 00 00 00 00 # STR longPressDefault
+ 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST multiTap
+ 00 00 00 00 # flicks
+ # (#0003) hmaqtugha
+ 27 01 00 00 # UTF-32 'U+0127'
+ 00 00 00 00 # KMX_DWORD (flags: none)
+ index(strNull,strHmaqtugha,2) # KMXPLUS_STR 'hmaqtugha'
+ 00 00 00 00 # KMXPLUS_STR switch
+ 0A 00 00 00 # KMX_DWORD width*10
+ 02 00 00 00 # TODO: index(listNull,indexAe,4) # LIST longPress 'a e'
+ 00 00 00 00 # STR longPressDefault
+ 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST multiTap
+ 00 00 00 00 # flicks 0
+ # (#0004) that
+ index(strNull,strKeys,2) # KMXPLUS_STR '...'
+ 01 00 00 00 # KMX_DWORD flags = extend
+ index(strNull,strThat,2) # KMXPLUS_STR 'that'
+ 00 00 00 00 # KMXPLUS_STR switch
+ 0A 00 00 00 # KMX_DWORD width*10
+ 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST longPress
+ 00 00 00 00 # STR longPressDefault
+ 00 00 00 00 # TODO: index(listNull,listNull,4) # LIST multiTap
+ 00 00 00 00 # flicks 0
+ # flicks
+ # flicks 0 - null
+ 00 00 00 00 # KMX_DWORD count
+ 00 00 00 00 # KMX_DWORD flick
+ 00 00 00 00 # KMX_STR id
+ # flick
+ # Right now there aren’t any flick elements.
+ #00 00 00 00 # LIST directions
+ #00 00 00 01 # flags
+ #00 00 00 00 # str: to
+
+ # kmapdata:
+ # note that 'a' and 'e' are omitted, as they aren't on the layers (just from gestures)
+
+ # moving these to 1-liners so they can be sorted! the structure is as follows:
+ ## 31 00 00 00 # KMX_DWORD vkey
+ ## 00 00 00 00 # KMX_DWORD modifiers (none)
+ ## 04 00 00 00 # KMX_DWORD key index (that)
+
+# following lines generated with this snippet:
+#
+# /** from scanCodes-implied.xml */
+# const scans = ("29 02 03 04 05 06 07 08 09 0A 0B 0C 0D" + " 10 11 12 13 14 15 16 17 18 19 1A 1B 2B" + " 1E 1F 20 21 22 23 24 25 26 27 28" + " 2C 2D 2E 2F 30 31 32 33 34 35").split(/ /);
+#
+# for (let s of scans) {
+# const n = Number.parseInt(s, 16);
+# const v = CLDRScanToUSVirtualKeyCodes[n]; // virtual-key-constants.ts
+# if (!v) {
+# console.error(`! ${s}`);
+# } else {
+# console.log(` ${Number(v).toString(16)} 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)`);
+# }
+# }
+
+# - vkey - - modifier - - key id -
+ 20 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 30 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 31 00 00 00 00 00 00 00 04 00 00 00 # "that"
+ 32 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 33 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 34 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 35 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 36 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 37 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 38 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 39 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 41 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 42 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 43 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 44 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 45 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 46 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 47 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 48 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 49 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 4a 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 4b 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 4c 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 4d 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 4e 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 4f 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 50 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 51 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 52 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 53 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 54 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 55 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 56 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 57 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 58 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 59 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ 5a 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ ba 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ bb 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ bc 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ bd 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ be 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ bf 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ c0 00 00 00 00 00 00 00 03 00 00 00 # "hmaqtugha"
+ db 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ dc 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ dd 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+ de 00 00 00 00 00 00 00 02 00 00 00 # gap (reserved)
+
+block(endkeys)
+
+# ----------------------------------------------------------------------------------------------------
+# layr
+# ----------------------------------------------------------------------------------------------------
+
+block(layr) # struct COMP_KMXPLUS_LAYR {
+ 6c 61 79 72 # KMX_DWORD header.ident; // 0000 Section name - layr
+ sizeof(layr) # KMX_DWORD header.size; // 0004 Section length
+ 00 13 00 00 # KMX_DWORD header.version; // 0008 Section version, 19 = 0x13
+ 01 00 00 00 # KMX_DWORD formCount
+ 01 00 00 00 # KMX_DWORD layerCount
+ 01 00 00 00 # KMX_DWORD rowCount
+ 02 00 00 00 # KMX_DWORD keyCount
+ # form 0
+ index(strNull,strUs,2) # KMXPLUS_STR hardware = 'us'
+ 00 00 00 00 # KMX_DWORD layer;
+ 01 00 00 00 # KMX_DWORD count
+ 7B 00 00 00 # KMX_DWORD minDeviceWidth; // 123
+ 00 00 00 00 # KMXPLUS_STR baseLayout v19
+ 00 00 00 00 # KMX_DWORD flags v19
+
+ # layers 0
+ 00 00 00 00 # KMXPLUS_STR id;
+ 00 00 00 00 # KMX_DWORD mod
+ 00 00 00 00 # KMX_DWORD row index
+ 01 00 00 00 # KMX_DWORD count
+ # rows 0
+ 00 00 00 00 # KMX_DWORD key index
+ 02 00 00 00 # KMX_DWORD count
+ # keys
+ index(strNull,strHmaqtugha,2) # KMXPLUS_STR locale; // 'hmaqtugha'
+ index(strNull,strThat,2) # KMXPLUS_STR locale; // 'that'
+
+# ----------------------------------------------------------------------------------------------------
+# list
+# ----------------------------------------------------------------------------------------------------
+
+# TODO-LDML: lots of comment-out ahead. Need to revisit.
+
+block(list) # struct COMP_KMXPLUS_LIST {
+ 6c 69 73 74 # KMX_DWORD header.ident; // 0000 Section name - list
+ diff(list,endList) # KMX_DWORD header.size; // 0004 Section length
+ 00 11 00 00 # KMX_DWORD header.version; // 0008 Section version, 17 = 0x11
+ 03 00 00 00 # KMX_DWORD listCount (should be 2)
+ 03 00 00 00 # KMX_DWORD indexCount (should be 2)
+ # list #0 the null list
+ block(listNull)
+ 00 00 00 00 #index(indexNull,indexNull,2) # KMX_DWORD list index (0)
+ 00 00 00 00 # KMX_DWORD lists[0].count
+ block(listA)
+ 00 00 00 00 # first index
+ 01 00 00 00 #count
+ block(listAe)
+ 01 00 00 00 # index(indexAe,indexNull,2) # KMX_DWORD list index (also 0)
+ 02 00 00 00 # KMX_DWORD count
+ block(endLists)
+ # indices
+ #block(indexNull)
+ # No null index
+ # index(strNull,strNull,2) # KMXPLUS_STR string index
+ block(indexA)
+ index(strNull,strA,2) # a
+ block(indexAe)
+ index(strNull,strA,2) # KMXPLUS_STR a
+ index(strNull,strElemBkspFrom2,2) # KMXPLUS_STR e
+ block(endIndices)
+ block(endList)
+
+# ----------------------------------------------------------------------------------------------------
+# loca
+# ----------------------------------------------------------------------------------------------------
+
+block(loca) # struct COMP_KMXPLUS_LOCA {
+ 6c 6f 63 61 # KMX_DWORD header.ident; // 0000 Section name - loca
+ sizeof(loca) # KMX_DWORD header.size; // 0004 Section length
+ 00 11 00 00 # KMX_DWORD header.version; // 0008 Section version, 17 = 0x11
+ 01 00 00 00 # KMX_DWORD count; // 000C number of locales
+ index(strNull,strLocale,2) # KMXPLUS_STR locale; // 0010+ locale string entry = 'mt'
+ # };
+# ----------------------------------------------------------------------------------------------------
+# meta
+# ----------------------------------------------------------------------------------------------------
+
+block(meta) # struct COMP_KMXPLUS_META {
+ 6d 65 74 61 # KMX_DWORD header.ident; // 0000 Section name - meta
+ sizeof(meta) # KMX_DWORD header.size; // 0004 Section length
+ 00 11 00 00 # KMX_DWORD header.version; // 0008 Section version, 17 = 0x11
+ index(strNull,strAuthor,2) # KMXPLUS_STR author;
+ index(strNull,strConformsTo,2) # KMXPLUS_STR conform;
+ index(strNull,strLayout,2) # KMXPLUS_STR layout;
+ index(strNull,strName,2) # KMXPLUS_STR name;
+ index(strNull,strIndicator,2) # KMXPLUS_STR indicator;
+ index(strNull,strVersion,2) # KMXPLUS_STR version;
+ 00 00 00 00 # KMX_DWORD settings;
+ # };
+
+# ----------------------------------------------------------------------------------------------------
+# strs
+# ----------------------------------------------------------------------------------------------------
+
+block(strs) # struct COMP_KMXPLUS_STRS {
+ 73 74 72 73 # KMX_DWORD header.ident; // 0000 Section name - strs
+ diff(strs,endstrs) # KMX_DWORD header.size; // 0004 Section length
+ 00 11 00 00 # KMX_DWORD header.version; // 0008 Section version, 17 = 0x11
+ index(strNull,endstrs,2) # KMX_DWORD count; // 000C count of str entries
+ # };
+
+ # Next sections are string entries
+ # KMX_DWORD offset; // 0010+ offset from this blob
+ # KMX_DWORD length; // 0014+ str length (UTF-16LE units)
+
+ diff(strs,strNull) sizeof(strNull,2)
+ diff(strs,strVersion) sizeof(strVersion,2)
+ diff(strs,strConformsTo) sizeof(strConformsTo,2)
+ diff(strs,strName) sizeof(strName,2)
+ diff(strs,strFromSet) sizeof(strFromSet,2)
+ diff(strs,strUSet) sizeof(strUSet,2)
+ diff(strs,strAmarker) sizeof(strAmarker,2)
+ diff(strs,strSentinel0001r) sizeof(strSentinel0001r,2)
+ diff(strs,strElemTranFrom1) sizeof(strElemTranFrom1,2)
+ diff(strs,strElemTranFrom1a) sizeof(strElemTranFrom1a,2)
+ diff(strs,strElemTranFrom1b) sizeof(strElemTranFrom1b,2)
+ diff(strs,strA) sizeof(strA,2)
+ diff(strs,strSet) sizeof(strSet,2)
+ diff(strs,strSet2) sizeof(strSet2,2)
+ diff(strs,strTranTo) sizeof(strTranTo,2)
+ diff(strs,strElemBkspFrom2) sizeof(strElemBkspFrom2,2)
+ diff(strs,strGapReserved) sizeof(strGapReserved,2)
+ diff(strs,strHmaqtugha) sizeof(strHmaqtugha,2)
+ diff(strs,strLocale) sizeof(strLocale,2)
+ diff(strs,strLayout) sizeof(strLayout,2)
+ diff(strs,strAuthor) sizeof(strAuthor,2)
+ diff(strs,strThat) sizeof(strThat,2)
+ diff(strs,strUs) sizeof(strUs,2)
+ diff(strs,strVse) sizeof(strVse,2)
+ diff(strs,strVst) sizeof(strVst,2)
+ diff(strs,strVus) sizeof(strVus,2)
+ diff(strs,strKeys) sizeof(strKeys,2)
+ diff(strs,strIndicator) sizeof(strIndicator,2)
+ diff(strs,strSentinel0001) sizeof(strSentinel0001,2)
+
+ # String table -- block(x) is used to store the null u16char at end of each string
+ # without interfering with sizeof() calculation above
+
+#str #00
+ block(strNull) block(x) 00 00 # the zero-length string
+ block(strVersion) 31 00 2e 00 30 00 2e 00 30 00 block(x) 00 00 # '1.0.0'
+ block(strConformsTo) 34 00 35 00 block(x) 00 00 # '45'
+ block(strName) 54 00 65 00 73 00 74 00 4b 00 62 00 64 00 block(x) 00 00 # 'TestKbd'
+ block(strFromSet) 5B 00 5C 00 75 00 31 00 41 00 37 00 35 00 2D 00 5C 00 75 00 31 00 41 00 37 00 39 00 5D 00 block(x) 00 00 # [\u1a75-\u1a79]
+ block(strUSet) 5b 00 61 00 62 00 63 00 5d 00 block(x) 00 00 # '[abc]'
+ block(strAmarker) 5C 00 6D 00 7B 00 61 00 7D 00 block(x) 00 00 # '\m{a}'
+ block(strSentinel0001r) 5c 00 75 00 66 00 66 00 66 00 66 00 5c 00 75 00 30 00 30 00 30 00 38 00 5C 00 75 00 30 00 30 00 30 00 31 00 block(x) 00 00 # UC_SENTINEL CODE_DEADKEY \u0001 (regex form)
+ block(strElemTranFrom1) 5E 00 block(x) 00 00 # '^'
+ block(strElemTranFrom1a) 5E 00 61 00 block(x) 00 00 # '^a'
+ block(strElemTranFrom1b) 5E 00 65 00 block(x) 00 00 # '^e'
+ block(strA) 61 00 block(x) 00 00 # 'a'
+ block(strSet) 61 00 20 00 62 00 20 00 63 00 block(x) 00 00 # 'a b c'
+#str #0A
+ block(strSet2) 61 00 62 00 63 00 block(x) 00 00 # 'abc'
+ block(strTranTo) 61 00 02 03 block(x) 00 00 # 'â' (U+0061 U+0302)
+ block(strElemBkspFrom2) 65 00 block(x) 00 00 # 'e'
+ block(strGapReserved) 67 00 61 00 70 00 20 00 28 00 72 00 65 00 73 00 65 00 72 00 76 00 65 00 64 00 29 00 block(x) 00 00 # 'gap (reserved)'
+ block(strHmaqtugha) 68 00 6d 00 61 00 71 00 74 00 75 00 67 00 68 00 61 00 block(x) 00 00 # 'hmaqtugha'
+#str #10
+ block(strLocale) 6d 00 74 00 block(x) 00 00 # 'mt'
+ block(strLayout) 71 00 77 00 65 00 72 00 74 00 79 00 block(x) 00 00 # 'qwerty'
+ block(strAuthor) 73 00 72 00 6c 00 32 00 39 00 35 00 block(x) 00 00 # 'srl295'
+ block(strThat) 74 00 68 00 61 00 74 00 block(x) 00 00 # 'that'
+ block(strUs) 75 00 73 00 block(x) 00 00 # 'us' (layout)
+ block(strVse) 76 00 73 00 65 00 block(x) 00 00 # 'vse'
+ block(strVst) 76 00 73 00 74 00 block(x) 00 00 # 'vst'
+ block(strVus) 76 00 75 00 73 00 block(x) 00 00 # 'vus'
+#str #1a
+ block(strKeys) 90 17 b6 17 block(x) 00 00 # 'ថា'
+ #
+ block(strIndicator) 3d d8 40 de block(x) 00 00 # '🙀'
+ block(strSentinel0001) FF FF 08 00 01 00 block(x) 00 00 # UC_SENTINEL CODE_DEADKEY U+0001
+
+
+block(endstrs) # end of strs block
+
+# ----------------------------------------------------------------------------------------------------
+# tran
+# ----------------------------------------------------------------------------------------------------
+
+block(tran) # struct COMP_KMXPLUS_TRAN {
+ 74 72 61 6e # KMX_DWORD header.ident; // 0000 Section name - tran
+ diff(tran,endtran) # KMX_DWORD header.size; // 0004 Section length
+ 00 11 00 00 # KMX_DWORD header.version; // 0008 Section version, 17 = 0x11
+ diff(tranGroupStart,tranTransformStart,12) # KMX_DWORD groupCount;
+ diff(tranTransformStart,tranReorderStart,16) # KMX_DWORD transformCount;
+ diff(tranReorderStart,endtran,8) # KMX_DWORD reorderCount;
+
+ block(tranGroupStart) # COMP_KMXPLUS_TRAN_GROUP
+ # group 0
+ 00 00 00 00 # KMX_DWORD type = transform
+ 02 00 00 00 # KMX_DWORD count
+ diff(tranTransformStart,tranTransform0,16) # KMX_DWORD index
+
+ # group 1
+ 00 00 00 00 # KMX_DWORD type = transform
+ 01 00 00 00 # KMX_DWORD count
+ diff(tranTransformStart,tranTransform2,16) # KMX_DWORD index
+
+ # group 2
+ 01 00 00 00 # KMX_DWORD type = reorder
+ 01 00 00 00 # KMX_DWORD count
+ diff(tranReorderStart,tranReorder0,8) # KMX_DWORD index
+
+ # transforms
+ block(tranTransformStart) # COMP_KMXPLUS_TRAN_TRANSFORM
+ block(tranTransform0)
+ index(strNull,strElemTranFrom1a,2) # KMXPLUS_STR from;
+ index(strNull,strTranTo,2) # KMXPLUS_STR to;
+ index(strNull,strNull,2) # mapFrom
+ index(strNull,strNull,2) # mapTo
+
+ block(tranTransform1)
+ index(strNull,strA,2) # KMXPLUS_STR from; 'a'
+ index(strNull,strSentinel0001,2) # KMXPLUS_STR to; \m{a} (plain form)
+ index(strNull,strNull,2) # mapFrom
+ index(strNull,strNull,2) # mapTo
+
+ block(tranTransform2) # Next group
+ index(strNull,strSentinel0001r,2) # KMXPLUS_STR from; (\m{a}) (regex form)
+ index(strNull,strNull,2) # KMXPLUS_STR to; (none)
+ index(strNull,strNull,2) # mapFrom
+ index(strNull,strNull,2) # mapTo
+
+ # reorders
+ block(tranReorderStart) # COMP_KMXPLUS_TRAN_REORDER
+ block(tranReorder0)
+ index(elemNull,elemOrdrFrom) # KMXPLUS_ELEM elements;
+ index(elemNull,elemOrdrBefore) # KMXPLUS_ELEM before;
+
+block(endtran)
+
+block(uset)
+ 75 73 65 74 # KMX_DWORD header.ident; // 0000 Section name - uset
+ diff(uset,enduset) # KMX_DWORD header.size; // 0004 Section length
+ 00 11 00 00 # KMX_DWORD header.version; // 0008 Section version, 17 = 0x11
+ 01 00 00 00 # lists
+ 01 00 00 00 # elements
+ # lists
+ 00 00 00 00 # first list
+ 01 00 00 00 # size: 1
+ index(strNull,strFromSet,2) # str
+ # ranges
+ # range @0
+ 75 1A 00 00 # start
+ 79 1A 00 00 # end
+ block(enduset)
+
+block(vars) # struct COMP_KMXPLUS_VARS {
+ 76 61 72 73 # KMX_DWORD header.ident; // 0000 Section name - vars
+ diff(vars,endvars) # KMX_DWORD header.size; // 0004 Section length
+ 00 11 00 00 # KMX_DWORD header.version; // 0008 Section version, 17 = 0x11
+ 01 00 00 00 # KMX_DWORD markers - list 1 ['a']
+ diff(varsBegin,endvars,16) # KMX_DWORD varCount
+
+ block(varsBegin)
+ # var 0
+ 00 00 00 00 # KMX_DWORD type = string
+ index(strNull,strA,2) # KMXPLUS_STR id 'a'
+ index(strNull,strAmarker,2) # KMXPLUS_STR value '\m{a}'
+ 00 00 00 00 # KMXPLUS_ELEM
+
+ # var 1
+ 01 00 00 00 # KMX_DWORD type = set
+ index(strNull,strVse,2) # KMXPLUS_STR id 'vse'
+ index(strNull,strSet,2) # KMXPLUS_STR value 'a b c'
+ 01 00 00 00 # KMXPLUS_ELEM elem 'a b c' see 'elemSet'
+
+ # var 2
+ 00 00 00 00 # KMX_DWORD type = string
+ index(strNull,strVst,2) # KMXPLUS_STR id 'vst'
+ index(strNull,strSet2,2) # KMXPLUS_STR value 'abc'
+ 00 00 00 00 # KMXPLUS_ELEM elem
+
+ # var 3
+ 02 00 00 00 # KMX_DWORD type = uset
+ index(strNull,strVus,2) # KMXPLUS_STR id 'vus'
+ index(strNull,strUSet,2) # KMXPLUS_STR value '[abc]'
+ 00 00 00 00 # KMXPLUS_ELEM elem
+block(endvars)
+
+block(eof) # end of file
diff --git a/developer/src/kmc-ldml/test/fixtures/basic.xml b/common/test/keyboards/kmx-plus/basic.xml
similarity index 100%
rename from developer/src/kmc-ldml/test/fixtures/basic.xml
rename to common/test/keyboards/kmx-plus/basic.xml
diff --git a/common/test/resources/keyboards/options_with_save.js b/common/test/resources/keyboards/options_with_save.js
index 8252b516604..aa8507e0734 100644
--- a/common/test/resources/keyboards/options_with_save.js
+++ b/common/test/resources/keyboards/options_with_save.js
@@ -1,4 +1,4 @@
-// Original source may be found within the repo at /common/tests/keyboards/baseline/k_023___options_with_save.kmn
+// Original source may be found within the repo at /common/tests/keyboards/baseline/k_0502___options_with_save.kmn
if(typeof keyman === 'undefined') {
console.log('Keyboard requires KeymanWeb 10.0 or later');
@@ -61,4 +61,4 @@ function Keyboard_options_with_save() {
}
return r;
};
-}
\ No newline at end of file
+}
diff --git a/common/test/resources/keyboards/test_8568_deadkeys.kmn b/common/test/resources/keyboards/test_8568_deadkeys.kmn
index 4cf7cc799d1..f7f3067daa4 100644
--- a/common/test/resources/keyboards/test_8568_deadkeys.kmn
+++ b/common/test/resources/keyboards/test_8568_deadkeys.kmn
@@ -1,6 +1,6 @@
store(&NAME) 'Testcases for deadkeys bug (#8568)'
c Description: Tests deadkey backspacing
-c See also common/test/keyboards/baseline/k_020___deadkeys_and_backspace.kmn
+c See also common/test/keyboards/baseline/k_0302___deadkeys_and_backspace.kmn
c 1. One deadkey in context dk(1) + BKSP = nul
c 2. One char and one deadkey in context 'a' dk(2) + BKSP = nul
c 3. One deadkey and one char in context dk(3) 'a' + BKSP = nul
diff --git a/common/test/resources/keyboards/test_rtl.js b/common/test/resources/keyboards/test_rtl.js
new file mode 100644
index 00000000000..9ec0d156a35
--- /dev/null
+++ b/common/test/resources/keyboards/test_rtl.js
@@ -0,0 +1,48 @@
+if(typeof keyman === 'undefined') {
+ console.log('Keyboard requires KeymanWeb 10.0 or later');
+ if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");
+} else {
+KeymanWeb.KR(new Keyboard_test_rtl());
+}
+function Keyboard_test_rtl()
+{
+ var modCodes = keyman.osk.modifierCodes;
+ var keyCodes = keyman.osk.keyCodes;
+
+ this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;
+ this.KI="Keyboard_test_rtl";
+ this.KN ="RTL Keyboard";
+ this.KMINVER="10.0";
+ this.KV=null;
+ this.KDU=0;
+ this.KH='';
+ this.KM=0;
+ this.KBVER="1.0";
+ this.KMBM=0 /* 0x0000 */;
+ this.KRTL=1;
+ this.KVS=[];
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.gs=function(t,e) {
+ return this.g_Main_0(t,e);
+ };
+ this.g_Main_0=function(t,e) {
+ var k=KeymanWeb,r=0,m=0;
+ if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) {
+ if(1){
+ r=m=1; // Line 10
+ k.KDC(0,t);
+ k.KO(-1,t,"ش");
+ }
+ }
+ else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_B /* 0x42 */)) {
+ if(1){
+ r=m=1; // Line 11
+ k.KDC(0,t);
+ k.KO(-1,t,"ال");
+ }
+ }
+ return r;
+ };
+}
diff --git a/common/test/resources/keyboards/test_rtl.kmn b/common/test/resources/keyboards/test_rtl.kmn
new file mode 100644
index 00000000000..52efb684c53
--- /dev/null
+++ b/common/test/resources/keyboards/test_rtl.kmn
@@ -0,0 +1,11 @@
+store(&NAME) 'RTL Keyboard'
+
+store(&version) '10.0'
+store(&KMW_RTL) '1'
+
+begin Unicode > use(Main)
+
+group(Main) using keys
+
++ [K_A] > 'ش'
++ [K_B] > 'ال'
diff --git a/common/test/resources/keyboards/test_rtl.kmx b/common/test/resources/keyboards/test_rtl.kmx
new file mode 100644
index 00000000000..3571b6aa43c
Binary files /dev/null and b/common/test/resources/keyboards/test_rtl.kmx differ
diff --git a/common/test/resources/mocha-teamcity-reporter/teamcity.cjs b/common/test/resources/mocha-teamcity-reporter/teamcity.cjs
index 24c76a5f2b8..fd901faaa1b 100644
--- a/common/test/resources/mocha-teamcity-reporter/teamcity.cjs
+++ b/common/test/resources/mocha-teamcity-reporter/teamcity.cjs
@@ -135,6 +135,8 @@ function Teamcity(runner, options) {
const ignoredTests = {};
const testState = { pending: 0 };
+ log('Initializing Mocha TeamCity Reporter');
+
runner.on(EVENT_SUITE_BEGIN, function (suite) {
handleFlow(true, hasParentFlowId);
if (suite.root) {
diff --git a/common/test/resources/playwright-TC-reporter.ts b/common/test/resources/playwright-TC-reporter.ts
new file mode 100644
index 00000000000..20d6ac39d49
--- /dev/null
+++ b/common/test/resources/playwright-TC-reporter.ts
@@ -0,0 +1,200 @@
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ */
+
+import type {
+ FullConfig, FullResult, Reporter, Suite, TestCase, TestResult
+} from '@playwright/test/reporter';
+
+
+class TestNode {
+ public static Nodes = new Map();
+ private static OpenNodes: string[] = [];
+ public static RootFlow: string;
+ private id: string;
+ private suiteOrTest: Suite | TestCase;
+ private flowId: number;
+ private parent: TestNode | null;
+ private childrenToVisit: TestNode[] = [];
+
+ public constructor(suiteOrTest: Suite | TestCase) {
+ this.suiteOrTest = suiteOrTest;
+ this.id = this.suiteOrTest.titlePath().toString();
+ this.flowId = Math.floor(Math.random() * 100000 + 1);
+ this.parent = null; // will be set by parent
+ if (!this.isTest) {
+ for (const child of (suiteOrTest).entries()) {
+ const node = new TestNode(child);
+ this.childrenToVisit.push(node);
+ node.parent = this;
+ }
+ }
+ TestNode.Nodes.set(this.id, this);
+ }
+
+ private get isTest(): boolean {
+ return this.suiteOrTest.type == 'test';
+ }
+
+ public get parentFlowId(): number | string {
+ return this.parent?.flowId ?? TestNode.RootFlow;
+ }
+
+ private start(): void {
+ this.parent?.start();
+ if (TestNode.OpenNodes.includes(this.id)) {
+ // already started, nothing to do
+ if (this.isTest) {
+ console.error(`Test '${this.suiteOrTest.title}' is already started!`)
+ }
+ return;
+ }
+
+ if (this.suiteOrTest.title !== '') {
+ console.log(`##teamcity[flowStarted flowId='${this.flowId}' parent='${this.parentFlowId}']`);
+ if (this.isTest) {
+ console.log(`##teamcity[testStarted name='${this.suiteOrTest.title}' captureStandardOutput='true']`);
+ } else {
+ console.log(`##teamcity[testSuiteStarted name='${this.suiteOrTest.title}']`);
+ }
+ }
+
+ TestNode.OpenNodes.push(this.id);
+ }
+
+ private escape(message: string): string {
+ // TeamCity escaping rules for: ' | [ ] \n \r \uNNNN
+ // See: https://www.jetbrains.com/help/teamcity/service-messages.html#Escaped+Values
+ return message?.replace(/['|\[\]]/g, (matched) => `|${matched}`)
+ .replace(/\n/g, '|n')
+ .replace(/\r/g, '|r')
+ .replace(/[\u0080-\uFFFF]/g, c => `|0x${c.charCodeAt(0).toString(16).padStart(4, '0')}`) ?? '';
+ }
+
+ private getTestResult(result: TestResult): { msgTitle: string, details: string } {
+ if (!result) {
+ return null;
+ }
+ switch (result.status) {
+ case 'passed':
+ return { msgTitle: 'testFinished', details: `duration='${result.duration}'` };
+ case 'failed':
+ case 'interrupted':
+ case 'timedOut':
+ return { msgTitle: 'testFailed', details: `message='${this.escape(result.error?.message)}' details='${this.escape(result.error?.value ?? result.error?.cause)}'` };
+ case 'skipped':
+ return { msgTitle: 'testIgnored', details: `message='${this.escape(result.annotations?.toString()) ?? ''}'` };
+ }
+ }
+
+ private end(result: TestResult): void {
+ if (this.suiteOrTest.title !== '') {
+ if (this.isTest) {
+ const { msgTitle, details } = this.getTestResult(result) ?? { msgTitle: 'testFinished', details: '' };
+ console.log(`##teamcity[${msgTitle} name='${this.suiteOrTest.title}' ${details}]`);
+ } else {
+ console.log(`##teamcity[testSuiteFinished name='${this.suiteOrTest.title}']`);
+ }
+ console.log(`##teamcity[flowFinished flowId='${this.flowId}']`);
+ }
+
+ this.removeFromOpenNodes();
+ this.parent?.removeChild(this);
+ TestNode.Nodes.delete(this.id);
+ }
+
+ private removeFromOpenNodes(): void {
+ const ourIndex = TestNode.OpenNodes.indexOf(this.id);
+ if (ourIndex < 0) {
+ console.error(`Can't find '${this.id}' in open nodes`);
+ return;
+ }
+ TestNode.OpenNodes.splice(ourIndex, 1);
+ }
+
+ private removeChild(child: TestNode) {
+ const childIndex = this.childrenToVisit.indexOf(child);
+ if (childIndex < 0) {
+ console.error(`Can't find child '${child.id}' in parent '${this.id}'`);
+ return;
+ }
+ this.childrenToVisit.splice(childIndex, 1);
+
+ if (this.childrenToVisit.length > 0) {
+ return;
+ }
+
+ // No more children, so close this node
+ this.end(null);
+ }
+
+ public static startTest(test: TestCase): void {
+ const node = TestNode.Nodes.get(test.titlePath().toString());
+ if (!node) {
+ console.error(`Can't find test node for ${test.titlePath().toString()}`);
+ return;
+ }
+ node.start();
+ }
+
+ public static endTest(test: TestCase, result: TestResult): void {
+ const node = TestNode.Nodes.get(test.titlePath().toString());
+ if (!node) {
+ console.error(`Can't find test node for ${test.titlePath().toString()}`);
+ return;
+ }
+ node.end(result);
+ }
+
+ public endAll(): void {
+ if (this.childrenToVisit.length > 0) {
+ console.error(`Root node still has ${this.childrenToVisit.length} open children`);
+ }
+ if (TestNode.OpenNodes.length > 0) {
+ console.error(`Still have ${TestNode.OpenNodes.length} open nodes`);
+ while (TestNode.OpenNodes.length > 0) {
+ const id = TestNode.OpenNodes[TestNode.OpenNodes.length - 1];
+ console.log(`Closing '${id}'`);
+ const node = TestNode.Nodes.get(id);
+ if (!node) {
+ console.error(`Node for '${id}' not found in Nodes map, removing from OpenNodes`);
+ TestNode.OpenNodes.pop();
+ continue;
+ }
+
+ node.end(null);
+ }
+ }
+ if (TestNode.Nodes.size > 0) {
+ console.error(`Still have ${TestNode.Nodes.size} nodes hanging around`);
+ for (const [id, node] of Array.from(TestNode.Nodes.entries())) {
+ console.error(`Remaining node title: '${node.suiteOrTest.title}' (id: '${id}')`);
+ }
+ }
+ }
+}
+
+export default class PlaywrightTeamcityReporter implements Reporter {
+ private root!: TestNode;
+
+ public constructor(options: { parentFlow?: string } = {}) {
+ console.log('Initializing Playwright TeamCity Reporter');
+ TestNode.RootFlow = options.parentFlow ?? 'unit_tests';
+ }
+
+ public onBegin(config: FullConfig, suite: Suite) {
+ this.root = new TestNode(suite);
+ }
+
+ public onTestBegin(test: TestCase, result: TestResult) {
+ TestNode.startTest(test);
+ }
+
+ public onTestEnd(test: TestCase, result: TestResult) {
+ TestNode.endTest(test, result);
+ }
+
+ public onEnd(result: FullResult) {
+ this.root.endAll();
+ }
+}
diff --git a/common/test/resources/test-runner-TC-reporter.mjs b/common/test/resources/test-runner-TC-reporter.mjs
index 8a6aeaccf21..33198592afa 100644
--- a/common/test/resources/test-runner-TC-reporter.mjs
+++ b/common/test/resources/test-runner-TC-reporter.mjs
@@ -94,14 +94,16 @@ export default function teamcityReporter({ name="Web Test Runner JavaScript test
/** @type {import('@web/test-runner').Reporter} */
const reporter = {
- start({config, sessions}) {
+ start({ config, sessions }) {
+ logger = config.logger;
+ logger.log('Initializing Web Test Runner TeamCity Reporter');
+
rootDir = config.rootDir;
for(const session of sessions.all()) {
testDefMap.set(buildSessionName(session), new Map());
}
- logger = config.logger;
logger.log(`##teamcity[blockOpened name='${e(name)}']`);
},
stop(args) {
diff --git a/common/tools/es-bundling/.gitignore b/common/tools/es-bundling/.gitignore
new file mode 100644
index 00000000000..07ed7069a24
--- /dev/null
+++ b/common/tools/es-bundling/.gitignore
@@ -0,0 +1 @@
+build/*
\ No newline at end of file
diff --git a/web/src/tools/es-bundling/README.md b/common/tools/es-bundling/README.md
similarity index 100%
rename from web/src/tools/es-bundling/README.md
rename to common/tools/es-bundling/README.md
diff --git a/web/src/tools/es-bundling/build.sh b/common/tools/es-bundling/build.sh
similarity index 68%
rename from web/src/tools/es-bundling/build.sh
rename to common/tools/es-bundling/build.sh
index 3a05714120c..8ff7f035f40 100755
--- a/web/src/tools/es-bundling/build.sh
+++ b/common/tools/es-bundling/build.sh
@@ -3,7 +3,7 @@
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
-. "${THIS_SCRIPT%/*}/../../../../resources/build/builder-full.inc.sh"
+. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
## END STANDARD BUILD SCRIPT INCLUDE
. "$KEYMAN_ROOT/resources/build/utils.inc.sh"
@@ -11,17 +11,17 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
################################ Main script ################################
-builder_describe "Builds KMW's esbuild-oriented common configuration & tooling" \
+builder_describe "esbuild style bundling tooling for web and common/web" \
"clean" \
"configure" \
"build"
builder_describe_outputs \
configure /node_modules \
- build /web/src/tools/es-bundling/build/index.mjs
+ build /common/tools/es-bundling/build/index.mjs
builder_parse "$@"
builder_run_action configure node_select_version_and_npm_ci
builder_run_action clean rm -rf build/
-builder_run_action build tsc -b tsconfig.json
+builder_run_action build tsc -b
diff --git a/web/src/tools/es-bundling/src/classTreeshaker.mts b/common/tools/es-bundling/src/classTreeshaker.mts
similarity index 100%
rename from web/src/tools/es-bundling/src/classTreeshaker.mts
rename to common/tools/es-bundling/src/classTreeshaker.mts
diff --git a/web/src/tools/es-bundling/src/common-bundle.mts b/common/tools/es-bundling/src/common-bundle.mts
similarity index 100%
rename from web/src/tools/es-bundling/src/common-bundle.mts
rename to common/tools/es-bundling/src/common-bundle.mts
diff --git a/web/src/tools/es-bundling/src/configuration.mts b/common/tools/es-bundling/src/configuration.mts
similarity index 100%
rename from web/src/tools/es-bundling/src/configuration.mts
rename to common/tools/es-bundling/src/configuration.mts
diff --git a/web/src/tools/es-bundling/src/index.mts b/common/tools/es-bundling/src/index.mts
similarity index 100%
rename from web/src/tools/es-bundling/src/index.mts
rename to common/tools/es-bundling/src/index.mts
diff --git a/web/src/tools/es-bundling/src/tslibTreeshaking.mts b/common/tools/es-bundling/src/tslibTreeshaking.mts
similarity index 100%
rename from web/src/tools/es-bundling/src/tslibTreeshaking.mts
rename to common/tools/es-bundling/src/tslibTreeshaking.mts
diff --git a/web/src/tools/es-bundling/tsconfig.json b/common/tools/es-bundling/tsconfig.json
similarity index 75%
rename from web/src/tools/es-bundling/tsconfig.json
rename to common/tools/es-bundling/tsconfig.json
index 1c317aabec6..5870280035f 100644
--- a/web/src/tools/es-bundling/tsconfig.json
+++ b/common/tools/es-bundling/tsconfig.json
@@ -1,8 +1,7 @@
{
- "extends": "../../../../tsconfig.base.json",
+ "extends": "../../../tsconfig.base.json",
"compilerOptions": {
"allowSyntheticDefaultImports": true,
- "baseUrl": "./",
"outDir": "build/",
"tsBuildInfoFile": "build/tsconfig.tsbuildinfo",
"rootDir": "./src"
diff --git a/common/tools/hextobin/filesystem.ts b/common/tools/hextobin/filesystem.ts
new file mode 100644
index 00000000000..a9b11a4e27a
--- /dev/null
+++ b/common/tools/hextobin/filesystem.ts
@@ -0,0 +1,16 @@
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
+ * hextobin filesystem interfaces
+ */
+
+import * as fs from 'node:fs';
+import { hextobin, HexToBinOptions } from './main.js';
+
+export function hextobinFromFile(inputFilename: string, outputFilename?: string, options?: HexToBinOptions): Uint8Array {
+ const result = hextobin(fs.readFileSync(inputFilename, 'utf-8'), options);
+ if(result && outputFilename) {
+ fs.writeFileSync(outputFilename, result);
+ }
+ return result;
+}
diff --git a/common/tools/hextobin/hextobin.ts b/common/tools/hextobin/hextobin.ts
index 144217e1c48..9bca0a30a15 100644
--- a/common/tools/hextobin/hextobin.ts
+++ b/common/tools/hextobin/hextobin.ts
@@ -1,6 +1,13 @@
#!/usr/bin/env node
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
+ * hextobin - a tool to convert a structured hex text file format into a binary file.
+ * This is the command line wrapper.
+ */
+
import { Command } from 'commander';
-import hextobin from './index.js';
+import { hextobinFromFile } from './filesystem.js';
let inputFilename: string = "";
let outputFilename: string = "";
@@ -43,16 +50,15 @@ function exitDueToUsageError(message: string): never {
return process.exit(64); // SysExits.EX_USAGE
}
-hextobin(inputFilename, outputFilename, { silent: false })
- .then((buffer) => {
- if (buffer) {
- process.exit(0); // OK
- } else {
- console.error(`${program._name}: Failed.`);
- process.exit(1); // no buffer - some err.
- }
- }, (err) => {
- console.error(err);
+try {
+ if(hextobinFromFile(inputFilename, outputFilename, { silent: false })) {
+ process.exit(0); // OK
+ } else {
console.error(`${program._name}: Failed.`);
- process.exit(1);
- });
+ process.exit(1); // no buffer - some err.
+ }
+} catch(err) {
+ console.error(err);
+ console.error(`${program._name}: Failed.`);
+ process.exit(1);
+}
diff --git a/common/tools/hextobin/index.ts b/common/tools/hextobin/index.ts
index 168ccb05394..019cbca9f93 100644
--- a/common/tools/hextobin/index.ts
+++ b/common/tools/hextobin/index.ts
@@ -1,233 +1,6 @@
-import * as fs from 'node:fs';
-import * as rd from 'node:readline';
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ */
-export default async function hextobin(inputFilename: string, outputFilename?: string, options?: {silent?: boolean}): Promise {
-
- options = {...options};
- options.silent = !!options.silent;
-
- let reader = rd.createInterface(fs.createReadStream(inputFilename));
-
- type HexBlockRefType =
- 'sizeof' | // gets the size of a block, optionally divided by divisor: sizeof(block,divisor)
- 'offset' | // gets the offset of a block in bytes from start of file: offset(block)
- 'diff' | // gets the offset of a block in bytes from start of a base block: offset(baseBlock,block)
- 'index'; // gets the index of a block from a base block, optionally divided by divisor: index(baseBlock,block,divisor)
-
- interface HexBlockRef {
- type: HexBlockRefType;
- blockName: string;
- blockName2?: string; // used only by diff, index
- divisor?: number; // used only by sizeof, index
- offset: number; // actual byte offset in hex data (i.e. offset in string is * 2)
- };
-
- interface HexBlock {
- name: string;
- offset: number; // calculated during reconciliation phase
- hex: string;
- refs: HexBlockRef[];
- };
-
- let blocks: HexBlock[] = [];
-
- let currentLine = '';
- let currentLineNumber = 0;
-
- if(!await load()) {
- return null;
- }
-
- if(!reconciliation()) {
- return null;
- }
-
- return save();
-
- function reportError(message: string) {
- if(!options.silent) {
- console.error(`Invalid input file: ${message} on line #${currentLineNumber}: "${currentLine}"`);
- }
- }
-
- function currentBlock(): HexBlock {
- return blocks.length ? blocks[blocks.length-1] : null;
- }
-
- function parseToken(token: string): { command: string, parameters: string[] } {
- let m = /^([a-z]+)\((.+)\)$/.exec(token);
- if(!m) {
- if(!token.match(/^[a-fA-F0-9]{2}$/)) {
- return null;
- }
- // hex byte
- return { command: 'data', parameters: [token] };
- }
-
- // processing command
- return { command: m[1], parameters: m[2].split(',') };
- }
-
- function token(token: string) {
- const t = parseToken(token);
- if(!t) {
- reportError(`expected command or hex at token "${token}"`);
- return false;
- }
-
- if(t.command == 'block') {
- blocks.push({name: t.parameters[0], offset: 0, hex: '', refs: []});
- return true;
- }
-
- const b = currentBlock();
- if(!b) {
- reportError(`expected block() before data`);
- return false;
- }
-
- switch(t.command) {
- case 'offset':
- b.refs.push({type: 'offset', blockName: t.parameters[0], offset: b.hex.length / 2});
- b.hex += "_".repeat(8); // always 4 bytes, placeholder will be filled in during reconciliation phase
- break;
- case 'sizeof':
- // sizeof can take a second parameter, divisor
- {
- let divisor = t.parameters.length > 1 ? parseInt(t.parameters[1],10) : 1;
- b.refs.push({type: 'sizeof', blockName: t.parameters[0], divisor: divisor, offset: b.hex.length / 2});
- b.hex += "_".repeat(8); // always 4 bytes, placeholder will be filled in during reconciliation phase
- }
- break;
- case 'diff':
- // diff can also take a divisor, defaults to 1
- {
- const divisor = t.parameters.length > 2 ? parseInt(t.parameters[2],10) : 1;
- b.refs.push({type: 'diff', blockName: t.parameters[0], blockName2: t.parameters[1], divisor: divisor, offset: b.hex.length / 2});
- b.hex += "_".repeat(8); // always 4 bytes, placeholder will be filled in during reconciliation phase
- }
- break;
- case 'index':
- // index can take a third parameter, divisor
- {
- let divisor = t.parameters.length > 2 ? parseInt(t.parameters[2],10) : 1;
- b.refs.push({type: 'index', blockName: t.parameters[0], blockName2: t.parameters[1], divisor: divisor, offset: b.hex.length / 2});
- b.hex += "_".repeat(8); // always 4 bytes, placeholder will be filled in during reconciliation phase
- }
- break;
- case 'data':
- b.hex += t.parameters[0];
- break;
- default:
- reportError(`unknown command ${t.command}`);
- return false;
- }
- return true;
- }
-
- async function load() {
- for await (const l of reader) {
- // Error reporting variables
- currentLine = l;
- currentLineNumber++;
-
- const tokens = l.split(/[ \t]+/);
- for(let t of tokens) {
- if(t.startsWith('#')) {
- // comment, ignore all subsequent tokens to EOL
- break;
- }
- if(t == '') {
- continue;
- }
- if(!token(t)) {
- return false;
- }
- }
- }
- return true;
- }
-
- function dwordLeToHex(v: number): string {
- // hacky but who cares
- let h = v.toString(16);
- h = "0".repeat(8-h.length) + h;
- return h.substring(6,8) + h.substring(4, 6) + h.substring(2, 4) + h.substring(0, 2);
- }
-
- function fillBlockPlaceholder(block: HexBlock, offset: number, value: number): void {
- const hexvalue = dwordLeToHex(value);
- block.hex = block.hex.substring(0, offset * 2) + hexvalue + block.hex.substring(offset * 2 + 8);
- }
-
- function reconciliation(): boolean {
- let offset = 0;
-
- // calculate block sizes
- for(let b of blocks) {
- b.offset = offset;
- offset += b.hex.length / 2;
- }
-
- // reconcile block offsets and sizes
- for(let b of blocks) {
- for(let r of b.refs) {
- const v = blocks.find(q => q.name == r.blockName);
- if(!v) {
- reportError(`Could not find block ${r.blockName} when reconciling ${b.name}`);
- return false;
- }
- switch(r.type) {
- case 'diff':
- {
- const v2 = blocks.find(q => q.name == r.blockName2);
- if(!v2) {
- reportError(`Could not find block ${r.blockName2} when reconciling ${b.name}`);
- return false;
- }
- fillBlockPlaceholder(b, r.offset, (v2.offset - v.offset) / r.divisor);
- }
- break;
- case 'offset':
- fillBlockPlaceholder(b, r.offset, v.offset);
- break;
- case 'sizeof':
- fillBlockPlaceholder(b, r.offset, v.hex.length / 2 / r.divisor);
- break;
- case 'index':
- {
- const v2 = blocks.find(q => q.name == r.blockName2);
- if(!v2) {
- reportError(`Could not find block ${r.blockName2} when reconciling ${b.name}`);
- return false;
- }
- fillBlockPlaceholder(b, r.offset, (blocks.indexOf(v2) - blocks.indexOf(v)) / r.divisor);
- }
- break;
- default:
- reportError(`Invalid ref ${r.type}`);
- return false;
- }
- }
- }
- return true;
- }
-
- function save(): Uint8Array {
- let total = blocks.reduce((total: number, item: HexBlock) => Math.max(item.offset + item.hex.length/2, total), 0);
-
- if(!options.silent) {
- console.log(`${currentLineNumber} lines read; ${blocks.length} sections to write. Total file size = ${total} bytes.` );
- }
- let buffer = new Uint8Array(total);
- blocks.forEach(item => {
- let buf = Buffer.from(item.hex, 'hex');
- buffer.set(buf, item.offset);
- });
-
- if(outputFilename) {
- fs.writeFileSync(outputFilename, buffer);
- }
- return buffer;
- }
-}
+export { hextobin, HexToBinOptions } from './main.js';
+export { hextobinFromFile } from './filesystem.js';
diff --git a/common/tools/hextobin/main.ts b/common/tools/hextobin/main.ts
new file mode 100644
index 00000000000..f9ed20aa494
--- /dev/null
+++ b/common/tools/hextobin/main.ts
@@ -0,0 +1,267 @@
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
+ * hextobin - a tool to convert a structured hex text file format into a binary file.
+ */
+
+export interface HexToBinOptions {
+ silent?: boolean;
+ startBlock?: string;
+ endBlock?: string;
+};
+
+export function hextobin(source: string, options?: HexToBinOptions): Uint8Array {
+ options = {...options};
+ options.silent = !!options.silent;
+
+ const lines = source.split(/\n|\r\n/);
+ // let reader = rd.createInterface(fs.createReadStream(inputFilename));
+
+ type HexBlockRefType =
+ 'sizeof' | // gets the size of a block, optionally divided by divisor: sizeof(block,divisor)
+ 'offset' | // gets the offset of a block in bytes from start of file: offset(block)
+ 'diff' | // gets the offset of a block in bytes from start of a base block: offset(baseBlock,block)
+ 'index'; // gets the index of a block from a base block, optionally divided by divisor: index(baseBlock,block,divisor)
+
+ interface HexBlockRef {
+ type: HexBlockRefType;
+ blockName: string;
+ blockName2?: string; // used only by diff, index
+ divisor?: number; // used only by sizeof, index
+ offset: number; // actual byte offset in hex data (i.e. offset in string is * 2)
+ };
+
+ interface HexBlock {
+ name: string;
+ offset: number; // calculated during reconciliation phase
+ hex: string;
+ refs: HexBlockRef[];
+ };
+
+ let blocks: HexBlock[] = [];
+
+ let currentLine = '';
+ let currentLineNumber = 0;
+
+ if(!load()) {
+ return null;
+ }
+
+ if(!reconciliation()) {
+ return null;
+ }
+
+ filter();
+
+ return save();
+
+ /**
+ * remove blocks that are not in the section of interest, after reconciliation
+ * with other blocks. the remaining blocks may have binary references that
+ * don't make sense after filtering, so use judicially.
+ */
+ function filter() {
+ if(options.startBlock && options.endBlock) {
+ let offset = 0;
+ let filteredBlocks: HexBlock[] = [];
+ let include = false;
+ for(const b of blocks) {
+ if(b.name == options.startBlock) {
+ include = b.name != options.endBlock;
+ } else if(b.name == options.endBlock) {
+ include = false;
+ } else if(!include) {
+ continue;
+ }
+ filteredBlocks.push(b);
+ b.offset = offset;
+ offset += b.hex.length / 2;
+ }
+ blocks = filteredBlocks;
+ }
+ }
+
+ function reportError(message: string) {
+ if(!options.silent) {
+ console.error(`Invalid input file: ${message} on line #${currentLineNumber}: "${currentLine}"`);
+ }
+ }
+
+ function currentBlock(): HexBlock {
+ return blocks.length ? blocks[blocks.length-1] : null;
+ }
+
+ function parseToken(token: string): { command: string, parameters: string[] } {
+ let m = /^([a-z]+)\((.+)\)$/.exec(token);
+ if(!m) {
+ if(!token.match(/^[a-fA-F0-9]{2}$/)) {
+ return null;
+ }
+ // hex byte
+ return { command: 'data', parameters: [token] };
+ }
+
+ // processing command
+ return { command: m[1], parameters: m[2].split(',') };
+ }
+
+ function token(token: string) {
+ const t = parseToken(token);
+ if(!t) {
+ reportError(`expected command or hex at token "${token}"`);
+ return false;
+ }
+
+ if(t.command == 'block') {
+ blocks.push({name: t.parameters[0], offset: 0, hex: '', refs: []});
+ return true;
+ }
+
+ const b = currentBlock();
+ if(!b) {
+ reportError(`expected block() before data`);
+ return false;
+ }
+
+ switch(t.command) {
+ case 'offset':
+ b.refs.push({type: 'offset', blockName: t.parameters[0], offset: b.hex.length / 2});
+ b.hex += "_".repeat(8); // always 4 bytes, placeholder will be filled in during reconciliation phase
+ break;
+ case 'sizeof':
+ // sizeof can take a second parameter, divisor
+ {
+ let divisor = t.parameters.length > 1 ? parseInt(t.parameters[1],10) : 1;
+ b.refs.push({type: 'sizeof', blockName: t.parameters[0], divisor: divisor, offset: b.hex.length / 2});
+ b.hex += "_".repeat(8); // always 4 bytes, placeholder will be filled in during reconciliation phase
+ }
+ break;
+ case 'diff':
+ // diff can also take a divisor, defaults to 1
+ {
+ const divisor = t.parameters.length > 2 ? parseInt(t.parameters[2],10) : 1;
+ b.refs.push({type: 'diff', blockName: t.parameters[0], blockName2: t.parameters[1], divisor: divisor, offset: b.hex.length / 2});
+ b.hex += "_".repeat(8); // always 4 bytes, placeholder will be filled in during reconciliation phase
+ }
+ break;
+ case 'index':
+ // index can take a third parameter, divisor
+ {
+ let divisor = t.parameters.length > 2 ? parseInt(t.parameters[2],10) : 1;
+ b.refs.push({type: 'index', blockName: t.parameters[0], blockName2: t.parameters[1], divisor: divisor, offset: b.hex.length / 2});
+ b.hex += "_".repeat(8); // always 4 bytes, placeholder will be filled in during reconciliation phase
+ }
+ break;
+ case 'data':
+ b.hex += t.parameters[0];
+ break;
+ default:
+ reportError(`unknown command ${t.command}`);
+ return false;
+ }
+ return true;
+ }
+
+ function load() {
+ for (const l of lines) {
+ // Error reporting variables
+ currentLine = l;
+ currentLineNumber++;
+
+ const tokens = l.split(/[ \t]+/);
+ for(let t of tokens) {
+ if(t.startsWith('#')) {
+ // comment, ignore all subsequent tokens to EOL
+ break;
+ }
+ if(t == '') {
+ continue;
+ }
+ if(!token(t)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ function dwordLeToHex(v: number): string {
+ // hacky but who cares
+ let h = v.toString(16);
+ h = "0".repeat(8-h.length) + h;
+ return h.substring(6,8) + h.substring(4, 6) + h.substring(2, 4) + h.substring(0, 2);
+ }
+
+ function fillBlockPlaceholder(block: HexBlock, offset: number, value: number): void {
+ const hexvalue = dwordLeToHex(value);
+ block.hex = block.hex.substring(0, offset * 2) + hexvalue + block.hex.substring(offset * 2 + 8);
+ }
+
+ function reconciliation(): boolean {
+ let offset = 0;
+
+ // calculate block sizes
+ for(let b of blocks) {
+ b.offset = offset;
+ offset += b.hex.length / 2;
+ }
+
+ // reconcile block offsets and sizes
+ for(let b of blocks) {
+ for(let r of b.refs) {
+ const v = blocks.find(q => q.name == r.blockName);
+ if(!v) {
+ reportError(`Could not find block ${r.blockName} when reconciling ${b.name}`);
+ return false;
+ }
+ switch(r.type) {
+ case 'diff':
+ {
+ const v2 = blocks.find(q => q.name == r.blockName2);
+ if(!v2) {
+ reportError(`Could not find block ${r.blockName2} when reconciling ${b.name}`);
+ return false;
+ }
+ fillBlockPlaceholder(b, r.offset, (v2.offset - v.offset) / r.divisor);
+ }
+ break;
+ case 'offset':
+ fillBlockPlaceholder(b, r.offset, v.offset);
+ break;
+ case 'sizeof':
+ fillBlockPlaceholder(b, r.offset, v.hex.length / 2 / r.divisor);
+ break;
+ case 'index':
+ {
+ const v2 = blocks.find(q => q.name == r.blockName2);
+ if(!v2) {
+ reportError(`Could not find block ${r.blockName2} when reconciling ${b.name}`);
+ return false;
+ }
+ fillBlockPlaceholder(b, r.offset, (blocks.indexOf(v2) - blocks.indexOf(v)) / r.divisor);
+ }
+ break;
+ default:
+ reportError(`Invalid ref ${r.type}`);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ function save(): Uint8Array {
+ let total = blocks.reduce((total: number, item: HexBlock) => Math.max(item.offset + item.hex.length/2, total), 0);
+
+ if(!options.silent) {
+ console.log(`${currentLineNumber} lines read; ${blocks.length} sections to write. Total file size = ${total} bytes.` );
+ }
+ let buffer = new Uint8Array(total);
+ blocks.forEach(item => {
+ let buf = Buffer.from(item.hex, 'hex');
+ buffer.set(buf, item.offset);
+ });
+
+ return buffer;
+ }
+}
diff --git a/common/tools/hextobin/tsconfig.json b/common/tools/hextobin/tsconfig.json
index 55e3f66308e..a702e620392 100644
--- a/common/tools/hextobin/tsconfig.json
+++ b/common/tools/hextobin/tsconfig.json
@@ -5,5 +5,5 @@
"outDir": "build/"
},
"exclude": ["node_modules"],
- "files": ["index.ts", "hextobin.ts"]
+ "files": ["index.ts", "hextobin.ts", "filesystem.ts", "main.ts"]
}
diff --git a/common/web/sentry-manager/build.sh b/common/web/sentry-manager/build.sh
new file mode 100755
index 00000000000..4e725290e49
--- /dev/null
+++ b/common/web/sentry-manager/build.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+## START STANDARD BUILD SCRIPT INCLUDE
+# adjust relative paths as necessary
+THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
+. "${THIS_SCRIPT%/*}/../../../resources/build/builder-full.inc.sh"
+## END STANDARD BUILD SCRIPT INCLUDE
+
+. "${KEYMAN_ROOT}/resources/build/utils.inc.sh"
+. "${KEYMAN_ROOT}/resources/build/node.inc.sh"
+
+builder_describe "Sentry error reporting module for embedded Keyman Engine for Web" \
+ "@/common/tools/es-bundling" \
+ "@/common/web/keyman-version" \
+ clean configure build
+
+builder_describe_outputs \
+ configure /node_modules \
+ build /common/web/sentry-manager/build/lib/index.js
+
+builder_parse "$@"
+
+# ---------------------------------------------------------------------------------
+
+function do_build() {
+ tsc --build "./tsconfig.json"
+
+ node_es_bundle "${KEYMAN_ROOT}/common/web/sentry-manager/build/obj/src/index.js" \
+ --out "${KEYMAN_ROOT}/common/web/sentry-manager/build/lib/index.js" \
+ --sourceRoot "@keymanapp/keyman/common/web/sentry-manager/build/lib/"
+}
+
+builder_run_action clean rm -rf build/
+builder_run_action configure node_select_version_and_npm_ci
+builder_run_action build do_build
+
diff --git a/web/src/engine/sentry-manager/package.json b/common/web/sentry-manager/package.json
similarity index 100%
rename from web/src/engine/sentry-manager/package.json
rename to common/web/sentry-manager/package.json
diff --git a/web/src/engine/sentry-manager/src/index.ts b/common/web/sentry-manager/src/index.ts
similarity index 100%
rename from web/src/engine/sentry-manager/src/index.ts
rename to common/web/sentry-manager/src/index.ts
diff --git a/common/web/sentry-manager/tsconfig.json b/common/web/sentry-manager/tsconfig.json
new file mode 100644
index 00000000000..0967154d905
--- /dev/null
+++ b/common/web/sentry-manager/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "compilerOptions": {
+ "baseUrl": "./",
+ "lib": ["es6", "dom"],
+ "outDir": "./build/obj",
+ "rootDir": "./",
+ "tsBuildInfoFile": "./build/obj/tsconfig.tsbuildinfo",
+ },
+
+ "include": [
+ "src/*.ts"
+ ]
+}
\ No newline at end of file
diff --git a/common/web/types/build.sh b/common/web/types/build.sh
index 08419fa35df..fd29d697ca6 100755
--- a/common/web/types/build.sh
+++ b/common/web/types/build.sh
@@ -11,6 +11,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
builder_describe "Build Keyman common file types module" \
"@/core/include/ldml" \
+ "@/common/tools/hextobin" \
"@/common/web/keyman-version" \
"configure" \
"build" \
@@ -82,4 +83,4 @@ function do_configure() {
builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo ./src/schemas/ ./node_modules/ ./obj/
builder_run_action configure do_configure
builder_run_action build tsc --build
-builder_run_action test typescript_run_eslint_mocha_tests 60
+builder_run_action test typescript_run_eslint_mocha_tests
diff --git a/common/web/types/package.json b/common/web/types/package.json
index 2b2122f6828..2ca87ad0ab2 100644
--- a/common/web/types/package.json
+++ b/common/web/types/package.json
@@ -36,6 +36,8 @@
"restructure": "3.0.1"
},
"devDependencies": {
+ "@keymanapp/hextobin": "*",
+ "@types/node": "^20.4.1",
"ajv": "^8.12.0",
"ajv-cli": "^5.0.0",
"ajv-formats": "^2.1.1",
@@ -77,7 +79,6 @@
"src/schemas/*",
"tests/",
"src/keyboard-object.ts",
- "src/outputTarget.interface.ts",
"src/*.d.ts",
"src/main.ts",
"src/schema-validators.ts",
diff --git a/common/web/types/src/consts/character-constants.ts b/common/web/types/src/consts/character-constants.ts
new file mode 100644
index 00000000000..95d8b069ef2
--- /dev/null
+++ b/common/web/types/src/consts/character-constants.ts
@@ -0,0 +1,13 @@
+/*
+ * Keyman is copyright (C) SIL International. MIT License.
+ *
+ * Common character codes
+ */
+
+export enum CharacterConstant {
+ DOTTED_CIRCLE = 0x25CC, // ◌
+};
+
+export const CharacterConstantString = {
+ DOTTED_CIRCLE: String.fromCodePoint(CharacterConstant.DOTTED_CIRCLE),
+};
diff --git a/common/web/types/src/consts/modifier-key-constants.ts b/common/web/types/src/consts/modifier-key-constants.ts
index 245cbf70f37..cfa8f03b016 100644
--- a/common/web/types/src/consts/modifier-key-constants.ts
+++ b/common/web/types/src/consts/modifier-key-constants.ts
@@ -4,9 +4,16 @@
* Modifier key bit-flags
*/
+import { VisualKeyboardShiftState } from "../kvk/visual-keyboard.js";
+
+/**
+ * This type is declared as a `const` rather than as an `enum` for
+ * historical reasons. Use `ModifierKeyConstant` instead where possible.
+ */
export const ModifierKeyConstants = {
- // Define Keyman Developer modifier bit-flags (exposed for use by other modules)
- // Compare against /common/include/kmx_file.h. CTRL+F "#define LCTRLFLAG" to find the secton.
+ // Define Keyman Developer modifier bit-flags (exposed for use by other
+ // modules) Compare against /common/include/kmx_file.h. CTRL+F "#define
+ // LCTRLFLAG" to find the section.
LCTRLFLAG: 0x0001, // Left Control flag
RCTRLFLAG: 0x0002, // Right Control flag
LALTFLAG: 0x0004, // Left Alt flag
@@ -30,4 +37,194 @@ export const ModifierKeyConstants = {
// Note: OTHER_MODIFIER = 0x10000, used by KMX+ for the
// other modifier flag in layers, > 16 bit so not available here.
// See keys_mod_other in keyman_core_ldml.ts
+};
+
+/**
+ * Defines the standard modifier key flags used by Keyman. Note that some keys
+ * are chiral, and toggle key state is ignored if neither the __FLAG nor the
+ * corresponding NOT__FLAG are set.
+ */
+export enum ModifierKeyConstant {
+ NO_MODIFIER = 0,
+ LCTRLFLAG = 0x0001, // Left Control flag
+ RCTRLFLAG = 0x0002, // Right Control flag
+ LALTFLAG = 0x0004, // Left Alt flag
+ RALTFLAG = 0x0008, // Right Alt flag
+ K_SHIFTFLAG = 0x0010, // Either shift flag
+ K_CTRLFLAG = 0x0020, // Either ctrl flag
+ K_ALTFLAG = 0x0040, // Either alt flag
+ K_METAFLAG = 0x0080, // Either Meta-key flag (tentative). Not usable in keyboard rules;
+ // Used internally (currently, only by KMW) to ensure Meta-key
+ // shortcuts safely bypass rules
+ // Meta key = Command key on macOS, Windows key on Windows/Linux
+ CAPITALFLAG = 0x0100, // Caps lock on
+ NOTCAPITALFLAG = 0x0200, // Caps lock NOT on
+ NUMLOCKFLAG = 0x0400, // Num lock on
+ NOTNUMLOCKFLAG = 0x0800, // Num lock NOT on
+ SCROLLFLAG = 0x1000, // Scroll lock on
+ NOTSCROLLFLAG = 0x2000, // Scroll lock NOT on
+ ISVIRTUALKEY = 0x4000, // It is a Virtual Key Sequence
+ VIRTUALCHARKEY = 0x8000, // Keyman 6.0: Virtual Key Cap Sequence NOT YET
+};
+
+export const LDML_MODIFIER_TO_KVK_MODIFIER = /* @__PURE__ */ (() => {
+ const m = new Map();
+ m.set(ModifierKeyConstants.LCTRLFLAG, VisualKeyboardShiftState.KVKS_LCTRL);
+ m.set(ModifierKeyConstants.RCTRLFLAG, VisualKeyboardShiftState.KVKS_RCTRL);
+ m.set(ModifierKeyConstants.LALTFLAG, VisualKeyboardShiftState.KVKS_LALT);
+ m.set(ModifierKeyConstants.RALTFLAG, VisualKeyboardShiftState.KVKS_RALT);
+ m.set(ModifierKeyConstants.K_SHIFTFLAG, VisualKeyboardShiftState.KVKS_SHIFT);
+ m.set(ModifierKeyConstants.K_CTRLFLAG, VisualKeyboardShiftState.KVKS_CTRL);
+ m.set(ModifierKeyConstants.K_ALTFLAG, VisualKeyboardShiftState.KVKS_ALT);
+ return m;
+})();
+
+export const KVK_MODIFIER_TO_LDML_MODIFIER = /* @__PURE__ */ (() => {
+ const m = new Map();
+ m.set(VisualKeyboardShiftState.KVKS_LCTRL, ModifierKeyConstants.LCTRLFLAG);
+ m.set(VisualKeyboardShiftState.KVKS_RCTRL, ModifierKeyConstants.RCTRLFLAG);
+ m.set(VisualKeyboardShiftState.KVKS_LALT, ModifierKeyConstants.LALTFLAG);
+ m.set(VisualKeyboardShiftState.KVKS_RALT, ModifierKeyConstants.RALTFLAG);
+ m.set(VisualKeyboardShiftState.KVKS_SHIFT, ModifierKeyConstants.K_SHIFTFLAG);
+ m.set(VisualKeyboardShiftState.KVKS_CTRL, ModifierKeyConstants.K_CTRLFLAG);
+ m.set(VisualKeyboardShiftState.KVKS_ALT, ModifierKeyConstants.K_ALTFLAG);
+ return m;
+})();
+
+export function translateLdmlModifiersToVisualKeyboardShift(modifiers: ModifierKeyConstant): VisualKeyboardShiftState {
+ if(modifiers == ModifierKeyConstant.NO_MODIFIER) {
+ return VisualKeyboardShiftState.KVKS_NORMAL;
+ }
+
+ if(modifiers &
+ (ModifierKeyConstant.CAPITALFLAG | ModifierKeyConstant.NUMLOCKFLAG | ModifierKeyConstant.SCROLLFLAG)
+ ) {
+ // Caps/Num/Scroll are not supported in .kvk, in combination or alone
+ return null;
+ }
+
+ let shift: VisualKeyboardShiftState = 0;
+
+ for(const mod of LDML_MODIFIER_TO_KVK_MODIFIER.keys()) {
+ if(modifiers & mod) {
+ shift |= LDML_MODIFIER_TO_KVK_MODIFIER.get(mod);
+ }
+ }
+
+ return shift;
+}
+
+export function translateVisualKeyboardShiftToLdmlModifiers(shift: VisualKeyboardShiftState): ModifierKeyConstant {
+ if(shift == VisualKeyboardShiftState.KVKS_NORMAL) {
+ return 0;
+ }
+
+ let mod = 0;
+ for(const state of KVK_MODIFIER_TO_LDML_MODIFIER.keys()) {
+ if(shift & state) {
+ mod |= KVK_MODIFIER_TO_LDML_MODIFIER.get(state);
+ }
+ }
+
+ return mod;
+}
+
+
+
+function VkShiftStateToKmxShiftState(ShiftState: number): number {
+
+ interface TVKToKMX {
+ VK: VisualKeyboardShiftState; KMX: ModifierKeyConstant;
+ }
+
+ const Map: TVKToKMX[] = [
+ {VK: VisualKeyboardShiftState.KVKS_SHIFT, KMX: ModifierKeyConstant.K_SHIFTFLAG},
+ {VK: VisualKeyboardShiftState.KVKS_CTRL, KMX: ModifierKeyConstant.K_CTRLFLAG},
+ {VK: VisualKeyboardShiftState.KVKS_ALT, KMX: ModifierKeyConstant.K_ALTFLAG},
+ {VK: VisualKeyboardShiftState.KVKS_LCTRL, KMX: ModifierKeyConstant.LCTRLFLAG},
+ {VK: VisualKeyboardShiftState.KVKS_RCTRL, KMX: ModifierKeyConstant.RCTRLFLAG},
+ {VK: VisualKeyboardShiftState.KVKS_LALT, KMX: ModifierKeyConstant.LALTFLAG},
+ {VK: VisualKeyboardShiftState.KVKS_RALT, KMX: ModifierKeyConstant.RALTFLAG}
+ ];
+
+ let result = 0;
+ for(let i = 0; i < Map.length; i++) {
+ if (ShiftState & Map[i].VK) {
+ result |= Map[i].KMX;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Convert a VK modifier combination bitmask to a hyphen-separated string of
+ * modifier names, e.g. for use in layer names and key identifiers. The name
+ * order matches the bit flag order from ModifierKeyConstant, not the bit flag
+ * order from VisualKeyboardShiftState, for historical reasons.
+ */
+export function visualKeyboardShiftToLayerName(shift: VisualKeyboardShiftState): string {
+
+ // index is ModifierKeyConstant, not VisualKeyboardShiftState
+ const modifierNames: string[] = [
+ 'leftctrl',
+ 'rightctrl',
+ 'leftalt',
+ 'rightalt',
+ 'shift',
+ 'ctrl',
+ 'alt'
+ ];
+
+ const mod = VkShiftStateToKmxShiftState(shift);
+ if(mod == 0) {
+ return 'default';
+ }
+
+ let result = '';
+ for(let i = 0; i < modifierNames.length; i++) {
+ if(mod & (1 << i)) {
+ result += modifierNames[i] + '-';
+ }
+ }
+ return result.substring(0, result.length - 1);
+}
+
+/**
+ * Get modifier key state from a hyphen-separated modifier string, such as
+ * found in layer ids or key "layer" properties. Ignores unrecognized
+ * components. Order of components is not significant.
+ *
+ * @param modifierString a modifier string such as 'ctrl-shift'
+ * @return Keyman modifier state bitmask
+ */
+export function modifierStringToState(modifierString: string): ModifierKeyConstant {
+ const map = new Map([
+ ['shift', ModifierKeyConstant.K_SHIFTFLAG],
+ ['leftctrl', ModifierKeyConstant.LCTRLFLAG],
+ ['rightctrl', ModifierKeyConstant.RCTRLFLAG],
+ ['ctrl', ModifierKeyConstant.K_CTRLFLAG],
+ ['leftalt', ModifierKeyConstant.LALTFLAG],
+ ['rightalt', ModifierKeyConstant.RALTFLAG],
+ ['alt', ModifierKeyConstant.K_ALTFLAG],
+ ['caps', ModifierKeyConstant.CAPITALFLAG],
+ // legacy mappings: these were deprecated when we introduced chiral
+ // modifiers in KeymanWeb in ~v14.0; typically these would have been used
+ // without other components, but for the purposes of this function, we can
+ // treat them as components without harm
+ ['altshift', ModifierKeyConstant.K_ALTFLAG | ModifierKeyConstant.K_SHIFTFLAG],
+ ['ctrlalt', ModifierKeyConstant.K_CTRLFLAG | ModifierKeyConstant.K_ALTFLAG],
+ ['ctrlaltshift', ModifierKeyConstant.K_CTRLFLAG | ModifierKeyConstant.K_ALTFLAG | ModifierKeyConstant.K_SHIFTFLAG],
+ ['ctrlshift', ModifierKeyConstant.K_CTRLFLAG | ModifierKeyConstant.K_SHIFTFLAG],
+ ]);
+
+ let modifier = 0;
+ const components = modifierString.split('-');
+ for(const component of components) {
+ if(map.has(component)) {
+ modifier |= map.get(component);
+ }
+ }
+
+ return modifier;
}
diff --git a/common/web/types/src/consts/virtual-key-constants.ts b/common/web/types/src/consts/virtual-key-constants.ts
index 7b083241bdc..ad1570fff2d 100644
--- a/common/web/types/src/consts/virtual-key-constants.ts
+++ b/common/web/types/src/consts/virtual-key-constants.ts
@@ -127,6 +127,10 @@ export const USVirtualKeyCodes = {
'k_?C1':193,
K_oDF:0xDF,
K_ODF:0xDF,
+
+ // Key codes > 50000 are special virtual key codes that are only used
+ // in touch layouts and should probably not be used elsewhere.
+ // See https://github.com/keymanapp/keyman/pull/15343#discussion_r2674949933
K_LOPT:50001,
K_ROPT:50002,
K_NUMERALS:50003,
@@ -141,10 +145,8 @@ export const USVirtualKeyCodes = {
K_TABFWD:50012
};
-const k = USVirtualKeyCodes;
-
/** Map a CLDR scancode to a US VKey ala USVirtualKeyCodes */
-export const CLDRScanToUSVirtualKeyCodes = {
+export const CLDRScanToUSVirtualKeyCodes = /* @__PURE__ */ ((k) => ({
0x02: k.K_1,
0x03: k.K_2,
0x04: k.K_3,
@@ -202,7 +204,7 @@ export const CLDRScanToUSVirtualKeyCodes = {
0x73: k.K_oC1,
0x7D: k.K_oE2, // << Same as 0x56; found on jis
-};
+}))(USVirtualKeyCodes);
export type KeyMap = number[][];
@@ -230,3 +232,27 @@ export function CLDRScanToVkey(scan: number, badScans?: Set): number {
}
}
+const USVirtualKeyCodeNames = new Map();
+
+function fillVirtualKeyNames() {
+ Object.keys(USVirtualKeyCodes).forEach(name => {
+ USVirtualKeyCodeNames.set((USVirtualKeyCodes)[name], name);
+ });
+
+ // These three keys have multiple definitions
+ USVirtualKeyCodeNames.set(USVirtualKeyCodes.K_oE2, 'K_oE2');
+ USVirtualKeyCodeNames.set(USVirtualKeyCodes.K_oC1, 'K_oC1');
+ USVirtualKeyCodeNames.set(USVirtualKeyCodes.K_oDF, 'K_oDF');
+}
+
+/**
+ * Get the defined name of a virtual key
+ * @param vk A defined Keyman virtual key
+ * @returns the name of the virtual key, or undefined if not found
+ */
+export function usVirtualKeyName(vk: number): string {
+ if(USVirtualKeyCodeNames.size == 0) {
+ fillVirtualKeyNames();
+ }
+ return USVirtualKeyCodeNames.get(vk);
+}
\ No newline at end of file
diff --git a/common/web/types/src/keyboard-object.ts b/common/web/types/src/keyboard-object.ts
index afd0029af0d..3ce697ef4b1 100644
--- a/common/web/types/src/keyboard-object.ts
+++ b/common/web/types/src/keyboard-object.ts
@@ -8,8 +8,8 @@ export type ComplexKeyboardStore = (string | { t: 'd', d: number } | { ['t']: 'b
// A stub for KeyEvent which is properly defined in KeymanWeb
type KeyEventStub = {};
-// A stub for OutputTarget which is properly defined in KeymanWeb
-type OutputTargetStub = {};
+// A stub for TextStore which is properly defined in KeymanWeb
+type TextStoreStub = {};
export interface EncodedVisualKeyboard {
/** Represents CSS font styling to use for VisualKeyboard text */
@@ -43,31 +43,31 @@ export type KeyboardObject = {
* group-start: the function triggering processing for the keyboard's
* "Unicode" start group, corresponding to `begin Unicode > use(_____)` in
* Keyman keyboard language.
- * @param outputTarget The context to which the keystroke applies
+ * @param textStore The context to which the keystroke applies
* @param keystroke The full, pre-processed keystroke triggering
* keyboard-rule application.
*/
- gs(outputTarget: OutputTargetStub, keystroke: KeyEventStub): boolean;
+ gs(textStore: TextStoreStub, keystroke: KeyEventStub): boolean;
/**
* group-newcontext: the function triggering processing for the keyboard's
* "NewContext" start group, corresponding to `begin NewContext > use(_____)`
* in Keyman keyboard language.
- * @param outputTarget The new context to be used with future keystrokes
+ * @param textStore The new context to be used with future keystrokes
* @param keystroke A 'null' `KeyEvent` providing current modifier + state information.
*/
- gn?(outputTarget: OutputTargetStub, keystroke: KeyEventStub): boolean;
+ gn?(textStore: TextStoreStub, keystroke: KeyEventStub): boolean;
/**
* group-postkeystroke: the function triggering processing for the keyboard's
* "PostKeystroke" start group, corresponding to `begin PostKeystroke >
* use(_____)` in Keyman keyboard language.
- * @param outputTarget The context altered by a recent keystroke. As a
+ * @param textStore The context altered by a recent keystroke. As a
* precondition, all changes due to `gs` / `begin Unicode` should already be
* applied.
* @param keystroke A 'null' `KeyEvent` providing current modifier + state information.
*/
- gpk?(outputTarget: OutputTargetStub, keystroke: KeyEventStub): boolean;
+ gpk?(textStore: TextStoreStub, keystroke: KeyEventStub): boolean;
/**
* Keyboard ID: the uniquely-identifying name for this keyboard. Includes the standard
@@ -175,6 +175,6 @@ export type KeyboardObject = {
* @param {number} _PData 1 or 0
* @returns
*/
- KNS?: (_PCommand: number, _PTarget: OutputTargetStub, _PData: number) => void;
+ KNS?: (_PCommand: number, _PTarget: TextStoreStub, _PData: number) => void;
} & Record<`s${number}`, string>
diff --git a/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts b/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts
index e24fd295c84..5f6e471e5b3 100644
--- a/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts
+++ b/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts
@@ -1,23 +1,26 @@
-//
-// .keyman-touch-layout JSON format
-//
-// Follows /common/schemas/keyman-touch-layout/keyman-touch-layout.spec.json for
-// reading and
-// /common/schemas/keyman-touch-layout/keyman-touch-layout.clean.spec.json for
-// writing
-//
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
+ * .keyman-touch-layout JSON format definitions
+ *
+ * Follows schemas in /common/schemas/keyman-touch-layout/, using
+ * keyman-touch-layout.spec.json for reading and
+ * keyman-touch-layout.clean.spec.json for writing
+ */
/**
* On screen keyboard description consisting of specific layouts for tablet, phone,
* and desktop. Despite its name, this format is used for both touch layouts and
* hardware-style layouts.
*/
-export interface TouchLayoutFile {
+export type TouchLayoutFile = {
tablet?: TouchLayoutPlatform;
phone?: TouchLayoutPlatform;
desktop?: TouchLayoutPlatform;
};
+export type TouchLayoutPlatformName = keyof TouchLayoutFile;
+
export type TouchLayoutFont = string;
export type TouchLayoutFontSize = string;
export type TouchLayoutDefaultHint = "none"|"dot"|"longpress"|"multitap"|"flick"|"flick-n"|"flick-ne"|"flick-e"|"flick-se"|"flick-s"|"flick-sw"|"flick-w"|"flick-nw";
diff --git a/common/web/types/src/kmx/kmx-plus/kmx-plus-file-reader.ts b/common/web/types/src/kmx/kmx-plus/kmx-plus-file-reader.ts
new file mode 100644
index 00000000000..6335b53e318
--- /dev/null
+++ b/common/web/types/src/kmx/kmx-plus/kmx-plus-file-reader.ts
@@ -0,0 +1,495 @@
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
+ * KMX+ file reader: load a KMX+ file into KMXPlus structures
+ */
+
+import * as r from 'restructure';
+import { KMXPlusVersion, SectionIdent, constants } from "@keymanapp/ldml-keyboard-constants";
+import { KeymanTypesError } from "../../util/errors.js";
+import { ListIndex } from '../../ldml-keyboard/string-list.js';
+import { UnicodeSet, UnicodeSetParser } from '../../ldml-keyboard/unicodeset-parser-api.js';
+import { ICOMP_PLUS_DISP_v17, ICOMP_PLUS_DISP_v19, ICOMP_PLUS_ELEM, ICOMP_PLUS_ELEM_ELEMENT, ICOMP_PLUS_KEYS, ICOMP_PLUS_LAYR_v17, ICOMP_PLUS_LAYR_v19, ICOMP_PLUS_LIST, ICOMP_PLUS_LOCA, ICOMP_PLUS_META, ICOMP_PLUS_SECT, ICOMP_PLUS_SectionHeader, ICOMP_PLUS_STRS, ICOMP_PLUS_TRAN, ICOMP_PLUS_USET, ICOMP_PLUS_VARS, KMXPlusFileFormat } from "./kmx-plus-file.js";
+import * as KMXPlus from "./kmx-plus.js";
+import { ElemElement, ElementString } from './element-string.js';
+import { KMXFile } from '../kmx.js';
+
+export class KMXPlusFileReaderError extends KeymanTypesError {
+ constructor(message?: string, options?: any /* ErrorOptions type not yet broadly available */) {
+ super(message, options);
+ this.name = this.constructor.name;
+ }
+}
+
+export const KMXPLUS_FILE_READER_ERROR = {
+ SOURCE_IS_REQUIRED: () => "source is required",
+ FILE_IS_TOO_SHORT: () => "file is too short",
+ UNRECOGNIZED_MAGIC: () => "header magic bytes should be 'sect' or 'sec2'",
+ VERSION_SHOULD_MATCH_CONSTRUCTOR: (o:{version: KMXPlusVersion, constructorVersion: KMXPlusVersion}) => `Expected version '${o.version}' to match constructor version '${o.constructorVersion}'`,
+ MISSING_SECT: () => "Missing 'sect' or 'sec2' section",
+ EXPECTED_SECT_OR_SEC2: () => "Expected 'sect' or 'sec2' section",
+ UNKNOWN_ELEMENT_TYPE: (o:{type:string}) => `Internal Error: Unknown element type 0x${o.type}`,
+ UNKNOWN_VAR_TYPE: (o:{type:number, id:number}) => `Unrecognized var type ${o.type} for ${o.id}`,
+ UNKNOWN_GROUP_TYPE: (o:{type:number}) => `Unrecognized group type ${o.type}`,
+ NOT_A_VALID_KMX_FILE: () => `Not a .kmx file: header does not contain FILEID_COMPILED`,
+ KMX_FILE_DOES_NOT_INCLUDE_KMXPLUS_SECTION: () => `.kmx file does not include a KMX+ section`,
+};
+
+export class KMXPlusFileReader {
+ private format: KMXPlusFileFormat;
+
+ constructor(private version?: KMXPlusVersion) {
+ if(this.version) {
+ this.format = new KMXPlusFileFormat(this.version);
+ }
+ }
+
+ /**
+ * Read KMX+ data from a whole KMX file.
+ * Throws if file is not a .kmx file or if the file does not contain a KMX+ section.
+ * Results may be undefined if file is not a completely valid KMX+ file.
+ * @param input A whole .kmx file
+ * @returns in-memory representation of the KMX+ data
+ */
+ public readFromKmx(input: Uint8Array): KMXPlus.KMXPlusData {
+ const kmx = new KMXFile();
+ if(input.length < KMXFile.COMP_KEYBOARD_SIZE + KMXFile.COMP_KEYBOARD_KMXPLUSINFO_SIZE) {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.FILE_IS_TOO_SHORT());
+ }
+ const header = kmx.COMP_KEYBOARD.fromBuffer(input);
+ if(header.dwIdentifier != KMXFile.FILEID_COMPILED) {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.NOT_A_VALID_KMX_FILE());
+ }
+ if((header.dwFlags & (KMXFile.KF_KMXPLUS | KMXFile.KF_KMXPLUSOSK)) == 0) {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.KMX_FILE_DOES_NOT_INCLUDE_KMXPLUS_SECTION());
+ }
+ const binaryKmxPlusHeader = kmx.COMP_KEYBOARD_KMXPLUSINFO.fromBuffer(input.slice(KMXFile.COMP_KEYBOARD_SIZE));
+ return this.read(input.slice(binaryKmxPlusHeader.dpKMXPlus, binaryKmxPlusHeader.dpKMXPlus + binaryKmxPlusHeader.dwKMXPlusSize));
+ }
+
+ /**
+ * Read the KMX+ data into memory. Results may be undefined if file is not a completely valid KMX+ data blob
+ * @param source KMX+ data blob starting at the sect/sec2 section -- does not include the KMX wrapper
+ * @returns in-memory representation of the KMX+ data
+ */
+ public read(source: Uint8Array): KMXPlus.KMXPlusData {
+ if(!source) {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.SOURCE_IS_REQUIRED());
+ }
+
+ const version = this.findVersionFromMagic(source);
+ if(!this.version) {
+ this.version = version;
+ this.format = new KMXPlusFileFormat(version);
+ } else if(version !== this.version) {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.VERSION_SHOULD_MATCH_CONSTRUCTOR({version, constructorVersion: this.version}));
+ }
+
+ const sect = this.format.COMP_PLUS_SECT.fromBuffer(source) as ICOMP_PLUS_SECT;
+ if(!sect) {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.MISSING_SECT());
+ }
+
+ if(sect.header.ident != constants.hex_section_id('sect') && sect.header.ident != constants.hex_section_id('sec2')) {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.EXPECTED_SECT_OR_SEC2());
+ }
+
+ if(source.length < sect.total) {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.FILE_IS_TOO_SHORT());
+ }
+
+ const kmx: KMXPlus.KMXPlusData = {
+ sect: {},
+ };
+
+ // strs, list, uset, elem are dependency sections, must be read in this order
+ this.readSection('strs', sect, source, kmx);
+ this.readSection('list', sect, source, kmx);
+ this.readSection('uset', sect, source, kmx);
+ this.readSection('elem', sect, source, kmx);
+ // read remaining sections in alphabetical order
+ this.readSection('bksp', sect, source, kmx);
+ this.readSection('disp', sect, source, kmx);
+ this.readSection('keys', sect, source, kmx);
+ this.readSection('layr', sect, source, kmx);
+ this.readSection('loca', sect, source, kmx);
+ this.readSection('meta', sect, source, kmx);
+ // no need to re-read sect/sec2
+ this.readSection('tran', sect, source, kmx);
+ this.readSection('vars', sect, source, kmx);
+
+ return kmx;
+ }
+
+ private sectionReaders: {[index in SectionIdent]: any} = {
+ bksp: this.readBkspSection,
+ disp: this.readDispSection,
+ elem: this.readElemSection,
+ keys: this.readKeysSection,
+ layr: this.readLayrSection,
+ list: this.readListSection,
+ loca: this.readLocaSection,
+ meta: this.readMetaSection,
+ sect: null, // this is handled directly by read()
+ strs: this.readStrsSection,
+ tran: this.readTranSection,
+ uset: this.readUsetSection,
+ vars: this.readVarsSection,
+ };
+
+ private readSection(identString: SectionIdent, sect: any, source: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const ident = constants.hex_section_id(identString);
+ for(let i = 0; i < sect.count; i++) {
+ if(sect.items[i].sect == ident) {
+ return this.readSectionData(source.slice(sect.items[i].offset), kmx);
+ }
+ }
+ return null;
+ }
+
+ private readSectionData(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const header = this.format.COMP_PLUS_SectionHeader.fromBuffer(sect) as ICOMP_PLUS_SectionHeader;
+ const ident = constants.str_section_id(header.ident) as SectionIdent;
+ const reader = this.sectionReaders[ident];
+ if(typeof reader != 'function') {
+ throw new Error(`Unsupported section ${ident}`);
+ }
+
+ kmx[ident] = reader.bind(this)(sect, kmx);
+ }
+
+ private readString(id: number, kmx: KMXPlus.KMXPlusData) {
+ return new KMXPlus.StrsItem(kmx.strs.strings[id].value);
+ }
+
+ private readBkspSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ return this._readTranSection(new KMXPlus.Bksp(), sect, kmx) as KMXPlus.Bksp;
+ }
+
+ private readDispSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const result = new KMXPlus.Disp();
+ if(this.version == KMXPlusVersion.Version17) {
+ const disp = this.format.COMP_PLUS_DISP_v17.fromBuffer(sect) as ICOMP_PLUS_DISP_v17;
+ result.baseCharacter = this.readString(disp.baseCharacter ?? 0, kmx);
+ for(const item of disp.items) {
+ const resultItem: KMXPlus.DispItem = {
+ to: this.readString(item.to, kmx),
+ display: this.readString(item.display, kmx),
+ flags: item.to ? 0 : KMXPlus.DispItemFlags.isId,
+ id: this.readString(item.id, kmx),
+ toId: null,
+ };
+ resultItem.toId = item.to ? resultItem.to : resultItem.id;
+ result.disps.push(resultItem);
+ }
+ } else {
+ const disp = this.format.COMP_PLUS_DISP_v19.fromBuffer(sect) as ICOMP_PLUS_DISP_v19;
+ result.baseCharacter = this.readString(disp.baseCharacter ?? 0, kmx);
+ for(const item of disp.items) {
+ const toId = this.readString(item.toId, kmx);
+ const resultItem: KMXPlus.DispItem = {
+ to: item.flags & KMXPlus.DispItemFlags.isId ? this.readString(0, kmx) : toId,
+ display: this.readString(item.display, kmx),
+ flags: item.flags,
+ id: item.flags & KMXPlus.DispItemFlags.isId ? toId : this.readString(0, kmx),
+ toId,
+ };
+ result.disps.push(resultItem);
+ }
+ }
+ return result;
+ }
+
+ private readElemSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const elem = this.format.COMP_PLUS_ELEM.fromBuffer(sect) as ICOMP_PLUS_ELEM;
+ const result = new KMXPlus.Elem({});
+ result.strings = []; // remove the default elem, as we'll be adding it on load
+ for(const string of elem.strings) {
+ let offset = string.offset;
+ const resultString = new ElementString();
+ for(let i = 0; i < string.length; i++) {
+ const element = this.format.COMP_PLUS_ELEM_ELEMENT.fromBuffer(sect.slice(offset, offset + constants.length_elem_item_element)) as ICOMP_PLUS_ELEM_ELEMENT;
+ const type = (element.flags & constants.elem_flags_type);
+ const resultElement = new ElemElement();
+ resultElement.flags = element.flags & constants.elem_flags_flags_mask;
+ resultElement.order = (element.flags & constants.elem_flags_order_mask) >> constants.elem_flags_order_bitshift;
+ resultElement.tertiary = (element.flags & constants.elem_flags_tertiary_mask) >> constants.elem_flags_tertiary_bitshift;
+ if (type === constants.elem_flags_type_char) {
+ resultElement.value = new KMXPlus.CharStrsItem(String.fromCodePoint(element.element));
+ } else if (type == constants.elem_flags_type_str) {
+ resultElement.value = this.readString(element.element, kmx);
+ } else if (type == constants.elem_flags_type_uset) {
+ resultElement.uset = kmx.uset.usets[element.element];
+ resultElement.value = kmx.strs.strings[0];
+ // TODO-EMBED-OSK-IN-KMX: loading usets is incomplete - see TODO-LDML
+ } else {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.UNKNOWN_ELEMENT_TYPE({type:type.toString(16)}));
+ }
+ resultString.push(resultElement);
+ offset += constants.length_elem_item_element;
+ }
+ result.strings.push(resultString);
+ }
+ return result;
+ }
+
+ private readKeysSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const keys = this.format.COMP_PLUS_KEYS.fromBuffer(sect) as ICOMP_PLUS_KEYS;
+ const result = new KMXPlus.Keys(kmx.strs);
+
+ result.flicks = []; // remove the default flick, as we'll be adding it on load
+ for(const flicks of keys.flicks) {
+ const resultFlicks = new KMXPlus.KeysFlicks(this.readString(flicks.id, kmx));
+ for(let i = flicks.flick; i < flicks.flick + flicks.count; i++) {
+ const flick = keys.flick[i];
+ const resultFlick = new KMXPlus.KeysFlick();
+ resultFlick.directions = kmx.list.lists[flick.directions];
+ resultFlick.keyId = this.readString(flick.to, kmx);
+ resultFlicks.flicks.push(resultFlick);
+ }
+ result.flicks.push(resultFlicks);
+ }
+
+ for(const key of keys.keys) {
+ const resultKey = new KMXPlus.KeysKeys();
+ resultKey.flags = key.flags;
+ resultKey.id = this.readString(key.id, kmx);
+ if(key.flags & KMXPlus.KeysKeysFlags.extend) {
+ resultKey.to = this.readString(key.to, kmx);
+ } else {
+ resultKey.to = new KMXPlus.StrsItem(String.fromCodePoint(key.to), key.to);
+ }
+ resultKey.flicks = result.flicks[key.flicks].id.value;
+ resultKey.longPress = kmx.list.lists[key.longPress];
+ resultKey.longPressDefault = this.readString(key.longPressDefault, kmx);
+ resultKey.multiTap = kmx.list.lists[key.multiTap];
+ resultKey.switch = this.readString(key.switch, kmx);
+ resultKey.width = key.width;
+ result.keys.push(resultKey);
+ }
+
+ for(const kmap of keys.kmap) {
+ const resultKmap = new KMXPlus.KeysKmap();
+ resultKmap.vkey = kmap.vkey;
+ resultKmap.mod = kmap.mod;
+ resultKmap.key = result.keys[kmap.key].id.value;
+ result.kmap.push(resultKmap);
+ }
+ return result;
+ }
+
+ private readLayrSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const result = new KMXPlus.Layr();
+ if(this.version == KMXPlusVersion.Version17) {
+ const layr = this.format.COMP_PLUS_LAYR_v17.fromBuffer(sect) as ICOMP_PLUS_LAYR_v17;
+ for(const form of layr.forms) {
+ const resultForm = new KMXPlus.LayrForm();
+ resultForm.baseLayout = this.readString(0, kmx);
+ resultForm.flags = 0;
+ resultForm.hardware = this.readString(form.hardware, kmx);
+ resultForm.minDeviceWidth = form.minDeviceWidth;
+ for(let i = 0; i < form.count; i++) {
+ const layer = layr.layers[form.layer + i];
+ const resultLayer = new KMXPlus.LayrEntry();
+ resultLayer.id = this.readString(layer.id, kmx);
+ resultLayer.mod = layer.mod;
+ for(let j = 0; j < layer.count; j++) {
+ const row = layr.rows[layer.row + j];
+ const resultRow = new KMXPlus.LayrRow();
+ for(let k = 0; k < row.count; k++) {
+ resultRow.keys.push(this.readString(layr.keys[row.key + k].key, kmx));
+ }
+ resultLayer.rows.push(resultRow);
+ }
+ resultForm.layers.push(resultLayer);
+ }
+ result.forms.push(resultForm);
+ }
+ } else {
+ const layr = this.format.COMP_PLUS_LAYR_v19.fromBuffer(sect) as ICOMP_PLUS_LAYR_v19;
+ for(const form of layr.forms) {
+ const resultForm = new KMXPlus.LayrForm();
+ resultForm.baseLayout = this.readString(form.baseLayout, kmx);
+ resultForm.flags = form.flags;
+ resultForm.hardware = this.readString(form.hardware, kmx);
+ resultForm.minDeviceWidth = form.minDeviceWidth;
+ for(let i = 0; i < form.count; i++) {
+ const layer = layr.layers[form.layer + i];
+ const resultLayer = new KMXPlus.LayrEntry();
+ resultLayer.id = this.readString(layer.id, kmx);
+ resultLayer.mod = layer.mod;
+ for(let j = 0; j < layer.count; j++) {
+ const row = layr.rows[layer.row + j];
+ const resultRow = new KMXPlus.LayrRow();
+ for(let k = 0; k < row.count; k++) {
+ resultRow.keys.push(this.readString(layr.keys[row.key + k].key, kmx));
+ }
+ resultLayer.rows.push(resultRow);
+ }
+ resultForm.layers.push(resultLayer);
+ }
+ result.forms.push(resultForm);
+ }
+ }
+ return result;
+ }
+
+ private readListSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const list = this.format.COMP_PLUS_LIST.fromBuffer(sect) as ICOMP_PLUS_LIST;
+ const result = new KMXPlus.List(kmx.strs);
+ result.lists = []; // remove the default list, as we'll be adding it on load
+ for(const listItem of list.lists) {
+ const resultList = new KMXPlus.ListItem();
+ for(let i = 0; i < listItem.count; i++) {
+ const item = new ListIndex(kmx.strs.strings[list.indices[listItem.index + i].str]);
+ resultList.push(item);
+ }
+ result.lists.push(resultList);
+ }
+ return result;
+ }
+
+ private readLocaSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const loca = this.format.COMP_PLUS_LOCA.fromBuffer(sect) as ICOMP_PLUS_LOCA;
+ const result = new KMXPlus.Loca();
+ for(const item of loca.items) {
+ result.locales.push(this.readString(item, kmx));
+ }
+ return result;
+ }
+
+ private readMetaSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const meta = this.format.COMP_PLUS_META.fromBuffer(sect) as ICOMP_PLUS_META;
+ const result = new KMXPlus.Meta();
+ result.author = this.readString(meta.author, kmx);
+ result.conform = this.readString(meta.conform, kmx);
+ result.indicator = this.readString(meta.indicator, kmx);
+ result.layout = this.readString(meta.layout, kmx);
+ result.name = this.readString(meta.name, kmx);
+ result.settings = meta.settings;
+ result.version = this.readString(meta.version, kmx);
+ return result;
+ }
+
+ private readStrsSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const strs = this.format.COMP_PLUS_STRS.fromBuffer(sect) as ICOMP_PLUS_STRS;
+ const result = new KMXPlus.Strs();
+ result.strings = []; // remove the default string, as we'll be adding it on load
+ const strReader = new r.String(null, 'utf16le');
+ for(const str of strs.items) {
+ const buffer = strReader.fromBuffer(sect.slice(str.offset, str.offset + str.length * 2));
+ result.strings.push(new KMXPlus.StrsItem(buffer));
+ }
+ return result;
+ }
+
+ private readTranSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ return this._readTranSection(new KMXPlus.Tran(), sect, kmx);
+ }
+
+ private _readTranSection(result: KMXPlus.Tran, sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const tran = this.format.COMP_PLUS_TRAN.fromBuffer(sect) as ICOMP_PLUS_TRAN;
+ for(const group of tran.groups) {
+ const resultGroup = new KMXPlus.TranGroup();
+ resultGroup.type = group.type;
+ if(group.type == constants.tran_group_type_transform) {
+ for(let i = group.index; i < group.index + group.count; i++) {
+ const transform = tran.transforms[i];
+ const resultTransform = new KMXPlus.TranTransform();
+ resultTransform.from = this.readString(transform.from, kmx);
+ resultTransform.to = this.readString(transform.to, kmx);
+ resultTransform.mapFrom = this.readString(transform.mapFrom, kmx);
+ resultTransform.mapTo = this.readString(transform.mapTo, kmx);
+ resultGroup.transforms.push(resultTransform);
+ }
+ } else if(group.type == constants.tran_group_type_reorder) {
+ for(let i = group.index; i < group.index + group.count; i++) {
+ const reorder = tran.reorders[i];
+ const resultReorder = new KMXPlus.TranReorder();
+ resultReorder.before = kmx.elem.strings[reorder.before];
+ resultReorder.elements = kmx.elem.strings[reorder.elements];
+ resultGroup.reorders.push(resultReorder);
+ }
+ } else {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.UNKNOWN_GROUP_TYPE(group));
+ }
+ result.groups.push(resultGroup);
+ }
+ return result;
+ }
+
+ private readUsetSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const uset = this.format.COMP_PLUS_USET.fromBuffer(sect) as ICOMP_PLUS_USET;
+ const result = new KMXPlus.Uset();
+ for(const item of uset.usets) {
+ const pattern = this.readString(item.pattern, kmx);
+ const unicodeSet: UnicodeSet = new UnicodeSet(pattern.value, uset.ranges.slice(item.range, item.range + item.count).map(r => [r.start, r.end]));
+ const resultUsetItem = new KMXPlus.UsetItem(unicodeSet, pattern);
+ result.usets.push(resultUsetItem);
+ }
+ return result;
+ }
+
+ private readVarsSection(sect: Uint8Array, kmx: KMXPlus.KMXPlusData) {
+ const vars = this.format.COMP_PLUS_VARS.fromBuffer(sect) as ICOMP_PLUS_VARS;
+ const result = new KMXPlus.Vars();
+ result.markers = kmx.list.lists[vars.markers];
+ for(const v of vars.varEntries) {
+ const id = this.readString(v.id, kmx);
+ const value = this.readString(v.value, kmx);
+ if(v.type == constants.vars_entry_type_string) {
+ const str = new KMXPlus.StringVarItem(id.value, value.value, kmx);
+ result.strings.push(str);
+ } else if(v.type == constants.vars_entry_type_set) {
+ const set = new KMXPlus.SetVarItem(id.value, value.value.split(' '), kmx);
+ result.sets.push(set);
+ } else if(v.type == constants.vars_entry_type_unicodeSet) {
+ // TODO-EMBED-OSK-IN-KMX: seems like we need to re-parse the unicode set
+ // on load -- data is not available see also
+ // /docs/file-formats/kmx-plus-file-format.md#L563
+ const usetparser: UnicodeSetParser = {
+ sizeUnicodeSet: (pattern: string, compileContext?: any): number => -1, // skip parsing
+ parseUnicodeSet: null
+ };
+ const uset = new KMXPlus.UnicodeSetItem(id.value, value.value, { ...kmx, usetparser }, usetparser);
+ uset.unicodeSet = null;
+ result.usets.push(uset);
+ } else {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.UNKNOWN_VAR_TYPE(v));
+ }
+ }
+ return result;
+ }
+
+ private findVersionFromMagic(source: Uint8Array) {
+ if(source.length < constants.length_sect) {
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.FILE_IS_TOO_SHORT());
+ }
+
+ if(source[0] == 0x73 && source[1] == 0x65 && source[2] == 0x63 && source[3] == 0x74) { // 'sect'
+ return KMXPlusVersion.Version17;
+ }
+
+ if(source[0] == 0x73 && source[1] == 0x65 && source[2] == 0x63 && source[3] == 0x32) { // 'sec2'
+ return KMXPlusVersion.Version19;
+ }
+
+ throw new KMXPlusFileReaderError(KMXPLUS_FILE_READER_ERROR.UNRECOGNIZED_MAGIC());
+ }
+
+ /** @internal */
+ public unitTestEndpoints = {
+ readBkspSection: this.readBkspSection.bind(this),
+ readDispSection: this.readDispSection.bind(this),
+ readElemSection: this.readElemSection.bind(this),
+ readKeysSection: this.readKeysSection.bind(this),
+ readLayrSection: this.readLayrSection.bind(this),
+ readListSection: this.readListSection.bind(this),
+ readLocaSection: this.readLocaSection.bind(this),
+ readMetaSection: this.readMetaSection.bind(this),
+ readStrsSection: this.readStrsSection.bind(this),
+ readTranSection: this.readTranSection.bind(this),
+ readUsetSection: this.readUsetSection.bind(this),
+ readVarsSection: this.readVarsSection.bind(this),
+ }
+}
diff --git a/common/web/types/src/kmx/kmx-plus/kmx-plus-file.ts b/common/web/types/src/kmx/kmx-plus/kmx-plus-file.ts
new file mode 100644
index 00000000000..0ccc14fb4fe
--- /dev/null
+++ b/common/web/types/src/kmx/kmx-plus/kmx-plus-file.ts
@@ -0,0 +1,729 @@
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
+ * Created by mcdurdin on 2025-10-08
+ *
+ * Binary file format Restructure structs for KMX+
+ */
+import * as KMX from '../kmx.js';
+import * as r from 'restructure';
+import KMXFile = KMX.KMXFile;
+import { KMXPlusVersion } from '@keymanapp/ldml-keyboard-constants';
+import { ModifierKeyConstant } from '../../consts/modifier-key-constants.js';
+
+/* interfaces that match the COMP_PLUS_* structs -- for type safety */
+
+export type IIDENT = number;
+export type ISTR_REF = number;
+export type ISTR_OR_CHAR32_OR_USET = number;
+export type ITABLE_REF = number;
+
+export interface ICOMP_PLUS_SectionHeader {
+ ident: IIDENT;
+ size: number;
+ version?: number;
+};
+
+// 'sect'
+
+export interface ICOMP_PLUS_SECT_ITEM {
+ sect: IIDENT;
+ offset: number;
+};
+
+export interface ICOMP_PLUS_SECT {
+ header: ICOMP_PLUS_SectionHeader;
+ total: number;
+ count: number;
+ items: ICOMP_PLUS_SECT_ITEM[];
+};
+
+// 'bksp' - see 'tran'
+
+// 'disp'
+
+export interface ICOMP_PLUS_DISP_ITEM_v17 {
+ to: ISTR_REF;
+ id: ISTR_REF;
+ display: ISTR_REF;
+};
+
+export interface ICOMP_PLUS_DISP_v17 {
+ header: ICOMP_PLUS_SectionHeader;
+ count: number;
+ baseCharacter: ISTR_REF;
+ items: ICOMP_PLUS_DISP_ITEM_v17[];
+};
+
+export interface ICOMP_PLUS_DISP_ITEM_v19 {
+ toId: ISTR_REF;
+ display: ISTR_REF;
+ flags: number;
+};
+
+export interface ICOMP_PLUS_DISP_v19 {
+ header: ICOMP_PLUS_SectionHeader;
+ count: number;
+ baseCharacter: ISTR_REF;
+ items: ICOMP_PLUS_DISP_ITEM_v19[];
+};
+
+// 'elem'
+
+export interface ICOMP_PLUS_ELEM_ELEMENT {
+ element: ISTR_OR_CHAR32_OR_USET;
+ flags: number;
+};
+
+export interface ICOMP_PLUS_ELEM_STRING {
+ offset: number;
+ length: number;
+};
+
+export interface ICOMP_PLUS_ELEM {
+ header: ICOMP_PLUS_SectionHeader;
+ count: number;
+ strings: ICOMP_PLUS_ELEM_STRING[];
+ // + variable subtable: Element data (see KMXPlusBuilder.emitElements())
+};
+
+// 'keys'
+
+export type ISTR_OR_CHAR32 = number;
+export type ILIST_REF = number;
+
+export interface ICOMP_PLUS_KEYS_FLICK {
+ directions: ILIST_REF; // list
+ to: ISTR_OR_CHAR32; // str | codepoint
+};
+
+export interface ICOMP_PLUS_KEYS_FLICKS {
+ count: number;
+ flick: ITABLE_REF; // keys.flick index
+ id: ISTR_REF; // str
+};
+
+export interface ICOMP_PLUS_KEYS_KEY {
+ to: ISTR_OR_CHAR32; // str | codepoint
+ flags: number;
+ id: ISTR_REF; // str
+ switch: ISTR_REF; // str
+ width: number; // width*10 ( 1 = 0.1 keys)
+ longPress: ILIST_REF; // list index
+ longPressDefault: ISTR_REF; // str
+ multiTap: ILIST_REF; // list index
+ flicks: ITABLE_REF; // keys.flicks index
+};
+
+export interface ICOMP_PLUS_KEYS_KMAP {
+ vkey: number;
+ mod: ModifierKeyConstant;
+ key: ITABLE_REF; // keys.keys index
+};
+
+export interface ICOMP_PLUS_KEYS {
+ header: ICOMP_PLUS_SectionHeader;
+ keyCount: number;
+ flicksCount: number;
+ flickCount: number;
+ kmapCount: number;
+ keys: ICOMP_PLUS_KEYS_KEY[];
+ flicks: ICOMP_PLUS_KEYS_FLICKS[];
+ flick: ICOMP_PLUS_KEYS_FLICK[];
+ kmap: ICOMP_PLUS_KEYS_KMAP[];
+};
+
+export interface ICOMP_PLUS_LAYR_ENTRY {
+ id: ISTR_REF; // str
+ mod: ModifierKeyConstant; // bitfield
+ row: ITABLE_REF; // layr.rows index
+ count: number;
+};
+
+export interface ICOMP_PLUS_LAYR_KEY {
+ key: ISTR_REF; // str: key id
+};
+
+export interface ICOMP_PLUS_LAYR_FORM_v17 {
+ hardware: ISTR_REF; // str: hardware name
+ layer: ITABLE_REF; // layr.layers index
+ count: number;
+ minDeviceWidth: number; // integer: millimeters
+};
+
+export interface ICOMP_PLUS_LAYR_FORM_v19 {
+ hardware: ISTR_REF; // str: hardware name
+ layer: ITABLE_REF; // layr.layers index
+ count: number;
+ minDeviceWidth: number; // integer: millimeters
+ baseLayout: ISTR_REF; // v19: str: identifier for base layout (reserved)
+ flags: number; // v19: flags
+};
+
+export interface ICOMP_PLUS_LAYR_ROW {
+ key: ITABLE_REF; // layr.keys index
+ count: number;
+};
+
+export interface ICOMP_PLUS_LAYR_v17 {
+ header: ICOMP_PLUS_SectionHeader;
+ formCount: number;
+ layerCount: number;
+ rowCount: number;
+ keyCount: number;
+ forms: ICOMP_PLUS_LAYR_FORM_v17[];
+ layers: ICOMP_PLUS_LAYR_ENTRY[];
+ rows: ICOMP_PLUS_LAYR_ROW[];
+ keys: ICOMP_PLUS_LAYR_KEY[];
+};
+
+export interface ICOMP_PLUS_LAYR_v19 {
+ header: ICOMP_PLUS_SectionHeader;
+ formCount: number;
+ layerCount: number;
+ rowCount: number;
+ keyCount: number;
+ forms: ICOMP_PLUS_LAYR_FORM_v19[];
+ layers: ICOMP_PLUS_LAYR_ENTRY[];
+ rows: ICOMP_PLUS_LAYR_ROW[];
+ keys: ICOMP_PLUS_LAYR_KEY[];
+};
+
+// 'list'
+
+export interface ICOMP_PLUS_LIST_LIST {
+ index: ITABLE_REF; // list.indices index
+ count: number;
+};
+
+export interface ICOMP_PLUS_LIST_INDEX {
+ str: ISTR_REF; // str
+};
+
+export interface ICOMP_PLUS_LIST {
+ header: ICOMP_PLUS_SectionHeader;
+ listCount: number;
+ indexCount: number;
+ lists: ICOMP_PLUS_LIST_LIST[];
+ indices: ICOMP_PLUS_LIST_INDEX[];
+};
+
+// 'loca'
+
+export type ICOMP_PLUS_LOCA_ITEM = ISTR_REF; //str
+
+export interface ICOMP_PLUS_LOCA {
+ header: ICOMP_PLUS_SectionHeader;
+ count: number;
+ items: ICOMP_PLUS_LOCA_ITEM[];
+};
+
+// 'meta'
+
+export interface ICOMP_PLUS_META {
+ header: ICOMP_PLUS_SectionHeader;
+ author: ISTR_REF; //str
+ conform: ISTR_REF; //str
+ layout: ISTR_REF; //str
+ name: ISTR_REF; //str
+ indicator: ISTR_REF; //str
+ version: ISTR_REF; //str
+ settings: number; //new r.Bitfield(number, ['normalizationDisabled'])
+};
+
+// 'strs'
+
+export interface ICOMP_PLUS_STRS_ITEM {
+ // While we use length which is number of utf-16 code units excluding null terminator,
+ // we always write a null terminator, so we can get restructure to do that for us here
+ offset: number; // offset to utf16le string
+ length: number;
+};
+
+export interface ICOMP_PLUS_STRS {
+ header: ICOMP_PLUS_SectionHeader;
+ count: number;
+ items: ICOMP_PLUS_STRS_ITEM[];
+ // + variable subtable: String data (see KMXPlusBuilder.emitStrings())
+};
+
+// 'tran'
+
+export interface ICOMP_PLUS_TRAN_GROUP {
+ type: number; // type of group
+ count: number; // number of items
+ index: ITABLE_REF; // tran.transforms or tran.reorders index (per type)
+};
+
+export type IELEM_REF = number;
+
+export interface ICOMP_PLUS_TRAN_TRANSFORM {
+ from: ISTR_REF; //str
+ to: ISTR_REF; //str
+ mapFrom: IELEM_REF; //elem
+ mapTo: IELEM_REF; //elem
+};
+
+export interface ICOMP_PLUS_TRAN_REORDER {
+ elements: IELEM_REF; //elem
+ before: IELEM_REF; //elem
+};
+
+export interface ICOMP_PLUS_TRAN {
+ header: ICOMP_PLUS_SectionHeader;
+ groupCount: number;
+ transformCount: number;
+ reorderCount: number;
+ groups: ICOMP_PLUS_TRAN_GROUP[];
+ transforms: ICOMP_PLUS_TRAN_TRANSFORM[];
+ reorders: ICOMP_PLUS_TRAN_REORDER[];
+};
+
+// 'uset'
+
+export interface ICOMP_PLUS_USET_USET {
+ range: ITABLE_REF; // uset.ranges
+ count: number;
+ pattern: ISTR_REF; // str
+};
+
+export type ICHAR32 = number;
+
+export interface ICOMP_PLUS_USET_RANGE {
+ start: ICHAR32;
+ end: ICHAR32;
+};
+
+export interface ICOMP_PLUS_USET {
+ header: ICOMP_PLUS_SectionHeader;
+ usetCount: number;
+ rangeCount: number;
+ usets: ICOMP_PLUS_USET_USET[];
+ ranges: ICOMP_PLUS_USET_RANGE[];
+};
+
+// 'vars'
+
+export interface ICOMP_PLUS_VARS_ITEM {
+ type: number;
+ id: ISTR_REF; // str
+ value: ISTR_REF; // str
+ elem: IELEM_REF;
+};
+
+export interface ICOMP_PLUS_VARS {
+ header: ICOMP_PLUS_SectionHeader;
+ markers: ILIST_REF;
+ varCount: number;
+ varEntries: ICOMP_PLUS_VARS_ITEM[];
+};
+
+// Aliases
+
+export type ICOMP_PLUS_BKSP = ICOMP_PLUS_TRAN;
+
+/**
+ * Binary representation of KMX+ data, using Restructure. These structures
+ * should be directly used only by KMX+ file readers and writers; in general,
+ * most things should use the in-memory `KMXPlusData` structures in kmx-plus.ts.
+ */
+export class KMXPlusFileFormat extends KMXFile {
+
+ /* KMXPlus file structures */
+
+ public readonly COMP_PLUS_SECT_ITEM: any;
+ public readonly COMP_PLUS_SECT: any;
+
+ // COMP_PLUS_BKSP == COMP_PLUS_TRAN
+ public readonly COMP_PLUS_BKSP_ITEM: any;
+ public readonly COMP_PLUS_BKSP: any;
+
+ public readonly COMP_PLUS_DISP_ITEM_v17: any;
+ public readonly COMP_PLUS_DISP_v17: any;
+ public readonly COMP_PLUS_DISP_ITEM_v19: any;
+ public readonly COMP_PLUS_DISP_v19: any;
+
+ public readonly COMP_PLUS_ELEM_ELEMENT: any;
+ public readonly COMP_PLUS_ELEM_STRING: any;
+ public readonly COMP_PLUS_ELEM: any;
+
+ // COMP_PLUS_KEYS is now COMP_PLUS_KEYS_KMAP
+
+ public readonly COMP_PLUS_LAYR_ENTRY: any;
+ public readonly COMP_PLUS_LAYR_KEY: any;
+ public readonly COMP_PLUS_LAYR_FORM_v17: any;
+ public readonly COMP_PLUS_LAYR_FORM_v19: any;
+ public readonly COMP_PLUS_LAYR_ROW: any;
+ public readonly COMP_PLUS_LAYR_v17: any;
+ public readonly COMP_PLUS_LAYR_v19: any;
+
+ public readonly COMP_PLUS_KEYS_FLICK: any;
+ public readonly COMP_PLUS_KEYS_FLICKS: any;
+ public readonly COMP_PLUS_KEYS_KEY: any;
+ public readonly COMP_PLUS_KEYS_KMAP: any;
+ public readonly COMP_PLUS_KEYS: any;
+
+ public readonly COMP_PLUS_LIST_LIST: any;
+ public readonly COMP_PLUS_LIST_INDEX: any;
+ public readonly COMP_PLUS_LIST: any;
+
+ public readonly COMP_PLUS_LOCA_ITEM: any;
+ public readonly COMP_PLUS_LOCA: any;
+
+ public readonly COMP_PLUS_META: any;
+
+ public readonly COMP_PLUS_STRS_ITEM: any;
+ public readonly COMP_PLUS_STRS: any;
+
+ public readonly COMP_PLUS_TRAN_GROUP: any;
+ public readonly COMP_PLUS_TRAN_TRANSFORM: any;
+ public readonly COMP_PLUS_TRAN_REORDER: any;
+ public readonly COMP_PLUS_TRAN: any;
+
+ public readonly COMP_PLUS_USET_USET: any;
+ public readonly COMP_PLUS_USET_RANGE: any;
+ public readonly COMP_PLUS_USET: any;
+
+ public readonly COMP_PLUS_VKEY_ITEM: any;
+ public readonly COMP_PLUS_VKEY: any;
+
+ public readonly COMP_PLUS_VARS: any;
+ public readonly COMP_PLUS_VARS_ITEM: any;
+
+ public readonly COMP_PLUS_SectionHeader: any;
+
+ constructor(public readonly version: KMXPlusVersion) {
+ super();
+ // Binary-correct structures matching kmx_plus.h
+
+ if(![KMXPlusVersion.Version17, KMXPlusVersion.Version19].includes(version)) {
+ throw new Error(`Support for version ${version} not implemented`);
+ }
+
+ // helpers
+ const STR_REF = r.uint32le;
+ const ELEM_REF = r.uint32le;
+ const LIST_REF = r.uint32le;
+ const STR_OR_CHAR32 = r.uint32le;
+ const CHAR32 = r.uint32le;
+ const STR_OR_CHAR32_OR_USET = r.uint32le;
+ const IDENT = r.uint32le;
+
+ // Section header - version dependent
+
+ if(version == KMXPlusVersion.Version17) {
+ this.COMP_PLUS_SectionHeader = new r.Struct({
+ ident: IDENT,
+ size: r.uint32le,
+ });
+ } else {
+ this.COMP_PLUS_SectionHeader = new r.Struct({
+ ident: IDENT,
+ size: r.uint32le,
+ version: r.uint32le,
+ });
+ }
+
+ // 'sect'
+
+ this.COMP_PLUS_SECT_ITEM = new r.Struct({
+ sect: r.uint32le,
+ offset: r.uint32le //? new r.VoidPointer(r.uint32le, {type: 'global'})
+ });
+
+ this.COMP_PLUS_SECT = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ total: r.uint32le,
+ count: r.uint32le,
+ items: new r.Array(this.COMP_PLUS_SECT_ITEM, 'count')
+ });
+
+ // 'bksp' - see 'tran'
+
+ // 'disp'
+ this.COMP_PLUS_DISP_ITEM_v17 = new r.Struct({
+ to: STR_REF,
+ id: STR_REF,
+ display: STR_REF,
+ });
+
+ this.COMP_PLUS_DISP_v17 = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ count: r.uint32le,
+ baseCharacter: STR_REF,
+ items: new r.Array(this.COMP_PLUS_DISP_ITEM_v17, 'count'),
+ });
+
+ this.COMP_PLUS_DISP_ITEM_v19 = new r.Struct({
+ toId: STR_REF,
+ display: STR_REF,
+ flags: r.uint32le,
+ });
+
+ this.COMP_PLUS_DISP_v19 = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ count: r.uint32le,
+ baseCharacter: STR_REF,
+ items: new r.Array(this.COMP_PLUS_DISP_ITEM_v19, 'count'),
+ });
+
+ // 'elem'
+
+ this.COMP_PLUS_ELEM_ELEMENT = new r.Struct({
+ element: STR_OR_CHAR32_OR_USET,
+ flags: r.uint32le
+ });
+
+ this.COMP_PLUS_ELEM_STRING = new r.Struct({
+ offset: r.uint32le,
+ length: r.uint32le
+ });
+
+ this.COMP_PLUS_ELEM = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ count: r.uint32le,
+ strings: new r.Array(this.COMP_PLUS_ELEM_STRING, 'count')
+ // + variable subtable: Element data (see KMXPlusBuilder.emitElements())
+ });
+
+ // 'finl' - see 'tran'
+
+ // 'keys' - see 'keys.kmap'
+
+ // 'layr'
+
+ this.COMP_PLUS_LAYR_ENTRY = new r.Struct({
+ id: STR_REF, // str
+ mod: r.uint32le, // bitfield
+ row: r.uint32le, // index into rows
+ count: r.uint32le,
+ });
+
+ this.COMP_PLUS_LAYR_KEY = new r.Struct({
+ key: STR_REF, // str: key id
+ });
+
+ this.COMP_PLUS_LAYR_FORM_v17 = new r.Struct({
+ hardware: STR_REF, // str: hardware name
+ layer: r.uint32le, // index into layers
+ count: r.uint32le,
+ minDeviceWidth: r.uint32le, // integer: millimeters
+ });
+
+ this.COMP_PLUS_LAYR_FORM_v19 = new r.Struct({
+ hardware: STR_REF, // str: hardware name
+ layer: r.uint32le, // index into layers
+ count: r.uint32le,
+ minDeviceWidth: r.uint32le, // integer: millimeters
+ baseLayout: STR_REF, // v19: str: identifier for base layout (reserved)
+ flags: r.uint32le, // v19: flags
+ });
+
+ this.COMP_PLUS_LAYR_ROW = new r.Struct({
+ key: r.uint32le,
+ count: r.uint32le,
+ });
+
+ this.COMP_PLUS_LAYR_v17 = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ formCount: r.uint32le,
+ layerCount: r.uint32le,
+ rowCount: r.uint32le,
+ keyCount: r.uint32le,
+ forms: new r.Array(this.COMP_PLUS_LAYR_FORM_v17, 'formCount'),
+ layers: new r.Array(this.COMP_PLUS_LAYR_ENTRY, 'layerCount'),
+ rows: new r.Array(this.COMP_PLUS_LAYR_ROW, 'rowCount'),
+ keys: new r.Array(this.COMP_PLUS_LAYR_KEY, 'keyCount'),
+ });
+
+ this.COMP_PLUS_LAYR_v19 = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ formCount: r.uint32le,
+ layerCount: r.uint32le,
+ rowCount: r.uint32le,
+ keyCount: r.uint32le,
+ forms: new r.Array(this.COMP_PLUS_LAYR_FORM_v19, 'formCount'),
+ layers: new r.Array(this.COMP_PLUS_LAYR_ENTRY, 'layerCount'),
+ rows: new r.Array(this.COMP_PLUS_LAYR_ROW, 'rowCount'),
+ keys: new r.Array(this.COMP_PLUS_LAYR_KEY, 'keyCount'),
+ });
+
+ // 'keys'
+
+ this.COMP_PLUS_KEYS_FLICK = new r.Struct({
+ directions: LIST_REF, // list
+ to: STR_OR_CHAR32, // str | codepoint
+ });
+
+ this.COMP_PLUS_KEYS_FLICKS = new r.Struct({
+ count: r.uint32le,
+ flick: r.uint32le,
+ id: STR_REF, // str
+ });
+
+ this.COMP_PLUS_KEYS_KEY = new r.Struct({
+ to: STR_OR_CHAR32, // str | codepoint
+ flags: r.uint32le,
+ id: STR_REF, // str
+ switch: STR_REF, // str
+ width: r.uint32le, // width*10 ( 1 = 0.1 keys)
+ longPress: LIST_REF, // list index
+ longPressDefault: STR_REF, // str
+ multiTap: LIST_REF, // list index
+ flicks: r.uint32le, // index into flicks table
+ });
+
+ this.COMP_PLUS_KEYS_KMAP = new r.Struct({
+ vkey: r.uint32le,
+ mod: r.uint32le,
+ key: r.uint32le, // index into 'keys' subtable
+ });
+
+ this.COMP_PLUS_KEYS = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ keyCount: r.uint32le,
+ flicksCount: r.uint32le,
+ flickCount: r.uint32le,
+ kmapCount: r.uint32le,
+ keys: new r.Array(this.COMP_PLUS_KEYS_KEY, 'keyCount'),
+ flicks: new r.Array(this.COMP_PLUS_KEYS_FLICKS, 'flicksCount'),
+ flick: new r.Array(this.COMP_PLUS_KEYS_FLICK, 'flickCount'),
+ kmap: new r.Array(this.COMP_PLUS_KEYS_KMAP, 'kmapCount'),
+ });
+
+ // 'list'
+
+ this.COMP_PLUS_LIST_LIST = new r.Struct({
+ index: r.uint32le,
+ count: r.uint32le,
+ });
+
+ this.COMP_PLUS_LIST_INDEX = new r.Struct({
+ str: STR_REF, // str
+ });
+
+ this.COMP_PLUS_LIST = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ listCount: r.uint32le,
+ indexCount: r.uint32le,
+ lists: new r.Array(this.COMP_PLUS_LIST_LIST, 'listCount'),
+ indices: new r.Array(this.COMP_PLUS_LIST_INDEX, 'indexCount'),
+ });
+
+ // 'loca'
+
+ this.COMP_PLUS_LOCA_ITEM = r.uint32le; //str
+
+ this.COMP_PLUS_LOCA = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ count: r.uint32le,
+ items: new r.Array(this.COMP_PLUS_LOCA_ITEM, 'count')
+ });
+
+ // 'meta'
+
+ this.COMP_PLUS_META = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ author: STR_REF, //str
+ conform: STR_REF, //str
+ layout: STR_REF, //str
+ name: STR_REF, //str
+ indicator: STR_REF, //str
+ version: STR_REF, //str
+ settings: r.uint32le, //new r.Bitfield(r.uint32le, ['normalizationDisabled'])
+ });
+
+ // 'name' is gone
+
+ // 'ordr' now part of 'tran'
+
+ // 'strs'
+
+ this.COMP_PLUS_STRS_ITEM = new r.Struct({
+ // While we use length which is number of utf-16 code units excluding null terminator,
+ // we always write a null terminator, so we can get restructure to do that for us here
+ offset: r.uint32le, //? new r.Pointer(r.uint32le, new r.String(null, 'utf16le')),
+ length: r.uint32le
+ });
+
+ this.COMP_PLUS_STRS = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ count: r.uint32le,
+ items: new r.Array(this.COMP_PLUS_STRS_ITEM, 'count')
+ // + variable subtable: String data (see KMXPlusBuilder.emitStrings())
+ });
+
+ // 'tran'
+
+ this.COMP_PLUS_TRAN_GROUP = new r.Struct({
+ type: r.uint32le, //type of group
+ count: r.uint32le, //number of items
+ index: r.uint32le, //index into subtable
+ });
+
+ this.COMP_PLUS_TRAN_TRANSFORM = new r.Struct({
+ from: STR_REF, //str
+ to: STR_REF, //str
+ mapFrom: ELEM_REF, //elem
+ mapTo: ELEM_REF //elem
+ });
+
+ this.COMP_PLUS_TRAN_REORDER = new r.Struct({
+ elements: ELEM_REF, //elem
+ before: ELEM_REF, //elem
+ });
+
+ this.COMP_PLUS_TRAN = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ groupCount: r.uint32le,
+ transformCount: r.uint32le,
+ reorderCount: r.uint32le,
+ groups: new r.Array(this.COMP_PLUS_TRAN_GROUP, 'groupCount'),
+ transforms: new r.Array(this.COMP_PLUS_TRAN_TRANSFORM, 'transformCount'),
+ reorders: new r.Array(this.COMP_PLUS_TRAN_REORDER, 'reorderCount'),
+ });
+
+ // 'uset'
+
+ this.COMP_PLUS_USET_USET = new r.Struct({
+ range: r.uint32le,
+ count: r.uint32le,
+ pattern: STR_REF, // str
+ });
+
+ this.COMP_PLUS_USET_RANGE = new r.Struct({
+ start: CHAR32,
+ end: CHAR32,
+ });
+
+ this.COMP_PLUS_USET = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ usetCount: r.uint32le,
+ rangeCount: r.uint32le,
+ usets: new r.Array(this.COMP_PLUS_USET_USET, 'usetCount'),
+ ranges: new r.Array(this.COMP_PLUS_USET_RANGE, 'rangeCount'),
+ });
+
+ // 'vars'
+
+ this.COMP_PLUS_VARS_ITEM = new r.Struct({
+ type: r.uint32le,
+ id: STR_REF, // str
+ value: STR_REF, // str
+ elem: ELEM_REF,
+ });
+
+ this.COMP_PLUS_VARS = new r.Struct({
+ header: this.COMP_PLUS_SectionHeader,
+ markers: LIST_REF,
+ varCount: r.uint32le,
+ varEntries: new r.Array(this.COMP_PLUS_VARS_ITEM, 'varCount'),
+ });
+
+ // 'vkey' is removed
+
+ // Aliases
+
+ this.COMP_PLUS_BKSP = this.COMP_PLUS_TRAN;
+ }
+}
diff --git a/common/web/types/src/kmx/kmx-plus/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus/kmx-plus.ts
index 4fd5dcd6320..feb0992108e 100644
--- a/common/web/types/src/kmx/kmx-plus/kmx-plus.ts
+++ b/common/web/types/src/kmx/kmx-plus/kmx-plus.ts
@@ -1,24 +1,29 @@
-import { constants } from '@keymanapp/ldml-keyboard-constants';
-import * as r from 'restructure';
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
+ * KMX+ file format structures and helper functions
+ */
+import { constants, KMXPlusVersion, SectionIdent } from '@keymanapp/ldml-keyboard-constants';
import { ElementString } from './element-string.js';
import { ListItem } from '../../ldml-keyboard/string-list.js';
import * as util from '../../util/util.js';
-import * as KMX from '../kmx.js';
import { UnicodeSetParser, UnicodeSet } from '../../ldml-keyboard/unicodeset-parser-api.js';
import { VariableParser } from '../../ldml-keyboard/pattern-parser.js';
import { MarkerParser } from '../../ldml-keyboard/pattern-parser.js';
+import { ModifierKeyConstant } from '../../consts/modifier-key-constants.js';
import isOneChar = util.isOneChar;
import toOneChar = util.toOneChar;
import unescapeString = util.unescapeString;
import escapeStringForRegex = util.escapeStringForRegex;
-import KMXFile = KMX.KMXFile;
+import { KMXPlusFileFormat } from './kmx-plus-file.js';
// Implementation of file structures from /core/src/ldml/C7043_ldml.md
// Writer in kmx-builder.ts
// Reader in kmx-loader.ts
export class Section {
+ static dependencies: SectionIdent[] = [];
}
/**
@@ -43,6 +48,8 @@ export class Elem extends Section {
super();
this.strings.push(ElementString.fromStrings(sections, {}, '')); // C7043: null element string
}
+ static override dependencies: SectionIdent[] = ['strs', 'uset'];
+
/**
* @param source if a string array, does not get reinterpreted as UnicodeSet. This is used with vars, etc. Or pass `["str"]` for an explicit 1-element elem.
* If it is a string, will be interpreted per reorder element ruls.
@@ -65,6 +72,7 @@ export class Elem extends Section {
export class Loca extends Section {
locales: StrsItem[] = [];
+ static override dependencies: SectionIdent[] = ['strs'];
};
// 'meta'
@@ -83,6 +91,8 @@ export class Meta extends Section {
version: StrsItem; // semver version string, defaults to "0"
settings: KeyboardSettings;
+ static override dependencies: SectionIdent[] = ['strs'];
+
/** convenience for checking settings */
get normalizationDisabled() {
return this?.settings & KeyboardSettings.normalizationDisabled;
@@ -262,6 +272,8 @@ export class Vars extends Section {
sets: SetVarItem[] = [];
usets: UnicodeSetItem[] = [];
+ static override dependencies: SectionIdent[] = ['strs', 'list', 'uset', 'elem'];
+
/**
*
* @returns false if any invalid variables
@@ -371,13 +383,12 @@ export class Vars extends Section {
* Common base for variable sections
* See Variable
*/
-export class VarsItem extends Section {
+export class VarsItem {
id: StrsItem;
value: StrsItem;
compileContext?: any;
constructor(id: string, value: string, sections: DependencySections, compileContext?: any) {
- super();
this.id = sections.strs.allocString(id);
this.value = sections.strs.allocString(value, { unescape: true });
this.compileContext = compileContext;
@@ -389,6 +400,7 @@ export class VarsItem extends Section {
};
export class UnicodeSetItem extends VarsItem {
+ // TODO-EMBED-OSK-IN-KMX: second usetparser ref (outside `sections`) is never used, cleanup
constructor(id: string, value: string, sections: DependencySections, usetparser: UnicodeSetParser, compileContext?: any) {
super(id, value, sections, compileContext);
const needRanges = sections.usetparser.sizeUnicodeSet(value);
@@ -450,6 +462,7 @@ export class Tran extends Section {
get id() {
return constants.section.tran;
}
+ static override dependencies: SectionIdent[] = ['strs', 'list', 'uset', 'elem'];
};
export class UsetItem {
@@ -462,6 +475,7 @@ export class UsetItem {
export class Uset extends Section {
usets: UsetItem[] = [];
+ static override dependencies: SectionIdent[] = ['strs'];
allocUset(set: UnicodeSet, sections: DependencySections, compileContext?: any) : UsetItem {
// match the same pattern
let result = this.usets.find(s => set.pattern == s.uset.pattern);
@@ -480,27 +494,126 @@ export class Bksp extends Tran {
}
};
+export enum DispItemFlags {
+ isId = constants.disp_item_flags_is_id,
+ isSvg = constants.disp_item_flags_is_svg,
+
+ maskHint = constants.disp_item_flags_mask_hint,
+
+ hintPrimary = constants.disp_item_hint_primary << constants.disp_item_flags_shift_hint,
+ hintNW = constants.disp_item_hint_nw << constants.disp_item_flags_shift_hint,
+ hintN = constants.disp_item_hint_n << constants.disp_item_flags_shift_hint,
+ hintNE = constants.disp_item_hint_ne << constants.disp_item_flags_shift_hint,
+ hintW = constants.disp_item_hint_w << constants.disp_item_flags_shift_hint,
+ hintE = constants.disp_item_hint_e << constants.disp_item_flags_shift_hint,
+ hintSW = constants.disp_item_hint_sw << constants.disp_item_flags_shift_hint,
+ hintS = constants.disp_item_hint_s << constants.disp_item_flags_shift_hint,
+ hintSE = constants.disp_item_hint_se << constants.disp_item_flags_shift_hint,
+
+ maskKeyCapType = constants.disp_item_flags_mask_key_cap_type,
+
+ keyCapShift = constants.disp_key_cap_shift << constants.disp_item_flags_shift_key_cap_type,
+ keyCapEnter = constants.disp_key_cap_enter << constants.disp_item_flags_shift_key_cap_type,
+ keyCapTab = constants.disp_key_cap_tab << constants.disp_item_flags_shift_key_cap_type,
+ keyCapBksp = constants.disp_key_cap_bksp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapMenu = constants.disp_key_cap_menu << constants.disp_item_flags_shift_key_cap_type,
+ keyCapHide = constants.disp_key_cap_hide << constants.disp_item_flags_shift_key_cap_type,
+ keyCapAlt = constants.disp_key_cap_alt << constants.disp_item_flags_shift_key_cap_type,
+ keyCapCtrl = constants.disp_key_cap_ctrl << constants.disp_item_flags_shift_key_cap_type,
+ keyCapCaps = constants.disp_key_cap_caps << constants.disp_item_flags_shift_key_cap_type,
+ keyCapAbc_Upper = constants.disp_key_cap_abc_upper << constants.disp_item_flags_shift_key_cap_type,
+ keyCapAbc_Lower = constants.disp_key_cap_abc_lower << constants.disp_item_flags_shift_key_cap_type,
+ keyCap123 = constants.disp_key_cap_123 << constants.disp_item_flags_shift_key_cap_type,
+ keyCapSymbol = constants.disp_key_cap_symbol << constants.disp_item_flags_shift_key_cap_type,
+ keyCapCurrency = constants.disp_key_cap_currency << constants.disp_item_flags_shift_key_cap_type,
+ keyCapShifted = constants.disp_key_cap_shifted << constants.disp_item_flags_shift_key_cap_type,
+ keyCapAltgr = constants.disp_key_cap_altgr << constants.disp_item_flags_shift_key_cap_type,
+ keyCapTableft = constants.disp_key_cap_tableft << constants.disp_item_flags_shift_key_cap_type,
+ keyCapLalt = constants.disp_key_cap_lalt << constants.disp_item_flags_shift_key_cap_type,
+ keyCapRalt = constants.disp_key_cap_ralt << constants.disp_item_flags_shift_key_cap_type,
+ keyCapLctrl = constants.disp_key_cap_lctrl << constants.disp_item_flags_shift_key_cap_type,
+ keyCapRctrl = constants.disp_key_cap_rctrl << constants.disp_item_flags_shift_key_cap_type,
+ keyCapLaltctrl = constants.disp_key_cap_laltctrl << constants.disp_item_flags_shift_key_cap_type,
+ keyCapRaltctrl = constants.disp_key_cap_raltctrl << constants.disp_item_flags_shift_key_cap_type,
+ keyCapLaltctrlshift = constants.disp_key_cap_laltctrlshift << constants.disp_item_flags_shift_key_cap_type,
+ keyCapRaltctrlshift = constants.disp_key_cap_raltctrlshift << constants.disp_item_flags_shift_key_cap_type,
+ keyCapAltshift = constants.disp_key_cap_altshift << constants.disp_item_flags_shift_key_cap_type,
+ keyCapCtrlshift = constants.disp_key_cap_ctrlshift << constants.disp_item_flags_shift_key_cap_type,
+ keyCapAltctrlshift = constants.disp_key_cap_altctrlshift << constants.disp_item_flags_shift_key_cap_type,
+ keyCapLaltshift = constants.disp_key_cap_laltshift << constants.disp_item_flags_shift_key_cap_type,
+ keyCapRaltshift = constants.disp_key_cap_raltshift << constants.disp_item_flags_shift_key_cap_type,
+ keyCapLctrlshift = constants.disp_key_cap_lctrlshift << constants.disp_item_flags_shift_key_cap_type,
+ keyCapRctrlshift = constants.disp_key_cap_rctrlshift << constants.disp_item_flags_shift_key_cap_type,
+ keyCapLtrenter = constants.disp_key_cap_ltrenter << constants.disp_item_flags_shift_key_cap_type,
+ keyCapLtrbksp = constants.disp_key_cap_ltrbksp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapRtlenter = constants.disp_key_cap_rtlenter << constants.disp_item_flags_shift_key_cap_type,
+ keyCapRtlbksp = constants.disp_key_cap_rtlbksp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapShiftlock = constants.disp_key_cap_shiftlock << constants.disp_item_flags_shift_key_cap_type,
+ keyCapShiftedlock = constants.disp_key_cap_shiftedlock << constants.disp_item_flags_shift_key_cap_type,
+ keyCapZwnj = constants.disp_key_cap_zwnj << constants.disp_item_flags_shift_key_cap_type,
+ keyCapZwnjios = constants.disp_key_cap_zwnjios << constants.disp_item_flags_shift_key_cap_type,
+ keyCapZwnjandroid = constants.disp_key_cap_zwnjandroid << constants.disp_item_flags_shift_key_cap_type,
+ keyCapZwnjgeneric = constants.disp_key_cap_zwnjgeneric << constants.disp_item_flags_shift_key_cap_type,
+ keyCapSp = constants.disp_key_cap_sp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapNbsp = constants.disp_key_cap_nbsp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapNarnbsp = constants.disp_key_cap_narnbsp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapEnq = constants.disp_key_cap_enq << constants.disp_item_flags_shift_key_cap_type,
+ keyCapEmq = constants.disp_key_cap_emq << constants.disp_item_flags_shift_key_cap_type,
+ keyCapEnsp = constants.disp_key_cap_ensp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapEmsp = constants.disp_key_cap_emsp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapPunctsp = constants.disp_key_cap_punctsp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapThsp = constants.disp_key_cap_thsp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapHsp = constants.disp_key_cap_hsp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapZwsp = constants.disp_key_cap_zwsp << constants.disp_item_flags_shift_key_cap_type,
+ keyCapZwj = constants.disp_key_cap_zwj << constants.disp_item_flags_shift_key_cap_type,
+ keyCapWj = constants.disp_key_cap_wj << constants.disp_item_flags_shift_key_cap_type,
+ keyCapCgj = constants.disp_key_cap_cgj << constants.disp_item_flags_shift_key_cap_type,
+ keyCapLtrm = constants.disp_key_cap_ltrm << constants.disp_item_flags_shift_key_cap_type,
+ keyCapRtlm = constants.disp_key_cap_rtlm << constants.disp_item_flags_shift_key_cap_type,
+ keyCapSh = constants.disp_key_cap_sh << constants.disp_item_flags_shift_key_cap_type,
+ keyCapHtab = constants.disp_key_cap_htab << constants.disp_item_flags_shift_key_cap_type,
+}
+
// 'disp'
-export class DispItem {
- to: StrsItem;
- id: StrsItem;
+export interface DispItem {
+ to: StrsItem; // not used in v19
+ id: StrsItem; // not used in v19
display: StrsItem;
+ toId: StrsItem; // v19
+ flags: DispItemFlags; // v19
};
export class Disp extends Section {
baseCharacter: StrsItem;
disps: DispItem[] = [];
+ static override dependencies: SectionIdent[] = ['strs'];
};
// 'layr'
+export enum LayrFormFlags {
+ showBaseLayout = constants.layr_form_flags_show_base_layout,
+ chiralSeparate = constants.layr_form_flags_chiral_separate,
+};
+
+export enum LayrFormHardware {
+ touch = 'touch', // layr_form_hardware_touch
+ abnt2 = 'abnt2', // layr_form_hardware_abnt2
+ iso = 'iso', // layr_form_hardware_iso
+ jis = 'jis', // layr_form_hardware_jis
+ ks = 'ks', // layr_form_hardware_ks
+ us = 'us', // layr_form_hardware_us
+};
+
/**
* In-memory ``
*/
-export class LayrList {
+export class LayrForm {
hardware: StrsItem;
layers: LayrEntry[] = [];
minDeviceWidth: number; // millimeters
+ baseLayout: StrsItem; // v19
+ flags: LayrFormFlags; // v19
};
/**
@@ -508,7 +621,7 @@ export class LayrList {
*/
export class LayrEntry {
id: StrsItem;
- mod: number;
+ mod: ModifierKeyConstant;
rows: LayrRow[] = [];
};
@@ -520,11 +633,24 @@ export class LayrList {
};
export class Layr extends Section {
- lists: LayrList[] = [];
+ forms: LayrForm[] = [];
+ static override dependencies: SectionIdent[] = ['strs'];
+};
+
+export enum KeysKeysFlags {
+ /**
+ * 0 if to is a char, 1 if it is a string
+ */
+ extend = constants.keys_key_flags_extend,
+
+ /**
+ * 1 if the key is a gap
+ */
+ gap = constants.keys_key_flags_gap,
};
export class KeysKeys {
- flags: number;
+ flags: KeysKeysFlags;
flicks: string; // for in-memory only
id: StrsItem;
longPress: ListItem;
@@ -538,7 +664,7 @@ export class KeysKeys {
export class KeysKmap {
vkey: number;
- mod: number;
+ mod: ModifierKeyConstant;
key: string; // for in-memory only
};
@@ -567,6 +693,7 @@ export class Keys extends Section {
const nullFlicks = new KeysFlicks(strs.allocString(''));
this.flicks.push(nullFlicks); // C7043: null element string
}
+ static override dependencies: SectionIdent[] = ['strs', 'list'];
};
export class List extends Section {
@@ -608,13 +735,18 @@ export class List extends Section {
super();
this.lists.push(ListItem.fromStrings([], {}, { strs })); // C7043: null element string
}
+ static override dependencies: SectionIdent[] = ['strs'];
lists: ListItem[] = [];
};
export { ListItem as ListItem };
+/**
+ * In-memory representation of KMX+ data. See also `KMXPlusFileFormat` and
+ * `KMXPlusFile`.
+ */
export interface KMXPlusData {
- sect?: Strs; // sect is ignored in-memory
+ sect?: Sect; // sect is ignored in-memory
bksp?: Bksp;
disp?: Disp;
elem?: Elem; // elem is ignored in-memory
@@ -629,356 +761,31 @@ export interface KMXPlusData {
vars?: Vars;
};
-export class KMXPlusFile extends KMXFile {
-
- /* KMXPlus file structures */
-
- public readonly COMP_PLUS_SECT_ITEM: any;
- public readonly COMP_PLUS_SECT: any;
-
- // COMP_PLUS_BKSP == COMP_PLUS_TRAN
- public readonly COMP_PLUS_BKSP_ITEM: any;
- public readonly COMP_PLUS_BKSP: any;
-
- public readonly COMP_PLUS_DISP_ITEM: any;
- public readonly COMP_PLUS_DISP: any;
-
- public readonly COMP_PLUS_ELEM_ELEMENT: any;
- public readonly COMP_PLUS_ELEM_STRING: any;
- public readonly COMP_PLUS_ELEM: any;
-
- // COMP_PLUS_KEYS is now COMP_PLUS_KEYS_KMAP
-
- public readonly COMP_PLUS_LAYR_ENTRY: any;
- public readonly COMP_PLUS_LAYR_KEY: any;
- public readonly COMP_PLUS_LAYR_LIST: any;
- public readonly COMP_PLUS_LAYR_ROW: any;
- public readonly COMP_PLUS_LAYR: any;
-
- public readonly COMP_PLUS_KEYS_FLICK: any;
- public readonly COMP_PLUS_KEYS_FLICKS: any;
- public readonly COMP_PLUS_KEYS_KEY: any;
- public readonly COMP_PLUS_KEYS_KMAP: any;
- public readonly COMP_PLUS_KEYS: any;
-
- public readonly COMP_PLUS_LIST_LIST: any;
- public readonly COMP_PLUS_LIST_INDEX: any;
- public readonly COMP_PLUS_LIST: any;
-
- public readonly COMP_PLUS_LOCA_ITEM: any;
- public readonly COMP_PLUS_LOCA: any;
-
- public readonly COMP_PLUS_META: any;
-
- public readonly COMP_PLUS_STRS_ITEM: any;
- public readonly COMP_PLUS_STRS: any;
-
- public readonly COMP_PLUS_TRAN_GROUP: any;
- public readonly COMP_PLUS_TRAN_TRANSFORM: any;
- public readonly COMP_PLUS_TRAN_REORDER: any;
- public readonly COMP_PLUS_TRAN: any;
-
- public readonly COMP_PLUS_USET_USET: any;
- public readonly COMP_PLUS_USET_RANGE: any;
- public readonly COMP_PLUS_USET: any;
-
- public readonly COMP_PLUS_VKEY_ITEM: any;
- public readonly COMP_PLUS_VKEY: any;
-
- public readonly COMP_PLUS_VARS: any;
- public readonly COMP_PLUS_VARS_ITEM: any;
-
+export class KMXPlusFile extends KMXPlusFileFormat {
/* File in-memory data */
-
public kmxplus: KMXPlusData = { };
-
- constructor() {
- super();
- // Binary-correct structures matching kmx_plus.h
-
- // helpers
- const STR_REF = r.uint32le;
- const ELEM_REF = r.uint32le;
- const LIST_REF = r.uint32le;
- const STR_OR_CHAR32 = r.uint32le;
- const CHAR32 = r.uint32le;
- const STR_OR_CHAR32_OR_USET = r.uint32le;
- const IDENT = r.uint32le;
- // 'sect'
-
- this.COMP_PLUS_SECT_ITEM = new r.Struct({
- sect: r.uint32le,
- offset: r.uint32le //? new r.VoidPointer(r.uint32le, {type: 'global'})
- });
-
- this.COMP_PLUS_SECT = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- total: r.uint32le,
- count: r.uint32le,
- items: new r.Array(this.COMP_PLUS_SECT_ITEM, 'count')
- });
-
- // 'bksp' - see 'tran'
-
- // 'disp'
- this.COMP_PLUS_DISP_ITEM = new r.Struct({
- to: STR_REF,
- id: STR_REF,
- display: STR_REF,
- });
-
- this.COMP_PLUS_DISP = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- count: r.uint32le,
- baseCharacter: CHAR32,
- items: new r.Array(this.COMP_PLUS_DISP_ITEM, 'count'),
- });
-
- // 'elem'
-
- this.COMP_PLUS_ELEM_ELEMENT = new r.Struct({
- element: STR_OR_CHAR32_OR_USET,
- flags: r.uint32le
- });
-
- this.COMP_PLUS_ELEM_STRING = new r.Struct({
- offset: r.uint32le,
- length: r.uint32le
- });
-
- this.COMP_PLUS_ELEM = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- count: r.uint32le,
- strings: new r.Array(this.COMP_PLUS_ELEM_STRING, 'count')
- // + variable subtable: Element data (see KMXPlusBuilder.emitElements())
- });
-
- // 'finl' - see 'tran'
-
- // 'keys' - see 'keys.kmap'
-
- // 'layr'
-
- this.COMP_PLUS_LAYR_ENTRY = new r.Struct({
- id: r.uint32le, // str
- mod: r.uint32le, // bitfield
- row: r.uint32le, // index into rows
- count: r.uint32le,
- });
-
- this.COMP_PLUS_LAYR_KEY = new r.Struct({
- key: r.uint32le, // str: key id
- });
-
- this.COMP_PLUS_LAYR_LIST = new r.Struct({
- hardware: STR_REF, // str: hardware name
- layer: r.uint32le, // index into layers
- count: r.uint32le,
- minDeviceWidth: r.uint32le, // integer: millimeters
- });
-
- this.COMP_PLUS_LAYR_ROW = new r.Struct({
- key: r.uint32le,
- count: r.uint32le,
- });
-
- this.COMP_PLUS_LAYR = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- listCount: r.uint32le,
- layerCount: r.uint32le,
- rowCount: r.uint32le,
- keyCount: r.uint32le,
- lists: new r.Array(this.COMP_PLUS_LAYR_LIST, 'listCount'),
- layers: new r.Array(this.COMP_PLUS_LAYR_ENTRY, 'layerCount'),
- rows: new r.Array(this.COMP_PLUS_LAYR_ROW, 'rowCount'),
- keys: new r.Array(this.COMP_PLUS_LAYR_KEY, 'keyCount'),
- });
-
- this.COMP_PLUS_KEYS_FLICK = new r.Struct({
- directions: LIST_REF, // list
- to: STR_OR_CHAR32, // str | codepoint
- });
-
- this.COMP_PLUS_KEYS_FLICKS = new r.Struct({
- count: r.uint32le,
- flick: r.uint32le,
- id: STR_REF, // str
- });
-
- this.COMP_PLUS_KEYS_KEY = new r.Struct({
- to: STR_OR_CHAR32, // str | codepoint
- flags: r.uint32le,
- id: STR_REF, // str
- switch: STR_REF, // str
- width: r.uint32le, // width*10 ( 1 = 0.1 keys)
- longPress: LIST_REF, // list index
- longPressDefault: STR_REF, // str
- multiTap: LIST_REF, // list index
- flicks: r.uint32le, // index into flicks table
- });
-
- this.COMP_PLUS_KEYS_KMAP = new r.Struct({
- vkey: r.uint32le,
- mod: r.uint32le,
- key: r.uint32le, // index into 'keys' subtable
- });
-
- this.COMP_PLUS_KEYS = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- keyCount: r.uint32le,
- flicksCount: r.uint32le,
- flickCount: r.uint32le,
- kmapCount: r.uint32le,
- keys: new r.Array(this.COMP_PLUS_KEYS_KEY, 'keyCount'),
- flicks: new r.Array(this.COMP_PLUS_KEYS_FLICKS, 'flicksCount'),
- flick: new r.Array(this.COMP_PLUS_KEYS_FLICK, 'flickCount'),
- kmap: new r.Array(this.COMP_PLUS_KEYS_KMAP, 'kmapCount'),
- });
-
- // 'list'
-
- this.COMP_PLUS_LIST_LIST = new r.Struct({
- index: r.uint32le,
- count: r.uint32le,
- });
-
- this.COMP_PLUS_LIST_INDEX = new r.Struct({
- str: STR_REF, // str
- });
-
- this.COMP_PLUS_LIST = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- listCount: r.uint32le,
- indexCount: r.uint32le,
- lists: new r.Array(this.COMP_PLUS_LIST_LIST, 'listCount'),
- indices: new r.Array(this.COMP_PLUS_LIST_INDEX, 'indexCount'),
- });
-
- // 'loca'
-
- this.COMP_PLUS_LOCA_ITEM = r.uint32le; //str
-
- this.COMP_PLUS_LOCA = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- count: r.uint32le,
- items: new r.Array(this.COMP_PLUS_LOCA_ITEM, 'count')
- });
-
- // 'meta'
-
- this.COMP_PLUS_META = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- author: STR_REF, //str
- conform: STR_REF, //str
- layout: STR_REF, //str
- name: STR_REF, //str
- indicator: STR_REF, //str
- version: STR_REF, //str
- settings: r.uint32le, //new r.Bitfield(r.uint32le, ['normalizationDisabled'])
- });
-
- // 'name' is gone
-
- // 'ordr' now part of 'tran'
-
- // 'strs'
-
- this.COMP_PLUS_STRS_ITEM = new r.Struct({
- // While we use length which is number of utf-16 code units excluding null terminator,
- // we always write a null terminator, so we can get restructure to do that for us here
- offset: r.uint32le, //? new r.Pointer(r.uint32le, new r.String(null, 'utf16le')),
- length: r.uint32le
- });
-
- this.COMP_PLUS_STRS = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- count: r.uint32le,
- items: new r.Array(this.COMP_PLUS_STRS_ITEM, 'count')
- // + variable subtable: String data (see KMXPlusBuilder.emitStrings())
- });
-
- // 'tran'
-
- this.COMP_PLUS_TRAN_GROUP = new r.Struct({
- type: r.uint32le, //type of group
- count: r.uint32le, //number of items
- index: r.uint32le, //index into subtable
- });
-
- this.COMP_PLUS_TRAN_TRANSFORM = new r.Struct({
- from: STR_REF, //str
- to: STR_REF, //str
- mapFrom: ELEM_REF, //elem
- mapTo: ELEM_REF //elem
- });
-
- this.COMP_PLUS_TRAN_REORDER = new r.Struct({
- elements: ELEM_REF, //elem
- before: ELEM_REF, //elem
- });
-
- this.COMP_PLUS_TRAN = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- groupCount: r.uint32le,
- transformCount: r.uint32le,
- reorderCount: r.uint32le,
- groups: new r.Array(this.COMP_PLUS_TRAN_GROUP, 'groupCount'),
- transforms: new r.Array(this.COMP_PLUS_TRAN_TRANSFORM, 'transformCount'),
- reorders: new r.Array(this.COMP_PLUS_TRAN_REORDER, 'reorderCount'),
- });
-
- // 'uset'
- this.COMP_PLUS_USET_USET = new r.Struct({
- range: r.uint32le,
- count: r.uint32le,
- pattern: STR_REF, // str
- });
-
- this.COMP_PLUS_USET_RANGE = new r.Struct({
- start: CHAR32,
- end: CHAR32,
- });
-
- this.COMP_PLUS_USET = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- usetCount: r.uint32le,
- rangeCount: r.uint32le,
- usets: new r.Array(this.COMP_PLUS_USET_USET, 'usetCount'),
- ranges: new r.Array(this.COMP_PLUS_USET_RANGE, 'rangeCount'),
- });
-
- // 'vars'
-
- this.COMP_PLUS_VARS_ITEM = new r.Struct({
- type: r.uint32le,
- id: STR_REF, // str
- value: STR_REF, // str
- elem: ELEM_REF,
- });
-
- this.COMP_PLUS_VARS = new r.Struct({
- ident: IDENT,
- size: r.uint32le,
- markers: LIST_REF,
- varCount: r.uint32le,
- varEntries: new r.Array(this.COMP_PLUS_VARS_ITEM, 'varCount'),
- });
-
- // 'vkey' is removed
-
- // Aliases
-
- this.COMP_PLUS_BKSP = this.COMP_PLUS_TRAN;
+ static createEmptyMinimalKMXPlusFile(version: KMXPlusVersion): KMXPlusFile {
+ const kmx = new KMXPlusFile(version);
+ const strs = kmx.kmxplus.strs = new Strs();
+ kmx.kmxplus.layr = new Layr();
+ kmx.kmxplus.elem = new Elem(kmx.kmxplus);
+ kmx.kmxplus.disp = new Disp();
+ kmx.kmxplus.disp.baseCharacter = strs.allocString();
+ kmx.kmxplus.keys = new Keys(strs);
+ kmx.kmxplus.list = new List(strs);
+ kmx.kmxplus.loca = new Loca();
+
+ kmx.kmxplus.meta = new Meta();
+ kmx.kmxplus.meta.author = strs.allocString();
+ kmx.kmxplus.meta.conform = strs.allocString();
+ kmx.kmxplus.meta.indicator = strs.allocString();
+ kmx.kmxplus.meta.layout = strs.allocString();
+ kmx.kmxplus.meta.name = strs.allocString();
+ kmx.kmxplus.meta.settings = 0;
+ kmx.kmxplus.meta.version = strs.allocString();
+
+ // Are there other sections we need?
+
+ return kmx;
}
-}
+};
diff --git a/common/web/types/src/kmx/kmx.ts b/common/web/types/src/kmx/kmx.ts
index 4f53e29fde1..e5e7c2deff9 100644
--- a/common/web/types/src/kmx/kmx.ts
+++ b/common/web/types/src/kmx/kmx.ts
@@ -3,7 +3,8 @@ import { ModifierKeyConstants } from '../consts/modifier-key-constants.js';
/* Definitions from kmx_file.h. Must be kept in sync */
-// TODO: split kmx-file from kmx in-memory, similar to what I've done for kvk (keep restructure decl + BUILDER_ interfaces together)
+// TODO-embed-osk-in-kmx: split kmx-file from kmx in-memory, similar to what I've done for kvk (keep restructure decl + BUILDER_ interfaces together)
+// this will make it possible to use these declarations in KeymanWeb
// In memory representations of KMX structures
// kmx-builder will transform these to the corresponding COMP_xxxx
@@ -23,9 +24,46 @@ export enum KMX_Version {
VERSION_140 = 0x00000E00,
VERSION_150 = 0x00000F00,
VERSION_160 = 0x00001000,
- VERSION_170 = 0x00001100
+ VERSION_170 = 0x00001100,
+ VERSION_190 = 0x00001300,
};
+/**
+ * Convert a version string from 6.0 - current Keyman version into a
+ * KMX_Version value. Earlier versions are not supported
+ * @param version
+ * @returns null if not matched, otherwise a valid KMX_Version
+ */
+export function versionStringToKmxVersion(version: string): KMX_Version {
+ // We allow version strings to be 'x.y' or just 'x'
+ if(typeof version !== 'string') {
+ return null;
+ }
+ if(!/^\d+(\.0)?$/.test(version)) {
+ return null;
+ }
+
+ const major = parseInt(version, 10);
+ if(Number.isNaN(major)) {
+ return null;
+ }
+
+ // assuming a reasonable range for Keyman versions for now
+ if(major < 6 || major > 999) {
+ return null;
+ }
+
+ // Version number is 16 bit number with MINOR in lower 8 bits,
+ // MAJOR in upper 8 bits. In practice, we now only use MAJOR
+ // version for Keyman versions.
+ const num = major << 8;
+
+ if(Object.values(KMX_Version).includes(num)) {
+ return num;
+ }
+
+ return null;
+}
export class KEYBOARD {
fileVersion?: number; // dwFileVersion (TSS_FILEVERSION)
@@ -164,9 +202,10 @@ export class KMXFile {
public static readonly VERSION_150 = KMX_Version.VERSION_150;
public static readonly VERSION_160 = KMX_Version.VERSION_160;
public static readonly VERSION_170 = KMX_Version.VERSION_170;
+ public static readonly VERSION_190 = KMX_Version.VERSION_190;
public static readonly VERSION_MIN = this.VERSION_50;
- public static readonly VERSION_MAX = this.VERSION_170;
+ public static readonly VERSION_MAX = this.VERSION_190;
//
// Backspace types
@@ -337,8 +376,14 @@ export class KMXFile {
public static readonly KF_LOGICALLAYOUT = 0x0008;
public static readonly KF_AUTOMATICVERSION = 0x0010;
- // 16.0: Support for LDML Keyboards in KMXPlus file format
- public static readonly KF_KMXPLUS = 0x0020;
+ /** 16.0+: A `COMP_KEYBOARD_KMXPLUSINFO` structure is present immediately after `COMP_KEYBOARD` */
+ public static readonly KF_KMXPLUS = 0x0020;
+
+ /**
+ * 19.0+: The `COMP_KEYBOARD_KMXPLUSINFO` structure contains only OSK, and not a
+ * LDML keyboard; KF_KMXPLUS should not be set
+ */
+ public static readonly KF_KMXPLUSOSK = 0x0040;
public static readonly HK_ALT = 0x00010000;
public static readonly HK_CTRL = 0x00020000;
@@ -466,7 +511,7 @@ export class KMXFile {
dpGroupArray: r.uint32le, // 0024 [LPGROUP] address of first item in group array
StartGroup_ANSI: r.uint32le, // 0028 index of starting ANSI group
- StartGroup_Unicode: r.uint32le, // 0028 index of starting Unicode groups
+ StartGroup_Unicode: r.uint32le, // 002C index of starting Unicode groups
dwFlags: r.uint32le, // 0030 Flags for the keyboard file
diff --git a/common/web/types/src/main.ts b/common/web/types/src/main.ts
index 779af42ba57..30309a90d6a 100644
--- a/common/web/types/src/main.ts
+++ b/common/web/types/src/main.ts
@@ -1,3 +1,7 @@
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ */
+
export * as KMX from './kmx/kmx.js';
export { KmxFileReader, KmxFileReaderError } from './kmx/kmx-file-reader.js';
export * as KeymanTargets from './kmx/keyman-targets.js';
@@ -7,10 +11,21 @@ export { default as KvkFileReader } from './kvk/kvk-file-reader.js';
export { default as KvkFileWriter } from './kvk/kvk-file-writer.js';
export * as KvkFile from './kvk/kvk-file.js';
+export { KMXPlusFileReader } from './kmx/kmx-plus/kmx-plus-file-reader.js';
+export * as KMXPlusFileFormat from './kmx/kmx-plus/kmx-plus-file.js';
-export { USVirtualKeyCodes } from './consts/virtual-key-constants.js';
+export { USVirtualKeyCodes, usVirtualKeyName } from './consts/virtual-key-constants.js';
export * as Constants from './consts/virtual-key-constants.js';
-export { ModifierKeyConstants } from './consts/modifier-key-constants.js';
+export {
+ ModifierKeyConstant, ModifierKeyConstants, LDML_MODIFIER_TO_KVK_MODIFIER, KVK_MODIFIER_TO_LDML_MODIFIER,
+ translateLdmlModifiersToVisualKeyboardShift, translateVisualKeyboardShiftToLdmlModifiers,
+ visualKeyboardShiftToLayerName,
+ modifierStringToState,
+} from './consts/modifier-key-constants.js';
+export {
+ CharacterConstant,
+ CharacterConstantString,
+} from './consts/character-constants.js';
export * as TouchLayout from './keyman-touch-layout/keyman-touch-layout-file.js';
diff --git a/common/web/types/src/util/errors.ts b/common/web/types/src/util/errors.ts
index b4b35af32f4..1a5abf91610 100644
--- a/common/web/types/src/util/errors.ts
+++ b/common/web/types/src/util/errors.ts
@@ -1,6 +1,11 @@
-/**
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
* Base class for all common/web/types errors thrown
*/
export class KeymanTypesError extends Error {
-
+ constructor(message?: string, options?: any /* ErrorOptions type not yet broadly available */) {
+ super(message, options);
+ this.name = this.constructor.name;
+ }
}
\ No newline at end of file
diff --git a/common/web/types/tests/consts/modifier-key-constants.tests.ts b/common/web/types/tests/consts/modifier-key-constants.tests.ts
new file mode 100644
index 00000000000..87ce5c062d3
--- /dev/null
+++ b/common/web/types/tests/consts/modifier-key-constants.tests.ts
@@ -0,0 +1,31 @@
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ */
+
+import 'mocha';
+import { assert } from 'chai';
+import { ModifierKeyConstant, modifierStringToState } from '../../src/consts/modifier-key-constants.js';
+
+describe('modifierStringToState', function() {
+ it('should map modifiers correctly', function() {
+ assert.equal(modifierStringToState(''), 0);
+ assert.equal(modifierStringToState('shift'), ModifierKeyConstant.K_SHIFTFLAG);
+ assert.equal(modifierStringToState('ctrl'), ModifierKeyConstant.K_CTRLFLAG);
+ assert.equal(modifierStringToState('rightctrl'), ModifierKeyConstant.RCTRLFLAG);
+ assert.equal(modifierStringToState('ctrl-shift'), ModifierKeyConstant.K_CTRLFLAG | ModifierKeyConstant.K_SHIFTFLAG);
+ assert.equal(modifierStringToState('ctrl-shift-alt'), ModifierKeyConstant.K_CTRLFLAG | ModifierKeyConstant.K_SHIFTFLAG | ModifierKeyConstant.K_ALTFLAG);
+ assert.equal(modifierStringToState('ctrl-shift-alt-caps'), ModifierKeyConstant.K_CTRLFLAG | ModifierKeyConstant.K_SHIFTFLAG | ModifierKeyConstant.K_ALTFLAG | ModifierKeyConstant.CAPITALFLAG);
+ assert.equal(modifierStringToState('caps-alt-ctrl-shift'), ModifierKeyConstant.K_CTRLFLAG | ModifierKeyConstant.K_SHIFTFLAG | ModifierKeyConstant.K_ALTFLAG | ModifierKeyConstant.CAPITALFLAG);
+ });
+
+ it('should map legacy modifiers correctly', function() {
+ assert.equal(modifierStringToState('altshift'), ModifierKeyConstant.K_ALTFLAG | ModifierKeyConstant.K_SHIFTFLAG);
+ assert.equal(modifierStringToState('ctrlaltshift'), ModifierKeyConstant.K_CTRLFLAG | ModifierKeyConstant.K_SHIFTFLAG | ModifierKeyConstant.K_ALTFLAG);
+ });
+
+ it('should ignore unknown modifiers', function() {
+ assert.equal(modifierStringToState('ctrl-shift-none'), ModifierKeyConstant.K_CTRLFLAG | ModifierKeyConstant.K_SHIFTFLAG);
+ assert.equal(modifierStringToState('ctrlraltshift'), 0);
+ assert.equal(modifierStringToState('ncaps'), 0);
+ });
+});
diff --git a/common/web/types/tests/helpers/index.ts b/common/web/types/tests/helpers/index.ts
index 717b2e75191..bd3a1336cb2 100644
--- a/common/web/types/tests/helpers/index.ts
+++ b/common/web/types/tests/helpers/index.ts
@@ -2,7 +2,7 @@ import * as path from "path";
import { fileURLToPath } from "url";
/**
- * Builds a path to the fixture with the given path components.
+ * Builds a path to the common/web/types fixture with the given path components.
*
* e.g., makePathToFixture('basic.xml')
*
@@ -11,3 +11,16 @@ import { fileURLToPath } from "url";
export function makePathToFixture(...components: string[]): string {
return fileURLToPath(new URL(path.join('..', '..', '..', 'tests', 'fixtures', ...components), import.meta.url));
}
+
+/**
+ * Builds a path to the /common/test file with the given path components. Note
+ * that this links to the base of /common/test, not /common/test/fixtures,
+ * because the /common/test folder currently has a mix of paths.
+ *
+ * e.g., makePathToFixture('basic.xml')
+ *
+ * @param components One or more path components.
+ */
+export function makePathToCommonFixture(...components: string[]): string {
+ return fileURLToPath(new URL(path.join('..', '..', '..', '..', '..', 'test', ...components), import.meta.url));
+}
diff --git a/common/web/types/tests/kmx/kmx-file.tests.ts b/common/web/types/tests/kmx/kmx-file.tests.ts
index b50521427c6..ce6fafb56db 100644
--- a/common/web/types/tests/kmx/kmx-file.tests.ts
+++ b/common/web/types/tests/kmx/kmx-file.tests.ts
@@ -3,7 +3,7 @@ import 'mocha';
import { assert } from 'chai';
import { makePathToFixture } from '../helpers/index.js';
import { KmxFileReader } from "../../src/kmx/kmx-file-reader.js";
-import { KMXFile } from "../../src/kmx/kmx.js";
+import { KMX_Version, KMXFile, versionStringToKmxVersion } from "../../src/kmx/kmx.js";
describe('kmx-file-reader', function () {
it('should read a valid file', function() {
@@ -27,3 +27,42 @@ describe('kmx-file-reader', function () {
// TODO: add header, group, key tests once we have added support in KmxFileReader
});
});
+
+describe('versionStringToKmxVersion', function() {
+ [
+ // We only care about v6.0 and up these days
+ {s:'6.0', v:KMX_Version.VERSION_60},
+ {s:'6', v:KMX_Version.VERSION_60},
+ {s:'7.0', v:KMX_Version.VERSION_70},
+ {s:'7', v:KMX_Version.VERSION_70},
+ {s:'8.0', v:KMX_Version.VERSION_80},
+ {s:'8', v:KMX_Version.VERSION_80},
+ {s:'9.0', v:KMX_Version.VERSION_90},
+ {s:'9', v:KMX_Version.VERSION_90},
+ {s:'10.0', v:KMX_Version.VERSION_100},
+ {s:'10', v:KMX_Version.VERSION_100},
+ {s:'14.0', v:KMX_Version.VERSION_140},
+ {s:'14', v:KMX_Version.VERSION_140},
+ {s:'15.0', v:KMX_Version.VERSION_150},
+ {s:'15', v:KMX_Version.VERSION_150},
+ {s:'16.0', v:KMX_Version.VERSION_160},
+ {s:'16', v:KMX_Version.VERSION_160},
+ {s:'17.0', v:KMX_Version.VERSION_170},
+ {s:'17', v:KMX_Version.VERSION_170},
+ {s:'19.0', v:KMX_Version.VERSION_190},
+ {s:'19', v:KMX_Version.VERSION_190},
+ ].forEach(function(v) {
+ it(`should convert valid version string ${v.s}`, function() {
+ const actual = versionStringToKmxVersion(v.s);
+ assert.equal(actual, v.v);
+ });
+ });
+
+ ['zero','six','VERSION_60','1.0','-6','','5.0.1','19.0-alpha',null,undefined,]
+ .forEach(function(v) {
+ it(`should reject invalid version string '${v}'`, function() {
+ const actual = versionStringToKmxVersion(v);
+ assert.isNull(actual);
+ });
+ });
+});
\ No newline at end of file
diff --git a/common/web/types/tests/kmx/kmx-plus/kmx-plus-file-reader.tests.ts b/common/web/types/tests/kmx/kmx-plus/kmx-plus-file-reader.tests.ts
new file mode 100644
index 00000000000..f36b92d06ec
--- /dev/null
+++ b/common/web/types/tests/kmx/kmx-plus/kmx-plus-file-reader.tests.ts
@@ -0,0 +1,431 @@
+/*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
+ * KMX+ file reader unit tests
+ */
+
+import 'mocha';
+import { assert } from 'chai';
+import { hextobinFromFile } from '@keymanapp/hextobin';
+import { constants, KMXPlusVersion, SECTION_IDENTS, SectionIdent } from '@keymanapp/ldml-keyboard-constants';
+import { KMXPlus } from '../../../src/main.js';
+import { KMXPLUS_FILE_READER_ERROR, KMXPlusFileReader } from '../../../src/kmx/kmx-plus/kmx-plus-file-reader.js';
+import { makePathToCommonFixture } from '../../helpers/index.js';
+
+function sectToSectionClass(ident: SectionIdent) {
+ return ident[0].toUpperCase()+ident.slice(1);
+}
+
+function sectToSectionReader(ident: SectionIdent) {
+ return `read${sectToSectionClass(ident)}Section`;
+}
+
+describe('KMXPlusFileReader', function() {
+
+ // read
+
+ it('should throw when no data is provided', function() {
+ const reader = new KMXPlusFileReader();
+ assert.throws(() => reader.read(null), KMXPLUS_FILE_READER_ERROR.SOURCE_IS_REQUIRED());
+ });
+
+ it('should throw when the KMX+ section is too short', function() {
+ const reader = new KMXPlusFileReader();
+ const file = new Uint8Array([0,0,0]);
+ assert.throws(() => reader.read(file), KMXPLUS_FILE_READER_ERROR.FILE_IS_TOO_SHORT());
+ });
+
+ it('should throw when the KMX+ section has invalid magic', function() {
+ const reader = new KMXPlusFileReader();
+ const file = new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
+ assert.throws(() => reader.read(file), KMXPLUS_FILE_READER_ERROR.UNRECOGNIZED_MAGIC());
+ });
+
+ // readFromKmx
+
+ it('should throw when the .kmx file is too short', function() {
+ const reader = new KMXPlusFileReader();
+ const file = new Uint8Array([0,0,0,0,0,0,0,0]);
+ assert.throws(() => reader.readFromKmx(file), KMXPLUS_FILE_READER_ERROR.FILE_IS_TOO_SHORT());
+ });
+
+ it('should throw when the .kmx file has invalid magic', function() {
+ const reader = new KMXPlusFileReader();
+ const file = new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
+ assert.throws(() => reader.readFromKmx(file), KMXPLUS_FILE_READER_ERROR.NOT_A_VALID_KMX_FILE());
+ });
+
+ it('should throw when the .kmx file does not contain a KMX+ section', function() {
+ const reader = new KMXPlusFileReader();
+ const file = new Uint8Array([0x4B, 0x58, 0x54, 0x53, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
+ assert.throws(() => reader.readFromKmx(file), KMXPLUS_FILE_READER_ERROR.KMX_FILE_DOES_NOT_INCLUDE_KMXPLUS_SECTION());
+ });
+
+ // deeper tests
+
+ [[17, KMXPlusVersion.Version17], [19, KMXPlusVersion.Version19]].forEach( ([versionMajor, version]) => {
+ function preloadSections(sections: SectionIdent[], thisSection: SectionIdent) {
+ const kmx: KMXPlus.KMXPlusData = {};
+ const path = makePathToCommonFixture('keyboards', 'kmx-plus', `basic-${versionMajor}.txt`);
+ const reader = new KMXPlusFileReader(version);
+ for(const section of [...sections, thisSection]) {
+ const fixture = hextobinFromFile(path, null, { silent: true, startBlock: section, endBlock: 'end'+section });
+ kmx[section] = (reader.unitTestEndpoints as any)[sectToSectionReader(section)](fixture, kmx);
+ }
+ return kmx;
+ }
+
+ SECTION_IDENTS.forEach((ident: SectionIdent) => {
+ if(ident == 'sect') {
+ // we don't have a separate test for 'sect' -- it is effectively tested
+ // automatically by any other section test
+ return;
+ }
+
+ it(`should parse a v${versionMajor} '${ident}' section`, async function() {
+ const kmx = preloadSections((KMXPlus as any)[sectToSectionClass(ident)].dependencies, ident);
+ test_endpoints[ident](kmx[ident]);
+ });
+
+ });
+
+ it(`should read a v${versionMajor} KMX+ file into memory`, async function() {
+ // Note: see developer/src/common/web/utils for a round-trip-test
+ const path = makePathToCommonFixture('keyboards', 'kmx-plus', `basic-${versionMajor}.txt`);
+ const input = hextobinFromFile(path, null, { silent: true });
+ const reader = new KMXPlusFileReader();
+ const result = reader.readFromKmx(input);
+ assert.isObject(result);
+ for(const ident of SECTION_IDENTS) {
+ test_endpoints[ident](result[ident]);
+ }
+ });
+ });
+
+ const test_endpoints: {[index in SectionIdent]: Function} = {
+ 'sect': function test_sect(sect: KMXPlus.Sect) {
+ // nothing to test
+ assert.isTrue(true);
+ },
+
+ 'bksp': function test_bksp(bksp: KMXPlus.Bksp) {
+ assert.equal(bksp.id, 'bksp');
+ assert.lengthOf(bksp.groups, 1);
+ assert.equal(bksp.groups[0].type, constants.tran_group_type_transform);
+ assert.lengthOf(bksp.groups[0].transforms, 1);
+ assert.lengthOf(bksp.groups[0].reorders, 0);
+
+ assert.equal(bksp.groups[0].transforms[0].from.value, '^e');
+ assert.equal(bksp.groups[0].transforms[0].to.value, '');
+ assert.equal(bksp.groups[0].transforms[0].mapFrom.value, '');
+ assert.equal(bksp.groups[0].transforms[0].mapTo.value, '');
+ },
+
+ 'disp': function test_disp(disp: KMXPlus.Disp) {
+ assert.isObject(disp);
+ assert.equal(disp.baseCharacter.value, 'e');
+ assert.isArray(disp.disps);
+ assert.lengthOf(disp.disps, 2); // counted from basic-17.txt
+
+ assert.equal(disp.disps[0].display.value, '^');
+ assert.equal(disp.disps[0].flags, 0);
+ assert.equal(disp.disps[0].id.value, '');
+ assert.equal(disp.disps[0].to.value, 'a');
+ assert.equal(disp.disps[0].toId.value, 'a');
+
+ assert.equal(disp.disps[1].display.value, '^e');
+ assert.equal(disp.disps[1].flags, KMXPlus.DispItemFlags.isId);
+ assert.equal(disp.disps[1].id.value, 'e');
+ assert.equal(disp.disps[1].to.value, '');
+ assert.equal(disp.disps[1].toId.value, 'e');
+ },
+
+ 'elem': function test_elem(elem: KMXPlus.Elem) {
+ assert.lengthOf(elem.strings, 4);
+
+ assert.lengthOf(elem.strings[0], 0);
+
+ // 'a b c' set
+
+ assert.lengthOf(elem.strings[1], 3);
+
+ assert.equal(elem.strings[1][0].flags, constants.elem_flags_type_char);
+ assert.equal(elem.strings[1][0].order, 0);
+ assert.equal(elem.strings[1][0].tertiary, 0);
+ assert.isUndefined(elem.strings[1][0].uset);
+ assert.equal(elem.strings[1][0].value.value, 'a');
+
+ assert.equal(elem.strings[1][1].flags, constants.elem_flags_type_char);
+ assert.equal(elem.strings[1][1].order, 0);
+ assert.equal(elem.strings[1][1].tertiary, 0);
+ assert.isUndefined(elem.strings[1][1].uset);
+ assert.equal(elem.strings[1][1].value.value, 'b');
+
+ assert.equal(elem.strings[1][2].flags, constants.elem_flags_type_char);
+ assert.equal(elem.strings[1][2].order, 0);
+ assert.equal(elem.strings[1][2].tertiary, 0);
+ assert.isUndefined(elem.strings[1][2].uset);
+ assert.equal(elem.strings[1][2].value.value, 'c');
+
+ // from="\u{1A60}[\u1A75-\u1A79]\u{1A45}" order="10 55 10"
+
+ assert.lengthOf(elem.strings[2], 3);
+
+ assert.equal(elem.strings[2][0].flags, constants.elem_flags_type_char);
+ assert.equal(elem.strings[2][0].order, 10);
+ assert.equal(elem.strings[2][0].tertiary, 0);
+ assert.isUndefined(elem.strings[2][0].uset);
+ assert.equal(elem.strings[2][0].value.value, '\u{1A60}');
+
+ assert.equal(elem.strings[2][1].flags, constants.elem_flags_type_uset);
+ assert.equal(elem.strings[2][1].order, 55);
+ assert.equal(elem.strings[2][1].tertiary, 0);
+ assert.equal(elem.strings[2][1].uset.str.value, '[\\u1A75-\\u1A79]');
+ assert.equal(elem.strings[2][1].value.value, '');
+
+ assert.equal(elem.strings[2][2].flags, constants.elem_flags_type_char);
+ assert.equal(elem.strings[2][2].order, 10);
+ assert.equal(elem.strings[2][2].tertiary, 0);
+ assert.isUndefined(elem.strings[2][2].uset);
+ assert.equal(elem.strings[2][2].value.value, '\u{1A45}');
+
+ // before="\u{1A6B}"
+
+ assert.lengthOf(elem.strings[3], 1);
+
+ assert.equal(elem.strings[3][0].flags, constants.elem_flags_type_char);
+ assert.equal(elem.strings[3][0].order, 0);
+ assert.equal(elem.strings[3][0].tertiary, 0);
+ assert.isUndefined(elem.strings[3][0].uset);
+ assert.equal(elem.strings[3][0].value.value, '\u{1A6B}');
+ },
+
+ 'keys': function test_keys(keys: KMXPlus.Keys) {
+ assert.isObject(keys);
+ assert.isArray(keys.flicks);
+ assert.isArray(keys.keys);
+ assert.isArray(keys.kmap);
+ assert.lengthOf(keys.flicks, 1); // counted from basic-17.txt
+ assert.equal(keys.flicks[0].id.value, '');
+ assert.lengthOf(keys.flicks[0].flicks, 0);
+
+ assert.lengthOf(keys.keys, 5); // counted from basic-17.txt
+ assert.lengthOf(keys.kmap, 48); // counted from basic-17.txt
+
+ assert.equal(keys.keys[0].id.value, 'a');
+ assert.equal(keys.keys[0].to.value, 'a');
+ assert.equal(keys.keys[0].flags, 0);
+ assert.lengthOf(keys.keys[0].flicks, 0);
+ assert.lengthOf(keys.keys[0].longPress, 0);
+ assert.equal(keys.keys[0].longPressDefault.value, '');
+ assert.equal(keys.keys[0].multiTap.length, 0);
+ assert.equal(keys.keys[0].switch.value, '');
+ assert.equal(keys.keys[0].width, 10);
+
+ assert.equal(keys.keys[1].id.value, 'e');
+ assert.equal(keys.keys[1].to.value, 'e');
+ assert.equal(keys.keys[1].flags, 0);
+ assert.lengthOf(keys.keys[1].flicks, 0);
+ assert.lengthOf(keys.keys[1].longPress, 0);
+ assert.equal(keys.keys[1].longPressDefault.value, '');
+ assert.equal(keys.keys[1].multiTap.length, 0);
+ assert.equal(keys.keys[1].switch.value, '');
+ assert.equal(keys.keys[1].width, 10);
+
+ assert.equal(keys.keys[2].id.value, 'gap (reserved)');
+ assert.equal(keys.keys[2].to.value, '');
+ assert.equal(keys.keys[2].flags, KMXPlus.KeysKeysFlags.gap | KMXPlus.KeysKeysFlags.extend);
+ assert.lengthOf(keys.keys[2].flicks, 0);
+ assert.lengthOf(keys.keys[2].longPress, 0);
+ assert.equal(keys.keys[2].longPressDefault.value, '');
+ assert.equal(keys.keys[2].multiTap.length, 0);
+ assert.equal(keys.keys[2].switch.value, '');
+ assert.equal(keys.keys[2].width, 10);
+
+ assert.equal(keys.keys[3].id.value, 'hmaqtugha');
+ assert.equal(keys.keys[3].to.value, 'ħ');
+ assert.equal(keys.keys[3].flags, 0);
+ assert.lengthOf(keys.keys[3].flicks, 0);
+ assert.lengthOf(keys.keys[3].longPress, 2);
+ assert.equal(keys.keys[3].longPress[0].value.value, 'a');
+ assert.equal(keys.keys[3].longPress[1].value.value, 'e');
+ assert.equal(keys.keys[3].longPressDefault.value, '');
+ assert.equal(keys.keys[3].multiTap.length, 0);
+ assert.equal(keys.keys[3].switch.value, '');
+ assert.equal(keys.keys[3].width, 10);
+
+ assert.equal(keys.keys[4].id.value, 'that');
+ assert.equal(keys.keys[4].to.value, 'ថា');
+ assert.equal(keys.keys[4].flags, KMXPlus.KeysKeysFlags.extend);
+ assert.lengthOf(keys.keys[4].flicks, 0);
+ assert.lengthOf(keys.keys[4].longPress, 0);
+ assert.equal(keys.keys[4].longPressDefault.value, '');
+ assert.equal(keys.keys[4].multiTap.length, 0);
+ assert.equal(keys.keys[4].switch.value, '');
+ assert.equal(keys.keys[4].width, 10);
+
+ // TODO-EMBED-OSK-IN-KMX: add flicks
+
+ assert.equal(keys.kmap[0].vkey, 0x20);
+ assert.equal(keys.kmap[0].mod, 0);
+ assert.equal(keys.kmap[0].key, 'gap (reserved)');
+
+ assert.equal(keys.kmap[2].vkey, 0x31);
+ assert.equal(keys.kmap[2].mod, 0);
+ assert.equal(keys.kmap[2].key, 'that');
+ },
+
+ 'layr': function test_layr(layr: KMXPlus.Layr) {
+ assert.isObject(layr);
+ assert.isArray(layr.forms);
+ assert.lengthOf(layr.forms, 1); // counted from basic-17.txt
+
+ assert.equal(layr.forms[0].baseLayout.value, '');
+ assert.equal(layr.forms[0].flags, 0);
+ assert.equal(layr.forms[0].hardware.value, 'us');
+ assert.equal(layr.forms[0].minDeviceWidth, 123);
+
+ assert.lengthOf(layr.forms[0].layers, 1);
+ assert.equal(layr.forms[0].layers[0].id.value, '');
+ assert.equal(layr.forms[0].layers[0].mod, 0);
+
+ assert.lengthOf(layr.forms[0].layers[0].rows, 1);
+ assert.lengthOf(layr.forms[0].layers[0].rows[0].keys, 2);
+
+ assert.equal(layr.forms[0].layers[0].rows[0].keys[0].value, 'hmaqtugha');
+ assert.equal(layr.forms[0].layers[0].rows[0].keys[1].value, 'that');
+ },
+
+ 'list': function test_list(list: KMXPlus.List) {
+ assert.isObject(list);
+ assert.isArray(list.lists);
+ assert.lengthOf(list.lists, 3); // counted from basic-17.txt
+
+ assert.lengthOf(list.lists[0], 0);
+ assert.lengthOf(list.lists[1], 1);
+ assert.lengthOf(list.lists[2], 2);
+
+ assert.equal(list.lists[1][0].value.value, 'a');
+
+ assert.equal(list.lists[2][0].value.value, 'a');
+ assert.equal(list.lists[2][1].value.value, 'e');
+ },
+
+ 'loca': function test_loca(loca: KMXPlus.Loca) {
+ assert.isObject(loca);
+ assert.lengthOf(loca.locales, 1);
+ assert.equal(loca.locales[0].value, 'mt');
+ },
+
+ 'meta': function test_meta(meta: KMXPlus.Meta) {
+ assert.isObject(meta);
+ assert.equal(meta.author.value, 'srl295');
+ assert.equal(meta.conform.value, '45');
+ assert.equal(meta.indicator.value, '🙀');
+ assert.equal(meta.layout.value, 'qwerty');
+ assert.equal(meta.name.value, 'TestKbd');
+ assert.equal(meta.settings, 0);
+ assert.equal(meta.version.value, '1.0.0');
+ },
+
+ 'strs': function test_strs(strs: KMXPlus.Strs) {
+ assert.isObject(strs);
+ assert.isArray(strs.strings);
+ assert.lengthOf(strs.strings, 29); // counted from basic-17.txt
+ // Check a few strings
+ assert.equal(strs.strings[0].value, '');
+ assert.equal(strs.strings[1].value, '1.0.0');
+ assert.equal(strs.strings[2].value, '45');
+ assert.equal(strs.strings[3].value, 'TestKbd');
+ assert.equal(strs.strings[27].value, '🙀');
+ },
+
+ 'tran': function test_tran(tran: KMXPlus.Tran) {
+ assert.equal(tran.id, 'tran');
+ assert.lengthOf(tran.groups, 3);
+ assert.equal(tran.groups[0].type, constants.tran_group_type_transform);
+ assert.lengthOf(tran.groups[0].transforms, 2);
+ assert.lengthOf(tran.groups[0].reorders, 0);
+
+ assert.equal(tran.groups[0].transforms[0].from.value, '^a');
+ assert.equal(tran.groups[0].transforms[0].to.value, '\u{0061}\u{0302}'); // 'â'
+ assert.equal(tran.groups[0].transforms[0].mapFrom.value, ''); // TODO-LDML: no usage of mapFrom, mapTo in our tests?
+ assert.equal(tran.groups[0].transforms[0].mapTo.value, '');
+
+ assert.equal(tran.groups[0].transforms[1].from.value, 'a');
+ assert.equal(tran.groups[0].transforms[1].to.value, '\u{FFFF}\u{0008}\u{0001}'); // UC_SENTINEL CODE_DEADKEY U+0001
+ assert.equal(tran.groups[0].transforms[1].mapFrom.value, '');
+ assert.equal(tran.groups[0].transforms[1].mapTo.value, '');
+
+ assert.equal(tran.groups[1].type, constants.tran_group_type_transform);
+ assert.lengthOf(tran.groups[1].transforms, 1);
+ assert.lengthOf(tran.groups[1].reorders, 0);
+
+ assert.equal(tran.groups[1].transforms[0].from.value, '\\uffff\\u0008\\u0001');
+ assert.equal(tran.groups[1].transforms[0].to.value, '');
+ assert.equal(tran.groups[1].transforms[0].mapFrom.value, '');
+ assert.equal(tran.groups[1].transforms[0].mapTo.value, '');
+
+ assert.equal(tran.groups[2].type, constants.tran_group_type_reorder);
+ assert.lengthOf(tran.groups[2].transforms, 0);
+ assert.lengthOf(tran.groups[2].reorders, 1);
+
+ // Verify that we are connected to the correct elem, no need to check every aspect of elem
+ assert.lengthOf(tran.groups[2].reorders[0].before, 1);
+ assert.equal(tran.groups[2].reorders[0].before[0].value.value, '\u{1A6B}');
+ assert.lengthOf(tran.groups[2].reorders[0].elements, 3);
+ assert.equal(tran.groups[2].reorders[0].elements[0].value.value, '\u{1A60}');
+ },
+
+ 'uset': function test_uset(uset: KMXPlus.Uset) {
+ assert.lengthOf(uset.usets, 1);
+ assert.equal(uset.usets[0].str.value, '[\\u1A75-\\u1A79]');
+ assert.equal(uset.usets[0].uset.pattern, '[\\u1A75-\\u1A79]');
+ assert.lengthOf(uset.usets[0].uset.ranges, 1);
+ assert.lengthOf(uset.usets[0].uset.ranges[0], 2);
+ assert.equal(uset.usets[0].uset.ranges[0][0], 0x1A75); // from
+ assert.equal(uset.usets[0].uset.ranges[0][1], 0x1A79); // from
+
+ // TODO-EMBED-OSK-IN-KMX: other usets and variables not used in transforms in basic-17.kmx?
+ },
+
+ 'vars': function test_vars(vars: KMXPlus.Vars) {
+ assert.lengthOf(vars.markers, 1);
+ assert.equal(vars.markers[0].value.value, 'a');
+
+ assert.lengthOf(vars.strings, 2);
+ assert.equal(vars.strings[0].id.value, 'a');
+ assert.equal(vars.strings[0].value.value, '\\m{a}');
+ assert.equal(vars.strings[1].id.value, 'vst');
+ assert.equal(vars.strings[1].value.value, 'abc');
+
+ assert.lengthOf(vars.sets, 1);
+ assert.equal(vars.sets[0].id.value, 'vse');
+ assert.equal(vars.sets[0].value.value, 'a b c');
+ assert.lengthOf(vars.sets[0].items, 3);
+
+ assert.equal(vars.sets[0].items[0].flags, 0);
+ assert.isUndefined(vars.sets[0].items[0].uset);
+ assert.equal(vars.sets[0].items[0].value.value, 'a');
+
+ assert.equal(vars.sets[0].items[1].flags, 0);
+ assert.isUndefined(vars.sets[0].items[1].uset);
+ assert.equal(vars.sets[0].items[1].value.value, 'b');
+
+ assert.equal(vars.sets[0].items[2].flags, 0);
+ assert.isUndefined(vars.sets[0].items[2].uset);
+ assert.equal(vars.sets[0].items[2].value.value, 'c');
+
+ assert.lengthOf(vars.usets, 1);
+ assert.equal(vars.usets[0].id.value, 'vus');
+ // TODO-LDML: uset variable never stored in uset data? should use `elem` prop
+ // assert.equal(vars.usets[0].unicodeSet.pattern, '[abc]');
+ // assert.lengthOf(vars.usets[0].unicodeSet.ranges, 1);
+ // assert.lengthOf(vars.usets[0].unicodeSet.ranges[0], 2);
+ // assert.equal(vars.usets[0].unicodeSet.ranges[0][0], 0x0061);
+ // assert.equal(vars.usets[0].unicodeSet.ranges[0][1], 0x0063);
+ assert.equal(vars.usets[0].value.value, '[abc]');
+ }
+ };
+
+});
\ No newline at end of file
diff --git a/common/web/types/tests/tsconfig.json b/common/web/types/tests/tsconfig.json
index 45e5311fdc0..9c0bdc711fc 100644
--- a/common/web/types/tests/tsconfig.json
+++ b/common/web/types/tests/tsconfig.json
@@ -16,6 +16,7 @@
"references": [
{ "path": "../../keyman-version" },
{ "path": "../../../../core/include/ldml/"},
+ { "path": "../../../tools/hextobin/" },
{ "path": "../" },
],
}
diff --git a/common/windows/cpp/include/legacy_kmx_file.h b/common/windows/cpp/include/legacy_kmx_file.h
deleted file mode 100644
index d3bc3aed0f6..00000000000
--- a/common/windows/cpp/include/legacy_kmx_file.h
+++ /dev/null
@@ -1,424 +0,0 @@
-// TODO: merge and replace with kmx_file.h from core
-
-/*
- Name: legacy_kmx_file
- Copyright: Copyright (C) SIL International.
- Documentation:
- Description: Describes .kmx binary format. To be replaced with common/include/kmx_file.h
- Create Date: 4 Jan 2007
-
- Modified Date: 24 Aug 2015
- Authors: mcdurdin
- Related Files:
- Dependencies:
-
- Bugs:
- Todo:
- Notes:
- History: 04 Jan 2007 - mcdurdin - Add CODE_NOTANY
- 22 Mar 2010 - mcdurdin - Compiler tidyup
- 25 May 2010 - mcdurdin - I1632 - Keyboard Options
- 24 Oct 2013 - mcdurdin - I3933 - V9.0 - Keyman tray icon menu is not showing installed keyboards
- 19 Mar 2014 - mcdurdin - I4140 - V9.0 - Add keyboard version information to keyboards
- 16 Jun 2014 - mcdurdin - I4271 - V9.0 - Switch language for all applications is not working
- 31 Dec 2014 - mcdurdin - I4548 - V9.0 - When Alt is down, release of Ctrl, Shift is not detectable within TIP in some languages
- 24 Aug 2015 - mcdurdin - I4865 - Add treat hints and warnings as errors into project
- 24 Aug 2015 - mcdurdin - I4866 - Add warn on deprecated features to project and compile
-
-*/
-
-#ifndef _COMPILER_H
-#define _COMPILER_H
-
-
-
-/* WM_UNICHAR */
-
-#define WM_UNICHAR 0x0109
-#define UNICODE_NOCHAR 0xFFFF
-
-/* */
-
-#define KEYMAN_LAYOUT_DEFAULT 0x000005FE
-
-#define KEYMANID_NONKEYMAN 0xFFFFFFFF
-#define KEYMANID_IGNORE 0xFFFFFFFE
-#define KEYMANID_INVALID 0xFFFFFFFD
-
-/* Shift flags for hotkeys (version 1.0) */
-
-#define SHIFTFLAG 0x2000
-#define CTRLFLAG 0x4000
-#define ALTFLAG 0x8000
-
-/* Miscellaneous flags and defines */
-
-#define UM_DRAWICONS 0x01
-#define NUL '\0'
-
-#define MAXGROUPS 128
-
-/* File version identifiers */
-
-#define VERSION_30 0x00000300
-#define VERSION_31 0x00000301
-#define VERSION_32 0x00000302
-#define VERSION_40 0x00000400
-#define VERSION_50 0x00000500
-#define VERSION_501 0x00000501
-#define VERSION_60 0x00000600
-#define VERSION_70 0x00000700
-#define VERSION_80 0x00000800
-#define VERSION_90 0x00000900
-#define VERSION_100 0x00000A00
-#define VERSION_140 0x00000E00
-#define VERSION_150 0x00000F00
-#define VERSION_160 0x00001000
-#define VERSION_170 0x00001100
-#define VERSION_MIN VERSION_50
-#define VERSION_MAX VERSION_170
-
-/*
- Special flag for WM_CHAR/WM_KEY???/WM_SYSKEY???: says that key has been
- processed by Keyman.
-*/
-
-//#define KEYMAN_CHARFLAG 0x0000000L
-#define KEYMAN_CHARFLAG 0x02000000L
-
-#define CHAR_TRANSTATE 0x00000001L // Flag for WM_CHAR: key is down, first repeat
-#define KEYUP_TRANSTATE 0xC0000001L // Flag for WM_KEYUP: key is up, first repeat
-#define KEYDOWN_TRANSTATE 0x00000001L // Flag for WM_KEYDOWN: key is down, first rpt
-#define ALT_TRANSTATE 0x20000000L // Flag for WM_KEYBOARD messages: alt is down
-
-#define IDM_DISABLEKEY 0xFF00
-
-#define WINDOWS_VERSION_3_1 0x030A
-#define WINDOWS_VERSION_3_11 0x030B
-#define WINDOWS_VERSION_4_0 0x035F
-#define WINDOWS_VERSION_95 0x035F
-
-#define HKLM HKEY_LOCAL_MACHINE
-#define HKCU HKEY_CURRENT_USER
-
-//
-// DEBUGINFO states
-//
-#define KDS_KEYBOARD 0x0001
-#define KDS_PROGRAM 0x0002
-#define KDS_MESSAGE 0x0004
-#define KDS_INTERNAT 0x0008
-
-#define KDS_CONTROL 0x8000
-
-//
-// Backspace flags
-//
-
-// Delete a deadkey from context, do not pass on to app
-#define BK_DEFAULT 0
-#define BK_DEADKEY 1
-
-// User pressed backspace so clear deadkeys either side of next deleted character
-#define BK_BACKSPACE 2
-
-// Next character to delete is a Unicode surrogate pair
-#define BK_SURROGATE 4
-
-/*
- A blank key (in a group without "using keys") cannot be '0' as that is
- used for error testing and blanking out unused keys and you don't really
- want that tested!
-*/
-
-#define BLANKKEY 0xFF // Blank key
-
-// Different begin types
-#define BEGIN_ANSI 0
-#define BEGIN_UNICODE 1
-#define BEGIN_NEWCONTEXT 2
-#define BEGIN_POSTKEYSTROKE 3
-
-//#define lpuch (LPBYTE)
-
-#define TSS_NONE 0
-#define TSS_BITMAP 1
-#define TSS_COPYRIGHT 2
-#define TSS_HOTKEY 3
-#define TSS_LANGUAGE 4
-#define TSS_LAYOUT 5
-#define TSS_MESSAGE 6
-#define TSS_NAME 7
-#define TSS_VERSION 8
-#define TSS_CAPSONONLY 9
-#define TSS_CAPSALWAYSOFF 10
-#define TSS_SHIFTFREESCAPS 11
-#define TSS_LANGUAGENAME 12
-
-#define TSS_CALLDEFINITION 13
-#define TSS_CALLDEFINITION_LOADFAILED 14
-
-#define TSS_ETHNOLOGUECODE 15
-
-#define TSS_DEBUG_LINE 16
-
-#define TSS_MNEMONIC 17
-
-#define TSS_INCLUDECODES 18
-
-#define TSS_OLDCHARPOSMATCHING 19
-
-#define TSS_COMPILEDVERSION 20
-#define TSS_KEYMANCOPYRIGHT 21
-
-#define TSS_CUSTOMKEYMANEDITION 22
-#define TSS_CUSTOMKEYMANEDITIONNAME 23
-
-/* Keyman 7.0 system stores */
-
-#define TSS__KEYMAN_60_MAX 23
-
-#define TSS_VISUALKEYBOARD 24
-#define TSS_KMW_RTL 25
-#define TSS_KMW_HELPFILE 26
-#define TSS_KMW_HELPTEXT 27
-#define TSS_KMW_EMBEDJS 28
-
-#define TSS_WINDOWSLANGUAGES 29
-
-#define TSS__KEYMAN_70_MAX 29
-
-/* Keyman 8.0 system stores */
-
-#define TSS_COMPARISON 30
-
-/* Keyman 9.0 system stores */
-
-#define TSS_PLATFORM 31
-#define TSS_BASELAYOUT 32
-#define TSS_LAYER 33
-
-#define TSS_PLATFORM_NOMATCH 0x8001 // Reserved for internal use - after platform statement is run, set to either TSS_PLATFORM_NOMATCH or TSS_PLATFORM_MATCH
-#define TSS_PLATFORM_MATCH 0x8002 // Reserved for internal use - as the result will never change for the lifetime of the process.
-
-#define TSS_VKDICTIONARY 34 // Dictionary of virtual key names for v9 dynamic layouts
-#define TSS_LAYOUTFILE 35 // Keyman 9 layer-based JSON OSK
-#define TSS_KEYBOARDVERSION 36 // &keyboardversion system store // I4140
-#define TSS_KMW_EMBEDCSS 37
-
-#define TSS_TARGETS 38
-
-#define TSS_CASEDKEYS 39
-
-#define TSS__KEYMAN_140_MAX 39
-
-#define TSS_BEGIN_NEWCONTEXT 40
-#define TSS_BEGIN_POSTKEYSTROKE 41
-
-#define TSS_NEWLAYER 42
-#define TSS_OLDLAYER 43
-
-#define TSS__MAX 43
-
-/* wm_keyman_control_internal message control codes */
-
-#define KMCI_SELECTKEYBOARD 3 // I3933
-#define KMCI_SELECTKEYBOARD_TSF 4 // I3933
-#define KMCI_GETACTIVEKEYBOARD 5 // I3933
-#define KMCI_SETFOREGROUND 6 // I3933
-#define KMCI_SELECTKEYBOARD_BACKGROUND 7 // I4271
-#define KMCI_SELECTKEYBOARD_BACKGROUND_TSF 8 // I4271
-
-#define FILEID_COMPILED 0x5354584B
-
-#define SZMAX_LANGUAGENAME 80
-#define SZMAX_KEYBOARDNAME 80
-#define SZMAX_COPYRIGHT 256
-#define SZMAX_MESSAGE 1024
-
-#define UC_SENTINEL 0xFFFF
-#define UC_SENTINEL_EXTENDEDEND 0x10 // was ((CODE_LASTCODE)+1)... what was I thinking?
-
-/*
- * VK__MAX defines the highest virtual key code defined in the system = 0xFF. Custom VK codes start at 256
- */
-#define VK__MAX 255
-
-#define CODE_ANY 0x01
-#define CODE_INDEX 0x02
-#define CODE_CONTEXT 0x03
-#define CODE_NUL 0x04
-#define CODE_USE 0x05
-#define CODE_RETURN 0x06
-#define CODE_BEEP 0x07
-#define CODE_DEADKEY 0x08
-// 0x09 = bkspace.-- we don't need to keep this separate though with UC_SENTINEL
-#define CODE_EXTENDED 0x0A
-//#define CODE_EXTENDEDEND 0x0B deprecated
-#define CODE_SWITCH 0x0C
-#define CODE_KEY 0x0D
-#define CODE_CLEARCONTEXT 0x0E
-#define CODE_CALL 0x0F
-// UC_SENTINEL_EXTENDEDEND 0x10
-#define CODE_CONTEXTEX 0x11
-
-#define CODE_NOTANY 0x12
-
-#define CODE_KEYMAN70_LASTCODE 0x12
-
-#define CODE_SETOPT 0x13
-#define CODE_IFOPT 0x14
-#define CODE_SAVEOPT 0x15
-#define CODE_RESETOPT 0x16
-
-#define CODE_KEYMAN80_LASTCODE 0x16
-
-/* Keyman 9.0 codes */
-
-#define CODE_IFSYSTEMSTORE 0x17
-#define CODE_SETSYSTEMSTORE 0x18
-
-#define CODE_LASTCODE 0x18
-
-#define KF_SHIFTFREESCAPS 0x0001
-#define KF_CAPSONONLY 0x0002
-#define KF_CAPSALWAYSOFF 0x0004
-#define KF_LOGICALLAYOUT 0x0008
-#define KF_AUTOMATICVERSION 0x0010
-
-// 16.0: Support for LDML Keyboards in KMXPlus file format
-#define KF_KMXPLUS 0x0020
-
-#define HK_ALT 0x00010000
-#define HK_CTRL 0x00020000
-#define HK_SHIFT 0x00040000
-
-#define HK_RALT_INVALID 0x00100000
-#define HK_RCTRL_INVALID 0x00200000
-#define HK_RSHIFT_INVALID 0x00400000
-
-#define LCTRLFLAG 0x0001 // Left Control flag
-#define RCTRLFLAG 0x0002 // Right Control flag
-#define LALTFLAG 0x0004 // Left Alt flag
-#define RALTFLAG 0x0008 // Right Alt flag
-#define K_SHIFTFLAG 0x0010 // Either shift flag
-#define K_CTRLFLAG 0x0020 // Either ctrl flag
-#define K_ALTFLAG 0x0040 // Either alt flag
-//#define K_METAFLAG 0x0080 // Either Meta-key flag (tentative). Not usable in keyboard rules;
- // Used internally (currently, only by KMW) to ensure Meta-key
- // shortcuts safely bypass rules
- // Meta key = Command key on macOS, Windows key on Windows
-#define CAPITALFLAG 0x0100 // Caps lock on
-#define NOTCAPITALFLAG 0x0200 // Caps lock NOT on
-#define NUMLOCKFLAG 0x0400 // Num lock on
-#define NOTNUMLOCKFLAG 0x0800 // Num lock NOT on
-#define SCROLLFLAG 0x1000 // Scroll lock on
-#define NOTSCROLLFLAG 0x2000 // Scroll lock NOT on
-#define ISVIRTUALKEY 0x4000 // It is a Virtual Key Sequence
-#define VIRTUALCHARKEY 0x8000 // Keyman 6.0: Virtual Key Cap Sequence NOT YET
-
-#define K_MODIFIERFLAG 0x007F
-#define K_NOTMODIFIERFLAG 0xFF00 // I4548
-
-// Note: OTHER_MODIFIER = 0x10000, used by KMX+ for the
-// other modifier flag in layers, > 16 bit so not available here.
-// See keys_mod_other in keyman_core_ldml.ts
-
-/*
- These sanity checks help ensure we don't
- break on-disk struct sizes when we cross
- compilers, bitness and platforms. They must
- correspond to the equivalent constants in
- kmxfile.pas. For historical reasons, these
- structures have the prefix COMP_ while
- the pas versions are TKeyboardFile_, names
- which correspond more closely to what the
- structures are for.
-*/
-
-#define KEYBOARDFILEHEADER_SIZE 64
-#define KEYBOARDFILESTORE_SIZE 12
-#define KEYBOARDFILEGROUP_SIZE 24
-#define KEYBOARDFILEKEY_SIZE 20
-
-struct COMP_STORE {
- DWORD dwSystemID;
- DWORD dpName;
- DWORD dpString;
- };
-
-static_assert(sizeof(COMP_STORE) == KEYBOARDFILESTORE_SIZE, "COMP_STORE must be KEYBOARDFILESTORE_SIZE bytes");
-
-struct COMP_KEY {
- WORD Key;
- WORD _reserved;
- DWORD Line;
- DWORD ShiftFlags;
- DWORD dpOutput;
- DWORD dpContext;
- };
-
-static_assert(sizeof(COMP_KEY) == KEYBOARDFILEKEY_SIZE, "COMP_KEY must be KEYBOARDFILEKEY_SIZE bytes");
-
-struct COMP_GROUP {
- DWORD dpName;
- DWORD dpKeyArray; // [LPKEY] address of first item in key array
- DWORD dpMatch;
- DWORD dpNoMatch;
- DWORD cxKeyArray; // in array entries
- BOOL fUsingKeys; // group(xx) [using keys] <-- specified or not
- };
-
-static_assert(sizeof(COMP_GROUP) == KEYBOARDFILEGROUP_SIZE, "COMP_GROUP must be KEYBOARDFILEGROUP_SIZE bytes");
-
-struct COMP_KEYBOARD {
- DWORD dwIdentifier; // 0000 Keyman compiled keyboard id
-
- DWORD dwFileVersion; // 0004 Version of the file - Keyman 4.0 is 0x0400
-
- DWORD dwCheckSum; // 0008 As stored in keyboard. DEPRECATED as of 16.0
- DWORD KeyboardID; // 000C as stored in HKEY_LOCAL_MACHINE//system//currentcontrolset//control//keyboard layouts
- DWORD IsRegistered; // 0010
- DWORD version; // 0014 keyboard version
-
- DWORD cxStoreArray; // 0018 in array entries
- DWORD cxGroupArray; // 001C in array entries
-
- DWORD dpStoreArray; // 0020 [LPSTORE] address of first item in store array
- DWORD dpGroupArray; // 0024 [LPGROUP] address of first item in group array
-
- DWORD StartGroup[2]; // 0028 index of starting groups [2 of them]
- //DWORD StartGroupIndex; // StartGroup current index
-
- DWORD dwFlags; // 0030 Flags for the keyboard file
-
- DWORD dwHotKey; // 0034 standard windows hotkey (hiword=shift/ctrl/alt stuff, loword=vkey)
-
- //DWORD dpName; // offset of name
- //DWORD dpLanguageName; // offset of language name;
- //DWORD dpCopyright; // offset of copyright
- //DWORD dpMessage; // offset of message in Keyboard About box
-
- DWORD dpBitmapOffset; // 0038 offset of the bitmaps in the file
- DWORD dwBitmapSize; // 003C size in bytes of the bitmaps
- };
-
-static_assert(sizeof(COMP_KEYBOARD) == KEYBOARDFILEHEADER_SIZE, "COMP_KEYBOARD must be KEYBOARDFILEHEADER_SIZE bytes");
-
-typedef COMP_KEYBOARD *PCOMP_KEYBOARD;
-typedef COMP_STORE *PCOMP_STORE;
-typedef COMP_KEY *PCOMP_KEY;
-typedef COMP_GROUP *PCOMP_GROUP;
-
-
-typedef struct _COMPILER_OPTIONS {
- DWORD dwSize;
- BOOL ShouldAddCompilerVersion;
-} COMPILER_OPTIONS;
-
-typedef COMPILER_OPTIONS *PCOMPILER_OPTIONS;
-
-typedef int (CALLBACK *CompilerMessageProc)(int line, DWORD dwMsgCode, LPSTR szText);
-
-#endif // _COMPILER_H
-
diff --git a/common/windows/cpp/include/legacy_kmx_memory.h b/common/windows/cpp/include/legacy_kmx_memory.h
index 771a4d57ca9..b0c03f271ea 100644
--- a/common/windows/cpp/include/legacy_kmx_memory.h
+++ b/common/windows/cpp/include/legacy_kmx_memory.h
@@ -21,41 +21,37 @@ typedef struct tagKEY
typedef struct tagGROUP
{
PWSTR dpName;
- LPKEY dpKeyArray; // [LPKEY] address of first item in key array
+ LPKEY dpKeyArray; // [LPKEY] address of first item in key array
PWSTR dpMatch;
PWSTR dpNoMatch;
- DWORD cxKeyArray; // in array entries
- BOOL fUsingKeys; // group(xx) [using keys] <-- specified or not
+ DWORD cxKeyArray; // in array entries
+ BOOL fUsingKeys; // group(xx) [using keys] <-- specified or not
} GROUP, * LPGROUP;
typedef struct tagKEYBOARD
{
- DWORD dwIdentifier; // Keyman compiled keyboard id
+ DWORD dwIdentifier; // Keyman compiled keyboard id
- DWORD dwFileVersion; // Version of the file - Keyman 4.0 is 0x0400
+ DWORD dwFileVersion; // Version of the file - Keyman 4.0 is 0x0400
- DWORD dwCheckSum; // As stored in keyboard. DEPRECATED as of 16.0
- DWORD xxkbdlayout; // as stored in HKEY_LOCAL_MACHINE//system//currentcontrolset//control//keyboard layouts
- DWORD IsRegistered; // layout id, from same registry key
- DWORD version; // keyboard version
+ DWORD dwCheckSum; // As stored in keyboard. DEPRECATED as of 16.0
+ DWORD xxkbdlayout; // as stored in HKEY_LOCAL_MACHINE//system//currentcontrolset//control//keyboard layouts
+ DWORD IsRegistered; // layout id, from same registry key
+ DWORD version; // keyboard version
- DWORD cxStoreArray; // in array entries
- DWORD cxGroupArray; // in array entries
+ DWORD cxStoreArray; // in array entries
+ DWORD cxGroupArray; // in array entries
- LPSTORE dpStoreArray; // [LPSTORE] address of first item in store array, from start of file
- LPGROUP dpGroupArray; // [LPGROUP] address of first item in group array, from start of file
+ LPSTORE dpStoreArray; // [LPSTORE] address of first item in store array, from start of file
+ LPGROUP dpGroupArray; // [LPGROUP] address of first item in group array, from start of file
- DWORD StartGroup[2]; // index of starting groups [2 of them]
+ DWORD StartGroup[2]; // index of starting groups [2 of them]
// Ansi=0, Unicode=1
- DWORD dwFlags; // Flags for the keyboard file
+ DWORD dwFlags; // Flags for the keyboard file
- DWORD dwHotKey; // standard windows hotkey (hiword=shift/ctrl/alt stuff, loword=vkey)
+ DWORD dwHotKey; // standard windows hotkey (hiword=shift/ctrl/alt stuff, loword=vkey)
- //PWSTR dpName; // offset of name
- //PWSTR dpLanguageName; // offset of language name;
- //PWSTR dpCopyright; // offset of copyright
- //PWSTR dpMessage; // offset of message in Keyboard About box
-
- HBITMAP hBitmap; // handle to the bitmap in the file;
+ DWORD dpBitmapOffset; // 0038 offset of the bitmaps in the file
+ DWORD dwBitmapSize; // 003C size in bytes of the bitmaps
} KEYBOARD, * LPKEYBOARD;
diff --git a/common/windows/delphi/keyboards/kmxfileconsts.pas b/common/windows/delphi/keyboards/kmxfileconsts.pas
index 8e1955aaa6a..7f901f90efa 100644
--- a/common/windows/delphi/keyboards/kmxfileconsts.pas
+++ b/common/windows/delphi/keyboards/kmxfileconsts.pas
@@ -99,9 +99,10 @@ interface
VERSION_150 = $00000F00;
VERSION_160 = $00001000;
VERSION_170 = $00001100;
+ VERSION_190 = $00001300;
VERSION_MIN = VERSION_50;
- VERSION_MAX = VERSION_170;
+ VERSION_MAX = VERSION_190;
VERSION_MASK_MINOR = $00FF;
VERSION_MASK_MAJOR = $FF00;
@@ -216,7 +217,9 @@ interface
TSS_NEWLAYER = 42;
TSS_OLDLAYER = 43;
- TSS__MAX = 43;
+ TSS_DISPLAYMAP = 44;
+
+ TSS__MAX = 44;
type
TSystemStore = (ssNone = 0, ssBitmap = 1, ssCopyright = 2, ssHotkey = 3, ssLanguage = 4, ssLayout = 5, ssMessage = 6,
diff --git a/core/CORE_API_VERSION.md b/core/CORE_API_VERSION.md
index 227cea21564..4a36342fcab 100644
--- a/core/CORE_API_VERSION.md
+++ b/core/CORE_API_VERSION.md
@@ -1 +1 @@
-2.0.0
+3.0.0
diff --git a/core/build.sh b/core/build.sh
index acfff38d1b7..44fd0c8c1a4 100755
--- a/core/build.sh
+++ b/core/build.sh
@@ -62,6 +62,7 @@ Libraries will be built in 'build///src'.
"@/common/tools/hextobin" \
"@/common/web/keyman-version" \
"@/developer/src/kmc" \
+ "@/core/tools/api-header-extractor" \
"clean" \
"configure" \
"build" \
@@ -177,6 +178,8 @@ if builder_has_option --test; then
testparams="$opt_tests $testparams"
fi
+JUNIT_RESULTS=()
+
do_action test
if builder_start_action test:mac; then
@@ -186,15 +189,24 @@ if builder_start_action test:mac; then
target=mac-`uname -m`
MESON_PATH="$KEYMAN_ROOT/core/build/$target/$BUILDER_CONFIGURATION"
meson test -C "$MESON_PATH" $testparams
+ JUNIT_RESULTS+=("##teamcity[importData type='junit' path='keyman/core/build/$target/$BUILDER_CONFIGURATION/meson-logs/testlog.junit.xml']")
builder_finish_action success test:mac
fi
if builder_start_action test:win; then
- # We can assume that build:win has run so both architectures will be available
- MESON_PATH="$KEYMAN_ROOT/core/build/x86/$BUILDER_CONFIGURATION"
- meson test -C "$MESON_PATH" $testparams
- MESON_PATH="$KEYMAN_ROOT/core/build/x64/$BUILDER_CONFIGURATION"
- meson test -C "$MESON_PATH" $testparams
+ if ! builder_has_action test:x86; then
+ # We can assume that build:win has run so both architectures will be available
+ MESON_PATH="$KEYMAN_ROOT/core/build/x86/$BUILDER_CONFIGURATION"
+ meson test -C "$MESON_PATH" $testparams
+ JUNIT_RESULTS+=("##teamcity[importData type='junit' path='keyman/core/build/x86/$BUILDER_CONFIGURATION/meson-logs/testlog.junit.xml']")
+ fi
+
+ if ! builder_has_action test:x64; then
+ MESON_PATH="$KEYMAN_ROOT/core/build/x64/$BUILDER_CONFIGURATION"
+ meson test -C "$MESON_PATH" $testparams
+ JUNIT_RESULTS+=("##teamcity[importData type='junit' path='keyman/core/build/x64/$BUILDER_CONFIGURATION/meson-logs/testlog.junit.xml']")
+ fi
+
# We do not yet have CI/build hardware support for arm64 Windows testing
#MESON_PATH="$KEYMAN_ROOT/core/build/arm64/$BUILDER_CONFIGURATION"
#meson test -C "$MESON_PATH" $testparams
@@ -202,6 +214,13 @@ if builder_start_action test:win; then
builder_finish_action success test:win
fi
+# Report JUnit test results to CI
+if builder_has_action test; then
+ if builder_is_ci_build; then
+ printf '%s\n' "${JUNIT_RESULTS[@]}"
+ fi
+fi
+
# -------------------------------------------------------------------------------
do_action install
diff --git a/core/commands.inc.sh b/core/commands.inc.sh
index 4584b2dfd69..1f23fabe38a 100644
--- a/core/commands.inc.sh
+++ b/core/commands.inc.sh
@@ -100,6 +100,7 @@ do_test() {
fi
fi
+ JUNIT_RESULTS+=("##teamcity[importData type='junit' path='keyman/core/build/$target/$BUILDER_CONFIGURATION/meson-logs/testlog.junit.xml']")
builder_finish_action success test:$target
}
diff --git a/core/docs/BUILDING.md b/core/docs/BUILDING.md
deleted file mode 100644
index 5dfd46f9d61..00000000000
--- a/core/docs/BUILDING.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# How to build the keyboard processor
-
-## Prerequisites
-See [build configuration](../../docs/build/index.md) for details on how to configure your build environment.
-
-## Building
-
-On all platforms, use `build.sh`.
-
-* See `./build.sh --help` for more details
diff --git a/core/docs/api/actions.md b/core/docs/api/actions.md
new file mode 100644
index 00000000000..acdf5aeeb50
--- /dev/null
+++ b/core/docs/api/actions.md
@@ -0,0 +1,226 @@
+---
+title: Actions - Keyman Core API (internal)
+---
+
+
+As of version 17.0, Actions APIs are now available only to the keyboard
+debugger, IMX, and Core unit tests. Do not use these APIs for other cases.
+Instead, use [km_core_state_get_actions].
+
+typedef struct {
+ uint8_t expected_type; // km_core_backspace_type
+ uintptr_t expected_value; // used mainly in unit tests
+} km_core_backspace_item;
+
+enum km_core_backspace_type {
+ KM_CORE_BT_UNKNOWN = 0, // Used at beginning of context; user-initiated backspace
+ KM_CORE_BT_CHAR = 1, // Deleting a character prior to insertion point
+ KM_CORE_BT_MARKER = 2, // Deleting a marker prior to insertion point
+ KM_CORE_BT_MAX_TYPE_ID
+};
+
+
+# km_core_action_item struct {#km_core_action_item}
+
+This provides the results of processing a key event to the Platform layer and
+should be processed by the Platform layer to issue commands to the os text
+services framework to transform the text store in the Client Application, among
+other actions.
+
+This struct is deprecated; instead use [km_core_actions].
+
+```c
+typedef struct {
+ uint8_t type;
+ uint8_t _reserved[sizeof(void*)-sizeof(uint8_t)];
+ union {
+ uint32_t marker; // MARKER type
+ km_core_option_item const * option; // OPT types
+ km_core_usv character; // CHAR type
+ uint8_t capsLock; // CAPSLOCK type, 1 to turn on, 0 to turn off; re name see #9833
+ km_core_backspace_item backspace; // BACKSPACE type
+ };
+} km_core_action_item;
+enum km_core_action_type {
+ KM_CORE_IT_END = 0, // Marks end of action items list.
+ KM_CORE_IT_CHAR = 1, // A Unicode character has been generated.
+ KM_CORE_IT_MARKER = 2, // Correlates to kmn's "deadkey" markers.
+ KM_CORE_IT_ALERT = 3, // The keyboard has triggered a alert/beep/bell.
+ KM_CORE_IT_BACK = 4, // Delete the codepoint preceding the insertion point.
+ KM_CORE_IT_PERSIST_OPT = 5, // The indicated option needs to be stored.
+ KM_CORE_IT_EMIT_KEYSTROKE = 6, // Emit the current keystroke to the application
+ KM_CORE_IT_INVALIDATE_CONTEXT = 7,
+ KM_CORE_IT_CAPSLOCK = 8, // Enable or disable capsLock
+ KM_CORE_IT_MAX_TYPE_ID
+};
+```
+
+# km_core_state_action_items function {#km_core_state_action_items}
+
+Get the list of action items generated by the last call to
+`km_core_process_event`.
+
+
+## Parameters
+
+### state
+A pointer to the opaque `km_core_state` object to be
+queried.
+
+### num_items
+A pointer to a result variable: The number of items in the
+action item list including the `KM_CORE_IT_END`
+terminator. May be null if not that information is
+required.
+
+## Returns
+A pointer to a `km_core_action_item` list, of `*num_items` in
+length. This data becomes invalid when the state object is
+destroyed, or after a call to `km_core_process_event`. Do not
+modify the contents of this data. The returned array is terminated
+with a `KM_CORE_IT_END` entry.
+
+```c
+KMN_API
+km_core_action_item const *
+km_core_state_action_items(
+ km_core_state const *state,
+ size_t *num_items
+);
+```
+
+# km_core_state_queue_action_items function {#km_core_state_queue_action_items}
+
+Queue actions for the current keyboard processor state; normally
+used in IMX callbacks called during `km_core_process_event`.
+
+
+## Parameters
+
+### state
+A pointer to the opaque `km_core_state` object to be
+queried.
+
+### action_items
+The action items to be added to the core queue. Must be
+terminated with a `KM_CORE_IT_END` entry.
+
+## Returns
+One of the following values:
+
+`KM_CORE_STATUS_OK`
+: On success.
+
+`KM_CORE_STATUS_INVALID_ARGUMENT`
+: In the event the `state` or `action_items` pointer are null.
+
+```c
+KMN_API
+km_core_status
+km_core_state_queue_action_items(
+ km_core_state *state,
+ km_core_action_item const *action_items
+);
+```
+
+# km_core_process_queued_actions function {#km_core_process_queued_actions}
+
+Process the keyboard processors queued actions for the opaque state object.
+Updates the state object as appropriate and fills out its action list.
+The client can add actions externally via the
+`km_core_state_queue_action_items` and then request the processing of the
+actions with this method.
+
+The state action list will be cleared at the start of this call; options and context in
+the state may also be modified.
+
+
+## Parameters
+
+### state
+A pointer to the opaque state object.
+
+## Returns
+One of the following values:
+
+`KM_CORE_STATUS_OK`
+: On success.
+
+`KM_CORE_STATUS_NO_MEM`
+: In the event memory is unavailable to allocate internal buffers.
+
+`KM_CORE_STATUS_INVALID_ARGUMENT`
+: In the event the `state` pointer is null
+
+```c
+KMN_API
+km_core_status
+km_core_process_queued_actions(
+ km_core_state *state
+);
+
+[km_core_cu]: background#km_core_cu "km_core_cu type"
+[km_core_usv]: background#km_core_usv "km_core_usv type"
+[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
+[km_core_status]: background#km_core_status "km_core_status type"
+[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
+[km_core_state]: background#km_core_state "km_core_state struct"
+[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
+[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
+[km_core_attr]: background#km_core_attr "km_core_attr struct"
+[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
+[km_core_get_engine_attrs]: background#km_core_get_engine_attrs "km_core_get_engine_attrs function"
+[km_core_bool]: background#km_core_bool "km_core_bool enum"
+[km_core_caps_state]: state#km_core_caps_state "km_core_caps_state enum"
+[km_core_actions]: state#km_core_actions "km_core_actions struct"
+[km_core_state_get_actions]: state#km_core_state_get_actions "km_core_state_get_actions function"
+[km_core_context_status]: state#km_core_context_status "km_core_context_status enum"
+[km_core_state_context_set_if_needed]: state#km_core_state_context_set_if_needed "km_core_state_context_set_if_needed function"
+[km_core_state_context_clear]: state#km_core_state_context_clear "km_core_state_context_clear function"
+[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
+[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
+[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
+[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
+[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
+[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
+[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
+[km_core_keyboard_load_from_blob]: keyboards#km_core_keyboard_load_from_blob "km_core_keyboard_load_from_blob function"
+[km_core_keyboard_dispose]: keyboards#km_core_keyboard_dispose "km_core_keyboard_dispose function"
+[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
+[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
+[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
+[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
+[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
+[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
+[km_core_state_create]: keyboards#km_core_state_create "km_core_state_create function"
+[km_core_state_clone]: keyboards#km_core_state_clone "km_core_state_clone function"
+[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
+[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
+[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
+[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
+[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
+[km_core_event]: processor#km_core_event "km_core_event function"
+[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
\ No newline at end of file
diff --git a/core/docs/api/background.md b/core/docs/api/background.md
index d4c638f0ce7..9672d8bc901 100644
--- a/core/docs/api/background.md
+++ b/core/docs/api/background.md
@@ -1,6 +1,7 @@
---
title: Background - Keyman Core API
---
+
## Namespace
All calls, types and enums are prefixed with the namespace identifier `km_core_`
@@ -81,302 +82,205 @@ interface.
-------------------------------------------------------------------------------
-# Common functions, types, and macros
-## Basic types
-
-Fundamental types for representing data passed across the API.
-
-### km_core_cp type {#km_core_cp}
-
-`uint16_t/char16_t`
+# km_core_cu type {#km_core_cu}
Represents a UTF16 codepoint, most strings are passed as UTF16.
-### km_core_usv type {#km_core_usv}
-
-`uint32_t/char32_t`
-
-An integral type capable of holding a single Unicode Scalar Value, a decoded UTF
-codepoint.
+```c
+typedef uint16_t/char16_t km_core_cu;
+```
-### km_core_virtual_key type {#km_core_virtual_key}
+```c
+```
-`uint16_t`
+# km_core_usv type {#km_core_usv}
-An integral type capable of holding a platform specific virtual key code.
+An integral type capable of holding a single Unicode Scalar Value, a decoded
+UTF codepoint.
-### km_core_status type {#km_core_status}
+```c
+typedef uint32_t/char32_t km_core_usv;
+```
-`uint32_t`
+```c
+```
-An integral 32 bit wide type capable of holding any valid status code as defined
-by the `enum` [km_core_status_codes].
+# km_core_virtual_key type {#km_core_virtual_key}
-### km_core_modifier_state type {#km_core_modifier_state}
+An integral type capable of holding a platform specific virtual key code.
-`uint16_t`
+```c
+typedef uint16_t km_core_virtual_key;
+```
-An integral type bitmask representing the state of each modifier key.
+# km_core_status type {#km_core_status}
+An integral 32 bit wide type capable of holding any valid status code as
+defined by the `enum` [km_core_status_codes].
+```c
+typedef uint32_t km_core_status;
+```
-## Resource types
+# km_core_keyboard struct {#km_core_keyboard}
-Opaque types for representing resources provided or created by the keyboard
-processor implementation.
+Represents a keyboard loaded from disk, that can be executed by the keyboard
+processor to consume events, update state associated with an insertion point
+and produce action items. A keyboard object may be referenced by any number
+of state objects but must be disposed of after all state objects referencing
+it have first been disposed of.
-### km_core_keyboard struct {#km_core_keyboard}
+```c
+typedef struct km_core_keyboard km_core_keyboard;
+```
-Represents a keyboard loaded from disk, that can be executed by the keyboard
-processor to consume events, update state associated with an insertion point and
-produce action items. A keyboard object may be referenced by any number of state
-objects but must be disposed of after all state objects referencing it have
-first been disposed of.
+# km_core_state struct {#km_core_state}
-### km_core_state struct {#km_core_state}
+Represents all state associated with an insertion point using a keyboard.
+This tracks context, and current action items resulting from a processed
+keyboard event. There can be many state objects using the same keyboard. A
+state object may not live longer than the keyboard it manages state for.
-Represents all state associated with an insertion point using a keyboard. This
-tracks context, and current action items resulting from a processed keyboard
-event. There can be many state objects using the same keyboard. A state object
-may not live longer than the keyboard it manages state for.
+```c
+typedef struct km_core_state km_core_state;
+```
-### km_core_options struct {#km_core_options}
+# km_core_options struct {#km_core_options}
Represents a set of option items for environmental state and keyboard state.
-
+# km_core_keyboard_imx_platform callback function {#km_core_keyboard_imx_platform}
-
-
--------------------------------------------------------------------------------
# km_core_status_codes enum {#km_core_status_codes}
-## Description
-
-An error code mechanism similar to COM’s `HRESULT` scheme (unlike COM, any
+An error code mechanism similar to COM's `HRESULT` scheme (unlike COM, any
non-zero value is an error).
-## Specification
-
```c
enum km_core_status_codes {
+ /** Success code. Call completed as documented. */
KM_CORE_STATUS_OK = 0,
+ /** The call failed to allocate memory during its execution, causing it to fail. */
KM_CORE_STATUS_NO_MEM = 1,
+ /** The call performed an I/O operation which failed, causing it to fail. */
KM_CORE_STATUS_IO_ERROR = 2,
+ /** The call detected one of its parameters was invalid or unsafe. */
KM_CORE_STATUS_INVALID_ARGUMENT = 3,
+ /** The provided key or index into a collection object was not present. */
KM_CORE_STATUS_KEY_ERROR = 4,
+ /** The provided buffer did not contain enough space to fully encode or copy
+ * the result of this call. */
KM_CORE_STATUS_INSUFFICENT_BUFFER = 5,
+ /** A malformed or partial UTF sequence prevented complete decoding of a
+ * unicode string. */
KM_CORE_STATUS_INVALID_UTF = 6,
+ /** An attempt to decode a keyboard file failed. */
KM_CORE_STATUS_INVALID_KEYBOARD = 7,
KM_CORE_STATUS_NOT_IMPLEMENTED = 8,
+ /** This allows encapsulating a platform error code: the remaining 31 low bits
+ * are the error code returned by the OS for cases where the failure mode is
+ * platform specific. For HRESULT codes this only permits failure codes to be
+ * passed and not success codes. */
KM_CORE_STATUS_OS_ERROR = 0x80000000
};
-
```
-## Values
-
-`KM_CORE_STATUS_OK`
-
-: Success code. Call completed as documented.
-
-`KM_CORE_STATUS_NO_MEM`
-
-: The call failed to allocate memory during its execution, causing it to fail.
-
-`KM_CORE_STATUS_IO_ERROR`
-
-: The call performed an I/O operation which failed, causing it to fail.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-
-: The call detected one of its parameters was invalid or unsafe.
-
-`KM_CORE_STATUS_KEY_ERROR`
-
-: The provided key or index into a collection object was not present.
-
-`KM_CORE_STATUS_INSUFFICENT_BUFFER`
-
-: The provided buffer did not contain enough space to fully encode or copy the
-result of this call.
-
-`KM_CORE_STATUS_INVALID_UTF`
-
-: A malformed or partial UTF sequence prevented complete decoding of a unicode
-string.
-
-`KM_CORE_STATUS_INVALID_KEYBOARD`
-
-: An attempt to decode a keyboard file failed.
-
-`KM_CORE_STATUS_OS_ERROR`
-
-: This allows encapsulating a platform error code: the remaining 31 low bits are
-the error code returned by the OS for cases where the failure mode is platform
-specific. For HRESULT codes this only permits failure codes to be passed and not
-success codes.
-
--------------------------------------------------------------------------------
-
# km_core_attr struct {#km_core_attr}
-## Description
-
A structure describing information about Keyman Core implementing this API.
-## Specification
-
```c
-
typedef struct {
+ /** Maximum context size supported by processor in code points. */
size_t max_context;
+ /** Current API number supported. */
uint16_t current;
+ /** Implementation number of current API. */
uint16_t revision;
+ /** current - age == Oldest API number supported. */
uint16_t age;
+ /** A bit field of [km_core_tech_value] values, specifiying which Keyboard
+ * technologies the engine supports. */
uint16_t technology;
+ /** A UTF-8 encoded string identifying the implementer of the processor. */
char const *vendor;
} km_core_attr;
-
```
-## Members
-
-`max_context`
-: Maximum context size supported by processor.
-
-`current`
-: Current API number supported.
-
-`revision`
-: Implementation number of current API.
-
-`age`
-: current - age == Oldest API number supported.
-
-`technology`
-: A bit field of [km_core_tech_value] values,
-specifiying which Keyboard technologies the engine supports.
-
-`vendor`
-: A UTF-8 encoded string identifying the implementer of the processor.
-
--------------------------------------------------------------------------------
# km_core_tech_value enum {#km_core_tech_value}
-## Description
-
Values for a bit field indicating which keyboarding technologies a keyboard
processor supports.
-## Specification
-
```c
-
enum km_core_tech_value {
+ /** The keyboard processor implementation does not disclose which technologies
+ * it implements. */
KM_CORE_TECH_UNSPECIFIED = 0,
+ /** The keyboard processor implements a simple en-US keyboard for the purposes
+ * of testing the API. */
KM_CORE_TECH_MOCK = 1 << 0,
+ /** The keyboard processor implements a Keyman KMX compatible engine. */
KM_CORE_TECH_KMX = 1 << 1,
+ /** The keyboard processor implements a LDML capable processing engine. */
KM_CORE_TECH_LDML = 1 << 2
};
-
```
-## Values
-
-`KM_CORE_TECH_UNSPECIFIED`
-: The keyboard processor implementation does not disclose which technologies it
-implements.
-
-`KM_CORE_TECH_MOCK`
-: The keyboard processor implements a simple en-US keyboard for the purposes of
-testing the API.
-`KM_CORE_TECH_UNSPECIFIED`
-: The keyboard processor implements a Keyman KMX compatible engine.
+# km_core_get_engine_attrs function {#km_core_get_engine_attrs}
-`KM_CORE_TECH_UNSPECIFIED`
-: The keyboard processor implements a LDML capable processing engine.
+Get access processors attributes describing version and technology
+implemented.
--------------------------------------------------------------------------------
-# km_core_get_engine_attrs() {#km_core_get_engine_attrs}
+## Parameters
-## Description
+### state
+An opaque pointer to a [km_core_state].
-Get access processors attributes describing version and technology implemented.
-## Specification
+## Returns
+A pointer to a [km_core_attr] structure. Do not modify the contents
+of this structure.
```c
KMN_API
km_core_attr const *
-km_core_get_engine_attrs(km_core_state const *state);
-
+km_core_get_engine_attrs(
+ km_core_state const *state
+);
```
-## Parameters
-
-`state`
-: An opaque pointer to a [km_core_state].
-
-## Returns
-A pointer to a [km_core_attr] structure. Do not modify the contents of this
-structure.
-
--------------------------------------------------------------------------------
-
# km_core_bool enum {#km_core_bool}
-## Description
-
Defines a boolean state.
-## Specification
```c
typedef enum { KM_CORE_FALSE = 0, KM_CORE_TRUE = 1 } km_core_bool;
-
```
--------------------------------------------------------------------------------
-
-[km_core_cp]: background#km_core_cp "km_core_cp type"
+[km_core_cu]: background#km_core_cu "km_core_cu type"
[km_core_usv]: background#km_core_usv "km_core_usv type"
[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
[km_core_status]: background#km_core_status "km_core_status type"
-[km_core_modifier_state]: background#km_core_modifier_state "km_core_modifier_state type"
[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
[km_core_state]: background#km_core_state "km_core_state struct"
[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
[km_core_attr]: background#km_core_attr "km_core_attr struct"
[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
@@ -391,8 +295,8 @@ typedef enum { KM_CORE_FALSE = 0, KM_CORE_TRUE = 1 } km_core_bool;
[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
-[km_core_state_options_to_json]: options#km_core_state_options_to_json "km_core_state_options_to_json function"
[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
@@ -401,6 +305,7 @@ typedef enum { KM_CORE_FALSE = 0, KM_CORE_TRUE = 1 } km_core_bool;
[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
@@ -409,9 +314,27 @@ typedef enum { KM_CORE_FALSE = 0, KM_CORE_TRUE = 1 } km_core_bool;
[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
-[km_core_cp_dispose]: keyboards#km_core_cp_dispose "km_core_cp_dispose function"
-[km_core_state_to_json]: keyboards#km_core_state_to_json "km_core_state_to_json function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
[km_core_event]: processor#km_core_event "km_core_event function"
-[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
\ No newline at end of file
+[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
\ No newline at end of file
diff --git a/core/docs/api/building.md b/core/docs/api/building.md
index 65ca558670a..4cf23a4bd51 100644
--- a/core/docs/api/building.md
+++ b/core/docs/api/building.md
@@ -1,6 +1,7 @@
---
title: How to build Keyman Core
---
+
## Prerequisites
@@ -61,3 +62,70 @@ Otherwise, add the path where you extracted the kmcomp archive.
### Linux & MacOS
Install kmc from the NPM package.
+
+
+[km_core_cu]: background#km_core_cu "km_core_cu type"
+[km_core_usv]: background#km_core_usv "km_core_usv type"
+[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
+[km_core_status]: background#km_core_status "km_core_status type"
+[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
+[km_core_state]: background#km_core_state "km_core_state struct"
+[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
+[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
+[km_core_attr]: background#km_core_attr "km_core_attr struct"
+[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
+[km_core_get_engine_attrs]: background#km_core_get_engine_attrs "km_core_get_engine_attrs function"
+[km_core_bool]: background#km_core_bool "km_core_bool enum"
+[km_core_caps_state]: state#km_core_caps_state "km_core_caps_state enum"
+[km_core_actions]: state#km_core_actions "km_core_actions struct"
+[km_core_state_get_actions]: state#km_core_state_get_actions "km_core_state_get_actions function"
+[km_core_context_status]: state#km_core_context_status "km_core_context_status enum"
+[km_core_state_context_set_if_needed]: state#km_core_state_context_set_if_needed "km_core_state_context_set_if_needed function"
+[km_core_state_context_clear]: state#km_core_state_context_clear "km_core_state_context_clear function"
+[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
+[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
+[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
+[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
+[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
+[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
+[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
+[km_core_keyboard_load_from_blob]: keyboards#km_core_keyboard_load_from_blob "km_core_keyboard_load_from_blob function"
+[km_core_keyboard_dispose]: keyboards#km_core_keyboard_dispose "km_core_keyboard_dispose function"
+[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
+[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
+[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
+[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
+[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
+[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
+[km_core_state_create]: keyboards#km_core_state_create "km_core_state_create function"
+[km_core_state_clone]: keyboards#km_core_state_clone "km_core_state_clone function"
+[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
+[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
+[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
+[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
+[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
+[km_core_event]: processor#km_core_event "km_core_event function"
+[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
\ No newline at end of file
diff --git a/core/docs/api/changes.md b/core/docs/api/changes.md
index e5736494abc..d1d98f4b7b5 100644
--- a/core/docs/api/changes.md
+++ b/core/docs/api/changes.md
@@ -1,6 +1,14 @@
---
title: Changes - Keyman Core API
---
+
+
+## Changes between 18.0 and 19.0
+
+* Removed deprecated `km_core_keyboard_attrs.folder_path`
+* The JSON introspection APIs (which were not fully implemented),
+ `km_core_state_options_to_json` and `km_core_state_to_json`, have been
+ removed.
## Changes between 16.0 and 17.0
@@ -16,14 +24,14 @@ title: Changes - Keyman Core API
-------------------------------------------------------------------------------
-[km_core_cp]: background#km_core_cp "km_core_cp type"
+[km_core_cu]: background#km_core_cu "km_core_cu type"
[km_core_usv]: background#km_core_usv "km_core_usv type"
[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
[km_core_status]: background#km_core_status "km_core_status type"
-[km_core_modifier_state]: background#km_core_modifier_state "km_core_modifier_state type"
[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
[km_core_state]: background#km_core_state "km_core_state struct"
[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
[km_core_attr]: background#km_core_attr "km_core_attr struct"
[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
@@ -38,8 +46,8 @@ title: Changes - Keyman Core API
[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
-[km_core_state_options_to_json]: options#km_core_state_options_to_json "km_core_state_options_to_json function"
[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
@@ -48,6 +56,7 @@ title: Changes - Keyman Core API
[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
@@ -56,9 +65,27 @@ title: Changes - Keyman Core API
[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
-[km_core_cp_dispose]: keyboards#km_core_cp_dispose "km_core_cp_dispose function"
-[km_core_state_to_json]: keyboards#km_core_state_to_json "km_core_state_to_json function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
[km_core_event]: processor#km_core_event "km_core_event function"
-[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
\ No newline at end of file
+[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
diff --git a/core/docs/api/context.md b/core/docs/api/context.md
new file mode 100644
index 00000000000..06955a91f9e
--- /dev/null
+++ b/core/docs/api/context.md
@@ -0,0 +1,390 @@
+---
+title: Internal Context - Keyman Core API
+---
+
+
+As of version 17.0, Context APIs are now available only to the keyboard
+debugger, IMX, and Core unit tests. Do not use these APIs for other cases.
+Instead, use [`km_core_state_context_set_if_needed`].
+
+----------------------------------------------------------------------------------
+
+The context is the text prior to the insertion point (caret, cursor). The
+context is constructed by the Platform layer, typically by interrogating the
+Client Application. The context will be updated by the engine for keystroke
+events. If the Platform layer code caches the context, the context should be
+reset when a context state change is detected. Context state changes can occur
+when the user uses the mouse to move the insertion point, uses cursor keys,
+switches applications or input fields, or presses hotkeys such as Ctrl+N to
+start a new document. The full set of context state change triggers is up to the
+Platform layer.
+
+Context can also contain positional Markers (also known as 'deadkeys' in kmn
+keyboards), which are transitory state flags that are erased whenever a context
+state change is detected. Markers are always controlled by the Engine.
+
+Contexts are always owned by their state. They may be set to a list of
+context_items or interrogated for their current list of context items.
+
+Core maintains and caches the context. Engine can update the context with
+[`km_core_state_context_set_if_needed`] and [`km_core_state_context_clear`].
+These two functions are available in keyman_core_api.h.
+
+The Keyboard Debugger in Keyman Developer, and IMX in Keyman for Windows, make
+use of the context functionality in this header, but these functions should not
+be used in other places.
+
+-------------------------------------------------------------------------------
+
+
+# km_core_context_type enum {#km_core_context_type}
+
+[`km_core_context_item`].type values which identify which data value (if any)
+the context item carries.
+
+```c
+enum km_core_context_type {
+ /** A [`km_core_context_item`](#km_core_context_item) of this type marks the
+ * end of an array of context items. */
+ KM_CORE_CT_END,
+ /** The context item contains a Unicode Scalar Value which must be accessed
+ * through the [`km_core_context_item.character`](#km_core_context_item)
+ * union member. */
+ KM_CORE_CT_CHAR,
+ /** The context item contains a positional marker which must be accessed
+ * through the [`km_core_context_item.marker`](#km_core_context_item) union
+ * member. */
+ KM_CORE_CT_MARKER
+};
+```
+
+# km_core_context_item struct {#km_core_context_item}
+
+A tagged union representing an element of context which can be either a
+Unicode character or a positional marker.
+
+```c
+typedef struct {
+ /** Identifies the union member to access. A value of enum
+ * [`km_core_context_type`] values. */
+ uint8_t type;
+ /** Space reserved for alignment purposes and possible future use. */
+ uint8_t _reserved[3];
+ union {
+ /** A Unicode Scalar Value. */
+ km_core_usv character;
+ /** A marker value, only meaningful to an engine. */
+ uint32_t marker;
+ };
+} km_core_context_item;
+```
+
+# KM_CORE_CONTEXT_ITEM_END macro {#KM_CORE_CONTEXT_ITEM_END}
+
+Convenience macro to declare a terminating entry in a
+[`km_core_context_item`] item array.
+
+```c
+#define KM_CORE_CONTEXT_ITEM_END {KM_CORE_CT_END, {0,}, {0,}}
+```
+
+# km_core_state_get_intermediate_context function {#km_core_state_get_intermediate_context}
+
+Get access to the state object's keyboard processor's intermediate context.
+This context is used during an IMX callback, part way through processing a
+keystroke.
+
+
+## Parameters
+
+### stat
+e A pointer to the opaque state object to be queried.
+
+### context_items
+A pointer to a variable to receive a context item
+array. Must be disposed of by a call to
+[`km_core_context_items_dispose`].
+
+## Returns
+`KM_CORE_STATUS_OK` on success
+
+```c
+KMN_API
+km_core_status
+km_core_state_get_intermediate_context(
+ km_core_state *state,
+ km_core_context_item ** context_items
+);
+```
+
+# km_core_context_items_dispose function {#km_core_context_items_dispose}
+
+Free the allocated memory belonging to a `km_core_context_item` array
+previously returned by `km_core_state_get_intermediate_context` (internally,
+also `context_items_from_utf16` and `km_core_context_get`)
+
+
+## Parameters
+
+### context_items
+A pointer to the start of the `km_core_context_item`
+array to be disposed of.
+
+```c
+KMN_API
+void
+km_core_context_items_dispose(
+ km_core_context_item *context_items
+);
+```
+
+# km_core_state_context function {#km_core_state_context}
+
+Get access to the state object's cached context.
+
+
+## Parameters
+
+### state
+A pointer to the opaque state object to be queried.
+
+## Returns
+A pointer to an opaque context object. This pointer is valid for
+the lifetime of the state object. If null is passed in, then null
+is returned.
+
+```c
+KMN_API
+km_core_context *
+km_core_state_context(
+ km_core_state const *state
+);
+```
+
+# km_core_state_app_context function {#km_core_state_app_context}
+
+Get access to the state object's application context.
+
+
+## Parameters
+
+### state
+A pointer to the opaque state object to be queried.
+
+## Returns
+A pointer to an opaque context object. This pointer is valid for
+the lifetime of the state object. If null is passed in, then null
+is returned.
+
+```c
+KMN_API
+km_core_context *
+km_core_state_app_context(
+ km_core_state const *state
+);
+```
+
+# km_core_context_set function {#km_core_context_set}
+
+Replace the contents of the current context with a new sequence of
+`km_core_context_item` entries.
+
+
+## Parameters
+
+### context
+A pointer to an opaque context object
+
+### context_items
+A pointer to the start of the `km_core_context_item`
+array containing the new context. It must be
+terminated with an item of type `KM_CORE_CT_END`.
+
+## Returns
+One of the following values:
+
+`KM_CORE_STATUS_OK`
+: On success.
+
+`KM_CORE_STATUS_INVALID_ARGUMENT`
+: If non-optional parameters are null.
+
+`KM_CORE_STATUS_NO_MEM`
+: In the event not enough memory can be allocated to grow the context
+buffer internally.
+
+```c
+KMN_API
+km_core_status
+km_core_context_set(
+ km_core_context *context,
+ km_core_context_item const *context_items
+);
+```
+
+# km_core_context_clear function {#km_core_context_clear}
+
+Removes all context_items from the internal array. If `context` is null, has
+no effect.
+
+
+## Parameters
+
+### context
+A pointer to an opaque context object
+
+```c
+KMN_API
+void
+km_core_context_clear(
+ km_core_context *
+);
+```
+
+# km_core_context_item_list_size function {#km_core_context_item_list_size}
+
+Return the length of a terminated `km_core_context_item` array.
+
+
+## Parameters
+
+### context_items
+A pointer to a `KM_CORE_CT_END` terminated array of
+`km_core_context_item` values.
+
+## Returns
+The number of items in the list, not including terminating item, or
+0 if `context_items` is null.
+
+```c
+KMN_API
+size_t
+km_core_context_item_list_size(
+ km_core_context_item const *context_items
+);
+```
+
+# km_core_context_get function {#km_core_context_get}
+
+Copies all items in the context into a new array and returns the new array.
+This must be disposed of by caller using `km_core_context_items_dispose`.
+
+
+## Parameters
+
+### context
+A pointer to an opaque context object
+
+### out
+Pointer to the result variable: A pointer to the start of
+the `km_core_context_item` array containing a copy of the
+context. Terminated with a type of `KM_CORE_CT_END`. Must be
+disposed of with `km_core_context_items_dispose`.
+
+## Returns
+One of the following values:
+
+`KM_CORE_STATUS_OK`
+: On success.
+
+`KM_CORE_STATUS_INVALID_ARGUMENT`
+: If non-optional parameters are null.
+
+`KM_CORE_STATUS_NO_MEM`
+: In the event not enough memory can be allocated for the output buffer.
+
+```c
+KMN_API
+km_core_status
+km_core_context_get(
+ km_core_context const *context,
+ km_core_context_item **out
+);
+```
+
+# km_core_context_length function {#km_core_context_length}
+
+Return the number of items in the context.
+
+
+## Parameters
+
+### context
+A pointer to an opaque context object
+
+## Returns
+The number of items in the context, and will return 0 if passed a
+null `context` pointer.
+
+```c
+KMN_API
+size_t
+km_core_context_length(
+ km_core_context *context
+);
+
+[km_core_cu]: background#km_core_cu "km_core_cu type"
+[km_core_usv]: background#km_core_usv "km_core_usv type"
+[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
+[km_core_status]: background#km_core_status "km_core_status type"
+[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
+[km_core_state]: background#km_core_state "km_core_state struct"
+[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
+[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
+[km_core_attr]: background#km_core_attr "km_core_attr struct"
+[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
+[km_core_get_engine_attrs]: background#km_core_get_engine_attrs "km_core_get_engine_attrs function"
+[km_core_bool]: background#km_core_bool "km_core_bool enum"
+[km_core_caps_state]: state#km_core_caps_state "km_core_caps_state enum"
+[km_core_actions]: state#km_core_actions "km_core_actions struct"
+[km_core_state_get_actions]: state#km_core_state_get_actions "km_core_state_get_actions function"
+[km_core_context_status]: state#km_core_context_status "km_core_context_status enum"
+[km_core_state_context_set_if_needed]: state#km_core_state_context_set_if_needed "km_core_state_context_set_if_needed function"
+[km_core_state_context_clear]: state#km_core_state_context_clear "km_core_state_context_clear function"
+[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
+[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
+[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
+[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
+[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
+[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
+[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
+[km_core_keyboard_load_from_blob]: keyboards#km_core_keyboard_load_from_blob "km_core_keyboard_load_from_blob function"
+[km_core_keyboard_dispose]: keyboards#km_core_keyboard_dispose "km_core_keyboard_dispose function"
+[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
+[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
+[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
+[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
+[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
+[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
+[km_core_state_create]: keyboards#km_core_state_create "km_core_state_create function"
+[km_core_state_clone]: keyboards#km_core_state_clone "km_core_state_clone function"
+[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
+[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
+[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
+[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
+[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
+[km_core_event]: processor#km_core_event "km_core_event function"
+[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
\ No newline at end of file
diff --git a/core/docs/api/index.md b/core/docs/api/index.md
index 427e6f1ded3..f8f1e7967fa 100644
--- a/core/docs/api/index.md
+++ b/core/docs/api/index.md
@@ -1,6 +1,7 @@
---
title: Keyman Core API
---
+
## Overview
Keyman Core is the component of Keyman Engine that implements keyboarding rules.
@@ -20,7 +21,6 @@ This is an internal API intended for use only within Keyman Engine.
* [Processor](processor)
* [State and Actions](state)
* [Key Handling](keyhandling)
-* [JSON introspection Schema](json-schema)
* [Building Keyman Core](building)
## Requirements
@@ -32,7 +32,6 @@ This is an internal API intended for use only within Keyman Engine.
6. Support querying Keyboard attributes.
7. Idempotent
-
## Design decisions in support of requirements
- Use C or C99 types and calling convention for the interface, it has the
broadest language FFI support. [1,2]
@@ -78,14 +77,15 @@ Caps Lock.
- __See more in__ [Keyman Glossary](https://github.com/keymanapp/keyman/wiki/Keyman-glossary)
-[km_core_cp]: background#km_core_cp "km_core_cp type"
+
+[km_core_cu]: background#km_core_cu "km_core_cu type"
[km_core_usv]: background#km_core_usv "km_core_usv type"
[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
[km_core_status]: background#km_core_status "km_core_status type"
-[km_core_modifier_state]: background#km_core_modifier_state "km_core_modifier_state type"
[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
[km_core_state]: background#km_core_state "km_core_state struct"
[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
[km_core_attr]: background#km_core_attr "km_core_attr struct"
[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
@@ -100,8 +100,8 @@ Caps Lock.
[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
-[km_core_state_options_to_json]: options#km_core_state_options_to_json "km_core_state_options_to_json function"
[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
@@ -110,6 +110,7 @@ Caps Lock.
[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
@@ -118,9 +119,27 @@ Caps Lock.
[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
-[km_core_cp_dispose]: keyboards#km_core_cp_dispose "km_core_cp_dispose function"
-[km_core_state_to_json]: keyboards#km_core_state_to_json "km_core_state_to_json function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
[km_core_event]: processor#km_core_event "km_core_event function"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
\ No newline at end of file
diff --git a/core/docs/api/json-schema.md b/core/docs/api/json-schema.md
deleted file mode 100644
index 4fca89e677b..00000000000
--- a/core/docs/api/json-schema.md
+++ /dev/null
@@ -1,77 +0,0 @@
----
-title: JSON Introspection Schema
----
-
-The [`km_core_state_to_json()`](state#km_core_state_to_json) call
-generates a JSON document describing the internal state of the keyboard
-processor. This is the schema describing that document.
-
-**WARNING**: The structure and format of the JSON document is independently
-versioned and is not considered part of the C API. It is intended solely for use in
-diagnostics or by development and debugging tools which may need to be aware of
-keyboard processor engine implementation details.
-
-```json
-{"$schema": "http://json-schema.org/draft-06/schema#",
- "$id": "@replace_me_with_introspection_schema_uri@",
- "$comment": "© 2018 SIL International. MIT Licensed",
- "title": "Keyboard Processor State object introspection",
- "description": "Internal data from a Keyboard Processor State object, sufficient for debugging and diagnostics",
- "type": "object",
-
- "definitions": {
- "options": {
- "type": "object",
- "properties": {
- "scope": { "enum": ["unknown", "environment", "keyboard"] },
- "options": {
- "type": "object",
- "default": {}
- }
- },
- "required": ["scope", "options"]
- },
- "rule": {
- "type": "object"
- },
- "keyboard" : {
- "type": "object",
- "properties": {
- "id": { "type": "string" },
- "version": { "type": "string" },
- "folder": { "type": "string"},
- "options": { "$ref": "#definitions/options" },
- "rules": {
- "type":"array",
- "items": { "$ref": "#/definitions/rule" }
- }
- },
- "required": ["id", "version", "folder", "options", "rules"]
- },
- "context": {
- "type":"array",
- "items": {"$ref": "#/definitions/context_item"},
- "default": []
- },
- "context_item": {
- "oneOf": [
- {"type": "string", "minLength": 1, "maxLength": 4},
- {"type": "number"}
- ]
- }
- },
- "properties": {
- "$schema": { "const": "keyman/core/docs/introspection.schema" },
- "keyboard": { "$ref": "#/definitions/keyboard" },
- "options": {
- "type": "object",
- "properties": {
- "enviroment": { "$ref": "#/definitions/options" },
- "dynamic": { "$ref": "#/definitions/options" }
- },
- "required": [ "enviroment","dynamic"]
- },
- "context": { "$ref": "#/definitions/context" }
- }
-}
-```
diff --git a/core/docs/api/keyboards.md b/core/docs/api/keyboards.md
index 3439e20722b..f607d32dbbc 100644
--- a/core/docs/api/keyboards.md
+++ b/core/docs/api/keyboards.md
@@ -1,6 +1,7 @@
---
title: Keyboards - Keyman Core API
---
+
A keyboard is a set of rules and transforms in a Processor specific format for
transforming key events into action items. The keyboard is parsed and loaded by
@@ -9,135 +10,79 @@ of state objects.
-------------------------------------------------------------------------------
-# km_core_keyboard_attrs struct {#km_core_keyboard_attrs}
-## Description
+# km_core_keyboard_attrs struct {#km_core_keyboard_attrs}
Provides read-only information about a keyboard.
-## Specification
```c
typedef struct {
- km_core_cp const * version_string;
- km_core_cp const * id;
- km_core_path_name folder_path;
+ /** Processor specific version string. */
+ km_core_cu const * version_string;
+ /** Keyman keyboard ID string. */
+ km_core_cu const * id;
+ /** Set of default values for any options included in the keyboard. */
km_core_option_item const * default_options;
} km_core_keyboard_attrs;
-
```
-## Members
-
-`version_string`
-: Processor specific version string.
-
-`id`
-: Keyman keyboard ID string.
-
-`folder_path`
-: Path to the unpacked folder containing the keyboard and associated resources.
-
-`default_options`
-: Set of default values for any options included in the keyboard.
-
--------------------------------------------------------------------------------
# km_core_keyboard_key struct {#km_core_keyboard_key}
-## Description
-
Describes a single key and modifier combination that a keyboard handles, for
use by the Platform layer. This is used when the Platform layer must know in
advance which keys are used by a given keyboard.
-## Specification
-
```c
typedef struct {
+ /** A virtual key. */
km_core_virtual_key key;
+ /** A [km_core_modifier_state] bitmask. */
uint32_t modifier_flag;
} km_core_keyboard_key;
-
#define KM_CORE_KEYBOARD_KEY_LIST_END { 0, 0 }
-
```
-## Members
-
-`key`
-: A virtual key.
-
-`modifier_flag`
-: A [km_core_modifier_state] bitmask.
-
--------------------------------------------------------------------------------
-
# km_core_keyboard_imx struct {#km_core_keyboard_imx}
-## Description
-
Describes a single Input Method eXtension library and entry point.
-## Specification
-
```c
typedef struct {
- km_core_cp const * library_name;
- km_core_cp const * function_name;
+ /** The fully-qualified path and filename of the dynamically loaded library
+ * file. */
+ km_core_cu const * library_name;
+ /** The entry point for the IMX. */
+ km_core_cu const * function_name;
+ /** unique identifier used to call this function */
uint32_t imx_id;
} km_core_keyboard_imx;
-
#define KM_CORE_KEYBOARD_IMX_END { 0, 0, 0 }
-
```
-## Members
-
-`library_name`
-: The fully-qualified path and filename of the dynamically loaded library file.
-
-`function_name`
-: The entry point for the IMX.
-`imx_id`
-: unique identifier used to call this function
+# km_core_keyboard_load_from_blob function {#km_core_keyboard_load_from_blob}
--------------------------------------------------------------------------------
-
-# km_core_keyboard_load_from_blob() {#km_core_keyboard_load_from_blob}
-
-## Description
-
-Parse and load a keyboard from the supplied blob and return a pointer to the
-loaded keyboard in the out parameter.
-
-## Specification
-
-```c
-KMN_API
-km_core_status
-km_core_keyboard_load_from_blob(const km_core_path_name kb_name,
- const void* blob,
- const size_t blob_size,
- km_core_keyboard** keyboard);
+Parse and load keyboard from the supplied blob and a pointer to the loaded
+keyboard into the out paramter.
-```
## Parameters
-`kb_name`
-: a string with the name of the keyboard.
+### kb_name
+a string with the name of the keyboard.
-`blob`
-: a byte array containing the content of a KMX/KMX+ file.
+### blob
+a byte array containing the content of a KMX/KMX+ file.
-`blob_size`
-: a size_t variable with the size of the blob in bytes.
+### blob_size
+a size_t variable with the size of the blob in bytes.
-`keyboard`
-: A pointer to result variable: A pointer to the opaque keyboard
- object returned by the Processor. This memory must be freed with a
- call to [km_core_keyboard_dispose].
+### keyboard
+A pointer to result variable: A pointer to the opaque
+keyboard object returned by the Processor. This memory
+must be freed with a call to [km_core_keyboard_dispose].
## Returns
+One of the following values:
`KM_CORE_STATUS_OK`
: On success.
@@ -149,59 +94,58 @@ km_core_keyboard_load_from_blob(const km_core_path_name kb_name,
: In the event the keyboard file is unparseable for any reason
`KM_CORE_STATUS_INVALID_ARGUMENT`
-: In the event the file doesn't exist or is inaccesible or `keyboard` is null.
+: In the event `keyboard` is null.
`KM_CORE_STATUS_OS_ERROR`
: Bit 31 (high bit) set, bits 0-30 are an OS-specific error code.
--------------------------------------------------------------------------------
-
-# km_core_keyboard_dispose() {#km_core_keyboard_dispose}
+```c
+KMN_API
+km_core_status km_core_keyboard_load_from_blob(
+ const km_core_path_name kb_name,
+ const void* blob,
+ const size_t blob_size,
+ km_core_keyboard** keyboard
+);
+```
-## Description
+# km_core_keyboard_dispose function {#km_core_keyboard_dispose}
Free the allocated memory belonging to an opaque keyboard object previously
returned by [km_core_keyboard_load_from_blob].
-## Specification
+
+## Parameters
+
+### keyboard
+A pointer to the opaque keyboard object to be disposed of.
```c
KMN_API
void
-km_core_keyboard_dispose(km_core_keyboard *keyboard);
-
+km_core_keyboard_dispose(
+ km_core_keyboard const* keyboard
+);
```
-## Parameters
-`keyboard`
-: A pointer to the opaque keyboard object to be disposed of.
-
--------------------------------------------------------------------------------
-
-# km_core_keyboard_get_attrs() {#km_core_keyboard_get_attrs}
-## Description
+# km_core_keyboard_get_attrs function {#km_core_keyboard_get_attrs}
-Returns the const internal attributes of the keyboard. This structure is valid
-for the lifetime of the opaque keyboard object. Do not modify the returned data.
+Returns the const internal attributes of the keyboard. This structure is
+valid for the lifetime of the opaque keyboard object. Do not modify the
+returned data.
-## Specification
-```c
-KMN_API
-km_core_status
-km_core_keyboard_get_attrs(km_core_keyboard const *keyboard,
- km_core_keyboard_attrs const **out);
-
-```
## Parameters
-`keyboard`
-: A pointer to the opaque keyboard object to be queried.
+### keyboard
+A pointer to the opaque keyboard object to be queried.
-`out`
-: A pointer to the result: A pointer to a [km_core_keyboard_attrs] structure.
+### out
+A pointer to the result: A pointer to a
+[km_core_keyboard_attrs] structure.
## Returns
+One of the following values:
`KM_CORE_STATUS_OK`
: On success.
@@ -209,34 +153,33 @@ km_core_keyboard_get_attrs(km_core_keyboard const *keyboard,
`KM_CORE_STATUS_INVALID_ARGUMENT`
: If non-optional parameters are null.
--------------------------------------------------------------------------------
-
-# km_core_keyboard_get_key_list() {#km_core_keyboard_get_key_list}
-
-## Description
-
-Returns the unordered full set of modifier+virtual keys that are handled by the
-keyboard. The matching dispose call needs to be called to free the memory.
-
-## Specification
-
```c
KMN_API
km_core_status
-km_core_keyboard_get_key_list(km_core_keyboard const *keyboard,
- km_core_keyboard_key **out);
-
+km_core_keyboard_get_attrs(
+ km_core_keyboard const *keyboard,
+ km_core_keyboard_attrs const **out
+);
```
+
+# km_core_keyboard_get_key_list function {#km_core_keyboard_get_key_list}
+
+Returns the unordered full set of modifier+virtual keys that are handled by
+the keyboard. The matching dispose call needs to be called to free the
+memory.
+
+
## Parameters
-`keyboard`
-: A pointer to the opaque keyboard object to be queried.
+### keyboard
+A pointer to the opaque keyboard object to be queried.
-`out`
-: A pointer to an array of [km_core_keyboard_key] structures,
- terminated by `KM_CORE_KEYBOARD_KEY_LIST_END`.
+### out
+A pointer to an array of [km_core_keyboard_key] structures,
+terminated by `KM_CORE_KEYBOARD_KEY_LIST_END`.
## Returns
+One of the following values:
`KM_CORE_STATUS_OK`
: On success.
@@ -244,52 +187,52 @@ km_core_keyboard_get_key_list(km_core_keyboard const *keyboard,
`KM_CORE_STATUS_INVALID_ARGUMENT`
: If non-optional parameters are null.
--------------------------------------------------------------------------------
-
-# km_core_keyboard_key_list_dispose() {#km_core_keyboard_key_list_dispose}
+```c
+KMN_API
+km_core_status
+km_core_keyboard_get_key_list(
+ km_core_keyboard const *keyboard,
+ km_core_keyboard_key **out
+);
+```
-## Description
+# km_core_keyboard_key_list_dispose function {#km_core_keyboard_key_list_dispose}
Free the allocated memory belonging to a keyboard key list previously
returned by [km_core_keyboard_get_key_list].
-## Specification
-
-```c
-KMN_API
-void km_core_keyboard_key_list_dispose(km_core_keyboard_key *key_list);
-```
## Parameters
-`key_list`
-: A pointer to the keyboard key list to be disposed of.
-
--------------------------------------------------------------------------------
+### key_list
+A pointer to the keyboard key list to be disposed of.
-# km_core_keyboard_get_imx_list
+```c
+KMN_API
+void
+km_core_keyboard_key_list_dispose(
+ km_core_keyboard_key *key_list
+);
+```
-## Description
+# km_core_keyboard_get_imx_list function {#km_core_keyboard_get_imx_list}
Returns the list of IMX libraries and function names that are referenced by
-the keyboard. The matching dispose call needs to be called to free the memory.
-
-## Specification
+the keyboard. The matching dispose call needs to be called to free the
+memory.
-```c
-KMN_API
-km_core_status km_core_keyboard_get_imx_list(km_core_keyboard const *keyboard, km_core_keyboard_imx **imx_list);
-```
## Parameters
-`keyboard`
-: A pointer to the keyboard
+### keyboard
+A pointer to the keyboard
-`imx_list`
-: A pointer to a variable that will contain a pointer to the IMX list.
+### imx_list
+A pointer to a variable that will contain a pointer to
+the IMX list.
## Returns
+One of the following values:
`KM_CORE_STATUS_OK`
: On success.
@@ -297,106 +240,105 @@ km_core_status km_core_keyboard_get_imx_list(km_core_keyboard const *keyboard, k
`KM_CORE_STATUS_INVALID_ARGUMENT`
: If non-optional parameters are null.
--------------------------------------------------------------------------------
-
-# km_core_keyboard_imx_list_dispose() {#km_core_keyboard_imx_list_dispose}
-
-## Description
-
-Disposes of the IMX list.
-
-## Specification
-
```c
KMN_API
-void km_core_keyboard_imx_list_dispose(km_core_keyboard_imx *imx_list);
-
+km_core_status
+km_core_keyboard_get_imx_list(
+ km_core_keyboard const *keyboard,
+ km_core_keyboard_imx **imx_list
+);
```
-## Parameters
-`imx_list`
-: A pointer to the IMX list.
-
--------------------------------------------------------------------------------
+# km_core_keyboard_imx_list_dispose function {#km_core_keyboard_imx_list_dispose}
-# km_core_state_imx_register_callback() {#km_core_state_imx_register_callback}
+Disposes of the IMX list.
-## Description
-Register the IMX callback endpoint for the client.
+## Parameters
-## Specification
+### imx_list
+A pointer to the IMX list.
```c
KMN_API
-void km_core_state_imx_register_callback(km_core_state *state, km_core_keyboard_imx_platform imx_callback, void *callback_object);
-
+void
+km_core_keyboard_imx_list_dispose(
+ km_core_keyboard_imx *imx_list
+);
```
-## Parameters
-`state`
-: A pointer to the opaque state object
+# km_core_state_imx_register_callback function {#km_core_state_imx_register_callback}
-`imx_callback`
-: pointer to a function that implements the IMX callback
-
-`callback_object`
-: TODO
+Register the IMX callback endpoint for the client.
--------------------------------------------------------------------------------
-# km_core_state_imx_deregister_callback() {#km_core_state_imx_deregister_callback}
+## Parameters
-## Description
+### state
+A pointer to the opaque state object
-De-register IMX callback endpoint for the client.
+### imx_callback
+Pointer to a function that implements the IMX
+callback
-## Specification
+### callback_object
+An opaque pointer that can be used to pass context
+information to the callback function, usually it is
+a user-defined data structure.
```c
KMN_API
-void km_core_state_imx_deregister_callback(km_core_state *state);
-
+void
+km_core_state_imx_register_callback(
+ km_core_state *state,
+ km_core_keyboard_imx_platform imx_callback,
+ void *callback_object
+);
```
-## Parameters
-
-`state`
-: A pointer to the opaque state object
--------------------------------------------------------------------------------
+# km_core_state_imx_deregister_callback function {#km_core_state_imx_deregister_callback}
-# km_core_state_create() {#km_core_state_create}
+De-register IMX callback endpoint for the client.
-## Description
-Create a keyboard processor state object, maintaining state for the keyboard in
-the environment passed.
+## Parameters
-## Specification
+### state
+A pointer to the opaque state object
```c
KMN_API
-km_core_status
-km_core_state_create(km_core_keyboard *keyboard,
- km_core_option_item const *env,
- km_core_state **out);
-
+void
+km_core_state_imx_deregister_callback(
+ km_core_state *state
+);
```
+
+# km_core_state_create function {#km_core_state_create}
+
+Create a keyboard processor state object, maintaining state for the keyboard
+in the environment passed.
+
+
## Parameters
-`keyboard`
-: A pointer to the opaque keyboard object this object will hold state for.
+### keyboard
+A pointer to the opaque keyboard object this object will
+hold state for.
-`env`
-: The array of [km_core_option_item] key/value pairs used to initialise the
- environment, terminated by `KM_CORE_OPTIONS_END`.
+### env
+The array of [km_core_option_item] key/value pairs used to
+initialise the environment, terminated by
+`KM_CORE_OPTIONS_END`.
-`out`
-: A pointer to result variable: A pointer to the opaque state object
- returned by the Processor, initalised to maintain state for `keyboard`.
- This must be disposed of by a call to [km_core_state_dispose].
+### out
+A pointer to result variable: A pointer to the opaque state
+object returned by the Processor, initalised to maintain
+state for `keyboard`. This must be disposed of by a call to
+[km_core_state_dispose].
## Returns
+One of the following values:
`KM_CORE_STATUS_OK`
: On success.
@@ -407,34 +349,34 @@ km_core_state_create(km_core_keyboard *keyboard,
`KM_CORE_STATUS_INVALID_ARGUMENT`
: In the event the `keyboard` or `out` pointer are null.
--------------------------------------------------------------------------------
-
-# km_core_state_clone() {#km_core_state_clone}
+```c
+KMN_API
+km_core_status
+km_core_state_create(
+ km_core_keyboard const *keyboard,
+ km_core_option_item const *env,
+ km_core_state **out
+);
+```
-## Description
+# km_core_state_clone function {#km_core_state_clone}
Clone an existing opaque state object.
-## Specification
-
-```c
-KMN_API
-km_core_status
-km_core_state_clone(km_core_state const *state,
- km_core_state **out);
-```
## Parameters
-`state`
-: A pointer to the opaque statea object to be cloned.
+### state
+A pointer to the opaque state object to be cloned.
-`out`
-: A pointer to result variable: A pointer to the opaque state object
- returned by the Processor, cloned from the existing object `state`. This
- must be disposed of by a call to [km_core_state_dispose].
+### out
+A pointer to result variable: A pointer to the opaque state
+object returned by the Processor, cloned from the existing
+object `state`. This must be disposed of by a call to
+[km_core_state_dispose].
## Returns
+One of the following values:
`KM_CORE_STATUS_OK`
: On success.
@@ -445,186 +387,122 @@ km_core_state_clone(km_core_state const *state,
`KM_CORE_STATUS_INVALID_ARGUMENT`
: In the event the `state` or `out` pointer are null.
--------------------------------------------------------------------------------
-
-# km_core_state_dispose() {#km_core_state_dispose}
+```c
+KMN_API
+km_core_status
+km_core_state_clone(
+ km_core_state const *state,
+ km_core_state **out
+);
+```
-## Description
+# km_core_state_dispose function {#km_core_state_dispose}
Free the allocated resources belonging to a [km_core_state] object previously
returned by [km_core_state_create] or [km_core_state_clone]. After this all
-pointers previously returned by any [km_core_state] family of calls will become
-invalid.
+pointers previously returned by any [km_core_state] family of calls will
+become invalid.
-## Specification
+
+## Parameters
+
+### state
+A pointer to the opaque state object to be disposed.
```c
KMN_API
void
-km_core_state_dispose(km_core_state *state);
-
+km_core_state_dispose(
+ km_core_state const *state
+);
```
-## Parameters
-
-`state`
-: A pointer to the opaque state object to be disposed.
-
--------------------------------------------------------------------------------
# km_core_debug_context_type enum {#km_core_debug_context_type}
-As of version 17, the cached context is an internal property of the
-state, not exposed to the consumer of the API -- apart from the
-Keyman Developer Keyboard Debugger. However, for other debug
-purposes, it is helpful to be able to examine the cached context, so
-a debug-formatted version of the context is made available with
-[km_core_state_context_debug]. This is not intended to be parsed for
-reading the context for other purposes, and the format may change.
-The three context types are: cached, intermediate, and app.
+As of version 17, the cached context is an internal property of the state,
+not exposed to the consumer of the API -- apart from the Keyman Developer
+Keyboard Debugger. However, for other debug purposes, it is helpful to be
+able to examine the cached context, so a debug-formatted version of the
+context is made available with [km_core_state_context_debug]. This is not
+intended to be parsed for reading the context for other purposes, and the
+format may change.
-## Specification
+The three context types are: cached, intermediate, and app.
```c
typedef enum {
+ /** the internal context used by Core, which may be normalized and may contain
+ * markers. This is set via [km_core_state_context_set_if_needed], and will
+ * be modified during keystroke event processing. */
KM_CORE_DEBUG_CONTEXT_CACHED = 0,
+ /** internal context used by IMX, only valid during keystroke event
+ * processing. */
KM_CORE_DEBUG_CONTEXT_INTERMEDIATE = 1,
+ /** an exact copy of the current context passed in to
+ * [km_core_state_context_set_if_needed], which is used to verify the precise
+ * text manipulations required when emitted changes. This input context is in
+ * "NFU" -- normalization form unknown, and may be mixed normalization so may
+ * require fixups when it is manipulated by keyboard processors that support
+ * normalization, such as the LDML keyboard processor. */
KM_CORE_DEBUG_CONTEXT_APP = 2
} km_core_debug_context_type;
-
```
-## Values
-
-`KM_CORE_DEBUG_CONTEXT_CACHED`
-: the internal context used by Core, which may be normalized
- and may contain markers. This is set via
- [km_core_state_context_set_if_needed], and will be modified
- during keystroke event processing.
-
-`KM_CORE_DEBUG_CONTEXT_INTERMEDIATE`
-: internal context used by IMX, only valid during
- keystroke event processing.
-
-`KM_CORE_DEBUG_CONTEXT_APP`
-: an exact copy of the current context passed in to
- [km_core_state_context_set_if_needed], which is used to verify
- the precise text manipulations required when emitted changes.
- This input context is in "NFU" -- normalization form unknown,
- and may be mixed normalization so may require fixups when
- it is manipulated by keyboard processors that support
- normalization, such as the LDML keyboard processor.
--------------------------------------------------------------------------------
-
-# km_core_state_context_debug() {#km_core_state_context_debug}
-
-## Description
+# km_core_state_context_debug function {#km_core_state_context_debug}
Returns a debug formatted string of the context from the state.
-## Specification
-
-```c
-KMN_API
-km_core_cp *
-km_core_state_context_debug(km_core_state *state, km_core_debug_context_type context_type);
-```
## Parameters
-`state`
-: A pointer to the opaque state object to be queried.
+### state
+A pointer to the opaque state object to be queried.
-`context_type`
-: The type of context to retrieve from the state.
+### context_type
+The type of context to retrieve from the state.
## Returns
-
-A pointer to a [km_core_cp] UTF-16 string. Must be disposed of by a call
-to [km_core_cp_dispose].
-
--------------------------------------------------------------------------------
-
-# km_core_cp_dispose() {#km_core_cp_dispose}
-
-## Description
-
-Free the allocated memory belonging to a [km_core_cp] array previously
-returned by [km_core_state_context_debug]. May be `nullptr`.
-
-## Specification
+A pointer to a [km_core_cu] UTF-16 string. Must be disposed of by a
+call to [km_core_cu_dispose].
```c
KMN_API
-void
-km_core_cp_dispose(km_core_cp *cp);
-
+km_core_cu *
+km_core_state_context_debug(
+ const km_core_state *state,
+ km_core_debug_context_type context_type
+);
```
-## Parameters
-`cp`
-: A pointer to the start of the [km_core_cp] array to be disposed of.
+# km_core_cu_dispose function {#km_core_cu_dispose}
--------------------------------------------------------------------------------
-
-# km_core_state_to_json() {#km_core_state_to_json}
-
-## Description
+Free the allocated memory belonging to a [km_core_cu] array previously
+returned by [km_core_state_context_debug]. May be `nullptr`.
-Export the internal state of a [km_core_state] object to a JSON format document
-and place it in the supplied buffer, reporting how much space was used. If null
-is passed as the buffer the number of bytes required is returned. If there is
-insufficent space to hold the document, the contents of the buffer is undefined.
-The encoding of the returned data is UTF-8.
-__WARNING__: The structure and format of the JSON document while independently
-versioned is not part of this API and is intended solely for use in diagnostics
-or by development and debugging tools which are aware of processor
-implementation details.
+## Parameters
-## Specification
+### cp
+A pointer to the start of the [km_core_cu] array to be disposed
+of.
```c
KMN_API
-km_core_status
-km_core_state_to_json(km_core_state const *state,
- char *buf,
- size_t *space);
-
+void
+km_core_cu_dispose(
+ km_core_cu *cp
+);
```
-## Parameters
-
-`state`
-: An pointer to an opaque state object.
-
-`buf`
-: A pointer to the buffer to place the C string containing the JSON
- document into. May be null.
-
-`space`
-: A pointer to a size_t variable. This variable must contain the
- number of bytes available in the buffer pointed to by `buf`, unless `buf` is
- null. On return it will hold how many bytes were used.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event an internal memory allocation fails.
-
--------------------------------------------------------------------------------
-
-[km_core_cp]: background#km_core_cp "km_core_cp type"
+[km_core_cu]: background#km_core_cu "km_core_cu type"
[km_core_usv]: background#km_core_usv "km_core_usv type"
[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
[km_core_status]: background#km_core_status "km_core_status type"
-[km_core_modifier_state]: background#km_core_modifier_state "km_core_modifier_state type"
[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
[km_core_state]: background#km_core_state "km_core_state struct"
[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
[km_core_attr]: background#km_core_attr "km_core_attr struct"
[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
@@ -639,8 +517,8 @@ km_core_state_to_json(km_core_state const *state,
[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
-[km_core_state_options_to_json]: options#km_core_state_options_to_json "km_core_state_options_to_json function"
[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
@@ -649,6 +527,7 @@ km_core_state_to_json(km_core_state const *state,
[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
@@ -657,9 +536,27 @@ km_core_state_to_json(km_core_state const *state,
[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
-[km_core_cp_dispose]: keyboards#km_core_cp_dispose "km_core_cp_dispose function"
-[km_core_state_to_json]: keyboards#km_core_state_to_json "km_core_state_to_json function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
[km_core_event]: processor#km_core_event "km_core_event function"
-[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
\ No newline at end of file
+[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
diff --git a/core/docs/api/keyhandling.md b/core/docs/api/keyhandling.md
index 76576b56d81..43a92e2d1ac 100644
--- a/core/docs/api/keyhandling.md
+++ b/core/docs/api/keyhandling.md
@@ -1,39 +1,114 @@
+---
+title: Keyhandling
+---
+
+
# Key Handling
-While Keyman Core does not generally emit actions from key up events,
-the `emit_keystroke` value for a key up event will match the corresponding
-key down event, in order to avoid 'stuck key' scenarios'.
+For each key press the processor will called twice, for the key down event
+as well as for the key up event. Depending on the type of key pressed the
+processor might handle the key itself or pass it on to the application.
+The value of `emit_keystroke` in `km_core_actions` struct tells if the
+processor handled the key (`emit_keystroke=0`) or not (`emit_keystroke=1`).
+It is important that the same value gets set for both key down and key up
+events, otherwise the application might miss some events which then looks
+to the user like keys are stuck.
Usually the processor won't handle any frame keys and let the application
deal with it. This allows shortcut keys like Ctrl+C
-to work. One exception is the Backspace key which is
-handled internally if enough context is available. Keyboards can define
-rules for other frame keys, e.g. Ctrl+Key which are then
-also handled internally. Regular keys will be handled by the processor.
+to work. The only exception is the Backspace key which is
+handled internally if enough context is available. Regular keys will be
+handled by the processor.
Note that there is a difference between CLDR/LDML and KMN keyboards if
there are no rules/transforms defined for a key: KMN keyboards will
output the cap value of the key (e.g. pressing a will output
`a` if no rule is defined), whereas CLDR/LDML keyboards will suppress
-any output for that key. However, `km_core_actions.emit_keystroke` will
-be `FALSE` for both KMN and CLR/LDML keyboards.
+any output for that key.
The following table lists the state of `km_core_actions.emit_keystroke`
on return of `km_core_process_event` when the following type of key is
pressed:
-|Type | KeyDown/Up |
-|-----------------------------|------------|
-|Core key with rule | FALSE |
-|Core key w/o rule | FALSE |
-|Framekeys: | |
-|Enter | TRUE |
-|Backspace¹ | FALSE |
-|Backspace² | TRUE |
-|Ctrl+Key | TRUE |
-|Modifier key Shift| TRUE |
-|Modifier key Ctrl | TRUE |
-|Modifier key LAlt | TRUE |
+|Type | KeyDown | KeyUp |
+|-----------------------------|---------------|----------|
+|Key with rule | FALSE | FALSE |
+|Key w/o rule | FALSE | FALSE |
+|Framekeys: | | |
+|Enter | TRUE | TRUE |
+|Backspace¹ | FALSE | FALSE |
+|Backspace² | TRUE | TRUE |
+|Ctrl+Key | TRUE | TRUE |
+|Modifier key Shift| TRUE | TRUE |
+|Modifier key Ctrl | TRUE | TRUE |
+|Modifier key LAlt | TRUE | TRUE |
1: context available
2: without or with empty context
+
+
+[km_core_cu]: background#km_core_cu "km_core_cu type"
+[km_core_usv]: background#km_core_usv "km_core_usv type"
+[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
+[km_core_status]: background#km_core_status "km_core_status type"
+[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
+[km_core_state]: background#km_core_state "km_core_state struct"
+[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
+[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
+[km_core_attr]: background#km_core_attr "km_core_attr struct"
+[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
+[km_core_get_engine_attrs]: background#km_core_get_engine_attrs "km_core_get_engine_attrs function"
+[km_core_bool]: background#km_core_bool "km_core_bool enum"
+[km_core_caps_state]: state#km_core_caps_state "km_core_caps_state enum"
+[km_core_actions]: state#km_core_actions "km_core_actions struct"
+[km_core_state_get_actions]: state#km_core_state_get_actions "km_core_state_get_actions function"
+[km_core_context_status]: state#km_core_context_status "km_core_context_status enum"
+[km_core_state_context_set_if_needed]: state#km_core_state_context_set_if_needed "km_core_state_context_set_if_needed function"
+[km_core_state_context_clear]: state#km_core_state_context_clear "km_core_state_context_clear function"
+[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
+[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
+[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
+[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
+[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
+[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
+[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
+[km_core_keyboard_load_from_blob]: keyboards#km_core_keyboard_load_from_blob "km_core_keyboard_load_from_blob function"
+[km_core_keyboard_dispose]: keyboards#km_core_keyboard_dispose "km_core_keyboard_dispose function"
+[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
+[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
+[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
+[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
+[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
+[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
+[km_core_state_create]: keyboards#km_core_state_create "km_core_state_create function"
+[km_core_state_clone]: keyboards#km_core_state_clone "km_core_state_clone function"
+[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
+[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
+[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
+[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
+[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
+[km_core_event]: processor#km_core_event "km_core_event function"
+[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
\ No newline at end of file
diff --git a/core/docs/api/options.md b/core/docs/api/options.md
index c5539d7ae1e..ac864bdddd9 100644
--- a/core/docs/api/options.md
+++ b/core/docs/api/options.md
@@ -1,6 +1,7 @@
---
title: Options - Keyman Core API
---
+
A state’s default options are set from the keyboard at creation time and the
environment. The Platform layer is then expected to apply any persisted
@@ -17,127 +18,90 @@ value.
-------------------------------------------------------------------------------
-# km_core_option_scope enum
-## Description
-
-## Specification
+# km_core_option_scope enum {#km_core_option_scope}
```c
enum km_core_option_scope {
+ /** An unknown option type. Reserved. */
KM_CORE_OPT_UNKNOWN = 0,
+ /** An option that is defined for the currently active keyboard; not all
+ * processors support this type of option. These options are specific to the
+ * active keyboard. */
KM_CORE_OPT_KEYBOARD = 1,
+ /** Properties of the current environment, often but not necessarily always
+ * read-only. */
KM_CORE_OPT_ENVIRONMENT = 2,
KM_CORE_OPT_MAX_SCOPES
};
-
```
-## Values
-
-`KM_CORE_OPT_UNKNOWN`
-: An unknown option type. Reserved.
-`KM_CORE_OPT_KEYBOARD`
-: An option that is defined for the currently active keyboard;
- not all processors support this type of option. These options
- are specific to the active keyboard.
+# km_core_option_item struct {#km_core_option_item}
-`KM_CORE_OPT_ENVIRONMENT`
-: Properties of the current environment, often but not necessarily
- always read-only.
-
--------------------------------------------------------------------------------
+Defines a single option to be passed into the Keyman Core from the Platform
+layer.
-# km_core_option_item struct
-
-## Description
-
-Defines a single option to be passed into the Keyman Core from the
-Platform layer.
-
-## Specification
```c
struct km_core_option_item {
- km_core_cp const * key;
- km_core_cp const * value;
+ /** Null-terminated string key for the option */
+ km_core_cu const * key;
+ /** Null-terminated string value for the option */
+ km_core_cu const * value;
+ /** Scope which an option belongs to, from [km_core_option_scope]. */
uint8_t scope;
};
-
#define KM_CORE_OPTIONS_END { 0, 0, 0 }
```
-## Members
-`key`
-: Null-terminated string key for the option
+# km_core_options_list_size function {#km_core_options_list_size}
-`value`
-: Null-terminated string value for the option
+Return the length of a terminated [km_core_option_item] array (options
+list).
-`scope`
-: Scope which an option belongs to, from [km_core_option_scope].
--------------------------------------------------------------------------------
+## Parameters
-# km_core_options_list_size()
+### opts
+A pointer to a `KM_CORE_OPTIONS_END` terminated array of
+[km_core_option_item] values.
-## Description
-Return the length of a terminated [km_core_option_item] array (options
-list).
+## Returns
+The number of items in the list, not including terminating
+item, or 0 if `opts` is null.
-## Specification
```c
KMN_API
size_t
km_core_options_list_size(km_core_option_item const *opts);
-
```
-## Parameters
-
-`opts`
-: A pointer to a `KM_CORE_OPTIONS_END` terminated array of
- [km_core_option_item] values.
-
-## Returns
-
-The number of items in the list, not including terminating item,
-or 0 if `opts` is null.
-
--------------------------------------------------------------------------------
-
-# km_core_state_option_lookup
-## Description
+# km_core_state_option_lookup function {#km_core_state_option_lookup}
Lookup an option based on its key, in an options list.
-## Specification
-```c
-KMN_API
-km_core_status
-km_core_state_option_lookup(km_core_state const *state,
- uint8_t scope,
- km_core_cp const *key,
- km_core_cp const **value);
-```
## Parameters
-`state`
-: An opaque pointer to a state object.
+### state
+An opaque pointer to a state object.
-`scope`
-: Which key-value store to interrogate.
+### scope
+Which key-value store to interrogate.
-`key`
-: A UTF-16 string that matches the key in the target [km_core_option_item].
+### key
+A UTF-16 string that matches the key in the target
+[km_core_option_item].
+
+### value
+A pointer to the result variable: A pointer to a UTF-16
+string value owned by the state or keyboard object at the
+time of the call. This pointer is only valid *until* the next
+call to any function on this API and should be used
+immediately.
-`value`
-: A pointer to the result variable: A pointer to a UTF-16 string value owned
- by the state or keyboard object at the time of the call. This pointer is
- only valid *until* the next call to any function on this API and should be
- used immediately.
## Returns
+One of the following values:
`KM_CORE_STATUS_OK`
: On success.
@@ -148,31 +112,33 @@ km_core_state_option_lookup(km_core_state const *state,
`KM_CORE_STATUS_KEY_ERROR`
: The key cannot be found.
--------------------------------------------------------------------------------
-
-# km_core_state_options_update()
+```c
+KMN_API
+km_core_status
+km_core_state_option_lookup(
+ km_core_state const *state,
+ uint8_t scope,
+ km_core_cu const *key,
+ km_core_cu const **value
+);
+```
-## Description
+# km_core_state_options_update function {#km_core_state_options_update}
Adds or updates one or more options from a list of [km_core_option_item]s.
-## Specification
-``` */
-KMN_API
-km_core_status
-km_core_state_options_update(km_core_state *state,
- km_core_option_item const *new_opts);
-```
## Parameters
-`state`
-: An opaque pointer to a state object.
-`new_opts`
-: An array of [km_core_option_item] objects to update or add. Must be
- terminated with `KM_CORE_OPTIONS_END`.
+### state
+An opaque pointer to a state object.
+
+### new_opts
+An array of [km_core_option_item] objects to update or add.
+Must be terminated with `KM_CORE_OPTIONS_END`.
## Returns
+One of the following values:
`KM_CORE_STATUS_OK`
: On success.
@@ -186,64 +152,23 @@ km_core_state_options_update(km_core_state *state,
`KM_CORE_STATUS_KEY_ERROR`
: The key cannot be found.
--------------------------------------------------------------------------------
-
-# km_core_state_options_to_json()
-
-## Description
-
-Export the contents of a [km_core_options] array to a JSON formatted document and
-place it in the supplied buffer, reporting how much space was used. If null is
-passed as the buffer the number of bytes required is returned in `space`. If
-there is insufficent space to hold the document the contents of the buffer is
-undefined. The returned buffer uses UTF-8 encoding.
-
-## Specification
-
```c
KMN_API
km_core_status
-km_core_state_options_to_json(km_core_state const *state,
- char *buf,
- size_t *space);
-
+km_core_state_options_update(
+ km_core_state *state,
+ km_core_option_item const *new_opts
+);
```
-## Parameters
-
-`state`
-: An opaque pointer to a state object.
-
-`buf`
-: A pointer to the buffer to place the C string containing the JSON
- document into, can be null.
-
-`space`
-: A pointer to a size_t variable. This variable must contain the
- number of bytes available in the buffer pointed to by `buf`, unless `buf` is
- null. On return it will hold how many bytes were used.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: If non-optional parameters are null.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event an internal memory allocation fails.
-
--------------------------------------------------------------------------------
-
-[km_core_cp]: background#km_core_cp "km_core_cp type"
+[km_core_cu]: background#km_core_cu "km_core_cu type"
[km_core_usv]: background#km_core_usv "km_core_usv type"
[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
[km_core_status]: background#km_core_status "km_core_status type"
-[km_core_modifier_state]: background#km_core_modifier_state "km_core_modifier_state type"
[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
[km_core_state]: background#km_core_state "km_core_state struct"
[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
[km_core_attr]: background#km_core_attr "km_core_attr struct"
[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
@@ -258,8 +183,8 @@ km_core_state_options_to_json(km_core_state const *state,
[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
-[km_core_state_options_to_json]: options#km_core_state_options_to_json "km_core_state_options_to_json function"
[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
@@ -268,6 +193,7 @@ km_core_state_options_to_json(km_core_state const *state,
[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
@@ -276,9 +202,27 @@ km_core_state_options_to_json(km_core_state const *state,
[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
-[km_core_cp_dispose]: keyboards#km_core_cp_dispose "km_core_cp_dispose function"
-[km_core_state_to_json]: keyboards#km_core_state_to_json "km_core_state_to_json function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
[km_core_event]: processor#km_core_event "km_core_event function"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
\ No newline at end of file
diff --git a/core/docs/api/processor.md b/core/docs/api/processor.md
index e8a8f7c0f4c..134351048bd 100644
--- a/core/docs/api/processor.md
+++ b/core/docs/api/processor.md
@@ -1,71 +1,52 @@
---
title: Processor - Keyman Core API
---
+
-# km_core_event_flags enum {#km_core_event_flags}
-
-## Description
-Bit flags to be used with the `event_flags` parameter of [km_core_process_event]
+# km_core_event_flags enum {#km_core_event_flags}
-## Specification
+Bit flags to be used with the `event_flags` parameter of
+[km_core_process_event].
```c
enum km_core_event_flags {
+ /** default value: hardware */
KM_CORE_EVENT_FLAG_DEFAULT = 0,
+ /** set if the event is touch, otherwise hardware */
KM_CORE_EVENT_FLAG_TOUCH = 1,
};
```
-## Values
-
-`KM_CORE_EVENT_FLAG_DEFAULT`
-: default value: hardware
-
-`KM_CORE_EVENT_FLAG_TOUCH`
-: set if the event is touch, otherwise hardware
-
--------------------------------------------------------------------------------
+# km_core_process_event function {#km_core_process_event}
-# km_core_process_event() {#km_core_process_event}
+Run the keyboard on an opaque state object with the provided virtual key and
+modifer key state. Updates the state object as appropriate and fills out its
+internal set of actions, which can be retrieved with
+[km_core_state_get_actions].
-## Description
+The state's actions will be cleared at the start of this call; options and
+context in the state may also be modified.
-Run the keyboard on an opaque state object with the provided virtual key and modifer
-key state. Updates the state object as appropriate and fills out its internal set
-of actions, which can be retrieved with [km_core_state_get_actions].
-
-The state's actions will be cleared at the start of this call; options and context in
-the state may also be modified.
-
-## Specification
-
-```c
-KMN_API
-km_core_status
-km_core_process_event(km_core_state *state,
- km_core_virtual_key vk,
- uint16_t modifier_state,
- uint8_t is_key_down,
- uint16_t event_flags);
-```
## Parameters
-`state`
-: A pointer to the opaque state object.
+### state
+A pointer to the opaque state object.
-`vk`
-: A virtual key to be processed.
+### vk
+A virtual key to be processed.
-`modifier_state`
-: The combinations of modifier keys set at the time key `vk` was pressed, bitmask
- from the [km_core_modifier_state] enum.
+### modifier_state
+The combinations of modifier keys set at the time key
+`vk` was pressed, bitmask from the
+[km_core_modifier_state] enum.
-`event_flags`
-: Event level flags, see [km_core_event_flags]
+### event_flags
+Event level flags, see [km_core_event_flags]
## Returns
+One of the following values:
`KM_CORE_STATUS_OK`
: On success.
@@ -74,17 +55,25 @@ km_core_process_event(km_core_state *state,
: In the event memory is unavailable to allocate internal buffers.
`KM_CORE_STATUS_INVALID_ARGUMENT`
-: In the event the `state` pointer is null or an invalid virtual key or modifier
- state is passed.
+: In the event the `state` pointer is null or an invalid virtual key or
+modifier state is passed.
--------------------------------------------------------------------------------
-
-# km_core_event() {#km_core_event}
+```c
+KMN_API
+km_core_status
+km_core_process_event(
+ km_core_state const *state,
+ km_core_virtual_key vk,
+ uint16_t modifier_state,
+ uint8_t is_key_down,
+ uint16_t event_flags
+);
+```
-## Description
+# km_core_event function {#km_core_event}
-Tell the keyboard processor that an external event has occurred, such as a keyboard
-being activated through the language switching UI.
+Tell the keyboard processor that an external event has occurred, such as a
+keyboard being activated through the language switching UI.
The keyboard processor may generate actions which should be processed by the
consumer of the API.
@@ -92,30 +81,22 @@ consumer of the API.
The actions will be cleared at the start of this call; options and context in
the state may also be modified.
-## Specification
-```c
-KMN_API
-km_core_status
-km_core_event(
- km_core_state *state,
- uint32_t event,
- void* data
-);
-
-```
## Parameters
-`state`
-: A pointer to the opaque state object.
+### state
+A pointer to the opaque state object.
-`event`
-: The event to be processed, from [km_core_event_code] enumeration
+### event
+The event to be processed, from [km_core_event_code]
+enumeration
-`data`
-: Additional event-specific data. Currently unused, must be nullptr.
+### data
+Additional event-specific data. Currently unused, must be
+nullptr.
## Returns
+One of the following values:
`KM_CORE_STATUS_OK`
: On success.
@@ -124,40 +105,38 @@ km_core_event(
: In the event memory is unavailable to allocate internal buffers.
`KM_CORE_STATUS_INVALID_ARGUMENT`
-: In the event the `state` pointer is null or an invalid event or data is passed.
+: In the event the `state` pointer is null or an invalid event or data is
+passed.
--------------------------------------------------------------------------------
+```c
+KMN_API
+km_core_status
+km_core_event(
+ km_core_state const *state,
+ uint32_t event,
+ void* data
+);
+```
# km_core_event_code enum {#km_core_event_code}
-## Description
-
Possible events to be passed into Keyman Core from the Platform layer.
-## Specification
-
```c
enum km_core_event_code {
+ /** A keyboard has been activated by the user. The processor may use this
+ * event, for example, to switch caps lock state or provide other UX. */
KM_CORE_EVENT_KEYBOARD_ACTIVATED = 1,
- //future: KM_CORE_EVENT_KEYBOARD_DEACTIVATED = 2,
};
-```
-## Values
-
-`KM_CORE_EVENT_KEYBOARD_ACTIVATED`
-: A keyboard has been activated by the user. The processor may use this
- event, for example, to switch caps lock state or provide other UX.
-
-
-[km_core_cp]: background#km_core_cp "km_core_cp type"
+[km_core_cu]: background#km_core_cu "km_core_cu type"
[km_core_usv]: background#km_core_usv "km_core_usv type"
[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
[km_core_status]: background#km_core_status "km_core_status type"
-[km_core_modifier_state]: background#km_core_modifier_state "km_core_modifier_state type"
[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
[km_core_state]: background#km_core_state "km_core_state struct"
[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
[km_core_attr]: background#km_core_attr "km_core_attr struct"
[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
@@ -172,8 +151,8 @@ enum km_core_event_code {
[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
-[km_core_state_options_to_json]: options#km_core_state_options_to_json "km_core_state_options_to_json function"
[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
@@ -182,6 +161,7 @@ enum km_core_event_code {
[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
@@ -190,9 +170,27 @@ enum km_core_event_code {
[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
-[km_core_cp_dispose]: keyboards#km_core_cp_dispose "km_core_cp_dispose function"
-[km_core_state_to_json]: keyboards#km_core_state_to_json "km_core_state_to_json function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
[km_core_event]: processor#km_core_event "km_core_event function"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
\ No newline at end of file
diff --git a/core/docs/api/state.md b/core/docs/api/state.md
index 32d3dc9b325..b184824e7ce 100644
--- a/core/docs/api/state.md
+++ b/core/docs/api/state.md
@@ -1,6 +1,7 @@
---
title: State and Actions - Keyman Core API
---
+
A State object maintains all per keyboard related state including context
and dynamic options ("option stores" in kmn format).
@@ -11,162 +12,107 @@ owned by the state object.
-------------------------------------------------------------------------------
-# km_core_caps_state enum {#km_core_caps_state}
-## Description
+# km_core_caps_state enum {#km_core_caps_state}
Describes a change to the hardware caps lock indicator state requested by
Keyman Core to the Platform layer.
-## Specification
```c
-typedef enum { KM_CORE_CAPS_UNCHANGED = -1, KM_CORE_CAPS_OFF = 0, KM_CORE_CAPS_ON = 1 } km_core_caps_state;
-
+typedef enum {
+ /** Caps lock state has not changed in this event. */
+ KM_CORE_CAPS_UNCHANGED = -1,
+ /** As a result of processing this event, the Platform layer should switch off
+ * Caps Lock on the hardware keyboard. */
+ KM_CORE_CAPS_OFF = 0,
+ /** As a result of processing this event, the Platform layer should switch on
+ * Caps Lock on the hardware keyboard.*/
+ KM_CORE_CAPS_ON = 1
+} km_core_caps_state;
```
-## Values
-
-`KM_CORE_CAPS_UNCHANGED`
-: Caps lock state has not changed in this event.
-
-`KM_CORE_CAPS_OFF`
-: As a result of processing this event, the Platform layer should switch off
- Caps Lock on the hardware keyboard.
-
-`KM_CORE_CAPS_ON`
-: As a result of processing this event, the Platform layer should switch on
- Caps Lock on the hardware keyboard.
-
--------------------------------------------------------------------------------
# km_core_actions struct {#km_core_actions}
-## Description
-
-This structure provides the results of processing a key event to the Platform layer and
-should be processed by the Platform layer to issue commands to the os text
-services framework to transform the text store in the Client Application, among
-other actions.
+This structure provides the results of processing a key event to the Platform
+layer and should be processed by the Platform layer to issue commands to the
+os text services framework to transform the text store in the Client
+Application, among other actions.
This API replaces the Action items APIs, which are now deprecated and will be
removed in the future.
-## Specification
-
```c
typedef struct {
+ /** Number of codepoints (not codeunits!) to delete from app context. */
unsigned int code_points_to_delete;
+ /** Null-term string of characters to insert into document. */
const km_core_usv* output;
+ /** List of options to persist, terminated with `KM_CORE_OPTIONS_END`. */
km_core_option_item * persist_options;
+ /** Issue a beep, 0 = no, 1 = yes. */
km_core_bool do_alert;
+ /** Emit the (unmodified) input keystroke to the application, 0 = no, 1 = yes.
+ * On most platforms this signals whether the processor handled the event (0)
+ * or not (1). See also [key handling](keyhandling). */
km_core_bool emit_keystroke;
+ /** -1=unchanged, 0=off, 1=on */
km_core_caps_state new_caps_lock_state;
+ /** Reference copy of actual UTF32 codepoints deleted from end of context
+ * (closest to caret) exactly `code_points_to_delete` in length (plus null
+ * terminator). Used to determine encoding conversion differences when
+ * deleting; only set when using [km_core_state_get_actions], otherwise
+ * `nullptr`. */
const km_core_usv* deleted_context;
} km_core_actions;
```
-## Members
-
-`code_points_to_delete`
-: Number of codepoints (not codeunits!) to delete from app context.
-
-`output`
-: Null-term string of characters to insert into document.
-
-`persist_options`
-: List of options to persist, terminated with `KM_CORE_OPTIONS_END`.
-
-`do_alert`
-: Issue a beep, 0 = no, 1 = yes.
+# km_core_state_get_actions function {#km_core_state_get_actions}
-`emit_keystroke`
-: Emit the (unmodified) input keystroke to the application, 0 = no, 1 = yes.
- On most platforms this signals whether the processor handled the event
- (0) or not (1). See also [key handling](keyhandling).
+Returns a pointer to an actions object which details all the actions that the
+Platform layer must take after a keystroke. The `code_points_to_delete`
+action must be performed before the `output` action, but the other actions
+may be performed in any order.
-`new_caps_lock_state`
-: -1=unchanged, 0=off, 1=on
-`deleted_context`
-: Reference copy of actual UTF32 codepoints deleted from end of context
- (closest to caret) exactly `code_points_to_delete` in length (plus null
- terminator). Used to determine encoding conversion differences when
- deleting; only set when using [km_core_state_get_actions], otherwise `nullptr`.
-
--------------------------------------------------------------------------------
-
-# km_core_state_get_actions() {#km_core_state_get_actions}
+## Parameters
-## Description
+### state
+An opaque pointer to a state object.
-Returns a pointer to an actions object which details all the actions
-that the Platform layer must take after a keystroke. The `code_points_to_delete`
-action must be performed before the `output` action, but the other
-actions may be performed in any order.
+## Returns
+A pointer to a [km_core_actions] object. This data becomes invalid
+when the state object is destroyed, or after a call to
+[km_core_process_event]. Do not modify the contents of this data.
-## Specification
```c
KMN_API
km_core_actions const *
km_core_state_get_actions(
km_core_state const *state
);
-
```
-## Parameters
-
-`state`
-: An opaque pointer to a state object.
-
-## Returns
-
-A pointer to a [km_core_actions] object. This data becomes invalid
-when the state object is destroyed, or after a call to
-[km_core_process_event]. Do not modify the contents of this data.
-
--------------------------------------------------------------------------------
# km_core_context_status enum {#km_core_context_status}
-## Description
-
Return values for [km_core_state_context_set_if_needed].
-## Specification
-
```c
typedef enum {
+ /** Cached context change was not needed. */
KM_CORE_CONTEXT_STATUS_UNCHANGED = 0,
+ /** Cached context was set to application context. */
KM_CORE_CONTEXT_STATUS_UPDATED = 1,
+ /** Application context was invalid, perhaps had unpaired surrogates, and so
+ * cached context was cleared instead. */
KM_CORE_CONTEXT_STATUS_CLEARED = 2,
+ /** Internal error. */
KM_CORE_CONTEXT_STATUS_ERROR = 3,
+ /** One or more parameters was null. */
KM_CORE_CONTEXT_STATUS_INVALID_ARGUMENT = 4,
} km_core_context_status;
-
```
-## Values
-
-`KM_CORE_CONTEXT_STATUS_UNCHANGED`
-: Cached context change was not needed.
-
-`KM_CORE_CONTEXT_STATUS_UPDATED`
-: Cached context was set to application context.
-
-`KM_CORE_CONTEXT_STATUS_CLEARED`
-: Application context was invalid, perhaps had unpaired surrogates,
- and so cached context was cleared instead.
-
-`KM_CORE_CONTEXT_STATUS_ERROR`
-: Internal error.
-
-`KM_CORE_CONTEXT_STATUS_INVALID_ARGUMENT`
-: One or more parameters was null.
-
--------------------------------------------------------------------------------
-
-# km_core_state_context_set_if_needed() {#km_core_state_context_set_if_needed}
-
-## Description
+# km_core_state_context_set_if_needed function {#km_core_state_context_set_if_needed}
Sets the internal cached context for the state object, to the passed-in
application context string, if it differs from the codepoints in the
@@ -181,34 +127,31 @@ application context, and thus any cached markers will be cleared.
[km_core_state_context_set_if_needed] and [km_core_state_context_clear]
will replace most uses of the existing Core context APIs.
-## Specification
-```c
-KMN_API
-km_core_context_status
-km_core_state_context_set_if_needed(
- km_core_state *state,
- km_core_cp const *application_context
-);
-```
## Parameters
-`state`
-: An opaque pointer to a state object.
+### state
+An opaque pointer to a state object.
-`application_context`
-: A pointer to an null-terminated array of utf16 encoded data representing
- the current context from the application.
+### application_context
+A pointer to an null-terminated array of utf16
+encoded data representing the current context
+from the application.
## Returns
-
A value from the [km_core_context_status] enum.
--------------------------------------------------------------------------------
+```c
+KMN_API
+km_core_context_status
+km_core_state_context_set_if_needed(
+ km_core_state *state,
+ km_core_cu const *application_context
+);
+```
-# km_core_state_context_clear() {#km_core_state_context_clear}
-## Description
+# km_core_state_context_clear function {#km_core_state_context_clear}
Clears the internal cached context for the state. This is the same as
`km_core_context_clear(km_core_state_context(&state))`.
@@ -216,21 +159,14 @@ Clears the internal cached context for the state. This is the same as
[km_core_state_context_set_if_needed] and [km_core_state_context_clear]
will replace most uses of the existing Core context APIs.
-## Specification
-```c
-KMN_API
-km_core_status
-km_core_state_context_clear(
- km_core_state *state
-);
-```
## Parameters
-`state`:
+### state
An opaque pointer to a state object.
## Returns
+One of the following values:
`KM_CORE_STATUS_OK`
: On success.
@@ -238,17 +174,22 @@ An opaque pointer to a state object.
`KM_CORE_STATUS_INVALID_ARGUMENT`
: If any parameters are null.
--------------------------------------------------------------------------------
-
+```c
+KMN_API
+km_core_status
+km_core_state_context_clear(
+ km_core_state *state
+);
+```
-[km_core_cp]: background#km_core_cp "km_core_cp type"
+[km_core_cu]: background#km_core_cu "km_core_cu type"
[km_core_usv]: background#km_core_usv "km_core_usv type"
[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
[km_core_status]: background#km_core_status "km_core_status type"
-[km_core_modifier_state]: background#km_core_modifier_state "km_core_modifier_state type"
[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
[km_core_state]: background#km_core_state "km_core_state struct"
[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
[km_core_attr]: background#km_core_attr "km_core_attr struct"
[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
@@ -263,8 +204,8 @@ An opaque pointer to a state object.
[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
-[km_core_state_options_to_json]: options#km_core_state_options_to_json "km_core_state_options_to_json function"
[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
@@ -273,6 +214,7 @@ An opaque pointer to a state object.
[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
@@ -281,9 +223,27 @@ An opaque pointer to a state object.
[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
-[km_core_cp_dispose]: keyboards#km_core_cp_dispose "km_core_cp_dispose function"
-[km_core_state_to_json]: keyboards#km_core_state_to_json "km_core_state_to_json function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
[km_core_event]: processor#km_core_event "km_core_event function"
[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
\ No newline at end of file
diff --git a/core/docs/api/virtual-keys.md b/core/docs/api/virtual-keys.md
new file mode 100644
index 00000000000..c86a0259b1c
--- /dev/null
+++ b/core/docs/api/virtual-keys.md
@@ -0,0 +1,391 @@
+---
+title: Virtual Keys - Keyman Core API
+---
+
+
+API declarations for modifier keys, handy access masks and Keyman VKEY names.
+These follow the same keytop->code associations as the Windows API. This is a
+separate header to maintain readability of the primary API header.
+
+
+# km_core_modifier_state enum {#km_core_modifier_state}
+
+An integral type bitmask representing the state of each modifier key.
+
+```c
+enum km_core_modifier_state {
+ KM_CORE_MODIFIER_NONE = 0,
+ KM_CORE_MODIFIER_LCTRL = 1 << 0,
+ KM_CORE_MODIFIER_RCTRL = 1 << 1,
+ KM_CORE_MODIFIER_LALT = 1 << 2,
+ KM_CORE_MODIFIER_RALT = 1 << 3,
+ KM_CORE_MODIFIER_SHIFT = 1 << 4,
+ KM_CORE_MODIFIER_CTRL = 1 << 5,
+ KM_CORE_MODIFIER_ALT = 1 << 6,
+ /*
+ KM_CORE_MODIFIER_META = 1 << 7, // Either Meta-key flag (tentative). Not usable by keyboards currently
+ */
+ KM_CORE_MODIFIER_CAPS = 1 << 8,
+ KM_CORE_MODIFIER_NOCAPS = 1 << 9,
+ /*
+ KM_CORE_MODIFIER_NUMLOCK = 1 << 10,
+ KM_CORE_MODIFIER_NONUMLOCK = 1 << 11,
+ KM_CORE_MODIFIER_SCROLLOCK = 1 << 12,
+ KM_CORE_MODIFIER_NOSCROLLOCK = 1 << 13,
+ KM_CORE_MODIFIER_VIRTUALKEY = 1 << 14,
+ */
+};
+```
+
+# km_core_modifier_mask {#km_core_modifier_mask}
+
+An integral type bitmask representing common bitmask sets for
+km_core_modifier_state
+
+```c
+enum km_core_modifier_mask {
+ KM_CORE_MODIFIER_MASK_ALL = 0x7f,
+ KM_CORE_MODIFIER_MASK_ALT_GR_SIM = KM_CORE_MODIFIER_LCTRL|KM_CORE_MODIFIER_LALT,
+ KM_CORE_MODIFIER_MASK_CHIRAL = 0x1f,
+ KM_CORE_MODIFIER_MASK_IS_CHIRAL = 0x0f,
+ KM_CORE_MODIFIER_MASK_NON_CHIRAL = 0x7f,
+ KM_CORE_MODIFIER_MASK_CAPS = 0x0300,
+/*KM_CORE_MODIFIER_MASK_NUMLOCK = 0x0C00,
+ KM_CORE_MODIFIER_MASK_SCROLLLOCK = 0x3000,*/
+};
+```
+
+# km_core_virtual_key_value {#km_core_virtual_key_value}
+
+These are Windows API VKEYs, using Keyman VKEY names. Underlying values from
+winuser.h
+(https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes)
+Note, some codes are defined here for parity with winuser.h, but are not
+available in .kmn language. These codes are marked as "internal" by using
+`__` in the name: `KM_CORE_VKEY____`
+
+```c
+enum km_core_virtual_key_value {
+ KM_CORE_VKEY__00,
+ KM_CORE_VKEY_LBUTTON, // 0x01
+ KM_CORE_VKEY_RBUTTON, // 0x02
+ KM_CORE_VKEY_CANCEL, // 0x03
+ KM_CORE_VKEY_MBUTTON, // 0x04
+ KM_CORE_VKEY__XBUTTON1__, // 0x05
+ KM_CORE_VKEY__XBUTTON2__, // 0x06
+ KM_CORE_VKEY__07, // 0x07 - Reserved
+ KM_CORE_VKEY_BKSP, // 0x08 - VK_BACK
+ KM_CORE_VKEY_TAB, // 0x09
+ KM_CORE_VKEY__0A, // 0x0a - Reserved
+ KM_CORE_VKEY__0B, // 0x0b - Reserved
+ KM_CORE_VKEY_KP5, // 0x0c - VK_CLEAR
+ KM_CORE_VKEY_ENTER, // 0x0d - VK_RETURN
+ KM_CORE_VKEY__0E, // 0x0e - Reserved
+ KM_CORE_VKEY__0F, // 0x0f - Reserved
+ KM_CORE_VKEY_SHIFT, // 0x10
+ KM_CORE_VKEY_CONTROL, // 0x11
+ KM_CORE_VKEY_ALT, // 0x12 - VK_MENU
+ KM_CORE_VKEY_PAUSE, // 0x13
+ KM_CORE_VKEY_CAPS, // 0x14 - VK_CAPITAL
+ KM_CORE_VKEY__KANA__, // 0x15
+ KM_CORE_VKEY__HANGUL__, // 0x16
+ KM_CORE_VKEY__JUNJA__, // 0x17
+ KM_CORE_VKEY__FINAL__, // 0x18
+ KM_CORE_VKEY__HANJA__, // 0x19 - or VK_KANJI
+ KM_CORE_VKEY__IMEOFF__, // 0x1a - VK_IME_OFF
+ KM_CORE_VKEY_ESC, // 0x1b - VK_ESCAPE
+ KM_CORE_VKEY__CONVERT__, // 0x1c
+ KM_CORE_VKEY__NONCONVERT__, // 0x1d
+ KM_CORE_VKEY__ACCEPT__, // 0x1e
+ KM_CORE_VKEY__MODECHANGE__, // 0x1f
+ KM_CORE_VKEY_SPACE, // 0x20
+ KM_CORE_VKEY_PGUP, // 0x21 - VK_PRIOR
+ KM_CORE_VKEY_PGDN, // 0x22 - VK_NEXT
+ KM_CORE_VKEY_END, // 0x23
+ KM_CORE_VKEY_HOME, // 0x24
+ KM_CORE_VKEY_LEFT, // 0x25
+ KM_CORE_VKEY_UP, // 0x26
+ KM_CORE_VKEY_RIGHT, // 0x27
+ KM_CORE_VKEY_DOWN, // 0x28
+ KM_CORE_VKEY_SEL, // 0x29 - VK_SELECT
+ KM_CORE_VKEY_PRINT, // 0x2a
+ KM_CORE_VKEY_EXEC, // 0x2b - VK_EXECUTE
+ KM_CORE_VKEY_PRTSCN, // 0x2c - VK_SNAPSHOT
+ KM_CORE_VKEY_INS, // 0x2d - VK_INSERT
+ KM_CORE_VKEY_DEL, // 0x2e - VK_DELETE
+ KM_CORE_VKEY_HELP, // 0x2f
+ KM_CORE_VKEY_0, // 0x30
+ KM_CORE_VKEY_1, // 0x31
+ KM_CORE_VKEY_2, // 0x32
+ KM_CORE_VKEY_3, // 0x33
+ KM_CORE_VKEY_4, // 0x34
+ KM_CORE_VKEY_5, // 0x35
+ KM_CORE_VKEY_6, // 0x36
+ KM_CORE_VKEY_7, // 0x37
+ KM_CORE_VKEY_8, // 0x38
+ KM_CORE_VKEY_9, // 0x39
+ KM_CORE_VKEY__3A, // 0x3a - undefined
+ KM_CORE_VKEY__3B, // 0x3b - undefined
+ KM_CORE_VKEY__3C, // 0x3c - undefined
+ KM_CORE_VKEY__3D, // 0x3d - undefined
+ KM_CORE_VKEY__3E, // 0x3e - undefined
+ KM_CORE_VKEY__3F, // 0x3f - undefined
+ KM_CORE_VKEY__40, // 0x40 - undefined
+ KM_CORE_VKEY_A, // 0x41
+ KM_CORE_VKEY_B, // 0x42
+ KM_CORE_VKEY_C, // 0x43
+ KM_CORE_VKEY_D, // 0x44
+ KM_CORE_VKEY_E, // 0x45
+ KM_CORE_VKEY_F, // 0x46
+ KM_CORE_VKEY_G, // 0x47
+ KM_CORE_VKEY_H, // 0x48
+ KM_CORE_VKEY_I, // 0x49
+ KM_CORE_VKEY_J, // 0x4a
+ KM_CORE_VKEY_K, // 0x4b
+ KM_CORE_VKEY_L, // 0x4c
+ KM_CORE_VKEY_M, // 0x4d
+ KM_CORE_VKEY_N, // 0x4e
+ KM_CORE_VKEY_O, // 0x4f
+ KM_CORE_VKEY_P, // 0x50
+ KM_CORE_VKEY_Q, // 0x51
+ KM_CORE_VKEY_R, // 0x52
+ KM_CORE_VKEY_S, // 0x53
+ KM_CORE_VKEY_T, // 0x54
+ KM_CORE_VKEY_U, // 0x55
+ KM_CORE_VKEY_V, // 0x56
+ KM_CORE_VKEY_W, // 0x57
+ KM_CORE_VKEY_X, // 0x58
+ KM_CORE_VKEY_Y, // 0x59
+ KM_CORE_VKEY_Z, // 0x5a
+ KM_CORE_VKEY__LWIN__, // 0x5b
+ KM_CORE_VKEY__RWIN__, // 0x5c
+ KM_CORE_VKEY__APPS__, // 0x5d
+ KM_CORE_VKEY__5E, // 0x5e - reserved
+ KM_CORE_VKEY__SLEEP__, // 0x5f
+ KM_CORE_VKEY_NP0, // 0x60 - VK_NUMPAD0
+ KM_CORE_VKEY_NP1, // 0x61 - VK_NUMPAD1
+ KM_CORE_VKEY_NP2, // 0x62 - VK_NUMPAD2
+ KM_CORE_VKEY_NP3, // 0x63 - VK_NUMPAD3
+ KM_CORE_VKEY_NP4, // 0x64 - VK_NUMPAD4
+ KM_CORE_VKEY_NP5, // 0x65 - VK_NUMPAD5
+ KM_CORE_VKEY_NP6, // 0x66 - VK_NUMPAD6
+ KM_CORE_VKEY_NP7, // 0x67 - VK_NUMPAD7
+ KM_CORE_VKEY_NP8, // 0x68 - VK_NUMPAD8
+ KM_CORE_VKEY_NP9, // 0x69 - VK_NUMPAD9
+ KM_CORE_VKEY_NPSTAR, // 0x6a - VK_MULTIPLY
+ KM_CORE_VKEY_NPPLUS, // 0x6b - VK_ADD
+ KM_CORE_VKEY_SEPARATOR, // 0x6c
+ KM_CORE_VKEY_NPMINUS, // 0x6d - VK_SUBTRACT
+ KM_CORE_VKEY_NPDOT, // 0x6e - VK_DECIMAL
+ KM_CORE_VKEY_NPSLASH, // 0x6f - VK_DIVIDE
+ KM_CORE_VKEY_F1, // 0x70
+ KM_CORE_VKEY_F2, // 0x71
+ KM_CORE_VKEY_F3, // 0x72
+ KM_CORE_VKEY_F4, // 0x73
+ KM_CORE_VKEY_F5, // 0x74
+ KM_CORE_VKEY_F6, // 0x75
+ KM_CORE_VKEY_F7, // 0x76
+ KM_CORE_VKEY_F8, // 0x77
+ KM_CORE_VKEY_F9, // 0x78
+ KM_CORE_VKEY_F10, // 0x79
+ KM_CORE_VKEY_F11, // 0x7a
+ KM_CORE_VKEY_F12, // 0x7b
+ KM_CORE_VKEY_F13, // 0x7c
+ KM_CORE_VKEY_F14, // 0x7d
+ KM_CORE_VKEY_F15, // 0x7e
+ KM_CORE_VKEY_F16, // 0x7f
+ KM_CORE_VKEY_F17, // 0x80
+ KM_CORE_VKEY_F18, // 0x81
+ KM_CORE_VKEY_F19, // 0x82
+ KM_CORE_VKEY_F20, // 0x83
+ KM_CORE_VKEY_F21, // 0x84
+ KM_CORE_VKEY_F22, // 0x85
+ KM_CORE_VKEY_F23, // 0x86
+ KM_CORE_VKEY_F24, // 0x87
+ KM_CORE_VKEY__88, // 0x88 - reserved
+ KM_CORE_VKEY__89, // 0x89 - reserved
+ KM_CORE_VKEY__8A, // 0x8a - reserved
+ KM_CORE_VKEY__8B, // 0x8b - reserved
+ KM_CORE_VKEY__8C, // 0x8c - reserved
+ KM_CORE_VKEY__8D, // 0x8d - reserved
+ KM_CORE_VKEY__8E, // 0x8e - reserved
+ KM_CORE_VKEY__8F, // 0x8f - reserved
+ KM_CORE_VKEY_NUMLOCK, // 0x90
+ KM_CORE_VKEY_SCROLL, // 0x91
+ KM_CORE_VKEY__92, // 0x92 - OEM specific
+ KM_CORE_VKEY__93, // 0x93 - OEM specific
+ KM_CORE_VKEY__94, // 0x94 - OEM specific
+ KM_CORE_VKEY__95, // 0x95 - OEM specific
+ KM_CORE_VKEY__96, // 0x96 - OEM specific
+ KM_CORE_VKEY__97, // 0x97 - unassigned
+ KM_CORE_VKEY__98, // 0x98 - unassigned
+ KM_CORE_VKEY__99, // 0x99 - unassigned
+ KM_CORE_VKEY__9A, // 0x9a - unassigned
+ KM_CORE_VKEY__9B, // 0x9b - unassigned
+ KM_CORE_VKEY__9C, // 0x9c - unassigned
+ KM_CORE_VKEY__9D, // 0x9d - unassigned
+ KM_CORE_VKEY__9E, // 0x9e - unassigned
+ KM_CORE_VKEY__9F, // 0x9f - unassigned
+ KM_CORE_VKEY__LSHIFT__, // 0xa0
+ KM_CORE_VKEY__RSHIFT__, // 0xa1
+ KM_CORE_VKEY__LCONTROL__, // 0xa2
+ KM_CORE_VKEY__RCONTROL__, // 0xa3
+ KM_CORE_VKEY__LMENU__, // 0xa4
+ KM_CORE_VKEY__RMENU__, // 0xa5
+ KM_CORE_VKEY__BROWSERBACK__, // 0xa6 - VK_BROWSER_BACK
+ KM_CORE_VKEY__BROWSERFORWARD__, // 0xa7 - VK_BROWSER_FORWARD
+ KM_CORE_VKEY__BROWSERREFRESH__, // 0xa8 - VK_BROWSER_REFRESH
+ KM_CORE_VKEY__BROWSERSTOP__, // 0xa9 - VK_BROWSER_STOP
+ KM_CORE_VKEY__BROWSERSEARCH__, // 0xaa - VK_BROWSER_SEARCH
+ KM_CORE_VKEY_BROWSERFAVORITES, // 0xab - VK_BROWSER_FAVORITES
+ KM_CORE_VKEY__BROWSERHOME__, // 0xac - VK_BROWSER_HOME
+ KM_CORE_VKEY__VOLUMEMUTE__, // 0xad - VK_VOLUME_MUTE
+ KM_CORE_VKEY__VOLUMEDOWN__, // 0xae - VK_VOLUME_DOWN
+ KM_CORE_VKEY__VOLUMEUP__, // 0xaf - VK_VOLUME_UP
+ KM_CORE_VKEY__MEDIANEXT__, // 0xb0 - VK_MEDIA_NEXT_TRACK
+ KM_CORE_VKEY__MEDIAPREV__, // 0xb1 - VK_MEDIA_PREV_TRACK
+ KM_CORE_VKEY__MEDIASTOP__, // 0xb2 - VK_MEDIA_STOP
+ KM_CORE_VKEY__MEDIAPLAY__, // 0xb3 - VK_MEDIA_PLAY_PAUSE
+ KM_CORE_VKEY__MAIL__, // 0xb4 - VK_LAUNCH_MAIL
+ KM_CORE_VKEY__MEDIA__, // 0xb5 - VK_LAUNCH_MEDIA_SELECT
+ KM_CORE_VKEY__APP1__, // 0xb6 - VK_LAUNCH_APP1
+ KM_CORE_VKEY__APP2__, // 0xb7 - VK_LAUNCH_APP2
+ KM_CORE_VKEY__B8, // 0xb8 - reserved
+ KM_CORE_VKEY__B9, // 0xb9 - reserved
+ KM_CORE_VKEY_COLON, // 0xba - VK_OEM_1
+ KM_CORE_VKEY_EQUAL, // 0xbb - VK_OEM_PLUS
+ KM_CORE_VKEY_COMMA, // 0xbc - VK_OEM_COMMA
+ KM_CORE_VKEY_HYPHEN, // 0xbd - VK_OEM_MINUS
+ KM_CORE_VKEY_PERIOD, // 0xbe - VK_OEM_PERIOD
+ KM_CORE_VKEY_SLASH, // 0xbf - VK_OEM_2
+ KM_CORE_VKEY_BKQUOTE, // 0xc0 - VK_OEM_3
+ KM_CORE_VKEY__C1, // 0xc1 - reserved
+ KM_CORE_VKEY__C2, // 0xc2 - reserved
+ KM_CORE_VKEY__C3, // 0xc3 - reserved
+ KM_CORE_VKEY__C4, // 0xc4 - reserved
+ KM_CORE_VKEY__C5, // 0xc5 - reserved
+ KM_CORE_VKEY__C6, // 0xc6 - reserved
+ KM_CORE_VKEY__C7, // 0xc7 - reserved
+ KM_CORE_VKEY__C8, // 0xc8 - reserved
+ KM_CORE_VKEY__C9, // 0xc9 - reserved
+ KM_CORE_VKEY__CA, // 0xca - reserved
+ KM_CORE_VKEY__CB, // 0xcb - reserved
+ KM_CORE_VKEY__CC, // 0xcc - reserved
+ KM_CORE_VKEY__CD, // 0xcd - reserved
+ KM_CORE_VKEY__CE, // 0xce - reserved
+ KM_CORE_VKEY__CF, // 0xcf - reserved
+ KM_CORE_VKEY__D0, // 0xd0 - reserved
+ KM_CORE_VKEY__D1, // 0xd1 - reserved
+ KM_CORE_VKEY__D2, // 0xd2 - reserved
+ KM_CORE_VKEY__D3, // 0xd3 - reserved
+ KM_CORE_VKEY__D4, // 0xd4 - reserved
+ KM_CORE_VKEY__D5, // 0xd5 - reserved
+ KM_CORE_VKEY__D6, // 0xd6 - reserved
+ KM_CORE_VKEY__D7, // 0xd7 - reserved
+ KM_CORE_VKEY__D8, // 0xd8 - reserved
+ KM_CORE_VKEY__D9, // 0xd9 - reserved
+ KM_CORE_VKEY__DA, // 0xda - reserved
+ KM_CORE_VKEY_LBRKT, // 0xdb - VK_OEM_4
+ KM_CORE_VKEY_BKSLASH, // 0xdc - VK_OEM_5
+ KM_CORE_VKEY_RBRKT, // 0xdd - VK_OEM_6
+ KM_CORE_VKEY_QUOTE, // 0xde - VK_OEM_7
+ KM_CORE_VKEY_oDF, // 0xdf - VK_OEM_8
+ KM_CORE_VKEY_oE0, // 0xe0 - reserved
+ KM_CORE_VKEY_oE1, // 0xe1 - reserved
+ KM_CORE_VKEY_oE2, // 0xe2 - VK_OEM_102 - 102nd key on European layouts
+ KM_CORE_VKEY_oE3, // 0xe3 - OEM specific
+ KM_CORE_VKEY_oE4, // 0xe4 - OEM specific
+ KM_CORE_VKEY__PROCESSKEY__, // 0xe5
+ KM_CORE_VKEY_oE6, // 0xe6 - OEM specific
+ KM_CORE_VKEY__PACKET__, // 0xe7
+ KM_CORE_VKEY__E8, // 0xe8 - unassigned
+ KM_CORE_VKEY_oE9, // 0xe9 - OEM specific
+ KM_CORE_VKEY_oEA, // 0xea - OEM specific
+ KM_CORE_VKEY_oEB, // 0xeb - OEM specific
+ KM_CORE_VKEY_oEC, // 0xec - OEM specific
+ KM_CORE_VKEY_oED, // 0xed - OEM specific
+ KM_CORE_VKEY_oEE, // 0xee - OEM specific
+ KM_CORE_VKEY_oEF, // 0xef - OEM specific
+ KM_CORE_VKEY_oF0, // 0xf0 - OEM specific
+ KM_CORE_VKEY_oF1, // 0xf1 - OEM specific
+ KM_CORE_VKEY_oF2, // 0xf2 - OEM specific
+ KM_CORE_VKEY_oF3, // 0xf3 - OEM specific
+ KM_CORE_VKEY_oF4, // 0xf4 - OEM specific
+ KM_CORE_VKEY_oF5, // 0xf5 - OEM specific
+ KM_CORE_VKEY__ATTN__, // 0xf6
+ KM_CORE_VKEY__CRSEL__, // 0xf7
+ KM_CORE_VKEY__EXSEL__, // 0xf8
+ KM_CORE_VKEY__EREOF__, // 0xf9
+ KM_CORE_VKEY__PLAY__, // 0xfa
+ KM_CORE_VKEY__ZOOM__, // 0xfb
+ KM_CORE_VKEY__NONAME__, // 0xfc
+ KM_CORE_VKEY__PA1__, // 0xfd
+ KM_CORE_VKEY__OEMCLEAR__, // 0xfe
+ KM_CORE_VKEY__FF, // 0xff
+};
+
+[km_core_cu]: background#km_core_cu "km_core_cu type"
+[km_core_usv]: background#km_core_usv "km_core_usv type"
+[km_core_virtual_key]: background#km_core_virtual_key "km_core_virtual_key type"
+[km_core_status]: background#km_core_status "km_core_status type"
+[km_core_keyboard]: background#km_core_keyboard "km_core_keyboard struct"
+[km_core_state]: background#km_core_state "km_core_state struct"
+[km_core_options]: background#km_core_options "km_core_options struct"
+[km_core_keyboard_imx_platform]: background#km_core_keyboard_imx_platform "km_core_keyboard_imx_platform callback function"
+[km_core_status_codes]: background#km_core_status_codes "km_core_status_codes enum"
+[km_core_attr]: background#km_core_attr "km_core_attr struct"
+[km_core_tech_value]: background#km_core_tech_value "km_core_tech_value enum"
+[km_core_get_engine_attrs]: background#km_core_get_engine_attrs "km_core_get_engine_attrs function"
+[km_core_bool]: background#km_core_bool "km_core_bool enum"
+[km_core_caps_state]: state#km_core_caps_state "km_core_caps_state enum"
+[km_core_actions]: state#km_core_actions "km_core_actions struct"
+[km_core_state_get_actions]: state#km_core_state_get_actions "km_core_state_get_actions function"
+[km_core_context_status]: state#km_core_context_status "km_core_context_status enum"
+[km_core_state_context_set_if_needed]: state#km_core_state_context_set_if_needed "km_core_state_context_set_if_needed function"
+[km_core_state_context_clear]: state#km_core_state_context_clear "km_core_state_context_clear function"
+[km_core_option_scope]: options#km_core_option_scope "km_core_option_scope enum"
+[km_core_option_item]: options#km_core_option_item "km_core_option_item struct"
+[km_core_options_list_size]: options#km_core_options_list_size "km_core_options_list_size function"
+[km_core_state_option_lookup]: options#km_core_state_option_lookup "km_core_state_option_lookup function"
+[km_core_state_options_update]: options#km_core_state_options_update "km_core_state_options_update function"
+[km_core_keyboard_attrs]: keyboards#km_core_keyboard_attrs "km_core_keyboard_attrs struct"
+[km_core_keyboard_key]: keyboards#km_core_keyboard_key "km_core_keyboard_key struct"
+[km_core_keyboard_imx]: keyboards#km_core_keyboard_imx "km_core_keyboard_imx struct"
+[km_core_keyboard_load_from_blob]: keyboards#km_core_keyboard_load_from_blob "km_core_keyboard_load_from_blob function"
+[km_core_keyboard_dispose]: keyboards#km_core_keyboard_dispose "km_core_keyboard_dispose function"
+[km_core_keyboard_get_attrs]: keyboards#km_core_keyboard_get_attrs "km_core_keyboard_get_attrs function"
+[km_core_keyboard_get_key_list]: keyboards#km_core_keyboard_get_key_list "km_core_keyboard_get_key_list function"
+[km_core_keyboard_key_list_dispose]: keyboards#km_core_keyboard_key_list_dispose "km_core_keyboard_key_list_dispose function"
+[km_core_keyboard_get_imx_list]: keyboards#km_core_keyboard_get_imx_list "km_core_keyboard_get_imx_list function"
+[km_core_keyboard_imx_list_dispose]: keyboards#km_core_keyboard_imx_list_dispose "km_core_keyboard_imx_list_dispose function"
+[km_core_state_imx_register_callback]: keyboards#km_core_state_imx_register_callback "km_core_state_imx_register_callback function"
+[km_core_state_imx_deregister_callback]: keyboards#km_core_state_imx_deregister_callback "km_core_state_imx_deregister_callback function"
+[km_core_state_create]: keyboards#km_core_state_create "km_core_state_create function"
+[km_core_state_clone]: keyboards#km_core_state_clone "km_core_state_clone function"
+[km_core_state_dispose]: keyboards#km_core_state_dispose "km_core_state_dispose function"
+[km_core_debug_context_type]: keyboards#km_core_debug_context_type "km_core_debug_context_type enum"
+[km_core_state_context_debug]: keyboards#km_core_state_context_debug "km_core_state_context_debug function"
+[km_core_cu_dispose]: keyboards#km_core_cu_dispose "km_core_cu_dispose function"
+[km_core_event_flags]: processor#km_core_event_flags "km_core_event_flags enum"
+[km_core_process_event]: processor#km_core_process_event "km_core_process_event function"
+[km_core_event]: processor#km_core_event "km_core_event function"
+[km_core_event_code]: processor#km_core_event_code "km_core_event_code enum"
+[km_core_action_item]: actions#km_core_action_item "km_core_action_item struct"
+[km_core_state_action_items]: actions#km_core_state_action_items "km_core_state_action_items function"
+[km_core_state_queue_action_items]: actions#km_core_state_queue_action_items "km_core_state_queue_action_items function"
+[km_core_process_queued_actions]: actions#km_core_process_queued_actions "km_core_process_queued_actions function"
+[km_core_context_type]: context#km_core_context_type "km_core_context_type enum"
+[km_core_context_item]: context#km_core_context_item "km_core_context_item struct"
+[KM_CORE_CONTEXT_ITEM_END]: context#KM_CORE_CONTEXT_ITEM_END "KM_CORE_CONTEXT_ITEM_END macro"
+[km_core_state_get_intermediate_context]: context#km_core_state_get_intermediate_context "km_core_state_get_intermediate_context function"
+[km_core_context_items_dispose]: context#km_core_context_items_dispose "km_core_context_items_dispose function"
+[km_core_state_context]: context#km_core_state_context "km_core_state_context function"
+[km_core_state_app_context]: context#km_core_state_app_context "km_core_state_app_context function"
+[km_core_context_set]: context#km_core_context_set "km_core_context_set function"
+[km_core_context_clear]: context#km_core_context_clear "km_core_context_clear function"
+[km_core_context_item_list_size]: context#km_core_context_item_list_size "km_core_context_item_list_size function"
+[km_core_context_get]: context#km_core_context_get "km_core_context_get function"
+[km_core_context_length]: context#km_core_context_length "km_core_context_length function"
+[km_core_modifier_state]: virtual-keys#km_core_modifier_state "km_core_modifier_state enum"
+[km_core_modifier_mask]: virtual-keys#km_core_modifier_mask "km_core_modifier_mask "
+[km_core_virtual_key_value]: virtual-keys#km_core_virtual_key_value "km_core_virtual_key_value "
\ No newline at end of file
diff --git a/core/docs/internal/hotdoc.json b/core/docs/internal/hotdoc.json
deleted file mode 100644
index 946c62beac0..00000000000
--- a/core/docs/internal/hotdoc.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "project_name": "@project_name@",
- "project_version": "@project_version@",
- "sitemap": "@doc_dir@/sitemap.txt",
- "index": "@doc_dir@/markdown_files/index.md",
- "c_sources": [
- "@include_dir@/keyman/keyman_core_api.h"
- ],
- "c_include_directories": [
- "@include_dir@",
- "@doc_dir@/../../common/include"
- ],
- "c_smart_index" : true,
- "output": ".",
- "extra_assets": [
- "../../assets"
- ]
-}
diff --git a/core/docs/internal/markdown_files/index.md b/core/docs/internal/markdown_files/index.md
deleted file mode 100644
index 1075674191f..00000000000
--- a/core/docs/internal/markdown_files/index.md
+++ /dev/null
@@ -1,72 +0,0 @@
-# Keyman Keyboard Processor API
-
-## Requirements
-
-1. Cross platform.
-2. Cross language.
-3. Facilitate stateless operation of the Engine.
-4. Keyboard format agnostic -- support both KMN and future LDML based keyboards.
-5. Support querying Engine attributes.
-6. Support querying Keyboard attributes.
-7. Idempotent
-
-## Design decisions in support of requirements:
-
-- Use C or C99 types and calling convention for the interface, it has the
- broadest language FFI support. [1,2]
-- Have client (platform glue) code load keyboards, manage & pass state. [3,4,7]
-- Provide query calls to return static attributes data for keyboards and
- engine [5,6]
-- Provide get/set calls for client accessible keyboard state information [3,4]
-
-## Glossary
-
-- __Platform layer:__
-the code that consumes the Keyman Keyboard Processor API, and provides the
-operating system-specific handling of keystroke events and integration with
-applications.
-- __Client Application:__
-the application that has the focus and receives text events from the Platform
-layer.
-- __Context:__ Text preceding the insertion point
-- __Marker:__ Positional state that can be placed in the Context.
-- __Keyboard:__ A set of rules for execution by an Engine
-- __Option:__ A variable in a dynamic or static key value store.
-- __Processor:__
-The component that implements this API and can parse and execute a particular
-keyboard.
-- __State:__ An object that holds internal state of the Processor for a given
-insertion point
-- __Action:__
-A directive output by the processor detailing how the Platform layer should
-transform the Client Application's text buffer. There may be several items
-produced by a single keyboard event.
-- __Keyboard Event:__
-A virtual key board event and modifier map recevied from the Platform layer to be
-processed with the state object for this Client application.
-- __Virtual Key:__
-A code based on the US English layout, with values matching the Windows
-virtual key codes. See `keyman_core_api_vkeys.h` for definitions.
-- __Modifier Key:__
-The set of Control, Shift, Alt, Caps Lock keys. On some platforms these may
-have other names (e.g. Alt is called Option on macOS); other platform-specific
-modifiers such as Windows key are excluded from this set. Some modifiers are
-transient, such as Control, and others have long-lasting state, such as
-Caps Lock.
-
-## API
-
-### Namespace
-
-All calls, types and enums are prefixed with the namespace identifier `km_core_`
-
-### Changes from 16.0
-
-* The namespace identifier has changed from `km_kbp_` to `km_core_`.
-* Most context APIs are now private, and `km_core_context_set_if_needed` is the
- primary context function. Private APIs are available in
- `keyman_core_api_context.h`.
-* The action queue APIs are now private and deprecated. Instead, use
- `km_core_state_get_actions`. Private APIs are available in
- `keyman_core_api_actions.h`.
-* Debug APIs are available in `keyman_core_api_debug.h`.
diff --git a/core/docs/internal/meson.build b/core/docs/internal/meson.build
deleted file mode 100644
index a0f2bc4e6d5..00000000000
--- a/core/docs/internal/meson.build
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright: © 2018 SIL International.
-# Description: Cross platform build script to extract and generate
-# documentation for the API. This is speculative ATM.
-# Create Date: 5 Oct 2018
-# Authors: Tim Eves (TSE)
-#
-
-hotdoc = find_program('hotdoc', required: false)
-installdir = join_paths(get_option('datadir'), 'doc', meson.project_name())
-
-if hotdoc.found()
- cfg = configuration_data()
- cfg.set('project_name', meson.project_name())
- cfg.set('project_version', meson.project_version())
- cfg.set('doc_dir', meson.current_source_dir())
- cfg.set('include_dir', meson.current_source_dir() / '../include')
- configure_file(input: 'hotdoc.json',
- output: 'hotdoc.json',
- configuration: cfg)
- deps = files(
- '../include/keyman/keyman_core_api.h',
- '../src/jsonpp.hpp',
- '../../common/cpp/utfcodec.hpp'
- )
-
- docs = custom_target('docs',
- output: ['html'],
- input: ['sitemap.txt', 'markdown_files/index.md', deps],
- command: [hotdoc, '--verbose', '--conf-file=doc/hotdoc.json', '--output=doc', 'run'],
- depend_files: deps,
- install: true,
- install_dir: installdir)
-endif
diff --git a/core/docs/internal/sitemap.txt b/core/docs/internal/sitemap.txt
deleted file mode 100644
index 047dd866e96..00000000000
--- a/core/docs/internal/sitemap.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-index.md
- c-index
diff --git a/core/docs/introspection.schema b/core/docs/introspection.schema
deleted file mode 100644
index ffccc59f466..00000000000
--- a/core/docs/introspection.schema
+++ /dev/null
@@ -1,62 +0,0 @@
-{"$schema": "http://json-schema.org/draft-06/schema#",
- "$id": "@replace_me_with_introspection_schema_uri@",
- "$comment": "© 2018 SIL International. MIT Licensed",
- "title": "Keyboard Processor State object introspection",
- "description": "Internal data from a Keyboard Processor State object, sufficient for debugging and diagnostics",
- "type": "object",
-
- "definitions": {
- "options": {
- "type": "object",
- "properties": {
- "scope": { "enum": ["unknown", "environment", "keyboard"] },
- "options": {
- "type": "object",
- "default": {}
- }
- },
- "required": ["scope", "options"]
- },
- "rule": {
- "type": "object"
- },
- "keyboard" : {
- "type": "object",
- "properties": {
- "id": { "type": "string" },
- "version": { "type": "string" },
- "folder": { "type": "string"},
- "options": { "$ref": "#definitions/options" },
- "rules": {
- "type":"array",
- "items": { "$ref": "#/definitions/rule" }
- }
- },
- "required": ["id", "version", "folder", "options", "rules"]
- },
- "context": {
- "type":"array",
- "items": {"$ref": "#/definitions/context_item"},
- "default": []
- },
- "context_item": {
- "oneOf": [
- {"type": "string", "minLength": 1, "maxLength": 4},
- {"type": "number"}
- ]
- }
- },
- "properties": {
- "$schema": { "const": "keyman/core/docs/introspection.schema" },
- "keyboard": { "$ref": "#/definitions/keyboard" },
- "options": {
- "type": "object",
- "properties": {
- "enviroment": { "$ref": "#/definitions/options" },
- "dynamic": { "$ref": "#/definitions/options" }
- },
- "required": [ "enviroment","dynamic"]
- },
- "context": { "$ref": "#/definitions/context" }
- }
-}
diff --git a/core/include/keyman/keyman_core_api.h b/core/include/keyman/keyman_core_api.h
index c26cbdf9ff5..f3563e5f37c 100644
--- a/core/include/keyman/keyman_core_api.h
+++ b/core/include/keyman/keyman_core_api.h
@@ -1,9 +1,10 @@
/*
- Copyright: © 2018 SIL International.
- Description: Cross platform API C/C++ declarations for libkeymancore
- Create Date: 2 Oct 2018
- Authors: Tim Eves (TSE)
-*/
+ * Keyman is copyright (C) SIL International. MIT License.
+ *
+ * Created by Tim Eves (TSE) on 2018-10-02
+ *
+ * Cross platform API C/C++ declarations for libkeymancore
+ */
#pragma once
#include
@@ -17,7 +18,7 @@ extern "C"
{
#endif
-/*
+/**
---
filename: index.md
title: Keyman Core API
@@ -40,7 +41,7 @@ This is an internal API intended for use only within Keyman Engine.
* [Options](options)
* [Processor](processor)
* [State and Actions](state)
-* [JSON introspection Schema](json-schema)
+* [Key Handling](keyhandling)
* [Building Keyman Core](building)
## Requirements
@@ -52,7 +53,6 @@ This is an internal API intended for use only within Keyman Engine.
6. Support querying Keyboard attributes.
7. Idempotent
-
## Design decisions in support of requirements
- Use C or C99 types and calling convention for the interface, it has the
broadest language FFI support. [1,2]
@@ -96,11 +96,140 @@ modifiers such as Windows key are excluded from this set. Some modifiers are
transient, such as Control, and others have long-lasting state, such as
Caps Lock.
+- __See more in__ [Keyman Glossary](https://github.com/keymanapp/keyman/wiki/Keyman-glossary)
+
+---
+filename: building.md
+title: How to build Keyman Core
+---
+
+## Prerequisites
+
+### To build
+
+* Python 3
+* Meson build system.
+* C++17 or later compiler.
+
+### Optional
+
+* [kmc](https://keyman.com/developer/download) (for testing)
+
+## Installing Python3
+
+### Linux
+
+You will be able to install a python3 package in any reputable recent version of
+linux using its package manager if it's not already installed.
+
+### macOS
+
+You can get the official installer from the official Python site:
+[https://www.python.org/downloads/mac-osx](https://www.python.org/downloads/mac-osx/)
+
+### Windows
+
+You can get the official installer from the official Python site:
+[https://www.python.org/downloads/windows](https://www.python.org/downloads/windows/)
+
+## Installing Meson
+
+Ensure you have Python3 correctly installed and can run the command `pip3`.
+
+ $> python3 -m pip install meson
+
+## Building
+
+In your source directory do the following:
+
+ $> cd core
+ $> ./build.sh configure build test
+
+## Note on kmc
+
+kmc is node.js-based the command-line compiler from Keyman Developer, available
+from [keyman.com](https://keyman.com/developer/) or on npm at
+[@keymanapp/kmc](https://npmjs.com/package/@keymanapp/kmc).
+
+### Windows
+
+The search path can be edited through System settings / Advanced system settings
+/ Environment Variables / User environment variables.
+
+If you have Keyman Developer installed, kmc should already be on your path.
+Otherwise, add the path where you extracted the kmcomp archive.
+
+### Linux & MacOS
+
+Install kmc from the NPM package.
+
+---
+filename: keyhandling.md
+title: Keyhandling
+---
+
+# Key Handling
+
+For each key press the processor will called twice, for the key down event
+as well as for the key up event. Depending on the type of key pressed the
+processor might handle the key itself or pass it on to the application.
+The value of `emit_keystroke` in `km_core_actions` struct tells if the
+processor handled the key (`emit_keystroke=0`) or not (`emit_keystroke=1`).
+It is important that the same value gets set for both key down and key up
+events, otherwise the application might miss some events which then looks
+to the user like keys are stuck.
+
+Usually the processor won't handle any frame keys and let the application
+deal with it. This allows shortcut keys like Ctrl+C
+to work. The only exception is the Backspace key which is
+handled internally if enough context is available. Regular keys will be
+handled by the processor.
+
+Note that there is a difference between CLDR/LDML and KMN keyboards if
+there are no rules/transforms defined for a key: KMN keyboards will
+output the cap value of the key (e.g. pressing a will output
+`a` if no rule is defined), whereas CLDR/LDML keyboards will suppress
+any output for that key.
+
+The following table lists the state of `km_core_actions.emit_keystroke`
+on return of `km_core_process_event` when the following type of key is
+pressed:
+
+|Type | KeyDown | KeyUp |
+|-----------------------------|---------------|----------|
+|Key with rule | FALSE | FALSE |
+|Key w/o rule | FALSE | FALSE |
+|Framekeys: | | |
+|Enter | TRUE | TRUE |
+|Backspace¹ | FALSE | FALSE |
+|Backspace² | TRUE | TRUE |
+|Ctrl+Key | TRUE | TRUE |
+|Modifier key Shift| TRUE | TRUE |
+|Modifier key Ctrl | TRUE | TRUE |
+|Modifier key LAlt | TRUE | TRUE |
+
+1: context available
+2: without or with empty context
+
---
filename: changes.md
title: Changes - Keyman Core API
---
+## Changes between 18.0 and 19.0
+
+* Removed deprecated `km_core_keyboard_attrs.folder_path`
+* The JSON introspection APIs (which were not fully implemented),
+ `km_core_state_options_to_json` and `km_core_state_to_json`, have been
+ removed.
+
+## Changes between 17.0 and 18.0
+
+* Replaced `km_core_keyboard_load` with `km_core_keyboard_load_from_blob`.
+ Keyboard loading is now done in the engine, and the content of the
+ keyboard passed to Core as a blob.
+* Deprecated `km_core_keyboard_attrs.folder_path`
+
## Changes between 16.0 and 17.0
* The namespace identifier has changed from `km_kbp_` to `km_core_`.
@@ -197,301 +326,217 @@ macros](#lib-version-macros) to check the loaded DLL supplies the correct
interface.
-------------------------------------------------------------------------------
+*/
-# Common functions, types, and macros
-
-
-## Basic types
-
-Fundamental types for representing data passed across the API.
-
-### km_core_cu type
-
-`uint16_t/char16_t`
-
-Represents a UTF16 codepoint, most strings are passed as UTF16.
-
-### km_core_usv type
-
-`uint32_t/char32_t`
-
-An integral type capable of holding a single Unicode Scalar Value, a decoded UTF
-codepoint.
-
-### km_core_virtual_key type
-
-`uint16_t`
-
-An integral type capable of holding a platform specific virtual key code.
-
-### km_core_status type
-
-`uint32_t`
-
-An integral 32 bit wide type capable of holding any valid status code as defined
-by the `enum` [km_core_status_codes].
-
-### km_core_modifier_state type
-
-`uint16_t`
-
-An integral type bitmask representing the state of each modifier key.
-
-
-
-## Resource types
-
-Opaque types for representing resources provided or created by the keyboard
-processor implementation.
-
-### km_core_keyboard struct
-
-Represents a keyboard loaded from disk, that can be executed by the keyboard
-processor to consume events, update state associated with an insertion point and
-produce action items. A keyboard object may be referenced by any number of state
-objects but must be disposed of after all state objects referencing it have
-first been disposed of.
-
-### km_core_state struct
-
-Represents all state associated with an insertion point using a keyboard. This
-tracks context, and current action items resulting from a processed keyboard
-event. There can be many state objects using the same keyboard. A state object
-may not live longer than the keyboard it manages state for.
-
-### km_core_options struct
-
-Represents a set of option items for environmental state and keyboard state.
+// Note: `km_core_cu` and `km_core_usv` are defined in common/km_types.h, as the
+// underlying type may vary by platform, and the types are shared across
+// multiple projects.
+
+/**
+ * @name km_core_cu type
+ *
+ * Represents a UTF16 codepoint, most strings are passed as UTF16.
+ *
+ * ```c
+ * typedef uint16_t/char16_t km_core_cu;
+ * ```
+ */
+
+/**
+ * @name km_core_usv type
+ *
+ * An integral type capable of holding a single Unicode Scalar Value, a decoded
+ * UTF codepoint.
+ *
+ * ```c
+ * typedef uint32_t/char32_t km_core_usv;
+ * ```
+ */
+
+//-------------------------------------------------------------------------------
+
+/**
+ * @name km_core_virtual_key type
+ *
+ * An integral type capable of holding a platform specific virtual key code.
+ */
+typedef uint16_t km_core_virtual_key;
+
+/**
+ * @name km_core_status type
+ *
+ * An integral 32 bit wide type capable of holding any valid status code as
+ * defined by the `enum` [km_core_status_codes].
+ */
+typedef uint32_t km_core_status;
-
+/**
+ * @name km_core_state struct
+ *
+ * Represents all state associated with an insertion point using a keyboard.
+ * This tracks context, and current action items resulting from a processed
+ * keyboard event. There can be many state objects using the same keyboard. A
+ * state object may not live longer than the keyboard it manages state for.
+ */
+typedef struct km_core_state km_core_state;
+/**
+ * @name km_core_options struct
+ *
+ * Represents a set of option items for environmental state and keyboard state.
+ */
+typedef struct km_core_options km_core_options;
-
-
--------------------------------------------------------------------------------
-
-# km_core_status_codes enum
-
-## Description
-
-An error code mechanism similar to COM’s `HRESULT` scheme (unlike COM, any
-non-zero value is an error).
-
-## Specification
-
-```c */
+/**
+ * @name km_core_status_codes enum
+ *
+ * An error code mechanism similar to COM's `HRESULT` scheme (unlike COM, any
+ * non-zero value is an error).
+ */
enum km_core_status_codes {
+ // keep in sync with web/src/engine/src/core-adapter/KM_Core.ts
+ // (see https://github.com/emscripten-core/emscripten/issues/18585)
+
+ /** Success code. Call completed as documented. */
KM_CORE_STATUS_OK = 0,
+
+ /** The call failed to allocate memory during its execution, causing it to fail. */
KM_CORE_STATUS_NO_MEM = 1,
+
+ /** The call performed an I/O operation which failed, causing it to fail. */
KM_CORE_STATUS_IO_ERROR = 2,
+
+ /** The call detected one of its parameters was invalid or unsafe. */
KM_CORE_STATUS_INVALID_ARGUMENT = 3,
+
+ /** The provided key or index into a collection object was not present. */
KM_CORE_STATUS_KEY_ERROR = 4,
+
+ /** The provided buffer did not contain enough space to fully encode or copy
+ * the result of this call. */
KM_CORE_STATUS_INSUFFICENT_BUFFER = 5,
+
+ /** A malformed or partial UTF sequence prevented complete decoding of a
+ * unicode string. */
KM_CORE_STATUS_INVALID_UTF = 6,
+
+ /** An attempt to decode a keyboard file failed. */
KM_CORE_STATUS_INVALID_KEYBOARD = 7,
+
KM_CORE_STATUS_NOT_IMPLEMENTED = 8,
+
+ /** This allows encapsulating a platform error code: the remaining 31 low bits
+ * are the error code returned by the OS for cases where the failure mode is
+ * platform specific. For HRESULT codes this only permits failure codes to be
+ * passed and not success codes. */
KM_CORE_STATUS_OS_ERROR = 0x80000000
};
-/*
-```
-
-## Values
-
-`KM_CORE_STATUS_OK`
-
-: Success code. Call completed as documented.
-
-`KM_CORE_STATUS_NO_MEM`
-
-: The call failed to allocate memory during its execution, causing it to fail.
-
-`KM_CORE_STATUS_IO_ERROR`
-
-: The call performed an I/O operation which failed, causing it to fail.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-
-: The call detected one of its parameters was invalid or unsafe.
-
-`KM_CORE_STATUS_KEY_ERROR`
-
-: The provided key or index into a collection object was not present.
-
-`KM_CORE_STATUS_INSUFFICENT_BUFFER`
-
-: The provided buffer did not contain enough space to fully encode or copy the
-result of this call.
-
-`KM_CORE_STATUS_INVALID_UTF`
-
-: A malformed or partial UTF sequence prevented complete decoding of a unicode
-string.
-
-`KM_CORE_STATUS_INVALID_KEYBOARD`
-
-: An attempt to decode a keyboard file failed.
-
-`KM_CORE_STATUS_OS_ERROR`
-
-: This allows encapsulating a platform error code: the remaining 31 low bits are
-the error code returned by the OS for cases where the failure mode is platform
-specific. For HRESULT codes this only permits failure codes to be passed and not
-success codes.
-
--------------------------------------------------------------------------------
-
-# km_core_attr struct
-
-## Description
-
-A structure describing information about Keyman Core implementing this API.
-
-## Specification
-
-```c */
-
+/**
+ * @name km_core_attr struct
+ *
+ * A structure describing information about Keyman Core implementing this API.
+ */
typedef struct {
+ /** Maximum context size supported by processor in code points. */
size_t max_context;
+
+ /** Current API number supported. */
uint16_t current;
+
+ /** Implementation number of current API. */
uint16_t revision;
+
+ /** current - age == Oldest API number supported. */
uint16_t age;
+
+ /** A bit field of [km_core_tech_value] values, specifiying which Keyboard
+ * technologies the engine supports. */
uint16_t technology;
+
+ /** A UTF-8 encoded string identifying the implementer of the processor. */
char const *vendor;
} km_core_attr;
-/*
-```
-## Members
-
-`max_context`
-: Maximum context size supported by processor.
-
-`current`
-: Current API number supported.
-
-`revision`
-: Implementation number of current API.
-
-`age`
-: current - age == Oldest API number supported.
-
-`technology`
-: A bit field of [km_core_tech_value] values,
-specifiying which Keyboard technologies the engine supports.
-
-`vendor`
-: A UTF-8 encoded string identifying the implementer of the processor.
-
--------------------------------------------------------------------------------
-
-# km_core_tech_value enum
-
-## Description
-
-Values for a bit field indicating which keyboarding technologies a keyboard
-processor supports.
-
-## Specification
-
-```c */
-
+/**
+ * @name km_core_tech_value enum
+ *
+ * Values for a bit field indicating which keyboarding technologies a keyboard
+ * processor supports.
+ */
enum km_core_tech_value {
+ /** The keyboard processor implementation does not disclose which technologies
+ * it implements. */
KM_CORE_TECH_UNSPECIFIED = 0,
+
+ /** The keyboard processor implements a simple en-US keyboard for the purposes
+ * of testing the API. */
KM_CORE_TECH_MOCK = 1 << 0,
+
+ /** The keyboard processor implements a Keyman KMX compatible engine. */
KM_CORE_TECH_KMX = 1 << 1,
+
+ /** The keyboard processor implements a LDML capable processing engine. */
KM_CORE_TECH_LDML = 1 << 2
};
-/*
-```
-## Values
-
-`KM_CORE_TECH_UNSPECIFIED`
-: The keyboard processor implementation does not disclose which technologies it
-implements.
-
-`KM_CORE_TECH_MOCK`
-: The keyboard processor implements a simple en-US keyboard for the purposes of
-testing the API.
-
-`KM_CORE_TECH_UNSPECIFIED`
-: The keyboard processor implements a Keyman KMX compatible engine.
-
-`KM_CORE_TECH_UNSPECIFIED`
-: The keyboard processor implements a LDML capable processing engine.
--------------------------------------------------------------------------------
-
-# km_core_get_engine_attrs()
-
-## Description
-
-Get access processors attributes describing version and technology implemented.
-
-## Specification
-
-```c */
+/**
+ * @name km_core_get_engine_attrs function
+ *
+ * Get access processors attributes describing version and technology
+ * implemented.
+ *
+ * @param state An opaque pointer to a [km_core_state].
+ *
+ * @returns A pointer to a [km_core_attr] structure. Do not modify the contents
+ * of this structure.
+ */
KMN_API
km_core_attr const *
-km_core_get_engine_attrs(km_core_state const *state);
-
-/*
-```
-
-## Parameters
-
-`state`
-: An opaque pointer to a [km_core_state].
-
-## Returns
-A pointer to a [km_core_attr] structure. Do not modify the contents of this
-structure.
-
--------------------------------------------------------------------------------
-
-# km_core_bool enum
-
-## Description
-
-Defines a boolean state.
+km_core_get_engine_attrs(
+ km_core_state const *state
+);
-## Specification
-```c */
+/**
+ * @name km_core_bool enum
+ *
+ * Defines a boolean state.
+ */
typedef enum { KM_CORE_FALSE = 0, KM_CORE_TRUE = 1 } km_core_bool;
-/*
-```
--------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------
+/**
---
filename: state.md
title: State and Actions - Keyman Core API
@@ -505,182 +550,131 @@ for the Platform layer to emit to the Client application. These actions are
owned by the state object.
-------------------------------------------------------------------------------
+*/
-# km_core_caps_state enum
-
-## Description
-
-Describes a change to the hardware caps lock indicator state requested by
-Keyman Core to the Platform layer.
-
-## Specification
-```c */
-typedef enum { KM_CORE_CAPS_UNCHANGED = -1, KM_CORE_CAPS_OFF = 0, KM_CORE_CAPS_ON = 1 } km_core_caps_state;
-
-/*
-```
-## Values
-
-`KM_CORE_CAPS_UNCHANGED`
-: Caps lock state has not changed in this event.
-
-`KM_CORE_CAPS_OFF`
-: As a result of processing this event, the Platform layer should switch off
- Caps Lock on the hardware keyboard.
-
-`KM_CORE_CAPS_ON`
-: As a result of processing this event, the Platform layer should switch on
- Caps Lock on the hardware keyboard.
-
--------------------------------------------------------------------------------
-
-# km_core_actions struct
-
-## Description
-
-This structure provides the results of processing a key event to the Platform layer and
-should be processed by the Platform layer to issue commands to the os text
-services framework to transform the text store in the Client Application, among
-other actions.
-
-This API replaces the Action items APIs, which are now deprecated and will be
-removed in the future.
-
-## Specification
-```c */
+/**
+ * @name km_core_caps_state enum
+ *
+ * Describes a change to the hardware caps lock indicator state requested by
+ * Keyman Core to the Platform layer.
+ */
+typedef enum {
+ /** Caps lock state has not changed in this event. */
+ KM_CORE_CAPS_UNCHANGED = -1,
+
+ /** As a result of processing this event, the Platform layer should switch off
+ * Caps Lock on the hardware keyboard. */
+ KM_CORE_CAPS_OFF = 0,
+
+ /** As a result of processing this event, the Platform layer should switch on
+ * Caps Lock on the hardware keyboard.*/
+ KM_CORE_CAPS_ON = 1
+} km_core_caps_state;
+
+/**
+ * @name km_core_actions struct
+ *
+ * This structure provides the results of processing a key event to the Platform
+ * layer and should be processed by the Platform layer to issue commands to the
+ * os text services framework to transform the text store in the Client
+ * Application, among other actions.
+ *
+ * This API replaces the Action items APIs, which are now deprecated and will be
+ * removed in the future.
+ */
typedef struct {
+ /** Number of codepoints (not codeunits!) to delete from app context. */
unsigned int code_points_to_delete;
+
+ /** Null-term string of characters to insert into document. */
const km_core_usv* output;
+
+ /** List of options to persist, terminated with `KM_CORE_OPTIONS_END`. */
km_core_option_item * persist_options;
+
+ /** Issue a beep, 0 = no, 1 = yes. */
km_core_bool do_alert;
+
+ /** Emit the (unmodified) input keystroke to the application, 0 = no, 1 = yes.
+ * On most platforms this signals whether the processor handled the event (0)
+ * or not (1). See also [key handling](keyhandling). */
km_core_bool emit_keystroke;
+
+ /** -1=unchanged, 0=off, 1=on */
km_core_caps_state new_caps_lock_state;
+
+ /** Reference copy of actual UTF32 codepoints deleted from end of context
+ * (closest to caret) exactly `code_points_to_delete` in length (plus null
+ * terminator). Used to determine encoding conversion differences when
+ * deleting; only set when using [km_core_state_get_actions], otherwise
+ * `nullptr`. */
const km_core_usv* deleted_context;
} km_core_actions;
-/*
-```
-## Members
-
-`code_points_to_delete`
-: Number of codepoints (not codeunits!) to delete from app context.
-
-`output`
-: Null-term string of characters to insert into document.
-
-`persist_options`
-: List of options to persist, terminated with `KM_CORE_OPTIONS_END`.
-
-`do_alert`
-: Issue a beep, 0 = no, 1 = yes.
-
-`emit_keystroke`
-: Emit the (unmodified) input keystroke to the application, 0 = no, 1 = yes.
- On most platforms this signals whether the processor handled the event
- (0) or not (1). See also [key handling](keyhandling).
-
-`new_caps_lock_state`
-: -1=unchanged, 0=off, 1=on
-
-`deleted_context`
-: Reference copy of actual UTF32 codepoints deleted from end of context
- (closest to caret) exactly `code_points_to_delete` in length (plus null
- terminator). Used to determine encoding conversion differences when
- deleting; only set when using [km_core_state_get_actions], otherwise `nullptr`.
-
--------------------------------------------------------------------------------
-
-# km_core_state_get_actions()
-
-## Description
-
-Returns a pointer to an actions object which details all the actions
-that the Platform layer must take after a keystroke. The `code_points_to_delete`
-action must be performed before the `output` action, but the other
-actions may be performed in any order.
-
-## Specification
-```c */
+/**
+ * @name km_core_state_get_actions function
+ *
+ * Returns a pointer to an actions object which details all the actions that the
+ * Platform layer must take after a keystroke. The `code_points_to_delete`
+ * action must be performed before the `output` action, but the other actions
+ * may be performed in any order.
+ *
+ * @param state An opaque pointer to a state object.
+ * @returns A pointer to a [km_core_actions] object. This data becomes invalid
+ * when the state object is destroyed, or after a call to
+ * [km_core_process_event]. Do not modify the contents of this data.
+ */
KMN_API
km_core_actions const *
km_core_state_get_actions(
km_core_state const *state
);
-/*
-```
-## Parameters
-
-`state`
-: An opaque pointer to a state object.
-
-## Returns
-
-A pointer to a [km_core_actions] object. This data becomes invalid
-when the state object is destroyed, or after a call to
-[km_core_process_event]. Do not modify the contents of this data.
-
--------------------------------------------------------------------------------
-
-# km_core_context_status enum
-
-## Description
-
-Return values for [km_core_state_context_set_if_needed].
-
-## Specification
-
-```c */
+/**
+ * @name km_core_context_status enum
+ *
+ * Return values for [km_core_state_context_set_if_needed].
+ */
typedef enum {
+ /** Cached context change was not needed. */
KM_CORE_CONTEXT_STATUS_UNCHANGED = 0,
+
+ /** Cached context was set to application context. */
KM_CORE_CONTEXT_STATUS_UPDATED = 1,
+
+ /** Application context was invalid, perhaps had unpaired surrogates, and so
+ * cached context was cleared instead. */
KM_CORE_CONTEXT_STATUS_CLEARED = 2,
+
+ /** Internal error. */
KM_CORE_CONTEXT_STATUS_ERROR = 3,
+
+ /** One or more parameters was null. */
KM_CORE_CONTEXT_STATUS_INVALID_ARGUMENT = 4,
} km_core_context_status;
-/*
-```
-
-## Values
-
-`KM_CORE_CONTEXT_STATUS_UNCHANGED`
-: Cached context change was not needed.
-
-`KM_CORE_CONTEXT_STATUS_UPDATED`
-: Cached context was set to application context.
-
-`KM_CORE_CONTEXT_STATUS_CLEARED`
-: Application context was invalid, perhaps had unpaired surrogates,
- and so cached context was cleared instead.
-
-`KM_CORE_CONTEXT_STATUS_ERROR`
-: Internal error.
-
-`KM_CORE_CONTEXT_STATUS_INVALID_ARGUMENT`
-: One or more parameters was null.
-
--------------------------------------------------------------------------------
-
-# km_core_state_context_set_if_needed()
-
-## Description
-
-Sets the internal cached context for the state object, to the passed-in
-application context string, if it differs from the codepoints in the
-cached context. For the purposes of comparison, (1) cached markers are
-ignored, (2) if the cached context is shorter than the application
-context, it is considered identical, but (3) if the cached context is
-longer, then it is considered different.
-
-If a difference is found, then the cached context will be set to the
-application context, and thus any cached markers will be cleared.
-
-[km_core_state_context_set_if_needed] and [km_core_state_context_clear]
-will replace most uses of the existing Core context APIs.
-
-## Specification
-```c */
+/**
+ * @name km_core_state_context_set_if_needed function
+ *
+ * Sets the internal cached context for the state object, to the passed-in
+ * application context string, if it differs from the codepoints in the
+ * cached context. For the purposes of comparison, (1) cached markers are
+ * ignored, (2) if the cached context is shorter than the application
+ * context, it is considered identical, but (3) if the cached context is
+ * longer, then it is considered different.
+ *
+ * If a difference is found, then the cached context will be set to the
+ * application context, and thus any cached markers will be cleared.
+ *
+ * [km_core_state_context_set_if_needed] and [km_core_state_context_clear]
+ * will replace most uses of the existing Core context APIs.
+ *
+ * @param state An opaque pointer to a state object.
+ * @param application_context A pointer to an null-terminated array of utf16
+ * encoded data representing the current context
+ * from the application.
+ * @returns A value from the [km_core_context_status] enum.
+ */
KMN_API
km_core_context_status
km_core_state_context_set_if_needed(
@@ -688,58 +682,33 @@ km_core_state_context_set_if_needed(
km_core_cu const *application_context
);
-/*
-```
-## Parameters
-
-`state`
-: An opaque pointer to a state object.
-
-`application_context`
-: A pointer to an null-terminated array of utf16 encoded data representing
- the current context from the application.
-
-## Returns
-
-A value from the [km_core_context_status] enum.
-
--------------------------------------------------------------------------------
-
-# km_core_state_context_clear()
-
-## Description
-
-Clears the internal cached context for the state. This is the same as
-`km_core_context_clear(km_core_state_context(&state))`.
-
-[km_core_state_context_set_if_needed] and [km_core_state_context_clear]
-will replace most uses of the existing Core context APIs.
-
-## Specification
-```c */
+/**
+ *
+ * @name km_core_state_context_clear function
+ *
+ * Clears the internal cached context for the state. This is the same as
+ * `km_core_context_clear(km_core_state_context(&state))`.
+ *
+ * [km_core_state_context_set_if_needed] and [km_core_state_context_clear]
+ * will replace most uses of the existing Core context APIs.
+ *
+ * @param state An opaque pointer to a state object.
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : If any parameters are null.
+ */
KMN_API
km_core_status
km_core_state_context_clear(
km_core_state *state
);
-/*
-```
-## Parameters
-
-`state`:
-An opaque pointer to a state object.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: If any parameters are null.
-
--------------------------------------------------------------------------------
-
+//-------------------------------------------------------------------------------
+/**
---
filename: options.md
title: Options - Keyman Core API
@@ -759,232 +728,126 @@ the Platform layer should update that only if it manages a previously persisted
value.
-------------------------------------------------------------------------------
+*/
-# km_core_option_scope enum
-
-## Description
-
-## Specification
-
-```c */
+/**
+ * @name km_core_option_scope enum
+ */
enum km_core_option_scope {
+ /** An unknown option type. Reserved. */
KM_CORE_OPT_UNKNOWN = 0,
+
+ /** An option that is defined for the currently active keyboard; not all
+ * processors support this type of option. These options are specific to the
+ * active keyboard. */
KM_CORE_OPT_KEYBOARD = 1,
+
+ /** Properties of the current environment, often but not necessarily always
+ * read-only. */
KM_CORE_OPT_ENVIRONMENT = 2,
KM_CORE_OPT_MAX_SCOPES
};
-/*
-```
-## Values
-
-`KM_CORE_OPT_UNKNOWN`
-: An unknown option type. Reserved.
-
-`KM_CORE_OPT_KEYBOARD`
-: An option that is defined for the currently active keyboard;
- not all processors support this type of option. These options
- are specific to the active keyboard.
-
-`KM_CORE_OPT_ENVIRONMENT`
-: Properties of the current environment, often but not necessarily
- always read-only.
-
--------------------------------------------------------------------------------
-
-# km_core_option_item struct
-
-## Description
-
-Defines a single option to be passed into the Keyman Core from the
-Platform layer.
-
-## Specification
-```c */
+/**
+ * @name km_core_option_item struct
+ *
+ * Defines a single option to be passed into the Keyman Core from the Platform
+ * layer.
+ */
struct km_core_option_item {
+ /** Null-terminated string key for the option */
km_core_cu const * key;
+
+ /** Null-terminated string value for the option */
km_core_cu const * value;
+
+ /** Scope which an option belongs to, from [km_core_option_scope]. */
uint8_t scope;
};
#define KM_CORE_OPTIONS_END { 0, 0, 0 }
-/*
-```
-## Members
-
-`key`
-: Null-terminated string key for the option
-
-`value`
-: Null-terminated string value for the option
-`scope`
-: Scope which an option belongs to, from [km_core_option_scope].
-
--------------------------------------------------------------------------------
-
-# km_core_options_list_size()
-
-## Description
-Return the length of a terminated [km_core_option_item] array (options
-list).
-
-## Specification
-```c */
+/**
+ * @name km_core_options_list_size function
+ *
+ * Return the length of a terminated [km_core_option_item] array (options
+ * list).
+ *
+ * @param opts A pointer to a `KM_CORE_OPTIONS_END` terminated array of
+ * [km_core_option_item] values.
+ * @returns The number of items in the list, not including terminating
+ * item, or 0 if `opts` is null.
+ */
KMN_API
size_t
km_core_options_list_size(km_core_option_item const *opts);
-/*
-```
-## Parameters
-
-`opts`
-: A pointer to a `KM_CORE_OPTIONS_END` terminated array of
- [km_core_option_item] values.
-
-## Returns
-
-The number of items in the list, not including terminating item,
-or 0 if `opts` is null.
-
--------------------------------------------------------------------------------
-
-# km_core_state_option_lookup
-
-## Description
-
-Lookup an option based on its key, in an options list.
-
-## Specification
-```c */
-KMN_API
-km_core_status
-km_core_state_option_lookup(km_core_state const *state,
- uint8_t scope,
- km_core_cu const *key,
- km_core_cu const **value);
-
-/*
-```
-## Parameters
-
-`state`
-: An opaque pointer to a state object.
-
-`scope`
-: Which key-value store to interrogate.
-
-`key`
-: A UTF-16 string that matches the key in the target [km_core_option_item].
-
-`value`
-: A pointer to the result variable: A pointer to a UTF-16 string value owned
- by the state or keyboard object at the time of the call. This pointer is
- only valid *until* the next call to any function on this API and should be
- used immediately.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: If non-optional parameters are null, or if the scope is invalid.
-
-`KM_CORE_STATUS_KEY_ERROR`
-: The key cannot be found.
-
--------------------------------------------------------------------------------
-
-# km_core_state_options_update()
-
-## Description
-
-Adds or updates one or more options from a list of [km_core_option_item]s.
-
-## Specification
-``` */
+/**
+ * @name km_core_state_option_lookup function
+ *
+ * Lookup an option based on its key, in an options list.
+ *
+ * @param state An opaque pointer to a state object.
+ * @param scope Which key-value store to interrogate.
+ * @param key A UTF-16 string that matches the key in the target
+ * [km_core_option_item].
+ * @param value A pointer to the result variable: A pointer to a UTF-16
+ * string value owned by the state or keyboard object at the
+ * time of the call. This pointer is only valid *until* the next
+ * call to any function on this API and should be used
+ * immediately.
+ *
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : If non-optional parameters are null, or if the scope is invalid.
+ *
+ * `KM_CORE_STATUS_KEY_ERROR`
+ * : The key cannot be found.
+ */
KMN_API
km_core_status
-km_core_state_options_update(km_core_state *state,
- km_core_option_item const *new_opts);
-
-/*
-```
-## Parameters
-`state`
-: An opaque pointer to a state object.
-
-`new_opts`
-: An array of [km_core_option_item] objects to update or add. Must be
- terminated with `KM_CORE_OPTIONS_END`.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: If non-optional parameters are null.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event an internal memory allocation fails.
-
-`KM_CORE_STATUS_KEY_ERROR`
-: The key cannot be found.
-
--------------------------------------------------------------------------------
-
-# km_core_state_options_to_json()
-
-## Description
-
-Export the contents of a [km_core_options] array to a JSON formatted document and
-place it in the supplied buffer, reporting how much space was used. If null is
-passed as the buffer the number of bytes required is returned in `space`. If
-there is insufficent space to hold the document the contents of the buffer is
-undefined. The returned buffer uses UTF-8 encoding.
-
-## Specification
+km_core_state_option_lookup(
+ km_core_state const *state,
+ uint8_t scope,
+ km_core_cu const *key,
+ km_core_cu const **value
+);
-```c
-*/
+/**
+ * @name km_core_state_options_update function
+ *
+ * Adds or updates one or more options from a list of [km_core_option_item]s.
+ *
+ * @param state An opaque pointer to a state object.
+ * @param new_opts An array of [km_core_option_item] objects to update or add.
+ * Must be terminated with `KM_CORE_OPTIONS_END`.
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : If non-optional parameters are null.
+ *
+ * `KM_CORE_STATUS_NO_MEM`
+ * : In the event an internal memory allocation fails.
+ *
+ * `KM_CORE_STATUS_KEY_ERROR`
+ * : The key cannot be found.
+ */
KMN_API
km_core_status
-km_core_state_options_to_json(km_core_state const *state,
- char *buf,
- size_t *space);
-
-/*
-```
-## Parameters
-
-`state`
-: An opaque pointer to a state object.
-
-`buf`
-: A pointer to the buffer to place the C string containing the JSON
- document into, can be null.
-
-`space`
-: A pointer to a size_t variable. This variable must contain the
- number of bytes available in the buffer pointed to by `buf`, unless `buf` is
- null. On return it will hold how many bytes were used.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: If non-optional parameters are null.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event an internal memory allocation fails.
-
--------------------------------------------------------------------------------
+km_core_state_options_update(
+ km_core_state *state,
+ km_core_option_item const *new_opts
+);
+//-------------------------------------------------------------------------------
+/**
---
filename: keyboards.md
title: Keyboards - Keyman Core API
@@ -996,800 +859,502 @@ the processsor and made available in an immutable fashion for use with any numbe
of state objects.
-------------------------------------------------------------------------------
-
-# km_core_keyboard_attrs struct
-
-## Description
-
-Provides read-only information about a keyboard.
-
-## Specification
-```c
*/
+
+/**
+ * @name km_core_keyboard_attrs struct
+ *
+ * Provides read-only information about a keyboard.
+ */
typedef struct {
+ /** Processor specific version string. */
km_core_cu const * version_string;
- km_core_cu const * id;
- // TODO-web-core: Deprecate this field (#12497)
- // KMN_DEPRECATED
- km_core_path_name folder_path;
+ /** Keyman keyboard ID string. */
+ km_core_cu const * id;
+ /** Set of default values for any options included in the keyboard. */
km_core_option_item const * default_options;
} km_core_keyboard_attrs;
-/*
-```
-## Members
-
-`version_string`
-: Processor specific version string.
-
-`id`
-: Keyman keyboard ID string.
-
-`folder_path`
-: Path to the unpacked folder containing the keyboard and associated resources (deprecated).
-
-`default_options`
-: Set of default values for any options included in the keyboard.
-
--------------------------------------------------------------------------------
-
-# km_core_keyboard_key struct
-
-## Description
-
-Describes a single key and modifier combination that a keyboard handles, for
-use by the Platform layer. This is used when the Platform layer must know in
-advance which keys are used by a given keyboard.
-
-## Specification
-```c */
+/**
+ * @name km_core_keyboard_key struct
+ *
+ * Describes a single key and modifier combination that a keyboard handles, for
+ * use by the Platform layer. This is used when the Platform layer must know in
+ * advance which keys are used by a given keyboard.
+ */
typedef struct {
+ /** A virtual key. */
km_core_virtual_key key;
+
+ /** A [km_core_modifier_state] bitmask. */
uint32_t modifier_flag;
} km_core_keyboard_key;
#define KM_CORE_KEYBOARD_KEY_LIST_END { 0, 0 }
-/*
-```
-
-## Members
-
-`key`
-: A virtual key.
-
-`modifier_flag`
-: A [km_core_modifier_state] bitmask.
-
--------------------------------------------------------------------------------
-
-# km_core_keyboard_imx struct
-
-## Description
-
-Describes a single Input Method eXtension library and entry point.
-
-## Specification
-
-```c */
+/**
+ * @name km_core_keyboard_imx struct
+ *
+ * Describes a single Input Method eXtension library and entry point.
+ */
typedef struct {
+ /** The fully-qualified path and filename of the dynamically loaded library
+ * file. */
km_core_cu const * library_name;
+
+ /** The entry point for the IMX. */
km_core_cu const * function_name;
+
+ /** unique identifier used to call this function */
uint32_t imx_id;
} km_core_keyboard_imx;
#define KM_CORE_KEYBOARD_IMX_END { 0, 0, 0 }
-/*
-```
-## Members
-
-`library_name`
-: The fully-qualified path and filename of the dynamically loaded library file.
-
-`function_name`
-: The entry point for the IMX.
-
-`imx_id`
-: unique identifier used to call this function
-
--------------------------------------------------------------------------------
-
-# km_core_keyboard_load_from_blob()
-
-## Description
-
-Parse and load keyboard from the supplied blob and a pointer to the loaded keyboard
-into the out paramter.
-
-## Specification
-
-```c */
+/**
+ * @name km_core_keyboard_load_from_blob function
+ *
+ * Parse and load keyboard from the supplied blob and a pointer to the loaded
+ * keyboard into the out paramter.
+ *
+ * @param kb_name a string with the name of the keyboard.
+ * @param blob a byte array containing the content of a KMX/KMX+ file.
+ * @param blob_size a size_t variable with the size of the blob in bytes.
+ * @param keyboard A pointer to result variable: A pointer to the opaque
+ * keyboard object returned by the Processor. This memory
+ * must be freed with a call to [km_core_keyboard_dispose].
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_NO_MEM`
+ * : In the event an internal memory allocation fails.
+ *
+ * `KM_CORE_STATUS_IO_ERROR`
+ * : In the event the keyboard file is unparseable for any reason
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : In the event `keyboard` is null.
+ *
+ * `KM_CORE_STATUS_OS_ERROR`
+ * : Bit 31 (high bit) set, bits 0-30 are an OS-specific error code.
+ */
KMN_API
-km_core_status km_core_keyboard_load_from_blob(const km_core_path_name kb_name,
- const void* blob,
- const size_t blob_size,
- km_core_keyboard** keyboard);
-
-/*
-```
-
-## Parameters
-
-`kb_name`
-: a string with the name of the keyboard.
-
-`blob`
-: a byte array containing the content of a KMX/KMX+ file.
-
-`blob_size`
-: a size_t variable with the size of the blob in bytes.
-
-`keyboard`
-: A pointer to result variable: A pointer to the opaque keyboard
- object returned by the Processor. This memory must be freed with a
- call to [km_core_keyboard_dispose].
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event an internal memory allocation fails.
-
-`KM_CORE_STATUS_IO_ERROR`
-: In the event the keyboard file is unparseable for any reason
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: In the event `keyboard` is null.
-
-`KM_CORE_STATUS_OS_ERROR`
-: Bit 31 (high bit) set, bits 0-30 are an OS-specific error code.
-
--------------------------------------------------------------------------------
-
-# km_core_keyboard_dispose()
-
-## Description
-
-Free the allocated memory belonging to an opaque keyboard object previously
-returned by [km_core_keyboard_load_from_blob].
-
-## Specification
+km_core_status km_core_keyboard_load_from_blob(
+ const km_core_path_name kb_name,
+ const void* blob,
+ const size_t blob_size,
+ km_core_keyboard** keyboard
+);
-```c */
+/**
+ * @name km_core_keyboard_dispose function
+ *
+ * Free the allocated memory belonging to an opaque keyboard object previously
+ * returned by [km_core_keyboard_load_from_blob].
+ *
+ * @param keyboard A pointer to the opaque keyboard object to be disposed of.
+ */
KMN_API
void
-km_core_keyboard_dispose(km_core_keyboard *keyboard);
-
-/*
-```
-## Parameters
-`keyboard`
-: A pointer to the opaque keyboard object to be disposed of.
-
--------------------------------------------------------------------------------
-
-# km_core_keyboard_get_attrs()
-
-## Description
-
-Returns the const internal attributes of the keyboard. This structure is valid
-for the lifetime of the opaque keyboard object. Do not modify the returned data.
-
-## Specification
+km_core_keyboard_dispose(
+ km_core_keyboard const* keyboard
+);
-```c */
+/**
+ * @name km_core_keyboard_get_attrs function
+ *
+ * Returns the const internal attributes of the keyboard. This structure is
+ * valid for the lifetime of the opaque keyboard object. Do not modify the
+ * returned data.
+ *
+ * @param keyboard A pointer to the opaque keyboard object to be queried.
+ * @param out A pointer to the result: A pointer to a
+ * [km_core_keyboard_attrs] structure.
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : If non-optional parameters are null.
+ */
KMN_API
km_core_status
-km_core_keyboard_get_attrs(km_core_keyboard const *keyboard,
- km_core_keyboard_attrs const **out);
-
-/*
-```
-## Parameters
-
-`keyboard`
-: A pointer to the opaque keyboard object to be queried.
-
-`out`
-: A pointer to the result: A pointer to a [km_core_keyboard_attrs] structure.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: If non-optional parameters are null.
-
--------------------------------------------------------------------------------
-
-# km_core_keyboard_get_key_list()
-
-## Description
-
-Returns the unordered full set of modifier+virtual keys that are handled by the
-keyboard. The matching dispose call needs to be called to free the memory.
-
-## Specification
+km_core_keyboard_get_attrs(
+ km_core_keyboard const *keyboard,
+ km_core_keyboard_attrs const **out
+);
-```c */
+/**
+ * @name km_core_keyboard_get_key_list function
+ *
+ * Returns the unordered full set of modifier+virtual keys that are handled by
+ * the keyboard. The matching dispose call needs to be called to free the
+ * memory.
+ *
+ * @param keyboard A pointer to the opaque keyboard object to be queried.
+ * @param out A pointer to an array of [km_core_keyboard_key] structures,
+ * terminated by `KM_CORE_KEYBOARD_KEY_LIST_END`.
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : If non-optional parameters are null.
+ */
KMN_API
km_core_status
-km_core_keyboard_get_key_list(km_core_keyboard const *keyboard,
- km_core_keyboard_key **out);
-
-/*
-```
-## Parameters
-
-`keyboard`
-: A pointer to the opaque keyboard object to be queried.
-
-`out`
-: A pointer to an array of [km_core_keyboard_key] structures,
- terminated by `KM_CORE_KEYBOARD_KEY_LIST_END`.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: If non-optional parameters are null.
-
--------------------------------------------------------------------------------
-
-# km_core_keyboard_key_list_dispose()
-
-## Description
-
-Free the allocated memory belonging to a keyboard key list previously
-returned by [km_core_keyboard_get_key_list].
-
-## Specification
+km_core_keyboard_get_key_list(
+ km_core_keyboard const *keyboard,
+ km_core_keyboard_key **out
+);
-```c */
+/**
+ * @name km_core_keyboard_key_list_dispose function
+ *
+ * Free the allocated memory belonging to a keyboard key list previously
+ * returned by [km_core_keyboard_get_key_list].
+ *
+ * @param key_list A pointer to the keyboard key list to be disposed of.
+ */
KMN_API
-void km_core_keyboard_key_list_dispose(km_core_keyboard_key *key_list);
-
-/*
-```
-## Parameters
-
-`key_list`
-: A pointer to the keyboard key list to be disposed of.
-
--------------------------------------------------------------------------------
-
-# km_core_keyboard_get_imx_list
-
-## Description
-
-Returns the list of IMX libraries and function names that are referenced by
-the keyboard. The matching dispose call needs to be called to free the memory.
-
-## Specification
+void
+km_core_keyboard_key_list_dispose(
+ km_core_keyboard_key *key_list
+);
-```c */
+/**
+ * @name km_core_keyboard_get_imx_list function
+ *
+ * Returns the list of IMX libraries and function names that are referenced by
+ * the keyboard. The matching dispose call needs to be called to free the
+ * memory.
+ *
+ * @param keyboard A pointer to the keyboard
+ * @param imx_list A pointer to a variable that will contain a pointer to
+ * the IMX list.
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : If non-optional parameters are null.
+ */
KMN_API
-km_core_status km_core_keyboard_get_imx_list(km_core_keyboard const *keyboard, km_core_keyboard_imx **imx_list);
-
-/*
-```
-## Parameters
-
-`keyboard`
-: A pointer to the keyboard
-
-`imx_list`
-: A pointer to a variable that will contain a pointer to the IMX list.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: If non-optional parameters are null.
-
--------------------------------------------------------------------------------
-
-# km_core_keyboard_imx_list_dispose()
-
-## Description
-
-Disposes of the IMX list.
-
-## Specification
+km_core_status
+km_core_keyboard_get_imx_list(
+ km_core_keyboard const *keyboard,
+ km_core_keyboard_imx **imx_list
+);
-```c */
+/**
+ * @name km_core_keyboard_imx_list_dispose function
+ *
+ * Disposes of the IMX list.
+ *
+ * @param imx_list A pointer to the IMX list.
+ */
KMN_API
-void km_core_keyboard_imx_list_dispose(km_core_keyboard_imx *imx_list);
-
-/*
-```
-## Parameters
-
-`imx_list`
-: A pointer to the IMX list.
-
--------------------------------------------------------------------------------
-
-# km_core_state_imx_register_callback()
-
-## Description
-
-Register the IMX callback endpoint for the client.
-
-## Specification
+void
+km_core_keyboard_imx_list_dispose(
+ km_core_keyboard_imx *imx_list
+);
-```c */
+/**
+ * @name km_core_state_imx_register_callback function
+ *
+ * Register the IMX callback endpoint for the client.
+ *
+ * @param state A pointer to the opaque state object
+ * @param imx_callback Pointer to a function that implements the IMX
+ * callback
+ * @param callback_object An opaque pointer that can be used to pass context
+ * information to the callback function, usually it is
+ * a user-defined data structure.
+ */
KMN_API
-void km_core_state_imx_register_callback(km_core_state *state, km_core_keyboard_imx_platform imx_callback, void *callback_object);
-
-/*
-```
-## Parameters
-
-`state`
-: A pointer to the opaque state object
-
-`imx_callback`
-: pointer to a function that implements the IMX callback
-
-`callback_object`
-: An opaque pointer that can be used to pass context information to the callback function,
- usually it is a user-defined data structure.
-
--------------------------------------------------------------------------------
-
-# km_core_state_imx_deregister_callback()
-
-## Description
-
-De-register IMX callback endpoint for the client.
-
-## Specification
+void
+km_core_state_imx_register_callback(
+ km_core_state *state,
+ km_core_keyboard_imx_platform imx_callback,
+ void *callback_object
+);
-```c */
+/**
+ * @name km_core_state_imx_deregister_callback function
+ *
+ * De-register IMX callback endpoint for the client.
+ *
+ * @param state A pointer to the opaque state object
+ */
KMN_API
-void km_core_state_imx_deregister_callback(km_core_state *state);
-
-/*
-```
-## Parameters
-
-`state`
-: A pointer to the opaque state object
-
--------------------------------------------------------------------------------
-
-# km_core_state_create()
-
-## Description
-
-Create a keyboard processor state object, maintaining state for the keyboard in
-the environment passed.
-
-## Specification
+void
+km_core_state_imx_deregister_callback(
+ km_core_state *state
+);
-```c */
+/**
+ * @name km_core_state_create function
+ *
+ * Create a keyboard processor state object, maintaining state for the keyboard
+ * in the environment passed.
+ *
+ * @param keyboard A pointer to the opaque keyboard object this object will
+ * hold state for.
+ * @param env The array of [km_core_option_item] key/value pairs used to
+ * initialise the environment, terminated by
+ * `KM_CORE_OPTIONS_END`.
+ * @param out A pointer to result variable: A pointer to the opaque state
+ * object returned by the Processor, initalised to maintain
+ * state for `keyboard`. This must be disposed of by a call to
+ * [km_core_state_dispose].
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_NO_MEM`
+ * : In the event memory is unavailable to allocate a state object.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : In the event the `keyboard` or `out` pointer are null.
+ */
KMN_API
km_core_status
-km_core_state_create(km_core_keyboard *keyboard,
- km_core_option_item const *env,
- km_core_state **out);
-
-/*
-```
-## Parameters
-
-`keyboard`
-: A pointer to the opaque keyboard object this object will hold state for.
-
-`env`
-: The array of [km_core_option_item] key/value pairs used to initialise the
- environment, terminated by `KM_CORE_OPTIONS_END`.
-
-`out`
-: A pointer to result variable: A pointer to the opaque state object
- returned by the Processor, initalised to maintain state for `keyboard`.
- This must be disposed of by a call to [km_core_state_dispose].
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event memory is unavailable to allocate a state object.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: In the event the `keyboard` or `out` pointer are null.
-
--------------------------------------------------------------------------------
-
-# km_core_state_clone()
-
-## Description
-
-Clone an existing opaque state object.
-
-## Specification
+km_core_state_create(
+ km_core_keyboard const *keyboard,
+ km_core_option_item const *env,
+ km_core_state **out
+);
-```c */
+/**
+ * @name km_core_state_clone function
+ *
+ * Clone an existing opaque state object.
+ *
+ * @param state A pointer to the opaque state object to be cloned.
+ * @param out A pointer to result variable: A pointer to the opaque state
+ * object returned by the Processor, cloned from the existing
+ * object `state`. This must be disposed of by a call to
+ * [km_core_state_dispose].
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_NO_MEM`
+ * : In the event memory is unavailable to allocate a state object.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : In the event the `state` or `out` pointer are null.
+ */
KMN_API
km_core_status
-km_core_state_clone(km_core_state const *state,
- km_core_state **out);
-
-/*
-```
-## Parameters
-
-`state`
-: A pointer to the opaque state object to be cloned.
-
-`out`
-: A pointer to result variable: A pointer to the opaque state object
- returned by the Processor, cloned from the existing object `state`. This
- must be disposed of by a call to [km_core_state_dispose].
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event memory is unavailable to allocate a state object.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: In the event the `state` or `out` pointer are null.
-
--------------------------------------------------------------------------------
-
-# km_core_state_dispose()
-
-## Description
-
-Free the allocated resources belonging to a [km_core_state] object previously
-returned by [km_core_state_create] or [km_core_state_clone]. After this all
-pointers previously returned by any [km_core_state] family of calls will become
-invalid.
-
-## Specification
+km_core_state_clone(
+ km_core_state const *state,
+ km_core_state **out
+);
-```c */
+/**
+ * @name km_core_state_dispose function
+ *
+ * Free the allocated resources belonging to a [km_core_state] object previously
+ * returned by [km_core_state_create] or [km_core_state_clone]. After this all
+ * pointers previously returned by any [km_core_state] family of calls will
+ * become invalid.
+ *
+ * @param state A pointer to the opaque state object to be disposed.
+ */
KMN_API
void
-km_core_state_dispose(km_core_state *state);
-
-/*
-```
-## Parameters
-
-`state`
-: A pointer to the opaque state object to be disposed.
-
--------------------------------------------------------------------------------
-
-# km_core_debug_context_type enum
-
-As of version 17, the cached context is an internal property of the
-state, not exposed to the consumer of the API -- apart from the
-Keyman Developer Keyboard Debugger. However, for other debug
-purposes, it is helpful to be able to examine the cached context, so
-a debug-formatted version of the context is made available with
-[km_core_state_context_debug]. This is not intended to be parsed for
-reading the context for other purposes, and the format may change.
-
-The three context types are: cached, intermediate, and app.
-
-## Specification
+km_core_state_dispose(
+ km_core_state const *state
+);
-```c */
+/**
+ * @name km_core_debug_context_type enum
+ *
+ *
+ * As of version 17, the cached context is an internal property of the state,
+ * not exposed to the consumer of the API -- apart from the Keyman Developer
+ * Keyboard Debugger. However, for other debug purposes, it is helpful to be
+ * able to examine the cached context, so a debug-formatted version of the
+ * context is made available with [km_core_state_context_debug]. This is not
+ * intended to be parsed for reading the context for other purposes, and the
+ * format may change.
+ *
+ * The three context types are: cached, intermediate, and app.
+ */
typedef enum {
+ /** the internal context used by Core, which may be normalized and may contain
+ * markers. This is set via [km_core_state_context_set_if_needed], and will
+ * be modified during keystroke event processing. */
KM_CORE_DEBUG_CONTEXT_CACHED = 0,
+
+ /** internal context used by IMX, only valid during keystroke event
+ * processing. */
KM_CORE_DEBUG_CONTEXT_INTERMEDIATE = 1,
+
+ /** an exact copy of the current context passed in to
+ * [km_core_state_context_set_if_needed], which is used to verify the precise
+ * text manipulations required when emitted changes. This input context is in
+ * "NFU" -- normalization form unknown, and may be mixed normalization so may
+ * require fixups when it is manipulated by keyboard processors that support
+ * normalization, such as the LDML keyboard processor. */
KM_CORE_DEBUG_CONTEXT_APP = 2
} km_core_debug_context_type;
-/*
-```
-## Values
-
-`KM_CORE_DEBUG_CONTEXT_CACHED`
-: the internal context used by Core, which may be normalized
- and may contain markers. This is set via
- [km_core_state_context_set_if_needed], and will be modified
- during keystroke event processing.
-
-`KM_CORE_DEBUG_CONTEXT_INTERMEDIATE`
-: internal context used by IMX, only valid during
- keystroke event processing.
-
-`KM_CORE_DEBUG_CONTEXT_APP`
-: an exact copy of the current context passed in to
- [km_core_state_context_set_if_needed], which is used to verify
- the precise text manipulations required when emitted changes.
- This input context is in "NFU" -- normalization form unknown,
- and may be mixed normalization so may require fixups when
- it is manipulated by keyboard processors that support
- normalization, such as the LDML keyboard processor.
-
--------------------------------------------------------------------------------
-
-# km_core_state_context_debug()
-
-## Description
-
-Returns a debug formatted string of the context from the state.
-
-## Specification
-
-```c */
+/**
+ * @name km_core_state_context_debug function
+ *
+ * Returns a debug formatted string of the context from the state.
+ *
+ * @param state A pointer to the opaque state object to be queried.
+ * @param context_type The type of context to retrieve from the state.
+ * @returns A pointer to a [km_core_cu] UTF-16 string. Must be disposed of by a
+ * call to [km_core_cu_dispose].
+ */
KMN_API
km_core_cu *
-km_core_state_context_debug(km_core_state *state, km_core_debug_context_type context_type);
-
-/*
-```
-## Parameters
-
-`state`
-: A pointer to the opaque state object to be queried.
-
-`context_type`
-: The type of context to retrieve from the state.
-
-## Returns
-
-A pointer to a [km_core_cu] UTF-16 string. Must be disposed of by a call
-to [km_core_cu_dispose].
-
--------------------------------------------------------------------------------
-
-# km_core_cu_dispose()
-
-## Description
-
-Free the allocated memory belonging to a [km_core_cu] array previously
-returned by [km_core_state_context_debug]. May be `nullptr`.
-
-## Specification
+km_core_state_context_debug(
+ const km_core_state *state,
+ km_core_debug_context_type context_type
+);
-```c */
+/**
+ * @name km_core_cu_dispose function
+ *
+ * Free the allocated memory belonging to a [km_core_cu] array previously
+ * returned by [km_core_state_context_debug]. May be `nullptr`.
+ *
+ * @param cp A pointer to the start of the [km_core_cu] array to be disposed
+ * of.
+ */
KMN_API
void
-km_core_cu_dispose(km_core_cu *cp);
-
-/*
-```
-## Parameters
-
-`cp`
-: A pointer to the start of the [km_core_cu] array to be disposed of.
-
--------------------------------------------------------------------------------
-
-# km_core_state_to_json()
-
-## Description
-
-Export the internal state of a [km_core_state] object to a JSON format document
-and place it in the supplied buffer, reporting how much space was used. If null
-is passed as the buffer the number of bytes required is returned. If there is
-insufficent space to hold the document, the contents of the buffer is undefined.
-The encoding of the returned data is UTF-8.
-
-__WARNING__: The structure and format of the JSON document while independently
-versioned is not part of this API and is intended solely for use in diagnostics
-or by development and debugging tools which are aware of processor
-implementation details.
-
-## Specification
-
-```c */
-KMN_API
-km_core_status
-km_core_state_to_json(km_core_state const *state,
- char *buf,
- size_t *space);
-
-/*
-```
-## Parameters
-
-`state`
-: An pointer to an opaque state object.
-
-`buf`
-: A pointer to the buffer to place the C string containing the JSON
- document into. May be null.
-
-`space`
-: A pointer to a size_t variable. This variable must contain the
- number of bytes available in the buffer pointed to by `buf`, unless `buf` is
- null. On return it will hold how many bytes were used.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event an internal memory allocation fails.
-
--------------------------------------------------------------------------------
+km_core_cu_dispose(
+ km_core_cu *cp
+);
+//-------------------------------------------------------------------------------
+/**
---
filename: processor.md
title: Processor - Keyman Core API
---
+*/
-# km_core_event_flags enum
-
-## Description
-
-Bit flags to be used with the `event_flags` parameter of [km_core_process_event]
-
-## Specification
-
-```c */
+/**
+ * @name km_core_event_flags enum
+ *
+ * Bit flags to be used with the `event_flags` parameter of
+ * [km_core_process_event].
+ */
enum km_core_event_flags {
+ /** default value: hardware */
KM_CORE_EVENT_FLAG_DEFAULT = 0,
+
+ /** set if the event is touch, otherwise hardware */
KM_CORE_EVENT_FLAG_TOUCH = 1,
};
-/*
-```
-## Values
-
-`KM_CORE_EVENT_FLAG_DEFAULT`
-: default value: hardware
-
-`KM_CORE_EVENT_FLAG_TOUCH`
-: set if the event is touch, otherwise hardware
-
--------------------------------------------------------------------------------
-
-# km_core_process_event()
-
-## Description
-
-Run the keyboard on an opaque state object with the provided virtual key and modifer
-key state. Updates the state object as appropriate and fills out its internal set
-of actions, which can be retrieved with [km_core_state_get_actions].
-
-The state's actions will be cleared at the start of this call; options and context in
-the state may also be modified.
-
-## Specification
-
-```c */
+/**
+ * @name km_core_process_event function
+ *
+ * Run the keyboard on an opaque state object with the provided virtual key and
+ * modifer key state. Updates the state object as appropriate and fills out its
+ * internal set of actions, which can be retrieved with
+ * [km_core_state_get_actions].
+ *
+ * The state's actions will be cleared at the start of this call; options and
+ * context in the state may also be modified.
+ *
+ * @param state A pointer to the opaque state object.
+ * @param vk A virtual key to be processed.
+ * @param modifier_state The combinations of modifier keys set at the time key
+ * `vk` was pressed, bitmask from the
+ * [km_core_modifier_state] enum.
+ * @param event_flags Event level flags, see [km_core_event_flags]
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_NO_MEM`
+ * : In the event memory is unavailable to allocate internal buffers.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : In the event the `state` pointer is null or an invalid virtual key or
+ * modifier state is passed.
+ */
KMN_API
km_core_status
-km_core_process_event(km_core_state *state,
- km_core_virtual_key vk,
- uint16_t modifier_state,
- uint8_t is_key_down,
- uint16_t event_flags);
-
-/*
-```
-## Parameters
-
-`state`
-: A pointer to the opaque state object.
-
-`vk`
-: A virtual key to be processed.
-
-`modifier_state`
-: The combinations of modifier keys set at the time key `vk` was pressed, bitmask
- from the [km_core_modifier_state] enum.
-
-`event_flags`
-: Event level flags, see [km_core_event_flags]
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event memory is unavailable to allocate internal buffers.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: In the event the `state` pointer is null or an invalid virtual key or modifier
- state is passed.
-
--------------------------------------------------------------------------------
-
-# km_core_event()
-
-## Description
-
-Tell the keyboard processor that an external event has occurred, such as a keyboard
-being activated through the language switching UI.
-
-The keyboard processor may generate actions which should be processed by the
-consumer of the API.
-
-The actions will be cleared at the start of this call; options and context in
-the state may also be modified.
-
-## Specification
+km_core_process_event(
+ km_core_state const *state,
+ km_core_virtual_key vk,
+ uint16_t modifier_state,
+ uint8_t is_key_down,
+ uint16_t event_flags
+);
-```c */
+/**
+ * @name km_core_event function
+ *
+ * Tell the keyboard processor that an external event has occurred, such as a
+ * keyboard being activated through the language switching UI.
+ *
+ * The keyboard processor may generate actions which should be processed by the
+ * consumer of the API.
+ *
+ * The actions will be cleared at the start of this call; options and context in
+ * the state may also be modified.
+ *
+ * @param state A pointer to the opaque state object.
+ * @param event The event to be processed, from [km_core_event_code]
+ * enumeration
+ * @param data Additional event-specific data. Currently unused, must be
+ * nullptr.
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_NO_MEM`
+ * : In the event memory is unavailable to allocate internal buffers.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : In the event the `state` pointer is null or an invalid event or data is
+ * passed.
+ */
KMN_API
km_core_status
km_core_event(
- km_core_state *state,
+ km_core_state const *state,
uint32_t event,
void* data
);
-/*
-```
-## Parameters
-
-`state`
-: A pointer to the opaque state object.
-
-`event`
-: The event to be processed, from [km_core_event_code] enumeration
-
-`data`
-: Additional event-specific data. Currently unused, must be nullptr.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event memory is unavailable to allocate internal buffers.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: In the event the `state` pointer is null or an invalid event or data is passed.
-
--------------------------------------------------------------------------------
-
-# km_core_event_code enum
-
-## Description
-
-Possible events to be passed into Keyman Core from the Platform layer.
-
-## Specification
-
-```c */
+/**
+ * @name km_core_event_code enum
+ *
+ * Possible events to be passed into Keyman Core from the Platform layer.
+ */
enum km_core_event_code {
+ /** A keyboard has been activated by the user. The processor may use this
+ * event, for example, to switch caps lock state or provide other UX. */
KM_CORE_EVENT_KEYBOARD_ACTIVATED = 1,
//future: KM_CORE_EVENT_KEYBOARD_DEACTIVATED = 2,
};
-/*
-```
-## Values
-
-`KM_CORE_EVENT_KEYBOARD_ACTIVATED`
-: A keyboard has been activated by the user. The processor may use this
- event, for example, to switch caps lock state or provide other UX.
-
-$EOF
-*/
+/* $EOF */
#if defined(__cplusplus)
} // extern "C"
#endif
-/*
-```
-*/
diff --git a/core/include/keyman/keyman_core_api_actions.h b/core/include/keyman/keyman_core_api_actions.h
index 2a31e75e9a1..93755eaa808 100644
--- a/core/include/keyman/keyman_core_api_actions.h
+++ b/core/include/keyman/keyman_core_api_actions.h
@@ -16,19 +16,15 @@ extern "C"
{
#endif
-// ----------------------------------------------------------------------------------
-// Actions APIs are now available only to the keyboard debugger, IMX, and Core unit
-// tests (17.0)
-// ----------------------------------------------------------------------------------
-
-/*
-```
-### Action Items
-These provide the results of processing a key event to the Platform layer and
-should be processed by the Platform layer to issue commands to the os text
-services framework to transform the text store in the Client Application, among
-other actions.
-```c
+/**
+---
+filename: actions.md
+title: Actions - Keyman Core API (internal)
+---
+
+As of version 17.0, Actions APIs are now available only to the keyboard
+debugger, IMX, and Core unit tests. Do not use these APIs for other cases.
+Instead, use [km_core_state_get_actions].
*/
typedef struct {
@@ -43,6 +39,16 @@ enum km_core_backspace_type {
KM_CORE_BT_MAX_TYPE_ID
};
+/**
+ * @name km_core_action_item struct
+ *
+ * This provides the results of processing a key event to the Platform layer and
+ * should be processed by the Platform layer to issue commands to the os text
+ * services framework to transform the text store in the Client Application, among
+ * other actions.
+ *
+ * This struct is deprecated; instead use [km_core_actions].
+ */
typedef struct {
uint8_t type;
uint8_t _reserved[sizeof(void*)-sizeof(uint8_t)];
@@ -72,79 +78,87 @@ enum km_core_action_type {
KM_CORE_IT_MAX_TYPE_ID
};
-/*
-```
-### `km_core_state_action_items`
-##### Description:
-Get the list of action items generated by the last call to
-`km_core_process_event`.
-##### Return:
-A pointer to a `km_core_action_item` list, of `*num_items` in length. This data
-becomes invalid when the state object is destroyed, or after a call to
-`km_core_process_event`. Do not modify the contents of this data. The returned
-array is terminated with a `KM_CORE_IT_END` entry.
-##### Parameters:
-- __state__: A pointer to the opaque `km_core_state` object to be queried.
-- __num_items__:
-A pointer to a result variable: The number of items in the action item list
-including the `KM_CORE_IT_END` terminator. May be null if not that
-information is required.
-
-```c
-*/
+/**
+ * @name km_core_state_action_items function
+ *
+ * Get the list of action items generated by the last call to
+ * `km_core_process_event`.
+ *
+ * @param state A pointer to the opaque `km_core_state` object to be
+ * queried.
+ * @param num_items A pointer to a result variable: The number of items in the
+ * action item list including the `KM_CORE_IT_END`
+ * terminator. May be null if not that information is
+ * required.
+ * @returns A pointer to a `km_core_action_item` list, of `*num_items` in
+ * length. This data becomes invalid when the state object is
+ * destroyed, or after a call to `km_core_process_event`. Do not
+ * modify the contents of this data. The returned array is terminated
+ * with a `KM_CORE_IT_END` entry.
+ */
KMN_API
km_core_action_item const *
-km_core_state_action_items(km_core_state const *state,
- size_t *num_items);
-
-/*
-```
-### `km_core_state_queue_action_items`
-##### Description:
-Queue actions for the current keyboard processor state; normally
-used in IMX callbacks called during `km_core_process_event`.
-##### Return:
-- `KM_CORE_STATUS_OK`: On success.
-- `KM_CORE_STATUS_INVALID_ARGUMENT`:
-In the event the `state` or `action_items` pointer are null.
-##### Parameters:
-- __state__: A pointer to the opaque `km_core_state` object to be queried.
-- __action_items__: The action items to be added to the core
- queue. Must be terminated with a `KM_CORE_IT_END` entry.
+km_core_state_action_items(
+ km_core_state const *state,
+ size_t *num_items
+);
-```c
-*/
+/**
+ * @name km_core_state_queue_action_items function
+ *
+ * Queue actions for the current keyboard processor state; normally
+ * used in IMX callbacks called during `km_core_process_event`.
+ *
+ * @param state A pointer to the opaque `km_core_state` object to be
+ * queried.
+ * @param action_items The action items to be added to the core queue. Must be
+ * terminated with a `KM_CORE_IT_END` entry.
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : In the event the `state` or `action_items` pointer are null.
+ */
KMN_API
km_core_status
-km_core_state_queue_action_items(km_core_state *state,
- km_core_action_item const *action_items);
-
-/*
-```
-### `km_core_process_queued_actions`
-##### Description:
-Process the keyboard processors queued actions for the opaque state object.
-Updates the state object as appropriate and fills out its action list.
-The client can add actions externally via the `km_core_state_queue_action_items` and
-then request the processing of the actions with this method.
-
-The state action list will be cleared at the start of this call; options and context in
-the state may also be modified.
-##### Return status:
-- `KM_CORE_STATUS_OK`: On success.
-- `KM_CORE_STATUS_NO_MEM`:
-In the event memory is unavailable to allocate internal buffers.
-- `KM_CORE_STATUS_INVALID_ARGUMENT`:
-In the event the `state` pointer is null
+km_core_state_queue_action_items(
+ km_core_state *state,
+ km_core_action_item const *action_items
+);
-##### Parameters:
-- __state__: A pointer to the opaque state object.
-
-```c
-*/
+/**
+ * @name km_core_process_queued_actions function
+ *
+ * Process the keyboard processors queued actions for the opaque state object.
+ * Updates the state object as appropriate and fills out its action list.
+ * The client can add actions externally via the
+ * `km_core_state_queue_action_items` and then request the processing of the
+ * actions with this method.
+ *
+ * The state action list will be cleared at the start of this call; options and context in
+ * the state may also be modified.
+ *
+ * @param state A pointer to the opaque state object.
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_NO_MEM`
+ * : In the event memory is unavailable to allocate internal buffers.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : In the event the `state` pointer is null
+ */
KMN_API
km_core_status
-km_core_process_queued_actions(km_core_state *state);
+km_core_process_queued_actions(
+ km_core_state *state
+);
+
+/* $EOF */
#if defined(__cplusplus)
} // extern "C"
diff --git a/core/include/keyman/keyman_core_api_bits.h b/core/include/keyman/keyman_core_api_bits.h
index bd1f519bffe..2fcc832f01e 100644
--- a/core/include/keyman/keyman_core_api_bits.h
+++ b/core/include/keyman/keyman_core_api_bits.h
@@ -33,7 +33,7 @@
#undef _kmn_export_flag
#undef _kmn_import_flag
#undef _kmn_static_flag
- #else // How MSVC sepcifies function level attributes adn deprecation
+ #else // How MSVC sepcifies function level attributes and deprecation
#define _kmn_and
#define _kmn_tag_fn(a) __declspec(a)
#define _kmn_deprecated_flag deprecated
diff --git a/core/include/keyman/keyman_core_api_context.h b/core/include/keyman/keyman_core_api_context.h
index 3c6e5b86efa..90b2ed00023 100644
--- a/core/include/keyman/keyman_core_api_context.h
+++ b/core/include/keyman/keyman_core_api_context.h
@@ -18,17 +18,18 @@ extern "C"
typedef struct km_core_context km_core_context;
-// ----------------------------------------------------------------------------------
-// Context APIs are now available only to the keyboard debugger, IMX, and Core unit
-// tests (17.0)
-// ----------------------------------------------------------------------------------
-
-/*
+/**
---
filename: context.md
title: Internal Context - Keyman Core API
---
+As of version 17.0, Context APIs are now available only to the keyboard
+debugger, IMX, and Core unit tests. Do not use these APIs for other cases.
+Instead, use [`km_core_state_context_set_if_needed`].
+
+----------------------------------------------------------------------------------
+
The context is the text prior to the insertion point (caret, cursor). The
context is constructed by the Platform layer, typically by interrogating the
Client Application. The context will be updated by the engine for keystroke
@@ -55,355 +56,233 @@ use of the context functionality in this header, but these functions should not
be used in other places.
-------------------------------------------------------------------------------
+*/
-# km\_core\_context\_type enum {#km_core_context_type}
-
-## Description
-
-[`km_core_context_item.type`](#km_core_context_item) values which identify which
-data value (if any) the context item carries.
-
-## Specification
-
-```c */
+/**
+ * @name km_core_context_type enum
+ *
+ * [`km_core_context_item`].type values which identify which data value (if any)
+ * the context item carries.
+ */
enum km_core_context_type {
+ /** A [`km_core_context_item`](#km_core_context_item) of this type marks the
+ * end of an array of context items. */
KM_CORE_CT_END,
+
+ /** The context item contains a Unicode Scalar Value which must be accessed
+ * through the [`km_core_context_item.character`](#km_core_context_item)
+ * union member. */
KM_CORE_CT_CHAR,
+
+ /** The context item contains a positional marker which must be accessed
+ * through the [`km_core_context_item.marker`](#km_core_context_item) union
+ * member. */
KM_CORE_CT_MARKER
};
-/*
-```
-## Values {#km_core_ct_type_values}
-
-`KM_CORE_CT_END`
-: A [`km_core_context_item`](#km_core_context_item) of this type marks the end of
-an array of context items.
-
-`KM_CORE_CT_CHAR`
-: The context item contains a Unicode Scalar Value which must be accessed
-through the [`km_core_context_item.character`](#km_core_context_item) union
-member.
-
-`KM_CORE_CT_MARKER`
-: The context item contains a positional marker which must be accessed through
-the [`km_core_context_item.marker`](#km_core_context_item) union member.
-
--------------------------------------------------------------------------------
-
-# km\_core\_context\_item struct {#km_core_context_item}
-
-## Description
-
-A tagged union representing an element of context which can be either a Unicode
-character or a positional marker.
-## Specification
-```c */
+/**
+ * @name km_core_context_item struct
+ *
+ * A tagged union representing an element of context which can be either a
+ * Unicode character or a positional marker.
+ */
typedef struct {
+ /** Identifies the union member to access. A value of enum
+ * [`km_core_context_type`] values. */
uint8_t type;
+
+ /** Space reserved for alignment purposes and possible future use. */
uint8_t _reserved[3];
+
union {
+ /** A Unicode Scalar Value. */
km_core_usv character;
+
+ /** A marker value, only meaningful to an engine. */
uint32_t marker;
};
} km_core_context_item;
-/*
-```
-## Members
-`type`
-:Identifies the union member to access. A value of enum
-[`km_core_context_type`](#km_core_context_type) values.
-
-`_reserved`
-:Space reserved for alignment purposes and possible future use.
-
-`character`
-:A Unicode Scalar Value.
-
-`marker`
-:A marker value, only meaningful to an engine.
-
--------------------------------------------------------------------------------
-
-# KM\_CORE\_CONTEXT\_ITEM\_END macro
-
-## Description
-
-Convenience macro to declare a terminating entry in a
-[`km_core_context_item`](#km_core_context_item) item array.
-
-## Specification
-```c */
+/**
+ * @name KM_CORE_CONTEXT_ITEM_END macro
+ *
+ * Convenience macro to declare a terminating entry in a
+ * [`km_core_context_item`] item array.
+ */
#define KM_CORE_CONTEXT_ITEM_END {KM_CORE_CT_END, {0,}, {0,}}
-/*
-```
--------------------------------------------------------------------------------
-
-# km_core_state_get_intermediate_context()
-
-## Description
-
-Get access to the state object's keyboard processor's intermediate context. This context
-is used during an IMX callback, part way through processing a keystroke.
-
-## Specification
-```c */
+/**
+ * @name km_core_state_get_intermediate_context function
+ *
+ * Get access to the state object's keyboard processor's intermediate context.
+ * This context is used during an IMX callback, part way through processing a
+ * keystroke.
+ *
+ * @param stat e A pointer to the opaque state object to be queried.
+ * @param context_items A pointer to a variable to receive a context item
+ * array. Must be disposed of by a call to
+ * [`km_core_context_items_dispose`].
+ * @returns `KM_CORE_STATUS_OK` on success
+ */
KMN_API
km_core_status
-km_core_state_get_intermediate_context(km_core_state *state, km_core_context_item ** context_items);
+km_core_state_get_intermediate_context(
+ km_core_state *state,
+ km_core_context_item ** context_items
+);
-/*
-```
-## Parameters
-
-`state`
-: A pointer to the opaque state object to be queried.
-
-## Returns
-
-A pointer to an context item array. Must be disposed of by a call
-to `km_core_context_items_dispose`.
-
--------------------------------------------------------------------------------
-
-# km_core_context_items_dispose()
-
-## Description
-
-Free the allocated memory belonging to a `km_core_context_item` array previously
-returned by `km_core_state_get_intermediate_context` (internally, also
-`context_items_from_utf16` and `km_core_context_get`)
-
-## Specification
-
-```c
-*/
+/**
+ * @name km_core_context_items_dispose function
+ *
+ * Free the allocated memory belonging to a `km_core_context_item` array
+ * previously returned by `km_core_state_get_intermediate_context` (internally,
+ * also `context_items_from_utf16` and `km_core_context_get`)
+ *
+ * @param context_items A pointer to the start of the `km_core_context_item`
+ * array to be disposed of.
+ */
KMN_API
void
-km_core_context_items_dispose(km_core_context_item *context_items);
-
-/*
-```
-## Parameters
-
-`context_items`
-: A pointer to the start of the `km_core_context_item` array
- to be disposed of.
-
--------------------------------------------------------------------------------
-
-# km_core_state_context()
-
-## Description
+km_core_context_items_dispose(
+ km_core_context_item *context_items
+);
-Get access to the state object's cached context.
-
-## Specification
-```c */
+/**
+ * @name km_core_state_context function
+ *
+ * Get access to the state object's cached context.
+ *
+ * @param state A pointer to the opaque state object to be queried.
+ * @returns A pointer to an opaque context object. This pointer is valid for
+ * the lifetime of the state object. If null is passed in, then null
+ * is returned.
+ */
KMN_API
km_core_context *
-km_core_state_context(km_core_state const *state);
+km_core_state_context(
+ km_core_state const *state
+);
-/*
-## Parameters
-
-`state`
-: A pointer to the opaque state object to be queried.
-
-## Returns
-
-A pointer to an opaque context object. This pointer is valid for the
-lifetime of the state object. If null is passed in, then null is
-returned.
-
--------------------------------------------------------------------------------
-
-# km_core_state_app_context()
-
-## Description
-
-Get access to the state object's application context.
-
-## Specification
-```c */
+/**
+ * @name km_core_state_app_context function
+ *
+ * Get access to the state object's application context.
+ *
+ * @param state A pointer to the opaque state object to be queried.
+ * @returns A pointer to an opaque context object. This pointer is valid for
+ * the lifetime of the state object. If null is passed in, then null
+ * is returned.
+ */
KMN_API
km_core_context *
-km_core_state_app_context(km_core_state const *state);
-
-/*
-```
-## Parameters
-
-`state`
-: A pointer to the opaque state object to be queried.
-
-## Returns
-
-A pointer to an opaque context object. This pointer is valid for the
-lifetime of the state object. If null is passed in, then null is
-returned.
-
--------------------------------------------------------------------------------
-
-# km_core_context_set()
-
-## Description
+km_core_state_app_context(
+ km_core_state const *state
+);
-Replace the contents of the current context with a new sequence of
-`km_core_context_item` entries.
-
-## Specification
-```c */
+/**
+ * @name km_core_context_set function
+ *
+ * Replace the contents of the current context with a new sequence of
+ * `km_core_context_item` entries.
+ *
+ * @param context A pointer to an opaque context object
+ * @param context_items A pointer to the start of the `km_core_context_item`
+ * array containing the new context. It must be
+ * terminated with an item of type `KM_CORE_CT_END`.
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : If non-optional parameters are null.
+ *
+ * `KM_CORE_STATUS_NO_MEM`
+ * : In the event not enough memory can be allocated to grow the context
+ * buffer internally.
+ */
KMN_API
km_core_status
-km_core_context_set(km_core_context *context,
- km_core_context_item const *context_items);
-
-/*
-```
-## Parameters
-
-`context`
-: A pointer to an opaque context object
+km_core_context_set(
+ km_core_context *context,
+ km_core_context_item const *context_items
+);
-`context_items`
-: A pointer to the start of the `km_core_context_item`
- array containing the new context. It must be terminated with an item
- of type `KM_CORE_CT_END`.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: If non-optional parameters are null.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event not enough memory can be allocated to
- grow the context buffer internally.
-
--------------------------------------------------------------------------------
-
-# km_core_context_clear()
-
-## Description
-
-Removes all context_items from the internal array. If `context` is
-null, has no effect.
-
-## Specification
-```c */
+/**
+ * @name km_core_context_clear function
+ *
+ * Removes all context_items from the internal array. If `context` is null, has
+ * no effect.
+ *
+ * @param context A pointer to an opaque context object
+ */
KMN_API
void
-km_core_context_clear(km_core_context *);
-
-/*
-```
-## Parameters
-
-`context`
-: A pointer to an opaque context object
-
--------------------------------------------------------------------------------
-
-# km_core_context_item_list_size()
-
-## Description
-
-Return the length of a terminated `km_core_context_item` array.
+km_core_context_clear(
+ km_core_context *
+);
-## Specification
-```c */
+/**
+ * @name km_core_context_item_list_size function
+ *
+ * Return the length of a terminated `km_core_context_item` array.
+ *
+ * @param context_items A pointer to a `KM_CORE_CT_END` terminated array of
+ * `km_core_context_item` values.
+ * @returns The number of items in the list, not including terminating item, or
+ * 0 if `context_items` is null.
+ */
KMN_API
size_t
-km_core_context_item_list_size(km_core_context_item const *context_items);
-
-/*
-```
-## Parameters
-
-`context_items`
-: A pointer to a `KM_CORE_CT_END` terminated array of
- `km_core_context_item` values.
-
-## Returns
-
-The number of items in the list, not including terminating item,
-or 0 if `context_items` is null.
-
--------------------------------------------------------------------------------
-
-# km_core_context_get()
+km_core_context_item_list_size(
+ km_core_context_item const *context_items
+);
-## Description
-
-Copies all items in the context into a new array and returns the new array.
-This must be disposed of by caller using `km_core_context_items_dispose`.
-
-## Specification
-```c */
+/**
+ * @name km_core_context_get function
+ *
+ * Copies all items in the context into a new array and returns the new array.
+ * This must be disposed of by caller using `km_core_context_items_dispose`.
+ *
+ * @param context A pointer to an opaque context object
+ * @param out Pointer to the result variable: A pointer to the start of
+ * the `km_core_context_item` array containing a copy of the
+ * context. Terminated with a type of `KM_CORE_CT_END`. Must be
+ * disposed of with `km_core_context_items_dispose`.
+ * @returns One of the following values:
+ *
+ * `KM_CORE_STATUS_OK`
+ * : On success.
+ *
+ * `KM_CORE_STATUS_INVALID_ARGUMENT`
+ * : If non-optional parameters are null.
+ *
+ * `KM_CORE_STATUS_NO_MEM`
+ * : In the event not enough memory can be allocated for the output buffer.
+ */
KMN_API
km_core_status
-km_core_context_get(km_core_context const *context,
- km_core_context_item **out);
-
-/*
-```
-## Parameters
+km_core_context_get(
+ km_core_context const *context,
+ km_core_context_item **out
+);
-`context`
-: A pointer to an opaque context object
-
-`out`
-: pointer to the result variable: A pointer to the start
- of the `km_core_context_item` array containing a copy of
- the context. Terminated with a type of `KM_CORE_CT_END`.
- Must be disposed of with `km_core_context_items_dispose`.
-
-## Returns
-
-`KM_CORE_STATUS_OK`
-: On success.
-
-`KM_CORE_STATUS_INVALID_ARGUMENT`
-: If non-optional parameters are null.
-
-`KM_CORE_STATUS_NO_MEM`
-: In the event not enough memory can be allocated for the
- output buffer.
-
--------------------------------------------------------------------------------
-
-# km_core_context_length()
-
-## Description
-
-Return the number of items in the context.
-
-## Specification
-```c */
+/**
+ * @name km_core_context_length function
+ *
+ * Return the number of items in the context.
+ *
+ * @param context A pointer to an opaque context object
+ * @returns The number of items in the context, and will return 0 if passed a
+ * null `context` pointer.
+ */
KMN_API
size_t
-km_core_context_length(km_core_context *);
-
-/*
-```
-## Parameters
+km_core_context_length(
+ km_core_context *context
+);
-`context`
-: A pointer to an opaque context object
-
-## Returns
-
-The number of items in the context, and will return 0 if
-passed a null `context` pointer.
-
-$EOF
--------------------------------------------------------------------------------
-*/
+/* $EOF */
#if defined(__cplusplus)
} // extern "C"
diff --git a/core/include/keyman/keyman_core_api_debug.h b/core/include/keyman/keyman_core_api_debug.h
index 493de708a42..00f7132877b 100644
--- a/core/include/keyman/keyman_core_api_debug.h
+++ b/core/include/keyman/keyman_core_api_debug.h
@@ -24,6 +24,8 @@ extern "C"
#endif
/**
+ * @name DEBUG_MAX_CONTEXT constant
+ *
* The maximum size of context in km_core_cu units for a single debug
* event. This is taken from MAXCONTEXT in keyman32 (Windows) and is purely
* a convenience value. We can increase it if there is a demonstrated need.
@@ -31,6 +33,8 @@ extern "C"
#define DEBUG_MAX_CONTEXT 80
/**
+ * @name DEBUG_MAX_STORE_OFFSETS constant
+ *
* The number of stores that can be processed in a rule. This is taken from
* MAXSTOREOFFSETS in keyman32 (Windows) and is purely a convenience value.
* We can increase it if there is a demonstrated need.
@@ -39,16 +43,27 @@ extern "C"
#define DEBUG_STORE_OFFSETS_SIZE (DEBUG_MAX_STORE_OFFSETS*2+1)
/**
- * These modifier flags are used internally in the kmx engine, so will be
- * exposed only in debugging modifier states.
+ * @name KM_CORE_MODIFIER_VIRTUALKEY constant
+ *
+ * This modifier flag is used internally in the kmx engine, so will be exposed
+ * only in debugging modifier states.
*/
#define KM_CORE_MODIFIER_VIRTUALKEY 0x4000
+
+/**
+ * @name KM_CORE_MODIFIER_VIRTUALCHARKEY constant
+ *
+ * This modifier flag is used internally in the kmx engine, so will be exposed
+ * only in debugging modifier states.
+ */
#define KM_CORE_MODIFIER_VIRTUALCHARKEY 0x8000
/**
- * Input key event data. The `character` member is derived from
- * a US English key event for vk + modifier_state, and is 0 if
- * the vk + modifier_state do not generate a character.
+ * @name km_core_state_debug_key_info struct
+ *
+ * Input key event data. The `character` member is derived from a US English key
+ * event for vk + modifier_state, and is 0 if the vk + modifier_state do not
+ * generate a character.
*
* Used only in event type KM_CORE_DEBUG_BEGIN.
*/
@@ -59,6 +74,8 @@ typedef struct {
} km_core_state_debug_key_info;
/**
+ * @name km_core_state_debug_kmx_option_info struct
+ *
* Option event data.
*
* Used only in event type KM_CORE_DEBUG_SET_OPTION.
@@ -69,6 +86,8 @@ typedef struct {
} km_core_state_debug_kmx_option_info;
/**
+ * @name km_core_state_debug_kmx_info struct
+ *
* KMX processor data for each event. kmx_base.h defines the types that are
* passed in here, read only. Warning: context may contain sentinel values
* for markers, with the kmx UC_SENTINEL, CODE_CONTEXT, DEADKEY pattern.
@@ -78,7 +97,6 @@ typedef struct {
*
* Used in all event types except KM_CORE_DEBUG_BEGIN, KM_CORE_DEBUG_END.
*/
-
typedef struct {
km_core_cu context[DEBUG_MAX_CONTEXT]; // The context matched by the rule (? may not need this?) // TODO: rename to context_matched
void *group; // LPGROUP
@@ -93,6 +111,8 @@ typedef struct {
} km_core_state_debug_kmx_info;
/**
+ * @name km_core_state_debug_item struct
+ *
* A single debug event.
*/
typedef struct {
@@ -103,6 +123,8 @@ typedef struct {
} km_core_state_debug_item;
/**
+ * @name km_core_debug_type enum
+ *
* A single debug event.
*/
enum km_core_debug_type {
@@ -132,29 +154,37 @@ enum km_core_debug_type {
#define KM_CORE_DEBUG_FLAG_OUTPUTKEYSTROKE 0x0001
/**
+ * @name km_core_state_debug_set function
+ *
* Enable or disable debug tracing
*
* @param state Pointer to initialized state
* @param value Set to 1 to enable debugging, 0 to disable
- *
* @returns KM_CORE_STATUS_OK on success
*/
KMN_API
km_core_status
-km_core_state_debug_set(km_core_state *state, int value);
+km_core_state_debug_set(
+ km_core_state *state,
+ int value
+);
/**
+ * @name km_core_state_debug_get function
+ *
* Get current debug tracing status
*
* @param state Pointer to initialized state
- *
* @returns 1 if debugging is enabled, 0 otherwise
*/
KMN_API
uint8_t
-km_core_state_debug_get(km_core_state const *state);
+km_core_state_debug_get(
+ km_core_state const *state
+);
/**
+ * @name km_core_state_debug_items function
* Read current debug trace log
*
* @param state Pointer to initialized state
@@ -166,7 +196,12 @@ km_core_state_debug_get(km_core_state const *state);
*/
KMN_API
km_core_state_debug_item const *
-km_core_state_debug_items(km_core_state const *state, size_t *num_items);
+km_core_state_debug_items(
+ km_core_state const *state,
+ size_t *num_items
+);
+
+/* $EOF */
#if defined(__cplusplus)
} // extern "C"
diff --git a/core/include/keyman/keyman_core_api_vkeys.h b/core/include/keyman/keyman_core_api_vkeys.h
index cd0b983ebef..efafedf5a55 100644
--- a/core/include/keyman/keyman_core_api_vkeys.h
+++ b/core/include/keyman/keyman_core_api_vkeys.h
@@ -1,20 +1,26 @@
/*
- Copyright: © 2018 SIL International.
- Description: API declarations for modifier keys, handy access masks and
- Keyman VKEY names. These follow the same keytop->code
- associations as the Windows API. This is a separate header to
- maintain readability of the primary API header.
- Create Date: 17 Oct 2018
- Authors: Tim Eves (TSE)
- History: 17 Oct 2018 - TSE - Moved & refactored km_core_modifier_state
- from keyman_core_api.h.
- - Added VKey and mask definitions.
- 6 Oct 2018 - TSE - Move into keyman folder.
+ * Keyman is copyright (C) SIL International. MIT License.
+ *
+ * Created by Tim Eves (TSE) on 2018-10-17
+ */
+#pragma once
-*/
+/**
+---
+filename: virtual-keys.md
+title: Virtual Keys - Keyman Core API
+---
-#pragma once
+API declarations for modifier keys, handy access masks and Keyman VKEY names.
+These follow the same keytop->code associations as the Windows API. This is a
+separate header to maintain readability of the primary API header.
+*/
+/**
+ * @name km_core_modifier_state enum
+ *
+ * An integral type bitmask representing the state of each modifier key.
+ */
enum km_core_modifier_state {
KM_CORE_MODIFIER_NONE = 0,
KM_CORE_MODIFIER_LCTRL = 1 << 0,
@@ -41,6 +47,12 @@ enum km_core_modifier_state {
*/
};
+/**
+ * @name km_core_modifier_mask
+ *
+ * An integral type bitmask representing common bitmask sets for
+ * km_core_modifier_state
+ */
enum km_core_modifier_mask {
KM_CORE_MODIFIER_MASK_ALL = 0x7f,
KM_CORE_MODIFIER_MASK_ALT_GR_SIM = KM_CORE_MODIFIER_LCTRL|KM_CORE_MODIFIER_LALT,
@@ -52,12 +64,17 @@ enum km_core_modifier_mask {
KM_CORE_MODIFIER_MASK_SCROLLLOCK = 0x3000,*/
};
-// These are Windows API VKEYs, using Keyman VKEY names.
-// Underlying values from winuser.h (https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes)
-// Note, some codes are defined here for parity with winuser.h, but are
-// not available in .kmn language. These codes are marked as "internal"
-// by using `__` in the name: `KM_CORE_VKEY____`
-enum km_kpb_virtual_key {
+/**
+ * @name km_core_virtual_key_value
+ *
+ * These are Windows API VKEYs, using Keyman VKEY names. Underlying values from
+ * winuser.h
+ * (https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes)
+ * Note, some codes are defined here for parity with winuser.h, but are not
+ * available in .kmn language. These codes are marked as "internal" by using
+ * `__` in the name: `KM_CORE_VKEY____`
+ */
+enum km_core_virtual_key_value {
KM_CORE_VKEY__00,
KM_CORE_VKEY_LBUTTON, // 0x01
KM_CORE_VKEY_RBUTTON, // 0x02
@@ -315,3 +332,5 @@ enum km_kpb_virtual_key {
KM_CORE_VKEY__OEMCLEAR__, // 0xfe
KM_CORE_VKEY__FF, // 0xff
};
+
+/* $EOF */
diff --git a/core/include/ldml/keyman_core_ldml.h b/core/include/ldml/keyman_core_ldml.h
index f521cb356fc..2782730cea6 100644
--- a/core/include/ldml/keyman_core_ldml.h
+++ b/core/include/ldml/keyman_core_ldml.h
@@ -21,6 +21,87 @@
#define LDML_CLDR_IMPORT_BASE "cldr"
#define LDML_CLDR_TEST_VERSION_LATEST "techpreview"
#define LDML_CLDR_VERSION_LATEST "46"
+#define LDML_DISP_ITEM_FLAGS_IS_BLANK_KEY 0x20
+#define LDML_DISP_ITEM_FLAGS_IS_DEADKEY 0x10
+#define LDML_DISP_ITEM_FLAGS_IS_FRAME_KEY 0x4
+#define LDML_DISP_ITEM_FLAGS_IS_HIGHLIGHTED 0x8
+#define LDML_DISP_ITEM_FLAGS_IS_ID 0x1
+#define LDML_DISP_ITEM_FLAGS_IS_SVG 0x2
+#define LDML_DISP_ITEM_FLAGS_MASK_FLAGS 0xFFF
+#define LDML_DISP_ITEM_FLAGS_MASK_HINT 0xF000
+#define LDML_DISP_ITEM_FLAGS_MASK_KEY_CAP_TYPE 0xFFFF0000
+#define LDML_DISP_ITEM_FLAGS_SHIFT_HINT 0xC
+#define LDML_DISP_ITEM_FLAGS_SHIFT_KEY_CAP_TYPE 0x10
+#define LDML_DISP_ITEM_HINT_E 0x5
+#define LDML_DISP_ITEM_HINT_N 0x2
+#define LDML_DISP_ITEM_HINT_NE 0x3
+#define LDML_DISP_ITEM_HINT_NW 0x1
+#define LDML_DISP_ITEM_HINT_PRIMARY 0x0
+#define LDML_DISP_ITEM_HINT_S 0x7
+#define LDML_DISP_ITEM_HINT_SE 0x8
+#define LDML_DISP_ITEM_HINT_SW 0x6
+#define LDML_DISP_ITEM_HINT_W 0x4
+#define LDML_DISP_KEY_CAP_123 0x13
+#define LDML_DISP_KEY_CAP_ABC_LOWER 0x11
+#define LDML_DISP_KEY_CAP_ABC_UPPER 0x10
+#define LDML_DISP_KEY_CAP_ALT 0x19
+#define LDML_DISP_KEY_CAP_ALTCTRLSHIFT 0x66
+#define LDML_DISP_KEY_CAP_ALTGR 0x2
+#define LDML_DISP_KEY_CAP_ALTSHIFT 0x64
+#define LDML_DISP_KEY_CAP_BKSP 0x4
+#define LDML_DISP_KEY_CAP_CAPS 0x3
+#define LDML_DISP_KEY_CAP_CGJ 0x7A
+#define LDML_DISP_KEY_CAP_CTRL 0x1
+#define LDML_DISP_KEY_CAP_CTRLSHIFT 0x65
+#define LDML_DISP_KEY_CAP_CURRENCY 0x14
+#define LDML_DISP_KEY_CAP_EMQ 0x85
+#define LDML_DISP_KEY_CAP_EMSP 0x87
+#define LDML_DISP_KEY_CAP_ENQ 0x84
+#define LDML_DISP_KEY_CAP_ENSP 0x86
+#define LDML_DISP_KEY_CAP_ENTER 0x5
+#define LDML_DISP_KEY_CAP_HIDE 0xA
+#define LDML_DISP_KEY_CAP_HSP 0x8E
+#define LDML_DISP_KEY_CAP_HTAB 0xA2
+#define LDML_DISP_KEY_CAP_LALT 0x56
+#define LDML_DISP_KEY_CAP_LALTCTRL 0x60
+#define LDML_DISP_KEY_CAP_LALTCTRLSHIFT 0x62
+#define LDML_DISP_KEY_CAP_LALTSHIFT 0x67
+#define LDML_DISP_KEY_CAP_LCTRL 0x58
+#define LDML_DISP_KEY_CAP_LCTRLSHIFT 0x69
+#define LDML_DISP_KEY_CAP_LTRBKSP 0x4
+#define LDML_DISP_KEY_CAP_LTRENTER 0x5
+#define LDML_DISP_KEY_CAP_LTRM 0x90
+#define LDML_DISP_KEY_CAP_MENU 0xB
+#define LDML_DISP_KEY_CAP_NARNBSP 0x83
+#define LDML_DISP_KEY_CAP_NBSP 0x82
+#define LDML_DISP_KEY_CAP_PUNCTSP 0x8C
+#define LDML_DISP_KEY_CAP_RALT 0x57
+#define LDML_DISP_KEY_CAP_RALTCTRL 0x61
+#define LDML_DISP_KEY_CAP_RALTCTRLSHIFT 0x63
+#define LDML_DISP_KEY_CAP_RALTSHIFT 0x68
+#define LDML_DISP_KEY_CAP_RCTRL 0x59
+#define LDML_DISP_KEY_CAP_RCTRLSHIFT 0x70
+#define LDML_DISP_KEY_CAP_RTLBKSP 0x72
+#define LDML_DISP_KEY_CAP_RTLENTER 0x71
+#define LDML_DISP_KEY_CAP_RTLM 0x91
+#define LDML_DISP_KEY_CAP_SH 0xA1
+#define LDML_DISP_KEY_CAP_SHIFT 0x8
+#define LDML_DISP_KEY_CAP_SHIFTED 0x9
+#define LDML_DISP_KEY_CAP_SHIFTEDLOCK 0x74
+#define LDML_DISP_KEY_CAP_SHIFTLOCK 0x73
+#define LDML_DISP_KEY_CAP_SP 0x80
+#define LDML_DISP_KEY_CAP_SYMBOL 0x15
+#define LDML_DISP_KEY_CAP_TAB 0x6
+#define LDML_DISP_KEY_CAP_TABLEFT 0x7
+#define LDML_DISP_KEY_CAP_THSP 0x8D
+#define LDML_DISP_KEY_CAP_WJ 0x78
+#define LDML_DISP_KEY_CAP_ZWJ 0x77
+#define LDML_DISP_KEY_CAP_ZWNJ 0x75
+#define LDML_DISP_KEY_CAP_ZWNJANDROID 0x76
+#define LDML_DISP_KEY_CAP_ZWNJGENERIC 0x79
+#define LDML_DISP_KEY_CAP_ZWNJIOS 0x75
+#define LDML_DISP_KEY_CAP_ZWSP 0x81
+#define LDML_ELEM_FLAGS_FLAGS_MASK 0xFFFF
#define LDML_ELEM_FLAGS_ORDER_BITSHIFT 0x10
#define LDML_ELEM_FLAGS_ORDER_MASK 0xFF0000
#define LDML_ELEM_FLAGS_PREBASE 0x8
@@ -45,7 +126,16 @@
#define LDML_KEYS_MOD_NONE 0x0
#define LDML_KEYS_MOD_OTHER 0x10000
#define LDML_KEYS_MOD_SHIFT 0x10
-#define LDML_LAYR_LIST_HARDWARE_TOUCH "touch"
+#define LDML_KMXPLUS_VERSION_17 0x1100
+#define LDML_KMXPLUS_VERSION_19 0x1300
+#define LDML_LAYR_FORM_FLAGS_CHIRAL_SEPARATE 0x2
+#define LDML_LAYR_FORM_FLAGS_SHOW_BASE_LAYOUT 0x1
+#define LDML_LAYR_FORM_HARDWARE_ABNT2 "abnt2"
+#define LDML_LAYR_FORM_HARDWARE_ISO "iso"
+#define LDML_LAYR_FORM_HARDWARE_JIS "jis"
+#define LDML_LAYR_FORM_HARDWARE_KS "ks"
+#define LDML_LAYR_FORM_HARDWARE_TOUCH "touch"
+#define LDML_LAYR_FORM_HARDWARE_US "us"
#define LDML_LAYR_MAX_MINDEVICEWIDTH 0x3E7
#define LDML_LAYR_MIN_MINDEVICEWIDTH 0x1
#define LDML_LENGTH_BKSP 0xC
@@ -57,7 +147,8 @@
#define LDML_LENGTH_ELEM_ITEM_ELEMENT 0x8
#define LDML_LENGTH_FINL 0x8
#define LDML_LENGTH_FINL_ITEM 0x10
-#define LDML_LENGTH_HEADER 0x8
+#define LDML_LENGTH_HEADER_17 0x8
+#define LDML_LENGTH_HEADER_19 0xC
#define LDML_LENGTH_KEYS 0x18
#define LDML_LENGTH_KEYS_FLICK_ELEMENT 0x8
#define LDML_LENGTH_KEYS_FLICK_LIST 0xC
@@ -65,8 +156,9 @@
#define LDML_LENGTH_KEYS_KMAP 0xC
#define LDML_LENGTH_LAYR 0x18
#define LDML_LENGTH_LAYR_ENTRY 0x10
+#define LDML_LENGTH_LAYR_FORM_V17 0x10
+#define LDML_LENGTH_LAYR_FORM_V19 0x18
#define LDML_LENGTH_LAYR_KEY 0x4
-#define LDML_LENGTH_LAYR_LIST 0x10
#define LDML_LENGTH_LAYR_ROW 0x8
#define LDML_LENGTH_LIST 0x10
#define LDML_LENGTH_LIST_INDEX 0x4
@@ -120,6 +212,8 @@
#define LDML_SECTIONNAME_USET "uset"
#define LDML_SECTIONID_VARS 0x73726176 /* "vars" */
#define LDML_SECTIONNAME_VARS "vars"
+#define LDML_SECTIONID_SEC2 0x32636573
+#define LDML_SECTIONNAME_SEC2 "sec2"
#define LDML_TRAN_FLAGS_ERROR 0x1
#define LDML_TRAN_GROUP_TYPE_REORDER 0x1
#define LDML_TRAN_GROUP_TYPE_TRANSFORM 0x0
diff --git a/core/include/ldml/keyman_core_ldml.ts b/core/include/ldml/keyman_core_ldml.ts
index c975c94b71d..e693b23c8ce 100644
--- a/core/include/ldml/keyman_core_ldml.ts
+++ b/core/include/ldml/keyman_core_ldml.ts
@@ -5,7 +5,6 @@
to be shared between TypeScript and C++ via the generator (below)
*/
-
// NOTICE!
//
// If you update this file, you *must* be sure to re-run
@@ -17,32 +16,43 @@
// It is not updated automatically.
+/**
+ * Names of the possible sections
+ */
+export const SECTION_IDENTS = [
+ // Keep this sorted, but with `sect` as the first entry.
+ 'sect',
+ 'bksp',
+ 'disp',
+ 'elem',
+ 'keys',
+ 'layr',
+ 'list',
+ 'loca',
+ 'meta',
+ 'strs',
+ 'tran',
+ 'uset',
+ 'vars'
+] as const;
+
/**
* Defines the section identifiers and ensures that we include each and every
* one of them in the `sections` block and gives us a type which we can iterate
* through.
*/
-export type SectionIdent =
-// Keep this sorted, but with `sect` as the first entry.
- 'sect' |
- 'bksp' |
- 'disp' |
- 'elem' |
- 'keys' |
- 'layr' |
- 'list' |
- 'loca' |
- 'meta' |
- 'strs' |
- 'tran' |
- 'uset' |
- 'vars';
+export type SectionIdent = typeof SECTION_IDENTS[number];
type SectionMap = {
[id in SectionIdent]: SectionIdent;
}
+export enum KMXPlusVersion {
+ Version17 = 0x1100, // == KMXFile.VERSION_170,
+ Version19 = 0x1300, // == KMXFile.VERSION_190,
+};
+
// TODO-LDML: namespace com.keyman.core.ldml {
/**
* Constants for the KMXPlus data format
@@ -79,11 +89,27 @@ class Constants {
/**
* Length of a raw section header, in bytes
*/
- readonly length_header = 8;
+ readonly length_header_17 = 8;
+ /**
+ * Length of a raw section header, in bytes
+ */
+ readonly length_header_19 = 12;
+
+ /**
+ * Version number 17 for KMX+ file format, initial release version,
+ * corresponds to Keyman 17.0
+ */
+ readonly kmxplus_version_17: KMXPlusVersion = KMXPlusVersion.Version17;
+
+ /**
+ * Version number 19 for KMX+ file format, new SEC2 section and version
+ * header, corresponds to Keyman 19.0
+ */
+ readonly kmxplus_version_19: KMXPlusVersion = KMXPlusVersion.Version19;
/* ------------------------------------------------------------------
- * sect section
- ------------------------------------------------------------------ */
+ * sect section
+ * ------------------------------------------------------------------ */
/**
* Minimum length of the 'sect' section, not including entries
@@ -95,8 +121,8 @@ class Constants {
readonly length_sect_item = 8;
/* ------------------------------------------------------------------
- * bksp section
- ------------------------------------------------------------------ */
+ * bksp section
+ * ------------------------------------------------------------------ */
/**
* Minimum length of the 'bksp' section, not including entries
@@ -112,21 +138,165 @@ class Constants {
readonly bksp_flags_error = 0x0001;
/* ------------------------------------------------------------------
- * disp section
- ------------------------------------------------------------------ */
+ * disp section
+ * ------------------------------------------------------------------ */
/**
* Minimum length of the 'disp' section, not including entries
*/
- readonly length_disp = 16;
- /**
- * Length of each entry in the 'disp' variable part
- */
- readonly length_disp_item = 12;
+ readonly length_disp = 16;
+ /**
+ * Length of each entry in the 'disp' variable part; note size is same in v17 and v19
+ */
+ readonly length_disp_item = 12;
+
+
+ /**
+ * in disp.item.flags, mask for flag bits (v19+)
+ */
+ readonly disp_item_flags_mask_flags = 0x00000FFF;
+
+ /**
+ * in disp.item.flags, if set, then item toId value is an id. (v19+)
+ */
+ readonly disp_item_flags_is_id = 0x00000001;
+ /**
+ * in disp.item.flags, if set, then item display string is an SVG file. (v19+)
+ */
+ readonly disp_item_flags_is_svg = 0x00000002;
+ /**
+ * in disp.item.flags, if set, then draw the key as a frame key (v19+)
+ */
+ readonly disp_item_flags_is_frame_key = 0x00000004;
+ /**
+ * in disp.item.flags, if set, then draw the key as highlighted (v19+)
+ */
+ readonly disp_item_flags_is_highlighted = 0x00000008;
+ /**
+ * in disp.item.flags, if set, then draw the key as a deadkey (v19+)
+ */
+ readonly disp_item_flags_is_deadkey = 0x00000010;
+ /**
+ * in disp.item.flags, if set, then the key is blank and non-interactive (v19+)
+ */
+ readonly disp_item_flags_is_blank_key = 0x00000020;
+
+ // reserved bits 6-11 for future flags
+
+ /**
+ * in disp.item.flags, mask for hint position (v19+)
+ */
+ readonly disp_item_flags_mask_hint = 0x0000F000;
+
+ /**
+ * in disp.item.flags, right shift value for masked hint position (v19+)
+ */
+ readonly disp_item_flags_shift_hint = 12;
+
+ /** hint position for primary key cap (v19+) */
+ readonly disp_item_hint_primary = 0;
+ /** hint position for north-west (top left) hint (v19+) */
+ readonly disp_item_hint_nw = 1;
+ /** hint position for north (top) hint (v19+) */
+ readonly disp_item_hint_n = 2;
+ /** hint position for north-east (top right) hint (v19+) */
+ readonly disp_item_hint_ne = 3;
+ /** hint position for west (left) hint (v19+) */
+ readonly disp_item_hint_w = 4;
+ /** hint position for east (right) hint (v19+) */
+ readonly disp_item_hint_e = 5;
+ /** hint position for south west (bottom left) hint (v19+) */
+ readonly disp_item_hint_sw = 6;
+ /** hint position for south (bottom) hint (v19+) */
+ readonly disp_item_hint_s = 7;
+ /** hint position for south east (bottom right) hint (v19+) */
+ readonly disp_item_hint_se = 8;
+
+ /**
+ * in disp.item.flags, mask for key cap type. (v19+)
+ */
+ readonly disp_item_flags_mask_key_cap_type = 0xFFFF0000;
+
+ /**
+ * in disp.item.flags, right shift value for key cap type.
+ */
+ readonly disp_item_flags_shift_key_cap_type = 16;
+
+ // The following values match web/.../specialCharacters.ts; see
+ // developer/src/kmc-kmn/test/kmw/constants.tests.ts for more information.
+
+ /**
+ * dis2.item.flags key cap type, sync with specialCharacters.ts (v19+)
+ */
+ readonly disp_key_cap_shift = 8;
+ readonly disp_key_cap_enter = 5;
+ readonly disp_key_cap_tab = 6;
+ readonly disp_key_cap_bksp = 4;
+ readonly disp_key_cap_menu = 11;
+ readonly disp_key_cap_hide = 10;
+ readonly disp_key_cap_alt = 25;
+ readonly disp_key_cap_ctrl = 1;
+ readonly disp_key_cap_caps = 3;
+ readonly disp_key_cap_abc_upper = 16; // differentiate '*ABC*' and '*abc*'
+ readonly disp_key_cap_abc_lower = 17; // differentiate '*ABC*' and '*abc*'
+ readonly disp_key_cap_123 = 19;
+ readonly disp_key_cap_symbol = 21;
+ readonly disp_key_cap_currency = 20;
+ readonly disp_key_cap_shifted = 9;
+ readonly disp_key_cap_altgr = 2;
+ readonly disp_key_cap_tableft = 7;
+ readonly disp_key_cap_lalt = 0x56;
+ readonly disp_key_cap_ralt = 0x57;
+ readonly disp_key_cap_lctrl = 0x58;
+ readonly disp_key_cap_rctrl = 0x59;
+ readonly disp_key_cap_laltctrl = 0x60;
+ readonly disp_key_cap_raltctrl = 0x61;
+ readonly disp_key_cap_laltctrlshift = 0x62;
+ readonly disp_key_cap_raltctrlshift = 0x63;
+ readonly disp_key_cap_altshift = 0x64;
+ readonly disp_key_cap_ctrlshift = 0x65;
+ readonly disp_key_cap_altctrlshift = 0x66;
+ readonly disp_key_cap_laltshift = 0x67;
+ readonly disp_key_cap_raltshift = 0x68;
+ readonly disp_key_cap_lctrlshift = 0x69;
+ readonly disp_key_cap_rctrlshift = 0x70;
+ // Added in Keyman 14.0.
+ readonly disp_key_cap_ltrenter = 0x05; // Default alias of '*Enter*'.
+ readonly disp_key_cap_ltrbksp = 0x04; // Default alias of '*BkSp*'.
+ readonly disp_key_cap_rtlenter = 0x71;
+ readonly disp_key_cap_rtlbksp = 0x72;
+ readonly disp_key_cap_shiftlock = 0x73;
+ readonly disp_key_cap_shiftedlock = 0x74;
+ readonly disp_key_cap_zwnj = 0x75; // If this one is specified, auto-detection will kick in.
+ readonly disp_key_cap_zwnjios = 0x75; // The iOS version will be used by default, but the
+ readonly disp_key_cap_zwnjandroid = 0x76; // Android platform has its own default glyph.
+ // Added in Keyman 17.0.
+ // Reference: https://github.com/silnrsi/font-symchar/blob/v4.000/documentation/encoding.md
+ readonly disp_key_cap_zwnjgeneric = 0x79; // Generic version of ZWNJ (no override)
+ readonly disp_key_cap_sp = 0x80; // Space
+ readonly disp_key_cap_nbsp = 0x82; // No-break Space
+ readonly disp_key_cap_narnbsp = 0x83; // Narrow No-break Space
+ readonly disp_key_cap_enq = 0x84; // En Quad
+ readonly disp_key_cap_emq = 0x85; // Em Quad
+ readonly disp_key_cap_ensp = 0x86; // En Space
+ readonly disp_key_cap_emsp = 0x87; // Em Space
+ // TODO: Skipping #-per-em-space
+ readonly disp_key_cap_punctsp = 0x8c; // Punctuation Space
+ readonly disp_key_cap_thsp = 0x8d; // Thin Space
+ readonly disp_key_cap_hsp = 0x8e; // Hair Space
+ readonly disp_key_cap_zwsp = 0x81; // Zero Width Space
+ readonly disp_key_cap_zwj = 0x77; // Zero Width Joiner
+ readonly disp_key_cap_wj = 0x78; // Word Joiner
+ readonly disp_key_cap_cgj = 0x7a; // Combining Grapheme Joiner
+ readonly disp_key_cap_ltrm = 0x90; // Left-to-right Mark
+ readonly disp_key_cap_rtlm = 0x91; // Right-to-left Mark
+ readonly disp_key_cap_sh = 0xa1; // Soft Hyphen
+ readonly disp_key_cap_htab = 0xa2; // Horizontal Tabulation
+
/* ------------------------------------------------------------------
- * elem section
- ------------------------------------------------------------------ */
+ * elem section
+ * ------------------------------------------------------------------ */
/**
* Minimum length of the 'elem' section, not including entries
@@ -176,6 +346,11 @@ class Constants {
*/
readonly elem_flags_prebase = 0x00000008;
+ /**
+ * bitwise mask for bit flags in elem[elemstr][element].flags.
+ */
+ readonly elem_flags_flags_mask = 0x0000FFFF;
+
/**
* bitwise mask for order in elem[elemstr][element].flags.
*
@@ -214,7 +389,7 @@ class Constants {
/* ------------------------------------------------------------------
* finl section
- ------------------------------------------------------------------ */
+ * ------------------------------------------------------------------ */
/**
* Minimum length of the 'finl' section, not including entries
@@ -230,8 +405,8 @@ class Constants {
readonly finl_flags_error = 0x0001;
/* ------------------------------------------------------------------
- * keys section is now keys.kmap
- ------------------------------------------------------------------ */
+ * keys section is now keys.kmap
+ * ------------------------------------------------------------------ */
/**
* Constant for no modifiers
@@ -300,7 +475,7 @@ class Constants {
/* ------------------------------------------------------------------
* keys section
- ------------------------------------------------------------------ */
+ * ------------------------------------------------------------------ */
/**
* Minimum length of the 'keys' section not including variable parts
@@ -335,20 +510,46 @@ class Constants {
/* ------------------------------------------------------------------
* layr section
- ------------------------------------------------------------------ */
+ * ------------------------------------------------------------------ */
/**
* Minimum length of the 'layr' section not including variable parts
*/
readonly length_layr = 24;
/**
- * Length of each layer list in the 'layr' section variable part
+ * Length of each layer form (renamed from 'list' in v19) in the 'layr' section variable part (v17)
+ */
+ readonly length_layr_form_v17 = 16;
+ /**
+ * Length of each layer form in the 'layr' section variable part (v19+)
*/
- readonly length_layr_list = 16;
+ readonly length_layr_form_v19 = 24;
+
/**
* for the 'hardware' field indicating a touch keyboard, non-hardware
*/
- readonly layr_list_hardware_touch = 'touch';
+ readonly layr_form_hardware_touch = 'touch';
+ /**
+ * for the 'hardware' field indicating a Brazilian 103 key ABNT2 layout (iso + extra key near right shift)
+ */
+ readonly layr_form_hardware_abnt2 = 'abnt2';
+ /**
+ * for the 'hardware' field indicating a European 102 key layout (extra key near left shift)
+ */
+ readonly layr_form_hardware_iso = 'iso';
+ /**
+ * for the 'hardware' field indicating a Japanese 109 key layout
+ */
+ readonly layr_form_hardware_jis = 'jis';
+ /**
+ * for the 'hardware' field indicating a Korean KS layout
+ */
+ readonly layr_form_hardware_ks = 'ks';
+ /**
+ * for the 'hardware' field indicating a US ANSI 101 key keyboard
+ */
+ readonly layr_form_hardware_us = 'us';
+
/**
* Length of each layer entry in the 'layr' section variable part
*/
@@ -361,20 +562,28 @@ class Constants {
* Length of each key entry in the 'layr' section variable part
*/
readonly length_layr_key = 4;
+ /**
+ * in layr.form.flags, if set, then base layout key caps hints should be shown
+ */
+ readonly layr_form_flags_show_base_layout = 0x00000001;
+ /**
+ * in layr.form.flags, if set, then left/right Ctrl and Alt keys function independently
+ */
+ readonly layr_form_flags_chiral_separate = 0x00000002;
/**
- * Minimum allowed minDeviceWidth for a layer list
+ * Minimum allowed minDeviceWidth for a layer form
*/
readonly layr_min_minDeviceWidth = 1;
/**
- * Maximum allowed minDeviceWidth for a layer list
+ * Maximum allowed minDeviceWidth for a layer form
*/
readonly layr_max_minDeviceWidth = 999;
/* ------------------------------------------------------------------
* list section
- ------------------------------------------------------------------ */
+ * ------------------------------------------------------------------ */
/**
* Minimum length of the 'list' section not including variable parts
@@ -391,7 +600,7 @@ class Constants {
/* ------------------------------------------------------------------
* loca section
- ------------------------------------------------------------------ */
+ * ------------------------------------------------------------------ */
/**
* Minimum length of the 'loca' section not including variable parts
@@ -403,8 +612,8 @@ class Constants {
readonly length_loca_item = 4;
/* ------------------------------------------------------------------
- * meta section
- ------------------------------------------------------------------ */
+ * meta section
+ ------------------------------------------------------------------ */
/**
* length of the 'meta' section
@@ -416,8 +625,8 @@ class Constants {
readonly meta_settings_normalization_disabled = 1;
/* ------------------------------------------------------------------
- * strs section
- ------------------------------------------------------------------ */
+ * strs section
+ ------------------------------------------------------------------ */
/**
* Minimum length of the 'strs' section not including variable parts
@@ -429,8 +638,8 @@ class Constants {
readonly length_strs_item = 8;
/* ------------------------------------------------------------------
- * tran section
- ------------------------------------------------------------------ */
+ * tran section
+ * ------------------------------------------------------------------ */
/**
* Minimum length of the 'tran' section, not including entries
@@ -528,6 +737,10 @@ class Constants {
vars: 'vars',
};
+ // v19+: special case for 'sect' override with 'sec2'
+ readonly sectionname_sec2 = 'sec2';
+ readonly sectionid_sec2 = 0x32636573;
+
/**
* Use to convert 4-char string into hex
* @param id section id such as 'sect'
@@ -580,6 +793,17 @@ class Constants {
treatAsLatest(version: string): boolean {
return cldrTreatAsLatest.has(version);
}
+
+ /**
+ * Difference in section header size from default v17 size
+ */
+ headerSizeDelta(version: KMXPlusVersion): number {
+ if(version == KMXPlusVersion.Version17) {
+ return 0;
+ }
+
+ return 4; /* KMXPlusVersion.Version19, additional version uint32le field */
+ }
};
/** There's no data or DTD change in 45, 46, 46.1, 47 so map them all to 46 at present. */
diff --git a/core/meson.build b/core/meson.build
index 01e016ad27d..8be073c7089 100644
--- a/core/meson.build
+++ b/core/meson.build
@@ -37,10 +37,12 @@ endif
# shared with a number of subdirs
if cpp_compiler.get_id() == 'emscripten'
- wasm_exported_runtime_methods = '-sEXPORTED_RUNTIME_METHODS=[\'UTF8ToString\',\'stringToNewUTF8\']'
+ wasm_exported_runtime_methods = '-sEXPORTED_RUNTIME_METHODS=[\'UTF8ToString\',\'stringToNewUTF8\',\'wasmExports\']'
+
+ # For Google Test
+ add_global_arguments('-pthread', language: [ 'cpp', 'c' ] )
endif
-subdir('docs/internal')
subdir('include')
subdir('src')
subdir('tests')
diff --git a/core/src/context.hpp b/core/src/context.hpp
index f56d637eff1..8ca37baf635 100644
--- a/core/src/context.hpp
+++ b/core/src/context.hpp
@@ -12,9 +12,6 @@
#include
#include "keyman_core.h"
-// Forward declarations
-class json;
-
namespace km {
namespace core
{
@@ -51,9 +48,6 @@ km_core_status set_context_from_string(km_core_context *context, km_core_cu cons
} // namespace core
} // namespace km
-json & operator << (json &, km::core::context const &);
-json & operator << (json &, km_core_context_item const &);
-
struct km_core_context : public km::core::context
{
diff --git a/core/src/jsonpp.cpp b/core/src/jsonpp.cpp
deleted file mode 100644
index cfde2da976c..00000000000
--- a/core/src/jsonpp.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- Copyright: © 2011,2018 SIL International.
- Description: JSON pretty printer for dumping debug/diagnostic data structure.
- Create Date: 15 Dec 2011
- Authors: Tim Eves (TSE)
- History: 28 Sep 2018 - TSE - Imported from graphite2 project.
- - TSE - Refactored to use C++ std::ostream.
- 25 Oct 2018 - TSE - Relicensed under the MIT license for
- inclusion in the Keyman project.
-*/
-#include
-#include
-#include
-
-#include "jsonpp.hpp"
-#include "utfcodec.hpp"
-
-#if defined(_MSC_VER)
-#define FORMAT_INTMAX "%lli"
-#define FORMAT_UINTMAX "%llu"
-#else
-#define FORMAT_INTMAX "%ji"
-#define FORMAT_UINTMAX "%ju"
-#endif
-
-namespace
-{
- enum
- {
- seq = ',',
- obj='}', member=':', empty_obj='{',
- arr=']', empty_arr='['
- };
-}
-
-const std::nullptr_t json::null = nullptr;
-
-inline
-void json::context(const char current) throw()
-{
- _stream << *_context;
- indent();
- *_context = current;
-}
-
-
-void json::indent(const int d) throw()
-{
- if (*_context == member || (_flatten && _flatten < _context))
- _stream.put(' ');
- else
- _stream << std::endl << std::setw(4*int(_context - _contexts + d)) << "";
-}
-
-
-inline
-void json::push_context(const char prefix, const char suffix) throw()
-{
- assert(_context - _contexts < ptrdiff_t(sizeof _contexts));
-
- if (_context == _contexts)
- *_context = suffix;
- else
- context(suffix);
- *++_context = prefix;
-}
-
-
-void json::pop_context() throw()
-{
- assert(_context > _contexts);
-
- if (*_context == seq) indent(-1);
- else _stream.put(*_context);
-
- _stream.put(*--_context);
- if (_context == _contexts) _stream << std::endl;
- _stream.flush();
-
- if (_flatten >= _context) _flatten = 0;
- *_context = seq;
-}
-
-
-// These four functions cannot be inlined as pointers to these
-// functions are needed for operator << (_context_t) to work.
-void json::flat(json & j) throw() { if (!j._flatten) j._flatten = j._context; }
-void json::close(json & j) throw() { j.pop_context(); }
-void json::object(json & j) throw() { j.push_context('{', '}'); }
-void json::array(json & j) throw() { j.push_context('[', ']'); }
-void json::item(json & j) throw()
-{
- while (j._context > j._contexts+1 && j._context[-1] != arr)
- j.pop_context();
-}
-
-
-json & json::operator << (json::string s) throw()
-{
- const char ctxt = _context[-1] == obj ? *_context == member ? seq : member : seq;
- context(ctxt);
- _stream << '"' << s << '"';
- if (ctxt == member) _stream.put(' ');
-
- return *this;
-}
-
-json & json::operator <<(const char16_t* s) throw()
-{
- std::u16string str16(s);
- std::string str = convert(str16);
-
- (*this) << str;
- return *this;
-}
-
-json & json::operator << (json::number f) throw()
-{
- context(seq);
- if (std::numeric_limits::infinity() == f)
- _stream << "Infinity";
- else if (-std::numeric_limits::infinity() == f)
- _stream << "-Infinity";
- else if (std::numeric_limits::quiet_NaN() == f ||
- std::numeric_limits::signaling_NaN() == f)
- _stream << "NaN";
- else
- _stream << f;
- return *this;
-}
-json & json::operator << (json::integer d) throw() { context(seq); _stream << intmax_t(d); return *this; }
-json & json::operator << (json::integer_u d) throw(){ context(seq); _stream << uintmax_t(d); return *this; }
-json & json::operator << (json::boolean b) throw() { context(seq); _stream << (b ? "true" : "false"); return *this; }
-json & json::operator << (std::nullptr_t) throw() { context(seq); _stream << "null"; return *this; }
diff --git a/core/src/jsonpp.hpp b/core/src/jsonpp.hpp
deleted file mode 100644
index d97afe50a62..00000000000
--- a/core/src/jsonpp.hpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- Copyright: © 2011,2018 SIL International.
- Description: JSON pretty printer for dumping debug/diagnostic internal
- state information.
- Create Date: 15 Dec 2011
- Authors: Tim Eves (TSE)
- History: 28 Sep 2018 - TSE - Imported from graphite2 project.
- - TSE - Refactored to use C++ std::ostream.
- 25 Oct 2018 - TSE - Relicensed under the MIT license for
- inclusion in the Keyman project.
-*/
-#pragma once
-
-#include
-#include
-#include
-
-#include "utfcodec.hpp"
-
-class json
-{
- // Prevent copying
- json(const json &) = delete;
- json & operator = (const json &) = delete;
-
- typedef void (*_context_t)(json &);
-
- std::ostream& _stream;
- char _contexts[128], // context stack
- * _context, // current context (top of stack)
- * _flatten; // if !0 points to context above which
- // pretty printed output should occur.
- std::vector _env;
-
- void context(const char current) throw();
- void indent(const int d=0) throw();
- void push_context(const char, const char) throw();
- void pop_context() throw();
-
-public:
- class closer;
-
- using string = const char *;
- using number = double;
- enum class integer : std::intmax_t {};
- enum class integer_u : std::uintmax_t {};
- using boolean = bool;
- static const std::nullptr_t null;
-
- void setenv(unsigned int index, void *val) { _env.reserve(index + 1); if (index >= _env.size()) _env.insert(_env.end(), _env.size() - index + 1, 0); _env[index] = val; }
- void *getenv(unsigned int index) const { return _env[index]; }
- const std::vector &getenvs() const { return _env; }
-
- static void flat(json &) throw();
- static void close(json &) throw();
- static void object(json &) throw();
- static void array(json &) throw();
- static void item(json &) throw();
-
- json(std::ostream& stream) throw();
- ~json() throw ();
-
- auto & stream() const throw();
-
- json & operator << (const char16_t*) throw();
- json & operator << (string) throw();
- json & operator << (number) throw();
- json & operator << (integer) throw();
- json & operator << (integer_u) throw();
- json & operator << (boolean) throw();
- json & operator << (std::nullptr_t) throw();
- json & operator << (_context_t) throw();
-
- operator bool() const throw();
- bool good() const throw();
- bool eof() const throw();
-};
-
-class json::closer
-{
- // Prevent copying.
- closer(const closer &) = delete;
- closer & operator = (const closer &) = delete;
-
- json * const _j;
-public:
- closer(json * const j) : _j(j) {}
- ~closer() throw() { if (_j) *_j << close; }
-};
-
-inline
-json::json(std::ostream& s) throw()
-: _stream(s), _context(_contexts), _flatten(0)
-{
- if (_stream.good())
- _stream.flush();
-}
-
-
-inline
-json::~json() throw ()
-{
- while (_context > _contexts) pop_context();
-}
-
-inline
-auto & json::stream() const throw() { return _stream; }
-
-
-inline
-json & json::operator << (json::_context_t ctxt) throw()
-{
- ctxt(*this);
- return *this;
-}
-
-inline
-json & operator << (json & j, std::string const & s) throw() { return j << json::string(s.c_str()); }
-
-inline
-json & operator << (json & j, std::string const * s) throw() { return j << json::string(s->c_str()); }
-
-inline
-json & operator << (json & j, std::u16string const & s) throw() { return j << json::string(convert(s).c_str()); }
-
-inline
-json & operator << (json & j, std::u16string const * s) throw() { return j << json::string(convert(*s).c_str()); }
-
-inline
-json & operator << (json & j, signed char d) throw() { return j << json::integer(d); }
-
-inline
-json & operator << (json & j, unsigned char d) throw() { return j << json::integer_u(d); }
-
-inline
-json & operator << (json & j, short int d) throw() { return j << json::integer(d); }
-
-inline
-json & operator << (json & j, unsigned short int d) throw() { return j << json::integer_u(d); }
-
-inline
-json & operator << (json & j, int d) throw() { return j << json::integer(d); }
-
-inline
-json & operator << (json & j, unsigned int d) throw() { return j << json::integer_u(d); }
-
-inline
-json & operator << (json & j, long int d) throw() { return j << json::integer(d); }
-
-inline
-json & operator << (json & j, unsigned long int d) throw() { return j << json::integer_u(d); }
-
-inline
-json & operator << (json & j, long long int d) throw() { return j << json::integer(d); }
-
-inline
-json & operator << (json & j, unsigned long long int d) throw() { return j << json::integer_u(d); }
-
-inline
-json::operator bool() const throw() { return good(); }
-
-inline
-bool json::good() const throw() { return _stream.good(); }
-
-inline
-bool json::eof() const throw() { return _stream.eof(); }
diff --git a/core/src/keyboard.cpp b/core/src/keyboard.cpp
index 0f104f9422a..d3804309c8e 100644
--- a/core/src/keyboard.cpp
+++ b/core/src/keyboard.cpp
@@ -6,7 +6,6 @@
History: 7 Oct 2018 - TSE - Refactored out of km_core_keyboard_api.cpp
*/
#include "keyboard.hpp"
-#include "jsonpp.hpp"
using namespace km::core;
@@ -26,7 +25,6 @@ keyboard_attributes::keyboard_attributes(std::u16string const & kbid,
options_store const &opts)
: _keyboard_id(kbid),
_version_string(version),
- _folder_path(""),
_default_opts(opts)
{
// Ensure that the default_options array will be properly terminated.
@@ -38,7 +36,6 @@ keyboard_attributes::keyboard_attributes(std::u16string const & kbid,
keyboard_attributes::keyboard_attributes(keyboard_attributes &&rhs)
: _keyboard_id(std::move(rhs._keyboard_id)),
_version_string(std::move(rhs._version_string)),
- _folder_path(""),
_default_opts(std::move(rhs._default_opts))
{
rhs.id = rhs.version_string = nullptr;
@@ -50,14 +47,3 @@ keyboard_attributes & keyboard_attributes::operator = (keyboard_attributes &&rhs
{
return *new (this) keyboard_attributes(std::move(rhs));
}
-
-
-json & km::core::operator << (json & j, km::core::keyboard_attributes const & kb)
-{
- j << json::object
- << "id" << kb.id
- << "version" << kb.version_string
- << "rules" << json::array << json::close;
-
- return j << json::close;
-}
diff --git a/core/src/keyboard.hpp b/core/src/keyboard.hpp
index 2ca7118d870..4bb7387ea5f 100644
--- a/core/src/keyboard.hpp
+++ b/core/src/keyboard.hpp
@@ -16,9 +16,6 @@
#include "option.hpp"
#include "path.hpp"
-// Forward declarations
-class json;
-
namespace km {
namespace core
{
@@ -26,8 +23,6 @@ namespace core
{
std::u16string _keyboard_id;
std::u16string _version_string;
- // unused and deprecated
- core::path _folder_path;
std::vector