diff --git a/.github/workflows/deb-packaging.yml b/.github/workflows/deb-packaging.yml index 80161dfeda0b94cf6f2874b752f855544af792e1..8495f5577a6617e31affd811a93b27038295aadb 100644 --- a/.github/workflows/deb-packaging.yml +++ b/.github/workflows/deb-packaging.yml @@ -102,7 +102,7 @@ jobs: strategy: fail-fast: true matrix: - dist: [focal, jammy, kinetic] + dist: [focal, jammy, kinetic, lunar] arch: [amd64] runs-on: ubuntu-latest @@ -119,8 +119,8 @@ jobs: platform: "${{ matrix.arch }}" source_dir: "artifacts/keyman-srcpkg" sourcepackage: "keyman_${{ needs.sourcepackage.outputs.VERSION }}-1.dsc" - deb_fullname: $DEBFULLNAME - deb_email: $DEBEMAIL + deb_fullname: ${{env.DEBFULLNAME}} + deb_email: ${{env.DEBEMAIL}} prerelease_tag: ${{ needs.sourcepackage.outputs.PRERELEASE_TAG }} - name: Output resulting .deb files @@ -159,7 +159,7 @@ jobs: upload-to-llso: name: Upload packages to llso - needs: deb_signing + needs: [sourcepackage, deb_signing] runs-on: self-hosted environment: "deploy (linux)" if: github.event.client_payload.isTestBuild == 'false' diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 0000000000000000000000000000000000000000..1c46a510eb4508ab1bbc640d7fab211fbe5931f1 --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,5 @@ +external-sources=true + +# If you get SC1091 for sourced files, add the following setting to +# your .vscode/settings.json file: +# "shellcheck.useWorkspaceRootAsCwd": false diff --git a/HISTORY.md b/HISTORY.md index 277044675bc952a87c16364566d3bf24a22e240b..3caaef473cb21fb51cdfc3a50bb8ea11432abb51 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,130 @@ # Keyman Version History +## 17.0.120 alpha 2023-06-07 + +* chore(core): look for emcc.py, not emcc (#8934) +* chore(linux): Move some files to keyman-config (#8917) +* fix(linux): Fix title of reference page and links (#8939) +* fix(android/engine): Re-enable KMManager tests (#8940) + +## 17.0.119 alpha 2023-06-06 + +* chore(developer): ensure kmcmplib messages are all UTF-8 (#8927) +* chore(linux): Fix lintian warnings (#8931) + +## 17.0.118 alpha 2023-06-05 + +* chore(ios): replace fv cert (#8900) +* fix(core): Fix compilation if hotdoc is installed (#8912) +* feat(linux): Rename column and add tooltip (#8918) + +## 17.0.117 alpha 2023-06-04 + +* chore(developer): verify long lines compile correctly (#8915) + +## 17.0.116 alpha 2023-06-02 + +* feat(linux): Add column for installation location (#8897) +* chore(developer): verify kvks files and report errors (#8892) +* refactor(developer): rearrange kmcmplib interface source (#8899) +* refactor(developer): move filename consistency check to kmc (#8907) +* chore(developer): loadFile callback error check and optimization (#8908) +* chore(common): remove url module ref from common/web/types (#8914) + +## 17.0.115 alpha 2023-06-01 + +* refactor(developer): complete fs move out of kmcmplib (#8882) +* fix(common): tweak pack/publish support for npm 9.5.1 and node 18.16.0 (#8894) +* feat(linux): Implement Options page and option to disable Sentry error reporting (#7989) + +## 17.0.114 alpha 2023-05-31 + +* chore(developer): replace cwrap wasm bindings (#8857) +* chore(developer): refactor kmcmplib interfaces (#8870) +* chore(developer): move keyboard repo fixtures (#8874) +* chore(linux): Move build steps to build.sh (#8864) + +## 17.0.113 alpha 2023-05-26 + +* docs(android): Update documentation for building Android on Linux (#8860) +* fix(android): Fix typo in build-utils.sh (#8859) +* refactor(developer): compiler interface part 1 for wasm (#8850) +* chore(developer): remove SetError macro (#8856) + +## 17.0.112 alpha 2023-05-25 + +* refactor(developer): move file write out of WriteCompiledKeyboard and CompileKeyboard merge (#8796) + +## 17.0.111 alpha 2023-05-24 + +* fix(android/app): Pathing for publish script (#8845) +* chore(developer): add test for missing .js file to kmc-package (#8846) +* feat(linux): Add dbus system service (#8509) +* chore(linux): Add missing dependency to GHA (#8849) + +## 17.0.110 alpha 2023-05-22 + +* fix(common): ensure child builds don't rebuild dependencies unnecessarily (#8834) + +## 17.0.109 alpha 2023-05-17 + +* fix(windows): add wrap-symbols to Text Editor Makefile (#8819) +* chore(linux): Make postinst script comply with Debian policy (#8810) +* chore(windows): remove legacy core and flag ️ (#8593) + +## 17.0.108 alpha 2023-05-16 + +* feat(windows): add text editor to the support makefile (#8750) +* feat(developer): verify keyboard versions in kmc-package (#8769) +* feat(developer): verify bcp47 tags are valid and minimal in kmc-package (#8778) +* feat(developer): verify at least one language in package (#8783) +* chore(developer): verify file types of content files in package (#8792) +* chore(developer): verify that package has at least a model or keyboard (#8793) +* chore(ios): Changes required for XCode 14.3 (#8746) + +## 17.0.107 alpha 2023-05-15 + +* chore(linux): Fix installation build step on TC (#8784) +* refactor(android/engine): Consolidate updateSelection (#8739) + +## 17.0.106 alpha 2023-05-12 + +* chore(developer): move package formats to common/web/types (#8729) +* feat(developer): add package validation (#8740) +* feat(developer): add validation of package filenames (#8751) +* feat(developer): validate content file names in packages (#8755) +* feat(developer): validate package name in compiler (#8757) +* chore(developer): rename Compiler and related classes (#8726) +* feat(developer): uset api from wasm! (#8716) + +## 17.0.105 alpha 2023-05-11 + +* chore(common): Update crowdin strings for Amharic (#8748) +* chore(android/app): Cleanup build-play-store-notes script (#8749) +* chore(linux): Add build.sh for keyman-config (#8733) + +## 17.0.104 alpha 2023-05-10 + +* chore(developer): rename kmc-keyboard to kmc-ldml (#8725) +* chore(linux): bring kmp.json for unit test up to spec (#8732) +* feat(common): add `builder_run_action` shorthand function (#8742) + +## 17.0.103 alpha 2023-05-09 + +* fix(mac): `$CONFIG` not `$CONFIGURATION` in build path (#8724) +* chore(linux): Fix another problem uploading to launchpad (#8727) + +## 17.0.102 alpha 2023-05-08 + +* chore(linux): Build for Lunar on all platforms (#8713) +* fix(linux): Fix linux tests (#8510) +* chore(linux): Fix uploading to launchpad (#8720) + +## 17.0.101 alpha 2023-05-05 + +* chore(common): `set -eu` in build-utils.sh (#8698) +* fix(android/app): Resize OSK when resuming app from background (#8704) + ## 17.0.100 alpha 2023-05-04 * (#8690) diff --git a/VERSION.md b/VERSION.md index 14ccd0be84373f93ef69d7885e435a5c37503ebb..4df3b18e58966dc5f91e717d97b785ceb3c3626e 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -17.0.101 \ No newline at end of file +17.0.121 \ No newline at end of file diff --git a/android/KMAPro/build-play-store-notes.inc.sh b/android/KMAPro/build-play-store-notes.inc.sh new file mode 100755 index 0000000000000000000000000000000000000000..04fa8764179ff7e888b8065bfbbd22ae31ff28c0 --- /dev/null +++ b/android/KMAPro/build-play-store-notes.inc.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +# Assumption: parent script that sources this already has START STANDARD BUILD SCRIPT INCLUDE + +################################ Main script ################################ + +function generateReleaseNotes() { + pushd "$KEYMAN_ROOT/android/KMAPro/" + + # + # Copy release notes for Gradle Play Publisher to upload + # Reference: https://github.com/Triple-T/gradle-play-publisher#uploading-release-notes + # + local PLAY_RELEASE_NOTES="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/play/release-notes/en-US/$TIER.txt" + if [ $TIER = "stable" ]; then + PLAY_RELEASE_NOTES="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/play/release-notes/en-US/default.txt" + fi + builder_heading "Generating Play Store release notes to $PLAY_RELEASE_NOTES" + # write to file + echo "" > "$PLAY_RELEASE_NOTES" + + # Copy whatsnew.md to release notes 1 line at a time, + # filtering for lines that start with "*". + # Pad release notes if whatsnew.md doesn't have any line items + # Play Store release notes have a limit of 500 characters + local DEFAULT_RELEASE_NOTE="* Additional bug fixes and improvements" + local FILTERED_LINES=$( grep '^\s*\*.*$' "$KEYMAN_ROOT/android/help/about/whatsnew.md" || [[ $? == 1 ]] ) # Continue if grep has no matches + if [ -z "$FILTERED_LINES" ]; then + FILTERED_LINES="$DEFAULT_RELEASE_NOTE" + builder_warn "Warning: whatsnew.md empty so using default release note: '$FILTERED_LINES'" + fi + + # Change IFS to new line + local old_IFS="${IFS}" + IFS=$'\n' + for line in $FILTERED_LINES + do + local CHARS_IN_RELEASE_NOTES=$( wc -m < "$PLAY_RELEASE_NOTES" ) + local CHARS_IN_CURRENT_LINE=$( wc -m <<< $line ) + if (( CHARS_IN_RELEASE_NOTES + CHARS_IN_CURRENT_LINE + 1 < 450 )); then + # Copy line to Play Store release notes + echo "$line" >> "$PLAY_RELEASE_NOTES" + else + # 450 chars reached + builder_warn "Warning: Play Store release notes approaching 500 character limit" + echo "$DEFAULT_RELEASE_NOTE" >> "$PLAY_RELEASE_NOTES" + break + fi + done + + # Restore IFS + IFS="${old_IFS}" + popd +} diff --git a/android/KMAPro/build-play-store-notes.sh b/android/KMAPro/build-play-store-notes.sh deleted file mode 100755 index 3846ce9c42df2576a5184bed0809747723ddb902..0000000000000000000000000000000000000000 --- a/android/KMAPro/build-play-store-notes.sh +++ /dev/null @@ -1,55 +0,0 @@ -# Set sensible script defaults: -# set -e: Terminate script if a command returns an error -set -e -# set -u: Terminate script if an unset variable is used -set -u - -## START STANDARD BUILD SCRIPT INCLUDE -# adjust relative paths as necessary -THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" -## END STANDARD BUILD SCRIPT INCLUDE - -QUIET=0 - -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" - -THIS_DIR="$(dirname "$THIS_SCRIPT")" - -# -# Copy release notes for Gradle Play Publisher to upload -# Reference: https://github.com/Triple-T/gradle-play-publisher#uploading-release-notes -# -PLAY_RELEASE_NOTES="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/play/release-notes/en-US/$TIER.txt" -if [ $TIER = "stable" ]; then - PLAY_RELEASE_NOTES="$KEYMAN_ROOT/android/KMAPro/kMAPro/src/main/play/release-notes/en-US/default.txt" -fi -echo "Generating Play Store release notes to $PLAY_RELEASE_NOTES" -echo "" > "$PLAY_RELEASE_NOTES" - -# Copy whatsnew.md to release notes 1 line at a time, -# filtering for lines that start with "*". -# Pad release notes if whatsnew.md doesn't have any line items -# Play Store release notes have a limit of 500 characters -DEFAULT_RELEASE_NOTE="* Additional bug fixes and improvements" -FILTERED_LINES=$( grep '^\s*\*.*$' "$KEYMAN_ROOT/android/help/about/whatsnew.md" || [[ $? == 1 ]] ) # Continue if grep has no matches -if [ -z "$FILTERED_LINES" ]; then - FILTERED_LINES="$DEFAULT_RELEASE_NOTE" - builder_warn "Warning: whatsnew.md empty so using default release note: '$FILTERED_LINES'" -fi - -IFS=$'\n' # Change IFS to new line -for line in $FILTERED_LINES -do - CHARS_IN_RELEASE_NOTES=$( wc -m < $PLAY_RELEASE_NOTES ) - CHARS_IN_CURRENT_LINE=$( wc -m <<< $line ) - if (( CHARS_IN_RELEASE_NOTES + CHARS_IN_CURRENT_LINE + 1 < 450 )); then - # Copy line to Play Store release notes - echo "$line" >> $PLAY_RELEASE_NOTES - else - # 450 chars reached - builder_warn "Warning: Play Store release notes approaching 500 character limit" - echo "$DEFAULT_RELEASE_NOTE" >> $PLAY_RELEASE_NOTES - break - fi -done diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index e112a6360d42d353b866124cb82e0d3865752726..6b92d4c2eb5ab8641b70876097c37d2727aa6b03 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -109,6 +109,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene private final int minTextSize = 16; private final int maxTextSize = 72; private int textSize = minTextSize; + private int lastOrientation = Configuration.ORIENTATION_UNDEFINED; private static final String defaultKeyboardInstalled = "DefaultKeyboardInstalled"; private static final String defaultDictionaryInstalled = "DefaultDictionaryInstalled"; private static final String userTextKey = "UserText"; @@ -262,6 +263,14 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene super.onResume(); KMManager.onResume(); KMManager.hideSystemKeyboard(); + + // onConfigurationChanged() only triggers when device is rotated while app is in foreground + // This handles when device is rotated while app is in background + Configuration newConfig = this.getResources().getConfiguration(); + if (newConfig != null && newConfig.orientation != lastOrientation) { + lastOrientation = newConfig.orientation; + KMManager.onConfigurationChanged(newConfig); + } resizeTextView(textView.isKeyboardVisible()); KMManager.addKeyboardEventListener(this); @@ -349,6 +358,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene getSupportActionBar().setBackgroundDrawable(getActionBarDrawable(this)); resizeTextView(textView.isKeyboardVisible()); invalidateOptionsMenu(); + lastOrientation = newConfig.orientation; } @SuppressLint("RestrictedApi") diff --git a/android/KMAPro/kMAPro/src/main/res/values-am-rET/strings.xml b/android/KMAPro/kMAPro/src/main/res/values-am-rET/strings.xml index 09b4d4134007a7adecf36cef3aa7ce2eedc5c784..5071cc42698b6dbbf3b1b5d2653b5e7c6d4fcac8 100644 --- a/android/KMAPro/kMAPro/src/main/res/values-am-rET/strings.xml +++ b/android/KMAPro/kMAPro/src/main/res/values-am-rET/strings.xml @@ -1,5 +1,6 @@ + አጋራ @@ -25,6 +26,7 @@ እዚህ ጋር መጻፍ ይጀምሩ … + የጽሑፍ መጠን፦ %1$d @@ -156,5 +158,5 @@ የፊደል ገበታው አዲሱን የኪይማን ስሪት ይፈልጋል - Unable to launch web browser + ብራውሰሩን ማስጀመር አልተቻለም diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java index 08717b9f217d01ba77d3ca5ecb30bb5b363c436c..bc0aed1d04965b9b138d354e2ae26f42c277915e 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java @@ -29,6 +29,7 @@ import android.Manifest; import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Color; @@ -47,6 +48,9 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputConnection; import android.webkit.ConsoleMessage; import android.webkit.WebChromeClient; import android.webkit.WebSettings; @@ -139,7 +143,7 @@ final class KMKeyboard extends WebView { } public boolean getShouldShowHelpBubble() { - if(this._shouldShowHelpBubble == null) { + if (this._shouldShowHelpBubble == null) { SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); this._shouldShowHelpBubble = prefs.getBoolean(KMManager.KMKey_ShouldShowHelpBubble, true); } @@ -151,10 +155,54 @@ final class KMKeyboard extends WebView { this._shouldShowHelpBubble = flag; } - protected boolean shouldIgnoreTextChange() { return shouldIgnoreTextChange; } - protected void setShouldIgnoreTextChange(boolean ignore) { this.shouldIgnoreTextChange = ignore; } - protected boolean shouldIgnoreSelectionChange() { return shouldIgnoreSelectionChange; } - protected void setShouldIgnoreSelectionChange(boolean ignore) { this.shouldIgnoreSelectionChange = ignore; } + protected boolean shouldIgnoreTextChange() { + return shouldIgnoreTextChange; + } + + protected void setShouldIgnoreTextChange(boolean ignore) { + this.shouldIgnoreTextChange = ignore; + } + + protected boolean shouldIgnoreSelectionChange() { + return shouldIgnoreSelectionChange; + } + + protected void setShouldIgnoreSelectionChange(boolean ignore) { + this.shouldIgnoreSelectionChange = ignore; + } + + protected boolean updateText(String text) { + boolean result = false; + String kmText = ""; + if (text != null) { + kmText = text.toString().replace("\\", "\\u005C").replace("'", "\\u0027").replace("\n", "\\n"); + } + + if (KMManager.isKeyboardLoaded(this.keyboardType) && !shouldIgnoreTextChange) { + this.loadJavascript(KMString.format("updateKMText('%s')", kmText)); + result = true; + } + + shouldIgnoreTextChange = false; + return result; + } + + protected boolean updateSelectionRange(int selStart, int selEnd) { + boolean result = false; + InputConnection ic = KMManager.getInputConnection(this.keyboardType); + if (ic != null) { + ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0); + if (icText != null) { + updateText(icText.text.toString()); + } + } + this.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd)); + result = true; + + return result; + } + + @SuppressWarnings("deprecation") @SuppressLint("SetJavaScriptEnabled") @@ -176,8 +224,11 @@ final class KMKeyboard extends WebView { getSettings().setUseWideViewPort(true); getSettings().setLoadWithOverviewMode(true); - setWebContentsDebuggingEnabled(true); - + if (0 != (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)) { + // Enable debugging of WebView via adb. Not used during unit tests + // Refer: https://developer.chrome.com/docs/devtools/remote-debugging/webviews/#configure_webviews_for_debugging + setWebContentsDebuggingEnabled(true); + } setWebChromeClient(new WebChromeClient() { public boolean onConsoleMessage(ConsoleMessage cm) { String msg = KMString.format("KMW JS Log: Line %d, %s:%s", cm.lineNumber(), cm.sourceId(), cm.message()); diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java index b819be2dad5b9514b30d35d0daa6d1bc30e5ee8a..c01b21db2c35f9fd0de2d8c5d18f3a641ab102b3 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java @@ -624,8 +624,11 @@ public final class KMManager { return; } - RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); - keyboard.setLayoutParams(params); + if (!isTestMode()) { + // Keyboard layout not needed in unit tests. #5125 + RelativeLayout.LayoutParams params = getKeyboardLayoutParams(); + keyboard.setLayoutParams(params); + } keyboard.setVerticalScrollBarEnabled(false); keyboard.setHorizontalScrollBarEnabled(false); keyboard.setWebViewClient(webViewClient); @@ -1950,25 +1953,11 @@ public final class KMManager { public static boolean updateText(KeyboardType kbType, String text) { boolean result = false; - String kmText = ""; - if (text != null) { - kmText = text.toString().replace("\\", "\\u005C").replace("'", "\\u0027").replace("\n", "\\n"); - } if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) { - if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreTextChange()) { - InAppKeyboard.loadJavascript(KMString.format("updateKMText('%s')", kmText)); - result = true; - } - - InAppKeyboard.setShouldIgnoreTextChange(false); + return InAppKeyboard.updateText(text); } else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { - if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreTextChange()) { - SystemKeyboard.loadJavascript(KMString.format("updateKMText('%s')", kmText)); - result = true; - } - - SystemKeyboard.setShouldIgnoreTextChange(false); + return SystemKeyboard.updateText(text); } return result; @@ -1978,23 +1967,13 @@ public final class KMManager { boolean result = false; if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) { if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_INAPP) && !InAppKeyboard.shouldIgnoreSelectionChange()) { - InAppKeyboard.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd)); - result = true; + result = InAppKeyboard.updateSelectionRange(selStart, selEnd); } InAppKeyboard.setShouldIgnoreSelectionChange(false); } else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) { if (isKeyboardLoaded(KeyboardType.KEYBOARD_TYPE_SYSTEM) && !SystemKeyboard.shouldIgnoreSelectionChange()) { - InputConnection ic = getInputConnection(KeyboardType.KEYBOARD_TYPE_SYSTEM); - if (ic != null) { - ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0); - if (icText != null) { - updateText(kbType, icText.text.toString()); - } - } - - SystemKeyboard.loadJavascript(KMString.format("updateKMSelectionRange(%d,%d)", selStart, selEnd)); - result = true; + result = SystemKeyboard.updateSelectionRange(selStart, selEnd); } SystemKeyboard.setShouldIgnoreSelectionChange(false); diff --git a/android/KMEA/app/src/main/res/values-am-rET/strings.xml b/android/KMEA/app/src/main/res/values-am-rET/strings.xml index dc9e7711add173453a6ffa4f061b698d50420e63..1bb95d51576187d5626c1791145dedbdc8b0b9ef 100644 --- a/android/KMEA/app/src/main/res/values-am-rET/strings.xml +++ b/android/KMEA/app/src/main/res/values-am-rET/strings.xml @@ -168,5 +168,5 @@ የፊደል ገበታውን ለመቀየር እዚህ ጋር ንካ - Unable to launch web browser + ብራውሰሩን ማስጀመር አልተቻለም diff --git a/android/KMEA/app/src/test/java/com/keyman/engine/KMManagerTest.java b/android/KMEA/app/src/test/java/com/keyman/engine/KMManagerTest.java index 97515b719086d625bfd00ea213b3231cc77f2c1c..59a0237b377f44a0c226af1c11f69627c8eb66ae 100644 --- a/android/KMEA/app/src/test/java/com/keyman/engine/KMManagerTest.java +++ b/android/KMEA/app/src/test/java/com/keyman/engine/KMManagerTest.java @@ -29,13 +29,14 @@ public class KMManagerTest { private static final String OLD_KEYBOARDS_LIST = "old_keyboards_list.dat"; ArrayList> dat_list; - @Before - public void loadOldKeyboardsList() throws FileNotFoundException { + // For some keyboard list tests, load an existing keyboard list. + // Can't use @Before because context is null before running tests. + public void loadOldKeyboardsList() { KMManager.initialize(ApplicationProvider.getApplicationContext(), KMManager.KeyboardType.KEYBOARD_TYPE_INAPP); File keyboards_dat = new File(TEST_RESOURCE_ROOT, OLD_KEYBOARDS_LIST); if (keyboards_dat == null || !keyboards_dat.exists()) { - throw new FileNotFoundException(); + Assert.fail(TAG + ": keyboards list not found"); } try { ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(keyboards_dat)); @@ -46,7 +47,6 @@ public class KMManagerTest { } } - @Ignore("Investigate ResourcesNotFoundException") @Test public void test_getTier() { String versionName = "14.0.248-alpha"; @@ -109,9 +109,9 @@ public class KMManagerTest { /* * This test is manually run to edit/regenerate the old keyboards list */ - @Ignore @Test public void create_newKeyboardsList() { + loadOldKeyboardsList(); dat_list = new ArrayList>(); HashMap usInfo = new HashMap(); @@ -181,9 +181,9 @@ public class KMManagerTest { } } - @Ignore("Investigate ResourcesNotFoundException") @Test public void test_updateOldKeyboardsList() { + loadOldKeyboardsList(); Assert.assertNotNull(dat_list); // Verify old keyboards list contains deprecated keyboards diff --git a/android/README.md b/android/README.md index 2c776873a396f948e8df1a16c58d27042f0e5b20..9b138ccfceae6e50d134894959c4ddc57a17a925 100644 --- a/android/README.md +++ b/android/README.md @@ -1,33 +1,48 @@ # Keyman for Android & Keyman Engine for Android ## Prerequisites -See [build configuration](../docs/build/index.md) for details on how to configure your build environment. + +See [build configuration](../docs/build/index.md) for details on how to configure +your build environment. ## Keyman Minimum Android Requirements + Keyman for Android has a minSdkVersion of 21 for [Android 5.0 Lollipop](https://developer.android.com/about/versions/lollipop) ## Keyman for Android Development -Keyman for Android (formerly named KMAPro) can be built from a command line (preferred) or Android Studio. -Building Keyman Web is a precursor for compiling KMEA, so verify your system has all the [Minimum Web Compilation Requirements](../web/README.md#minimum-web-compilation-requirements) +Keyman for Android (formerly named KMAPro) can be built from a command line +(preferred) or Android Studio. + +Building Keyman Web is a precursor for compiling KMEA, so verify your system has +all the [Minimum Web Compilation Requirements](../web/README.md#minimum-web-compilation-requirements) + +When attempting to build via Android Studio the first time, you'll also need to +accept the SDK licenses. Depending on your OS, you may need to run a command +line similar to: -When attempting to build via Android Studio the first time, you'll also need to accept the SDK licenses. Depending on your OS, you may need to run a command line similar to: ```bash yes | ~/Library/Android/sdk/tools/bin/sdkmanager --licenses ``` ### Crash Reporting -Keyman for Android uses [Sentry](https://sentry.io) for crash reporting at a server https://sentry.keyman.com. The analytics for Debug are associated with an App Bundle ID `com.tavultesoft.kmapro.debug`. - + +Keyman for Android uses [Sentry](https://sentry.io) for crash reporting. The +analytics for Debug are associated with an App Bundle ID +`com.tavultesoft.kmapro.debug`. + ### Compiling From Command Line + 1. Launch a command prompt and cd to the directory **keyman/android** 2. Run the top level build script `./build.sh configure build --debug` which will: * Compile KMEA (and its KMW dependency) * Download default keyboard and dictionary resources as needed * Compile KMAPro -3. The APK will be found in **keyman/android/KMAPro/kMAPro/build/outputs/apk/debug/keyman-${version}.apk** where `${version}` is the current version number. +3. The APK will be found in **keyman/android/KMAPro/kMAPro/build/outputs/apk/debug/keyman-\${version}.apk** + where `${version}` is the current version number. ### Compiling From Android Studio + 1. Ensure that [Keyman Engine for Android](#how-to-build-keyman-engine-for-android) is built. 2. Launch Android Studio and import the Gradle project **keyman/android/KMAPro/build.gradle** 3. From the project view, *configure* anything that Gradle reports. @@ -37,24 +52,31 @@ Keyman for Android uses [Sentry](https://sentry.io) for crash reporting at a ser 3. Change Module to kMAPro 4. Name your run configuration kMAPro (or as desired) 5. Run your new configuration: Select Run --> Run 'kMAPro' -6. Select a physical device or create a new virtual device to match your target API version - For Ubuntu 18.04, you will need to add your user to the `kvm` group for permission accessing the emulator. -``` +6. Select a physical device or create a new virtual device to match your target + API version. + + For Ubuntu 18.04, you will need to add your user to the `kvm` group + for permission accessing the emulator. + + ```bash sudo apt install qemu-kvm sudo adduser kvm -``` + ``` ### Running From Command Line + 1. Launch a command prompt 2. Ensure that a physical device is connected or an emulator is running. 1. To list physical devices: `$ANDROID_HOME/platform-tools/adb.exe devices` 2. To list created emulator devices: `$ANDROID_HOME/tools/emulator.exe -list-avds` - 3. [Create a new virtual device](https://developer.android.com/studio/run/managing-avds.html) if there are no - existing devices. + 3. [Create a new virtual device](https://developer.android.com/studio/run/managing-avds.html) + if there are no existing devices. 4. Start an emulator: `$ANDROID_HOME/tools/emulator.exe @nameofemulator` 3. Load the APK: `$ANDROID_HOME/platform-tools/adb.exe install -r path/to/apk.apk` - * If multiple devices are connected you may need `$ANDROID_HOME/platform-tools/adb.exe install -r -s SERIAL path/to/apk.apk`. Replace `SERIAL` with - the device serial number listed in step 2. + + * If multiple devices are connected you may need + `$ANDROID_HOME/platform-tools/adb.exe install -r -s SERIAL path/to/apk.apk`. + Replace `SERIAL` with the device serial number listed in step 2. ### Compiling the app's offline help Keyman for Android help is maintained in the Markdown files in android/help/. diff --git a/android/build-publish.sh b/android/build-publish.sh index 7e52edad9278ca6f4b00500d370057a7aef70ca9..29c152c993d5ae28a2463876413f19354dbe0a0a 100755 --- a/android/build-publish.sh +++ b/android/build-publish.sh @@ -2,11 +2,6 @@ # CI script to publish specified app APKs to the Play Store. # The APKs should already have been built from a separate script -# Set sensible script defaults: -# set -e: Terminate script if a command returns an error -set -e -# set -u: Terminate script if an unset variable is used -set -u # set -x: Debugging use, print each statement # set -x @@ -16,6 +11,8 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" . "${THIS_SCRIPT%/*}/../resources/build/build-utils.sh" ## END STANDARD BUILD SCRIPT INCLUDE +. "$KEYMAN_ROOT/android/KMAPro/build-play-store-notes.inc.sh" + echo Publishing APKs to Play Store # @@ -77,10 +74,9 @@ echo "BUILD_FLAGS $BUILD_FLAGS" # Publish Keyman for Android if [ "$DO_KMAPRO" = true ]; then - cd "$KEYMAN_ROOT/android/KMAPro/" # Copy Release Notes - ./build-play-store-notes.sh - + generateReleaseNotes + cd "$KEYMAN_ROOT/android/KMAPro/" ./gradlew $DAEMON_FLAG $BUILD_FLAGS fi diff --git a/common/predictive-text/unit_tests/test.sh b/common/predictive-text/unit_tests/test.sh index a0554b6325a714d31ab908cb6b9d8bd7c0481dba..f1b49635f62081e6b61958dad39cfebed4ed809e 100755 --- a/common/predictive-text/unit_tests/test.sh +++ b/common/predictive-text/unit_tests/test.sh @@ -58,7 +58,7 @@ if builder_start_action test:libraries; then pushd "$KEYMAN_ROOT/common/models/templates" echo - echo "### Running $builder_term common/models/templates) tests" + echo "### Running $(builder_term common/models/templates) tests" if builder_has_option --ci; then npm run test -- -reporter mocha-teamcity-reporter else @@ -68,7 +68,7 @@ if builder_start_action test:libraries; then pushd "$KEYMAN_ROOT/common/models/types" echo - echo "### Running $builder_term common/models/types) tests" + echo "### Running $(builder_term common/models/types) tests" # Is not mocha-based; it's TSC-based instead, as we're just ensuring that the .d.ts # file is a proper TS declaration file. npm run test @@ -100,7 +100,7 @@ if [[ $VERSION_ENVIRONMENT == test ]] && builder_has_action test :browser; then if builder_pull_get_details; then if ! ([[ $builder_pull_title =~ \(web\) ]] || builder_pull_has_label test-browserstack); then - echo "Auto-skipping $builder_term test:browser) for unrelated CI test build" + echo "Auto-skipping $(builder_term test:browser) for unrelated CI test build" exit 0 fi fi @@ -145,4 +145,4 @@ if builder_start_action test:browser; then npm run karma -- start $KARMA_INFO_LEVEL $KARMA_FLAGS $BROWSERS unit_tests/in_browser/$KARMA_CONFIG builder_finish_action success test:browser -fi \ No newline at end of file +fi diff --git a/common/test/keyboards/.gitignore b/common/test/keyboards/.gitignore index 577a20b276dd4532aabb0ad0156b7b2635c07665..5dda6d16d8af96edd60087dabcc4a8d1140de415 100644 --- a/common/test/keyboards/.gitignore +++ b/common/test/keyboards/.gitignore @@ -1,3 +1,4 @@ +baseline/kmp.json **/build/ */*_source.zip index.html diff --git a/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kmx b/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kmx new file mode 100644 index 0000000000000000000000000000000000000000..563a139c19f7c6168fb49117503263b82ac5b616 Binary files /dev/null and b/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kmx differ diff --git a/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kvk b/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kvk new file mode 100644 index 0000000000000000000000000000000000000000..79190ea1a00669de6aa6580f99793b43f565020a Binary files /dev/null and b/common/test/keyboards/caps_lock_layer_3620/binary/caps_lock_layer_3620.kvk differ diff --git a/common/web/types/src/kpj/kpj-file-reader.ts b/common/web/types/src/kpj/kpj-file-reader.ts index 5f768c54f1ea159dccd318e1eb997be1d1d5e5c6..21e92c975b2313a3dd0f1508cbd663ef20654188 100644 --- a/common/web/types/src/kpj/kpj-file-reader.ts +++ b/common/web/types/src/kpj/kpj-file-reader.ts @@ -27,8 +27,8 @@ export class KPJFileReader { return data as KPJFile; } - public validate(source: KPJFile, schemaBuffer: Buffer): void { - const schema = JSON.parse(schemaBuffer.toString('utf8')); + public validate(source: KPJFile, schemaBuffer: Uint8Array): void { + const schema = JSON.parse(new TextDecoder().decode(schemaBuffer)); const ajv = new Ajv(); if(!ajv.validate(schema, source)) { throw new Error(ajv.errorsText()); diff --git a/common/web/types/src/kvk/kvks-file-reader.ts b/common/web/types/src/kvk/kvks-file-reader.ts index 1a4c5a8b056646a608b1868125d6d0e4a1d76663..13c6c8716c11c1b6a02e98f8b63fc18ed12cb6d0 100644 --- a/common/web/types/src/kvk/kvks-file-reader.ts +++ b/common/web/types/src/kvk/kvks-file-reader.ts @@ -7,12 +7,6 @@ import { VisualKeyboard, VisualKeyboardHeaderFlags, VisualKeyboardKey, VisualKey import { USVirtualKeyCodes } from '../consts/virtual-key-constants.js'; import { BUILDER_KVK_HEADER_VERSION } from './kvk-file.js'; -export enum KVKSParseErrorType { invalidVkey }; -export class KVKSParseError extends Error { - public type: KVKSParseErrorType; - public vkey: string; -}; - export default class KVKSFileReader { public read(file: Uint8Array): KVKSourceFile { let source: KVKSourceFile; @@ -33,12 +27,21 @@ export default class KVKSFileReader { // rather than using the version tagged on npmjs.com. }); - parser.parseString(file, (e: unknown, r: unknown) => { source = r as KVKSourceFile }); - source = this.boxArrays(source); - this.cleanupUnderscore('visualkeyboard', source.visualkeyboard); + parser.parseString(file, (e: unknown, r: unknown) => { if(e) { throw e }; source = r as KVKSourceFile }); + if(source) { + source = this.boxArrays(source); + this.cleanupFlags(source); + this.cleanupUnderscore('visualkeyboard', source.visualkeyboard); + } return source; } + private cleanupFlags(source: any) { + if(source.visualkeyboard?.header?.flags === '') { + source.visualkeyboard.header.flags = {}; + } + } + /** * The only element that allows spaces is . Remove * all other empty whitespace-only values. @@ -65,15 +68,15 @@ export default class KVKSFileReader { } } - public validate(source: KVKSourceFile, schemaBuffer: Buffer): void { - const schema = JSON.parse(schemaBuffer.toString('utf8')); + public validate(source: KVKSourceFile, schemaBuffer: Uint8Array): void { + const schema = JSON.parse(new TextDecoder().decode(schemaBuffer)); const ajv = new Ajv(); if(!ajv.validate(schema, source)) { throw new Error(ajv.errorsText()); } } - public transform(source: KVKSourceFile, errors?: KVKSParseError[]): VisualKeyboard { + public transform(source: KVKSourceFile, invalidVkeys?: string[]): VisualKeyboard { // NOTE: at this point, the xml should have been validated // and matched the schema result so we can assume properties exist let result: VisualKeyboard = { @@ -111,11 +114,8 @@ export default class KVKSFileReader { for(let sourceKey of layer.key) { let vkey = (USVirtualKeyCodes as any)[sourceKey.$?.vkey]; if(!vkey) { - if(errors) { - let e = new KVKSParseError(); - e.type = KVKSParseErrorType.invalidVkey; - e.vkey = sourceKey.$?.vkey; - errors.push(e); + if(typeof invalidVkeys !== 'undefined') { + invalidVkeys.push(sourceKey.$?.vkey); } continue; } diff --git a/common/web/types/src/kvk/kvks-file-writer.ts b/common/web/types/src/kvk/kvks-file-writer.ts index 613c6caa5108289ac9c99901ddc00a68383313dd..1b3a819f580f2d2ef23f1582aade95c98a6b97a7 100644 --- a/common/web/types/src/kvk/kvks-file-writer.ts +++ b/common/web/types/src/kvk/kvks-file-writer.ts @@ -96,7 +96,6 @@ export default class KVKSFileWriter { l.key.push(k); } - // console.dir(kvks, {depth:8}); let result = builder.buildObject(kvks); return result; //Uint8Array.from(result); } diff --git a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts index 35d7f627956417b508e9b358473ce019147afc80..fa74126462451b8a0dd4c70a30532f7f82cc6c53 100644 --- a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts +++ b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml-reader.ts @@ -14,16 +14,20 @@ interface NameAndProps { '$$'?: any; // children }; -export default class LDMLKeyboardXMLSourceFileReader { - callbacks: CompilerCallbacks; +export class LDMLKeyboardXMLSourceFileReaderOptions { + importsPath: string; +}; + +export class LDMLKeyboardXMLSourceFileReader { + constructor(private options: LDMLKeyboardXMLSourceFileReaderOptions, private callbacks : CompilerCallbacks) { + } - constructor(callbacks : CompilerCallbacks) { - this.callbacks = callbacks; + static get defaultImportsURL() { + return new URL(`../import/`, import.meta.url); } - readImportFile(version: string, subpath: string): Buffer { - // TODO-LDML: sanitize input string - let importPath = new URL(`../import/${version}/${subpath}`, import.meta.url); + readImportFile(version: string, subpath: string): Uint8Array { + const importPath = this.callbacks.resolveFilename(this.options.importsPath, `${version}/${subpath}`); return this.callbacks.loadFile(importPath); } @@ -201,8 +205,8 @@ export default class LDMLKeyboardXMLSourceFileReader { /** * @returns true if valid, false if invalid */ - public validate(source: LDMLKeyboardXMLSourceFile | LDMLKeyboardTestDataXMLSourceFile, schemaSource: Buffer): boolean { - const schema = JSON.parse(schemaSource.toString('utf8')); + public validate(source: LDMLKeyboardXMLSourceFile | LDMLKeyboardTestDataXMLSourceFile, schemaSource: Uint8Array): boolean { + const schema = JSON.parse(new TextDecoder().decode(schemaSource)); const ajv = new Ajv(); if(!ajv.validate(schema, source)) { for (let err of ajv.errors) { diff --git a/common/web/types/src/main.ts b/common/web/types/src/main.ts index ffe682ba6ee8441408a2d0783a758854fefe723f..9e65ccb99a1606f3a0b6aa990e06df901c0f32db 100644 --- a/common/web/types/src/main.ts +++ b/common/web/types/src/main.ts @@ -14,11 +14,11 @@ export * as KvksFile from './kvk/kvk-file.js'; export * as LDMLKeyboard from './ldml-keyboard/ldml-keyboard-xml.js'; export { LDMLKeyboardTestDataXMLSourceFile } from './ldml-keyboard/ldml-keyboard-testdata-xml.js'; -export { default as LDMLKeyboardXMLSourceFileReader } from './ldml-keyboard/ldml-keyboard-xml-reader.js'; +export { LDMLKeyboardXMLSourceFileReader, LDMLKeyboardXMLSourceFileReaderOptions } from './ldml-keyboard/ldml-keyboard-xml-reader.js'; export * as Constants from './consts/virtual-key-constants.js'; -export { CompilerCallbacks, CompilerSchema, CompilerEvent, CompilerErrorNamespace, CompilerErrorSeverity, CompilerPathCallbacks, CompilerFileSystemCallbacks, CompilerMessageSpec, compilerErrorSeverity, compilerErrorSeverityName } from './util/compiler-interfaces.js'; +export { CompilerCallbacks, CompilerSchema, CompilerEvent, CompilerErrorNamespace, CompilerErrorSeverity, CompilerPathCallbacks, CompilerFileSystemCallbacks, CompilerMessageSpec, compilerErrorSeverityName, compilerExceptionToString, compilerErrorFormatCode } from './util/compiler-interfaces.js'; export { CommonTypesMessages } from './util/common-events.js'; export * as TouchLayout from './keyman-touch-layout/keyman-touch-layout-file.js'; @@ -29,4 +29,8 @@ export * as KPJ from './kpj/kpj-file.js'; export { KPJFileReader } from './kpj/kpj-file-reader.js'; export { KeymanDeveloperProject } from './kpj/keyman-developer-project.js'; -export * as util from './util/util.js'; \ No newline at end of file +export * as KpsFile from './package/kps-file.js'; +export * as KmpJsonFile from './package/kmp-json-file.js'; + +export * as util from './util/util.js'; + diff --git a/developer/src/kmc-package/src/kmp-json-file.ts b/common/web/types/src/package/kmp-json-file.ts similarity index 100% rename from developer/src/kmc-package/src/kmp-json-file.ts rename to common/web/types/src/package/kmp-json-file.ts diff --git a/developer/src/kmc-package/src/kps-file.ts b/common/web/types/src/package/kps-file.ts similarity index 100% rename from developer/src/kmc-package/src/kps-file.ts rename to common/web/types/src/package/kps-file.ts diff --git a/common/web/types/src/util/compiler-interfaces.ts b/common/web/types/src/util/compiler-interfaces.ts index a15690099ea52a30529fa89c82a483f391588e69..8de586685ad62d219c1a59c2e12324b3ead2a8ad 100644 --- a/common/web/types/src/util/compiler-interfaces.ts +++ b/common/web/types/src/util/compiler-interfaces.ts @@ -35,17 +35,29 @@ export function compilerErrorSeverityName(code: number): string { } } +/** + * Format the error code number + * example: "FATAL:0x03004" + */ +export function compilerErrorFormatCode(code: number): string { + const severity = code & CompilerErrorSeverity.Severity_Mask; + const severityName = compilerErrorSeverityName(severity); + const errorCode = code & CompilerErrorSeverity.Error_Mask; + const errorCodeString = Number(errorCode).toString(16).padStart(5,'0'); + return `${severityName}:0x${errorCodeString}`; +} + /** * Defines the error code ranges for various compilers. Once defined, these * ranges must not be changed as external modules may depend on specific error * codes. Individual errors are defined at a compiler level, for example, - * kmn-keyboard/src/compiler/messages.ts. + * kmc-ldml/src/compiler/messages.ts. */ export enum CompilerErrorNamespace { /** - * kmc-keyboard errors between 0x0000…0x0FFF + * kmc-ldml errors between 0x0000…0x0FFF */ - KeyboardCompiler = 0x0000, + LdmlKeyboardCompiler = 0x0000, /** * common/web/types errors between 0x1000…0x1FFF */ @@ -100,6 +112,7 @@ export interface CompilerFileSystemCallbacks { readFileSync(path: string, options?: { encoding?: null; flag?: string; } | null): Uint8Array; readFileSync(path: string, options: { encoding: string; flag?: string; } | string): string; readFileSync(path: string, options?: { encoding?: string | null; flag?: string; } | string | null): string | Uint8Array; + writeFileSync(path: string, data: Uint8Array): void; existsSync(name: string): boolean; } @@ -110,14 +123,11 @@ export interface CompilerFileSystemCallbacks { export interface CompilerCallbacks { /** * Attempt to load a file. Return falsy if not found. - * TODO: accept only string * TODO: never return falsy, just throw if not found? - * TODO: Buffer is Node-only. - * TODO: rename to readFile, consolidate with fs.readFileSync? * @param baseFilename * @param filename */ - loadFile(filename: string | URL): Buffer; + loadFile(filename: string): Uint8Array; get path(): CompilerPathCallbacks; get fs(): CompilerFileSystemCallbacks; @@ -129,7 +139,7 @@ export interface CompilerCallbacks { */ resolveFilename(baseFilename: string, filename: string): string; - loadSchema(schema: CompilerSchema): Buffer; + loadSchema(schema: CompilerSchema): Uint8Array; reportMessage(event: CompilerEvent): void; debug(msg: string): void; }; @@ -141,3 +151,10 @@ export interface CompilerCallbacks { * @returns */ export const CompilerMessageSpec = (code: number, message: string) : CompilerEvent => { return { code, message } }; + +/** + * @param e Error-like + */ +export function compilerExceptionToString(e?: any) : string { + return `${(e ?? 'unknown error').toString()}\n\nCall stack:\n${(e instanceof Error ? e.stack : (new Error()).stack)}`; +} diff --git a/common/web/types/test/helpers/TestCompilerCallbacks.ts b/common/web/types/test/helpers/TestCompilerCallbacks.ts index 596a731d58880a1fbf4d7dab95a81c795bf212e1..8316264a774bf8b85e4adeb94c48937935247fdf 100644 --- a/common/web/types/test/helpers/TestCompilerCallbacks.ts +++ b/common/web/types/test/helpers/TestCompilerCallbacks.ts @@ -42,7 +42,7 @@ export class TestCompilerCallbacks implements CompilerCallbacks { return resolveFilename(baseFilename, filename); } - loadFile(filename: string | URL): Buffer { + loadFile(filename: string): Uint8Array { // TODO: error management, does it belong here? try { return loadFile(filename); diff --git a/common/web/types/test/helpers/index.ts b/common/web/types/test/helpers/index.ts index 58dc32dc9c1618da13acb027d39ad3a379a53394..06906df148589fb5a10808b92e685888d5fd8a7b 100644 --- a/common/web/types/test/helpers/index.ts +++ b/common/web/types/test/helpers/index.ts @@ -28,7 +28,10 @@ export function loadSchema(schema: CompilerSchema): Buffer { } export function resolveFilename(baseFilename: string, filename: string) { - const basePath = path.dirname(baseFilename); + const basePath = + baseFilename.endsWith('/') || baseFilename.endsWith('\\') ? + baseFilename : + path.dirname(baseFilename); // Transform separators to platform separators -- we are agnostic // in our use here but path prefers files may use // either / or \, although older kps files were always \. diff --git a/common/web/types/test/helpers/reader-callback-test.ts b/common/web/types/test/helpers/reader-callback-test.ts index 8c85a759acf270a8a99e5eb947faf8b51db8bb93..204e0ec5bf4875168e082630354ff2453b756def 100644 --- a/common/web/types/test/helpers/reader-callback-test.ts +++ b/common/web/types/test/helpers/reader-callback-test.ts @@ -1,11 +1,16 @@ import 'mocha'; import {assert} from 'chai'; import { loadFile, makePathToFixture, loadSchema } from '../helpers/index.js'; -import LDMLKeyboardXMLSourceFileReader from '../../src/ldml-keyboard/ldml-keyboard-xml-reader.js'; +import { LDMLKeyboardXMLSourceFileReader, LDMLKeyboardXMLSourceFileReaderOptions } from '../../src/ldml-keyboard/ldml-keyboard-xml-reader.js'; import { CompilerEvent } from '../../src/util/compiler-interfaces.js'; import { LDMLKeyboardXMLSourceFile } from '../../src/ldml-keyboard/ldml-keyboard-xml.js'; import { LDMLKeyboardTestDataXMLSourceFile } from '../ldml-keyboard/ldml-keyboard-testdata-xml.js'; import { TestCompilerCallbacks } from './TestCompilerCallbacks.js'; +import { fileURLToPath } from 'url'; + +const readerOptions: LDMLKeyboardXMLSourceFileReaderOptions = { + importsPath: fileURLToPath(LDMLKeyboardXMLSourceFileReader.defaultImportsURL) +}; export interface CompilationCase { /** @@ -71,7 +76,7 @@ export interface TestDataCase { export function testReaderCases(cases : CompilationCase[]) { // we need our own callbacks rather than using the global so messages don't get mixed const callbacks = new TestCompilerCallbacks(); - const reader = new LDMLKeyboardXMLSourceFileReader(callbacks); + const reader = new LDMLKeyboardXMLSourceFileReader(readerOptions, callbacks); for (let testcase of cases) { const expectFailure = testcase.throws || !!(testcase.errors); // if true, we expect this to fail const testHeading = expectFailure ? `should fail to load: ${testcase.subpath}`: @@ -122,7 +127,7 @@ export function testReaderCases(cases : CompilationCase[]) { export function testTestdataReaderCases(cases : TestDataCase[]) { // we need our own callbacks rather than using the global so messages don't get mixed const callbacks = new TestCompilerCallbacks(); - const reader = new LDMLKeyboardXMLSourceFileReader(callbacks); + const reader = new LDMLKeyboardXMLSourceFileReader(readerOptions, callbacks); for (let testcase of cases) { const expectFailure = testcase.throws || !!(testcase.errors); // if true, we expect this to fail const testHeading = expectFailure ? `should fail to load: ${testcase.subpath}`: diff --git a/common/web/types/test/kpj/test-kpj-file-reader.ts b/common/web/types/test/kpj/test-kpj-file-reader.ts index b7c2f710131db0d9bdf8be3f9190a267ee4a2ad5..95b5f1d01ae86030139e1ff594eed17fb5796713 100644 --- a/common/web/types/test/kpj/test-kpj-file-reader.ts +++ b/common/web/types/test/kpj/test-kpj-file-reader.ts @@ -15,7 +15,6 @@ describe('kpj-file-reader', function () { const input = fs.readFileSync(path); const reader = new KPJFileReader(callbacks); const kpj = reader.read(input); - console.dir(kpj); assert.doesNotThrow(() => { reader.validate(kpj, loadSchema('kpj')); }); @@ -79,7 +78,6 @@ describe('kpj-file-reader', function () { assert.lengthOf(project.files, 2); let f: KeymanDeveloperProjectFile10 = project.files[0]; - console.dir(f); assert.equal(f.id, 'id_f347675c33d2e6b1c705c787fad4941a'); assert.equal(f.filename, 'khmer_angkor.kmn'); assert.equal(f.filePath, 'source/khmer_angkor.kmn'); diff --git a/common/web/types/test/kvk/test-kvk-round-trip.ts b/common/web/types/test/kvk/test-kvk-round-trip.ts index 41dbe2dbc0cb25b553c790172998f006269c416b..031ad60b9e6e1c2fda395c2dda7caf8614794a90 100644 --- a/common/web/types/test/kvk/test-kvk-round-trip.ts +++ b/common/web/types/test/kvk/test-kvk-round-trip.ts @@ -5,7 +5,7 @@ import Hexy from 'hexy'; import gitDiff from 'git-diff'; const { hexy } = Hexy; import { loadSchema, makePathToFixture } from '../helpers/index.js'; -import KvksFileReader, { KVKSParseError } from "../../src/kvk/kvks-file-reader.js"; +import KvksFileReader from "../../src/kvk/kvks-file-reader.js"; import KvkFileReader from "../../src/kvk/kvk-file-reader.js"; import KvkFileWriter from "../../src/kvk/kvk-file-writer.js"; import KvksFileWriter from "../../src/kvk/kvks-file-writer.js"; @@ -53,9 +53,9 @@ describe('kvks-file-reader', function () { assert.doesNotThrow(() => { reader.validate(kvks, loadSchema('kvks')); }); - const errors: KVKSParseError[] = []; - const vk = reader.transform(kvks, errors); - assert.isEmpty(errors); + const invalidVkeys: string[] = []; + const vk = reader.transform(kvks, invalidVkeys); + assert.isEmpty(invalidVkeys); const writer = new KvkFileWriter(); const output = writer.write(vk); assertBufferMatch(Buffer.from(output), compiled); @@ -76,9 +76,9 @@ describe('kvks-file-reader', function () { assert.doesNotThrow(() => { kvksReader.validate(kvks, loadSchema('kvks')); }); - const errors: KVKSParseError[] = []; - const vk2 = kvksReader.transform(kvks, errors); - assert.isEmpty(errors); + const invalidVkeys: string[] = []; + const vk2 = kvksReader.transform(kvks, invalidVkeys); + assert.isEmpty(invalidVkeys); // make sure the binary is the same assert.deepEqual(vk2, vk); diff --git a/common/web/types/test/kvk/test-kvks-file.ts b/common/web/types/test/kvk/test-kvks-file.ts index 523bfaf54d618ce21da9f9137c67d9b04c39bd6a..4cbb299a0085b188d405d5a46aa23e964ad1a344 100644 --- a/common/web/types/test/kvk/test-kvks-file.ts +++ b/common/web/types/test/kvk/test-kvks-file.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import 'mocha'; import { loadSchema, makePathToFixture } from '../helpers/index.js'; -import KvksFileReader, { KVKSParseError } from "../../src/kvk/kvks-file-reader.js"; +import KvksFileReader from "../../src/kvk/kvks-file-reader.js"; import KvksFileWriter from "../../src/kvk/kvks-file-writer.js"; import { verify_khmer_angkor } from './test-kvk-utils.js'; import { assert } from 'chai'; @@ -16,9 +16,9 @@ describe('kvks-file-reader', function() { assert.doesNotThrow(() => { reader.validate(kvks, loadSchema('kvks')); }); - const errors: KVKSParseError[] = []; - const vk = reader.transform(kvks, errors); - assert.isEmpty(errors); + const invalidVkeys: string[] = []; + const vk = reader.transform(kvks, invalidVkeys); + assert.isEmpty(invalidVkeys); verify_khmer_angkor(vk); }); }); @@ -30,9 +30,9 @@ describe('kvks-file-writer', function() { const reader = new KvksFileReader(); const kvksExpected = reader.read(input); - const errors: KVKSParseError[] = []; - const vk = reader.transform(kvksExpected, errors); - assert.isEmpty(errors); + const invalidVkeys: string[] = []; + const vk = reader.transform(kvksExpected, invalidVkeys); + assert.isEmpty(invalidVkeys); const writer = new KvksFileWriter(); const output = writer.write(vk); diff --git a/common/windows/cpp/include/legacy_kmx_file.h b/common/windows/cpp/include/legacy_kmx_file.h index 4aca74ed3d11f2e618d90a54610f5ebc4c149e88..dbc81328c872db78b9aa703516bcd06eba751a80 100644 --- a/common/windows/cpp/include/legacy_kmx_file.h +++ b/common/windows/cpp/include/legacy_kmx_file.h @@ -409,7 +409,6 @@ typedef COMP_GROUP *PCOMP_GROUP; typedef struct _COMPILER_OPTIONS { DWORD dwSize; BOOL ShouldAddCompilerVersion; - BOOL UseKmcmpLib; } COMPILER_OPTIONS; typedef COMPILER_OPTIONS *PCOMPILER_OPTIONS; diff --git a/common/windows/cpp/include/registry.h b/common/windows/cpp/include/registry.h index 4e2fe9ae35ba8bfe0e22e3597e83841e0ad32d6f..895f9e83becbbd3297c3375c5dcec036fd41d4b7 100644 --- a/common/windows/cpp/include/registry.h +++ b/common/windows/cpp/include/registry.h @@ -129,9 +129,6 @@ #define REGSZ_Flag_UseCachedHotkeyModifierState "Flag_UseCachedHotkeyModifierState" -/* REGSZ_Flag_UseKeymanCore DWORD: Turns on the common core - instead of windows core */ -#define REGSZ_Flag_UseKeymanCore "Flag_UseKeymanCore" - /* DWORD: Enable/disable deep TSF integration, default enabled; 0 = disabled, 1 = enabled, 2 = default */ #define REGSZ_DeepTSFIntegration "deep tsf integration" diff --git a/core/commands.inc.sh b/core/commands.inc.sh index 2e5fb9e7b9ffda235b6d665e250d27e69fc760f1..6916b61a9d97f887e7e7aeab62926a46c3daccd2 100644 --- a/core/commands.inc.sh +++ b/core/commands.inc.sh @@ -1,4 +1,7 @@ -#!/usr/bin/env bash +# shellcheck shell=bash +# no hashbang for .inc.sh + +. "$KEYMAN_ROOT/resources/locate_emscripten.inc.sh" # ---------------------------------------------------------------------------- # clean @@ -114,34 +117,6 @@ do_uninstall() { # utility functions # ---------------------------------------------------------------------------- -# -# We don't want to rely on emcc being on the path, because Emscripten puts far -# too many things onto the path (in particular for us, node). -# -# The following comment suggests that we don't need emcc on the path. -# https://github.com/emscripten-core/emscripten/issues/4848#issuecomment-1097357775 -# -# So we try and locate emcc in common locations ourselves. The search pattern -# is: -# -# 1. Look for $EMSCRIPTEN_BASE (our primary emscripten variable), which should -# point to the folder that emcc is located in -# 2. Look for $EMCC which should point to the emcc executable -# 3. Look for emcc on the path -# -locate_emscripten() { - if [[ -z ${EMSCRIPTEN_BASE+x} ]]; then - if [[ -z ${EMCC+x} ]]; then - local EMCC=`which emcc` - [[ -z $EMCC ]] && builder_die "locate_emscripten: Could not locate emscripten (emcc) on the path or with \$EMCC or \$EMSCRIPTEN_BASE" - fi - [[ -x $EMCC ]] || builder_die "locate_emscripten: Variable EMCC ($EMCC) does not point to a valid executable emcc" - EMSCRIPTEN_BASE="$(dirname "$EMCC")" - fi - - [[ -x ${EMSCRIPTEN_BASE}/emcc ]] || builder_die "locate_emscripten: Variable EMSCRIPTEN_BASE ($EMSCRIPTEN_BASE) does not point to emcc's folder" -} - build_meson_cross_file_for_wasm() { if [ $BUILDER_OS == win ]; then local R=$(cygpath -w $(echo $EMSCRIPTEN_BASE) | sed 's_\\_\\\\_g') @@ -159,6 +134,8 @@ build_meson_cross_file_for_wasm() { # This link.exe is not a linker. # You may need to reorder entries to your %PATH% variable to resolve this. # +# TODO: is this still required with meson 1.0? +# cleanup_visual_studio_path() { local _split_path _new_path="" diff --git a/core/doc/hotdoc.json b/core/doc/hotdoc.json index 61a0028fb0bd464c8e01ff40a8d4b22a32b41d51..4ece50d0f52c505abb5a21b46c5da2e8199d9227 100644 --- a/core/doc/hotdoc.json +++ b/core/doc/hotdoc.json @@ -1,10 +1,14 @@ { "project_name": "@project_name@", "project_version": "@project_version@", - "sitemap": "../../doc/sitemap.txt", - "index": "../../doc/markdown_files/index.md", + "sitemap": "@doc_dir@/sitemap.txt", + "index": "@doc_dir@/markdown_files/index.md", "c_sources": [ - "../include/keyboardprocessor.h" + "@include_dir@/keyman/keyboardprocessor.h" + ], + "c_include_directories": [ + "@include_dir@", + "@doc_dir@/../../common/include" ], "c_smart_index" : true, "output": ".", diff --git a/core/doc/meson.build b/core/doc/meson.build index 2772819ce7ff8b54cea5774e59f13cbea7802a00..01fcb5c98ad60250fb7e3ac3561c5ac5d9791a22 100644 --- a/core/doc/meson.build +++ b/core/doc/meson.build @@ -12,18 +12,20 @@ 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/keyboardprocessor.h.in', + '../include/keyman/keyboardprocessor.h', '../src/jsonpp.hpp', '../src/utfcodec.hpp' ) docs = custom_target('docs', output: ['html'], - input: ['sitemap.txt', 'markdown_files/index.md'], + input: ['sitemap.txt', 'markdown_files/index.md', deps], command: [hotdoc, '--verbose', '--conf-file=doc/hotdoc.json', '--output=doc', 'run'], depend_files: deps, install: true, diff --git a/core/include/keyman/keyboardprocessor.h b/core/include/keyman/keyboardprocessor.h index 3513cdede2e88223e47ada46547975b3d3bc856a..429a8ae6cbf9118850a12460db2a289c262920f1 100644 --- a/core/include/keyman/keyboardprocessor.h +++ b/core/include/keyman/keyboardprocessor.h @@ -779,7 +779,7 @@ km_kbp_keyboard_get_key_list(km_kbp_keyboard const *keyboard, km_kbp_keyboard_key **out); -/* +/** ``` ### `km_kbp_keyboard_key_list_dispose` ##### Description: @@ -794,28 +794,41 @@ returned by `km_kbp_keyboard_get_key_list`. KMN_API void km_kbp_keyboard_key_list_dispose(km_kbp_keyboard_key *key_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. + * km_kbp_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. */ KMN_API -km_kbp_status km_kbp_keyboard_get_imx_list(km_kbp_keyboard const *keyboard, km_kbp_keyboard_imx** imx_list); +km_kbp_status km_kbp_keyboard_get_imx_list(km_kbp_keyboard const *keyboard, km_kbp_keyboard_imx **imx_list); /** + * km_kbp_keyboard_imx_list_dispose: + * * Disposes of the IMX list + * + * Returns: -- */ KMN_API void km_kbp_keyboard_imx_list_dispose(km_kbp_keyboard_imx *imx_list); /** + * km_kbp_state_imx_register_callback: + * * Register the IMX callback endpoint for the client. + * + * Returns: -- */ KMN_API void km_kbp_state_imx_register_callback(km_kbp_state *state, km_kbp_keyboard_imx_platform imx_callback, void *callback_object); /** + * km_kbp_state_imx_deregister_callback: + * * De-register IMX callback endpoint for the client. + * + * Returns: -- */ KMN_API void km_kbp_state_imx_deregister_callback(km_kbp_state *state); @@ -1042,6 +1055,8 @@ enum km_kbp_tech_value { }; /** + * km_kbp_event_flags: + * * Bit flags to be used with the event_flags parameter of km_kbp_process_event */ enum km_kbp_event_flags { @@ -1162,10 +1177,8 @@ km_kbp_event( ); enum km_kbp_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. - */ + // 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_KBP_EVENT_KEYBOARD_ACTIVATED = 1, //future: KM_KBP_EVENT_KEYBOARD_DEACTIVATED = 2, }; diff --git a/core/src/ldml/C7532_ldml_updating.md b/core/src/ldml/C7532_ldml_updating.md index 62f30ad4df8465cf946d66eb66fc54302696aba4..168d205cea3ff959ee5a8f36e3dbceb5182c1d79 100644 --- a/core/src/ldml/C7532_ldml_updating.md +++ b/core/src/ldml/C7532_ldml_updating.md @@ -46,11 +46,11 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma ## In-memory data: Phase 2 - first the compiler tests - - add a section in `developer/src/kmc-keyboard/test/fixtures/sections` if needed - - add a test case such as `developer/src/kmc-keyboard/test/test-key2.ts` + - add a section in `developer/src/kmc-ldml/test/fixtures/sections` if needed + - add a test case such as `developer/src/kmc-ldml/test/test-key2.ts` - add a compiler - - `developer/src/kmc-keyboard/compiler/key2.ts` - - link it in to `developer/src/kmc-keyboard/src/compiler/compiler.ts` + - `developer/src/kmc-ldml/compiler/key2.ts` + - link it in to `developer/src/kmc-ldml/src/compiler/compiler.ts` - add the import - add to `SECTION_COMPILERS` @@ -68,10 +68,10 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma - Also note that restructure will happily ignore (write zeros for) any fields where the BUILDER_* fields don't match the COMP_PLUS_* fields. - update basic.xml and basic.txt - - Tweak `eveloper/src/kmc-keyboard/test/fixtures/basic.xml` as needed - - You can use `developer/src/kmc-keyboard/build.sh build-fixtures` which will generate these. The two .kmx files are supposed to match: if not, fix `basic.txt` or fix other bugs. - - `developer/src/kmc-keyboard/build/test/fixtures/basic-txt.kmx` - KMX generated from basic.txt. - - `developer/src/kmc-keyboard/build/test/fixtures/basic-xml.kmx` - KMX generated from basic.xml. - - `developer/src/kmc-keyboard/build/test/fixtures/basic-xml.kvk` - KVK generated from basic.xml. + - Tweak `eveloper/src/kmc-ldml/test/fixtures/basic.xml` as needed + - You can use `developer/src/kmc-ldml/build.sh build-fixtures` which will generate these. The two .kmx files are supposed to match: if not, fix `basic.txt` or fix other bugs. + - `developer/src/kmc-ldml/build/test/fixtures/basic-txt.kmx` - KMX generated from basic.txt. + - `developer/src/kmc-ldml/build/test/fixtures/basic-xml.kmx` - KMX generated from basic.xml. + - `developer/src/kmc-ldml/build/test/fixtures/basic-xml.kvk` - KVK generated from basic.xml. ## more to come diff --git a/developer/build.sh b/developer/build.sh index c30d35fa467a8609ca2c1843b08c8d332c8a0a61..3e74837c287046ee78d2950a2e57062f7bc8af04 100755 --- a/developer/build.sh +++ b/developer/build.sh @@ -28,9 +28,10 @@ builder_describe \ build \ test \ ":kmcmplib=src/kmcmplib Compiler - .kmn compiler" \ - ":kmc-keyboard=src/kmc-keyboard Compiler - LDML Keyboard Module" \ + ":kmc-analyze=src/kmc-analyze Compiler - Analysis Tools" \ ":kmc-kmn=src/kmc-kmn Compiler - .kmn wrapper Keyboard Module" \ ":kmc-kmw=src/kmc-kmw Compiler - .kmn Javascript Keyboard Module" \ + ":kmc-ldml=src/kmc-ldml Compiler - LDML Keyboard Module" \ ":kmc-model=src/kmc-model Compiler - Lexical Model Module" \ ":kmc-model-info=src/kmc-model-info Compiler - .model_info Module" \ ":kmc-package=src/kmc-package Compiler - Package Module" \ diff --git a/developer/src/Makefile b/developer/src/Makefile index c1ab53cce037f3cf9f48a8a8a0e523c24b034ea1..d4c6bd9384ab4264dade66f3a08bbd779abc27c0 100644 --- a/developer/src/Makefile +++ b/developer/src/Makefile @@ -69,12 +69,12 @@ server: .virtual cd $(DEVELOPER_ROOT)\src\server $(MAKE) $(TARGET) -kmc: kmc-keyboard kmc-kmn kmc-model kmc-model-info kmc-package +kmc: kmc-ldml kmc-kmn kmc-model kmc-model-info kmc-package cd $(DEVELOPER_ROOT)\src\kmc $(MAKE) $(TARGET) -kmc-keyboard: .virtual - cd $(DEVELOPER_ROOT)\src\kmc-keyboard +kmc-ldml: .virtual + cd $(DEVELOPER_ROOT)\src\kmc-ldml $(MAKE) $(TARGET) kmc-kmn: .virtual diff --git a/developer/src/README.md b/developer/src/README.md index e4de6c13284b2672cd454995e92ff3fa6543a6a4..95b33c36f44be4364cb3c71f9cdf5e74f1773f10 100644 --- a/developer/src/README.md +++ b/developer/src/README.md @@ -70,7 +70,7 @@ details. node-based next generation compiler, hosts kmc, kmlmi, kmlmc, kmlmp -### src/kmc-keyboard - LDML Keyboard Compiler +### src/kmc-ldml - LDML Keyboard Compiler Next Generation keyboard compiler package - LDML keyboards only at present. Command line access through kmc. diff --git a/developer/src/common/delphi/compiler/compile.pas b/developer/src/common/delphi/compiler/compile.pas index 04470239c567d55d281ec130b2dbe0994ccb39e9..aeaf3cf8df80ee3cd7a19bce00d7055216e2f92b 100644 --- a/developer/src/common/delphi/compiler/compile.pas +++ b/developer/src/common/delphi/compiler/compile.pas @@ -126,6 +126,8 @@ type dpDeadKeyArray: PFILE_DEADKEY; // temp - dead key array cxVKDictionary: DWORD; // I3438 dpVKDictionary: PFILE_VKDICTIONARY; // I3438 // temp - virtual key dictionary + + extra: Pointer; end; PFILE_KEYBOARD = ^FILE_KEYBOARD; @@ -153,13 +155,13 @@ const const // kcframe --sizeof returns these values {$IFDEF WIN64} - FILE_KEYBOARD_SIZE = 2984; + FILE_KEYBOARD_SIZE = 2992; FILE_GROUP_SIZE = 200; FILE_STORE_SIZE = 200; FILE_KEY_SIZE = 32; FILE_DEADKEY_SIZE = 160; {$ELSE} - FILE_KEYBOARD_SIZE = 2952; + FILE_KEYBOARD_SIZE = 2956; FILE_GROUP_SIZE = 188; FILE_STORE_SIZE = 192; FILE_KEY_SIZE = 20; @@ -176,7 +178,6 @@ type TCompilerOptions = record dwSize: DWORD; ShouldAddCompilerVersion: BOOL; - UseKmcmpLib: BOOL; end; COMPILER_OPTIONS = TCompilerOptions; diff --git a/developer/src/common/include/kmn_compiler_errors.h b/developer/src/common/include/kmn_compiler_errors.h index 6cfae03fc9ea00c559fe53f1b55ddb0e5759b66a..31c5faf0c7b33a806b43ce14d013714ac07f4e54 100644 --- a/developer/src/common/include/kmn_compiler_errors.h +++ b/developer/src/common/include/kmn_compiler_errors.h @@ -224,8 +224,8 @@ #define CWARN_KeyShouldIncludeNCaps 0x000020AD #define CHINT_UnreachableRule 0x000010AE -#define CHINT_FilenameHasDifferingCase 0x000010AF -#define CWARN_MissingFile 0x000020B0 +#define CHINT_FilenameHasDifferingCase 0x000010AF // only used in kmcmpdll +#define CWARN_MissingFile 0x000020B0 // only used in kmcmpdll #define CERR_BufferOverflow 0x000080C0 #define CERR_Break 0x000080C1 diff --git a/developer/src/common/web/test-helpers/index.ts b/developer/src/common/web/test-helpers/index.ts index 3416a16c876f5770059c86f2caa70b3895dcc337..a134aa67dfd40551100ede0ac74d57ac2ea0a075 100644 --- a/developer/src/common/web/test-helpers/index.ts +++ b/developer/src/common/web/test-helpers/index.ts @@ -3,9 +3,9 @@ import * as path from 'path'; import { CompilerEvent, CompilerCallbacks, CompilerSchema, CompilerPathCallbacks, CompilerFileSystemCallbacks, compilerErrorSeverityName } from '@keymanapp/common-types'; export { verifyCompilerMessagesObject } from './verifyCompilerMessagesObject.js'; -// TODO: schemas are only used by kmc-keyboard for now, so this works at this +// TODO: schemas are only used by kmc-ldml for now, so this works at this // time, but it's a little fragile if we need them elsewhere in the future -const SCHEMA_BASE = '../../../../kmc-keyboard/build/src/'; +const SCHEMA_BASE = '../../../../kmc-ldml/build/src/'; /** * A CompilerCallbacks implementation for testing @@ -36,7 +36,7 @@ export class TestCompilerCallbacks implements CompilerCallbacks { /* CompilerCallbacks */ - loadFile(filename: string | URL): Buffer { + loadFile(filename: string): Uint8Array { try { return fs.readFileSync(filename); } catch(e) { @@ -57,7 +57,10 @@ export class TestCompilerCallbacks implements CompilerCallbacks { } resolveFilename(baseFilename: string, filename: string): string { - const basePath = path.dirname(baseFilename); + const basePath = + baseFilename.endsWith('/') || baseFilename.endsWith('\\') ? + baseFilename : + path.dirname(baseFilename); // Transform separators to platform separators -- we are agnostic // in our use here but path prefers files may use // either / or \, although older kps files were always \. @@ -77,7 +80,7 @@ export class TestCompilerCallbacks implements CompilerCallbacks { this.messages.push(event); } - loadSchema(schema: CompilerSchema): Buffer { + loadSchema(schema: CompilerSchema): Uint8Array { return fs.readFileSync(new URL(SCHEMA_BASE + schema + '.schema.json', import.meta.url)); } diff --git a/developer/src/common/web/test-helpers/verifyCompilerMessagesObject.ts b/developer/src/common/web/test-helpers/verifyCompilerMessagesObject.ts index 7c52f403a271a85af4b3097f6e518495a76d0662..223ab430f3319d58b294be7695c2dacafc4bae5a 100644 --- a/developer/src/common/web/test-helpers/verifyCompilerMessagesObject.ts +++ b/developer/src/common/web/test-helpers/verifyCompilerMessagesObject.ts @@ -19,13 +19,13 @@ import {assert, expect} from 'chai'; const toTitleCase = (s: string) => s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase(); export function verifyCompilerMessagesObject(source: Record) { - let keys = Object.keys(source); + const keys = Object.keys(source); const m = source as Record; - let codes: number[] = []; + const codes: number[] = []; - for(let key of keys) { + for(const key of keys) { // Verify each object member matches the pattern we expect @@ -36,7 +36,7 @@ export function verifyCompilerMessagesObject(source: Record) { const c = o[1].toUpperCase() + '_' + o[2]; expect(m[c]).to.be.a('number', `Expected constant name ${c} to exist`); - let v = m[key]('','','','','','','','','','','','' /* ignore arguments*/); + const v = m[key]('','','','','','','','','','','','' /* ignore arguments*/); expect(v.code).to.equal(m[c], `Function ${key} returns the wrong code`); } else if(typeof m[key] == 'number') { @@ -63,4 +63,4 @@ export function verifyCompilerMessagesObject(source: Record) { codes.push(code); } } -} \ No newline at end of file +} diff --git a/developer/src/kmc-keyboard/src/compiler/compiler-options.ts b/developer/src/kmc-keyboard/src/compiler/compiler-options.ts deleted file mode 100644 index d36322513bf1d5057cc5285875ce97c5728e4ef6..0000000000000000000000000000000000000000 --- a/developer/src/kmc-keyboard/src/compiler/compiler-options.ts +++ /dev/null @@ -1,13 +0,0 @@ - - -export default interface CompilerOptions { - /** - * Add debug information to the .kmx file when compiling - */ - debug?: boolean; - - /** - * Add metadata about the compiler version to .kmx file when compiling - */ - addCompilerVersion?: boolean; -}; diff --git a/developer/src/kmc-keyboard/src/main.ts b/developer/src/kmc-keyboard/src/main.ts deleted file mode 100644 index 3bf1c37f1c476e79fab366b0fac0ff592f2e34bb..0000000000000000000000000000000000000000 --- a/developer/src/kmc-keyboard/src/main.ts +++ /dev/null @@ -1,10 +0,0 @@ - -export { default as Compiler } from './compiler/compiler.js'; -export { KeymanWebCompiler } from './compiler/keymanweb-compiler.js'; -export { default as VisualKeyboardCompiler } from './compiler/visual-keyboard-compiler.js'; -export { TouchLayoutCompiler } from './compiler/touch-layout-compiler.js'; -export { default as CompilerOptions } from './compiler/compiler-options.js'; -export { CompilerMessages } from './compiler/messages.js'; -export { default as KMXPlusMetadataCompiler } from './compiler/metadata-compiler.js'; - -export { KMXBuilder } from "@keymanapp/common-types"; diff --git a/developer/src/kmc-kmn/build.sh b/developer/src/kmc-kmn/build.sh index 8c57d71e984920db23b54dd6b18a0a07b17d52f1..a69dec7646607d8ae3d79176e0fe019d08637031 100755 --- a/developer/src/kmc-kmn/build.sh +++ b/developer/src/kmc-kmn/build.sh @@ -51,11 +51,15 @@ fi #------------------------------------------------------------------------------------------------------------------- -if builder_start_action build; then +function copy_deps() { mkdir -p build/src/import/kmcmplib mkdir -p src/import/kmcmplib cp ../kmcmplib/build/wasm/$BUILDER_CONFIGURATION/src/wasm-host.js ./src/import/kmcmplib/wasm-host.js cp ../kmcmplib/build/wasm/$BUILDER_CONFIGURATION/src/wasm-host.wasm ./build/src/import/kmcmplib/wasm-host.wasm +} + +if builder_start_action build; then + copy_deps tsc --build builder_finish_action success build fi @@ -63,6 +67,7 @@ fi #------------------------------------------------------------------------------------------------------------------- if builder_start_action test; then + copy_deps tsc --build test/ npm run lint c8 --reporter=lcov --reporter=text mocha "${builder_extra_params[@]}" diff --git a/developer/src/kmc-kmn/src/compiler/compiler.ts b/developer/src/kmc-kmn/src/compiler/compiler.ts index 283d227487f21ec258a7769ab723f8b4bb7b17c6..5e910ff45b836466a0085656051d44aa19f79a24 100644 --- a/developer/src/kmc-kmn/src/compiler/compiler.ts +++ b/developer/src/kmc-kmn/src/compiler/compiler.ts @@ -1,31 +1,26 @@ /* TODO: implement additional interfaces: - extern "C" bool kmcmp_CompileKeyboardFileToBuffer( - char* pszInfile, - void* pfkBuffer, - bool ACompilerWarningsAsErrors, - bool AWarnDeprecatedCode, - kmcmp_CompilerMessageProc pMsgproc, - void* AmsgprocContext, - int Target - ); - - typedef bool (*kmcmp_ValidateJsonMessageProc)(int64_t offset, const char* szText, void* context); - - extern "C" bool kmcmp_ValidateJsonFile( - std::fstream& f, - std::fstream& fd, - kmcmp_ValidateJsonMessageProc MessageProc, - void* context - ); + typedef bool (*kmcmp_ValidateJsonMessageProc)(); + extern "C" bool kmcmp_ValidateJsonFile(); */ // TODO: rename wasm-host? -import { CompilerCallbacks } from '@keymanapp/common-types'; +import { CompilerCallbacks, CompilerEvent, KvkFileWriter, KvksFileReader } from '@keymanapp/common-types'; import loadWasmHost from '../import/kmcmplib/wasm-host.js'; import { CompilerMessages, mapErrorFromKmcmplib } from './messages.js'; +export interface CompilerResultFile { + filename: string; + data: Uint8Array; +}; + +export interface CompilerResult { + kmx?: CompilerResultFile; + kvk?: CompilerResultFile; + js?: CompilerResultFile; +}; + export interface CompilerOptions { shouldAddCompilerVersion?: boolean; saveDebug?: boolean; @@ -49,42 +44,66 @@ const baseOptions: CompilerOptions = { */ let callbackProcIdentifier = 0; -export class Compiler { - wasmModule: any; - compileKeyboardFile: any; - setCompilerOptions: any; - callbackName: string; +const + callbackPrefix = 'kmnCompilerCallbacks_'; + +export class KmnCompiler { + private Module: any; + callbackID: string; // a unique numeric id added to globals with prefixed names callbacks: CompilerCallbacks; constructor() { - this.callbackName = 'kmnCompilerCallback' + callbackProcIdentifier; + this.callbackID = callbackPrefix + callbackProcIdentifier.toString(); callbackProcIdentifier++; } - public async init(): Promise { - if(!this.wasmModule) { - this.wasmModule = await loadWasmHost(); - this.compileKeyboardFile = this.wasmModule.cwrap('kmcmp_Wasm_CompileKeyboardFile', 'boolean', ['string', 'string', - 'number', 'number', 'number', 'string', 'number']); - this.setCompilerOptions = this.wasmModule.cwrap('kmcmp_Wasm_SetCompilerOptions', 'boolean', ['number']); + public async init(callbacks: CompilerCallbacks): Promise { + this.callbacks = callbacks; + if(!this.Module) { + try { + this.Module = await loadWasmHost(); + } catch(e: any) { + /* c8 ignore next 3 */ + this.callbacks.reportMessage(CompilerMessages.Fatal_MissingWasmModule({e})); + return false; + } } - return this.compileKeyboardFile !== undefined && this.setCompilerOptions !== undefined; - } - public run(infile: string, outfile: string, callbacks: CompilerCallbacks, options?: CompilerOptions): boolean { - this.callbacks = callbacks; + return this.verifyInitialized(); + } - if(!this.wasmModule) { - this.callbacks.reportMessage(CompilerMessages.Fatal_MissingWasmModule()); + /** + * Verify that wasm is spun up OK. + * @returns true if OK + */ + public verifyInitialized() : boolean { + if(!this.callbacks) { + // Can't report a message here. + throw Error('Must call Compiler.init(callbacks) before proceeding'); + } + if(!this.Module) { + /* c8 ignore next 4 */ + // fail if wasm not loaded or function not found + this.callbacks.reportMessage(CompilerMessages.Fatal_MissingWasmModule({})); return false; } + return true; + } - options = {...baseOptions, ...options}; - (globalThis as any)[this.callbackName] = this.compilerMessageCallback; - // TODO: use callbacks for file access -- so kmc-kmn is entirely fs agnostic + public run(infile: string, outfile: string, options?: CompilerOptions): boolean { let result = this.runCompiler(infile, outfile, options); - delete (globalThis as any)[this.callbackName]; - return result; + if(result) { + if(result.kmx) { + this.callbacks.fs.writeFileSync(result.kmx.filename, result.kmx.data); + } + if(result.kvk) { + this.callbacks.fs.writeFileSync(result.kvk.filename, result.kvk.data); + } + if(result.js) { + this.callbacks.fs.writeFileSync(result.js.filename, result.js.data); + } + } + return !!result; } private compilerMessageCallback = (line: number, code: number, msg: string): number => { @@ -92,26 +111,202 @@ export class Compiler { return 1; } - private runCompiler(infile: string, outfile: string, options: CompilerOptions): boolean { - const CKF_KEYMAN = 0; - // const CKF_KEYMANWEB = 1; // not used -- legacy compiler only - const CKF_KEYMANWEB_MODERN = 2; + private cachedFile: {filename: string; data: Uint8Array} = { + filename: null, + data: null + }; + + private loadFileCallback = (filename: string, baseFilename: string, buffer: number, bufferSize: number): number => { + let resolvedFilename = this.callbacks.resolveFilename(baseFilename, filename); + let data: Uint8Array; + if(this.cachedFile.filename == resolvedFilename) { + data = this.cachedFile.data; + } + else { + data = this.callbacks.loadFile(resolvedFilename); + if(!data) { + return -1; + } + this.cachedFile.filename = resolvedFilename; + this.cachedFile.data = data; + } + + if(buffer == 0) { + /* We need to return buffer size required */ + return data.byteLength; + } + + if(bufferSize != data.byteLength) { + /* c8 ignore next 2 */ + throw new Error(`loadFileCallback: second call, expected file size ${bufferSize} == ${data.byteLength}`); + } + + this.Module.HEAP8.set(data, buffer); + + return 1; + } + + public runCompiler(infile: string, outfile: string, options: CompilerOptions): CompilerResult { + if(!this.verifyInitialized()) { + /* c8 ignore next 2 */ + return null; + } + + options = {...baseOptions, ...options}; + + (globalThis as any)[this.callbackID] = { + message: this.compilerMessageCallback, + loadFile: this.loadFileCallback + }; + let result: CompilerResult = {}; + let wasm_interface = new this.Module.CompilerInterface(); + let wasm_options = new this.Module.CompilerOptions(); + let wasm_result = null; try { - if(!this.setCompilerOptions(options.shouldAddCompilerVersion)) { - this.callbacks.reportMessage(CompilerMessages.Fatal_UnableToSetCompilerOptions()); + wasm_options.saveDebug = options.saveDebug; + wasm_options.compilerWarningsAsErrors = options.compilerWarningsAsErrors; + wasm_options.warnDeprecatedCode = options.warnDeprecatedCode; + wasm_options.shouldAddCompilerVersion = options.shouldAddCompilerVersion; + wasm_options.target = 0; //CKF_KEYMAN; TODO, support KMW + wasm_interface.callbacksKey = this.callbackID; // key of object on globalThis + wasm_result = this.Module.kmcmp_compile(infile, wasm_options, wasm_interface); + if(!wasm_result.result) { + return null; + } + + if(wasm_result.kvksFilename) { + result.kvk = this.runKvkCompiler(wasm_result.kvksFilename, infile, outfile); + if(!result.kvk) { + return null; + } } - return this.compileKeyboardFile( - infile, - outfile, - options.saveDebug ? 1 : 0, - options.compilerWarningsAsErrors ? 1 : 0, - options.warnDeprecatedCode ? 1 : 0, - this.callbackName, - options.target == 'js' ? CKF_KEYMANWEB_MODERN : CKF_KEYMAN); + + result.kmx = { + filename: outfile, + data: new Uint8Array(this.Module.HEAP8.buffer, wasm_result.kmx, wasm_result.kmxSize) + }; + + return result; } catch(e) { + /* c8 ignore next 3 */ this.callbacks.reportMessage(CompilerMessages.Fatal_UnexpectedException({e:e})); - return false; + return null; + } finally { + if(wasm_result) { + wasm_result.delete(); + } + wasm_interface.delete(); + wasm_options.delete(); + delete (globalThis as any)[this.callbackID]; + } + } + + private runKvkCompiler(kvksFilename: string, kmnFilename: string, kmxFilename: string) { + // The compiler detected a .kvks file, which needs to be captured + let reader = new KvksFileReader(); + kvksFilename = this.callbacks.resolveFilename(kmnFilename, kvksFilename); + let filename = this.callbacks.path.basename(kvksFilename); + let kvks = null; + try { + kvks = reader.read(this.callbacks.loadFile(kvksFilename)); + reader.validate(kvks, this.callbacks.loadSchema('kvks')); + } catch(e) { + this.callbacks.reportMessage(CompilerMessages.Error_InvalidKvksFile({filename, e})); + return null; + } + let invalidVkeys: string[] = []; + let vk = reader.transform(kvks, invalidVkeys); + for(let invalidVkey of invalidVkeys) { + this.callbacks.reportMessage(CompilerMessages.Warn_InvalidVkeyInKvksFile({filename, invalidVkey})); + } + let writer = new KvkFileWriter(); + return { + filename: this.callbacks.path.join(this.callbacks.path.dirname(kmxFilename), this.callbacks.path.basename(kvksFilename, '.kvks') + '.kvk'), + data: writer.write(vk) + }; + } + + /** + * + * @param pattern UnicodeSet pattern such as `[a-z]` + * @param bufferSize guess as to the buffer size + * @returns UnicodeSet accessor object, or null on failure + */ + public parseUnicodeSet(pattern: string, bufferSize: number) : UnicodeSet | null { + if(!this.verifyInitialized()) { + /* c8 ignore next 2 */ + return null; + } + + if (!bufferSize) { + /* c8 ignore next 2 */ + bufferSize = 100; // TODO-LDML: Preflight mode? Reuse buffer? + } + const buf = this.Module.asm.malloc(bufferSize * 2 * this.Module.HEAPU32.BYTES_PER_ELEMENT); + // TODO-LDML: Catch OOM + const rc = this.Module.kmcmp_parseUnicodeSet(pattern, buf, bufferSize); + if (rc >= 0) { + const ranges = []; + const startu = (buf / this.Module.HEAPU32.BYTES_PER_ELEMENT); + for (let i = 0; i < rc; i++) { + const low = this.Module.HEAPU32[startu + (i * 2) + 0]; + const high = this.Module.HEAPU32[startu + (i * 2) + 1]; + ranges.push([low, high]); + } + // TODO-LDML: no free?? + // Module.asm.free(buf); + return new UnicodeSet(pattern, ranges); + } else { + // translate error + // TODO-LDML: no free?? + // Module.asm.free(buf); + this.callbacks.reportMessage(getUnicodeSetError(rc)); + return null; } } -} \ No newline at end of file +} + +/** + * translate UnicodeSet return code into a compiler event + * @param rc parseUnicodeSet error code + * @returns the compiler event + */ +function getUnicodeSetError(rc: number) : CompilerEvent { + // from kmcmplib.h + const KMCMP_ERROR_SYNTAX_ERR = -1; + const KMCMP_ERROR_HAS_STRINGS = -2; + const KMCMP_ERROR_UNSUPPORTED_PROPERTY = -3; + const KMCMP_FATAL_OUT_OF_RANGE = -4; + switch(rc) { + case KMCMP_ERROR_SYNTAX_ERR: + return CompilerMessages.Error_UnicodeSetSyntaxError(); + case KMCMP_ERROR_HAS_STRINGS: + return CompilerMessages.Error_UnicodeSetHasStrings(); + case KMCMP_ERROR_UNSUPPORTED_PROPERTY: + return CompilerMessages.Error_UnicodeSetHasProperties(); + case KMCMP_FATAL_OUT_OF_RANGE: + return CompilerMessages.Fatal_UnicodeSetOutOfRange(); + default: + /* c8 ignore next */ + return CompilerMessages.Fatal_UnexpectedException({e: `Unexpected UnicodeSet error code ${rc}`}); + } +} + +/** + * Represents a parsed UnicodeSet + */ +export class UnicodeSet { + constructor(public pattern: string, public ranges: number[][]) { + } + /** + * Number of ranges + */ + get length() : number { + return this.ranges.length; + } + + toString() : string { + return this.pattern; + } +} diff --git a/developer/src/kmc-kmn/src/compiler/messages.ts b/developer/src/kmc-kmn/src/compiler/messages.ts index 866a19d9bebc476bffb6887036babc4c545514e7..0769096e070014b221a545ce4c34736ec95b9d02 100644 --- a/developer/src/kmc-kmn/src/compiler/messages.ts +++ b/developer/src/kmc-kmn/src/compiler/messages.ts @@ -1,4 +1,4 @@ -import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec as m } from "@keymanapp/common-types"; +import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec as m, compilerExceptionToString as exc } from "@keymanapp/common-types"; const Namespace = CompilerErrorNamespace.KmnCompiler; const SevInfo = CompilerErrorSeverity.Info | Namespace; @@ -44,14 +44,37 @@ export const enum KmnCompilerMessageRanges { and the below ranges are reserved. */ export class CompilerMessages { - static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, `Unexpected exception: ${(o.e ?? 'unknown error').toString()}\n\nCall stack:\n${(o.e instanceof Error ? o.e.stack : (new Error()).stack)}`); + static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, `Unexpected exception: ${exc(o.e)}`); static FATAL_UnexpectedException = SevFatal | 0x1000; - static Fatal_MissingWasmModule = () => m(this.FATAL_MissingWasmModule, `Could not instanatiate WASM compiler module`); + static Fatal_MissingWasmModule = (o:{e?: any}) => m(this.FATAL_MissingWasmModule, `Could not instantiate WASM compiler module or initialization failed: ${exc(o.e)}`); static FATAL_MissingWasmModule = SevFatal | 0x1001; static Fatal_UnableToSetCompilerOptions = () => m(this.FATAL_UnableToSetCompilerOptions, `Unable to set compiler options`); static FATAL_UnableToSetCompilerOptions = SevFatal | 0x1002; + + static Fatal_CallbacksNotSet = () => m(this.FATAL_CallbacksNotSet, `Callbacks were not set with init`); + static FATAL_CallbacksNotSet = SevFatal | 0x1003; + + static Fatal_UnicodeSetOutOfRange = () => m(this.FATAL_UnicodeSetOutOfRange, `UnicodeSet buffer was too small`); + static FATAL_UnicodeSetOutOfRange = SevFatal | 0x1004; + + static Error_UnicodeSetHasStrings = () => m(this.ERROR_UnicodeSetHasStrings, `UnicodeSet contains strings, not allowed`); + static ERROR_UnicodeSetHasStrings = SevError | 0x1005; + + static Error_UnicodeSetHasProperties = () => m(this.ERROR_UnicodeSetHasProperties, `UnicodeSet contains properties, not allowed`); + static ERROR_UnicodeSetHasProperties = SevError | 0x1006; + + static Error_UnicodeSetSyntaxError = () => m(this.ERROR_UnicodeSetSyntaxError, `UnicodeSet had a Syntax Error while parsing`); + static ERROR_UnicodeSetSyntaxError = SevError | 0x1007; + + static Error_InvalidKvksFile = (o:{filename: string, e: any}) => m(this.ERROR_InvalidKvksFile, + `Error encountered parsing ${o.filename}: ${o.e}`); + static ERROR_InvalidKvksFile = SevError | 0x1008; + + static Warn_InvalidVkeyInKvksFile = (o:{filename: string, invalidVkey: string}) => m(this.WARN_InvalidVkeyInKvksFile, + `Invalid virtual key ${o.invalidVkey} found in ${o.filename}`); + static WARN_InvalidVkeyInKvksFile = SevWarn | 0x1009; } export function mapErrorFromKmcmplib(line: number, code: number, msg: string): CompilerEvent { diff --git a/developer/src/kmc-kmn/src/main.ts b/developer/src/kmc-kmn/src/main.ts index c3c3d6f17392b6a88880e2129d604a8036582210..70d11989c6bfca468558bb217b42c628cf7017de 100644 --- a/developer/src/kmc-kmn/src/main.ts +++ b/developer/src/kmc-kmn/src/main.ts @@ -1,2 +1,2 @@ -export { Compiler } from './compiler/compiler.js'; +export { KmnCompiler } from './compiler/compiler.js'; diff --git a/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/cerr_duplicate_group.kmn b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/cerr_duplicate_group.kmn new file mode 100644 index 0000000000000000000000000000000000000000..8382b5ea7f4002161b422d513ef5ecd973ffd300 --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/cerr_duplicate_group.kmn @@ -0,0 +1,30 @@ +c Description: Verifies that kmcmplib picks up on duplicated groups with Unicode names, +c and that the Unicode names are correctly reported in error messages in UTF-8 + +store(&NAME) 'cerr_duplicate_group' +store(&VERSION) '9.0' + +begin unicode > use(ខ្មែរ) + +group(ខ្មែរ) using keys + +store(c_key) [K_K] [K_X] [SHIFT K_K] [SHIFT K_X] [K_G] \ + [K_C] [K_Q] [SHIFT K_C] [SHIFT K_Q] [SHIFT K_J] \ + [K_D] [K_Z] [SHIFT K_D] [SHIFT K_Z] [SHIFT K_N] \ + [K_T] [K_F] [SHIFT K_T] [SHIFT K_F] [K_N] \ + [K_B] [K_P] [SHIFT K_B] [SHIFT K_P] [K_M] \ + [K_Y] [K_R] [K_L] [K_V] [K_S] [K_H] [SHIFT K_L] [SHIFT K_G] \ + [RALT K_K] [RALT K_B] +store(c_out) U+1780 U+1781 U+1782 U+1783 U+1784 \ + U+1785 U+1786 U+1787 U+1788 U+1789 \ + U+178A U+178B U+178C U+178D U+178E \ + U+178F U+1790 U+1791 U+1792 U+1793 \ + U+1794 U+1795 U+1796 U+1797 U+1798 \ + U+1799 U+179A U+179B U+179C U+179F U+17A0 U+17A1 U+17A2 \ + U+179D U+179E c deprecated, but they are used in minority languages + ++ any(c_key) > index(c_out, 1) + +group(ខ្មែរ) using keys + ++ 'a' > 'oops two groups!' diff --git a/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/cerr_duplicate_store.kmn b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/cerr_duplicate_store.kmn new file mode 100644 index 0000000000000000000000000000000000000000..14d27d5be608f2dd7a480072836e243cb8a529f7 --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/cerr_duplicate_store.kmn @@ -0,0 +1,13 @@ +c Description: Verifies that kmcmplib picks up on duplicated stores with Unicode names, +c and that the Unicode names are correctly reported in error messages in UTF-8 + +store(&NAME) 'cerr_duplicate_store' +store(&VERSION) '9.0' + +begin unicode > use(ខ្មែរ) + +group(ខ្មែរ) using keys + +store(ខ្មែរ) 'abc' +store(ខ្មែរ) 'def' + diff --git a/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kmn b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kmn new file mode 100644 index 0000000000000000000000000000000000000000..f37d6397c5099dbbb61bfb3fa464c90c29dc9958 --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kmn @@ -0,0 +1,12 @@ +c Description: Verifies that kmc throws an error with an invalid .kvks file + +store(&NAME) 'error_invalid_kvks_file' +store(&VERSION) '9.0' +store(&VISUALKEYBOARD) 'error_invalid_kvks_file.kvks' + +begin unicode > use(main) + +group(main) using keys + ++ [K_A] > 'a' +'a' + [K_B] > 'ខ្មែរ' diff --git a/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kvks b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kvks new file mode 100644 index 0000000000000000000000000000000000000000..c681064f5b188af0703f5c1a31c66332abccf68c --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/error_invalid_kvks_file.kvks @@ -0,0 +1,9 @@ + + +
+ 10.0 + caps_lock_layer_3620 + + +
+
diff --git a/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kmn b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kmn new file mode 100644 index 0000000000000000000000000000000000000000..18a88973e93d128ea46c93c30f5346cc0395c3b1 --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kmn @@ -0,0 +1,12 @@ +c Description: Verifies that kmc reports a warning with an invalid vkey in the kvks + +store(&NAME) 'warn_invalid_vkey_in_kvks_file' +store(&VERSION) '9.0' +store(&VISUALKEYBOARD) 'warn_invalid_vkey_in_kvks_file.kvks' + +begin unicode > use(main) + +group(main) using keys + ++ [K_A] > 'a' +'a' + [K_B] > 'ខ្មែរ' diff --git a/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kvks b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kvks new file mode 100644 index 0000000000000000000000000000000000000000..f7b247db34e1f9ea8072c40c3a57289d3b461380 --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/invalid-keyboards/warn_invalid_vkey_in_kvks_file.kvks @@ -0,0 +1,14 @@ + + +
+ 10.0 + caps_lock_layer_3620 + +
+ + + + ឞ + + +
diff --git a/developer/src/kmc-kmn/test/helpers/index.ts b/developer/src/kmc-kmn/test/helpers/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..8250dfd101a0a07fd2966cf3e5537ac3386a92f5 --- /dev/null +++ b/developer/src/kmc-kmn/test/helpers/index.ts @@ -0,0 +1,17 @@ +/** + * Helpers and utilities for the Mocha tests. + */ +import * as path from 'path'; +import { fileURLToPath } from 'url'; + +/** + * Builds a path to the fixture with the given path components. + * + * e.g., makePathToFixture('example.qaa.trivial') + * e.g., makePathToFixture('example.qaa.trivial', 'model.ts') + * + * @param components One or more path components. + */ + export function makePathToFixture(...components: string[]): string { + return fileURLToPath(new URL(path.join('..', '..', '..', 'test', 'fixtures', ...components), import.meta.url)); +} diff --git a/developer/src/kmc-kmn/test/test-compiler.ts b/developer/src/kmc-kmn/test/test-compiler.ts index 081ed7762bc8f1ed68392188cc9678731f76f9d7..8c353983613f6d766f6fb8780b91153558c8b51c 100644 --- a/developer/src/kmc-kmn/test/test-compiler.ts +++ b/developer/src/kmc-kmn/test/test-compiler.ts @@ -1,43 +1,46 @@ import 'mocha'; -import sinon from 'sinon'; -import chai, { assert } from 'chai'; -import sinonChai from 'sinon-chai'; -import { Compiler } from '../src/main.js'; +import { assert } from 'chai'; +import { KmnCompiler } from '../src/main.js'; import { dirname } from 'path'; import { fileURLToPath } from 'url'; import fs from 'fs'; import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; const __dirname = dirname(fileURLToPath(import.meta.url)).replace(/\\/g, '/'); -const baselineDir = __dirname + '/../../../../../common/test/keyboards/baseline/'; -chai.use(sinonChai); +const keyboardsDir = __dirname + '/../../../../../common/test/keyboards/'; +const baselineDir = keyboardsDir + 'baseline/'; describe('Compiler class', function() { - let consoleLog: any; - - beforeEach(function() { - consoleLog = sinon.spy(console, 'log'); - }); - - afterEach(function() { - consoleLog.restore(); + it('should throw on failure', async function() { + const compiler = new KmnCompiler(); + const callbacks : any = null; // ERROR + try { + await compiler.init(callbacks) + assert.fail('Expected exception'); + } catch(e) { + assert.ok(e); + } + assert.throws(() => compiler.verifyInitialized()); }); it('should start', async function() { - const compiler = new Compiler(); - assert(await compiler.init()); + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); }); it('should compile a basic keyboard', async function() { - const compiler = new Compiler(); + const compiler = new KmnCompiler(); const callbacks = new TestCompilerCallbacks(); - assert(await compiler.init()); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); const fixtureName = baselineDir + 'k_000___null_keyboard.kmx'; const infile = baselineDir + 'k_000___null_keyboard.kmn'; const outfile = __dirname + '/k_000___null_keyboard.kmx'; - assert(compiler.run(infile, outfile, callbacks, {saveDebug: true, shouldAddCompilerVersion: false})); + assert(compiler.run(infile, outfile, {saveDebug: true, shouldAddCompilerVersion: false})); assert(fs.existsSync(outfile)); const outfileData = fs.readFileSync(outfile); @@ -49,9 +52,10 @@ describe('Compiler class', function() { // Note, above test case is essentially a subset of this one, but will leave both because // the basic keyboard test is slightly simpler to read it('should build all baseline fixtures', async function() { - const compiler = new Compiler(); + const compiler = new KmnCompiler(); const callbacks = new TestCompilerCallbacks(); - assert(await compiler.init()); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); const files = fs.readdirSync(baselineDir); for(let file of files) { @@ -60,7 +64,7 @@ describe('Compiler class', function() { const infile = baselineDir + file.replace(/x$/, 'n'); const outfile = __dirname + '/' + file; - assert(compiler.run(infile, outfile, callbacks, {saveDebug: true, shouldAddCompilerVersion: false})); + assert(compiler.run(infile, outfile, {saveDebug: true, shouldAddCompilerVersion: false})); assert(fs.existsSync(outfile)); const outfileData = fs.readFileSync(outfile); @@ -69,6 +73,36 @@ describe('Compiler class', function() { assert.deepEqual(outfileData, fixtureData); } } + }); + + it('should compile a keyboard with visual keyboard', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + + const fixtureDir = keyboardsDir + 'caps_lock_layer_3620/' + const infile = fixtureDir + 'source/caps_lock_layer_3620.kmn'; + const kmxFixture = fixtureDir + '/binary/caps_lock_layer_3620.kmx'; + const kvkFixture = fixtureDir + '/binary/caps_lock_layer_3620.kvk'; + + const resultingKmxfile = __dirname + '/caps_lock_layer_3620.kmx'; + const resultingKvkfile = __dirname + '/caps_lock_layer_3620.kvk'; + + assert(compiler.run(infile, resultingKmxfile, {saveDebug: true, shouldAddCompilerVersion: false})); + assert(fs.existsSync(resultingKmxfile)); + assert(fs.existsSync(resultingKvkfile)); + + const kmxData = fs.readFileSync(resultingKmxfile); + const kmxFixtureData = fs.readFileSync(kmxFixture); + assert.equal(kmxData.byteLength, kmxFixtureData.byteLength); + assert.deepEqual(kmxData, kmxFixtureData); + + const kvkData = fs.readFileSync(resultingKvkfile); + const kvkFixtureData = fs.readFileSync(kvkFixture); + assert.equal(kvkData.byteLength, kvkFixtureData.byteLength); + assert.deepEqual(kvkData, kvkFixtureData); }); -}); \ No newline at end of file + +}); diff --git a/developer/src/kmc-kmn/test/test-messages.ts b/developer/src/kmc-kmn/test/test-messages.ts index 0dee53f12112d47b67ca19bc266630a791dd4bf9..c551e1f223f40c138259941ed50ebf8ae7696d95 100644 --- a/developer/src/kmc-kmn/test/test-messages.ts +++ b/developer/src/kmc-kmn/test/test-messages.ts @@ -1,9 +1,69 @@ import 'mocha'; +import path from 'path'; +import { assert } from 'chai'; import { CompilerMessages } from '../src/compiler/messages.js'; -import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; +import { TestCompilerCallbacks, verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; +import { makePathToFixture } from './helpers/index.js'; +import { KmnCompiler } from '../src/main.js'; describe('CompilerMessages', function () { + const callbacks = new TestCompilerCallbacks(); + it('should have a valid CompilerMessages object', function() { return verifyCompilerMessagesObject(CompilerMessages); }); + + // + // Message tests + // + + async function testForMessage(context: Mocha.Context, fixture: string[], messageId?: number) { + context.timeout(10000); + + callbacks.clear(); + + const compiler = new KmnCompiler(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + + const kmnPath = makePathToFixture(...fixture); + const outfile = path.basename(kmnPath, '.kmn') + '.kmx'; + + // Note: throwing away compile results (just to memory) + compiler.runCompiler(kmnPath, outfile, {saveDebug: true, shouldAddCompilerVersion: false}); + + if(messageId) { + assert.isTrue(callbacks.hasMessage(messageId), `messageId ${messageId.toString(16)} not generated, instead got: `+JSON.stringify(callbacks.messages,null,2)); + assert.lengthOf(callbacks.messages, 1); + } else { + assert.lengthOf(callbacks.messages, 0, `messages should be empty, but instead got: `+JSON.stringify(callbacks.messages,null,2)); + } + } + + // ERROR_InvalidKvksFile + + it('should generate ERROR_InvalidKvksFile if the kvks is not valid XML', async function() { + await testForMessage(this, ['invalid-keyboards', 'error_invalid_kvks_file.kmn'], CompilerMessages.ERROR_InvalidKvksFile); + }); + + // WARN_InvalidVkeyInKvksFile + + it('should generate WARN_InvalidVkeyInKvksFile if the kvks contains an invalid virtual key', async function() { + await testForMessage(this, ['invalid-keyboards', 'warn_invalid_vkey_in_kvks_file.kmn'], CompilerMessages.WARN_InvalidVkeyInKvksFile); + }); + + // CERR_DuplicateGroup + + it('should generate CERR_DuplicateGroup if the kmn contains two groups with the same name', async function() { + await testForMessage(this, ['invalid-keyboards', 'cerr_duplicate_group.kmn'], 0x302071); //TODO: consolidate messages from kmcmplib, CompilerMessages.CERR_DuplicateGroup + assert.equal(callbacks.messages[0].message, "A group with this name has already been defined. Group 'ខ្មែរ' declared on line 9"); + }); + + // CERR_DuplicateStore + + it('should generate CERR_DuplicateStore if the kmn contains two stores with the same name', async function() { + await testForMessage(this, ['invalid-keyboards', 'cerr_duplicate_store.kmn'], 0x302072); //TODO: consolidate messages from kmcmplib, CompilerMessages.CERR_DuplicateStore + assert.equal(callbacks.messages[0].message, "A store with this name has already been defined. Store 'ខ្មែរ' declared on line 11"); + }); + }); diff --git a/developer/src/kmc-kmn/test/test-wasm-uset.ts b/developer/src/kmc-kmn/test/test-wasm-uset.ts new file mode 100644 index 0000000000000000000000000000000000000000..a63b408a959dfbe410703b3f73b28df5ce915bb4 --- /dev/null +++ b/developer/src/kmc-kmn/test/test-wasm-uset.ts @@ -0,0 +1,66 @@ +import 'mocha'; +import { assert } from 'chai'; +import { KmnCompiler } from '../src/main.js'; +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; +import { CompilerMessages } from '../src/compiler/messages.js'; +import { compilerErrorFormatCode } from '@keymanapp/common-types'; + +describe('Compiler UnicodeSet function', function() { + it('should start', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + }); + + it('should compile a basic uset', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + + const pat = "[abc]"; + const set = compiler.parseUnicodeSet(pat, 23); + + assert(set.length === 1); + assert(set.ranges[0][0] === 'a'.charCodeAt(0)); + assert(set.ranges[0][1] === 'c'.charCodeAt(0)); + }); + it('should compile a more complex uset', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + + const pat = "[[🙀A-C]-[CB]]"; + const set = compiler.parseUnicodeSet(pat, 23); + + assert.equal(set.length, 2); + assert.equal(set.ranges[0][0], 'A'.charCodeAt(0)); + assert.equal(set.ranges[0][1], 'A'.charCodeAt(0)); + assert.equal(set.ranges[1][0], 0x1F640); + assert.equal(set.ranges[1][1], 0x1F640); + }); + it('should fail in various ways', async function() { + const compiler = new KmnCompiler(); + const callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + // map from string to failing error + const failures = { + '[:Adlm:]': CompilerMessages.ERROR_UnicodeSetHasProperties, // what it saye + '[acegik]': CompilerMessages.FATAL_UnicodeSetOutOfRange, // 6 ranges, allocated 1 + '[[\\p{Mn}]&[A-Z]]': CompilerMessages.ERROR_UnicodeSetHasProperties, + '[abc{def}]': CompilerMessages.ERROR_UnicodeSetHasStrings, + '[[]': CompilerMessages.ERROR_UnicodeSetSyntaxError, + }; + for(const [pat, expected] of Object.entries(failures)) { + callbacks.clear(); + assert.notOk(compiler.parseUnicodeSet(pat, 1)); + assert.equal(callbacks.messages.length, 1); + const firstMessage = callbacks.messages[0]; + const code = firstMessage.code; + assert.equal(code, expected, `${compilerErrorFormatCode(code)}≠${compilerErrorFormatCode(expected)} got ${firstMessage.message} for ${pat}`); + } + }); +}); diff --git a/developer/src/kmc-kmn/test/tsconfig.json b/developer/src/kmc-kmn/test/tsconfig.json index 115412f57f397294d7bb8336dfed238cd9b3040c..f61df15d4a630b4516228d00c0f80049248d2e55 100644 --- a/developer/src/kmc-kmn/test/tsconfig.json +++ b/developer/src/kmc-kmn/test/tsconfig.json @@ -13,7 +13,8 @@ }, }, "include": [ - "**/test-*.ts" + "**/test-*.ts", + "./helpers/index.ts" ], "references": [ { "path": "../../../../common/web/keyman-version/tsconfig.esm.json" }, diff --git a/developer/src/kmc-kmw/README.md b/developer/src/kmc-kmw/README.md new file mode 100644 index 0000000000000000000000000000000000000000..27f1ebaa9295213ad4656f317bef030a24423827 --- /dev/null +++ b/developer/src/kmc-kmw/README.md @@ -0,0 +1 @@ +# kmc-kmw \ No newline at end of file diff --git a/developer/src/kmc-kmw/build.sh b/developer/src/kmc-kmw/build.sh index aa712d10ce3e61875a2111574fa745139d240771..1e43793b16f8c331be1ee890ccbb2317690adeaa 100755 --- a/developer/src/kmc-kmw/build.sh +++ b/developer/src/kmc-kmw/build.sh @@ -1,31 +1,26 @@ #!/usr/bin/env bash -# -# Compiles the kmc KeymanWeb keyboard compiler. -# - -# Exit on command failure and when using unset variables: -set -eu - ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary -THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")" -. "$(dirname "$THIS_SCRIPT")/../../../resources/build/build-utils.sh" +THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +. "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" ## END STANDARD BUILD SCRIPT INCLUDE cd "$THIS_SCRIPT_PATH" . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" builder_describe "Build Keyman kmc KMW Keyboard Compiler module" \ - "@/common/web/keyman-version" \ "@/common/web/types" \ "@/developer/src/kmc-kmn" \ "configure" \ "build" \ "clean" \ "test" \ + "pack build a local .tgz pack for testing" \ "publish publish to npm" \ "--dry-run,-n don't actually publish, just dry run" + builder_describe_outputs \ configure /node_modules \ build build/src/main.js @@ -34,42 +29,40 @@ builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- -if builder_start_action clean; then - rm -rf ./build/ ./tsconfig.tsbuildinfo - builder_finish_action success clean -else - # We need the schema file at runtime and bundled, so always copy it for all actions except `clean` +function copy_schema() { + # We need the schema file at runtime and bundled, so always copy it for all + # actions except `clean` and `configure` mkdir -p "$THIS_SCRIPT_PATH/build/src/" cp "$KEYMAN_ROOT/common/schemas/keyman-touch-layout/keyman-touch-layout.spec.json" "$THIS_SCRIPT_PATH/build/src/" cp "$KEYMAN_ROOT/common/schemas/kvks/kvks.schema.json" "$THIS_SCRIPT_PATH/build/src/" -fi - -#------------------------------------------------------------------------------------------------------------------- - -if builder_start_action configure; then - verify_npm_setup - builder_finish_action success configure -fi +} -#------------------------------------------------------------------------------------------------------------------- - -if builder_start_action build; then +function do_build() { + copy_schema npm run build - builder_finish_action success build -fi - -#------------------------------------------------------------------------------------------------------------------- +} -if builder_start_action test; then +function do_test() { # TODO: add c8 for coverage + copy_schema npm test - builder_finish_action success test -fi +} + +function do_publish() { + copy_schema + builder_publish_to_npm +} + +function do_pack() { + copy_schema + builder_publish_to_pack +} #------------------------------------------------------------------------------------------------------------------- -if builder_start_action publish; then - . "$KEYMAN_ROOT/resources/build/npm-publish.inc.sh" - npm_publish - builder_finish_action success publish -fi +builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo +builder_run_action configure verify_npm_setup +builder_run_action build do_build +builder_run_action test do_test +builder_run_action pack do_pack +builder_run_action publish do_publish diff --git a/developer/src/kmc-kmw/src/compiler/javascript-strings.ts b/developer/src/kmc-kmw/src/compiler/javascript-strings.ts index 2504781b9bdd023e21c7fd1b5e869af461daa009..731e8377678b9128f14875b41a8a26a5673749f5 100644 --- a/developer/src/kmc-kmw/src/compiler/javascript-strings.ts +++ b/developer/src/kmc-kmw/src/compiler/javascript-strings.ts @@ -123,13 +123,10 @@ export function JavaScript_String(ch: number): string { // I2242 case 10: return '\\n'; case 13: return '\\r'; } - if(ch < 9) { - return '\\x0'+ch.toString(16).toUpperCase(); - } else { - return '\\x'+ch.toString(16).toUpperCase(); - } + return '\\x' + zeroPadHex(ch, 2); } else { + // Note: unpaired surrogates will be maintained return String.fromCodePoint(ch); } } @@ -776,7 +773,6 @@ function CheckStoreForInvalidFunctions(fk: KMX.KEYBOARD, key: KMX.KEY, store: KM // Used when targeting versions prior to 10.0, before the introduction of FullContextMatch/KFCM. function JavaScript_CompositeContextValue(fk: KMX.KEYBOARD, fkp: KMX.KEY, pwsz: string): string { - // Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions // var // StartQuotes, Len, Cur: Integer; // InQuotes: Boolean; diff --git a/developer/src/kmc-kmw/src/compiler/write-compiled-keyboard.ts b/developer/src/kmc-kmw/src/compiler/write-compiled-keyboard.ts index 7e89aa7cddbfdc832289e83ae14e40d3020d7b3e..082c7a1881d592130419eb65f2c9a47f3bd98acf 100644 --- a/developer/src/kmc-kmw/src/compiler/write-compiled-keyboard.ts +++ b/developer/src/kmc-kmw/src/compiler/write-compiled-keyboard.ts @@ -1,4 +1,4 @@ -import { KVKSParseError, VisualKeyboard } from "@keymanapp/common-types"; +import { VisualKeyboard } from "@keymanapp/common-types"; import { KMX, CompilerCallbacks, KvkFileReader, KvksFileReader } from "@keymanapp/common-types"; import { ExpandSentinel, incxstr, xstrlen } from "../util/util.js"; // import { KEY, KEYBOARD, KMX.KMXFile, STORE } from "../../../../../common/web/types/src/kmx/kmx.js"; @@ -202,8 +202,7 @@ export function WriteCompiledKeyboard(callbacks: CompilerCallbacks, kmnfile: str let reader = new KvksFileReader(); let source = reader.read(callbacks.loadFile(path)); reader.validate(source, callbacks.loadSchema("kvks")); // TODO: handle exceptions - let errors: KVKSParseError[]; - kvk = reader.transform(source, errors); + kvk = reader.transform(source); // TODO: log errors } else { diff --git a/developer/src/kmc-kmw/test/test-compiler-manual.ts b/developer/src/kmc-kmw/test/test-compiler-manual.ts index 9f4f5ea4db4501a2d11fd78ab7efc75a4d535a01..39869657f44e80294dc7f8e6ab34db0c0b2feaad 100644 --- a/developer/src/kmc-kmw/test/test-compiler-manual.ts +++ b/developer/src/kmc-kmw/test/test-compiler-manual.ts @@ -2,7 +2,7 @@ import { dirname } from 'path'; import { fileURLToPath } from 'url'; import fs from 'fs'; import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; -import { Compiler } from '@keymanapp/kmc-kmn'; +import { KmnCompiler } from '@keymanapp/kmc-kmn'; import { KMX, KmxFileReader } from '@keymanapp/common-types'; import { WriteCompiledKeyboard } from '../src/compiler/write-compiled-keyboard.js'; import { extractTouchLayout } from './util.js'; @@ -20,14 +20,14 @@ if(fs.existsSync(testOutfile)) { const callbacks = new TestCompilerCallbacks(); -const kmxCompiler = new Compiler(); -if(!await kmxCompiler.init()) { +const kmnCompiler = new KmnCompiler(); +if(!await kmnCompiler.init(callbacks)) { console.error('kmx compiler failed to init'); process.exit(1); } // TODO: runToMemory, add option to kmxCompiler to store debug-data for conversion to .js (e.g. store metadata, group readonly metadata, etc) -if(!kmxCompiler.run(infile, outfile, callbacks, { +if(!kmnCompiler.run(infile, outfile, { shouldAddCompilerVersion: false, saveDebug: true, target: 'js' diff --git a/developer/src/kmc-kmw/test/test-compiler.ts b/developer/src/kmc-kmw/test/test-compiler.ts index 7332d71bda16d20e384ad8f7c837fda75cd0c01a..7f086ca0ad39c97608e0ed314455e6ee11f68328 100644 --- a/developer/src/kmc-kmw/test/test-compiler.ts +++ b/developer/src/kmc-kmw/test/test-compiler.ts @@ -6,7 +6,7 @@ import { dirname } from 'path'; import { fileURLToPath } from 'url'; import fs from 'fs'; import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; -import { Compiler } from '@keymanapp/kmc-kmn'; +import { KmnCompiler } from '@keymanapp/kmc-kmn'; import { KMX, KmxFileReader } from '@keymanapp/common-types'; import { extractTouchLayout } from './util.js'; @@ -35,11 +35,11 @@ describe('Compiler class', function() { fs.unlinkSync(testOutfile); } - const kmxCompiler = new Compiler(); - assert.isTrue(await kmxCompiler.init()); + const kmnCompiler = new KmnCompiler(); + assert.isTrue(await kmnCompiler.init(callbacks)); // TODO: runToMemory, add option to kmxCompiler to store debug-data for conversion to .js (e.g. store metadata, group readonly metadata, visual keyboard source filename, etc) - assert.isTrue(kmxCompiler.run(infile, outfile, callbacks, { + assert.isTrue(kmnCompiler.run(infile, outfile, { shouldAddCompilerVersion: false, saveDebug: true, target: 'js' diff --git a/developer/src/kmc-keyboard/.eslintrc.cjs b/developer/src/kmc-ldml/.eslintrc.cjs similarity index 100% rename from developer/src/kmc-keyboard/.eslintrc.cjs rename to developer/src/kmc-ldml/.eslintrc.cjs diff --git a/developer/src/kmc-keyboard/Makefile b/developer/src/kmc-ldml/Makefile similarity index 100% rename from developer/src/kmc-keyboard/Makefile rename to developer/src/kmc-ldml/Makefile diff --git a/developer/src/kmc-keyboard/build.sh b/developer/src/kmc-ldml/build.sh similarity index 98% rename from developer/src/kmc-keyboard/build.sh rename to developer/src/kmc-ldml/build.sh index edcb229e2ace21ab5fde89664537e469047b3340..71f146e835adc94adac256997240c25f4a21317b 100755 --- a/developer/src/kmc-keyboard/build.sh +++ b/developer/src/kmc-ldml/build.sh @@ -30,7 +30,7 @@ builder_describe "Build Keyman kmc Keyboard Compiler module" \ "--dry-run,-n don't actually publish, just dry run" builder_describe_outputs \ configure /node_modules \ - build /developer/src/kmc-keyboard/build/src/main.js + build /developer/src/kmc-ldml/build/src/main.js builder_parse "$@" diff --git a/developer/src/kmc-keyboard/package.json b/developer/src/kmc-ldml/package.json similarity index 94% rename from developer/src/kmc-keyboard/package.json rename to developer/src/kmc-ldml/package.json index 829acf29f8835dc9bdda9bc750287fb2660ef825..fda0b44272aa05a36300be18b6e3fa9a5877a790 100644 --- a/developer/src/kmc-keyboard/package.json +++ b/developer/src/kmc-ldml/package.json @@ -1,6 +1,6 @@ { - "name": "@keymanapp/kmc-keyboard", - "description": "Keyman Developer keyboard compiler", + "name": "@keymanapp/kmc-ldml", + "description": "Keyman Developer LDML keyboard compiler", "keywords": [ "keyboard", "keyman", diff --git a/developer/src/kmc-ldml/src/compiler/compiler-options.ts b/developer/src/kmc-ldml/src/compiler/compiler-options.ts new file mode 100644 index 0000000000000000000000000000000000000000..da81ef2f7d5489f7ccf3b742a4bf0d7088fc6e13 --- /dev/null +++ b/developer/src/kmc-ldml/src/compiler/compiler-options.ts @@ -0,0 +1,18 @@ +import { LDMLKeyboardXMLSourceFileReaderOptions } from "@keymanapp/common-types"; + +export interface CompilerOptions { + /** + * Add debug information to the .kmx file when compiling + */ + debug?: boolean; + + /** + * Add metadata about the compiler version to .kmx file when compiling + */ + addCompilerVersion?: boolean; + + /** + * Paths and other options required for reading .xml files + */ + readerOptions: LDMLKeyboardXMLSourceFileReaderOptions; +}; diff --git a/developer/src/kmc-keyboard/src/compiler/compiler.ts b/developer/src/kmc-ldml/src/compiler/compiler.ts similarity index 92% rename from developer/src/kmc-keyboard/src/compiler/compiler.ts rename to developer/src/kmc-ldml/src/compiler/compiler.ts index 29e0d61a53cc72ed006fc7d130dc6bf428397556..4b1472a58530dac22ca620b783c7c4d8b68290df 100644 --- a/developer/src/kmc-keyboard/src/compiler/compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/compiler.ts @@ -1,5 +1,5 @@ import { LDMLKeyboardXMLSourceFileReader, LDMLKeyboard, KMXPlus, CompilerCallbacks, LDMLKeyboardTestDataXMLSourceFile } from '@keymanapp/common-types'; -import CompilerOptions from './compiler-options.js'; +import { CompilerOptions } from './compiler-options.js'; import { CompilerMessages } from './messages.js'; import { BkspCompiler, FinlCompiler, TranCompiler } from './tran.js'; import { DispCompiler } from './disp.js'; @@ -28,18 +28,16 @@ const SECTION_COMPILERS = [ VkeyCompiler, ]; -export default class Compiler { +export class LdmlKeyboardCompiler { private readonly callbacks: CompilerCallbacks; - // private readonly options: CompilerOptions; // not currently used + private readonly options: CompilerOptions; - constructor (callbacks: CompilerCallbacks, _options?: CompilerOptions) { - /* + constructor (callbacks: CompilerCallbacks, options: CompilerOptions) { this.options = { debug: false, addCompilerVersion: true, ...options }; - */ this.callbacks = callbacks; } @@ -54,7 +52,7 @@ export default class Compiler { * @returns the source file, or null if invalid */ public load(filename: string): LDMLKeyboardXMLSourceFile | null { - const reader = new LDMLKeyboardXMLSourceFileReader(this.callbacks); + const reader = new LDMLKeyboardXMLSourceFileReader(this.options.readerOptions, this.callbacks); const data = this.callbacks.loadFile(filename); if(!data) { this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({errorText: 'Unable to read XML file'})); @@ -84,7 +82,7 @@ export default class Compiler { * @returns the source file, or null if invalid */ public loadTestData(filename: string): LDMLKeyboardTestDataXMLSourceFile | null { - const reader = new LDMLKeyboardXMLSourceFileReader(this.callbacks); + const reader = new LDMLKeyboardXMLSourceFileReader(this.options.readerOptions, this.callbacks); const data = this.callbacks.loadFile(filename); if(!data) { this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({errorText: 'Unable to read XML file'})); diff --git a/developer/src/kmc-keyboard/src/compiler/disp.ts b/developer/src/kmc-ldml/src/compiler/disp.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/disp.ts rename to developer/src/kmc-ldml/src/compiler/disp.ts diff --git a/developer/src/kmc-keyboard/src/compiler/keymanweb-compiler.ts b/developer/src/kmc-ldml/src/compiler/keymanweb-compiler.ts similarity index 89% rename from developer/src/kmc-keyboard/src/compiler/keymanweb-compiler.ts rename to developer/src/kmc-ldml/src/compiler/keymanweb-compiler.ts index 3117134c2c6f5b11b5ed2ebed1678f0534b92fbd..7ecdbe5937dabcc013fc44af1f16bef71054188a 100644 --- a/developer/src/kmc-keyboard/src/compiler/keymanweb-compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/keymanweb-compiler.ts @@ -1,19 +1,19 @@ import { CompilerCallbacks, VisualKeyboard, LDMLKeyboard, TouchLayoutFileWriter } from "@keymanapp/common-types"; -import CompilerOptions from "./compiler-options.js"; +import { CompilerOptions } from "./compiler-options.js"; import { TouchLayoutCompiler } from "./touch-layout-compiler.js"; -import VisualKeyboardCompiler from "./visual-keyboard-compiler.js"; +import { LdmlKeyboardVisualKeyboardCompiler } from "./visual-keyboard-compiler.js"; const MINIMUM_KMW_VERSION = '16.0'; -export interface KeymanWebCompilerOptions extends CompilerOptions { +export interface LdmlKeyboardKeymanWebCompilerOptions extends CompilerOptions { }; -export class KeymanWebCompiler { - private readonly options: KeymanWebCompilerOptions; +export class LdmlKeyboardKeymanWebCompiler { + private readonly options: LdmlKeyboardKeymanWebCompilerOptions; private readonly nl: string; private readonly tab: string; - constructor(private callbacks: CompilerCallbacks, options?: KeymanWebCompilerOptions) { + constructor(private callbacks: CompilerCallbacks, options?: LdmlKeyboardKeymanWebCompilerOptions) { this.options = { ...options }; this.nl = this.options.debug ? "\n" : ''; this.tab = this.options.debug ? " " : ''; @@ -21,7 +21,7 @@ export class KeymanWebCompiler { public compileVisualKeyboard(source: LDMLKeyboard.LDMLKeyboardXMLSourceFile) { const nl = this.nl, tab = this.tab; - const vkc = new VisualKeyboardCompiler(); + const vkc = new LdmlKeyboardVisualKeyboardCompiler(); const vk: VisualKeyboard.VisualKeyboard = vkc.compile(source); let result = diff --git a/developer/src/kmc-keyboard/src/compiler/keys.ts b/developer/src/kmc-ldml/src/compiler/keys.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/keys.ts rename to developer/src/kmc-ldml/src/compiler/keys.ts diff --git a/developer/src/kmc-keyboard/src/compiler/layr.ts b/developer/src/kmc-ldml/src/compiler/layr.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/layr.ts rename to developer/src/kmc-ldml/src/compiler/layr.ts diff --git a/developer/src/kmc-keyboard/src/compiler/loca.ts b/developer/src/kmc-ldml/src/compiler/loca.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/loca.ts rename to developer/src/kmc-ldml/src/compiler/loca.ts diff --git a/developer/src/kmc-keyboard/src/compiler/messages.ts b/developer/src/kmc-ldml/src/compiler/messages.ts similarity index 98% rename from developer/src/kmc-keyboard/src/compiler/messages.ts rename to developer/src/kmc-ldml/src/compiler/messages.ts index 610f7732e7c7f12bbdef624fb7d640f1d107c74d..32eb943a683ee6ef1995932b685e6f9a467f3669 100644 --- a/developer/src/kmc-keyboard/src/compiler/messages.ts +++ b/developer/src/kmc-ldml/src/compiler/messages.ts @@ -1,10 +1,10 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types"; -const SevInfo = CompilerErrorSeverity.Info | CompilerErrorNamespace.KeyboardCompiler; -const SevHint = CompilerErrorSeverity.Hint | CompilerErrorNamespace.KeyboardCompiler; +const SevInfo = CompilerErrorSeverity.Info | CompilerErrorNamespace.LdmlKeyboardCompiler; +const SevHint = CompilerErrorSeverity.Hint | CompilerErrorNamespace.LdmlKeyboardCompiler; // const SevWarn = CompilerErrorSeverity.Warn | CompilerErrorNamespace.KeyboardCompiler; -const SevError = CompilerErrorSeverity.Error | CompilerErrorNamespace.KeyboardCompiler; -const SevFatal = CompilerErrorSeverity.Fatal | CompilerErrorNamespace.KeyboardCompiler; +const SevError = CompilerErrorSeverity.Error | CompilerErrorNamespace.LdmlKeyboardCompiler; +const SevFatal = CompilerErrorSeverity.Fatal | CompilerErrorNamespace.LdmlKeyboardCompiler; export class CompilerMessages { static Error_InvalidNormalization = (o:{form: string}) => m(this.ERROR_InvalidNormalization, `Invalid normalization form '${o.form}`); diff --git a/developer/src/kmc-keyboard/src/compiler/meta.ts b/developer/src/kmc-ldml/src/compiler/meta.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/meta.ts rename to developer/src/kmc-ldml/src/compiler/meta.ts diff --git a/developer/src/kmc-keyboard/src/compiler/metadata-compiler.ts b/developer/src/kmc-ldml/src/compiler/metadata-compiler.ts similarity index 94% rename from developer/src/kmc-keyboard/src/compiler/metadata-compiler.ts rename to developer/src/kmc-ldml/src/compiler/metadata-compiler.ts index f459d5fab96651d93051380bfa8ec6cd5ee8efbc..262b86c8019f19d1fdd733a4c73a4901c9eaa793 100644 --- a/developer/src/kmc-keyboard/src/compiler/metadata-compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/metadata-compiler.ts @@ -1,12 +1,12 @@ import { KMX, KMXPlus } from '@keymanapp/common-types'; -import CompilerOptions from "./compiler-options.js"; +import { CompilerOptions } from "./compiler-options.js"; import KEYMAN_VERSION from "@keymanapp/keyman-version"; import KMXPlusData = KMXPlus.KMXPlusData; import KMXFile = KMX.KMXFile; import KEYBOARD = KMX.KEYBOARD; -export default class KMXPlusMetadataCompiler { +export class KMXPlusMetadataCompiler { /** * Look for metadata fields in the KMXPlus data and copy them * through to the relevant KMX stores diff --git a/developer/src/kmc-keyboard/src/compiler/name.ts b/developer/src/kmc-ldml/src/compiler/name.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/name.ts rename to developer/src/kmc-ldml/src/compiler/name.ts diff --git a/developer/src/kmc-keyboard/src/compiler/ordr.ts b/developer/src/kmc-ldml/src/compiler/ordr.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/ordr.ts rename to developer/src/kmc-ldml/src/compiler/ordr.ts diff --git a/developer/src/kmc-keyboard/src/compiler/section-compiler.ts b/developer/src/kmc-ldml/src/compiler/section-compiler.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/section-compiler.ts rename to developer/src/kmc-ldml/src/compiler/section-compiler.ts diff --git a/developer/src/kmc-keyboard/src/compiler/touch-layout-compiler.ts b/developer/src/kmc-ldml/src/compiler/touch-layout-compiler.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/touch-layout-compiler.ts rename to developer/src/kmc-ldml/src/compiler/touch-layout-compiler.ts diff --git a/developer/src/kmc-keyboard/src/compiler/tran.ts b/developer/src/kmc-ldml/src/compiler/tran.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/tran.ts rename to developer/src/kmc-ldml/src/compiler/tran.ts diff --git a/developer/src/kmc-keyboard/src/compiler/visual-keyboard-compiler.ts b/developer/src/kmc-ldml/src/compiler/visual-keyboard-compiler.ts similarity index 97% rename from developer/src/kmc-keyboard/src/compiler/visual-keyboard-compiler.ts rename to developer/src/kmc-ldml/src/compiler/visual-keyboard-compiler.ts index b80b590b9cfcb9b2b26673fec14ae67ae0e85f8e..d951080b9b41467553e235db92cede920f2789c1 100644 --- a/developer/src/kmc-keyboard/src/compiler/visual-keyboard-compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/visual-keyboard-compiler.ts @@ -1,6 +1,6 @@ import { Constants, VisualKeyboard, LDMLKeyboard } from "@keymanapp/common-types"; -export default class VisualKeyboardCompiler { +export class LdmlKeyboardVisualKeyboardCompiler { public compile(source: LDMLKeyboard.LDMLKeyboardXMLSourceFile): VisualKeyboard.VisualKeyboard { let result = new VisualKeyboard.VisualKeyboard(); diff --git a/developer/src/kmc-keyboard/src/compiler/vkey.ts b/developer/src/kmc-ldml/src/compiler/vkey.ts similarity index 100% rename from developer/src/kmc-keyboard/src/compiler/vkey.ts rename to developer/src/kmc-ldml/src/compiler/vkey.ts diff --git a/developer/src/kmc-ldml/src/main.ts b/developer/src/kmc-ldml/src/main.ts new file mode 100644 index 0000000000000000000000000000000000000000..5d5d2f09e64947c9bf79baa94f6d9eeaa01e0e6e --- /dev/null +++ b/developer/src/kmc-ldml/src/main.ts @@ -0,0 +1,10 @@ + +export { LdmlKeyboardCompiler } from './compiler/compiler.js'; +export { LdmlKeyboardKeymanWebCompiler } from './compiler/keymanweb-compiler.js'; +export { LdmlKeyboardVisualKeyboardCompiler } from './compiler/visual-keyboard-compiler.js'; +export { TouchLayoutCompiler } from './compiler/touch-layout-compiler.js'; +export { CompilerOptions } from './compiler/compiler-options.js'; +export { CompilerMessages } from './compiler/messages.js'; +export { KMXPlusMetadataCompiler } from './compiler/metadata-compiler.js'; + +export { KMXBuilder } from "@keymanapp/common-types"; diff --git a/developer/src/kmc-keyboard/src/util/util.ts b/developer/src/kmc-ldml/src/util/util.ts similarity index 100% rename from developer/src/kmc-keyboard/src/util/util.ts rename to developer/src/kmc-ldml/src/util/util.ts diff --git a/developer/src/kmc-keyboard/test/README.md b/developer/src/kmc-ldml/test/README.md similarity index 100% rename from developer/src/kmc-keyboard/test/README.md rename to developer/src/kmc-ldml/test/README.md diff --git a/developer/src/kmc-keyboard/test/fixtures/basic-kvk.txt b/developer/src/kmc-ldml/test/fixtures/basic-kvk.txt similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/basic-kvk.txt rename to developer/src/kmc-ldml/test/fixtures/basic-kvk.txt diff --git a/developer/src/kmc-keyboard/test/fixtures/basic-no-debug.js b/developer/src/kmc-ldml/test/fixtures/basic-no-debug.js similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/basic-no-debug.js rename to developer/src/kmc-ldml/test/fixtures/basic-no-debug.js diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.js b/developer/src/kmc-ldml/test/fixtures/basic.js similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/basic.js rename to developer/src/kmc-ldml/test/fixtures/basic.js diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.txt b/developer/src/kmc-ldml/test/fixtures/basic.txt similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/basic.txt rename to developer/src/kmc-ldml/test/fixtures/basic.txt diff --git a/developer/src/kmc-keyboard/test/fixtures/basic.xml b/developer/src/kmc-ldml/test/fixtures/basic.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/basic.xml rename to developer/src/kmc-ldml/test/fixtures/basic.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/bksp/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/bksp/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/bksp/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/bksp/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/escaped.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/escaped.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/escaped.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/escaped.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/invalid-dupto.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/invalid-dupto.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/invalid-dupto.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/invalid-dupto.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/maximal.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/maximal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/maximal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/maximal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/options-only.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/options-only.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/options-only.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/options-only.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/disp/typical.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/typical.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/disp/typical.xml rename to developer/src/kmc-ldml/test/fixtures/sections/disp/typical.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/finl/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/finl/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/finl/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/finl/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/escaped.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/escaped.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/escaped.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/escaped.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/escaped2.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/escaped2.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/escaped2.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/escaped2.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/gap-switch.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/gap-switch.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/gap-switch.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/gap-switch.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/hardware.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/hardware.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware_iso.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/hardware_iso.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware_iso.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/hardware_iso.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware_us.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/hardware_us.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/hardware_us.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/hardware_us.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-bad-modifier.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-bad-modifier.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-bad-modifier.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-bad-modifier.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-hardware-too-many-keys.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-hardware-too-many-keys.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-hardware-too-many-keys.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-hardware-too-many-keys.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-hardware-too-many-rows.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-hardware-too-many-rows.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-hardware-too-many-rows.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-hardware-too-many-rows.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-key-missing-attrs.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-key-missing-attrs.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-key-missing-attrs.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-key-missing-attrs.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-missing-flick.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-missing-flick.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-missing-flick.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-missing-flick.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-undefined-key.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-undefined-key.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/invalid-undefined-key.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/invalid-undefined-key.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/maximal.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/maximal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/maximal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/maximal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/keys/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/keys/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/keys/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-invalid-form.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-invalid-form.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-invalid-form.xml rename to developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-invalid-form.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-hardware.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-hardware.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-hardware.xml rename to developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-hardware.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-layer.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-layer.xml rename to developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-layer2.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer2.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-missing-layer2.xml rename to developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-missing-layer2.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-multi-hardware.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-multi-hardware.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/layr/invalid-multi-hardware.xml rename to developer/src/kmc-ldml/test/fixtures/sections/layr/invalid-multi-hardware.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/loca/invalid-locale.xml b/developer/src/kmc-ldml/test/fixtures/sections/loca/invalid-locale.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/loca/invalid-locale.xml rename to developer/src/kmc-ldml/test/fixtures/sections/loca/invalid-locale.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/loca/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/loca/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/loca/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/loca/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/loca/multiple.xml b/developer/src/kmc-ldml/test/fixtures/sections/loca/multiple.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/loca/multiple.xml rename to developer/src/kmc-ldml/test/fixtures/sections/loca/multiple.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-normalization.xml b/developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-normalization.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-normalization.xml rename to developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-normalization.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-version-1.0.xml b/developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-version-1.0.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-version-1.0.xml rename to developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-version-1.0.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-version-v1.0.3.xml b/developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-version-v1.0.3.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/meta/invalid-version-v1.0.3.xml rename to developer/src/kmc-ldml/test/fixtures/sections/meta/invalid-version-v1.0.3.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/meta/maximal.xml b/developer/src/kmc-ldml/test/fixtures/sections/meta/maximal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/meta/maximal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/meta/maximal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/meta/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/meta/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/meta/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/meta/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/name/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/name/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/name/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/name/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/name/multiple.xml b/developer/src/kmc-ldml/test/fixtures/sections/name/multiple.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/name/multiple.xml rename to developer/src/kmc-ldml/test/fixtures/sections/name/multiple.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/ordr/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/ordr/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/ordr/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/ordr/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/tran/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/tran/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/tran/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/tran/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-from-vkey.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-from-vkey.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-from-vkey.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-from-vkey.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-repeated-vkey.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-repeated-vkey.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-repeated-vkey.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-repeated-vkey.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-to-vkey.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-to-vkey.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/invalid-to-vkey.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-to-vkey.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/minimal.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/minimal.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/minimal.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/redundant.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/redundant.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/redundant.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/redundant.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/sections/vkey/same-target.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/same-target.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/sections/vkey/same-target.xml rename to developer/src/kmc-ldml/test/fixtures/sections/vkey/same-target.xml diff --git a/developer/src/kmc-keyboard/test/fixtures/test-fr.json b/developer/src/kmc-ldml/test/fixtures/test-fr.json similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/test-fr.json rename to developer/src/kmc-ldml/test/fixtures/test-fr.json diff --git a/developer/src/kmc-keyboard/test/fixtures/test-fr.xml b/developer/src/kmc-ldml/test/fixtures/test-fr.xml similarity index 100% rename from developer/src/kmc-keyboard/test/fixtures/test-fr.xml rename to developer/src/kmc-ldml/test/fixtures/test-fr.xml diff --git a/developer/src/kmc-keyboard/test/helpers/index.ts b/developer/src/kmc-ldml/test/helpers/index.ts similarity index 86% rename from developer/src/kmc-keyboard/test/helpers/index.ts rename to developer/src/kmc-ldml/test/helpers/index.ts index 498975f71e2e3661ab2209bda41720d7b1ab4984..f10af945c82488d9e1543efbe9e623a138488c12 100644 --- a/developer/src/kmc-keyboard/test/helpers/index.ts +++ b/developer/src/kmc-ldml/test/helpers/index.ts @@ -6,11 +6,11 @@ import * as path from 'path'; import { fileURLToPath } from 'url'; import { SectionCompiler } from '../../src/compiler/section-compiler.js'; import { KMXPlus, LDMLKeyboardXMLSourceFileReader, VisualKeyboard, CompilerEvent, LDMLKeyboardTestDataXMLSourceFile } from '@keymanapp/common-types'; -import Compiler from '../../src/compiler/compiler.js'; +import { LdmlKeyboardCompiler } from '../../src/compiler/compiler.js'; import { assert } from 'chai'; -import KMXPlusMetadataCompiler from '../../src/compiler/metadata-compiler.js'; -import CompilerOptions from '../../src/compiler/compiler-options.js'; -import VisualKeyboardCompiler from '../../src/compiler/visual-keyboard-compiler.js'; +import { KMXPlusMetadataCompiler } from '../../src/compiler/metadata-compiler.js'; +import { CompilerOptions } from '../../src/compiler/compiler-options.js'; +import { LdmlKeyboardVisualKeyboardCompiler } from '../../src/compiler/visual-keyboard-compiler.js'; import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; import KMXPlusFile = KMXPlus.KMXPlusFile; @@ -33,6 +33,12 @@ export function makePathToFixture(...components: string[]): string { export const compilerTestCallbacks = new TestCompilerCallbacks(); +export const compilerTestOptions: CompilerOptions = { + readerOptions: { + importsPath: fileURLToPath(LDMLKeyboardXMLSourceFileReader.defaultImportsURL) + } +}; + beforeEach(function() { compilerTestCallbacks.clear(); }); @@ -43,14 +49,13 @@ afterEach(function() { } }); - export function loadSectionFixture(compilerClass: typeof SectionCompiler, filename: string, callbacks: TestCompilerCallbacks): Section { callbacks.messages = []; const inputFilename = makePathToFixture(filename); const data = callbacks.loadFile(inputFilename); assert.isNotNull(data); - const reader = new LDMLKeyboardXMLSourceFileReader(callbacks); + const reader = new LDMLKeyboardXMLSourceFileReader(compilerTestOptions.readerOptions, callbacks); const source = reader.load(data); assert.isNotNull(source); @@ -76,13 +81,13 @@ export function loadSectionFixture(compilerClass: typeof SectionCompiler, filena } export function loadTestdata(inputFilename: string, options: CompilerOptions) : LDMLKeyboardTestDataXMLSourceFile { - const k = new Compiler(compilerTestCallbacks, options); + const k = new LdmlKeyboardCompiler(compilerTestCallbacks, options); const source = k.loadTestData(inputFilename); return source; } export function compileKeyboard(inputFilename: string, options: CompilerOptions): KMXPlusFile { - const k = new Compiler(compilerTestCallbacks, options); + const k = new LdmlKeyboardCompiler(compilerTestCallbacks, options); const source = k.load(inputFilename); checkMessages(); assert.isNotNull(source, 'k.load should not have returned null'); @@ -103,7 +108,7 @@ export function compileKeyboard(inputFilename: string, options: CompilerOptions) } export function compileVisualKeyboard(inputFilename: string, options: CompilerOptions): VisualKeyboard.VisualKeyboard { - const k = new Compiler(compilerTestCallbacks, options); + const k = new LdmlKeyboardCompiler(compilerTestCallbacks, options); const source = k.load(inputFilename); checkMessages(); assert.isNotNull(source, 'k.load should not have returned null'); @@ -112,9 +117,9 @@ export function compileVisualKeyboard(inputFilename: string, options: CompilerOp checkMessages(); assert.isTrue(valid, 'k.validate should not have failed'); - const vk = (new VisualKeyboardCompiler()).compile(source); + const vk = (new LdmlKeyboardVisualKeyboardCompiler()).compile(source); checkMessages(); - assert.isNotNull(vk, 'VisualKeyboardCompiler.compile should not have returned null'); + assert.isNotNull(vk, 'LdmlKeyboardVisualKeyboardCompiler.compile should not have returned null'); return vk; } diff --git a/developer/src/kmc-keyboard/test/test-bksp.ts b/developer/src/kmc-ldml/test/test-bksp.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-bksp.ts rename to developer/src/kmc-ldml/test/test-bksp.ts diff --git a/developer/src/kmc-keyboard/test/test-compiler-e2e.ts b/developer/src/kmc-ldml/test/test-compiler-e2e.ts similarity index 81% rename from developer/src/kmc-keyboard/test/test-compiler-e2e.ts rename to developer/src/kmc-ldml/test/test-compiler-e2e.ts index ec711549051a4f4a5d43911f8ae8b2924996b962..7d8b179f3125782b8ae557143558a344afa61b86 100644 --- a/developer/src/kmc-keyboard/test/test-compiler-e2e.ts +++ b/developer/src/kmc-ldml/test/test-compiler-e2e.ts @@ -2,7 +2,7 @@ import 'mocha'; import {assert} from 'chai'; import x_hextobin from '@keymanapp/hextobin'; import { KMXBuilder } from '@keymanapp/common-types'; -import {checkMessages, compileKeyboard, makePathToFixture} from './helpers/index.js'; +import {checkMessages, compileKeyboard, compilerTestOptions, makePathToFixture} from './helpers/index.js'; const hextobin = (x_hextobin as any).default; @@ -17,7 +17,7 @@ describe('compiler-tests', function() { const binaryFilename = makePathToFixture('basic.txt'); // Compile the keyboard - const kmx = compileKeyboard(inputFilename, {debug: true, addCompilerVersion: false}); + const kmx = compileKeyboard(inputFilename, {...compilerTestOptions, debug: true, addCompilerVersion: false}); assert.isNotNull(kmx); // Use the builder to generate the binary output file diff --git a/developer/src/kmc-keyboard/test/test-disp.ts b/developer/src/kmc-ldml/test/test-disp.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-disp.ts rename to developer/src/kmc-ldml/test/test-disp.ts diff --git a/developer/src/kmc-keyboard/test/test-finl.ts b/developer/src/kmc-ldml/test/test-finl.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-finl.ts rename to developer/src/kmc-ldml/test/test-finl.ts diff --git a/developer/src/kmc-keyboard/test/test-keymanweb-compiler.ts b/developer/src/kmc-ldml/test/test-keymanweb-compiler.ts similarity index 69% rename from developer/src/kmc-keyboard/test/test-keymanweb-compiler.ts rename to developer/src/kmc-ldml/test/test-keymanweb-compiler.ts index 43f3721de69d7173261930337415327a54e1da79..711820e5e4051a751304f1620369e7f1f131d151 100644 --- a/developer/src/kmc-keyboard/test/test-keymanweb-compiler.ts +++ b/developer/src/kmc-ldml/test/test-keymanweb-compiler.ts @@ -1,11 +1,11 @@ import 'mocha'; import { assert } from 'chai'; -import { checkMessages, compilerTestCallbacks, makePathToFixture } from './helpers/index.js'; -import { KeymanWebCompiler } from '../src/compiler/keymanweb-compiler.js'; -import Compiler from '../src/compiler/compiler.js'; +import { checkMessages, compilerTestCallbacks, compilerTestOptions, makePathToFixture } from './helpers/index.js'; +import { LdmlKeyboardKeymanWebCompiler } from '../src/compiler/keymanweb-compiler.js'; +import { LdmlKeyboardCompiler } from '../src/compiler/compiler.js'; import * as fs from 'fs'; -describe('KeymanWebCompiler', function() { +describe('LdmlKeyboardKeymanWebCompiler', function() { it('should build a .js file', async function() { // Let's build basic.xml @@ -16,7 +16,7 @@ describe('KeymanWebCompiler', function() { // Load input data; we'll use the LDML keyboard compiler loader to save us // effort here - const k = new Compiler(compilerTestCallbacks, {debug: true, addCompilerVersion: false}); + const k = new LdmlKeyboardCompiler(compilerTestCallbacks, {...compilerTestOptions, debug: true, addCompilerVersion: false}); const source = k.load(inputFilename); checkMessages(); assert.isNotNull(source, 'k.load should not have returned null'); @@ -27,7 +27,7 @@ describe('KeymanWebCompiler', function() { assert.isTrue(valid, 'k.validate should not have failed'); // Actual test: compile to javascript - const jsCompiler = new KeymanWebCompiler(compilerTestCallbacks, {debug: true}); + const jsCompiler = new LdmlKeyboardKeymanWebCompiler(compilerTestCallbacks, {...compilerTestOptions, debug: true}); const output = jsCompiler.compile('basic.xml', source); assert.isNotNull(output); @@ -36,7 +36,7 @@ describe('KeymanWebCompiler', function() { assert.strictEqual(output, outputFixture); // Second test: compile to javascript without debug formatting - const jsCompilerNoDebug = new KeymanWebCompiler(compilerTestCallbacks, {debug: false}); + const jsCompilerNoDebug = new LdmlKeyboardKeymanWebCompiler(compilerTestCallbacks, {...compilerTestOptions, debug: false}); const outputNoDebug = jsCompilerNoDebug.compile('basic.xml', source); assert.isNotNull(outputNoDebug); diff --git a/developer/src/kmc-keyboard/test/test-keys.ts b/developer/src/kmc-ldml/test/test-keys.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-keys.ts rename to developer/src/kmc-ldml/test/test-keys.ts diff --git a/developer/src/kmc-keyboard/test/test-layr.ts b/developer/src/kmc-ldml/test/test-layr.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-layr.ts rename to developer/src/kmc-ldml/test/test-layr.ts diff --git a/developer/src/kmc-keyboard/test/test-loca.ts b/developer/src/kmc-ldml/test/test-loca.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-loca.ts rename to developer/src/kmc-ldml/test/test-loca.ts diff --git a/developer/src/kmc-keyboard/test/test-messages.ts b/developer/src/kmc-ldml/test/test-messages.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-messages.ts rename to developer/src/kmc-ldml/test/test-messages.ts diff --git a/developer/src/kmc-keyboard/test/test-meta.ts b/developer/src/kmc-ldml/test/test-meta.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-meta.ts rename to developer/src/kmc-ldml/test/test-meta.ts diff --git a/developer/src/kmc-keyboard/test/test-metadata-compiler.ts b/developer/src/kmc-ldml/test/test-metadata-compiler.ts similarity index 88% rename from developer/src/kmc-keyboard/test/test-metadata-compiler.ts rename to developer/src/kmc-ldml/test/test-metadata-compiler.ts index 59500d09950c313568948fb70b8322ea0799b0d7..ede77ed4bccfba209a0d6f1e570ffa9bd56e7755 100644 --- a/developer/src/kmc-keyboard/test/test-metadata-compiler.ts +++ b/developer/src/kmc-ldml/test/test-metadata-compiler.ts @@ -1,6 +1,6 @@ import 'mocha'; import { assert } from 'chai'; -import { checkMessages, compileKeyboard, makePathToFixture } from './helpers/index.js'; +import { checkMessages, compileKeyboard, compilerTestOptions, makePathToFixture } from './helpers/index.js'; import { KMX } from '@keymanapp/common-types'; import KEYMAN_VERSION from '@keymanapp/keyman-version'; @@ -13,7 +13,7 @@ describe('kmx metadata compiler', function () { const inputFilename = makePathToFixture('basic.xml'); // Compile the keyboard - const kmx = compileKeyboard(inputFilename, {debug:true, addCompilerVersion:true}); + const kmx = compileKeyboard(inputFilename, {...compilerTestOptions, debug:true, addCompilerVersion:true}); checkMessages(); assert.isNotNull(kmx); @@ -48,7 +48,7 @@ describe('kmx metadata compiler', function () { const inputFilename = makePathToFixture('basic.xml'); // Compile the keyboard - const kmx = compileKeyboard(inputFilename, {debug:true, addCompilerVersion:false}); + const kmx = compileKeyboard(inputFilename, {...compilerTestOptions, debug:true, addCompilerVersion:false}); checkMessages(); assert.isNotNull(kmx); diff --git a/developer/src/kmc-keyboard/test/test-name.ts b/developer/src/kmc-ldml/test/test-name.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-name.ts rename to developer/src/kmc-ldml/test/test-name.ts diff --git a/developer/src/kmc-keyboard/test/test-ordr.ts b/developer/src/kmc-ldml/test/test-ordr.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-ordr.ts rename to developer/src/kmc-ldml/test/test-ordr.ts diff --git a/developer/src/kmc-keyboard/test/test-testdata-e2e.ts b/developer/src/kmc-ldml/test/test-testdata-e2e.ts similarity index 75% rename from developer/src/kmc-keyboard/test/test-testdata-e2e.ts rename to developer/src/kmc-ldml/test/test-testdata-e2e.ts index 25ac1963213a5fed475292821837b4fbb8c005fb..ece4ce85608174bac50522c48810ca3f89fac3df 100644 --- a/developer/src/kmc-keyboard/test/test-testdata-e2e.ts +++ b/developer/src/kmc-ldml/test/test-testdata-e2e.ts @@ -1,7 +1,7 @@ import { readFileSync } from 'fs'; import 'mocha'; import {assert} from 'chai'; -import {loadTestdata, makePathToFixture} from './helpers/index.js'; +import {compilerTestOptions, loadTestdata, makePathToFixture} from './helpers/index.js'; describe('testdata-tests', function() { this.slow(500); // 0.5 sec -- json schema validation takes a while @@ -14,7 +14,7 @@ describe('testdata-tests', function() { const jsonFilename = makePathToFixture('test-fr.json'); // Compile the keyboard - const testData = loadTestdata(inputFilename, {debug: true, addCompilerVersion: false}); + const testData = loadTestdata(inputFilename, {...compilerTestOptions, debug: true, addCompilerVersion: false}); assert.isNotNull(testData); const jsonData = JSON.parse(readFileSync(jsonFilename, 'utf-8')); diff --git a/developer/src/kmc-keyboard/test/test-tran.ts b/developer/src/kmc-ldml/test/test-tran.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-tran.ts rename to developer/src/kmc-ldml/test/test-tran.ts diff --git a/developer/src/kmc-keyboard/test/test-utils.ts b/developer/src/kmc-ldml/test/test-utils.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-utils.ts rename to developer/src/kmc-ldml/test/test-utils.ts diff --git a/developer/src/kmc-keyboard/test/test-virtual-key-constants.ts b/developer/src/kmc-ldml/test/test-virtual-key-constants.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-virtual-key-constants.ts rename to developer/src/kmc-ldml/test/test-virtual-key-constants.ts diff --git a/developer/src/kmc-keyboard/test/test-visual-keyboard-compiler-e2e.ts b/developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts similarity index 80% rename from developer/src/kmc-keyboard/test/test-visual-keyboard-compiler-e2e.ts rename to developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts index 1b1e34069e5ac2b13982f5ee18654b80d1bacb6f..4af68278387c8dd89283108fcbe6d031ee8b51a8 100644 --- a/developer/src/kmc-keyboard/test/test-visual-keyboard-compiler-e2e.ts +++ b/developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts @@ -2,7 +2,7 @@ import 'mocha'; import {assert} from 'chai'; import x_hextobin from '@keymanapp/hextobin'; import { KvkFileWriter } from '@keymanapp/common-types'; -import {checkMessages, compileVisualKeyboard, makePathToFixture} from './helpers/index.js'; +import {checkMessages, compilerTestOptions, compileVisualKeyboard, makePathToFixture} from './helpers/index.js'; const hextobin = (x_hextobin as any).default; @@ -17,7 +17,7 @@ describe('visual-keyboard-compiler', function() { const binaryFilename = makePathToFixture('basic-kvk.txt'); // Compile the visual keyboard - const vk = compileVisualKeyboard(inputFilename, {debug: true, addCompilerVersion: false}); + const vk = compileVisualKeyboard(inputFilename, {...compilerTestOptions, debug: true, addCompilerVersion: false}); assert.isNotNull(vk); // Use the builder to generate the binary output file diff --git a/developer/src/kmc-keyboard/test/test-vkey.ts b/developer/src/kmc-ldml/test/test-vkey.ts similarity index 100% rename from developer/src/kmc-keyboard/test/test-vkey.ts rename to developer/src/kmc-ldml/test/test-vkey.ts diff --git a/developer/src/kmc-keyboard/test/tsconfig.json b/developer/src/kmc-ldml/test/tsconfig.json similarity index 100% rename from developer/src/kmc-keyboard/test/tsconfig.json rename to developer/src/kmc-ldml/test/tsconfig.json diff --git a/developer/src/kmc-keyboard/tsconfig.json b/developer/src/kmc-ldml/tsconfig.json similarity index 100% rename from developer/src/kmc-keyboard/tsconfig.json rename to developer/src/kmc-ldml/tsconfig.json diff --git a/developer/src/kmc-model-info/Makefile b/developer/src/kmc-model-info/Makefile index f3e73efd1239abdb668726af3ac9b51169d942ac..878d77f184f61fd91dd90ee8c403ed7c513c8f8c 100644 --- a/developer/src/kmc-model-info/Makefile +++ b/developer/src/kmc-model-info/Makefile @@ -4,7 +4,7 @@ !include ..\Defines.mak -# We don't depend on configure here because kmc-keyboard does that already +# We don't depend on configure here because kmc-ldml does that already build: .virtual $(GIT_BASH_FOR_KEYMAN) build.sh build diff --git a/developer/src/kmc-model-info/build.sh b/developer/src/kmc-model-info/build.sh index 02001fbc2bf78d30d40bc675844b4e16316f21c0..7c2119826f5c3dba16e250d92b387e60bb6cfbc1 100755 --- a/developer/src/kmc-model-info/build.sh +++ b/developer/src/kmc-model-info/build.sh @@ -1,11 +1,4 @@ #!/usr/bin/env bash -# -# Compiles the kmc lexical model model-info compiler. -# - -# Exit on command failure and when using unset variables: -set -eu - ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" @@ -17,14 +10,16 @@ cd "$THIS_SCRIPT_PATH" . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" builder_describe "Build Keyman kmc Lexical Model model-info Compiler module" \ - "@/developer/src/kmc-package" \ + "@/common/web/types" \ + "@/common/models/types" \ + "clean" \ "configure" \ "build" \ - "clean" \ "test" \ "pack build a local .tgz pack for testing" \ "publish publish to npm" \ "--dry-run,-n don't actually publish, just dry run" + builder_describe_outputs \ configure /node_modules \ build /developer/src/kmc-model-info/build/src/model-info-compiler.js diff --git a/developer/src/kmc-model-info/package.json b/developer/src/kmc-model-info/package.json index 957056e042b6d8c007dbbcceea3ef83f68d33d65..709f9d2d6fc0b29a0b732361a6463e6b487e42ad 100644 --- a/developer/src/kmc-model-info/package.json +++ b/developer/src/kmc-model-info/package.json @@ -31,8 +31,7 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "dependencies": { - "@keymanapp/keyman-version": "*", - "@keymanapp/kmc-package": "*", + "@keymanapp/common-types": "*", "@keymanapp/models-types": "*" }, "devDependencies": { diff --git a/developer/src/kmc-model-info/src/model-info-compiler.ts b/developer/src/kmc-model-info/src/model-info-compiler.ts index 0cfe663816b831a215d6d72c8e3ba4f417b595bf..7fd22308d557d9d0bb3ebec7064fbb87ffb1ced1 100644 --- a/developer/src/kmc-model-info/src/model-info-compiler.ts +++ b/developer/src/kmc-model-info/src/model-info-compiler.ts @@ -7,14 +7,14 @@ import * as fs from "fs"; import * as path from "path"; import { minKeymanVersion } from "./min-keyman-version.js"; import { ModelInfoFile } from "./model-info-file.js"; -import { type KmpJsonFile } from "@keymanapp/kmc-package"; +import { KmpJsonFile } from "@keymanapp/common-types"; export class ModelInfoOptions { /** The identifier for the model */ model_id: string; /** The data from the .kps file, transformed to kmp.json */ - kmpJsonData: KmpJsonFile; + kmpJsonData: KmpJsonFile.KmpJsonFile; /** The path in the keymanapp/lexical-models repo where this model may be found (optional) */ sourcePath?: string; diff --git a/developer/src/kmc-model-info/tsconfig.json b/developer/src/kmc-model-info/tsconfig.json index 83aa040f1a751e1b46f9a625e5ebaa46cd8fcdd1..1e6ba49b190f09623f7062e6b0abaf000d87662b 100644 --- a/developer/src/kmc-model-info/tsconfig.json +++ b/developer/src/kmc-model-info/tsconfig.json @@ -12,8 +12,7 @@ "src/**/*.ts" ], "references": [ - { "path": "../kmc-package" }, // can we eliminate this in future? - { "path": "../../../common/web/keyman-version/tsconfig.esm.json"}, + { "path": "../../../common/web/types" }, { "path": "../../../common/models/types" }, ] } diff --git a/developer/src/kmc-model/Makefile b/developer/src/kmc-model/Makefile index 353d923bd445a82056020bb49859e617054122c8..b41bbe81844c79856e4479de808232ef618bff98 100644 --- a/developer/src/kmc-model/Makefile +++ b/developer/src/kmc-model/Makefile @@ -4,7 +4,7 @@ !include ..\Defines.mak -# We don't depend on configure here because kmc-keyboard does that already +# We don't depend on configure here because kmc-ldml does that already build: .virtual $(GIT_BASH_FOR_KEYMAN) build.sh build diff --git a/developer/src/kmc-model/build.sh b/developer/src/kmc-model/build.sh index 8479610c0a337c95f0c6f160109d03500738844e..ecf58e20e478e40cdb2094d854acb523e483f261 100755 --- a/developer/src/kmc-model/build.sh +++ b/developer/src/kmc-model/build.sh @@ -1,33 +1,28 @@ #!/usr/bin/env bash -# -# Compiles the kmc lexical model compiler. -# - -# Exit on command failure and when using unset variables: -set -eu - ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" . "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh" ## END STANDARD BUILD SCRIPT INCLUDE -cd "$THIS_SCRIPT_PATH" - . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" + +cd "$THIS_SCRIPT_PATH" # TODO: "@/common/models/types" \ builder_describe "Build Keyman kmc Lexical Model Compiler module" \ "@/common/web/keyman-version" \ "@/developer/src/common/web/test-helpers" \ + "clean" \ "configure" \ "build" \ - "clean" \ "test" \ "pack build a local .tgz pack for testing" \ "publish publish to npm" \ "--dry-run,-n don't actually publish, just dry run" + builder_describe_outputs \ configure /node_modules \ build /developer/src/kmc-model/build/src/main.js @@ -36,44 +31,15 @@ builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- -if builder_start_action clean; then - rm -rf ./build/ ./tsconfig.tsbuildinfo - builder_finish_action success clean -fi - -#------------------------------------------------------------------------------------------------------------------- - -if builder_start_action configure; then - verify_npm_setup - builder_finish_action success configure -fi - -#------------------------------------------------------------------------------------------------------------------- - -if builder_start_action build; then - # Note: build-cjs only emits lexical-model-compiler.cjs at this time, as that - # is the only file required by other non-ES modules - # (common/web/input-processor tests) +function do_build() { mkdir -p build/cjs-src npm run build - builder_finish_action success build -fi +} -#------------------------------------------------------------------------------------------------------------------- - -if builder_start_action test; then - npm test - builder_finish_action success test -fi - -#------------------------------------------------------------------------------------------------------------------- +builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo +builder_run_action configure verify_npm_setup +builder_run_action build do_build +builder_run_action test npm test +builder_run_action publish builder_publish_to_npm +builder_run_action pack builder_publish_to_pack -if builder_start_action publish; then - . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" - builder_publish_to_npm - builder_finish_action success publish -elif builder_start_action pack; then - . "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" - builder_publish_to_pack - builder_finish_action success pack -fi diff --git a/developer/src/kmc-package/Makefile b/developer/src/kmc-package/Makefile index 02bf07dc025634e44b69d0784c7259050b461b7c..0a5ba6a1c10107a854a1170ce9ca317758f449b8 100644 --- a/developer/src/kmc-package/Makefile +++ b/developer/src/kmc-package/Makefile @@ -4,7 +4,7 @@ !include ..\Defines.mak -# We don't depend on configure here because kmc-keyboard does that already +# We don't depend on configure here because kmc-ldml does that already build: .virtual $(GIT_BASH_FOR_KEYMAN) build.sh build diff --git a/developer/src/kmc-package/build.sh b/developer/src/kmc-package/build.sh index 968b5fd241a61ca7a0132d727b0a546bcbc90fad..3206f60fda34cef55f689d2206c3229743836772 100755 --- a/developer/src/kmc-package/build.sh +++ b/developer/src/kmc-package/build.sh @@ -28,7 +28,7 @@ builder_describe "Build Keyman kmc Package Compiler module" \ "--dry-run,-n don't actually publish, just dry run" builder_describe_outputs \ configure /node_modules \ - build /developer/src/kmc-package/build/src/kmp-compiler.js + build /developer/src/kmc-package/build/src/main.js builder_parse "$@" #------------------------------------------------------------------------------------------------------------------- diff --git a/developer/src/kmc-package/package.json b/developer/src/kmc-package/package.json index 2183e3c20254a2107a97367c8c8c6290ae3d1ed5..1595dc21ecc47d6842584e31657a22c775f7514d 100644 --- a/developer/src/kmc-package/package.json +++ b/developer/src/kmc-package/package.json @@ -8,8 +8,7 @@ ], "type": "module", "exports": { - ".": "./build/src/kmp-compiler.js", - "./kmp-json-file": "./build/src/kmp-json-file.js" + ".": "./build/src/main.js" }, "files": [ "/build/src/" diff --git a/developer/src/kmc-package/src/compiler/keyman-targets.ts b/developer/src/kmc-package/src/compiler/keyman-targets.ts new file mode 100644 index 0000000000000000000000000000000000000000..14fc6572c4931446c8753bbbe981d04db31ca10e --- /dev/null +++ b/developer/src/kmc-package/src/compiler/keyman-targets.ts @@ -0,0 +1,53 @@ + +// +// This mirrors UKeymanTargets.pas +// + +export enum + KeymanTarget { + any = 'any', + windows = 'windows', macosx = 'macosx', linux = 'linux', + web = 'web', iphone = 'iphone', ipad = 'ipad', androidphone = 'androidphone', androidtablet = 'androidtablet', + mobile = 'mobile', desktop = 'desktop', tablet = 'tablet' + }; + +export const + // Note: if these change, update the copied values in KeyboardParser.pas accordingly + AllKeymanTargets: KeymanTarget[] = [ + KeymanTarget.any, + KeymanTarget.windows, KeymanTarget.macosx, KeymanTarget.linux, + KeymanTarget.web, KeymanTarget.iphone, KeymanTarget.ipad, KeymanTarget.androidphone, KeymanTarget.androidtablet, + KeymanTarget.mobile, KeymanTarget.desktop, KeymanTarget.tablet + ], + + TouchKeymanTargets: KeymanTarget[] = [ + KeymanTarget.iphone, KeymanTarget.ipad, KeymanTarget.androidphone, KeymanTarget.androidtablet, + KeymanTarget.mobile, KeymanTarget.tablet + ], + + // Compile to .kmx + KMXKeymanTargets: KeymanTarget[] = [ + KeymanTarget.windows, KeymanTarget.macosx, KeymanTarget.linux, KeymanTarget.desktop + ], + + // Compile to .js + KMWKeymanTargets: KeymanTarget[] = [ + KeymanTarget.web, KeymanTarget.iphone, KeymanTarget.ipad, KeymanTarget.androidphone, KeymanTarget.androidtablet, + KeymanTarget.mobile, KeymanTarget.tablet + ], + + // Supports .kvks + KeymanTargetsUsingKVK: KeymanTarget[] = [ + KeymanTarget.windows, KeymanTarget.macosx, KeymanTarget.linux, KeymanTarget.desktop, KeymanTarget.web + ], + + // Friendly names for targets, e.g. for documentation + + SKeymanTargetNames: { [index: string]: string } = { + [KeymanTarget.any]: 'All', + [KeymanTarget.windows]: 'Windows', [KeymanTarget.macosx]: 'macOS', [KeymanTarget.linux]: 'Linux', + [KeymanTarget.web]: 'Web', [KeymanTarget.iphone]: 'iPhone', [KeymanTarget.ipad]: 'iPad', + [KeymanTarget.androidphone]: 'Android phone', [KeymanTarget.androidtablet]: 'Android tablet', + [KeymanTarget.mobile]: 'Mobile devices', [KeymanTarget.desktop]: 'Desktop devices', + [KeymanTarget.tablet]: 'Tablet devices' + }; diff --git a/developer/src/kmc-package/src/kmp-compiler.ts b/developer/src/kmc-package/src/compiler/kmp-compiler.ts similarity index 57% rename from developer/src/kmc-package/src/kmp-compiler.ts rename to developer/src/kmc-package/src/compiler/kmp-compiler.ts index 4fd532b36817120a9de51c20b8dfbda245663623..198a1ef2de2823b43902867c517aa9b85aa66b91 100644 --- a/developer/src/kmc-package/src/kmp-compiler.ts +++ b/developer/src/kmc-package/src/compiler/kmp-compiler.ts @@ -2,36 +2,39 @@ import * as xml2js from 'xml2js'; import JSZip from 'jszip'; import KEYMAN_VERSION from "@keymanapp/keyman-version"; -import type { KpsFile, KpsFileContentFile, KpsFileInfo, KpsFileKeyboard, KpsFileLanguage, KpsFileLexicalModel, KpsFileOptions, KpsPackage } from './kps-file.js'; -import type { KmpJsonFile, KmpJsonFileInfo, KmpJsonFileLanguage, KmpJsonFileOptions } from './kmp-json-file.js'; import { CompilerCallbacks, KvkFile } from '@keymanapp/common-types'; import { CompilerMessages } from './messages.js'; -import { KMX, KmxFileReader } from '@keymanapp/common-types'; - -export { type KmpJsonFile } from './kmp-json-file.js'; +import { KmpJsonFile, KpsFile } from '@keymanapp/common-types'; +import { PackageVersionValidation } from './package-version-validation.js'; const FILEVERSION_KMP_JSON = '12.0'; -export default class KmpCompiler { +export class KmpCompiler { constructor(private callbacks: CompilerCallbacks) { } - public transformKpsToKmpObject(kpsFilename: string): KmpJsonFile { + public transformKpsToKmpObject(kpsFilename: string): KmpJsonFile.KmpJsonFile { // Load the KPS data from XML as JS structured data. - const data = this.callbacks.fs.readFileSync(kpsFilename, 'utf-8'); + const buffer = this.callbacks.loadFile(kpsFilename); + if(!buffer) { + this.callbacks.reportMessage(CompilerMessages.Error_FileDoesNotExist({filename: kpsFilename})); + return null; + } + const data = new TextDecoder().decode(buffer); const kpsPackage = (() => { - let a: KpsPackage; + let a: KpsFile.KpsPackage; let parser = new xml2js.Parser({ tagNameProcessors: [xml2js.processors.firstCharLowerCase], explicitArray: false }); - parser.parseString(data, (e: unknown, r: unknown) => { a = r as KpsPackage }); + // TODO: add unit test for xml errors parsing .kps file + parser.parseString(data, (e: unknown, r: unknown) => { if(e) throw e; a = r as KpsFile.KpsPackage }); return a; })(); - let kps: KpsFile = kpsPackage.package; + let kps: KpsFile.KpsFile = kpsPackage.package; // // To convert to kmp.json, we need to: @@ -46,7 +49,7 @@ export default class KmpCompiler { // Start to construct the kmp.json file from the .kps file - let kmp: KmpJsonFile = { + let kmp: KmpJsonFile.KmpJsonFile = { system: { fileVersion: FILEVERSION_KMP_JSON, keymanDeveloperVersion: KEYMAN_VERSION.VERSION @@ -58,7 +61,7 @@ export default class KmpCompiler { // Fill in additional fields // - let keys: [keyof KpsFileOptions, keyof KmpJsonFileOptions][] = [ + let keys: [keyof KpsFile.KpsFileOptions, keyof KmpJsonFile.KmpJsonFileOptions][] = [ ['executeProgram','executeProgram'], ['graphicFile', 'graphicFile'], ['msiFileName','msiFilename'], @@ -86,7 +89,7 @@ export default class KmpCompiler { // if(kps.files && kps.files.file) { - kmp.files = this.arrayWrap(kps.files.file).map((file: KpsFileContentFile) => { + kmp.files = this.arrayWrap(kps.files.file).map((file: KpsFile.KpsFileContentFile) => { return { name: file.name, description: file.description, @@ -108,16 +111,16 @@ export default class KmpCompiler { // if(kps.keyboards && kps.keyboards.keyboard) { - kmp.keyboards = this.arrayWrap(kps.keyboards.keyboard).map((keyboard: KpsFileKeyboard) => { - return { - displayFont: keyboard.displayFont ? this.callbacks.path.basename(keyboard.displayFont) : undefined, - oskFont: keyboard.oSKFont ? this.callbacks.path.basename(keyboard.oSKFont) : undefined, - name:keyboard.name, - id:keyboard.iD, - version:keyboard.version, - languages: this.kpsLanguagesToKmpLanguages(this.arrayWrap(keyboard.languages.language) as KpsFileLanguage[]) - }; - }); + kmp.keyboards = this.arrayWrap(kps.keyboards.keyboard).map((keyboard: KpsFile.KpsFileKeyboard) => ({ + displayFont: keyboard.displayFont ? this.callbacks.path.basename(keyboard.displayFont) : undefined, + oskFont: keyboard.oSKFont ? this.callbacks.path.basename(keyboard.oSKFont) : undefined, + name:keyboard.name, + id:keyboard.iD, + version:keyboard.version, + languages: keyboard.languages ? + this.kpsLanguagesToKmpLanguages(this.arrayWrap(keyboard.languages.language) as KpsFile.KpsFileLanguage[]) : + [] + })); } // @@ -125,20 +128,22 @@ export default class KmpCompiler { // if(kps.lexicalModels && kps.lexicalModels.lexicalModel) { - kmp.lexicalModels = this.arrayWrap(kps.lexicalModels.lexicalModel).map((model: KpsFileLexicalModel) => { - return { name:model.name, id:model.iD, languages: this.kpsLanguagesToKmpLanguages(this.arrayWrap(model.languages.language) as KpsFileLanguage[]) } - }); + kmp.lexicalModels = this.arrayWrap(kps.lexicalModels.lexicalModel).map((model: KpsFile.KpsFileLexicalModel) => ({ + name:model.name, + id:model.iD, + languages: model.languages ? + this.kpsLanguagesToKmpLanguages(this.arrayWrap(model.languages.language) as KpsFile.KpsFileLanguage[]) : [] + })); } // - // FollowKeyboardVersion support + // Verify version metadata; doing this in the transform + // while we have access to the .kps metadata, and keeping the // - if(kps.options?.followKeyboardVersion !== undefined) { - kmp.info.version = { - description: this.extractKeyboardVersionFromKmx(kpsFilename, kmp) - }; - // TODO: compare the extracted version with other keyboards in the package + const versionValidator = new PackageVersionValidation(this.callbacks); + if(!versionValidator.validateAndUpdateVersions(kpsFilename, kps, kmp)) { + return null; } // @@ -160,17 +165,17 @@ export default class KmpCompiler { kmp.strings = this.arrayWrap(kps.strings.string); } - kmp = this.stripUndefined(kmp) as KmpJsonFile; + kmp = this.stripUndefined(kmp) as KmpJsonFile.KmpJsonFile; return kmp; } // Helper functions - private kpsInfoToKmpInfo(info: KpsFileInfo): KmpJsonFileInfo { - let ni: KmpJsonFileInfo = {}; + private kpsInfoToKmpInfo(info: KpsFile.KpsFileInfo): KmpJsonFile.KmpJsonFileInfo { + let ni: KmpJsonFile.KmpJsonFileInfo = {}; - const keys: [(keyof KpsFileInfo), (keyof KmpJsonFileInfo)][] = [ + const keys: [(keyof KpsFile.KpsFileInfo), (keyof KmpJsonFile.KmpJsonFileInfo)][] = [ ['author','author'], ['copyright','copyright'], ['name','name'], @@ -195,71 +200,11 @@ export default class KmpCompiler { return [a]; }; - private kpsLanguagesToKmpLanguages(language: KpsFileLanguage[]): KmpJsonFileLanguage[] { + private kpsLanguagesToKmpLanguages(language: KpsFile.KpsFileLanguage[]): KmpJsonFile.KmpJsonFileLanguage[] { return language.map((element) => { return { name: element._, id: element.$.ID } }); }; - private extractKeyboardVersionFromKmx(kpsFilename: string, kmp: KmpJsonFile) { - // The DEFAULT_VERSION used to be '1.0', but we now use '0.0' to allow - // pre-release 0.x keyboards to be considered later than a keyboard without - // any version metadata at all. - const DEFAULT_VERSION = '0.0'; - // Note: there is often version metadata in the .kps element, but - // we don't read from the metadata because we want to ensure we have the - // most up-to-date keyboard version data here, from the compiled keyboard. - - // Lexical model packages do not allow FollowKeyboardVersion - if(kmp.lexicalModels && kmp.lexicalModels.length) { - this.callbacks.reportMessage(CompilerMessages.Error_FollowKeyboardVersionNotAllowedForModelPackages()); - return DEFAULT_VERSION; - } - - if(!kmp.keyboards || !kmp.keyboards.length) { - this.callbacks.reportMessage(CompilerMessages.Warn_FollowKeyboardVersionButNoKeyboards()); - return DEFAULT_VERSION; - } - - // Reset the keyboard version to the default in the kmp.json metadata, for - // warning/failure code paths in this file - kmp.keyboards[0].version = DEFAULT_VERSION; - - const file = kmp.files.find(file => this.callbacks.path.basename(file.name, '.kmx') == kmp.keyboards[0].id); - if(!file) { - this.callbacks.reportMessage(CompilerMessages.Error_KeyboardFileNotFound({id:kmp.keyboards[0].id})); - return DEFAULT_VERSION; - } - - const filename = this.callbacks.resolveFilename(kpsFilename, file.name); - if(!this.callbacks.fs.existsSync(filename)) { - // The zip phase will emit an error later if the file is missing, so - // we can just bail cleanly here - // console.debug(`The file ${filename} was not found`); - return DEFAULT_VERSION; - } - - // - // load the .kmx and extract the version number - // - const kmxFileData = this.callbacks.loadFile(filename); - const kmxReader: KmxFileReader = new KmxFileReader(); - const kmx: KMX.KEYBOARD = kmxReader.read(kmxFileData); - if(!kmx) { - // The file couldn't be read, it might be invalid or locked - this.callbacks.reportMessage(CompilerMessages.Error_KeyboardFileNotValid({filename})); - return DEFAULT_VERSION; - } - - const store = kmx.stores.find(store => store.dwSystemID == KMX.KMXFile.TSS_KEYBOARDVERSION); - if(!store) { - // We have no version number store, so use default version - this.callbacks.reportMessage(CompilerMessages.Warn_KeyboardFileHasNoKeyboardVersion({filename})); - return DEFAULT_VERSION; - } - - kmp.keyboards[0].version = store.dpString; - return store.dpString; - } private stripUndefined(o: any) { for(const key in o) { @@ -278,13 +223,13 @@ export default class KmpCompiler { * @param kpsFilename - Filename of the kps, not read, used only for calculating relative paths * @param kmpJsonData - The kmp.json Object */ - public buildKmpFile(kpsFilename: string, kmpJsonData: KmpJsonFile): Promise { + public buildKmpFile(kpsFilename: string, kmpJsonData: KmpJsonFile.KmpJsonFile): Promise { const zip = JSZip(); const kmpJsonFileName = 'kmp.json'; // Make a copy of kmpJsonData, as we mutate paths for writing - const data: KmpJsonFile = JSON.parse(JSON.stringify(kmpJsonData)); + const data: KmpJsonFile.KmpJsonFile = JSON.parse(JSON.stringify(kmpJsonData)); if(!data.files) { data.files = []; } @@ -345,9 +290,20 @@ export default class KmpCompiler { * we want that to remain the responsibility of the keyboard compiler, so we'll warn the * few users who are still doing this */ - private warnIfKvkFileIsNotBinary(filename: string, data: Buffer) { - // TODO: Buffer is not available on web - if(filename.match(/\.kvk$/) && data.compare(Buffer.from(KvkFile.KVK_HEADER_IDENTIFIER_BYTES), 0, 3, 0, 3) != 0) { + private warnIfKvkFileIsNotBinary(filename: string, data: Uint8Array) { + if(!filename.match(/\.kvk$/)) { + return; + } + + if(data.byteLength < 4) { + // TODO: Not a valid .kvk file; should we be reporting this? + return; + } + + if(data[0] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[0] || + data[1] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[1] || + data[2] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[2] || + data[3] != KvkFile.KVK_HEADER_IDENTIFIER_BYTES[3]) { this.callbacks.reportMessage(CompilerMessages.Warn_FileIsNotABinaryKvkFile({filename: filename})); } } diff --git a/developer/src/kmc-package/src/compiler/messages.ts b/developer/src/kmc-package/src/compiler/messages.ts new file mode 100644 index 0000000000000000000000000000000000000000..56727fea9bf61927ca7b2007df1a62103164c25b --- /dev/null +++ b/developer/src/kmc-package/src/compiler/messages.ts @@ -0,0 +1,118 @@ +import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types"; + +const Namespace = CompilerErrorNamespace.PackageCompiler; +const SevInfo = CompilerErrorSeverity.Info | Namespace; +// const SevHint = CompilerErrorSeverity.Hint | Namespace; +const SevWarn = CompilerErrorSeverity.Warn | Namespace; +const SevError = CompilerErrorSeverity.Error | Namespace; +const SevFatal = CompilerErrorSeverity.Fatal | Namespace; + +export class CompilerMessages { + static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, + `Unexpected exception: ${(o.e ?? 'unknown error').toString()}\n\nCall stack:\n${(o.e instanceof Error ? o.e.stack : (new Error()).stack)}`); + static FATAL_UnexpectedException = SevFatal | 0x0001; + + static Warn_AbsolutePath = (o:{filename: string}) => m(this.WARN_AbsolutePath, `File ${o.filename} has an absolute path, which is not portable.`); + static WARN_AbsolutePath = SevWarn | 0x0002; + + static Error_FileDoesNotExist = (o:{filename: string}) => m(this.ERROR_FileDoesNotExist, `File ${o.filename} does not exist.`); + static ERROR_FileDoesNotExist = SevError | 0x0003; + + static Error_FileCouldNotBeRead = (o:{filename: string; e: any}) => m(this.ERROR_FileCouldNotBeRead, + `File ${o.filename} could not be read: ${(o.e ?? 'unknown error').toString()}.`); + static ERROR_FileCouldNotBeRead = SevError | 0x0004; + + static Warn_FileIsNotABinaryKvkFile = (o:{filename: string}) => m(this.WARN_FileIsNotABinaryKvkFile, + `File ${o.filename} does not appear to be a valid binary .kvk file; this may be an old package that includes an xml-format .kvk file. `+ + `You must update the package to include the compiled .kvk file in the package.`); + static WARN_FileIsNotABinaryKvkFile = SevWarn | 0x0005; + + static Error_FollowKeyboardVersionNotAllowedForModelPackages = () => m(this.ERROR_FollowKeyboardVersionNotAllowedForModelPackages, + `FollowKeyboardVersion is not allowed in model packages`); + static ERROR_FollowKeyboardVersionNotAllowedForModelPackages = SevError | 0x0006; + + static Error_FollowKeyboardVersionButNoKeyboards = () => m(this.ERROR_FollowKeyboardVersionButNoKeyboards, + `FollowKeyboardVersion is set, but the package contains no keyboards`); + static ERROR_FollowKeyboardVersionButNoKeyboards = SevError | 0x0007; + + static Error_KeyboardContentFileNotFound = (o:{id:string}) => m(this.ERROR_KeyboardContentFileNotFound, + `Keyboard ${o.id} was listed in but a corresponding .kmx file was not found in `); + static ERROR_KeyboardContentFileNotFound = SevError | 0x0008; + + static Error_KeyboardFileNotValid = (o:{filename:string}) => m(this.ERROR_KeyboardFileNotValid, + `Keyboard file ${o.filename} is not a valid .kmx file`); + static ERROR_KeyboardFileNotValid = SevError | 0x0009; + + static Info_KeyboardFileHasNoKeyboardVersion = (o:{filename:string}) => m(this.INFO_KeyboardFileHasNoKeyboardVersion, + `Keyboard file ${o.filename} has no &KeyboardVersion store, using default '0.0'`); + static INFO_KeyboardFileHasNoKeyboardVersion = SevInfo | 0x000A; + + static Error_PackageCannotContainBothModelsAndKeyboards = () => m(this.ERROR_PackageCannotContainBothModelsAndKeyboards, + `The package contains both lexical models and keyboards, which is not permitted.`); + static ERROR_PackageCannotContainBothModelsAndKeyboards = SevError | 0x000B; + + static Warn_PackageShouldNotRepeatLanguages = (o:{resourceType: string, id: string, minimalTag: string, firstTag: string, secondTag: string}) => m(this.WARN_PackageShouldNotRepeatLanguages, + `Two language tags in ${o.resourceType} ${o.id}, '${o.firstTag}' and '${o.secondTag}', reduce to the same minimal tag '${o.minimalTag}'.`); + static WARN_PackageShouldNotRepeatLanguages = SevWarn | 0x000C; + + static Warn_PackageNameDoesNotFollowLexicalModelConventions = (o:{filename: string}) => m(this.WARN_PackageNameDoesNotFollowLexicalModelConventions, + `The package file ${o.filename} does not follow the recommended model filename conventions. The name should be all lower case, `+ + `include only alphanumeric characters and underscore (_), not start with a digit, and should have the structure `+ + `...model.kps.`); + static WARN_PackageNameDoesNotFollowLexicalModelConventions = SevWarn | 0x000D; + + static Warn_PackageNameDoesNotFollowKeyboardConventions = (o:{filename: string}) => m(this.WARN_PackageNameDoesNotFollowKeyboardConventions, + `The package file ${o.filename} does not follow the recommended keyboard filename conventions. The name should be all lower case, `+ + `include only alphanumeric characters and underscore (_), and not start with a digit.`); + static WARN_PackageNameDoesNotFollowKeyboardConventions = SevWarn | 0x000E; + + static Warn_FileInPackageDoesNotFollowFilenameConventions = (o:{filename: string}) => m(this.WARN_FileInPackageDoesNotFollowFilenameConventions, + `The file ${o.filename} does not follow the recommended filename conventions. The extension should be all lower case, `+ + `and the filename should include only alphanumeric characters, -, _, + and .`); + static WARN_FileInPackageDoesNotFollowFilenameConventions = SevWarn | 0x000F; + + static Error_PackageNameCannotBeBlank = () => m(this.ERROR_PackageNameCannotBeBlank, + `Package name cannot be an empty string.`); + static ERROR_PackageNameCannotBeBlank = SevError | 0x0010; + + static Error_KeyboardFileNotFound = (o:{filename:string}) => m(this.ERROR_KeyboardFileNotFound, + `Keyboard file ${o.filename} was not found. Has it been compiled?`); + static ERROR_KeyboardFileNotFound = SevError | 0x0011; + + static Warn_KeyboardVersionsDoNotMatch = (o: {keyboard:string, version:string, firstKeyboard:string, firstVersion:string}) => m(this.WARN_KeyboardVersionsDoNotMatch, + `Keyboard ${o.keyboard} version ${o.version} does not match keyboard ${o.firstKeyboard} version ${o.firstVersion}.`); + static WARN_KeyboardVersionsDoNotMatch = SevWarn | 0x0012; + + static Warn_KeyboardVersionsDoNotMatchPackageVersion = (o: {keyboard:string, keyboardVersion: string, packageVersion: string}) => m(this.WARN_KeyboardVersionsDoNotMatchPackageVersion, + `Keyboard ${o.keyboard} version ${o.keyboardVersion} does not match package version ${o.packageVersion}.`); + static WARN_KeyboardVersionsDoNotMatchPackageVersion = SevWarn | 0x0013; + + static Error_LanguageTagIsNotValid = (o: {resourceType: string, id:string, lang:string, e:any}) => m(this.ERROR_LanguageTagIsNotValid, + `Language tag '${o.lang}' in ${o.resourceType} ${o.id} is invalid.`); + static ERROR_LanguageTagIsNotValid = SevError | 0x0014; + + static Warn_LanguageTagIsNotMinimal = (o: {resourceType: string, id:string, actual:string, expected:string}) => m(this.WARN_LanguageTagIsNotMinimal, + `Language tag '${o.actual}' in ${o.resourceType} ${o.id} is not minimal, and should be '${o.expected}'.`); + static WARN_LanguageTagIsNotMinimal = SevWarn | 0x0015; + + static Error_MustHaveAtLeastOneLanguage = (o:{resourceType:string, id:string}) => m(this.ERROR_MustHaveAtLeastOneLanguage, + `The ${o.resourceType} ${o.id} must have at least one language specified.`); + static ERROR_MustHaveAtLeastOneLanguage = SevError | 0x0016; + + static Warn_RedistFileShouldNotBeInPackage = (o:{filename:string}) => m(this.WARN_RedistFileShouldNotBeInPackage, + `The Keyman system file '${o.filename}' should not be compiled into the package.`); + static WARN_RedistFileShouldNotBeInPackage = SevWarn | 0x0017; + + static Warn_DocFileDangerous = (o:{filename:string}) => m(this.WARN_DocFileDangerous, + `Microsoft Word .doc or .docx files ('${o.filename}') are not portable. You should instead use HTML or PDF format.`); + static WARN_DocFileDangerous = SevWarn | 0x0018; + + static Error_PackageMustContainAModelOrAKeyboard = () => m(this.ERROR_PackageMustContainAModelOrAKeyboard, + `Package must contain a lexical model or a keyboard.`); + static ERROR_PackageMustContainAModelOrAKeyboard = SevError | 0x0019; + + static Warn_JsKeyboardFileIsMissing = (o:{id: string}) => m(this.WARN_JsKeyboardFileIsMissing, + `Keyboard ${o.id} targets touch devices but corresponding ${o.id}.js file is not in the package.`); + static WARN_JsKeyboardFileIsMissing = SevWarn | 0x001A; +} + diff --git a/developer/src/kmc-package/src/compiler/package-validation.ts b/developer/src/kmc-package/src/compiler/package-validation.ts new file mode 100644 index 0000000000000000000000000000000000000000..d4317a3da012fdbf8838149daf785022552d6e35 --- /dev/null +++ b/developer/src/kmc-package/src/compiler/package-validation.ts @@ -0,0 +1,186 @@ +import { KmpJsonFile, CompilerCallbacks } from '@keymanapp/common-types'; +import { CompilerMessages } from './messages.js'; +import { keymanEngineForWindowsFiles, keymanForWindowsInstallerFiles, keymanForWindowsRedistFiles } from './redist-files.js'; + +// const SLexicalModelExtension = '.model.js'; + +// The keyboard ID SHOULD adhere to this pattern: +const KEYBOARD_ID_PATTERN_PACKAGE = /^[a-z_][a-z0-9_]*\.(kps|kmp)$/; + +// The model ID SHOULD adhere to this pattern: +// author .bcp47 .uniq +const MODEL_ID_PATTERN_PACKAGE = /^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.(kps|kmp)$/; +// const MODEL_ID_PATTERN_JS = /^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.js$/; +// const MODEL_ID_PATTERN_TS = /^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.ts$/; +// const MODEL_ID_PATTERN_PROJECT = /^[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_-]*\.[a-z_][a-z0-9_]*\.model\.kpj$/; + +// "Content files" within the package should adhere to these pattern: +const CONTENT_FILE_BASENAME_PATTERN = /^[a-z0-9_+.-]+$/; +const CONTENT_FILE_EXTENSION_PATTERN = /^\.[a-z0-9_]+$/; + +export class PackageValidation { + + constructor(private callbacks: CompilerCallbacks) { + } + + public validate(filename: string, kmpJson: KmpJsonFile.KmpJsonFile) { + if(!this.checkForModelsAndKeyboardsInSamePackage(kmpJson)) { + return false; + } + if(!this.checkKeyboards(filename, kmpJson)) { + return false; + } + if(!this.checkLexicalModels(filename, kmpJson)) { + return false; + } + if(!this.checkContentFiles(kmpJson)) { + return false; + } + if(!this.checkPackageInfo(kmpJson)) { + return false; + } + return true; + } + + private checkForDuplicatedOrNonMinimalLanguages(resourceType: 'keyboard'|'model', id: string, languages: KmpJsonFile.KmpJsonFileLanguage[]): boolean { + let minimalTags: {[tag: string]: string} = {}; + + if(languages.length == 0) { + this.callbacks.reportMessage(CompilerMessages.Error_MustHaveAtLeastOneLanguage({resourceType, id})); + return false; + } + + for(let lang of languages) { + let locale; + try { + locale = new Intl.Locale(lang.id); + } catch(e: any) { + this.callbacks.reportMessage(CompilerMessages.Error_LanguageTagIsNotValid({resourceType, id, lang: lang.id, e})); + return false; + } + + const minimalTag = locale.minimize().toString(); + + if(minimalTag.toLowerCase() !== lang.id.toLowerCase()) { + this.callbacks.reportMessage(CompilerMessages.Warn_LanguageTagIsNotMinimal({resourceType, id, actual: lang.id, expected: minimalTag})); + } + + if(minimalTags[minimalTag]) { + this.callbacks.reportMessage(CompilerMessages.Warn_PackageShouldNotRepeatLanguages({resourceType, id, minimalTag, firstTag: lang.id, secondTag: minimalTags[minimalTag]})); + } + else { + minimalTags[minimalTag] = lang.id; + } + } + + return true; + } + + private checkForModelsAndKeyboardsInSamePackage(kmpJson: KmpJsonFile.KmpJsonFile): boolean { + if(kmpJson.lexicalModels?.length > 0 && kmpJson.keyboards?.length > 0) { + this.callbacks.reportMessage(CompilerMessages.Error_PackageCannotContainBothModelsAndKeyboards()); + return false; + } + + if(!kmpJson.lexicalModels?.length && !kmpJson.keyboards?.length) { + // Note: we require at least 1 keyboard or model in the package. This may + // change in the future if we start to use packages to distribute, e.g. + // localizations or themes. + this.callbacks.reportMessage(CompilerMessages.Error_PackageMustContainAModelOrAKeyboard()); + return false; + } + + + + return true; + } + + private checkLexicalModels(filename: string, kmpJson: KmpJsonFile.KmpJsonFile): boolean { + if(!kmpJson.lexicalModels || kmpJson.lexicalModels.length == 0) { + return true; + } + + filename = this.callbacks.path.basename(filename); + + if(!MODEL_ID_PATTERN_PACKAGE.test(filename)) { + this.callbacks.reportMessage(CompilerMessages.Warn_PackageNameDoesNotFollowLexicalModelConventions({filename})); + } + + for(let model of kmpJson.lexicalModels) { + if(!this.checkForDuplicatedOrNonMinimalLanguages('model', model.id, model.languages)) { + return false; + } + } + + return true; + } + + private checkKeyboards(filename: string, kmpJson: KmpJsonFile.KmpJsonFile): boolean { + if(!kmpJson.keyboards || kmpJson.keyboards.length == 0) { + return true; + } + + filename = this.callbacks.path.basename(filename); + + if(!KEYBOARD_ID_PATTERN_PACKAGE.test(filename)) { + this.callbacks.reportMessage(CompilerMessages.Warn_PackageNameDoesNotFollowKeyboardConventions({filename})); + } + + for(let keyboard of kmpJson.keyboards) { + if(!this.checkForDuplicatedOrNonMinimalLanguages('keyboard', keyboard.id, keyboard.languages)) { + return false; + } + // Note: package-version-validation verifies that there is a corresponding + // content file for each keyboard + } + + return true; + } + + private checkContentFiles(kmpJson: KmpJsonFile.KmpJsonFile): boolean { + for(let file of kmpJson.files) { + if(!this.checkContentFile(file)) { + return false; + } + } + + return true; + } + + private checkContentFile(file: KmpJsonFile.KmpJsonFileContentFile): boolean { + const filename = this.callbacks.path.basename(file.name); + const ext = this.callbacks.path.extname(filename); + const base = filename.substring(0, filename.length-ext.length); + if(!CONTENT_FILE_BASENAME_PATTERN.test(base) || !CONTENT_FILE_EXTENSION_PATTERN.test(ext)) { + this.callbacks.reportMessage(CompilerMessages.Warn_FileInPackageDoesNotFollowFilenameConventions({filename})); + } + + if(!this.checkIfContentFileIsDangerous(file)) { + return false; + } + + return true; + } + + private checkIfContentFileIsDangerous(file: KmpJsonFile.KmpJsonFileContentFile): boolean { + let filename = this.callbacks.path.basename(file.name).toLowerCase(); + if(keymanForWindowsInstallerFiles.includes(filename) || + keymanForWindowsRedistFiles.includes(filename) || + keymanEngineForWindowsFiles.includes(filename)) { + this.callbacks.reportMessage(CompilerMessages.Warn_RedistFileShouldNotBeInPackage({filename})); + } + if(filename.match(/\.doc(x?)$/)) { + this.callbacks.reportMessage(CompilerMessages.Warn_DocFileDangerous({filename})); + } + return true; + } + + private checkPackageInfo(file: KmpJsonFile.KmpJsonFile) { + if(!file.info || !file.info.name || !file.info.name.description.trim()) { + this.callbacks.reportMessage(CompilerMessages.Error_PackageNameCannotBeBlank()); + return false; + } + + return true; + } +} diff --git a/developer/src/kmc-package/src/compiler/package-version-validation.ts b/developer/src/kmc-package/src/compiler/package-version-validation.ts new file mode 100644 index 0000000000000000000000000000000000000000..4f8ad4a41c1d3b232ffe3ee244f76f7b347ec96e --- /dev/null +++ b/developer/src/kmc-package/src/compiler/package-version-validation.ts @@ -0,0 +1,185 @@ +import { KmpJsonFile, CompilerCallbacks, KpsFile, KmxFileReader, KMX } from '@keymanapp/common-types'; +import { KeymanTarget, TouchKeymanTargets } from './keyman-targets.js'; +import { CompilerMessages } from './messages.js'; + +// The DEFAULT_VERSION used to be '1.0', but we now use '0.0' to allow +// pre-release 0.x keyboards to be considered later than a keyboard without +// any version metadata at all. +// TODO: move to a better location +const DEFAULT_VERSION = '0.0'; + +export class PackageVersionValidation { + + constructor(private callbacks: CompilerCallbacks) {} + + /** + * Verifies version information in corresponding keyboards and updates kmpJson + * metadata as the version information can be out of sync in the .kps file + * after updating a contained keyboard. + * @param kpsFilename + * @param kps + * @param kmp + * @returns + */ + public validateAndUpdateVersions(kpsFilename: string, kps: KpsFile.KpsFile, kmp: KmpJsonFile.KmpJsonFile) { + const followKeyboardVersion = kps.options?.followKeyboardVersion !== undefined; + + if(followKeyboardVersion) { + if(!this.checkFollowKeyboardVersion(kps, kmp)) { + return false; + } + } + + if(!kmp.keyboards) { + // Lexical models don't have version metadata; only their packages. + return true; + } + + let result = true; + + // We now know we have at least one keyboard in the package + + for(let keyboard of kmp.keyboards) { + const kmx = this.getKmxData(kpsFilename, kmp, keyboard); + if(!kmx) { + // Warnings or errors will have been raised by getKmxData + result = false; + continue; + } + + // get the targets from the .kmx + this.verifyTargets(keyboard, kmx, kmp); + + // Note: there is often version metadata in the .kps element, but + // we don't read from the metadata because we want to ensure we have the + // most up-to-date keyboard version data here, from the compiled keyboard. + let keyboardVersion = this.getKeyboardVersionFromKmx(kmx); + if(followKeyboardVersion && keyboardVersion === null) { + this.callbacks.reportMessage(CompilerMessages.Info_KeyboardFileHasNoKeyboardVersion({filename: keyboard.id})); + } + keyboard.version = keyboardVersion ?? DEFAULT_VERSION; + + if(result) { + if(kmp.keyboards[0].version !== keyboard.version) { + this.callbacks.reportMessage(CompilerMessages.Warn_KeyboardVersionsDoNotMatch({ + keyboard: keyboard.id, + version: keyboard.version, + firstKeyboard: kmp.keyboards[0].id, + firstVersion: kmp.keyboards[0].version + })); + } + } + } + + if(result) { + if(followKeyboardVersion) { + kmp.info.version.description = kmp.keyboards[0].version; + } + else if(kmp.info.version?.description != kmp.keyboards[0].version) { + // Only need to compare against first keyboard as we compare keyboards above + this.callbacks.reportMessage(CompilerMessages.Warn_KeyboardVersionsDoNotMatchPackageVersion({ + keyboard: kmp.keyboards[0].id, + keyboardVersion: kmp.keyboards[0].version, + packageVersion: kmp.info.version?.description + })); + } + } + + return result; + } + + private checkFollowKeyboardVersion(kps: KpsFile.KpsFile, kmp: KmpJsonFile.KmpJsonFile) { + // Lexical model packages do not allow FollowKeyboardVersion + if(kmp.lexicalModels && kmp.lexicalModels.length) { + this.callbacks.reportMessage(CompilerMessages.Error_FollowKeyboardVersionNotAllowedForModelPackages()); + return false; + } + + if(!kmp.keyboards || !kmp.keyboards.length) { + this.callbacks.reportMessage(CompilerMessages.Error_FollowKeyboardVersionButNoKeyboards()); + return false; + } + + return true; + } + + private getKmxData( + kpsFilename: string, + kmp: KmpJsonFile.KmpJsonFile, + keyboard: KmpJsonFile.KmpJsonFileKeyboard + ): KMX.KEYBOARD { + + const file = kmp.files.find(file => this.callbacks.path.basename(file.name, '.kmx') == keyboard.id); + if(!file) { + this.callbacks.reportMessage(CompilerMessages.Error_KeyboardContentFileNotFound({id:keyboard.id})); + return null; + } + + const filename = this.callbacks.resolveFilename(kpsFilename, file.name); + if(!this.callbacks.fs.existsSync(filename)) { + this.callbacks.reportMessage(CompilerMessages.Error_KeyboardFileNotFound({filename})); + return null; + } + + // + // load the .kmx and extract the version number + // + let kmxFileData; + try { + kmxFileData = this.callbacks.loadFile(filename); + } catch(e) { + this.callbacks.reportMessage(CompilerMessages.Error_FileCouldNotBeRead({filename, e})); + return null; + } + const kmxReader: KmxFileReader = new KmxFileReader(); + const kmx: KMX.KEYBOARD = kmxReader.read(kmxFileData); + if(!kmx) { + // The file couldn't be read, it might not be a .kmx file + this.callbacks.reportMessage(CompilerMessages.Error_KeyboardFileNotValid({filename})); + return null; + } + + return kmx; + } + + private getKeyboardVersionFromKmx( + kmx: KMX.KEYBOARD + ): string { + const store = kmx.stores.find(store => store.dwSystemID == KMX.KMXFile.TSS_KEYBOARDVERSION); + return store ? store.dpString : null; + } + + /** + * Verify that the package contains a .js if the keyboard targets touch + * devices + */ + private verifyTargets( + keyboard: KmpJsonFile.KmpJsonFileKeyboard, + kmx: KMX.KEYBOARD, + kmp: KmpJsonFile.KmpJsonFile + ): void { + // TODO: this function doesn't really belong in version validation, but we + // don't want to read .kmx twice so we'll leave it here for now; a future + // refactor could move .kmx loading into another class + + const store = kmx.stores.find(store => store.dwSystemID == KMX.KMXFile.TSS_TARGETS); + if(!store) { + // If there is no &TARGETS store, then there is nothing to check, + // because default is windows-only + return; + } + + // Note, if we have gotten this far, we've already located and loaded a + // .kmx, so no need to verify that the package includes a .kmx + + // If at least one target is a touch target, we need to check that the + // package also includes the .js + const targets = store.dpString.split(' '); + if(targets.some(target => ['any', ...TouchKeymanTargets].includes(target))) { + if(!kmp.files.find(file => this.callbacks.path.basename(file.name, '.js') == keyboard.id)) { + // .js version of the keyboard is not found, warn + this.callbacks.reportMessage(CompilerMessages.Warn_JsKeyboardFileIsMissing({id: keyboard.id})); + } + } + } +} diff --git a/developer/src/kmc-package/src/compiler/redist-files.ts b/developer/src/kmc-package/src/compiler/redist-files.ts new file mode 100644 index 0000000000000000000000000000000000000000..9fccb32944a9aaa8d6d38dde47d70c7b0681c704 --- /dev/null +++ b/developer/src/kmc-package/src/compiler/redist-files.ts @@ -0,0 +1,64 @@ + +/** + * This is a set of known redistributable files for Keyman for Windows that + * should not be included in packages. It is not critical that this list matches + * the current deployment; it is just for warning against accidental inclusion + * of these files by package authors. Some redistributable files have been + * intentionally excluded because they could legitimately be a different file + * with the same name. + * + * This matches behaviour from the legacy package compiler; we may want to + * reconsider how this is done in the future. + * + * These lists have been constructed from 17.0.109 alpha build. Filenames + * intentionally in lower case. + */ + +export const + keymanForWindowsInstallerFiles: string[] = [ + 'keymandesktop.msi', + 'keymanengine.msm' + ]; + + +export const + keymanEngineForWindowsFiles: string[] = [ + 'base.xslt', + 'crashpad_handler.exe', + 'keyman-debug-etw.man', + 'keyman.exe', + 'keyman32.dll', + 'keyman64.dll', + 'keymanmc.dll', + 'keymanx64.exe', + 'kmcomapi.dll', + 'kmcomapi.x64.dll', + 'kmrefresh.x64.exe', + 'kmrefresh.x86.exe', + 'kmtip.dll', + 'kmtip64.dll', + 'mcompile.exe', + 'sentry.dll', + 'sentry.x64.dll', + 'si_browsers.xslt', + 'si_fonts.xslt', + 'si_hookdlls.xslt', + 'si_keyman.xslt', + 'si_language.xslt', + 'si_office.xslt', + 'si_overview.xslt', + 'si_processes.xslt', + 'si_processes_x64.xslt', + 'si_startup.xslt', + 'tsysinfo.exe', + ]; + +export const + keymanForWindowsRedistFiles: string[] = [ + 'desktop_resources.dll', + 'keymandesktop.chm', + 'kmbrowserhost.exe', + 'kmconfig.exe', + 'kmshell.exe', + 'unicodedata.mdb', + ]; diff --git a/developer/src/kmc-package/src/main.ts b/developer/src/kmc-package/src/main.ts new file mode 100644 index 0000000000000000000000000000000000000000..5903a59de3055ef20bf5ac4b086c43f72d840ba7 --- /dev/null +++ b/developer/src/kmc-package/src/main.ts @@ -0,0 +1,2 @@ +export { KmpCompiler } from "./compiler/kmp-compiler.js"; +export { PackageValidation } from "./compiler/package-validation.js"; \ No newline at end of file diff --git a/developer/src/kmc-package/src/messages.ts b/developer/src/kmc-package/src/messages.ts deleted file mode 100644 index 3d2ba224342e68cef60475fb7bcc1996992bd5dd..0000000000000000000000000000000000000000 --- a/developer/src/kmc-package/src/messages.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types"; - -const Namespace = CompilerErrorNamespace.PackageCompiler; -// const SevInfo = CompilerErrorSeverity.Info | Namespace; -// const SevHint = CompilerErrorSeverity.Hint | Namespace; -const SevWarn = CompilerErrorSeverity.Warn | Namespace; -const SevError = CompilerErrorSeverity.Error | Namespace; -const SevFatal = CompilerErrorSeverity.Fatal | Namespace; - -export class CompilerMessages { - static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, - `Unexpected exception: ${(o.e ?? 'unknown error').toString()}\n\nCall stack:\n${(o.e instanceof Error ? o.e.stack : (new Error()).stack)}`); - static FATAL_UnexpectedException = SevFatal | 0x0001; - - static Warn_AbsolutePath = (o:{filename: string}) => m(this.WARN_AbsolutePath, `File ${o.filename} has an absolute path, which is not portable.`); - static WARN_AbsolutePath = SevWarn | 0x0002; - - static Error_FileDoesNotExist = (o:{filename: string}) => m(this.ERROR_FileDoesNotExist, `File ${o.filename} does not exist.`); - static ERROR_FileDoesNotExist = SevError | 0x0003; - - static Error_FileCouldNotBeRead = (o:{filename: string; e: any}) => m(this.ERROR_FileCouldNotBeRead, - `File ${o.filename} could not be read: ${(o.e ?? 'unknown error').toString()}.`); - static ERROR_FileCouldNotBeRead = SevError | 0x0004; - - static Warn_FileIsNotABinaryKvkFile = (o:{filename: string}) => m(this.WARN_FileIsNotABinaryKvkFile, - `File ${o.filename} does not appear to be a valid binary .kvk file; this may be an old package that includes an xml-format .kvk file. `+ - `You must update the package to include the compiled .kvk file in the package.`); - static WARN_FileIsNotABinaryKvkFile = SevWarn | 0x0005; - - static Error_FollowKeyboardVersionNotAllowedForModelPackages = () => m(this.ERROR_FollowKeyboardVersionNotAllowedForModelPackages, - `FollowKeyboardVersion is not allowed in model packages`); - static ERROR_FollowKeyboardVersionNotAllowedForModelPackages = SevError | 0x0006; - - static Warn_FollowKeyboardVersionButNoKeyboards = () => m(this.WARN_FollowKeyboardVersionButNoKeyboards, - `FollowKeyboardVersion is set, but the package contains no keyboards`); - static WARN_FollowKeyboardVersionButNoKeyboards = SevWarn | 0x0007; - - static Error_KeyboardFileNotFound = (o:{id:string}) => m(this.ERROR_KeyboardFileNotFound, - `Keyboard ${o.id} was listed in but a corresponding .kmx file was not found in `); - static ERROR_KeyboardFileNotFound = SevError | 0x0008; - - static Error_KeyboardFileNotValid = (o:{filename:string}) => m(this.ERROR_KeyboardFileNotValid, - `Keyboard file ${o.filename} is not a valid .kmx file`); - static ERROR_KeyboardFileNotValid = SevError | 0x0009; - - static Warn_KeyboardFileHasNoKeyboardVersion = (o:{filename:string}) => m(this.WARN_KeyboardFileHasNoKeyboardVersion, - `Keyboard file ${o.filename} has no &KeyboardVersion store`); - static WARN_KeyboardFileHasNoKeyboardVersion = SevWarn | 0x000A; - - -} - diff --git a/developer/src/kmc-package/test/fixtures/binary_kvk_file/source/binary_kvk_file.kps b/developer/src/kmc-package/test/fixtures/binary_kvk_file/source/binary_kvk_file.kps index d98764ea862df10bd0e4cea3de368655b794dc71..de17212a8451980f4c24b2e8cd46c723eef86677 100644 --- a/developer/src/kmc-package/test/fixtures/binary_kvk_file/source/binary_kvk_file.kps +++ b/developer/src/kmc-package/test/fixtures/binary_kvk_file/source/binary_kvk_file.kps @@ -17,6 +17,7 @@ Binary KVK File + 1.0 @@ -25,5 +26,21 @@ 0 .kvk + + ../../invalid/basic.kmx + Keyboard Basic + 0 + .kmx + + + + Basic + basic + 1.0 + + Khmer + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowKeyboardConventions.kps b/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowKeyboardConventions.kps new file mode 100644 index 0000000000000000000000000000000000000000..70f58d779b399f349a59a1d32d6a5e1c00502c2c --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowKeyboardConventions.kps @@ -0,0 +1,32 @@ + + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowLexicalModelConventions.kps b/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowLexicalModelConventions.kps new file mode 100644 index 0000000000000000000000000000000000000000..674ad85a2f055e9cf87e944bc9d4198469b77f4c --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/WARN_PackageNameDoesNotFollowLexicalModelConventions.kps @@ -0,0 +1,32 @@ + + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0.3 + + + + example.qaa.sencoten.model.js + Lexical model example.qaa.sencoten.model.js + 0 + .model.js + + + + + SENĆOŦEN dictionary + example.qaa.sencoten + + North Straits Salish + SENĆOŦEN + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/basic.kmn b/developer/src/kmc-package/test/fixtures/invalid/basic.kmn new file mode 100644 index 0000000000000000000000000000000000000000..48705f6b9939ae0229a0892fe352502d1a31ffa0 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/basic.kmn @@ -0,0 +1,6 @@ +store(&name) 'basic desktop-only kmx' +store(&keyboardversion) '1.0' + +begin unicode > use(main) + +group(main) using keys \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/invalid/basic.kmx b/developer/src/kmc-package/test/fixtures/invalid/basic.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bd6b832cab772d03a45460e6c982d79772a9ab6e Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/basic.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_language_tag_is_not_valid.kps b/developer/src/kmc-package/test/fixtures/invalid/error_language_tag_is_not_valid.kps new file mode 100644 index 0000000000000000000000000000000000000000..06d95266512afa80722fc23b1506d5350cf559de --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/error_language_tag_is_not_valid.kps @@ -0,0 +1,32 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + + English (Australian script) as spoken in Latin + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_package_cannot_contain_both_models_and_keyboards.kps b/developer/src/kmc-package/test/fixtures/invalid/error_package_cannot_contain_both_models_and_keyboards.kps new file mode 100644 index 0000000000000000000000000000000000000000..6483fe1c10c2ae5964920eb0d992446e5b3f667f --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/error_package_cannot_contain_both_models_and_keyboards.kps @@ -0,0 +1,49 @@ + + + + 15.0.266.0 + 7.0 + + + Khmer Angkor + © 2015-2022 SIL International + Makara Sok + 1.0 + https://keyman.com/keyboards/khmer_angkor + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + example.qaa.sencoten.model.js + Lexical model example.qaa.sencoten.model.js + 0 + .model.js + + + + + + SENĆOŦEN dictionary + example.qaa.sencoten + + North Straits Salish + SENĆOŦEN + + + + + + Basic + basic + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_package_must_contain_a_model_or_a_keyboard.kps b/developer/src/kmc-package/test/fixtures/invalid/error_package_must_contain_a_model_or_a_keyboard.kps new file mode 100644 index 0000000000000000000000000000000000000000..335201a6e5b0d47456237139691a32e24175bddd --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/error_package_must_contain_a_model_or_a_keyboard.kps @@ -0,0 +1,21 @@ + + + + 15.0.266.0 + 7.0 + + + error_package_must_contain_a_model_or_a_keyboard + 1.3 + + + + + khmer_angkor.kmn + Keyboard Khmer Angkor + 0 + .kmn + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank.kps b/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank.kps new file mode 100644 index 0000000000000000000000000000000000000000..5b7d6d3d46a07fe429c515b5431b3a2977ff49e8 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank.kps @@ -0,0 +1,32 @@ + + + + 15.0.266.0 + 7.0 + + + + + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank_2.kps b/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank_2.kps new file mode 100644 index 0000000000000000000000000000000000000000..f9beefebb986641a4b95ba5a7916c68be15053df --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/error_package_name_cannot_be_blank_2.kps @@ -0,0 +1,31 @@ + + + + 15.0.266.0 + 7.0 + + + + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/keyboardcontentfilenotfound.kps b/developer/src/kmc-package/test/fixtures/invalid/keyboardcontentfilenotfound.kps new file mode 100644 index 0000000000000000000000000000000000000000..cfcd52789453f18921025aa19b9f773280ee8ac8 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/keyboardcontentfilenotfound.kps @@ -0,0 +1,128 @@ + + + + 15.0.266.0 + 7.0 + + + + readme.htm + splash.gif + + + + + + + + + + Khmer Angkor + © 2015-2022 SIL International + Makara Sok + + https://keyman.com/keyboards/khmer_angkor + + + + + ..\build\khmer_angkor.js + File khmer_angkor.js + 0 + .js + + + ..\build\khmer_angkor.kvk + File khmer_angkor.kvk + 0 + .kvk + + + welcome\keyboard_layout.png + File keyboard_layout.png + 0 + .png + + + welcome\welcome.htm + File welcome.htm + 0 + .htm + + + ..\shared\fonts\khmer\mondulkiri\FONTLOG.txt + File FONTLOG.txt + 0 + .txt + + + ..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf + Font Khmer Mondulkiri + 0 + .ttf + + + ..\shared\fonts\khmer\mondulkiri\OFL.txt + File OFL.txt + 0 + .txt + + + ..\shared\fonts\khmer\mondulkiri\OFL-FAQ.txt + File OFL-FAQ.txt + 0 + .txt + + + welcome\KAK_Documentation_EN.pdf + File KAK_Documentation_EN.pdf + 0 + .pdf + + + welcome\KAK_Documentation_KH.pdf + File KAK_Documentation_KH.pdf + 0 + .pdf + + + readme.htm + File readme.htm + 0 + .htm + + + welcome\image002.png + File image002.png + 0 + .png + + + ..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf + Font KhmerBusraKbd + 0 + .ttf + + + splash.gif + File splash.gif + 0 + .gif + + + + + Khmer Angkor + khmer_angkor + 1.3 + ..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf + ..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf + + Central Khmer (Khmer, Cambodia) + + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotfound.kps b/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotfound.kps index 224dd52042cb6ca1f1a94cddc473b68806a69795..cf6e49053a507a1e98ee8125e4b94c23ec08d924 100644 --- a/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotfound.kps +++ b/developer/src/kmc-package/test/fixtures/invalid/keyboardfilenotfound.kps @@ -5,11 +5,6 @@ 7.0 - - readme.htm - splash.gif - - @@ -25,97 +20,18 @@ - ..\build\khmer_angkor.js - File khmer_angkor.js + + keyboardfilenotfound.kmx + Keyboard Khmer Angkor 0 - .js - - - ..\build\khmer_angkor.kvk - File khmer_angkor.kvk - 0 - .kvk - - - welcome\keyboard_layout.png - File keyboard_layout.png - 0 - .png - - - welcome\welcome.htm - File welcome.htm - 0 - .htm - - - ..\shared\fonts\khmer\mondulkiri\FONTLOG.txt - File FONTLOG.txt - 0 - .txt - - - ..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf - Font Khmer Mondulkiri - 0 - .ttf - - - ..\shared\fonts\khmer\mondulkiri\OFL.txt - File OFL.txt - 0 - .txt - - - ..\shared\fonts\khmer\mondulkiri\OFL-FAQ.txt - File OFL-FAQ.txt - 0 - .txt - - - welcome\KAK_Documentation_EN.pdf - File KAK_Documentation_EN.pdf - 0 - .pdf - - - welcome\KAK_Documentation_KH.pdf - File KAK_Documentation_KH.pdf - 0 - .pdf - - - readme.htm - File readme.htm - 0 - .htm - - - welcome\image002.png - File image002.png - 0 - .png - - - ..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf - Font KhmerBusraKbd - 0 - .ttf - - - splash.gif - File splash.gif - 0 - .gif + .kmx Khmer Angkor - khmer_angkor + keyboardfilenotfound 1.3 - ..\shared\fonts\khmer\busrakbd\khmer_busra_kbd.ttf - ..\shared\fonts\khmer\mondulkiri\Mondulkiri-R.ttf Central Khmer (Khmer, Cambodia) diff --git a/developer/src/kmc-package/test/fixtures/invalid/keyman.en.error_must_have_at_least_one_language.model.kps b/developer/src/kmc-package/test/fixtures/invalid/keyman.en.error_must_have_at_least_one_language.model.kps new file mode 100644 index 0000000000000000000000000000000000000000..b79330f06f1ec885b6e5a55f6d2901c7086a39f2 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/keyman.en.error_must_have_at_least_one_language.model.kps @@ -0,0 +1,28 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0.3 + + + + example.qaa.sencoten.model.js + Lexical model example.qaa.sencoten.model.js + 0 + .model.js + + + + + SENĆOŦEN dictionary + example.qaa.sencoten + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/keyman.en.warn_package_should_not_repeat_languages.model.kps b/developer/src/kmc-package/test/fixtures/invalid/keyman.en.warn_package_should_not_repeat_languages.model.kps new file mode 100644 index 0000000000000000000000000000000000000000..0fdb270321410e75782c9498017cebc42496902a --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/keyman.en.warn_package_should_not_repeat_languages.model.kps @@ -0,0 +1,33 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0.3 + + + + example.qaa.sencoten.model.js + Lexical model example.qaa.sencoten.model.js + 0 + .model.js + + + + + SENĆOŦEN dictionary + example.qaa.sencoten + + North Straits Salish + + Salish Again + SENĆOŦEN + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/khmer_angkor.kmx b/developer/src/kmc-package/test/fixtures/invalid/khmer_angkor.kmx new file mode 100644 index 0000000000000000000000000000000000000000..c3d0289f4863c7c4a998c348eb1d425caadca79d Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/khmer_angkor.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/my special keyboard.kmx b/developer/src/kmc-package/test/fixtures/invalid/my special keyboard.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bd6b832cab772d03a45460e6c982d79772a9ab6e Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/my special keyboard.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/my_special-keyboard.kmx b/developer/src/kmc-package/test/fixtures/invalid/my_special-keyboard.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bd6b832cab772d03a45460e6c982d79772a9ab6e Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/my_special-keyboard.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/version_four.kmn b/developer/src/kmc-package/test/fixtures/invalid/version_four.kmn new file mode 100644 index 0000000000000000000000000000000000000000..cb2cce5816df350bfcc819ec8b902ab7c09aa01e --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/version_four.kmn @@ -0,0 +1,6 @@ +store(&name) 'version 4' +store(&keyboardversion) '4.0' + +begin unicode > use(main) + +group(main) using keys \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/invalid/version_four.kmx b/developer/src/kmc-package/test/fixtures/invalid/version_four.kmx new file mode 100644 index 0000000000000000000000000000000000000000..7180460e961b26b2bbb372c70302c0d8f77934f8 Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/version_four.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_doc_file_dangerous.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_doc_file_dangerous.kps new file mode 100644 index 0000000000000000000000000000000000000000..54fc8df0ebe9e61d6ac41e5113087e6bb5314d1a --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_doc_file_dangerous.kps @@ -0,0 +1,38 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + khmer_angkor.docx + Documentation + 0 + .docx + + + + + Basic + basic + 1.0 + + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions.kps new file mode 100644 index 0000000000000000000000000000000000000000..9a8c6d588677f2399ef9baf8272bc463f731f55a --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions.kps @@ -0,0 +1,32 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + + my special keyboard.kmx + Keyboard my special keyboard + 0 + .kmx + + + + + Khmer Angkor + my special keyboard + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions_2.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions_2.kps new file mode 100644 index 0000000000000000000000000000000000000000..426592c2715940c8ba1ad34a1e0ae713b8b7c4e6 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_file_in_package_does_not_follow_filename_conventions_2.kps @@ -0,0 +1,39 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + + my_file.PDF + My Documentation + 0 + .PDF + + + + basic.kmx + Keyboard my special keyboard + 0 + .kmx + + + + + Khmer Angkor + basic + 1.3 + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmn b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmn new file mode 100644 index 0000000000000000000000000000000000000000..fe4f8457c498dd295aaa0de432e826b19d2daff5 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmn @@ -0,0 +1,8 @@ +store(&NAME) "warn_js_keyboard_file_is_missing" +store(&VERSION) "10.0" +store(&keyboardversion) "1.0" +store(&targets) "any" + +begin unicode > use(main) + +group(main) using keys \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmx b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ca697e872c2dadb3afd3a9f659bb38539d19607d Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kps new file mode 100644 index 0000000000000000000000000000000000000000..82227ffea62b9323dc80aadcec469e0255a446db --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_js_keyboard_file_is_missing.kps @@ -0,0 +1,40 @@ + + + + 15.0.266.0 + 7.0 + + + + + + + + + + Khmer Angkor + © 2015-2022 SIL International + Makara Sok + + https://keyman.com/keyboards/khmer_angkor + + + + warn_js_keyboard_file_is_missing.kmx + Keyboard Khmer Angkor + 0 + .kmx + + + + + Khmer Angkor + warn_js_keyboard_file_is_missing + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match.kps new file mode 100644 index 0000000000000000000000000000000000000000..d6a8aaf20182cc63481a3e41700dd0e3b6538445 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match.kps @@ -0,0 +1,45 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + version_four.kmx + Keyboard Version Four + 0 + .kmx + + + + + Basic + basic + 1.0 + + Khmer + + + + Version 4 + version_four + 4.0 + + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match_package_version.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match_package_version.kps new file mode 100644 index 0000000000000000000000000000000000000000..fe0276104bd72f7cfbec0a5698e11efd38274d7d --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_keyboard_versions_do_not_match_package_version.kps @@ -0,0 +1,31 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.3 + + + + version_four.kmx + Keyboard Version Four + 0 + .kmx + + + + + Version 4 + version_four + 4.0 + + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_language_tag_is_not_minimal.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_language_tag_is_not_minimal.kps new file mode 100644 index 0000000000000000000000000000000000000000..cc757d6431cc46887d755d7ad2cf84a8cc14089a --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_language_tag_is_not_minimal.kps @@ -0,0 +1,32 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + + Central Khmer (Khmer, Cambodia) + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_package_should_not_repeat_languages.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_package_should_not_repeat_languages.kps new file mode 100644 index 0000000000000000000000000000000000000000..ef8fcfb89df78010987478c92fc0d73c82b5f749 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_package_should_not_repeat_languages.kps @@ -0,0 +1,33 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + + Basic + basic + 1.0 + + + Central Khmer (Khmer, Cambodia) + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/invalid/warn_redist_file_should_not_be_in_package.kps b/developer/src/kmc-package/test/fixtures/invalid/warn_redist_file_should_not_be_in_package.kps new file mode 100644 index 0000000000000000000000000000000000000000..10d6212d67701e2dd340510b806e337d69056a57 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/warn_redist_file_should_not_be_in_package.kps @@ -0,0 +1,38 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Lexical Model + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + keyman.exe + Keyman Program + 0 + .exe + + + + + Basic + basic + 1.0 + + Khmer + + + + diff --git a/developer/src/kmc-package/test/fixtures/xml_kvk_file/source/xml_kvk_file.kps b/developer/src/kmc-package/test/fixtures/xml_kvk_file/source/xml_kvk_file.kps index f89e1dfd7a683fba7e846e2e65f904ca6bce3525..816a66d633883252ca3f51daee0bf4ac5e50f70f 100644 --- a/developer/src/kmc-package/test/fixtures/xml_kvk_file/source/xml_kvk_file.kps +++ b/developer/src/kmc-package/test/fixtures/xml_kvk_file/source/xml_kvk_file.kps @@ -17,6 +17,7 @@ XML KVK File + 1.0 @@ -25,5 +26,21 @@ 0 .kvk + + ../../invalid/basic.kmx + Keyboard Basic + 0 + .kmx + + + + Basic + basic + 1.0 + + Khmer + + + diff --git a/developer/src/kmc-package/test/test-messages.ts b/developer/src/kmc-package/test/test-messages.ts index d6040b28db3014afc438744f5b002ffa18e54665..5944ce956a636b4a06eda6cc76d4e6dbe76755ee 100644 --- a/developer/src/kmc-package/test/test-messages.ts +++ b/developer/src/kmc-package/test/test-messages.ts @@ -1,9 +1,200 @@ import 'mocha'; -import { CompilerMessages } from '../src/messages.js'; -import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; +import { assert } from 'chai'; +import { TestCompilerCallbacks, verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; +import { CompilerMessages } from '../src/compiler/messages.js'; +import { makePathToFixture } from './helpers/index.js'; +import { KmpCompiler } from '../src/compiler/kmp-compiler.js'; +import { PackageValidation } from '../src/compiler/package-validation.js'; + +const debug = false; +const callbacks = new TestCompilerCallbacks(); describe('CompilerMessages', function () { it('should have a valid CompilerMessages object', function() { return verifyCompilerMessagesObject(CompilerMessages); }); + + + // + // Message tests + // + + function testForMessage(context: Mocha.Context, fixture: string[], messageId?: number) { + context.timeout(10000); + + callbacks.clear(); + + const kpsPath = makePathToFixture(...fixture); + const kmpCompiler = new KmpCompiler(callbacks); + + let kmpJson = kmpCompiler.transformKpsToKmpObject(kpsPath); + if(kmpJson && callbacks.messages.length == 0) { + const validator = new PackageValidation(callbacks); + validator.validate(kpsPath, kmpJson); // we'll ignore return value and rely on the messages + } + + if(kmpJson && callbacks.messages.length == 0) { + // We'll try building the package if we have not yet received any messages + kmpCompiler.buildKmpFile(kpsPath, kmpJson) + } + + if(debug) callbacks.printMessages(); + + if(messageId) { + assert.lengthOf(callbacks.messages, 1); + assert.isTrue(callbacks.hasMessage(messageId)); + } else { + assert.lengthOf(callbacks.messages, 0); + } + } + + // WARN_FileIsNotABinaryKvkFile + + it('should generate WARN_FileIsNotABinaryKvkFile if a non-binary kvk file is included', async function() { + testForMessage(this, ['xml_kvk_file', 'source', 'xml_kvk_file.kps'], CompilerMessages.WARN_FileIsNotABinaryKvkFile); + }); + + it('should not warn if a binary kvk file is included', async function() { + testForMessage(this, ['binary_kvk_file', 'source', 'binary_kvk_file.kps']); + }); + + // ERROR_FollowKeyboardVersionNotAllowedForModelPackages + + it('should generate ERROR_FollowKeyboardVersionNotAllowedForModelPackages if is set for model packages', async function() { + testForMessage(this, ['invalid', 'followkeyboardversion.qaa.sencoten.model.kps'], CompilerMessages.ERROR_FollowKeyboardVersionNotAllowedForModelPackages); + }); + + // ERROR_FollowKeyboardVersionButNoKeyboards + + it('should generate ERROR_FollowKeyboardVersionButNoKeyboards if is set for a package with no keyboards', async function() { + testForMessage(this, ['invalid', 'followkeyboardversion.empty.kps'], CompilerMessages.ERROR_FollowKeyboardVersionButNoKeyboards); + }); + + // ERROR_KeyboardContentFileNotFound + + it('should generate ERROR_KeyboardContentFileNotFound if a is listed in a package but not found in ', async function() { + testForMessage(this, ['invalid', 'keyboardcontentfilenotfound.kps'], CompilerMessages.ERROR_KeyboardContentFileNotFound); + }); + + // ERROR_KeyboardFileNotValid + + it('should generate ERROR_KeyboardFileNotValid if a .kmx is not valid in ', async function() { + testForMessage(this, ['invalid', 'keyboardfilenotvalid.kps'], CompilerMessages.ERROR_KeyboardFileNotValid); + }); + + // INFO_KeyboardFileHasNoKeyboardVersion + + it('should generate INFO_KeyboardFileHasNoKeyboardVersion if is set but keyboard has no version', async function() { + testForMessage(this, ['invalid', 'nokeyboardversion.kps'], CompilerMessages.INFO_KeyboardFileHasNoKeyboardVersion); + }); + + // ERROR_PackageCannotContainBothModelsAndKeyboards + + it('should generate ERROR_PackageCannotContainBothModelsAndKeyboards if package has both keyboards and models', async function() { + testForMessage(this, ['invalid', 'error_package_cannot_contain_both_models_and_keyboards.kps'], CompilerMessages.ERROR_PackageCannotContainBothModelsAndKeyboards); + }); + + // WARN_PackageShouldNotRepeatLanguages (models) + + it('should generate WARN_PackageShouldNotRepeatLanguages if model has same language repeated', async function() { + testForMessage(this, ['invalid', 'keyman.en.warn_package_should_not_repeat_languages.model.kps'], CompilerMessages.WARN_PackageShouldNotRepeatLanguages); + }); + + // WARN_PackageShouldNotRepeatLanguages (keyboards) + + it('should generate WARN_PackageShouldNotRepeatLanguages if keyboard has same language repeated', async function() { + testForMessage(this, ['invalid', 'warn_package_should_not_repeat_languages.kps'], CompilerMessages.WARN_PackageShouldNotRepeatLanguages); + }); + + // WARN_PackageNameDoesNotFollowLexicalModelConventions + + it('should generate WARN_PackageNameDoesNotFollowLexicalModelConventions if filename has wrong conventions', async function() { + testForMessage(this, ['invalid', 'WARN_PackageNameDoesNotFollowLexicalModelConventions.kps'], CompilerMessages.WARN_PackageNameDoesNotFollowLexicalModelConventions); + }); + + // WARN_PackageNameDoesNotFollowKeyboardConventions + + it('should generate WARN_PackageNameDoesNotFollowKeyboardConventions if filename has wrong conventions', async function() { + testForMessage(this, ['invalid', 'WARN_PackageNameDoesNotFollowKeyboardConventions.kps'], CompilerMessages.WARN_PackageNameDoesNotFollowKeyboardConventions); + }); + + // WARN_FileInPackageDoesNotFollowFilenameConventions + + it('should generate WARN_FileInPackageDoesNotFollowFilenameConventions if content filename has wrong conventions', async function() { + testForMessage(this, ['invalid', 'warn_file_in_package_does_not_follow_filename_conventions.kps'], CompilerMessages.WARN_FileInPackageDoesNotFollowFilenameConventions); + testForMessage(this, ['invalid', 'warn_file_in_package_does_not_follow_filename_conventions_2.kps'], CompilerMessages.WARN_FileInPackageDoesNotFollowFilenameConventions); + }); + + // ERROR_PackageNameCannotBeBlank + + it('should generate ERROR_PackageNameCannotBeBlank if package info has empty name', async function() { + testForMessage(this, ['invalid', 'error_package_name_cannot_be_blank.kps'], CompilerMessages.ERROR_PackageNameCannotBeBlank); // blank field + testForMessage(this, ['invalid', 'error_package_name_cannot_be_blank_2.kps'], CompilerMessages.ERROR_PackageNameCannotBeBlank); // missing field + }); + + // ERROR_KeyboardFileNotFound + + it('should generate ERROR_KeyboardFileNotFound if a is listed in a package but not found in ', async function() { + testForMessage(this, ['invalid', 'keyboardfilenotfound.kps'], CompilerMessages.ERROR_KeyboardFileNotFound); + }); + + // WARN_KeyboardVersionsDoNotMatch + + it('should generate WARN_KeyboardVersionsDoNotMatch if two have different versions', async function() { + testForMessage(this, ['invalid', 'warn_keyboard_versions_do_not_match.kps'], CompilerMessages.WARN_KeyboardVersionsDoNotMatch); + }); + + // WARN_KeyboardVersionsDoNotMatchPackageVersion + + it('should generate WARN_KeyboardVersionsDoNotMatchPackageVersion if version does not match package version', async function() { + testForMessage(this, ['invalid', 'warn_keyboard_versions_do_not_match_package_version.kps'], CompilerMessages.WARN_KeyboardVersionsDoNotMatchPackageVersion); + }); + + // ERROR_LanguageTagIsNotValid + + it('should generate ERROR_LanguageTagIsNotValid if keyboard has an invalid language tag', async function() { + testForMessage(this, ['invalid', 'error_language_tag_is_not_valid.kps'], CompilerMessages.ERROR_LanguageTagIsNotValid); + }); + + // WARN_LanguageTagIsNotMinimal + + it('should generate WARN_LanguageTagIsNotMinimal if keyboard has a non-minimal language tag', async function() { + testForMessage(this, ['invalid', 'warn_language_tag_is_not_minimal.kps'], CompilerMessages.WARN_LanguageTagIsNotMinimal); + }); + + // ERROR_MustHaveAtLeastOneLanguage + + it('should generate ERROR_MustHaveAtLeastOneLanguage if model or keyboard has zero language tags', async function() { + testForMessage(this, ['invalid', 'keyman.en.error_must_have_at_least_one_language.model.kps'], + CompilerMessages.ERROR_MustHaveAtLeastOneLanguage); + }); + + // WARN_RedistFileShouldNotBeInPackage + + it('should generate WARN_RedistFileShouldNotBeInPackage if package contains a redist file', async function() { + testForMessage(this, ['invalid', 'warn_redist_file_should_not_be_in_package.kps'], + CompilerMessages.WARN_RedistFileShouldNotBeInPackage); + }); + + // WARN_DocFileDangerous + + it('should generate WARN_DocFileDangerous if package contains a .doc file', async function() { + testForMessage(this, ['invalid', 'warn_doc_file_dangerous.kps'], + CompilerMessages.WARN_DocFileDangerous); + }); + + // ERROR_PackageMustContainAPackageOrAKeyboard + + it('should generate ERROR_PackageMustContainAModelOrAKeyboard if package contains a .doc file', async function() { + testForMessage(this, ['invalid', 'error_package_must_contain_a_model_or_a_keyboard.kps'], + CompilerMessages.ERROR_PackageMustContainAModelOrAKeyboard); + }); + + // WARN_JsKeyboardFileIsMissing + + it('should generate WARN_JsKeyboardFileIsMissing if package is missing corresponding .js for a touch .kmx', async function() { + testForMessage(this, ['invalid', 'warn_js_keyboard_file_is_missing.kps'], + CompilerMessages.WARN_JsKeyboardFileIsMissing); + }); + }); diff --git a/developer/src/kmc-package/test/test-package-compiler.ts b/developer/src/kmc-package/test/test-package-compiler.ts index 7708047a2173bbb719838505308a4ca283d6362c..59d2acb55a32cd477c1f7f3f88794cd97dbafdfe 100644 --- a/developer/src/kmc-package/test/test-package-compiler.ts +++ b/developer/src/kmc-package/test/test-package-compiler.ts @@ -1,15 +1,18 @@ import 'mocha'; import * as fs from 'fs'; -import {assert} from 'chai'; - -import KmpCompiler from '../src/kmp-compiler.js'; -import {makePathToFixture} from './helpers/index.js'; +import { assert } from 'chai'; import JSZip from 'jszip'; + import KEYMAN_VERSION from "@keymanapp/keyman-version"; -import { type KmpJsonFile } from '../src/kmp-json-file.js'; +import { KmpJsonFile } from '@keymanapp/common-types'; import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; -import { CompilerMessages } from '../src/messages.js'; +import { makePathToFixture } from './helpers/index.js'; + +import { KmpCompiler } from '../src/compiler/kmp-compiler.js'; +import { CompilerMessages } from '../src/compiler/messages.js'; + +const debug = false; describe('KmpCompiler', function () { const MODELS : string[] = [ @@ -37,7 +40,7 @@ describe('KmpCompiler', function () { // Test just the transform from kps to kmp.json // it(`should transform ${modelID}.model.kps to kmp.json`, function () { - let kmpJson: KmpJsonFile; + let kmpJson: KmpJsonFile.KmpJsonFile; assert.doesNotThrow(() => { kmpJson = kmpCompiler.transformKpsToKmpObject(kpsPath); @@ -56,7 +59,7 @@ describe('KmpCompiler', function () { it(`should build a full .kmp for ${modelID}`, async function() { const zip = JSZip(); // Build kmp.json in memory - const kmpJson: KmpJsonFile = kmpCompiler.transformKpsToKmpObject(kpsPath); + const kmpJson: KmpJsonFile.KmpJsonFile = kmpCompiler.transformKpsToKmpObject(kpsPath); // Build file.kmp in memory const promise = kmpCompiler.buildKmpFile(kpsPath, kmpJson); promise.then(data => { @@ -86,7 +89,7 @@ describe('KmpCompiler', function () { const kmpJsonRefPath = makePathToFixture('khmer_angkor', 'ref', 'kmp.json'); const kmpCompiler = new KmpCompiler(callbacks); - const kmpJsonFixture: KmpJsonFile = JSON.parse(fs.readFileSync(kmpJsonRefPath, 'utf-8')); + const kmpJsonFixture: KmpJsonFile.KmpJsonFile = JSON.parse(fs.readFileSync(kmpJsonRefPath, 'utf-8')); // We override the fixture version so that we can compare with the compiler output kmpJsonFixture.system.keymanDeveloperVersion = KEYMAN_VERSION.VERSION; @@ -131,7 +134,7 @@ describe('KmpCompiler', function () { const kpsPath = makePathToFixture('absolute_path', 'source', 'absolute_path.kps'); const kmpCompiler = new KmpCompiler(callbacks); - let kmpJson: KmpJsonFile = null; + let kmpJson: KmpJsonFile.KmpJsonFile = null; assert.doesNotThrow(() => { kmpJson = kmpCompiler.transformKpsToKmpObject(kpsPath); @@ -139,75 +142,10 @@ describe('KmpCompiler', function () { await assert.isNull(kmpCompiler.buildKmpFile(kpsPath, kmpJson)); + if(debug) callbacks.printMessages(); + assert.lengthOf(callbacks.messages, 2); assert.deepEqual(callbacks.messages[0].code, CompilerMessages.WARN_AbsolutePath); assert.deepEqual(callbacks.messages[1].code, CompilerMessages.ERROR_FileDoesNotExist); }); - - // - // Message tests - // - - function testForMessage(context: Mocha.Context, fixture: string[], messageId?: number) { - context.timeout(10000); - - callbacks.clear(); - - const kpsPath = makePathToFixture(...fixture); - const kmpCompiler = new KmpCompiler(callbacks); - - let kmpJson = kmpCompiler.transformKpsToKmpObject(kpsPath); - if(kmpJson && callbacks.messages.length == 0) { - // We'll try building the package if we have not yet received any messages - kmpCompiler.buildKmpFile(kpsPath, kmpJson) - } - - if(messageId) { - assert.lengthOf(callbacks.messages, 1); - assert.isTrue(callbacks.hasMessage(messageId)); - } else { - assert.lengthOf(callbacks.messages, 0); - } - } - - // WARN_FileIsNotABinaryKvkFile - - it('should generate WARN_FileIsNotABinaryKvkFile if a non-binary kvk file is included', async function() { - testForMessage(this, ['xml_kvk_file', 'source', 'xml_kvk_file.kps'], CompilerMessages.WARN_FileIsNotABinaryKvkFile); - }); - - it('should not warn if a binary kvk file is included', async function() { - testForMessage(this, ['binary_kvk_file', 'source', 'binary_kvk_file.kps']); - }); - - // ERROR_FollowKeyboardVersionNotAllowedForModelPackages - - it('should generate ERROR_FollowKeyboardVersionNotAllowedForModelPackages if is set for model packages', async function() { - testForMessage(this, ['invalid', 'followkeyboardversion.qaa.sencoten.model.kps'], CompilerMessages.ERROR_FollowKeyboardVersionNotAllowedForModelPackages); - }); - - // WARN_FollowKeyboardVersionButNoKeyboards - - it('should generate WARN_FollowKeyboardVersionButNoKeyboards if is set for a package with no keyboards or models', async function() { - testForMessage(this, ['invalid', 'followkeyboardversion.empty.kps'], CompilerMessages.WARN_FollowKeyboardVersionButNoKeyboards); - }); - - // ERROR_KeyboardFileNotFound - - it('should generate ERROR_KeyboardFileNotFound if a is listed in a package but not found in ', async function() { - testForMessage(this, ['invalid', 'keyboardfilenotfound.kps'], CompilerMessages.ERROR_KeyboardFileNotFound); - }); - - // ERROR_KeyboardFileNotValid - - it('should generate ERROR_KeyboardFileNotValid if a .kmx is not valid in ', async function() { - testForMessage(this, ['invalid', 'keyboardfilenotvalid.kps'], CompilerMessages.ERROR_KeyboardFileNotValid); - }); - - // WARN_KeyboardFileHasNoKeyboardVersion - - it('should generate WARN_KeyboardFileHasNoKeyboardVersion if is set but keyboard has no version', async function() { - testForMessage(this, ['invalid', 'nokeyboardversion.kps'], CompilerMessages.WARN_KeyboardFileHasNoKeyboardVersion); - }); - }); diff --git a/developer/src/kmc-package/tsconfig.json b/developer/src/kmc-package/tsconfig.json index e7e5b3a93b082736056dd3e78b159eb056cf5289..8913057047eadc303181a6e070dbb16889d459ed 100644 --- a/developer/src/kmc-package/tsconfig.json +++ b/developer/src/kmc-package/tsconfig.json @@ -7,7 +7,7 @@ "allowSyntheticDefaultImports": true, // for jszip }, "include": [ - "src/**/*.ts" + "src/**/*.ts", ], "references": [ { "path": "../../../common/web/keyman-version/tsconfig.esm.json" }, diff --git a/developer/src/kmc/Makefile b/developer/src/kmc/Makefile index 8e8dc7d5f3187501bf08b8b5bf22e9cc94f1f87f..1078ab1a67f1d7a5fe1436d00bf44ba240547309 100644 --- a/developer/src/kmc/Makefile +++ b/developer/src/kmc/Makefile @@ -4,7 +4,7 @@ !include ..\Defines.mak -# We don't depend on configure here because kmc-keyboard does that already, +# We don't depend on configure here because kmc-ldml does that already, # called from parent folder Makefile build: .virtual $(GIT_BASH_FOR_KEYMAN) build.sh build diff --git a/developer/src/kmc/build.sh b/developer/src/kmc/build.sh index 1ba75d261fdc411edd527fe1abbdb43c243cfe4c..e37006ce5dd8e2dc384ec33ed2826db208a4e7ee 100755 --- a/developer/src/kmc/build.sh +++ b/developer/src/kmc/build.sh @@ -21,9 +21,9 @@ builder_describe "Build Keyman Keyboard Compiler kmc" \ "@/common/web/keyman-version" \ "@/common/web/types" \ "@/developer/src/kmc-analyze" \ - "@/developer/src/kmc-keyboard" \ "@/developer/src/kmc-kmn" \ "@/developer/src/kmc-kmw" \ + "@/developer/src/kmc-ldml" \ "@/developer/src/kmc-model" \ "@/developer/src/kmc-model-info" \ "@/developer/src/kmc-package" \ @@ -115,9 +115,9 @@ readonly PACKAGES=( common/models/types core/include/ldml developer/src/kmc-analyze - developer/src/kmc-keyboard developer/src/kmc-kmn developer/src/kmc-kmw + developer/src/kmc-ldml developer/src/kmc-model developer/src/kmc-model-info developer/src/kmc-package diff --git a/developer/src/kmc/package.json b/developer/src/kmc/package.json index 2a2ba6c9bddcc742749d1320cca59bca4ce64ab6..588e22dedda5fccc801408f823388079aa78b190 100644 --- a/developer/src/kmc/package.json +++ b/developer/src/kmc/package.json @@ -40,9 +40,9 @@ "@keymanapp/keyman-version": "*", "@keymanapp/common-types": "*", "@keymanapp/kmc-analyze": "*", - "@keymanapp/kmc-keyboard": "*", "@keymanapp/kmc-kmn": "*", "@keymanapp/kmc-kmw": "*", + "@keymanapp/kmc-ldml": "*", "@keymanapp/kmc-model": "*", "@keymanapp/kmc-model-info": "*", "@keymanapp/kmc-package": "*", diff --git a/developer/src/kmc/src/commands/build/BuildKmnKeyboard.ts b/developer/src/kmc/src/commands/build/BuildKmnKeyboard.ts index cd5b7c7ac39c985dc42be85ee7542c0483d23097..9055e2862b7b1c81e52eea12bf3d552e9b1e2e0a 100644 --- a/developer/src/kmc/src/commands/build/BuildKmnKeyboard.ts +++ b/developer/src/kmc/src/commands/build/BuildKmnKeyboard.ts @@ -1,6 +1,6 @@ import * as path from 'path'; import { BuildActivity, BuildActivityOptions } from './BuildActivity.js'; -import { Compiler } from '@keymanapp/kmc-kmn'; +import { KmnCompiler } from '@keymanapp/kmc-kmn'; import { platform } from 'os'; import { CompilerCallbacks } from '@keymanapp/common-types'; @@ -10,8 +10,8 @@ export class BuildKmnKeyboard extends BuildActivity { public get compiledExtension(): string { return '.kmx'; } public get description(): string { return 'Build a Keyman keyboard'; } public async build(infile: string, callbacks: CompilerCallbacks, options: BuildActivityOptions): Promise { - let compiler = new Compiler(); - if(!await compiler.init()) { + let compiler = new KmnCompiler(); + if(!await compiler.init(callbacks)) { return false; } @@ -23,7 +23,7 @@ export class BuildKmnKeyboard extends BuildActivity { // TODO: Currently this only builds .kmn->.kmx, and targeting .js is as-yet unsupported // TODO: Support additional options compilerWarningsAsErrors, warnDeprecatedCode - return compiler.run(infile, outfile, callbacks, + return compiler.run(infile, outfile, { saveDebug: options.debug, shouldAddCompilerVersion: options.compilerVersion, @@ -49,4 +49,4 @@ function getPosixAbsolutePath(filename: string): string { filename = filename.replace(/\\/g, '/'); } return filename; -} \ No newline at end of file +} diff --git a/developer/src/kmc/src/commands/build/BuildLdmlKeyboard.ts b/developer/src/kmc/src/commands/build/BuildLdmlKeyboard.ts index 8f114655841c69aa81060ae9866687e957984496..40b9513d320e1971824a93f30c62c3f4ea206604 100644 --- a/developer/src/kmc/src/commands/build/BuildLdmlKeyboard.ts +++ b/developer/src/kmc/src/commands/build/BuildLdmlKeyboard.ts @@ -1,8 +1,9 @@ import * as path from 'path'; import * as fs from 'fs'; -import * as kmc from '@keymanapp/kmc-keyboard'; -import { KvkFileWriter, CompilerCallbacks } from '@keymanapp/common-types'; +import * as kmcLdml from '@keymanapp/kmc-ldml'; +import { KvkFileWriter, CompilerCallbacks, LDMLKeyboardXMLSourceFileReader } from '@keymanapp/common-types'; import { BuildActivity, BuildActivityOptions } from './BuildActivity.js'; +import { fileURLToPath } from 'url'; export class BuildLdmlKeyboard extends BuildActivity { public get name(): string { return 'LDML keyboard'; } @@ -43,14 +44,17 @@ export class BuildLdmlKeyboard extends BuildActivity { } function buildLdmlKeyboardToMemory(inputFilename: string, callbacks: CompilerCallbacks, options: BuildActivityOptions): [Uint8Array, Uint8Array, Uint8Array] { - let compilerOptions: kmc.CompilerOptions = { + let compilerOptions: kmcLdml.CompilerOptions = { debug: options.debug ?? false, addCompilerVersion: options.compilerVersion ?? true, + readerOptions: { + importsPath: fileURLToPath(LDMLKeyboardXMLSourceFileReader.defaultImportsURL) + } // TODO: warnDeprecatedCode: options.warnDeprecatedCode, // TODO: treatWarningsAsErrors: options.treatWarningsAsErrors, } - const k = new kmc.Compiler(callbacks, options); + const k = new kmcLdml.LdmlKeyboardCompiler(callbacks, compilerOptions); let source = k.load(inputFilename); if (!source) { return [null, null, null]; @@ -62,13 +66,13 @@ function buildLdmlKeyboardToMemory(inputFilename: string, callbacks: CompilerCal // In order for the KMX file to be loaded by non-KMXPlus components, it is helpful // to duplicate some of the metadata - kmc.KMXPlusMetadataCompiler.addKmxMetadata(kmx.kmxplus, kmx.keyboard, compilerOptions); + kmcLdml.KMXPlusMetadataCompiler.addKmxMetadata(kmx.kmxplus, kmx.keyboard, compilerOptions); // Use the builder to generate the binary output file - const builder = new kmc.KMXBuilder(kmx, options.debug); + const builder = new kmcLdml.KMXBuilder(kmx, options.debug); const kmx_binary = builder.compile(); - const vkcompiler = new kmc.VisualKeyboardCompiler(); + const vkcompiler = new kmcLdml.LdmlKeyboardVisualKeyboardCompiler(); const vk = vkcompiler.compile(source); const writer = new KvkFileWriter(); const kvk_binary = writer.write(vk); @@ -78,7 +82,7 @@ function buildLdmlKeyboardToMemory(inputFilename: string, callbacks: CompilerCal // const tlcompiler = new kmc.TouchLayoutCompiler(); // const tl = tlcompiler.compile(source); // const tlwriter = new TouchLayoutFileWriter(); - const kmwcompiler = new kmc.KeymanWebCompiler(callbacks, compilerOptions); + const kmwcompiler = new kmcLdml.LdmlKeyboardKeymanWebCompiler(callbacks, compilerOptions); const kmw_string = kmwcompiler.compile(inputFilename, source); const encoder = new TextEncoder(); const kmw_binary = encoder.encode(kmw_string); diff --git a/developer/src/kmc/src/commands/build/BuildPackage.ts b/developer/src/kmc/src/commands/build/BuildPackage.ts index 29a2d3b068b87b52f0d1032c804eb3d68acb6a88..3c7c81f6a8bd2ba1358786e09203a7477764a53f 100644 --- a/developer/src/kmc/src/commands/build/BuildPackage.ts +++ b/developer/src/kmc/src/commands/build/BuildPackage.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import { BuildActivity, BuildActivityOptions } from './BuildActivity.js'; -import KmpCompiler from '@keymanapp/kmc-package'; import { CompilerCallbacks } from '@keymanapp/common-types'; +import { KmpCompiler, PackageValidation } from '@keymanapp/kmc-package'; export class BuildPackage extends BuildActivity { public get name(): string { return 'Package'; } @@ -22,6 +22,15 @@ export class BuildPackage extends BuildActivity { return false; } + // + // Validate the package file + // + + const validation = new PackageValidation(callbacks); + if(!validation.validate(infile, kmpJsonData)) { + return false; + } + // // Build the .kmp package file // diff --git a/developer/src/kmc/src/commands/buildTestData/index.ts b/developer/src/kmc/src/commands/buildTestData/index.ts index 2341b11082818cff2ffd99d66dacd037abe437a5..b6ebf34a739326e797565407d2dddb217a39a622 100644 --- a/developer/src/kmc/src/commands/buildTestData/index.ts +++ b/developer/src/kmc/src/commands/buildTestData/index.ts @@ -1,8 +1,9 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as kmc from '@keymanapp/kmc-keyboard'; -import { CompilerCallbacks, LDMLKeyboardTestDataXMLSourceFile } from '@keymanapp/common-types'; +import * as kmc from '@keymanapp/kmc-ldml'; +import { CompilerCallbacks, LDMLKeyboardTestDataXMLSourceFile, LDMLKeyboardXMLSourceFileReader } from '@keymanapp/common-types'; import { NodeCompilerCallbacks } from '../../messages/NodeCompilerCallbacks.js'; +import { fileURLToPath } from 'url'; export interface BuildTestDataOptions { outFile?: string; @@ -11,7 +12,10 @@ export interface BuildTestDataOptions { export function buildTestData(infile: string, options: BuildTestDataOptions) { let compilerOptions: kmc.CompilerOptions = { debug: false, - addCompilerVersion: false + addCompilerVersion: false, + readerOptions: { + importsPath: fileURLToPath(LDMLKeyboardXMLSourceFileReader.defaultImportsURL) + } }; let testData = loadTestData(infile, compilerOptions); @@ -29,7 +33,7 @@ export function buildTestData(infile: string, options: BuildTestDataOptions) { function loadTestData(inputFilename: string, options: kmc.CompilerOptions): LDMLKeyboardTestDataXMLSourceFile { const c: CompilerCallbacks = new NodeCompilerCallbacks(); - const k = new kmc.Compiler(c, options); + const k = new kmc.LdmlKeyboardCompiler(c, options); let source = k.loadTestData(inputFilename); if (!source) { return null; diff --git a/developer/src/kmc/src/kmlmi.ts b/developer/src/kmc/src/kmlmi.ts index de379e6372dba0abb7ebda6eb35143a0c5ea105f..0868e0ccfe442c21351aa11485b421a7b88a2ff3 100644 --- a/developer/src/kmc/src/kmlmi.ts +++ b/developer/src/kmc/src/kmlmi.ts @@ -5,8 +5,8 @@ import * as path from 'path'; import { Command } from 'commander'; -import KmpCompiler from '@keymanapp/kmc-package'; -import { ModelInfoOptions as ModelInfoOptions, writeMergedModelMetadataFile } from '@keymanapp/kmc-model-info'; +import { KmpCompiler, PackageValidation } from '@keymanapp/kmc-package'; +import { ModelInfoOptions, writeMergedModelMetadataFile } from '@keymanapp/kmc-model-info'; import { SysExits } from './util/sysexits.js'; import KEYMAN_VERSION from "@keymanapp/keyman-version"; import { NodeCompilerCallbacks } from './messages/NodeCompilerCallbacks.js'; @@ -48,6 +48,18 @@ let jsFilename = program.opts().jsFilename ? program.opts().jsFilename : path.jo const callbacks = new NodeCompilerCallbacks(); let kmpCompiler = new KmpCompiler(callbacks); let kmpJsonData = kmpCompiler.transformKpsToKmpObject(kpsFilename); +if(!kmpJsonData) { + process.exit(1); +} + +// +// Validate the package file +// + +const validation = new PackageValidation(callbacks); +if(!validation.validate(kpsFilename, kmpJsonData)) { + process.exit(1); +} // // Write out the merged .model_info file diff --git a/developer/src/kmc/src/kmlmp.ts b/developer/src/kmc/src/kmlmp.ts index 202bfce25437b14b89a2c424aa79f968c3441cb8..5a54a57d71bd015e75337aee9617b104780a7900 100644 --- a/developer/src/kmc/src/kmlmp.ts +++ b/developer/src/kmc/src/kmlmp.ts @@ -3,9 +3,11 @@ * kmlmp - Keyman Lexical Model Package Compiler */ +// Note: this is a deprecated package and will be removed in Keyman 18.0 + import * as fs from 'fs'; import { Command } from 'commander'; -import KmpCompiler from '@keymanapp/kmc-package'; +import { PackageValidation, KmpCompiler } from '@keymanapp/kmc-package'; import { SysExits } from './util/sysexits.js'; import KEYMAN_VERSION from "@keymanapp/keyman-version"; import { NodeCompilerCallbacks } from './messages/NodeCompilerCallbacks.js'; @@ -15,7 +17,7 @@ const program = new Command(); /* Arguments */ program - .description('Compiles Keyman lexical model packages') + .description('Compiles Keyman lexical model packages\nDeprecated: use instead; will be removed in v18') .version(KEYMAN_VERSION.VERSION_WITH_TAG) .arguments('') .action(infile => inputFilename = infile) @@ -38,6 +40,18 @@ let outputFilename: string = program.opts().outFile ? program.opts().outFile : i const callbacks = new NodeCompilerCallbacks(); let kmpCompiler = new KmpCompiler(callbacks); let kmpJsonData = kmpCompiler.transformKpsToKmpObject(inputFilename); +if(!kmpJsonData) { + process.exit(1); +} + +// +// Validate the package file +// + +const validation = new PackageValidation(callbacks); +if(!validation.validate(inputFilename, kmpJsonData)) { + process.exit(1); +} // // Build the .kmp package file diff --git a/developer/src/kmc/src/messages/NodeCompilerCallbacks.ts b/developer/src/kmc/src/messages/NodeCompilerCallbacks.ts index 05296c2c45e1f81038e19d7354566b9aaf284d8b..7b673188b3cccee672f5046c206ed9ee49f31fda 100644 --- a/developer/src/kmc/src/messages/NodeCompilerCallbacks.ts +++ b/developer/src/kmc/src/messages/NodeCompilerCallbacks.ts @@ -1,29 +1,52 @@ import * as fs from 'fs'; import * as path from 'path'; -import { CompilerCallbacks, CompilerSchema, CompilerEvent, compilerErrorSeverityName, CompilerPathCallbacks, CompilerFileSystemCallbacks, CompilerErrorSeverity, compilerErrorSeverity } from '@keymanapp/common-types'; +import { CompilerCallbacks, CompilerSchema, CompilerEvent, compilerErrorSeverityName, CompilerPathCallbacks, CompilerFileSystemCallbacks } from '@keymanapp/common-types'; +import { InfrastructureMessages } from './messages.js'; /** * Concrete implementation for CLI use */ -interface NodeCompilerCallbacksOptions { - quiet?: boolean; -} - -const defaultOptions: NodeCompilerCallbacksOptions = { - quiet: false -}; +// TODO: Make a common class for all the CompilerCallbacks implementations export class NodeCompilerCallbacks implements CompilerCallbacks { - private options: NodeCompilerCallbacksOptions; - private events: CompilerEvent[] = []; + /* NodeCompilerCallbacks */ + + messages: CompilerEvent[] = []; + silent: boolean; + + constructor(silent?: boolean) { + this.silent = !!silent; + } + + clear() { + this.messages = []; + } - constructor(options?: NodeCompilerCallbacksOptions) { - this.options = {...defaultOptions, ...options}; + hasMessage(code: number): boolean { + return this.messages.find((item) => item.code == code) === undefined ? false : true; } - // TODO: consolidate with this.fs.readFileSync - loadFile(filename: string | URL): Buffer { + private verifyFilenameConsistency(originalFilename: string): void { + if(fs.existsSync(originalFilename)) { + // Note, we only check this if the file exists, because + // if it is not found, that will be returned as an error + // from loadFile anyway. + const filename = fs.realpathSync(originalFilename); + const nativeFilename = fs.realpathSync.native(filename); + if(filename != nativeFilename) { + this.reportMessage(InfrastructureMessages.Hint_FilenameHasDifferingCase({ + reference: originalFilename, + filename: nativeFilename + })); + } + } + } + + /* CompilerCallbacks */ + + loadFile(filename: string): Uint8Array { + this.verifyFilenameConsistency(filename); try { return fs.readFileSync(filename); } catch (e) { @@ -44,12 +67,13 @@ export class NodeCompilerCallbacks implements CompilerCallbacks { } reportMessage(event: CompilerEvent): void { - this.events.push(event); - if(this.options.quiet && compilerErrorSeverity(event.code) < CompilerErrorSeverity.Error) { - // We'll only print errors if we are in 'quiet' mode + this.messages.push(event); + // // We'll only print errors if we are in 'quiet' mode + // TODO: kmc-analyze used this: if(this.options.quiet && compilerErrorSeverity(event.code) < CompilerErrorSeverity.Error) { + // + if(this.silent) { return; } - const code = event.code.toString(16); if(event.line) { console.log(`${compilerErrorSeverityName(event.code)} ${code} [${event.line}]: ${event.message}`); @@ -64,7 +88,7 @@ export class NodeCompilerCallbacks implements CompilerCallbacks { } } - loadSchema(schema: CompilerSchema) { + loadSchema(schema: CompilerSchema): Uint8Array { let schemaPath = new URL('../util/' + schema + '.schema.json', import.meta.url); return fs.readFileSync(schemaPath); } @@ -74,7 +98,10 @@ export class NodeCompilerCallbacks implements CompilerCallbacks { } resolveFilename(baseFilename: string, filename: string) { - const basePath = path.dirname(baseFilename); + const basePath = + baseFilename.endsWith('/') || baseFilename.endsWith('\\') ? + baseFilename : + path.dirname(baseFilename); // Transform separators to platform separators -- we are agnostic // in our use here but path prefers files may use // either / or \, although older kps files were always \. diff --git a/developer/src/kmc/src/messages/messages.ts b/developer/src/kmc/src/messages/messages.ts index 1f1761e715d6f70f523d95d074b2eebab1fe1500..358a59406bb3a80f1c025f9c88ed80a9e1945009 100644 --- a/developer/src/kmc/src/messages/messages.ts +++ b/developer/src/kmc/src/messages/messages.ts @@ -2,7 +2,7 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m const Namespace = CompilerErrorNamespace.Infrastructure; const SevInfo = CompilerErrorSeverity.Info | Namespace; -// const SevHint = CompilerErrorSeverity.Hint | Namespace; +const SevHint = CompilerErrorSeverity.Hint | Namespace; // const SevWarn = CompilerErrorSeverity.Warn | Namespace; const SevError = CompilerErrorSeverity.Error | Namespace; const SevFatal = CompilerErrorSeverity.Fatal | Namespace; @@ -39,5 +39,9 @@ export class InfrastructureMessages { static Error_InvalidProjectFile = (o:{message:string}) => m(this.ERROR_InvalidProjectFile, `Project file is not valid: ${o.message}`); static ERROR_InvalidProjectFile = SevError | 0x0008; + + static Hint_FilenameHasDifferingCase = (o:{reference:string, filename:string}) => m(this.HINT_FilenameHasDifferingCase, + `File ${o.filename} differs in case from reference ${o.reference}; this will fail on platforms with case-sensitive filesystems.`); + static HINT_FilenameHasDifferingCase = SevHint | 0x0009; } diff --git a/developer/src/kmc/test/fixtures/invalid-keyboards/hint_filename_has_differing_case.kmn b/developer/src/kmc/test/fixtures/invalid-keyboards/hint_filename_has_differing_case.kmn new file mode 100644 index 0000000000000000000000000000000000000000..5792016e10383d7bee622a0a49d6ec5e9ee03e09 --- /dev/null +++ b/developer/src/kmc/test/fixtures/invalid-keyboards/hint_filename_has_differing_case.kmn @@ -0,0 +1,6 @@ +store(&name) 'hint_filename_has_differing_case' +store(&version) '7.0' + +begin unicode > use(main) + +group(main) using keys diff --git a/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kps b/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kps index 28391377694c58bfa8a0be99ccc747d359b62eb6..af65dd9269b0c054be8ca465e3e7058c2d581dec 100644 --- a/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kps +++ b/developer/src/kmc/test/fixtures/relative_paths/k_000___null_keyboard.kps @@ -18,7 +18,7 @@ k_000___null_keyboard Copyright (C) Keyman Team Keyman Team - + 0.0 diff --git a/developer/src/kmc/test/test-messages.ts b/developer/src/kmc/test/test-messages.ts index 7b591ad72de7a2e3dec348e55f52155c77030cbb..045718075daaf989c6871ede3648af8fe5e0d675 100644 --- a/developer/src/kmc/test/test-messages.ts +++ b/developer/src/kmc/test/test-messages.ts @@ -1,9 +1,80 @@ import 'mocha'; +import { assert } from 'chai'; import { InfrastructureMessages } from '../src/messages/messages.js'; import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; +import { makePathToFixture } from './helpers/index.js'; +import { NodeCompilerCallbacks } from '../src/messages/NodeCompilerCallbacks.js'; describe('InfrastructureMessages', function () { it('should have a valid InfrastructureMessages object', function() { return verifyCompilerMessagesObject(InfrastructureMessages); }); + + // + // Message tests + // + + /* + TODO: + + let callbacks = new TestCompilerCallbacks(); + + async function testForMessage(context: Mocha.Context, fixture: string[], messageId?: number) { + context.timeout(10000); + + callbacks.clear(); + + const builder = new BuildKmnKeyboard(); + const path = makePathToFixture(...fixture); + let result = await builder.build(path, callbacks, { + compilerVersion: false, + compilerWarningsAsErrors: true, + debug: false, + warnDeprecatedCode: true, + }); + + if(messageId) { + assert.isTrue(callbacks.hasMessage(messageId), `messageId ${messageId.toString(16)} not generated, instead got: `+JSON.stringify(callbacks.messages,null,2)); + assert.lengthOf(callbacks.messages, 1); + } else { + assert.lengthOf(callbacks.messages, 0, `messages should be empty, but instead got: `+JSON.stringify(callbacks.messages,null,2)); + assert.isTrue(result); + } + } + + // ERROR_FileDoesNotExist + + it('should generate ERROR_FileDoesNotExist if a file does not exist', async function() { + await testForMessage(this, ['invalid-keyboards', 'error_file_does_not_exist.kmn'], CompilerMessages.ERROR_FileDoesNotExist); + }); + + // ERROR_FileTypeNotRecognized + + it('should generate ERROR_FileTypeNotRecognized if a file is not a recognized type', async function() { + await testForMessage(this, ['invalid-keyboards', 'error_file_type_not_recognized.xxx'], CompilerMessages.ERROR_FileTypeNotRecognized); + }); + + // ERROR_OutFileNotValidForProjects + + it('should generate ERROR_OutFileNotValidForProjects if an output file is specified for a project build', async function() { + await testForMessage(this, ['invalid-keyboards', 'error_out_file_not_valid_for_projects.kpj'], CompilerMessages.ERROR_OutFileNotValidForProjects); + }); + + // ERROR_InvalidProjectFile + + it('should generate ERROR_InvalidProjectFile if a project file is invalid', async function() { + await testForMessage(this, ['invalid-keyboards', 'error_invalid_project_file.kpj'], CompilerMessages.ERROR_InvalidProjectFile); + }); + */ + + // HINT_FilenameHasDifferingCase + + it('should generate HINT_FilenameHasDifferingCase if a referenced file has differing case', async function() { + // This message is generated by NodeCompilerCallbacks, because that's where the filesystem is visible, + // so we can't use our usual testForMessage pattern. + const ncb = new NodeCompilerCallbacks(true); + ncb.loadFile(makePathToFixture('invalid-keyboards', 'Hint_Filename_Has_Differing_Case.kmn')); + assert.isTrue(ncb.hasMessage(InfrastructureMessages.HINT_FilenameHasDifferingCase), + `HINT_FilenameHasDifferingCase not generated, instead got: `+JSON.stringify(ncb.messages,null,2)); + }); }); diff --git a/developer/src/kmc/test/test-project-build.ts b/developer/src/kmc/test/test-project-build.ts index a57f77d317862ee2591861c694461078b11d8471..e16c9fd464db80b0cd808dde22aaf37a72707ca8 100644 --- a/developer/src/kmc/test/test-project-build.ts +++ b/developer/src/kmc/test/test-project-build.ts @@ -16,7 +16,8 @@ describe('BuildProject', function () { debug: false, warnDeprecatedCode: true, }); - // 4 messages == starting build, build successful x 2 + // 4 messages == starting build x 2, build successful x 2 + // callbacks.printMessages(); assert.equal(callbacks.messages.length, 4); assert.isTrue(result); }); diff --git a/developer/src/kmc/tsconfig.json b/developer/src/kmc/tsconfig.json index 62c79b2b5170a88626b8a2ab36116f5c80b81393..bafd5d915a629070103dbc9dd66c81ea0d059a91 100644 --- a/developer/src/kmc/tsconfig.json +++ b/developer/src/kmc/tsconfig.json @@ -8,9 +8,9 @@ "paths": { "@keymanapp/common-types": [ "../../../common/web/types/src/main" ], "@keymanapp/kmc-analyze": [ "../kmc-analyze/src/index" ], - "@keymanapp/kmc-keyboard": [ "../kmc-keyboard/src/main" ], "@keymanapp/kmc-kmn": [ "../kmc-kmn/src/main" ], "@keymanapp/kmc-kmw": [ "../kmc-kmw/src/main" ], + "@keymanapp/kmc-ldml": [ "../kmc-ldml/src/main" ], "@keymanapp/kmc-model": [ "../kmc-model/src/main" ], "@keymanapp/kmc-model-info": [ "../kmc-model-info/src/model-info-compiler" ], "@keymanapp/kmc-package": [ "../kmc-package/src/kmp-compiler" ], @@ -23,9 +23,9 @@ { "path": "../../../common/web/keyman-version/tsconfig.esm.json" }, { "path": "../../../common/web/types" }, { "path": "../kmc-analyze" }, - { "path": "../kmc-keyboard" }, { "path": "../kmc-kmn" }, { "path": "../kmc-kmw" }, + { "path": "../kmc-ldml" }, { "path": "../kmc-model" }, { "path": "../kmc-model-info" }, { "path": "../kmc-package" }, diff --git a/developer/src/kmcmpdll/Compiler.cpp b/developer/src/kmcmpdll/Compiler.cpp index 847a1ff78c269bc37d2727561fbfdf027b13be08..23e23e791bc2001d3e9b2d3ca6b145953d8d1733 100644 --- a/developer/src/kmcmpdll/Compiler.cpp +++ b/developer/src/kmcmpdll/Compiler.cpp @@ -90,8 +90,6 @@ #include "UnreachableRules.h" #include "CheckForDuplicates.h" -#include "../kmcmplib/include/kmcmplibapi.h" - int xatoi(PWSTR *p); int atoiW(PWSTR p); void safe_wcsncpy(PWSTR out, PWSTR in, int cbMax); @@ -291,33 +289,15 @@ BOOL AddCompileMessage(DWORD msg) return FALSE; } -bool flag_use_new_kmcomp = true; // flag to switch to new kmcompx - extern "C" BOOL __declspec(dllexport) SetCompilerOptions(PCOMPILER_OPTIONS options) { if(!options || options->dwSize < sizeof(COMPILER_OPTIONS)) { return FALSE; } - flag_use_new_kmcomp = options->UseKmcmpLib; - - //printf("\n---> started in SetCompilerOptions() of kmcmpdll\n"); - if ( flag_use_new_kmcomp ) - { - KMCMP_COMPILER_OPTIONS kmcmp_options = {0}; - kmcmp_options.dwSize = sizeof(KMCMP_COMPILER_OPTIONS); - kmcmp_options.ShouldAddCompilerVersion = options->ShouldAddCompilerVersion; - return kmcmp_SetCompilerOptions(&kmcmp_options); - } - //printf("---> stayed in SetCompilerOptions() of kmcmpdll\n"); - FShouldAddCompilerVersion = options->ShouldAddCompilerVersion; return TRUE; } -int kmcmpMsgproc(int line, uint32_t dwMsgCode, char* szText, void* context) { - return static_cast(context)(line, dwMsgCode, szText); -} - extern "C" BOOL __declspec(dllexport) CompileKeyboardFile(PSTR pszInfile, PSTR pszOutfile, BOOL ASaveDebug, BOOL ACompilerWarningsAsErrors, BOOL AWarnDeprecatedCode, CompilerMessageProc pMsgProc) // I4865 // I4866 { return CompileKeyboardFileEx(pszInfile, pszOutfile, ASaveDebug, ACompilerWarningsAsErrors, AWarnDeprecatedCode, pMsgProc, CKF_KEYMAN); @@ -330,15 +310,6 @@ extern "C" BOOL __declspec(dllexport) CompileKeyboardFileEx(PSTR pszInfile, PSTR DWORD len; char str[260]; - //printf("\n---> started in CompileKeyboardFile() of kmcmpdll\n"); - - if ( flag_use_new_kmcomp ) - { - return kmcmp_CompileKeyboardFile(pszInfile, pszOutfile, ASaveDebug, ACompilerWarningsAsErrors,AWarnDeprecatedCode, kmcmpMsgproc, (void*) pMsgProc, Target); - } - - //printf("---> stayed in CompileKeyboardFile() of kmcmpdll\n"); - FSaveDebug = ASaveDebug; FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865 FWarnDeprecatedCode = AWarnDeprecatedCode; // I4866 @@ -423,15 +394,6 @@ extern "C" BOOL __declspec(dllexport) CompileKeyboardFileToBuffer(PSTR pszInfile DWORD len; char str[260]; - //printf("\n---> started in CompileKeyboardFileToBuffer() of kmcmpdll\n"); - if ( flag_use_new_kmcomp ) - { - return kmcmp_CompileKeyboardFileToBuffer( pszInfile, (void*) pfkBuffer, ACompilerWarningsAsErrors, AWarnDeprecatedCode, kmcmpMsgproc, (void*) pMsgProc, Target); - } - - //printf("---> stayed in CompileKeyboardFileToBuffer() of kmcmpdll\n"); - - FSaveDebug = TRUE; // I3681 FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865 FWarnDeprecatedCode = AWarnDeprecatedCode; // I4866 diff --git a/developer/src/kmcmpdll/compfile.h b/developer/src/kmcmpdll/compfile.h index 4fd6e68eb546a87d7f41ed22bca6421b36a13501..9e65c36600e893f302a7c687b25ef7f998fda0dc 100644 --- a/developer/src/kmcmpdll/compfile.h +++ b/developer/src/kmcmpdll/compfile.h @@ -22,6 +22,13 @@ #include "../../../common/windows/cpp/include/legacy_kmx_file.h" + +// This file is deprecated; see kmcmplib/src/compfile.h. However, as long as +// this file continues to live, we need to keep structures in it in exact sync +// with kmcmplib/src/compfile.h, as FILE_KEYBOARD is passed between kmcmplib and +// kmcmpdll. KMX_WCHAR on Windows is identical to wchar_t, so it is safe to map +// accordingly. + #define LINESIZE 8192 #define GLOBAL_BUFSIZE 4096 @@ -70,9 +77,9 @@ enum FileStoreType { FST_STORE, FST_OPTION, FST_RESERVED }; struct FILE_STORE { - DWORD dwSystemID; - WCHAR szName[SZMAX_STORENAME]; // the name of the store - PWSTR dpString; // from start of store structure + DWORD dwSystemID; + WCHAR szName[SZMAX_STORENAME]; // the name of the store + PWSTR dpString; // from start of store structure //FileStoreType fstType; BOOL fIsStore; BOOL fIsReserved; @@ -80,77 +87,75 @@ struct FILE_STORE { BOOL fIsDebug; BOOL fIsCall; int line; // TODO: int vs dword, line vs Line (see FILE_KEY, FILE_GROUP) - }; +}; typedef FILE_STORE *PFILE_STORE; struct FILE_KEY { - WCHAR Key; // WCHAR for consistency; only a byte used however - WORD LineStoreIndex; - DWORD Line; - DWORD ShiftFlags; - PWSTR dpOutput; // from start of key structure - PWSTR dpContext; // from start of key structure - }; - + WCHAR Key; // WCHAR for consistency; only a byte used however + WORD LineStoreIndex; + DWORD Line; + DWORD ShiftFlags; + PWSTR dpOutput; // from start of key structure + PWSTR dpContext; // from start of key structure +}; typedef FILE_KEY *PFILE_KEY; struct FILE_GROUP { - WCHAR szName[SZMAX_GROUPNAME]; - PFILE_KEY dpKeyArray; // address of first item in key array, from start of group structure - PWSTR dpMatch; // from start of group structure - PWSTR dpNoMatch; // from start of group structure - DWORD cxKeyArray; // in array items - BOOL fUsingKeys; // group(xx) [using keys] <-- specified or not + WCHAR szName[SZMAX_GROUPNAME]; + PFILE_KEY dpKeyArray; // address of first item in key array, from start of group structure + PWSTR dpMatch; // from start of group structure + PWSTR dpNoMatch; // from start of group structure + DWORD cxKeyArray; // in array items + BOOL fUsingKeys; // group(xx) [using keys] <-- specified or not BOOL fReadOnly; // group(xx) [readonly] <-- specified or not DWORD Line; }; typedef FILE_GROUP *PFILE_GROUP; -struct FILE_DEADKEY -{ - WCHAR szName[SZMAX_DEADKEYNAME]; +struct FILE_DEADKEY { + WCHAR szName[SZMAX_DEADKEYNAME]; }; typedef FILE_DEADKEY *PFILE_DEADKEY; -struct FILE_VKDICTIONARY -{ +struct FILE_VKDICTIONARY { WCHAR szName[SZMAX_VKDICTIONARYNAME]; }; - typedef FILE_VKDICTIONARY *PFILE_VKDICTIONARY; struct FILE_KEYBOARD { - DWORD KeyboardID; // as stored in HKEY_LOCAL_MACHINE//system//currentcontrolset//control//keyboard layouts + DWORD KeyboardID; // deprecated, unused - DWORD version; // keyboard file version with VERSION keyword + DWORD version; // keyboard file version with VERSION keyword - PFILE_STORE dpStoreArray; // address of first item in store array, from start of store structure - PFILE_GROUP dpGroupArray; // address of first item in group array, from start of group structure + PFILE_STORE dpStoreArray; // address of first item in store array, from start of store structure + PFILE_GROUP dpGroupArray; // address of first item in group array, from start of group structure - DWORD cxStoreArray; // in number of items - DWORD cxGroupArray; // in number of items - DWORD StartGroup[2]; // index of starting groups [ANSI=0, Unicode=1] + DWORD cxStoreArray; // in number of items + DWORD cxGroupArray; // in number of items + DWORD StartGroup[2]; // index of starting groups [ANSI=0, Unicode=1] - DWORD dwHotKey; // standard windows hotkey (hiword=shift/ctrl/alt stuff, loword=vkey) + DWORD dwHotKey; // standard windows hotkey (hiword=shift/ctrl/alt stuff, loword=vkey) - WCHAR szName[SZMAX_KEYBOARDNAME]; // Keyboard layout name - WCHAR szLanguageName[SZMAX_LANGUAGENAME]; // Language name - WCHAR szCopyright[SZMAX_COPYRIGHT]; // Copyright information - WCHAR szMessage[SZMAX_MESSAGE]; // General information about the keyboard - PBYTE lpBitmap; - DWORD dwBitmapSize; - DWORD dwFlags; // Flags for the keyboard file + WCHAR szName[SZMAX_KEYBOARDNAME]; // Keyboard layout name + WCHAR szLanguageName[SZMAX_LANGUAGENAME]; // Language name + WCHAR szCopyright[SZMAX_COPYRIGHT]; // Copyright information + WCHAR szMessage[SZMAX_MESSAGE]; // General information about the keyboard + PBYTE lpBitmap; + DWORD dwBitmapSize; + DWORD dwFlags; // Flags for the keyboard file - DWORD currentGroup; // temp - current processing group - DWORD currentStore; // temp - current processing store - DWORD cxDeadKeyArray; - PFILE_DEADKEY dpDeadKeyArray; // temp - dead key array + DWORD currentGroup; // temp - current processing group + DWORD currentStore; // temp - current processing store + DWORD cxDeadKeyArray; + PFILE_DEADKEY dpDeadKeyArray; // temp - dead key array DWORD cxVKDictionary; PFILE_VKDICTIONARY dpVKDictionary; // temp - virtual key dictionary - }; + + void* extra; // used by kmcmplib and its consumers; unused in kmcmpdll +}; typedef FILE_KEYBOARD *PFILE_KEYBOARD; @@ -168,34 +173,34 @@ const DWORD sz_FILE_VKDICTIONARY = sizeof(FILE_VKDICTIONARY); const DWORD sz_FILE_KEYBOARD = sizeof(FILE_KEYBOARD); struct COMPMSG { - char szText[SZMAX_ERRORTEXT]; - DWORD Line; - DWORD dwMsgCode; - }; + char szText[SZMAX_ERRORTEXT]; + DWORD Line; + DWORD dwMsgCode; +}; typedef COMPMSG *PCOMPMSG; struct COMPILEMESSAGES { - int nMessages; - int nErrors; + int nMessages; + int nErrors; - PCOMPMSG cm; + PCOMPMSG cm; - DWORD fatalCode; - char szFatalText[SZMAX_ERRORTEXT]; + DWORD fatalCode; + char szFatalText[SZMAX_ERRORTEXT]; - DWORD currentLine; - }; + DWORD currentLine; +}; typedef COMPILEMESSAGES *PCOMPILEMESSAGES; /* struct TVersion { - //int MinVersion; // 0x0500 usually - //int CompilerVersion[4]; - //int MinCompilerVersion[4]; - int KeyboardVersion; // 0x0500 usually + //int MinVersion; // 0x0500 usually + //int CompilerVersion[4]; + //int MinCompilerVersion[4]; + int KeyboardVersion; // 0x0500 usually }; extern TVersion FVersionInfo; diff --git a/developer/src/kmcmpdll/json-validation.cpp b/developer/src/kmcmpdll/json-validation.cpp index e35dc0485dcae1480b00b1f002bad5fa75b56d26..c3c1af0484318b176cacef53dec22f58c7f2f118 100644 --- a/developer/src/kmcmpdll/json-validation.cpp +++ b/developer/src/kmcmpdll/json-validation.cpp @@ -1,14 +1,11 @@ #include #include -#include "../kmcmplib/include/kmcmplibapi.h" #include typedef bool (*kmcmp_ValidateJsonMessageProc)(int64_t offset, const char* szText, void* context); -extern bool flag_use_new_kmcomp; - using nlohmann::json; using nlohmann::json_uri; using nlohmann::json_schema_draft4::json_validator; @@ -35,13 +32,6 @@ bool kmcmpMessageProc(int64_t offset, const char* szText, void* context) { extern "C" BOOL __declspec(dllexport) ValidateJsonFile(PWSTR pwszSchemaFile, PWSTR pwszJsonFile, ValidateJsonMessageProc MessageProc) { - if ( flag_use_new_kmcomp ) - { - std::fstream f(pwszSchemaFile); - std::fstream fd(pwszJsonFile); - return kmcmp_ValidateJsonFile(f, fd, kmcmpMessageProc, (void*)MessageProc); - } - std::fstream f(pwszSchemaFile); if (!f.good()) { MessageProc(-1, "Schema file could not be loaded."); diff --git a/developer/src/kmcmpdll/kcframe/kcframe.cpp b/developer/src/kmcmpdll/kcframe/kcframe.cpp index 4ac80bfe18011e36a6eb5bc909b3035f5af8afec..22402e52300d194079fae95422eea2ad6dd01d54 100644 --- a/developer/src/kmcmpdll/kcframe/kcframe.cpp +++ b/developer/src/kmcmpdll/kcframe/kcframe.cpp @@ -24,6 +24,8 @@ #include +extern "C" BOOL CompileKeyboardFile(PSTR pszInfile, PSTR pszOutfile, BOOL FSaveDebug, BOOL ACompilerWarningsAsErrors, BOOL AWarnDeprecatedCode, CompilerMessageProc pMsgProc); // I4865 // I4866 + int WINAPI msgproc(int line, DWORD dwMsgCode, LPSTR szText) { printf("line %d error %x %s\n", line, (unsigned int) dwMsgCode, szText); diff --git a/developer/src/kmcmpdll/kmcmpdll.vcxproj b/developer/src/kmcmpdll/kmcmpdll.vcxproj index c7f7e1f48f062c068d8cc15e1846f86f25942d9f..2bcc038bd00df813dc7fc9cd7db7a3f3b268d034 100644 --- a/developer/src/kmcmpdll/kmcmpdll.vcxproj +++ b/developer/src/kmcmpdll/kmcmpdll.vcxproj @@ -89,25 +89,25 @@ $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x86\$(Configuration)\src;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86) + $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86) $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator $(ProjectDir)obj\$(Platform)\$(Configuration)\ $(ProjectName).x64 $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x64\$(Configuration)\src;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator - $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x86\$(Configuration)\src;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86) + $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86) $(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\developer\src\common\include;$(KEYMAN_ROOT)\developer\src\ext\json;$(KEYMAN_ROOT)\developer\src\ext\json-schema-validator $(ProjectDir)obj\$(Platform)\$(Configuration)\ $(ProjectName).x64 $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(KEYMAN_ROOT)\developer\src\kmcmplib\build\x64\$(Configuration)\src;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) @@ -142,7 +142,7 @@ $(KEYMAN_ROOT)\windows\src\global\inc - libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) true true @@ -190,7 +190,7 @@ $(KEYMAN_ROOT)\windows\src\global\inc - libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) true true Windows @@ -233,7 +233,7 @@ $(KEYMAN_ROOT)\windows\src\global\inc - libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) true true true @@ -277,7 +277,7 @@ $(KEYMAN_ROOT)\windows\src\global\inc - libkmcmplib.a;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) + version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) true true true diff --git a/developer/src/kmcmplib/checkout-keyboards.inc.sh b/developer/src/kmcmplib/checkout-keyboards.inc.sh index defcb479ef4070a759b2767115d1949e7cc78ac1..6c76d53caa1fc9f39eeef1a9bcdce0140c57ed86 100644 --- a/developer/src/kmcmplib/checkout-keyboards.inc.sh +++ b/developer/src/kmcmplib/checkout-keyboards.inc.sh @@ -9,8 +9,8 @@ function locate_keyboards_repo() { builder_die "keyboards_commit_ref.txt does not exist, run prep.sh" fi - if [[ ! -d "$THIS_SCRIPT_PATH/tests/fixtures" ]]; then - builder_die "fixtures folder does not exist, run prep.sh" + if [[ ! -d "$THIS_SCRIPT_PATH/tests/fixtures/keyboards-repo" ]]; then + builder_die "fixtures/keyboards-repo folder does not exist, run prep.sh" fi if [[ -z "${KEYBOARDS_ROOT+x}" ]]; then diff --git a/developer/src/kmcmplib/commands.inc.sh b/developer/src/kmcmplib/commands.inc.sh index bdb8e0f139bfa19b078db1e94e0508dbf24fb2a7..d3647c41dd55e6219181ecf8d535711f432fed99 100644 --- a/developer/src/kmcmplib/commands.inc.sh +++ b/developer/src/kmcmplib/commands.inc.sh @@ -1,4 +1,7 @@ -#!/usr/bin/env bash +# shellcheck shell=bash +# no hashbang for .inc.sh + +. "$KEYMAN_ROOT/resources/locate_emscripten.inc.sh" # ---------------------------------------------------------------------------- # clean @@ -115,34 +118,6 @@ do_command() { # utility functions # ---------------------------------------------------------------------------- -# -# We don't want to rely on emcc being on the path, because Emscripten puts far -# too many things onto the path (in particular for us, node). -# -# The following comment suggests that we don't need emcc on the path. -# https://github.com/emscripten-core/emscripten/issues/4848#issuecomment-1097357775 -# -# So we try and locate emcc in common locations ourselves. The search pattern -# is: -# -# 1. Look for $EMSCRIPTEN_BASE (our primary emscripten variable), which should -# point to the folder that emcc is located in -# 2. Look for $EMCC which should point to the emcc executable -# 3. Look for emcc on the path -# -locate_emscripten() { - if [[ -z ${EMSCRIPTEN_BASE+x} ]]; then - if [[ -z ${EMCC+x} ]]; then - local EMCC=`which emcc` - [[ -z $EMCC ]] && builder_die "locate_emscripten: Could not locate emscripten (emcc) on the path or with \$EMCC or \$EMSCRIPTEN_BASE" - fi - [[ -x $EMCC ]] || builder_die "locate_emscripten: Variable EMCC ($EMCC) does not point to a valid executable emcc" - EMSCRIPTEN_BASE="$(dirname "$EMCC")" - fi - - [[ -x ${EMSCRIPTEN_BASE}/emcc ]] || builder_die "locate_emscripten: Variable EMSCRIPTEN_BASE ($EMSCRIPTEN_BASE) does not point to emcc's folder" -} - build_meson_cross_file_for_wasm() { if [ $BUILDER_OS == win ]; then local R=$(cygpath -w $(echo $EMSCRIPTEN_BASE) | sed 's_\\_\\\\_g') diff --git a/developer/src/kmcmplib/include/kmcmplibapi.h b/developer/src/kmcmplib/include/kmcmplibapi.h index 9b57d18ce645a08521e0c8d35297bb7f97d505bc..ff1b70b2c6419575bb1517e256c8fafff404c9e9 100644 --- a/developer/src/kmcmplib/include/kmcmplibapi.h +++ b/developer/src/kmcmplib/include/kmcmplibapi.h @@ -4,6 +4,8 @@ #ifdef __EMSCRIPTEN__ #include +#include + #else #define EMSCRIPTEN_KEEPALIVE #endif @@ -14,55 +16,57 @@ #define EXTERN EMSCRIPTEN_KEEPALIVE #endif -typedef struct _KMCMP_COMPILER_OPTIONS { - uint32_t dwSize; - bool ShouldAddCompilerVersion; -} KMCMP_COMPILER_OPTIONS; - -EXTERN bool kmcmp_SetCompilerOptions( - KMCMP_COMPILER_OPTIONS* options -); - -typedef int (*kmcmp_CompilerMessageProc)(int line, uint32_t dwMsgCode, char* szText, void* context); - -EXTERN bool kmcmp_CompileKeyboardFile( - char* pszInfile, - char* pszOutfile, - bool ASaveDebug, - bool ACompilerWarningsAsErrors, - bool AWarnDeprecatedCode, - kmcmp_CompilerMessageProc pMsgproc, - void* AmsgprocContext, - int target // CKF_KEYMAN || CKF_KEYMANWEB || CKF_KEYMANWEB_MODERN -); - /* Compile target */ #define CKF_KEYMAN 0 #define CKF_KEYMANWEB 1 // For legacy kmcomp compiler, to be deprecated and removed #define CKF_KEYMANWEB_MODERN 2 // For Typescript kmc-kmw compiler -EXTERN bool kmcmp_CompileKeyboardFileToBuffer( - char* pszInfile, - void* pfkBuffer, - bool ACompilerWarningsAsErrors, - bool AWarnDeprecatedCode, - kmcmp_CompilerMessageProc pMsgproc, - void* AmsgprocContext, - int Target -); +struct KMCMP_COMPILER_OPTIONS { + bool saveDebug; + bool compilerWarningsAsErrors; + bool warnDeprecatedCode; + bool shouldAddCompilerVersion; + int target; // CKF_KEYMAN, CKF_KEYMANWEB +}; -typedef bool (*kmcmp_ValidateJsonMessageProc)(int64_t offset, const char* szText, void* context); +struct KMCMP_COMPILER_RESULT { + void* kmx; + size_t kmxSize; + std::string kvksFilename; +}; -EXTERN bool kmcmp_ValidateJsonFile( - std::fstream& f, - std::fstream& fd, - kmcmp_ValidateJsonMessageProc MessageProc, - void* context +/** + * @param szText UTF-8 string +*/ +typedef int (*kmcmp_CompilerMessageProc)(int line, uint32_t dwMsgCode, const char* szText, void* context); + +// parameters in UTF-8 +// TODO typical usage: +// if(!kmcmp_LoadFileProc("filename.ico", "/tmp/filename.kmn", nullptr, &size)) { +// return error; +// } +// buf = new unsigned char[size]; +// if(!kmcmp_LoadFileProc("filename.ico", "/tmp/filename.kmn", buf, &size)) { +// delete[] buf; +// return error; +// } +typedef bool (*kmcmp_LoadFileProc)(const char* loadFilename, const char* baseFilename, void* buffer, int* bufferSize, void* context); + +/** + * @param pszInfile UTF-8 path to file.kmn + */ +EXTERN bool kmcmp_CompileKeyboard( + const char* pszInfile, + const KMCMP_COMPILER_OPTIONS& options, + kmcmp_CompilerMessageProc messageProc, + kmcmp_LoadFileProc loadFileProc, + const void* procContext, + KMCMP_COMPILER_RESULT& result ); /** - * kmcmp_ParseUnicodeSet is successful if it returns >= USET_OK + * kmcmp_parseUnicodeSet is successful if it returns >= USET_OK */ static const int KMCMP_USET_OK = 0; @@ -78,33 +82,24 @@ static const int KMCMP_ERROR_HAS_STRINGS = -2; * Error: Invalid, uses properties \p{Mn} or [:Mn:] */ static const int KMCMP_ERROR_UNSUPPORTED_PROPERTY = -3; -/** - * Error: Invalid, other unsupported feature - */ -static const int KMCMP_ERROR_UNSUPPORTED = -4; /** * Fatal: output buffer too small */ -static const int KMCMP_FATAL_OUT_OF_RANGE = -5; - -/** - * Function pointer to kmcmp_ParseUnicodeSet - */ -typedef int (*kmcmp_ParseUnicodeSetProc)(const char* szText, uint32_t* output, uint32_t outputLength); +static const int KMCMP_FATAL_OUT_OF_RANGE = -4; /** * Parse a UnicodeSet into 32-bit ranges. * For example, "[]" will return 0 (KMCMP_USET_OK) as a zero-length set. * "[" will return KMCMP_ERROR_SYNTAX_ERR, * and "[x A-C]" will return 2 and [0x41, 0x43, 0x78, 0x78] - * @param szText input txt, null terminated, in UTF-8 format + * @param text input txt, null terminated, in UTF-8 format * @param outputBuffer output buffer, owned by caller: Pairs of ranges in order * @param outputBufferSize length of output buffer. Needs to be twice the number of expected ranges * @return If >= KMCMP_USET_OK, number of ranges, otherwise one of the negative error values. */ -EXTERN int kmcmp_ParseUnicodeSet( - const char* szText, - uint32_t* outputBuffer, +EXTERN int kmcmp_parseUnicodeSet( + const std::string text, + uintptr_t outputBuffer_, uint32_t outputBufferSize ); diff --git a/developer/src/kmcmplib/include/meson.build b/developer/src/kmcmplib/include/meson.build index f98a4d8c762da2134f33054a4b18948aa3a5e658..dde8e69f6b0cd52d1c8f1ce66f2a65c413e3809f 100644 --- a/developer/src/kmcmplib/include/meson.build +++ b/developer/src/kmcmplib/include/meson.build @@ -20,7 +20,5 @@ configure_file( inc = include_directories( '.', '../../common/include', - '../../../../common/include', - '../../ext/json', - '../../ext/json-schema-validator' + '../../../../common/include' ) diff --git a/developer/src/kmcmplib/meson.build b/developer/src/kmcmplib/meson.build index ae8fd6ac62130addd0b3a1a7642b1f4530571b4b..c996981f47a896530db7f9070c90467ce98a1315 100644 --- a/developer/src/kmcmplib/meson.build +++ b/developer/src/kmcmplib/meson.build @@ -10,7 +10,8 @@ project('kmcmplib', 'cpp', 'c', default_options : ['buildtype=release', 'cpp_std=c++14', 'b_vscrt=static_from_buildtype', - 'default_library=static']) + 'default_library=static', + 'debug=true']) # Import our standard compiler defines; this is copied from # /resources/build/standard.meson.build by build.sh, because diff --git a/developer/src/kmcmplib/src/CheckFilenameConsistency.cpp b/developer/src/kmcmplib/src/CheckFilenameConsistency.cpp deleted file mode 100644 index 627298bf375de6155aeae55fb1ddff96a157df14..0000000000000000000000000000000000000000 --- a/developer/src/kmcmplib/src/CheckFilenameConsistency.cpp +++ /dev/null @@ -1,145 +0,0 @@ - -#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING 1 -#include "pch.h" -#include "compfile.h" -#include -#include "kmcmplib.h" -#include -#include "CheckFilenameConsistency.h" -#include "kmx_u16.h" -#include "filesystem.h" - -#ifdef _MSC_VER -#include -#endif - -namespace kmcmp { - extern KMX_CHAR CompileDir[260]; // TODO: this should not be a fixed buffer -} -bool IsRelativePath(KMX_CHAR const * p) { - // Relative path (returns TRUE): - // ..\...\BITMAP.BMP - // PATH\BITMAP.BMP - // BITMAP.BMP - - // Semi-absolute path (returns FALSE): - // \...\BITMAP.BMP - - // Absolute path (returns FALSE): - // C:\...\BITMAP.BMP - // \\SERVER\SHARE\...\BITMAP.BMP - - if ((*p == '\\') || (*p == '/')) return FALSE; - if (*p && *(p + 1) == ':') return FALSE; - - return TRUE; -} - -bool IsRelativePath(KMX_WCHAR const * p) { - // Relative path (returns TRUE): - // ..\...\BITMAP.BMP - // PATH\BITMAP.BMP - // BITMAP.BMP - - // Semi-absolute path (returns FALSE): - // \...\BITMAP.BMP - - // Absolute path (returns FALSE): - // C:\...\BITMAP.BMP - // \\SERVER\SHARE\...\BITMAP.BMP - - if ((*p == u'\\') || (*p == u'/'))return FALSE; - if (*p && *(p + 1) == u':') return FALSE; - - return TRUE; -} - -KMX_DWORD CheckFilenameConsistency( KMX_CHAR const * Filename, bool ReportMissingFile) { - PKMX_WCHAR WFilename = strtowstr(( KMX_CHAR *)Filename); - KMX_DWORD const result = CheckFilenameConsistency(WFilename, ReportMissingFile); - delete WFilename; - return result; -} - -KMX_DWORD CheckFilenameConsistency(KMX_WCHAR const * Filename, bool ReportMissingFile) { - // not ready yet: needs more attention-> common includes for non-Windows platforms - KMX_WCHAR Name[260]; // TODO: fixed buffer sizes bad - - - if (IsRelativePath(Filename)) { - PKMX_WCHAR WCompileDir = strtowstr(kmcmp::CompileDir); - u16ncpy(Name, WCompileDir, _countof(Name)); // I3481 - u16ncat(Name, Filename, _countof(Name)); // I3481 - delete[] WCompileDir; - } else { - u16ncpy(Name, Filename, _countof(Name)); // I3481 - } - -#ifndef _MSC_VER - // Filename consistency only needs to be checked on Windows, because other - // platforms are going to fail if the filename is inconsistent anyway! - if(!kmcmp_FileExists(Name)) { - if (ReportMissingFile) { - u16cpy(ErrExtraW, u"referenced file '"); - u16ncat(ErrExtraW, Filename, 256); - u16ncat(ErrExtraW, u"'", 256); - strcpy(ErrExtraLIB, string_from_u16string(ErrExtraW).c_str()); - AddWarning(CWARN_MissingFile); - } - return CERR_None; - } - return CERR_None; -#else - _wfinddata_t fi; - intptr_t n; - if ((n = _wfindfirst((const wchar_t*) Name, &fi)) == -1) { - if (ReportMissingFile) { - sprintf(ErrExtraLIB, "referenced file '%ls'", (wchar_t*) Filename); - AddWarning(CWARN_MissingFile); - } - return CERR_None; - } - - _findclose(n); - - KMX_WCHAR FName[_MAX_FNAME], Ext[_MAX_EXT]; - wchar_t WChName[_MAX_PATH]; - _wsplitpath_s((const wchar_t*)Filename, nullptr, 0, nullptr, 0, (wchar_t*) FName, _MAX_FNAME, (wchar_t*) Ext, _MAX_EXT); - _wmakepath_s(WChName, _MAX_PATH, nullptr, nullptr, (const wchar_t*) FName, (const wchar_t*) Ext); - if (wcscmp(WChName, fi.name) != 0) { - sprintf(ErrExtraLIB, "reference '%ls' does not match actual filename '%ls'", WChName, fi.name); - - AddWarning(CHINT_FilenameHasDifferingCase); - - } -#endif - - return CERR_None; -} - -KMX_DWORD CheckFilenameConsistencyForCalls(PFILE_KEYBOARD fk) { - // call() statements depend on a fairly ugly hack for js, - // where store(DllFunction) "my.dll:func" will look for a - // file called function.call_js. ( or should this be func.call_js ? ) - // This is ripe for rewrite! - // But let's check what we have anyway - - PFILE_STORE sp; - KMX_DWORD i, msg; - for (i = 0, sp = fk->dpStoreArray; i < fk->cxStoreArray; i++, sp++) { - if (!sp->fIsCall) continue; - - const std::u16string callsite(sp->dpString); - const auto colon = callsite.find(':'); - if (colon == std::u16string::npos) continue; - - auto func1 = callsite.substr(colon + 1); - std::u16string str_js(u".call_js"); - std::u16string func = func1+ str_js; - - if ((msg = CheckFilenameConsistency(func.c_str(), FALSE)) != CERR_None) { - return msg; - } - } - return CERR_None; -} diff --git a/developer/src/kmcmplib/src/CheckFilenameConsistency.h b/developer/src/kmcmplib/src/CheckFilenameConsistency.h deleted file mode 100644 index 39f86db391b1ac6b870d7a521f297372059bd9b4..0000000000000000000000000000000000000000 --- a/developer/src/kmcmplib/src/CheckFilenameConsistency.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "compfile.h" -#include "kmcmplib.h" - -KMX_DWORD CheckFilenameConsistencyForCalls(PFILE_KEYBOARD fk); -KMX_DWORD CheckFilenameConsistency(KMX_CHAR const * Filename, bool ReportMissingFile); -KMX_DWORD CheckFilenameConsistency(KMX_WCHAR const * Filename, bool ReportMissingFile); - -bool IsRelativePath(KMX_CHAR const * p); -bool IsRelativePath(KMX_WCHAR const * p); diff --git a/developer/src/kmcmplib/src/CheckForDuplicates.cpp b/developer/src/kmcmplib/src/CheckForDuplicates.cpp index 71268a4db59ac9eac0ef8fcdeed64931e6ab5d86..56b6195fb42d83ae232edfaa06f11730623d840b 100644 --- a/developer/src/kmcmplib/src/CheckForDuplicates.cpp +++ b/developer/src/kmcmplib/src/CheckForDuplicates.cpp @@ -18,8 +18,7 @@ KMX_DWORD CheckForDuplicateGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) noexcept { continue; } if (u16icmp(gp0->szName, gp->szName) == 0) { - u16sprintf(ErrExtraW, 256, L" Group '%ls' declared on line %d", u16fmt(gp0->szName).c_str(), gp0->Line); - strcpy(ErrExtraLIB, string_from_u16string(ErrExtraW).c_str()); + snprintf(ErrExtraLIB, ERR_EXTRA_LIB_LEN, " Group '%s' declared on line %d", string_from_u16string(gp->szName).c_str(), gp0->Line); return CERR_DuplicateGroup; } } @@ -39,8 +38,7 @@ KMX_DWORD CheckForDuplicateStore(PFILE_KEYBOARD fk, PFILE_STORE sp) noexcept { continue; } if (u16icmp(sp0->szName, sp->szName) == 0) { - u16sprintf(ErrExtraW,256, L" Store '%ls' declared on line %d", u16fmt(sp0->szName).c_str(), sp0->line); - strcpy(ErrExtraLIB, string_from_u16string(ErrExtraW).c_str()); + snprintf(ErrExtraLIB, ERR_EXTRA_LIB_LEN, " Store '%s' declared on line %d", string_from_u16string(sp0->szName).c_str(), sp0->line); return CERR_DuplicateStore; } } diff --git a/developer/src/kmcmplib/src/CompMsg.cpp b/developer/src/kmcmplib/src/CompMsg.cpp index 2fa0d68322dc1217207bb8541a62e304fee40f76..dd41336a0e9d262a96a0b4026cbaac54b80683a3 100644 --- a/developer/src/kmcmplib/src/CompMsg.cpp +++ b/developer/src/kmcmplib/src/CompMsg.cpp @@ -111,7 +111,6 @@ const struct CompilerError CompilerErrors[] = { { CERR_DuplicateStore , "A store with this name has already been defined."}, { CERR_RepeatedBegin , "Begin has already been set"}, - { CHINT_FilenameHasDifferingCase , "Casing differences may fail on some platforms: "}, { CHINT_UnreachableRule , "This rule will never be matched as another rule takes precedence"}, { CWARN_TooManyWarnings , "Too many warnings or errors"}, @@ -142,7 +141,6 @@ const struct CompilerError CompilerErrors[] = { { CWARN_NulNotFirstStatementInContext , "nul must be the first statement in the context"}, { CWARN_IfShouldBeAtStartOfContext , "if, platform and baselayout should be at start of context (after nul, if present)"}, { CWARN_KeyShouldIncludeNCaps , "Other rules which reference this key include CAPS or NCAPS modifiers, so this rule must include NCAPS modifier to avoid inconsistent matches"}, - { CWARN_MissingFile , "The referenced file could not be found: "}, { 0, nullptr } }; diff --git a/developer/src/kmcmplib/src/CompileKeyboardBuffer.cpp b/developer/src/kmcmplib/src/CompileKeyboardBuffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e90938f18a735ffca4ab986ee53004bb3703a62a --- /dev/null +++ b/developer/src/kmcmplib/src/CompileKeyboardBuffer.cpp @@ -0,0 +1,160 @@ +#include "pch.h" +#include +#include "kmcmplib.h" +#include "CheckNCapsConsistency.h" +#include "DeprecationChecks.h" +#include "versioning.h" +#include "CompileKeyboardBuffer.h" +#include "../../../../common/windows/cpp/include/keymanversion.h" + +bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk) +{ + PKMX_WCHAR str, p; + + KMX_DWORD msg; + + kmcmp::FMnemonicLayout = FALSE; + + if (!fk) { + AddCompileError(CERR_SomewhereIGotItWrong); + return FALSE; + } + + str = new KMX_WCHAR[LINESIZE]; + if (!str) { + AddCompileError(CERR_CannotAllocateMemory); + return FALSE; + } + + fk->KeyboardID = 0; + fk->version = 0; + fk->dpStoreArray = NULL; + fk->dpGroupArray = NULL; + fk->cxStoreArray = 0; + fk->cxGroupArray = 0; + fk->StartGroup[0] = fk->StartGroup[1] = -1; + fk->szName[0] = 0; + fk->szCopyright[0] = 0; + fk->dwFlags = KF_AUTOMATICVERSION; + fk->currentGroup = 0xFFFFFFFF; + fk->currentStore = 0; + fk->cxDeadKeyArray = 0; + fk->dpDeadKeyArray = NULL; + fk->cxVKDictionary = 0; // I3438 + fk->dpVKDictionary = NULL; // I3438 + fk->extra->kvksFilename = u""; +/* fk->szMessage[0] = 0; + fk->szLanguageName[0] = 0;*/ + fk->dwBitmapSize = 0; + fk->dwHotKey = 0; + + kmcmp::BeginLine[BEGIN_ANSI] = -1; + kmcmp::BeginLine[BEGIN_UNICODE] = -1; + kmcmp::BeginLine[BEGIN_NEWCONTEXT] = -1; + kmcmp::BeginLine[BEGIN_POSTKEYSTROKE] = -1; + + + /* Add a store for the Keyman 6.0 copyright information string */ + + if(kmcmp::FShouldAddCompilerVersion) { + u16sprintf(str,LINESIZE, L"Created with Keyman Developer version %d.%d.%d.%d", KEYMAN_VersionMajor, KEYMAN_VersionMinor, KEYMAN_VersionPatch, 0); + AddStore(fk, TSS_KEYMANCOPYRIGHT, str); + } + + /* Add a system store for the Keyman edition number */ + AddStore(fk, TSS_CUSTOMKEYMANEDITION, u"0"); + AddStore(fk, TSS_CUSTOMKEYMANEDITIONNAME, u"Keyman"); + + int offset = 0; + + // must preprocess for group and store names -> this isn't really necessary, but never mind! + while ((msg = ReadLine(infile, sz, offset, str, TRUE)) == CERR_None) + { + p = str; + switch (LineTokenType(&p)) + { + case T_VERSION: + *(p + 4) = 0; + if ((msg = AddStore(fk, TSS_VERSION, p)) != CERR_None) { + AddCompileError(msg); + return FALSE; + } + break; + + case T_GROUP: + if ((msg = ProcessGroupLine(fk, p)) != CERR_None) { + AddCompileError(msg); + return FALSE; + } + break; + + case T_STORE: + if ((msg = ProcessStoreLine(fk, p)) != CERR_None) { + AddCompileError(msg); + return FALSE; + } + break; + + default: + break; + } + } + + if (msg != CERR_EndOfFile) { + AddCompileError(msg); + return FALSE; + } + + offset = 0; + kmcmp::currentLine = 0; + + /* Reindex the list of codeconstants after stores added */ + + kmcmp::CodeConstants->reindex(); + + /* ReadLine will automatically skip over $Keyman lines, and parse wrapped lines */ + while ((msg = ReadLine(infile, sz, offset, str, FALSE)) == CERR_None) + { + msg = ParseLine(fk, str); + if (msg != CERR_None) { + AddCompileError(msg); + return FALSE; + } + } + + if (msg != CERR_EndOfFile) { + AddCompileError(msg); + return FALSE; + } + + ProcessGroupFinish(fk); + + if (kmcmp::FSaveDebug) kmcmp::RecordDeadkeyNames(fk); + + /* Add the compiler version as a system store */ + if ((msg = kmcmp::AddCompilerVersionStore(fk)) != CERR_None) { + AddCompileError(msg); + return FALSE; + } + + if ((msg = BuildVKDictionary(fk)) != CERR_None) { + AddCompileError(msg); + return FALSE; + } + + delete str; + + if (!kmcmp::CheckKeyboardFinalVersion(fk)) { + return FALSE; + } + + /* Warn on inconsistent use of NCAPS */ + if (!kmcmp::FMnemonicLayout) { + CheckNCapsConsistency(fk); + } + + /* Flag presence of deprecated features */ + kmcmp::CheckForDeprecatedFeatures(fk); + + return TRUE; +} diff --git a/developer/src/kmcmplib/src/CompileKeyboardBuffer.h b/developer/src/kmcmplib/src/CompileKeyboardBuffer.h new file mode 100644 index 0000000000000000000000000000000000000000..78bac6665399d721b52baf563c62de3732a570fb --- /dev/null +++ b/developer/src/kmcmplib/src/CompileKeyboardBuffer.h @@ -0,0 +1,5 @@ +#pragma once + +#include "compfile.h" + +bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk); diff --git a/developer/src/kmcmplib/src/Compiler.cpp b/developer/src/kmcmplib/src/Compiler.cpp index 4292dd21d9c603e48c10affb06775be78f3912ba..623e4f8d06669f9db2669869cebaeac59337e34a 100644 --- a/developer/src/kmcmplib/src/Compiler.cpp +++ b/developer/src/kmcmplib/src/Compiler.cpp @@ -94,11 +94,12 @@ #include "CasedKeys.h" #include #include -#include "CheckFilenameConsistency.h" +#include +#include + #include "UnreachableRules.h" #include "CheckForDuplicates.h" #include "kmx_u16.h" -#include "filesystem.h" #include /* These macros are adapted from winnt.h and legacy use only */ @@ -110,8 +111,7 @@ using namespace kmcmp; - char ErrExtraLIB[ERR_EXTRA_LIB_LEN]; - KMX_WCHAR ErrExtraW[ERR_EXTRA_W_LEN]; + char ErrExtraLIB[ERR_EXTRA_LIB_LEN]; // utf-8 KMX_BOOL AWarnDeprecatedCode_GLOBAL_LIB; namespace kmcmp{ @@ -122,7 +122,6 @@ namespace kmcmp{ KMX_BOOL FMnemonicLayout = FALSE; KMX_BOOL FOldCharPosMatching = FALSE; int CompileTarget; - KMX_CHAR CompileDir[260]; // TODO: this should not be a fixed buffer int BeginLine[4]; KMX_BOOL IsValidCallStore(PFILE_STORE fs); @@ -230,6 +229,8 @@ enum LinePrefixType { lptNone, lptKeymanAndKeymanWeb, lptKeymanWebOnly, lptKeyma /* Compile target */ kmcmp_CompilerMessageProc msgproc = NULL; +kmcmp_LoadFileProc loadfileproc = NULL; + void* msgprocContext = NULL; int kmcmp::currentLine = 0; @@ -784,7 +785,7 @@ KMX_DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PKMX_WCHAR p) return CheckForDuplicateStore(fk, sp); } -KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, KMX_WCHAR const * str, KMX_DWORD *dwStoreID) +KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, const KMX_WCHAR * str, KMX_DWORD *dwStoreID) { PFILE_STORE sp; sp = new FILE_STORE[fk->cxStoreArray + 1]; @@ -862,7 +863,6 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE int i, j; KMX_DWORD msg; PKMX_WCHAR p, q; - KMX_CHAR *pp; if (!pssBuf) pssBuf = new KMX_WCHAR[GLOBAL_BUFSIZE]; PKMX_WCHAR buf = pssBuf; @@ -912,13 +912,9 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE case TSS_INCLUDECODES: VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes); - pp = wstrtostr(sp->dpString); - if (!kmcmp::CodeConstants->LoadFile(pp)) - { - delete[] pp; + if (!kmcmp::CodeConstants->LoadFile(fk, sp->dpString)) { return CERR_CannotLoadIncludeFile; } - delete[] pp; kmcmp::CodeConstants->reindex(); // I4982 break; @@ -1023,6 +1019,9 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE case TSS_VISUALKEYBOARD: VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); { + // Store extra metadata for callers as we mutate this store during + // compilation + fk->extra->kvksFilename = sp->dpString; // Strip path from the store, leaving bare filename only p = sp->dpString; @@ -1042,21 +1041,8 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE pp2[4] = 0; } - if(CompileTarget != CKF_KEYMANWEB_MODERN) { - // When we compile to kmx, we want to save this info into the .kmx, and - // for the legacy kmw compiler, but for kmc-kmw, we need the original - // source file for the kmc-kmw compiler process - delete[] sp->dpString; - sp->dpString = q; - } - - if ((msg = CheckFilenameConsistency(q, FALSE)) != CERR_None) { - return msg; - } - - if(CompileTarget == CKF_KEYMANWEB_MODERN) { - delete[] q; - } + delete[] sp->dpString; + sp->dpString = q; } break; case TSS_KMW_RTL: @@ -1067,16 +1053,10 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE case TSS_KMW_HELPFILE: case TSS_KMW_EMBEDJS: VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); - if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) { - return msg; - } break; case TSS_KMW_EMBEDCSS: VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyEmbedCSS); - if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) { - return msg; - } break; case TSS_TARGETS: // I4504 @@ -1126,9 +1106,6 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE case TSS_LAYOUTFILE: // I3483 VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyLayoutFile); // I4140 - if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) { - return msg; - } // Used by KMW compiler break; @@ -2897,7 +2874,7 @@ KMX_BOOL kmcmp::CheckStoreUsage(PFILE_KEYBOARD fk, int storeIndex, KMX_BOOL fIsS return TRUE; } -KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, FILE* fp_out) +KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, KMX_BYTE**data, size_t& dataSize) { PFILE_GROUP fgp; PFILE_STORE fsp; @@ -2912,6 +2889,9 @@ KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, FILE* fp_out) size_t size; KMX_DWORD i, j; + *data = nullptr; + dataSize = 0; + // Calculate how much memory to allocate size = sizeof(COMP_KEYBOARD) + @@ -3065,20 +3045,13 @@ KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, FILE* fp_out) SetChecksum(buf, &ck->dwCheckSum, (KMX_DWORD)size); - KMX_DWORD dwBytesWritten = 0; - dwBytesWritten = (KMX_DWORD)fwrite(buf,1, (KMX_DWORD)size , fp_out); - - if (dwBytesWritten != size) { - delete[] buf; - return CERR_UnableToWriteFully; - } - - delete[] buf; + *data = buf; + dataSize = size; return CERR_None; } -KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess) +KMX_DWORD ReadLine(KMX_BYTE* infile, int sz, int& offset, PKMX_WCHAR wstr, KMX_BOOL PreProcess) { KMX_DWORD len; PKMX_WCHAR p; @@ -3086,21 +3059,26 @@ KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess) KMX_DWORD n; KMX_WCHAR currentQuotes = 0; KMX_WCHAR str[LINESIZE + 3]; - len = (KMX_DWORD)fread( str , 1 ,LINESIZE * 2,fp_in); - if (ferror(fp_in) ) return CERR_CannotReadInfile; - len /= 2; - str[len] = 0; auto cur = ftell(fp_in); - fseek(fp_in, 0, SEEK_END); - auto fsize = ftell(fp_in); - fseek(fp_in, cur, SEEK_SET); - if (cur == fsize) + if(offset >= sz) { + return CERR_EndOfFile; + } - // \r\n is still added here even though Linux doesn`t use \r. - // This is to ensure to still have a working windows-only-version - u16ncat(str, u"\r\n", _countof(str)); // I3481 // Always a "\r\n" to the EOF, avoids funny bugs + len = offset + LINESIZE*2 > sz ? sz-offset : LINESIZE*2; + memcpy(str, infile+offset, len); + offset += len; + len /= 2; + str[len] = 0; + + if(offset == sz) { + // \r\n is still added here even though Linux doesn`t use \r. + // This is to ensure to still have a working windows-only-version + u16ncat(str, u"\r\n", _countof(str)); // I3481 // Always a "\r\n" to the EOF, avoids funny bugs + } - if (len == 0) return CERR_EndOfFile; + if (len == 0) { + return CERR_EndOfFile; + } // neccessary to add this block for using on non-windows platforms (removes all \r for platforms that use \n instead of \r\n) for (p = str, n = 0; n < len; n++, p++) { @@ -3182,9 +3160,13 @@ KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess) return (PreProcess ? CERR_None : CERR_LineTooLong); } - if (*p == L'\n') kmcmp::currentLine++; + kmcmp::currentLine++; - fseek(fp_in, -(int)(len * 2 - (int)(p - str) * 2 - 2), SEEK_CUR); + offset -= (int)(len * 2 - (int)(p - str) * 2 - 2); + if(offset >= sz) { + // If we've appended a \n, we can go past EOF + offset = sz; + } p--; while (p >= str && iswspace(*p)) p--; @@ -3230,58 +3212,38 @@ KMX_BOOL IsSameToken(PKMX_WCHAR *p, KMX_WCHAR const * token) return FALSE; } -KMX_DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PKMX_WCHAR szName, PKMX_DWORD FileSize, PKMX_BYTE *Buf) +static bool endsWith(const std::string& str, const std::string& suffix) { - FILE *fp; - KMX_WCHAR szNewName[260]; - - if (IsRelativePath(szName)) - { - PKMX_WCHAR WCompileDir = strtowstr(kmcmp::CompileDir); - u16ncpy(szNewName, WCompileDir, _countof(szNewName)); // I3481 - u16ncat(szNewName,szName, _countof(szNewName )); // I3481 - } - else - u16ncpy(szNewName, szName, _countof(szNewName)); // I3481 - - fp=Open_File(szNewName, u"rb"); - - if ( fp == NULL) - { - // else if filename.bmp is not in the folder -> attempt to open filename.bmp.bmp ! - if ( u16cmp(szNewName+u16len(szNewName)-4, u".bmp") ) - u16ncat(szNewName, u".bmp", _countof(szNewName)); // I3481 + return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix); +} - fp= Open_File(szNewName, u"rb"); +KMX_DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PKMX_WCHAR szName, PKMX_DWORD FileSize, PKMX_BYTE *Buf) +{ + auto szNameUtf8 = string_from_u16string(szName); - if ( fp == NULL) + if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), nullptr, (int*) FileSize, msgprocContext)) { + // Append .bmp and try again + if(endsWith(szNameUtf8, ".bmp")) { return CERR_CannotReadBitmapFile; + } + szNameUtf8.append(".bmp"); + if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), nullptr, (int*) FileSize, msgprocContext)) { + return CERR_CannotReadBitmapFile; + } } - KMX_DWORD msg; - if ((msg = CheckFilenameConsistency(szNewName, FALSE)) != CERR_None) { - return msg; - } - - fseek(fp, 0, SEEK_END); - *FileSize = (KMX_DWORD)ftell(fp); - fseek(fp ,0,SEEK_SET); - if (*FileSize < 0) { - fclose(fp); + if(*FileSize < 2) { + // Zero-byte file is invalid; 2 byte file is too, but we only really care + // about the prolog at this point so we don't overrun our buffer return CERR_CannotReadBitmapFile; } - if (*FileSize < 2) return CERR_CannotReadBitmapFile; *Buf = new KMX_BYTE[*FileSize]; - - if (fread(*Buf, 1, *FileSize, fp) < (size_t) *FileSize) { - delete[] * Buf; - *Buf = NULL; + if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), *Buf, (int*) FileSize, msgprocContext)) { + delete[] *Buf; return CERR_CannotReadBitmapFile; } - fclose(fp); - /* Test for version 7.0 icon support */ if (*((PKMX_CHAR)*Buf) != 'B' && *(((PKMX_CHAR)*Buf) + 1) != 'M') { VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); @@ -3437,81 +3399,54 @@ KMX_BOOL kmcmp::IsValidCallStore(PFILE_STORE fs) return i == 1; } -FILE* CreateTempFile() -{ - return tmpfile(); +/////////////////// + +bool hasPreamble(std::u16string result) { + return result.size() > 0 && result[0] == 0xFEFF; } -/////////////////// +#include "unicode/ucnv.h" -FILE* UTF16TempFromUTF8(FILE* fp_in , KMX_BOOL hasPreamble) -{ - FILE *fp_out = CreateTempFile(); - if(fp_out == NULL) // I3228 // I3510 - { - fclose(fp_in); - return NULL; //return INVALID_HANDLE_VALUE; _S2 can I exchange that? +bool UTF16TempFromUTF8(KMX_BYTE* infile, int sz, KMX_BYTE** tempfile, int *sz16) { + if(sz == 0) { + return FALSE; } - PKMX_BYTE buf, p; - PKMX_WCHAR outbuf, poutbuf; - KMX_DWORD len; - KMX_DWORD len2; - KMX_WCHAR prolog = 0xFEFF; - fwrite(&prolog,2, 1, fp_out); - - fseek(fp_in, 0, SEEK_END); - len = (KMX_DWORD)ftell(fp_in); - fseek(fp_in, 0, SEEK_SET); - if (hasPreamble) { - fseek( fp_in,3,SEEK_SET); // Cut off UTF-8 marker - len -= 3; - } - - buf = new KMX_BYTE[len + 1]; // null terminated - outbuf = new KMX_WCHAR[len + 1]; - - len2= (KMX_DWORD)fread(buf,1,len,fp_in); - if (len2) { - buf[len2] = 0; - p = buf; - poutbuf = outbuf; - if (hasPreamble) { - // We have a preamble, so we attempt to read as UTF-8 and allow conversion errors to be filtered. This is not great for a - // compiler but matches existing behaviour -- in future versions we may not do lenient conversion. - ConvertUTF8toUTF16(&p, &buf[len2], (UTF16 **)&poutbuf, (const UTF16 *)&outbuf[len], lenientConversion); - fwrite(outbuf, (KMX_DWORD)(poutbuf - outbuf) * 2 , 1, fp_out); + std::u16string result; + + try { + std::wstring_convert, char16_t> converter; + result = converter.from_bytes((char*)infile, (char*)infile+sz); + } catch(std::range_error e) { + UErrorCode status = U_ZERO_ERROR; + // TODO: we need ICU data files here #8884 + UConverter* conv = ucnv_open("windows-1252", &status); + if(U_FAILURE(status)) { + return FALSE; } - else { - // No preamble, so we attempt to read as strict UTF-8 and fall back to ANSI if that fails - ConversionResult cr = ConvertUTF8toUTF16(&p, &buf[len2], (UTF16 **)&poutbuf, (const UTF16 *)&outbuf[len], strictConversion); - if (cr == sourceIllegal) { - // Not a valid UTF-8 file, so fall back to ANSI - // AddCompileError(CHINT_NonUnicodeFile); - // note, while this message is defined, for now we will not emit it - // because we don't support HINT/INFO messages yet and we don't want - // this to cause a blocking compile at this stage - // do strtowstr only when no invalid characters are found - if( p==0){ - poutbuf = strtowstr((PKMX_STR)buf); - fwrite(poutbuf, (KMX_DWORD)u16len(poutbuf) * 2 , 1, fp_out); - delete[] poutbuf; - } - else - AddCompileError(CERR_InvalidCharacter); - } - else { - fwrite(outbuf, (KMX_DWORD)(poutbuf - outbuf) * 2 , 1, fp_out); - } + char16_t* dest = new char16_t[sz*2]; + ucnv_toUChars(conv, dest, sz*2, (char*)infile, sz, &status); + if(U_FAILURE(status)) { + delete[] dest; + return FALSE; } + + result = dest; + delete[] dest; } - fclose( fp_in); - delete[] buf; - delete[] outbuf; - fseek( fp_out,2,SEEK_SET); - return fp_out; + if(hasPreamble(result)) { + *sz16 = result.size() * 2 - 2; + *tempfile = new KMX_BYTE[*sz16]; + memcpy(*tempfile, result.c_str() + 1, *sz16); + } else { + *sz16 = result.size() * 2; + *tempfile = new KMX_BYTE[*sz16]; + memcpy(*tempfile, result.c_str(), *sz16); + } + + return TRUE; } PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, KMX_DWORD dwSystemID) diff --git a/developer/src/kmcmplib/src/CompilerInterfaces.cpp b/developer/src/kmcmplib/src/CompilerInterfaces.cpp index 95f01353f4a93a46362f6919c37e47fb45b01bb0..fdfbdf19613cc26d3de1161ca47e2dfdeb9c7bd7 100644 --- a/developer/src/kmcmplib/src/CompilerInterfaces.cpp +++ b/developer/src/kmcmplib/src/CompilerInterfaces.cpp @@ -1,369 +1,110 @@ #include "pch.h" - #include #include #include "kmcmplib.h" -#include "filesystem.h" -#include "CheckFilenameConsistency.h" -#include "CheckNCapsConsistency.h" -#include "DeprecationChecks.h" -#include "versioning.h" #include "../../../../common/windows/cpp/include/ConvertUTF.h" -#include "../../../../common/windows/cpp/include/keymanversion.h" - -#define SetError(err) { if(AddCompileError(err) || (err & CERR_FATAL)) return FALSE; } - -bool CompileKeyboardHandle(FILE* fp_in, PFILE_KEYBOARD fk); - -EXTERN bool kmcmp_SetCompilerOptions(KMCMP_COMPILER_OPTIONS* options) { - //printf("°°-> changed to SetCompilerOptions() of kmcmplib \n"); - if(!options || options->dwSize < sizeof(KMCMP_COMPILER_OPTIONS)) { - return FALSE; - } - kmcmp::FShouldAddCompilerVersion = options->ShouldAddCompilerVersion; - return TRUE; -} - -#ifdef __EMSCRIPTEN__ - -/* - WASM interface for compiler message callback -*/ -EM_JS(int, wasm_msgproc, (int line, int msgcode, char* text, char* context), { - const proc = globalThis[UTF8ToString(context)]; - if(!proc || typeof proc != 'function') { - console.log(`[${line}: ${msgcode}: ${UTF8ToString(text)}]`); - return 0; - } else { - return proc(line, msgcode, UTF8ToString(text)); - } -}); - -int wasm_CompilerMessageProc(int line, uint32_t dwMsgCode, char* szText, void* context) { - char* msgProc = static_cast(context); - return wasm_msgproc(line, dwMsgCode, szText, msgProc); -} - -EXTERN bool kmcmp_Wasm_SetCompilerOptions(int ShouldAddCompilerVersion) { - KMCMP_COMPILER_OPTIONS options; - options.dwSize = sizeof(KMCMP_COMPILER_OPTIONS); - options.ShouldAddCompilerVersion = ShouldAddCompilerVersion; - return kmcmp_SetCompilerOptions(&options); -} - -EXTERN bool kmcmp_Wasm_CompileKeyboardFile(char* pszInfile, - char* pszOutfile, int ASaveDebug, int ACompilerWarningsAsErrors, - int AWarnDeprecatedCode, char* msgProc, int target +#include "CompileKeyboardBuffer.h" + +EXTERN bool kmcmp_CompileKeyboard( + const char* pszInfile, + const KMCMP_COMPILER_OPTIONS& options, + kmcmp_CompilerMessageProc messageProc, + kmcmp_LoadFileProc loadFileProc, + const void* procContext, + KMCMP_COMPILER_RESULT& result ) { - return kmcmp_CompileKeyboardFile( - pszInfile, - pszOutfile, - ASaveDebug, - ACompilerWarningsAsErrors, - AWarnDeprecatedCode, - wasm_CompilerMessageProc, - msgProc, - target - ); -} -#endif - -EXTERN bool kmcmp_CompileKeyboardFile(char* pszInfile, - char* pszOutfile, bool ASaveDebug, bool ACompilerWarningsAsErrors, - bool AWarnDeprecatedCode, kmcmp_CompilerMessageProc pMsgproc, void* AmsgprocContext, int Target -) { - FILE* fp_in = NULL; - FILE* fp_out = NULL; - KMX_CHAR str[260]; - - //printf("°°-> changed to CompileKeyboardFile() of kmcmplib \n"); - - kmcmp::FSaveDebug = ASaveDebug; - kmcmp::FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865 - AWarnDeprecatedCode_GLOBAL_LIB = AWarnDeprecatedCode; - - kmcmp::CompileTarget = Target; - - if (!pMsgproc || !pszInfile || !pszOutfile) SetError(CERR_BadCallParams); - - PKMX_STR p; - - if ((p = strrchr_slash(pszInfile)) != nullptr) - { - strncpy(kmcmp::CompileDir, pszInfile, (int)(p - pszInfile + 1)); // I3481 - kmcmp::CompileDir[(int)(p - pszInfile + 1)] = 0; - } - else - kmcmp::CompileDir[0] = 0; - - msgproc = pMsgproc; - msgprocContext = AmsgprocContext; - kmcmp::currentLine = 0; - kmcmp::nErrors = 0; - - fp_in = Open_File(pszInfile, "rb"); - - if (fp_in == NULL) { - SetError(CERR_InfileNotExist); - } - - // Transfer the file to a memory stream for processing UTF-8 or ANSI to UTF-16? - // What about really large files? Transfer to a temp file... - if (!fread(str, 1, 3, fp_in)) { - fclose(fp_in); - SetError(CERR_CannotReadInfile); - } - - fseek(fp_in, 0, SEEK_SET); - if (str[0] == UTF8Sig[0] && str[1] == UTF8Sig[1] && str[2] == UTF8Sig[2]) - fp_in = UTF16TempFromUTF8(fp_in, TRUE); - else if (str[0] == UTF16Sig[0] && str[1] == UTF16Sig[1]) - fseek(fp_in, 2, SEEK_SET); - else - fp_in = UTF16TempFromUTF8(fp_in, FALSE); - if (fp_in == NULL) { - SetError(CERR_CannotCreateTempfile); - } - - fp_out = Open_File(pszOutfile, "wb"); - - if (fp_out == NULL) { - SetError(CERR_CannotCreateOutfile); - } FILE_KEYBOARD fk; - kmcmp::CodeConstants = new kmcmp::NamedCodeConstants; - bool result = CompileKeyboardHandle(fp_in, &fk); - if(result) { - KMX_DWORD msg; - if ((msg = WriteCompiledKeyboard(&fk, fp_out)) != CERR_None) { - result = FALSE; - AddCompileError(msg); - } - } else { - AddCompileError(CERR_InvalidValue); - } + fk.extra = new FILE_KEYBOARD_EXTRA; + fk.extra->kmnFilename = pszInfile; - fclose(fp_in); - fclose(fp_out); - - delete kmcmp::CodeConstants; + kmcmp::FSaveDebug = options.saveDebug; // I3681 + kmcmp::FCompilerWarningsAsErrors = options.compilerWarningsAsErrors; // I4865 + AWarnDeprecatedCode_GLOBAL_LIB = options.warnDeprecatedCode; + kmcmp::FShouldAddCompilerVersion = options.shouldAddCompilerVersion; + kmcmp::CompileTarget = options.target; - if (kmcmp::nErrors > 0) - { - remove(pszOutfile); + if (!messageProc || !loadFileProc || !pszInfile) { + AddCompileError(CERR_BadCallParams); return FALSE; } - return result; -} - - - -EXTERN bool kmcmp_CompileKeyboardFileToBuffer(char* pszInfile, void* pfkBuffer, bool ACompilerWarningsAsErrors, bool AWarnDeprecatedCode, - kmcmp_CompilerMessageProc pMsgproc, void* AmsgprocContext, int Target) // I4865 // I4866 -{ - //printf("°°-> changed to CompileKeyboardFileToBuffer() of kmcmplib \n"); - FILE* fp_in = NULL; - KMX_CHAR str[260]; - - kmcmp::FSaveDebug = TRUE; // I3681 - kmcmp::FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865 - AWarnDeprecatedCode_GLOBAL_LIB = AWarnDeprecatedCode; - kmcmp::CompileTarget = Target; - - if (!pMsgproc || !pszInfile || !pfkBuffer) { - SetError(CERR_BadCallParams); - } - - PKMX_STR p; - - if ((p = strrchr_slash(pszInfile)) != nullptr) { - strncpy(kmcmp::CompileDir, pszInfile, (int)(p - pszInfile + 1)); // I3481 - kmcmp::CompileDir[(int)(p - pszInfile + 1)] = 0; - } - else { - kmcmp::CompileDir[0] = 0; - } - - msgproc = pMsgproc; - msgprocContext = AmsgprocContext; + msgproc = messageProc; + loadfileproc = loadFileProc; + msgprocContext = (void*)procContext; kmcmp::currentLine = 0; kmcmp::nErrors = 0; - fp_in = Open_File(pszInfile,"rb"); - - if (fp_in == NULL) { - SetError(CERR_InfileNotExist); - } - - // Transfer the file to a memory stream for processing UTF-8 or ANSI to UTF-16? - // What about really large files? Transfer to a temp file... - - if( !fread(str,1,3,fp_in)) - { - fclose(fp_in); - SetError(CERR_CannotReadInfile); - } - - fseek( fp_in,0,SEEK_SET); - if (str[0] == UTF8Sig[0] && str[1] == UTF8Sig[1] && str[2] == UTF8Sig[2]) - fp_in = UTF16TempFromUTF8(fp_in, TRUE); - else if (str[0] == UTF16Sig[0] && str[1] == UTF16Sig[1]) - fseek( fp_in,2,SEEK_SET); - else - fp_in = UTF16TempFromUTF8(fp_in, FALSE); - - kmcmp::CodeConstants = new kmcmp::NamedCodeConstants; - - bool result = CompileKeyboardHandle(fp_in, static_cast(pfkBuffer)); - delete kmcmp::CodeConstants; - fclose(fp_in); - - if (kmcmp::nErrors > 0) { + int sz; + if(!loadFileProc(pszInfile, "", nullptr, &sz, msgprocContext)) { + AddCompileError(CERR_InfileNotExist); return FALSE; } - return result; -} - -bool CompileKeyboardHandle(FILE* fp_in, PFILE_KEYBOARD fk) -{ - PKMX_WCHAR str, p; - - KMX_DWORD msg; - - kmcmp::FMnemonicLayout = FALSE; - - if (!fk) { - SetError(CERR_SomewhereIGotItWrong); - } - - str = new KMX_WCHAR[LINESIZE]; - if (!str) { - SetError(CERR_CannotAllocateMemory); - } - - fk->KeyboardID = 0; - fk->version = 0; - fk->dpStoreArray = NULL; - fk->dpGroupArray = NULL; - fk->cxStoreArray = 0; - fk->cxGroupArray = 0; - fk->StartGroup[0] = fk->StartGroup[1] = -1; - fk->szName[0] = 0; - fk->szCopyright[0] = 0; - fk->dwFlags = KF_AUTOMATICVERSION; - fk->currentGroup = 0xFFFFFFFF; - fk->currentStore = 0; - fk->cxDeadKeyArray = 0; - fk->dpDeadKeyArray = NULL; - fk->cxVKDictionary = 0; // I3438 - fk->dpVKDictionary = NULL; // I3438 - -/* fk->szMessage[0] = 0; - fk->szLanguageName[0] = 0;*/ - fk->dwBitmapSize = 0; - fk->dwHotKey = 0; - - kmcmp::BeginLine[BEGIN_ANSI] = -1; - kmcmp::BeginLine[BEGIN_UNICODE] = -1; - kmcmp::BeginLine[BEGIN_NEWCONTEXT] = -1; - kmcmp::BeginLine[BEGIN_POSTKEYSTROKE] = -1; - - /* Add a store for the Keyman 6.0 copyright information string */ - - if(kmcmp::FShouldAddCompilerVersion) { - u16sprintf(str,LINESIZE, L"Created with Keyman Developer version %d.%d.%d.%d", KEYMAN_VersionMajor, KEYMAN_VersionMinor, KEYMAN_VersionPatch, 0); - AddStore(fk, TSS_KEYMANCOPYRIGHT, str); + if(sz < 3) { + // Technically, a 3 byte file can never be a valid .kmn, so we can shortcut + // here and avoid testing outside memory bounds for looking at BOM + AddCompileError(CERR_CannotReadInfile); + return FALSE; } - /* Add a system store for the Keyman edition number */ - u16sprintf(str, LINESIZE, L"%d", 0); // I3481 - AddStore(fk, TSS_CUSTOMKEYMANEDITION, str); - - PKMX_WCHAR tbuf = strtowstr((KMX_CHAR*) "Keyman"); - AddStore(fk, TSS_CUSTOMKEYMANEDITIONNAME, tbuf); - delete tbuf; - - // must preprocess for group and store names -> this isn't really necessary, but never mind! - while ((msg = ReadLine(fp_in, str, TRUE)) == CERR_None) - { - p = str; - switch (LineTokenType(&p)) - { - case T_VERSION: - *(p + 4) = 0; - if ((msg = AddStore(fk, TSS_VERSION, p)) != CERR_None) SetError(msg); - break; - - case T_GROUP: - if ((msg = ProcessGroupLine(fk, p)) != CERR_None) SetError(msg); - break; - - case T_STORE: - if ((msg = ProcessStoreLine(fk, p)) != CERR_None) SetError(msg); - break; - - default: - break; - } + KMX_BYTE* infile = new KMX_BYTE[sz+1]; + if(!infile) { + AddCompileError(CERR_CannotAllocateMemory); + return FALSE; } - - if (msg != CERR_EndOfFile) { - SetError(msg); + if(!loadFileProc(pszInfile, "", infile, &sz, msgprocContext)) { + delete[] infile; + AddCompileError(CERR_CannotReadInfile); + return FALSE; } + infile[sz] = 0; // zero-terminate for safety, not technically needed but helps avoid memory bugs - fseek( fp_in,2,SEEK_SET); - kmcmp::currentLine = 0; - - /* Reindex the list of codeconstants after stores added */ - - kmcmp::CodeConstants->reindex(); - - /* ReadLine will automatically skip over $Keyman lines, and parse wrapped lines */ - while ((msg = ReadLine(fp_in, str, FALSE)) == CERR_None) - { - msg = ParseLine(fk, str); - if (msg != CERR_None) { - SetError(msg); + int offset = 0; + if(infile[0] == (KMX_BYTE) UTF16Sig[0] && infile[1] == (KMX_BYTE) UTF16Sig[1]) { + // UTF-16 source file + offset = 2; + } else { + // UTF-8 source file + KMX_BYTE* infile16; + int sz16; + if(!UTF16TempFromUTF8(infile, sz, &infile16, &sz16)) { + delete[] infile; + AddCompileError(CERR_CannotCreateTempfile); + return FALSE; } + delete[] infile; + infile = infile16; + sz = sz16; } - if (msg != CERR_EndOfFile) { - SetError(msg); - } - - ProcessGroupFinish(fk); - - if (kmcmp::FSaveDebug) kmcmp::RecordDeadkeyNames(fk); + kmcmp::CodeConstants = new kmcmp::NamedCodeConstants; + bool success = CompileKeyboardBuffer(infile+offset, sz-offset, &fk); + delete kmcmp::CodeConstants; - /* Add the compiler version as a system store */ - if ((msg = kmcmp::AddCompilerVersionStore(fk)) != CERR_None) { - SetError(msg); - } + delete[] infile; - if ((msg = BuildVKDictionary(fk)) != CERR_None) { - SetError(msg); // I3438 + if (kmcmp::nErrors > 0 || !success) { + return FALSE; } - if ((msg = CheckFilenameConsistencyForCalls(fk)) != CERR_None) { - SetError(msg); - } + // fill in result data + KMX_DWORD msg; + KMX_BYTE* data = nullptr; + size_t dataSize = 0; + msg = WriteCompiledKeyboard(&fk, &data, dataSize); - delete str; + //TODO: FreeKeyboardPointers(fk); - if (!kmcmp::CheckKeyboardFinalVersion(fk)) { + if(msg != CERR_None) { + AddCompileError(msg); return FALSE; } - /* Warn on inconsistent use of NCAPS */ - if (!kmcmp::FMnemonicLayout) { - CheckNCapsConsistency(fk); - } - - /* Flag presence of deprecated features */ - kmcmp::CheckForDeprecatedFeatures(fk); + result.kmx = data; + result.kmxSize = dataSize; + result.kvksFilename = string_from_u16string(fk.extra->kvksFilename); // convert to UTF8 return TRUE; -} \ No newline at end of file +} + diff --git a/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp b/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0aab9ba51d4abf4262595356feed46819d2c0c4a --- /dev/null +++ b/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp @@ -0,0 +1,117 @@ +#include "pch.h" +#include + +#ifdef __EMSCRIPTEN__ + +/* + WASM interface for compiler message callback +*/ +EM_JS(int, wasm_msgproc, (int line, int msgcode, const char* text, char* context), { + const proc = globalThis[UTF8ToString(context)].message; + if(!proc || typeof proc != 'function') { + console.log(`[${line}: ${msgcode}: ${UTF8ToString(text)}]`); + return 0; + } else { + return proc(line, msgcode, UTF8ToString(text)); + } +}); + +EM_JS(int, wasm_loadfileproc, (const char* filename, const char* baseFilename, void* buffer, int bufferSize, char* context), { + const proc = globalThis[UTF8ToString(context)].loadFile; + if(!proc || typeof proc != 'function') { + return 0; + } else { + if(buffer == 0) { + return proc(UTF8ToString(filename), UTF8ToString(baseFilename), 0, 0); + } else { + return proc(UTF8ToString(filename), UTF8ToString(baseFilename), buffer, bufferSize); + } + } +}); + +bool wasm_LoadFileProc(const char* filename, const char* baseFilename, void* buffer, int* bufferSize, void* context) { + char* msgProc = static_cast(context); + if(buffer == nullptr) { + *bufferSize = wasm_loadfileproc(filename, baseFilename, 0, 0, msgProc); + return *bufferSize != -1; + } else { + return wasm_loadfileproc(filename, baseFilename, buffer, *bufferSize, msgProc) == 1; + } +} + +int wasm_CompilerMessageProc(int line, uint32_t dwMsgCode, const char* szText, void* context) { + char* msgProc = static_cast(context); + return wasm_msgproc(line, dwMsgCode, szText, msgProc); +} + +struct WASM_COMPILER_INTERFACE { + std::string callbacksKey; // key of callbacks object on globalThis +}; + +struct WASM_COMPILER_RESULT { + bool result; + // Following are pointer offsets in heap + buffer size + int kmx; + int kmxSize; + // Following are compiler side-channel data, required for + // follow-on transform + std::string kvksFilename; + // TODO: additional data to be passed back +}; + +WASM_COMPILER_RESULT kmcmp_wasm_compile(std::string pszInfile, const KMCMP_COMPILER_OPTIONS options, const WASM_COMPILER_INTERFACE intf) { + WASM_COMPILER_RESULT r = {false}; + KMCMP_COMPILER_RESULT kr; + + r.kmx = 0; + r.kmxSize = 0; + r.kvksFilename = ""; + + r.result = kmcmp_CompileKeyboard( + pszInfile.c_str(), + options, + wasm_CompilerMessageProc, + wasm_LoadFileProc, + intf.callbacksKey.c_str(), + kr + ); + + if(r.result) { + // TODO: additional data as required by kmc_kmw + r.kmx = (int) kr.kmx; + r.kmxSize = (int) kr.kmxSize; + r.kvksFilename = kr.kvksFilename; + } + + return r; +} + +EMSCRIPTEN_BINDINGS(compiler_interface) { + + emscripten::class_("CompilerOptions") + .constructor<>() + .property("saveDebug", &KMCMP_COMPILER_OPTIONS::saveDebug) + .property("compilerWarningsAsErrors", &KMCMP_COMPILER_OPTIONS::compilerWarningsAsErrors) + .property("warnDeprecatedCode", &KMCMP_COMPILER_OPTIONS::warnDeprecatedCode) + .property("shouldAddCompilerVersion", &KMCMP_COMPILER_OPTIONS::shouldAddCompilerVersion) + .property("target", &KMCMP_COMPILER_OPTIONS::target) + ; + + emscripten::class_("CompilerInterface") + .constructor<>() + .property("callbacksKey", &WASM_COMPILER_INTERFACE::callbacksKey) + ; + + emscripten::class_("CompilerResult") + .constructor<>() + .property("result", &WASM_COMPILER_RESULT::result) + .property("kmx", &WASM_COMPILER_RESULT::kmx) + .property("kmxSize", &WASM_COMPILER_RESULT::kmxSize) + .property("kvksFilename", &WASM_COMPILER_RESULT::kvksFilename) + ; + + emscripten::function("kmcmp_compile", &kmcmp_wasm_compile); + emscripten::function("kmcmp_parseUnicodeSet", &kmcmp_parseUnicodeSet); +} + +#endif diff --git a/developer/src/kmcmplib/src/NamedCodeConstants.cpp b/developer/src/kmcmplib/src/NamedCodeConstants.cpp index e6b1ae6d70ce20846ba23d24e0ab0efd05d2120a..a8af8fa783c23ac87f211452266d1c85cb9b87c8 100644 --- a/developer/src/kmcmplib/src/NamedCodeConstants.cpp +++ b/developer/src/kmcmplib/src/NamedCodeConstants.cpp @@ -24,18 +24,14 @@ #include "pch.h" #include #include "NamedCodeConstants.h" -#include "CheckFilenameConsistency.h" #include #include "kmcompx.h" -#include "filesystem.h" using namespace kmcmp; int IsHangulSyllable(const KMX_WCHAR *codename, int *code); namespace kmcmp { - extern KMX_CHAR CompileDir[]; - int __cdecl sort_entries(const void *elem1, const void *elem2) { return u16icmp( @@ -117,85 +113,57 @@ char *kmc_strupr(char *s) { return s; } -KMX_BOOL NamedCodeConstants::IntLoadFile(const KMX_CHAR *filename) -{ +KMX_BOOL NamedCodeConstants::LoadFile(PFILE_KEYBOARD fk, const KMX_WCHAR *filename) { const int str_size = 256; - FILE *fp = NULL; - if (CheckFilenameConsistency(filename, FALSE) != 0) { + auto szNameUtf8 = string_from_u16string(filename); + + int FileSize; + KMX_BYTE* Buf; + if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), nullptr, &FileSize, msgprocContext)) { return FALSE; } - fp = Open_File(filename, "rt"); - if(fp == NULL) { - return FALSE; // I3481 + Buf = new KMX_BYTE[FileSize+1]; + if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), Buf, &FileSize, msgprocContext)) { + delete[] Buf; + return FALSE; } + Buf[FileSize] = 0; // zero-terminate for strtok - KMX_CHAR str[str_size], *p, *q, *context = NULL; - KMX_BOOL isEol , first = TRUE; + char* filetok; + char* filecontext; + filetok = strtok_r((char*)Buf, "\n", &filecontext); - while(fgets(str, str_size, fp)) - { - isEol = *(strchr(str, 0) - 1) == '\n'; - p = strtok_r(str, ";", &context); // I3481 - q = strtok_r(NULL, ";\n", &context); - if(p && q) - { - if(first && *p == (KMX_CHAR)0xEF && *(p+1) == (KMX_CHAR)0xBB && *(p+2) == (KMX_CHAR)0xBF) p += 3; // I3056 UTF-8 // I3512 - first = FALSE; + if(*filetok == (KMX_CHAR)0xEF && *(filetok+1) == (KMX_CHAR)0xBB && *(filetok+2) == (KMX_CHAR)0xBF) filetok += 3; // I3056 UTF-8 // I3512 + + while(filetok) { + KMX_CHAR str[str_size], *p, *q, *context = NULL; + + if(strlen(filetok) >= str_size) { + delete[] Buf; + // TODO chuck a wobbly + return FALSE; + } + strcpy(str, filetok); + p = strtok_r(str, ";\r", &context); // I3481 + q = strtok_r(nullptr, ";\r", &context); + if(p && q) { kmc_strupr(q); // I3481 // I3641 - long n = strtol(p, NULL, 16); + long n = strtol(p, nullptr, 16); if (*q != '<') { PKMX_WCHAR q0 = strtowstr(q); AddCode_IncludedCodes((int)n, q0); delete[] q0; } } - if(!isEol ) - { - while(fgets(str, str_size, fp)) if(*(strchr(str, 0)-1) == '\n') break; - } + filetok = strtok_r(nullptr, "\n", &filecontext); } - fclose(fp); - - return TRUE; -} - -KMX_BOOL NamedCodeConstants::LoadFile(const KMX_CHAR *filename) -{ - const int buf_size = 260; - KMX_CHAR buf[buf_size]; - // Look in current directory first -- REMOVED AS DANGEROUS - /* strncpy(buf, filename, (buf_size-1)); buf[buf_size-1] = 0; // I3481 - if(kmcmp_FileExists(buf)) - return IntLoadFile(buf); - */ - // Then look in keyboard file directory (CompileDir) - strncpy(buf, CompileDir, (buf_size-1)); buf[buf_size-1] = 0; // I3481 - strncat(buf, filename, (buf_size-1)-strlen(CompileDir)); buf[buf_size-1] = 0; - if(kmcmp_FileExists(buf)) - return IntLoadFile(buf); - - //TODO: sort out how to find common includes in non-Windows platforms: - #ifdef _WINDOWS_ - // Finally look in kmcmpdll.dll directory - GetModuleFileName(0, buf, buf_size); - - KMX_CHAR *p = strrchr_slash(buf); - if(p) - p++; - else - p = buf; - *p = 0; - strncat_s(buf, _countof(buf), filename, (buf_size-1)-strlen(buf)); buf[buf_size-1] = 0; // I3481 // I3641 - if(kmcmp_FileExists(buf)) - return IntLoadFile(buf); - #endif + delete[] Buf; reindex(); - - return FALSE; + return TRUE; } void NamedCodeConstants::reindex() diff --git a/developer/src/kmcmplib/src/NamedCodeConstants.h b/developer/src/kmcmplib/src/NamedCodeConstants.h index 83d707d092cc4535a24cf3676aaa3d2a44d7b859..c537a73361b40f5539a75275fe86787e244b3541 100644 --- a/developer/src/kmcmplib/src/NamedCodeConstants.h +++ b/developer/src/kmcmplib/src/NamedCodeConstants.h @@ -2,6 +2,8 @@ #ifndef NAMEDCODECONSTANTS_H #define NAMEDCODECONSTANTS_H +#include "compfile.h" + #define MAX_ENAME 128 #define ALLOC_SIZE 256 @@ -23,14 +25,13 @@ namespace kmcmp{ int GetCode_IncludedCodes(const KMX_WCHAR *codename); void AddCode_IncludedCodes(int n, const KMX_WCHAR *p); - KMX_BOOL IntLoadFile(const KMX_CHAR *filename); public: NamedCodeConstants(); ~NamedCodeConstants(); void reindex(); void AddCode(int n, const KMX_WCHAR *p, KMX_DWORD storeIndex); - KMX_BOOL LoadFile(const KMX_CHAR *filename); + KMX_BOOL LoadFile(PFILE_KEYBOARD fk, const KMX_WCHAR *filename); int GetCode(const KMX_WCHAR *codename, KMX_DWORD *storeIndex); }; } diff --git a/developer/src/kmcmplib/src/UnreachableRules.cpp b/developer/src/kmcmplib/src/UnreachableRules.cpp index 9c6aaa23adb32e2e7162794bda48f19d9d182ee8..d8980599a2bd1865795159daea26b6f4eefaba83 100644 --- a/developer/src/kmcmplib/src/UnreachableRules.cpp +++ b/developer/src/kmcmplib/src/UnreachableRules.cpp @@ -41,8 +41,7 @@ KMX_DWORD VerifyUnreachableRules(PFILE_GROUP gp) { if (kp->Line != k1.Line && reportedLines.count(kp->Line) == 0) { reportedLines.insert(kp->Line); kmcmp::currentLine = kp->Line; - u16sprintf(ErrExtraW, ERR_EXTRA_W_LEN, L" Overridden by rule on line %d", k1.Line); - strcpy(ErrExtraLIB, string_from_u16string(ErrExtraW).c_str()); + snprintf(ErrExtraLIB, ERR_EXTRA_LIB_LEN, " Overridden by rule on line %d", k1.Line); AddWarning(CHINT_UnreachableRule); } } diff --git a/developer/src/kmcmplib/src/compfile.h b/developer/src/kmcmplib/src/compfile.h index 48be5f20482f444dde7ec9b516b835b1d15ba77e..c54663aba7659f871ce6271089494aa81420f6d0 100644 --- a/developer/src/kmcmplib/src/compfile.h +++ b/developer/src/kmcmplib/src/compfile.h @@ -22,6 +22,7 @@ #include "kmcompx.h" #include +#include #define LINESIZE 8192 #define GLOBAL_BUFSIZE 4096 @@ -71,85 +72,94 @@ enum FileStoreType { FST_STORE, FST_OPTION, FST_RESERVED }; struct FILE_STORE { - KMX_DWORD dwSystemID; - KMX_WCHAR szName[SZMAX_STORENAME]; // the name of the store - PKMX_WCHAR dpString; // from start of store structure - //FileStoreType fstType; - KMX_BOOL fIsStore; - KMX_BOOL fIsReserved; - KMX_BOOL fIsOption; - KMX_BOOL fIsDebug; - KMX_BOOL fIsCall; - int line; - }; - -typedef FILE_STORE * PFILE_STORE; + KMX_DWORD dwSystemID; + KMX_WCHAR szName[SZMAX_STORENAME]; // the name of the store + PKMX_WCHAR dpString; // from start of store structure + //FileStoreType fstType; + KMX_BOOL fIsStore; + KMX_BOOL fIsReserved; + KMX_BOOL fIsOption; + KMX_BOOL fIsDebug; + KMX_BOOL fIsCall; + int line; +}; +typedef FILE_STORE *PFILE_STORE; struct FILE_KEY { - KMX_WCHAR Key; // WCHAR for consistency; only a byte used however - KMX_WORD LineStoreIndex; - KMX_DWORD Line; - KMX_DWORD ShiftFlags; - PKMX_WCHAR dpOutput; // from start of key structure - PKMX_WCHAR dpContext; // from start of key structure - }; + KMX_WCHAR Key; // WCHAR for consistency; only a byte used however + KMX_WORD LineStoreIndex; + KMX_DWORD Line; + KMX_DWORD ShiftFlags; + PKMX_WCHAR dpOutput; // from start of key structure + PKMX_WCHAR dpContext; // from start of key structure +}; typedef FILE_KEY *PFILE_KEY; - struct FILE_GROUP { - KMX_WCHAR szName[SZMAX_GROUPNAME]; - PFILE_KEY dpKeyArray; // address of first item in key array, from start of group structure - PKMX_WCHAR dpMatch; // from start of group structure - PKMX_WCHAR dpNoMatch; // from start of group structure - KMX_DWORD cxKeyArray; // in array items - KMX_BOOL fUsingKeys; // group(xx) [using keys] <-- specified or not - KMX_BOOL fReadOnly; // group(xx) [readonly] <-- specified or not - KMX_DWORD Line; - }; + KMX_WCHAR szName[SZMAX_GROUPNAME]; + PFILE_KEY dpKeyArray; // address of first item in key array, from start of group structure + PKMX_WCHAR dpMatch; // from start of group structure + PKMX_WCHAR dpNoMatch; // from start of group structure + KMX_DWORD cxKeyArray; // in array items + KMX_BOOL fUsingKeys; // group(xx) [using keys] <-- specified or not + KMX_BOOL fReadOnly; // group(xx) [readonly] <-- specified or not + KMX_DWORD Line; +}; typedef FILE_GROUP *PFILE_GROUP; -struct FILE_DEADKEY -{ - KMX_WCHAR szName[SZMAX_DEADKEYNAME]; +struct FILE_DEADKEY { + KMX_WCHAR szName[SZMAX_DEADKEYNAME]; }; + typedef FILE_DEADKEY *PFILE_DEADKEY; -struct FILE_VKDICTIONARY -{ - KMX_WCHAR szName[SZMAX_VKDICTIONARYNAME]; +struct FILE_VKDICTIONARY { + KMX_WCHAR szName[SZMAX_VKDICTIONARYNAME]; }; typedef FILE_VKDICTIONARY *PFILE_VKDICTIONARY; +/** + * Extra metadata for API consumers + */ +struct FILE_KEYBOARD_EXTRA { + std::string kmnFilename; // utf-8 + std::u16string kvksFilename; // utf-16, original TSS_VISUALKEYBOARD value +}; + +typedef struct FILE_KEYBOARD_EXTRA* PFILE_KEYBOARD_EXTRA; + struct FILE_KEYBOARD { - KMX_DWORD KeyboardID; // as stored in HKEY_LOCAL_MACHINE//system//currentcontrolset//control//keyboard layouts + KMX_DWORD KeyboardID; // deprecated, unused - KMX_DWORD version; // keyboard file version with VERSION keyword + KMX_DWORD version; // keyboard file version with VERSION keyword - PFILE_STORE dpStoreArray; // address of first item in store array, from start of store structure - PFILE_GROUP dpGroupArray; // address of first item in group array, from start of group structure + PFILE_STORE dpStoreArray; // address of first item in store array, from start of store structure + PFILE_GROUP dpGroupArray; // address of first item in group array, from start of group structure - KMX_DWORD cxStoreArray; // in number of items - KMX_DWORD cxGroupArray; // in number of items - KMX_DWORD StartGroup[2]; // index of starting groups [ANSI=0, Unicode=1] + KMX_DWORD cxStoreArray; // in number of items + KMX_DWORD cxGroupArray; // in number of items + KMX_DWORD StartGroup[2]; // index of starting groups [ANSI=0, Unicode=1] - KMX_DWORD dwHotKey; // standard windows hotkey (hiword=shift/ctrl/alt stuff, loword=vkey) + KMX_DWORD dwHotKey; // standard windows hotkey (hiword=shift/ctrl/alt stuff, loword=vkey) - KMX_WCHAR szName[SZMAX_KEYBOARDNAME]; // Keyboard layout name - KMX_WCHAR szLanguageName[SZMAX_LANGUAGENAME]; // Language name - KMX_WCHAR szCopyright[SZMAX_COPYRIGHT]; // Copyright information - KMX_WCHAR szMessage[SZMAX_MESSAGE]; // General information about the keyboard - PKMX_BYTE lpBitmap; - KMX_DWORD dwBitmapSize; - KMX_DWORD dwFlags; // Flags for the keyboard file + KMX_WCHAR szName[SZMAX_KEYBOARDNAME]; // Keyboard layout name + KMX_WCHAR szLanguageName[SZMAX_LANGUAGENAME]; // Language name + KMX_WCHAR szCopyright[SZMAX_COPYRIGHT]; // Copyright information + KMX_WCHAR szMessage[SZMAX_MESSAGE]; // General information about the keyboard + PKMX_BYTE lpBitmap; + KMX_DWORD dwBitmapSize; + KMX_DWORD dwFlags; // Flags for the keyboard file - KMX_DWORD currentGroup; // temp - current processing group - KMX_DWORD currentStore; // temp - current processing store - KMX_DWORD cxDeadKeyArray; - PFILE_DEADKEY dpDeadKeyArray; // temp - dead key array - KMX_DWORD cxVKDictionary; - PFILE_VKDICTIONARY dpVKDictionary; // temp - virtual key dictionary - }; + KMX_DWORD currentGroup; // temp - current processing group + KMX_DWORD currentStore; // temp - current processing store + KMX_DWORD cxDeadKeyArray; + PFILE_DEADKEY dpDeadKeyArray; // temp - dead key array + KMX_DWORD cxVKDictionary; + PFILE_VKDICTIONARY dpVKDictionary; // temp - virtual key dictionary + + PFILE_KEYBOARD_EXTRA extra; +}; typedef FILE_KEYBOARD *PFILE_KEYBOARD; @@ -167,54 +177,25 @@ const KMX_DWORD sz_FILE_VKDICTIONARY = sizeof(FILE_VKDICTIONARY); const KMX_DWORD sz_FILE_KEYBOARD = sizeof(FILE_KEYBOARD); struct COMPMSG { - KMX_CHAR szText[SZMAX_ERRORTEXT]; - KMX_DWORD Line; - KMX_DWORD dwMsgCode; - }; + KMX_CHAR szText[SZMAX_ERRORTEXT]; + KMX_DWORD Line; + KMX_DWORD dwMsgCode; +}; typedef COMPMSG *PCOMPMSG; struct COMPILEMESSAGES { - int nMessages; - int nErrors; - - PCOMPMSG cm; + int nMessages; + int nErrors; - KMX_DWORD fatalCode; - KMX_CHAR szFatalText[SZMAX_ERRORTEXT]; + PCOMPMSG cm; - KMX_DWORD currentLine; - }; + KMX_DWORD fatalCode; + KMX_CHAR szFatalText[SZMAX_ERRORTEXT]; -typedef COMPILEMESSAGES *PCOMPILEMESSAGES; - -/* -struct TVersion -{ - //int MinVersion; // 0x0500 usually - //int CompilerVersion[4]; - //int MinCompilerVersion[4]; - int KeyboardVersion; // 0x0500 usually + KMX_DWORD currentLine; }; -extern TVersion FVersionInfo; -*/ - -/* -#define bstrcpy(c,d) (LPBYTE)strcpy((LPSTR)(c),(LPSTR)(d)) -#define bstrlen(c) strlen((LPSTR)(c)) -#define bstrcmp(c,d) strcmp((LPSTR)(c),(LPSTR)(d)) -#define bstrncmp(c,d,n) strncmp((LPSTR)(c),(LPSTR)(d),(n)) -#define bstrnicmp(c,d,n) strnicmp((LPSTR)(c),(LPSTR)(d),(n)) -#define bstricmp(c,d) stricmp((LPSTR)(c),(LPSTR)(d)) -#define bstrchr(c,ch) (LPBYTE)strchr((LPSTR)(c),(char)ch) -#define bstrncpy(c,d,n) (LPBYTE)strncpy((LPSTR)(c),(LPSTR)(d),(n)) -#define bstrtok(c,d) (LPBYTE)strtok((LPSTR)(c),(LPSTR)(d)) -#define bstrcat(c,d) (LPBYTE)strcat((LPSTR)(c),(LPSTR)(d)) -#define bstrncat(c,d,n) (LPBYTE)strncat((LPSTR)(c),(LPSTR)(d),(n)) -#define bstrrev(c) (LPBYTE)strrev((LPSTR)(c)) -#define batoi(c) atoi((LPSTR)(c)) -#define bstrtol(c,d,n) strtol((LPSTR)(c),(LPSTR *)(d),(n)) -*/ +typedef COMPILEMESSAGES *PCOMPILEMESSAGES; #endif // _COMPFILE_H diff --git a/developer/src/kmcmplib/src/json-validation.cpp b/developer/src/kmcmplib/src/json-validation.cpp deleted file mode 100644 index f58d8c950534943427285a4f400fb39c190217ff..0000000000000000000000000000000000000000 --- a/developer/src/kmcmplib/src/json-validation.cpp +++ /dev/null @@ -1,68 +0,0 @@ - -#include -#include - -#include -#include "kmcompx.h" - -using nlohmann::json; -using nlohmann::json_uri; -using nlohmann::json_schema_draft4::json_validator; - -static void loader(const json_uri &uri, json &schema) -{ - std::fstream lf("." + uri.path()); - if (!lf.good()) - throw std::invalid_argument("could not open " + uri.url() + " tried with " + uri.path()); - - lf >> schema; -} - -EXTERN bool kmcmp_ValidateJsonFile(std::fstream& f, std::fstream& fd, kmcmp_ValidateJsonMessageProc MessageProc, void* context) { - if (!f.good()) { - MessageProc(-1, "Schema file could not be loaded.", context); - return FALSE; - } - - // 1) Read the schema for the document you want to validate - json schema; - try { - f >> schema; - } - catch (std::exception &e) { - MessageProc(f.tellp(), e.what(), context); - return FALSE; - } - - // 2) create the validator and - json_validator validator(loader, [](const std::string &, const std::string &) {}); - - try { - // insert this schema as the root to the validator - // this resolves remote-schemas, sub-schemas and references via the given loader-function - validator.set_root_schema(schema); - } - catch (std::exception &e) { - MessageProc(-2, e.what(), context); - return FALSE; - } - - // 3) do the actual validation of the document - json document; - - if (!fd.good()) { - MessageProc(-3, "Json file could not be loaded.", context); - return FALSE; - } - - try { - fd >> document; - validator.validate(document); - } - catch (std::exception &e) { - MessageProc(fd.tellp(), e.what(), context); - return FALSE; - } - - return TRUE; -} \ No newline at end of file diff --git a/developer/src/kmcmplib/src/kmcmplib.h b/developer/src/kmcmplib/src/kmcmplib.h index a851915de2b13ec9257e6e9ab44e4178968b86dc..0d7c4f90042d8bed81a142f3f967ec0c604513bc 100644 --- a/developer/src/kmcmplib/src/kmcmplib.h +++ b/developer/src/kmcmplib/src/kmcmplib.h @@ -14,7 +14,6 @@ namespace kmcmp { extern KMX_BOOL FMnemonicLayout; extern KMX_BOOL FOldCharPosMatching; extern int CompileTarget; - extern KMX_CHAR CompileDir[260]; // TODO: this should not be a fixed buffer extern int BeginLine[4]; extern int currentLine; extern NamedCodeConstants *CodeConstants; @@ -24,13 +23,12 @@ namespace kmcmp { } extern kmcmp_CompilerMessageProc msgproc; +extern kmcmp_LoadFileProc loadfileproc; extern void* msgprocContext; extern KMX_BOOL AWarnDeprecatedCode_GLOBAL_LIB; #define ERR_EXTRA_LIB_LEN 256 -#define ERR_EXTRA_W_LEN 256 extern char ErrExtraLIB[ERR_EXTRA_LIB_LEN]; -extern KMX_WCHAR ErrExtraW[ERR_EXTRA_W_LEN]; KMX_BOOL AddCompileError(KMX_DWORD msg); /// Use AddWarningBool for functions that return bool or KMX_BOOL @@ -40,10 +38,10 @@ KMX_BOOL AddCompileError(KMX_DWORD msg); PKMX_WCHAR strtowstr(PKMX_STR in); PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, KMX_DWORD dwSystemID); -FILE* UTF16TempFromUTF8(FILE* fp_in , KMX_BOOL hasPreamble); -KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, FILE* fp_out); -KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, KMX_WCHAR const * str, KMX_DWORD *dwStoreID= NULL); -KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess); +bool UTF16TempFromUTF8(KMX_BYTE* infile, int sz, KMX_BYTE** tempfile, int *sz16); +KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, KMX_BYTE**data, size_t& dataSize); +KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, const KMX_WCHAR * str, KMX_DWORD *dwStoreID= NULL); +KMX_DWORD ReadLine(KMX_BYTE* infile, int sz, int& offset, PKMX_WCHAR wstr, KMX_BOOL PreProcess); KMX_DWORD ParseLine(PFILE_KEYBOARD fk, PKMX_WCHAR str); KMX_DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PKMX_WCHAR p); KMX_DWORD ProcessGroupFinish(PFILE_KEYBOARD fk); diff --git a/developer/src/kmcmplib/src/meson.build b/developer/src/kmcmplib/src/meson.build index fa51ca28a502faf4405f20967bd2aedb70b2b915..c6ed2b84b01869c07311687fa5445dfed1b9f06d 100644 --- a/developer/src/kmcmplib/src/meson.build +++ b/developer/src/kmcmplib/src/meson.build @@ -7,6 +7,7 @@ # TODO: is this required? It should be Keyman Core only defns += ['-DKMN_KBP_EXPORTING'] version_res = [] +lib_links = [] if cpp_compiler.get_id() == 'gcc' or cpp_compiler.get_id() == 'clang' warns += [ @@ -24,12 +25,10 @@ endif name_suffix = [] if cpp_compiler.get_id() == 'emscripten' - links += ['-lnodefs.js', '-sMODULARIZE', '-sEXPORT_ES6', '-sEXPORTED_RUNTIME_METHODS=[\'cwrap\', \'UTF8ToString\']'] - # tests are building as ES6 so we need to declare the file extension - # note that meson currently struggles with the sanitycheckc_cross.exe - # program, because it has a hard coded extension (.exe) which is not - # valid for node programs in module mode. - name_suffix = '.mjs' + # wasm-exceptions supported in Node 18+, Chrome 95+, Firefox 100+, Safari 15.2+ + flags += ['-fwasm-exceptions'] + lib_links = ['--whole-archive', '-sMODULARIZE', '-sEXPORT_ES6'] + links += ['-fwasm-exceptions', '--bind', '-sEXPORTED_RUNTIME_METHODS=[\'UTF8ToString\']'] endif icu = subproject('icu-for-uset', default_options: [ 'default_library=static', 'cpp_std=c++17', 'warning_level=0', 'werror=false']) @@ -38,36 +37,29 @@ icuuc_dep = icu.get_variable('icuuc_dep') lib = library('kmcmplib', 'CasedKeys.cpp', 'CharToKeyConversion.cpp', + 'CheckForDuplicates.cpp', + 'CheckNCapsConsistency.cpp', + 'CompileKeyboardBuffer.cpp', 'Compiler.cpp', 'CompilerInterfaces.cpp', + 'CompilerInterfacesWasm.cpp', + 'CompMsg.cpp', 'DeprecationChecks.cpp', 'Edition.cpp', - 'filesystem.cpp', - 'json-validation.cpp', + 'kmx_u16.cpp', 'NamedCodeConstants.cpp', + 'UnreachableRules.cpp', + 'uset-api.cpp', 'versioning.cpp', 'virtualcharkeys.cpp', 'xstring.cpp', - 'CharToKeyConversion.cpp', - 'CheckFilenameConsistency.cpp', - 'CheckForDuplicates.cpp', - 'CheckNCapsConsistency.cpp', - 'UnreachableRules.cpp', - 'kmx_u16.cpp', - 'CompMsg.cpp', - 'uset-api.cpp', - '../../../../common/windows/cpp/src/ConvertUTF.c', '../../../../common/windows/cpp/src/crc32.cpp', '../../../../common/windows/cpp/src/vkeys.cpp', - 'xstring.cpp', - '../../ext/json-schema-validator/json-schema-draft4.json.cpp', - '../../ext/json-schema-validator/json-uri.cpp', - '../../ext/json-schema-validator/json-validator.cpp', version_res, cpp_args: defns + warns + flags, - link_args: links, + link_args: links + lib_links, version: meson.project_version(), include_directories: inc, install: true, @@ -81,7 +73,7 @@ if cpp_compiler.get_id() == 'emscripten' host = executable('wasm-host', #'wasm-host.cpp', cpp_args: defns, include_directories: inc, - link_args: links, + link_args: links + lib_links, objects: lib.extract_all_objects(), dependencies: icuuc_dep) endif diff --git a/developer/src/kmcmplib/src/pch.h b/developer/src/kmcmplib/src/pch.h index f878ad11be4f892ad4e9f0ece5864712c6af5c6b..358e28998abc8e65bba25cf2b594ca95cbed5b57 100644 --- a/developer/src/kmcmplib/src/pch.h +++ b/developer/src/kmcmplib/src/pch.h @@ -10,6 +10,4 @@ #include "../../../../common/windows/cpp/include/crc32.h" #include -#include - #include diff --git a/developer/src/kmcmplib/src/uset-api.cpp b/developer/src/kmcmplib/src/uset-api.cpp index 1aa22477b2b63465a8cb0db8b7d624409d9ce083..c2c72cab347587e64998ce7ecfed41614ea8fc49 100644 --- a/developer/src/kmcmplib/src/uset-api.cpp +++ b/developer/src/kmcmplib/src/uset-api.cpp @@ -4,16 +4,13 @@ #include "unicode/uniset.h" #include "unicode/unistr.h" -EXTERN int kmcmp_ParseUnicodeSet( - const char* szText, - uint32_t* outputBuffer, +EXTERN int kmcmp_parseUnicodeSet( + const std::string text, + uintptr_t outputBuffer_, uint32_t outputBufferSize ) { - if (szText == nullptr) { - // null string coming in - return KMCMP_ERROR_SYNTAX_ERR; - } - const icu::UnicodeString str = icu::UnicodeString::fromUTF8(szText); + uint32_t* outputBuffer = reinterpret_cast(outputBuffer_); + const icu::UnicodeString str = icu::UnicodeString::fromUTF8(text.c_str()); if (str.isBogus() || str.isEmpty()) { // empty string return KMCMP_ERROR_SYNTAX_ERR; diff --git a/developer/src/kmcmplib/tests/api-test.cpp b/developer/src/kmcmplib/tests/api-test.cpp index 4c08272d4b21a1d45f194acf0e50229e8d887b14..cb0337caef680107910d8b219bca8a69171c5a15 100644 --- a/developer/src/kmcmplib/tests/api-test.cpp +++ b/developer/src/kmcmplib/tests/api-test.cpp @@ -18,32 +18,20 @@ #include #include "../src/compfile.h" #include +#include "util_filesystem.h" +#include "util_callbacks.h" void setup(); -void test_kmcmp_CompileKeyboardFile(); -void test_kmcmp_CompileKeyboardFileToBuffer(); - -std::vector error_vec; - -int msgproc(int line, uint32_t dwMsgCode, char* szText, void* context) { - error_vec.push_back(dwMsgCode); - const char*t = "unknown"; - switch(dwMsgCode & 0xF000) { - case CERR_HINT: t=" hint"; break; - case CERR_WARNING: t="warning"; break; - case CERR_ERROR: t=" error"; break; - case CERR_FATAL: t=" fatal"; break; - } - printf("line %d %s %04.4x: %s\n", line, t, (unsigned int)dwMsgCode, szText); - return 1; -} +void test_kmcmp_CompileKeyboard(char *kmn_file); int main(int argc, char *argv[]) { + if(argc < 1) { + puts("Usage: api-test "); + puts("Warning: blank_keyboard will be overwritten"); + return 1; + } setup(); - test_kmcmp_CompileKeyboardFile(); - - setup(); - test_kmcmp_CompileKeyboardFileToBuffer(); + test_kmcmp_CompileKeyboard(argv[1]); return 0; } @@ -52,36 +40,27 @@ void setup() { error_vec.clear(); } -void test_kmcmp_CompileKeyboardFile() { - char kmn_file[L_tmpnam], kmx_file[L_tmpnam]; - tmpnam(kmn_file); - tmpnam(kmx_file); - - // Create an empty file - FILE *fp = fopen(kmn_file, "w"); - fclose(fp); - - // It should fail when a zero-byte file is passed in - assert(!kmcmp_CompileKeyboardFile(kmn_file, kmx_file, true, false, true, msgproc, nullptr, CKF_KEYMAN)); - assert(error_vec.size() == 1); - assert(error_vec[0] == CERR_CannotReadInfile); - - unlink(kmn_file); -} - -void test_kmcmp_CompileKeyboardFileToBuffer() { - char kmn_file[L_tmpnam], kmx_file[L_tmpnam]; - tmpnam(kmn_file); - tmpnam(kmx_file); +/* + TODO: tests to run: + 4. ANSI (no BOM of course) #8884 + 8. file without blank last line (cannot compare with fixture due to bug in kmcmpdll...) + Hint to add: k004_ansi.kmn: Hint: 10A6 Keyman Developer has detected that the file has ANSI encoding. Consider converting this file to UTF-8 +*/ +void test_kmcmp_CompileKeyboard(char *kmn_file) { // Create an empty file - FILE *fp = fopen(kmn_file, "w"); + FILE *fp = Open_File(kmn_file, "wb"); fclose(fp); - FILE_KEYBOARD fk; - // It should fail when a zero-byte file is passed in - assert(!kmcmp_CompileKeyboardFileToBuffer(kmn_file, &fk, true, false, msgproc, nullptr, CKF_KEYMAN)); + KMCMP_COMPILER_RESULT result; + KMCMP_COMPILER_OPTIONS options; + options.saveDebug = true; + options.compilerWarningsAsErrors = false; + options.warnDeprecatedCode = true; + options.shouldAddCompilerVersion = false; + options.target = CKF_KEYMAN; + assert(!kmcmp_CompileKeyboard(kmn_file, options, msgproc, loadfileProc, nullptr, result)); assert(error_vec.size() == 1); assert(error_vec[0] == CERR_CannotReadInfile); diff --git a/developer/src/kmcmplib/tests/fixtures/adiga_danef.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/adiga_danef.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/adiga_danef.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/adiga_danef.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/ahom_star.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ahom_star.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/ahom_star.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ahom_star.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/aksarabali_panlex.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/aksarabali_panlex.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/aksarabali_panlex.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/aksarabali_panlex.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/alephwithbeth.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/alephwithbeth.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/alephwithbeth.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/alephwithbeth.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/anii.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/anii.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/anii.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/anii.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/arabic_izza.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/arabic_izza.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/arabic_izza.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/arabic_izza.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/aramaic_hebrew.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/aramaic_hebrew.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/aramaic_hebrew.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/aramaic_hebrew.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/armenian_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/armenian_mnemonic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/armenian_mnemonic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/armenian_mnemonic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/armenian_mnemonic_r.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/armenian_mnemonic_r.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/armenian_mnemonic_r.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/armenian_mnemonic_r.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/athinkra_vai.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/athinkra_vai.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/athinkra_vai.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/athinkra_vai.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/athinkra_vai_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/athinkra_vai_typewriter.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/athinkra_vai_typewriter.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/athinkra_vai_typewriter.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/ausephon.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ausephon.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/ausephon.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ausephon.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_inpage.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_inpage.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/balochi_inpage.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_inpage.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/balochi_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_persian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_persian.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/balochi_persian.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_persian.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/balochi_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_scientific.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_scientific.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/balochi_scientific.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_scientific.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/balochi_urdu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_urdu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/balochi_urdu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/balochi_urdu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bangla_joy.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_joy.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bangla_joy.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_joy.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bangla_munir.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_munir.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bangla_munir.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_munir.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bangla_probhat.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_probhat.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bangla_probhat.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bangla_probhat.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbda1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbda1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbda2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda2.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbda2.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda2.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbda3.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda3.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbda3.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbda3.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdadlm.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdadlm.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdadlm.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdadlm.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdal.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdal.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdal.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdal.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarme.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarme.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdarme.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarme.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarmph.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmph.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdarmph.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmph.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarmty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdarmty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdarmw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmw.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdarmw.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdarmw.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdaze.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdaze.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdaze.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdaze.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdazel.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdazel.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdazel.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdazel.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdazst.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdazst.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdazst.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdazst.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbash.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbash.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbash.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbash.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbe.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbe.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbe.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbe.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbene.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbene.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbene.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbene.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbgph.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbgph.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbgph.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbgph.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbgph1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbgph1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbgph1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbgph1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbhc.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbhc.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbhc.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbhc.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdblr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdblr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdblr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdblr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbug.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbug.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbug.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbug.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdbulg.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbulg.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdbulg.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdbulg.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdca.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdca.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdca.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdca.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcan.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcan.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcan.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcher.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcher.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcher.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcher.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcherp.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcherp.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcherp.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcherp.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcz.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcz.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcz1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcz1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdcz2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz2.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdcz2.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdcz2.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdda.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdda.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdda.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdda.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddiv1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddiv1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbddiv1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddiv1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddiv2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddiv2.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbddiv2.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddiv2.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddv.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddv.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbddv.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddv.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbddzo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddzo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbddzo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbddzo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdes.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdes.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdes.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdes.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdest.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdest.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdest.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdest.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfar.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfar.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfar.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfi1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfi1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfi1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfi1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdfthrk.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfthrk.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdfthrk.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdfthrk.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgae.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgae.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgae.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgae.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgeo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeoer.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeoer.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgeoer.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeoer.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeome.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeome.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgeome.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeome.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeooa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeooa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgeooa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeooa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgeoqw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeoqw.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgeoqw.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgeoqw.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgkl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgkl.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgkl.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgkl.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgn.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgn.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgn.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgn.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgr1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgr1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgr1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgr1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgrlnd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgrlnd.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgrlnd.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgrlnd.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdgthc.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgthc.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdgthc.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdgthc.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhau.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhau.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhau.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhau.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhaw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhaw.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhaw.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhaw.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhe.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhe.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhe220.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe220.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhe220.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe220.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhe319.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe319.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhe319.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhe319.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdheb.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdheb.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdheb.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdheb.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhebl3.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhebl3.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhebl3.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhebl3.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhela2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhela2.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhela2.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhela2.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhela3.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhela3.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhela3.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhela3.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhept.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhept.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhept.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhept.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdhu1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhu1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdhu1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdhu1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdibo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdibo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdibo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdibo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinasa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinasa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinasa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinasa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinbe2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinbe2.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinbe2.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinbe2.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinben.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinben.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinben.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinben.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdindev.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdindev.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdindev.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdindev.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinen.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinen.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinen.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinen.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinguj.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinguj.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinguj.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinguj.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinhin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinhin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinhin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinhin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinkan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinkan.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinkan.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinkan.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinmal.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinmal.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinmal.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinmal.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinmar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinmar.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinmar.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinmar.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinori.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinori.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinori.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinori.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinpun.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinpun.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinpun.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinpun.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdintam.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdintam.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdintam.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdintam.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdintel.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdintel.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdintel.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdintel.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdinuk2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinuk2.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdinuk2.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdinuk2.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdir.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdir.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdir.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdir.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdit.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdit.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdit.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdit.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdit142.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdit142.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdit142.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdit142.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdiulat.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdiulat.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdiulat.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdiulat.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdjav.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdjav.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdjav.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdjav.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkaz.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkaz.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdkaz.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkaz.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkhmr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkhmr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdkhmr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkhmr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkni.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkni.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdkni.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkni.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkurd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkurd.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdkurd.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkurd.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdkyr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkyr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdkyr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdkyr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdla.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdla.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdla.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdla.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlao.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlao.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlao.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlao.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlisub.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlisub.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlisub.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlisub.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlisus.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlisus.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlisus.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlisus.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlt.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlt.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlt1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlt1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlt2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt2.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlt2.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlt2.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlv.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlv.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlv.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlv.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlv1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlv1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlv1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlv1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdlvst.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlvst.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdlvst.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdlvst.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmac.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmac.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmac.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmac.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmacst.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmacst.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmacst.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmacst.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmaori.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmaori.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmaori.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmaori.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmlt47.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmlt47.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmlt47.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmlt47.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmlt48.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmlt48.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmlt48.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmlt48.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmon.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmon.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmon.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmon.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmonmo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmonmo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmonmo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmonmo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmonst.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmonst.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmonst.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmonst.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdmyan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmyan.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdmyan.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdmyan.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdne.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdne.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdne.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdne.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdnepr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnepr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdnepr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnepr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdnko.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnko.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdnko.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnko.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdno.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdno.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdno.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdno.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdno1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdno1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdno1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdno1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdnso.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnso.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdnso.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdnso.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdntl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdntl.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdntl.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdntl.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdogham.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdogham.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdogham.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdogham.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdolch.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdolch.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdolch.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdolch.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdoldit.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdoldit.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdoldit.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdoldit.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdosa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdosa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdosa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdosa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdosm.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdosm.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdosm.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdosm.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpash.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpash.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdpash.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpash.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdphags.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdphags.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdphags.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdphags.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpl.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdpl.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpl.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpl1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpl1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdpl1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpl1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdpo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdpo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdpo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdropr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdropr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdropr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdropr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdrost.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdrost.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdrost.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdrost.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdru.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdru.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdru.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdru.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdru1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdru1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdru1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdru1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdrum.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdrum.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdrum.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdrum.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsf.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsf.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsf.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsf.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsg.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsg.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsg.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsg.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsl.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsl.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsl.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsl1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsl1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsl1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsl1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsmsfi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsmsfi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsmsfi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsmsfi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsmsno.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsmsno.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsmsno.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsmsno.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsn1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsn1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsn1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsn1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsora.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsora.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsora.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsora.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsorex.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsorex.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsorex.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsorex.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsors1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsors1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsors1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsors1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsp.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsp.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsp.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsp.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsw.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsw.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsw.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsw09.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsw09.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsw09.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsw09.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsyr1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsyr1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsyr1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsyr1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdsyr2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsyr2.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdsyr2.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdsyr2.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtaile.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtaile.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtaile.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtaile.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtajik.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtajik.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtajik.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtajik.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtam99.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtam99.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtam99.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtam99.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth0.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth0.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdth0.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth0.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdth1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth2.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdth2.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth2.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdth3.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth3.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdth3.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdth3.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtifi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtifi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtifi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtifi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtifi2.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtifi2.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtifi2.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtifi2.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtiprd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtiprd.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtiprd.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtiprd.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtt102.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtt102.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtt102.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtt102.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtuf.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtuf.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtuf.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtuf.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtuq.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtuq.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtuq.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtuq.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdturme.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdturme.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdturme.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdturme.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdtzm.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtzm.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdtzm.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdtzm.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdughr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdughr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdughr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdughr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdughr1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdughr1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdughr1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdughr1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbduk.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbduk.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbduk.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbduk.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdukx.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdukx.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdukx.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdukx.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdur.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdur.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdur.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdur.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdur1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdur1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdur1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdur1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdurdu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdurdu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdurdu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdurdu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdus.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdus.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdus.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdus.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdusa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusl.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdusl.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusl.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdusr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdusx.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusx.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdusx.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdusx.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbduzb.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbduzb.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbduzb.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbduzb.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdvntc.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdvntc.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdvntc.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdvntc.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdwol.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdwol.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdwol.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdwol.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdyak.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdyak.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdyak.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdyak.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdyba.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdyba.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdyba.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdyba.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdycc.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdycc.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdycc.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdycc.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/basic_kbdycl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdycl.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/basic_kbdycl.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/basic_kbdycl.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/batak.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/batak.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/batak.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/batak.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/baybayin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/baybayin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/baybayin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/baybayin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bhaiksuki_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bhaiksuki_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bhaiksuki_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bhaiksuki_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_east.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bj_cree_east.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_east_james_bay.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east_james_bay.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bj_cree_east_james_bay.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east_james_bay.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_east_latn.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east_latn.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bj_cree_east_latn.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_east_latn.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_west_latn.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_west_latn.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bj_cree_west_latn.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_west_latn.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bj_cree_woods.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_woods.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bj_cree_woods.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_cree_woods.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bj_innu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_innu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bj_innu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_innu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bj_innu_phonemic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_innu_phonemic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bj_innu_phonemic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_innu_phonemic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bj_mista_wasaha_cree.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_mista_wasaha_cree.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bj_mista_wasaha_cree.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_mista_wasaha_cree.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bj_naskapi_classic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_naskapi_classic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bj_naskapi_classic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_naskapi_classic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bj_naskapi_common.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_naskapi_common.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bj_naskapi_common.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_naskapi_common.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bj_oji_cree.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_oji_cree.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bj_oji_cree.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bj_oji_cree.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/brahmi_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/brahmi_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/brahmi_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/brahmi_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/btl_kenya.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/btl_kenya.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/btl_kenya.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/btl_kenya.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/bu_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/bu_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/bu_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/bu_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/buhid.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/buhid.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/buhid.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/buhid.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/burushaski_girminas.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/burushaski_girminas.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/burushaski_girminas.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/burushaski_girminas.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/cabecar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/cabecar.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/cabecar.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/cabecar.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/carian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/carian.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/carian.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/carian.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/chalchiteko.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/chalchiteko.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/chalchiteko.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/chalchiteko.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/chechen_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/chechen_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/chechen_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/chechen_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/chinuk_wawa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/chinuk_wawa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/chinuk_wawa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/chinuk_wawa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/choctaw_modern.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/choctaw_modern.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/choctaw_modern.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/choctaw_modern.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/cim.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/cim.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/cim.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/cim.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/clavbur9.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/clavbur9.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/clavbur9.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/clavbur9.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/colchis_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/colchis_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/colchis_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/colchis_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/colchis_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/colchis_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/colchis_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/colchis_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/common_devanagari.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/common_devanagari.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/common_devanagari.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/common_devanagari.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/coptic_greek.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/coptic_greek.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/coptic_greek.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/coptic_greek.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/coptic_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/coptic_qwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/coptic_qwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/coptic_qwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/dagbani.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/dagbani.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/dagbani.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/dagbani.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/dega.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/dega.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/dega.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/dega.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/dene.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/dene.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/dene.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/dene.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/deseret.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/deseret.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/deseret.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/deseret.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/dogra_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/dogra_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/dogra_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/dogra_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/easy_chakma.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/easy_chakma.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/easy_chakma.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/easy_chakma.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/ekwtamil99uni.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ekwtamil99uni.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/ekwtamil99uni.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ekwtamil99uni.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/el_dinka.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_dinka.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/el_dinka.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_dinka.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/el_harari_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_harari_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/el_harari_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_harari_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/el_naija.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_naija.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/el_naija.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_naija.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/el_nuer.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_nuer.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/el_nuer.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_nuer.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/el_pasifika.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_pasifika.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/el_pasifika.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_pasifika.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/el_yolngu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_yolngu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/el_yolngu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/el_yolngu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/embera_north.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/embera_north.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/embera_north.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/embera_north.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/enga.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/enga.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/enga.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/enga.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/enggano.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/enggano.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/enggano.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/enggano.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/english_shavian_igc.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/english_shavian_igc.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/english_shavian_igc.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/english_shavian_igc.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/english_shavian_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/english_shavian_qwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/english_shavian_qwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/english_shavian_qwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/engram.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/engram.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/engram.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/engram.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/esperuni.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/esperuni.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/esperuni.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/esperuni.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/farsiman.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/farsiman.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/farsiman.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/farsiman.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/finongan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/finongan.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/finongan.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/finongan.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fulfulde_ajami_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fulfulde_ajami_qwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fulfulde_ajami_qwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fulfulde_ajami_qwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fulfulde_latin_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fulfulde_latin_qwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fulfulde_latin_qwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fulfulde_latin_qwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_dakelh.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_dakelh.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_dakelh.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_dakelh.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_dane_zaa_zaage.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_dane_zaa_zaage.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_dane_zaa_zaage.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_dane_zaa_zaage.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_denesuline_epsilon.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_denesuline_epsilon.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_denesuline_epsilon.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_denesuline_epsilon.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_diitiidatx.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_diitiidatx.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_diitiidatx.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_diitiidatx.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_gitsenimx.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_gitsenimx.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_gitsenimx.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_gitsenimx.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_gwichin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_gwichin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_gwichin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_gwichin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_hailzaqvla.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_hailzaqvla.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_hailzaqvla.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_hailzaqvla.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_haisla.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_haisla.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_haisla.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_haisla.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_halqemeylem.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_halqemeylem.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_halqemeylem.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_halqemeylem.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_han.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_han.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_han.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_han.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_henqeminem.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_henqeminem.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_henqeminem.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_henqeminem.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_hlgaagilda_xaayda_kil.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_hlgaagilda_xaayda_kil.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_hlgaagilda_xaayda_kil.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_hlgaagilda_xaayda_kil.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_kanienkeha_e.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kanienkeha_e.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_kanienkeha_e.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kanienkeha_e.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_ktunaxa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_ktunaxa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_ktunaxa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_ktunaxa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_kwakwala.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kwakwala.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_kwakwala.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kwakwala.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_kwakwala_liqwala.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kwakwala_liqwala.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_kwakwala_liqwala.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_kwakwala_liqwala.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_migmaq.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_migmaq.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_migmaq.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_migmaq.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_natwits.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_natwits.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_natwits.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_natwits.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nisgaa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nisgaa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_nisgaa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nisgaa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nlekepmxcin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nlekepmxcin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_nlekepmxcin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nlekepmxcin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_northern_tutchone.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_northern_tutchone.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_northern_tutchone.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_northern_tutchone.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nsilxcen.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nsilxcen.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_nsilxcen.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nsilxcen.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_nuucaanul.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nuucaanul.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_nuucaanul.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_nuucaanul.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_secwepemctsin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_secwepemctsin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_secwepemctsin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_secwepemctsin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_sencoten.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_sencoten.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_sencoten.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_sencoten.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_shashishalhem.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_shashishalhem.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_shashishalhem.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_shashishalhem.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_smalgyax.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_smalgyax.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_smalgyax.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_smalgyax.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_southern_tutchone.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_southern_tutchone.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_southern_tutchone.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_southern_tutchone.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_statimcets.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_statimcets.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_statimcets.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_statimcets.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_stlatlimxec.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_stlatlimxec.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_stlatlimxec.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_stlatlimxec.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tagizi_dene.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tagizi_dene.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_tagizi_dene.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tagizi_dene.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_taltan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_taltan.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_taltan.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_taltan.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tlingit.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tlingit.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_tlingit.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tlingit.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tsekehne.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tsekehne.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_tsekehne.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tsekehne.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_tsilhqotin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tsilhqotin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_tsilhqotin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_tsilhqotin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_uwikala.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_uwikala.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_uwikala.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_uwikala.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/fv_xaislakala.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_xaislakala.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/fv_xaislakala.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/fv_xaislakala.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_greek_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_greek_mnemonic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/galaxie_greek_mnemonic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_greek_mnemonic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_greek_positional.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_greek_positional.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/galaxie_greek_positional.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_greek_positional.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_hebrew_mnemonic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_mnemonic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_hebrew_mnemonic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_positional.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_hebrew_positional.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/galaxie_hebrew_positional.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/galaxie_hebrew_positional.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gandhari.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gandhari.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gandhari.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gandhari.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/geezbrhan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/geezbrhan.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/geezbrhan.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/geezbrhan.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gff_amh_7.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_amh_7.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gff_amh_7.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_amh_7.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gff_amharic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_amharic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gff_amharic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_amharic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gff_blin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_blin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gff_blin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_blin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gff_ethiopic_7.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_ethiopic_7.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gff_ethiopic_7.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_ethiopic_7.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gff_geez.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_geez.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gff_geez.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_geez.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gff_gurage.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_gurage.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gff_gurage.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_gurage.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gff_gurage_legacy.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_gurage_legacy.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gff_gurage_legacy.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_gurage_legacy.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gff_musnad.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_musnad.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gff_musnad.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_musnad.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gff_tigrinya_eritrea.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_tigrinya_eritrea.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gff_tigrinya_eritrea.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_tigrinya_eritrea.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gff_tigrinya_ethiopia.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_tigrinya_ethiopia.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gff_tigrinya_ethiopia.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gff_tigrinya_ethiopia.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/ghana.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ghana.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/ghana.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ghana.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gilaki.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gilaki.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gilaki.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gilaki.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gilaki_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gilaki_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gilaki_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gilaki_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gondi_dev.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_dev.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gondi_dev.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_dev.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gondi_gunjala.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_gunjala.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gondi_gunjala.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_gunjala.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/gondi_tel.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_tel.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/gondi_tel.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/gondi_tel.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/greekclassical.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/greekclassical.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/greekclassical.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/greekclassical.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/hanunoo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hanunoo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/hanunoo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hanunoo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/haroi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/haroi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/haroi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/haroi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/hatran_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hatran_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/hatran_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hatran_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/hausa_ajami_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hausa_ajami_qwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/hausa_ajami_qwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hausa_ajami_qwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/hausa_kano.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hausa_kano.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/hausa_kano.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hausa_kano.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/hcesar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hcesar.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/hcesar.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hcesar.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/hieroglyphic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hieroglyphic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/hieroglyphic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hieroglyphic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/himyarit_musnad.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/himyarit_musnad.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/himyarit_musnad.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/himyarit_musnad.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/hindi_modular.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/hindi_modular.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/hindi_modular.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/hindi_modular.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/indigenous_nt.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/indigenous_nt.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/indigenous_nt.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/indigenous_nt.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/indonesia.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/indonesia.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/indonesia.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/indonesia.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/indonesian_suku.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/indonesian_suku.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/indonesian_suku.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/indonesian_suku.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/inuktitut_naqittaut.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/inuktitut_naqittaut.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/inuktitut_naqittaut.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/inuktitut_naqittaut.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/ishkashimi_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ishkashimi_cyrillic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/ishkashimi_cyrillic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ishkashimi_cyrillic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_bengali.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_bengali.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/itrans_bengali.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_bengali.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_devanagari_hindi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_devanagari_hindi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/itrans_devanagari_hindi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_devanagari_hindi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_devanagari_sanskrit_vedic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_devanagari_sanskrit_vedic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/itrans_devanagari_sanskrit_vedic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_devanagari_sanskrit_vedic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_gujarati.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_gujarati.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/itrans_gujarati.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_gujarati.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_gurmukhi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_gurmukhi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/itrans_gurmukhi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_gurmukhi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_odia.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_odia.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/itrans_odia.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_odia.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/itrans_roman.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_roman.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/itrans_roman.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/itrans_roman.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/jawa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/jawa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/jawa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/jawa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/jorai.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/jorai.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/jorai.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/jorai.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/karakalpak_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/karakalpak_cyrillic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/karakalpak_cyrillic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/karakalpak_cyrillic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/karakalpak_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/karakalpak_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/karakalpak_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/karakalpak_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/kayan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/kayan.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/kayan.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/kayan.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/kbdsn1.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/kbdsn1.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/kbdsn1.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/kbdsn1.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/kharoshthi_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/kharoshthi_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/kharoshthi_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/kharoshthi_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/khmer_advanced.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/khmer_advanced.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/khmer_advanced.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/khmer_advanced.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/khmer_angkor.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/khmer_angkor.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/khmer_angkor.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/khmer_angkor.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/khojki_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/khojki_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/khojki_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/khojki_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/kmhmu_2008.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/kmhmu_2008.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/kmhmu_2008.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/kmhmu_2008.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/koalibrere.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/koalibrere.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/koalibrere.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/koalibrere.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/korean_rr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/korean_rr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/korean_rr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/korean_rr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/koreguaje.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/koreguaje.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/koreguaje.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/koreguaje.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/krung.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/krung.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/krung.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/krung.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/lahu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lahu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/lahu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lahu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/lamkaang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lamkaang.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/lamkaang.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lamkaang.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/landuma.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/landuma.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/landuma.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/landuma.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/lao_2008_basic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_2008_basic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/lao_2008_basic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_2008_basic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/lao_2008_rapid.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_2008_rapid.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/lao_2008_rapid.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_2008_rapid.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/lao_pali.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_pali.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/lao_pali.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_pali.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/lao_pali_us.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_pali_us.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/lao_pali_us.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_pali_us.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/lao_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/lao_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lao_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/lazuri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/lazuri.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/lazuri.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/lazuri.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/libtralo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/libtralo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/libtralo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/libtralo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/makasar_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/makasar_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/makasar_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/makasar_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/malar_braille.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_braille.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/malar_braille.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_braille.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/malar_malayalam.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_malayalam.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/malar_malayalam.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_malayalam.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/malar_malayalam_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_malayalam_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/malar_malayalam_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_malayalam_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/malar_tirhuta.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_tirhuta.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/malar_tirhuta.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/malar_tirhuta.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/maltese.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/maltese.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/maltese.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/maltese.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/mandaic_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mandaic_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/mandaic_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mandaic_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/masaram_gondi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/masaram_gondi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/masaram_gondi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/masaram_gondi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/me_en.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/me_en.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/me_en.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/me_en.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/meitei_legacy.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/meitei_legacy.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/meitei_legacy.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/meitei_legacy.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/miluk_hanis_siuslaw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/miluk_hanis_siuslaw.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/miluk_hanis_siuslaw.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/miluk_hanis_siuslaw.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/modi_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/modi_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/modi_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/modi_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/mon_anonta.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mon_anonta.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/mon_anonta.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mon_anonta.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/mon_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mon_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/mon_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mon_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/mongolian_cyrillic_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mongolian_cyrillic_qwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/mongolian_cyrillic_qwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mongolian_cyrillic_qwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/mozhi_malayalam.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mozhi_malayalam.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/mozhi_malayalam.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mozhi_malayalam.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/mro_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/mro_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/mro_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/mro_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/multani_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/multani_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/multani_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/multani_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/multi_pak_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/multi_pak_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/multi_pak_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/multi_pak_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/munji.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/munji.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/munji.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/munji.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/myancode_san.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/myancode_san.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/myancode_san.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/myancode_san.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nabataean_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nabataean_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nabataean_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nabataean_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/naijatype.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/naijatype.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/naijatype.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/naijatype.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nailangs.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nailangs.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nailangs.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nailangs.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nasa_yuwe.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nasa_yuwe.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nasa_yuwe.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nasa_yuwe.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nepali_traditional.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nepali_traditional.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nepali_traditional.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nepali_traditional.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/newa_romanized.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/newa_romanized.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/newa_romanized.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/newa_romanized.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/newa_traditional.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/newa_traditional.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/newa_traditional.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/newa_traditional.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nias.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nias.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nias.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nias.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nisenan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nisenan.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nisenan.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nisenan.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nko.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nko.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nko.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nko.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nkonya.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nkonya.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nkonya.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nkonya.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_bengali_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_bengali_winscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nlci_bengali_winscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_bengali_winscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_devanagari_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_devanagari_winscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nlci_devanagari_winscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_devanagari_winscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_gujarati_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_gujarati_winscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nlci_gujarati_winscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_gujarati_winscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_gurmukhi_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_gurmukhi_winscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nlci_gurmukhi_winscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_gurmukhi_winscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_ipa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_ipa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nlci_ipa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_ipa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_kannada_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_kannada_winscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nlci_kannada_winscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_kannada_winscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_malayalam_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_malayalam_winscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nlci_malayalam_winscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_malayalam_winscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_oriya_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_oriya_winscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nlci_oriya_winscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_oriya_winscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_tamil_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_tamil_winscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nlci_tamil_winscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_tamil_winscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nlci_telugu_winscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_telugu_winscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nlci_telugu_winscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nlci_telugu_winscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nrc_makah.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nrc_makah.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nrc_makah.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nrc_makah.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/ntl_onekey.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ntl_onekey.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/ntl_onekey.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ntl_onekey.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/numanggang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/numanggang.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/numanggang.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/numanggang.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/nw_iranian_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/nw_iranian_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/nw_iranian_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/nw_iranian_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/o_tissi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/o_tissi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/o_tissi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/o_tissi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/obolo_chwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/obolo_chwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/obolo_chwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/obolo_chwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/obolo_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/obolo_qwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/obolo_qwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/obolo_qwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/old_hungarian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/old_hungarian.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/old_hungarian.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/old_hungarian.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/old_turkic_udw21_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/old_turkic_udw21_qwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/old_turkic_udw21_qwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/old_turkic_udw21_qwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/orma.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/orma.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/orma.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/orma.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/osage_nation.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/osage_nation.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/osage_nation.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/osage_nation.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/osage_nation_new.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/osage_nation_new.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/osage_nation_new.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/osage_nation_new.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/otoe_missouria.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/otoe_missouria.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/otoe_missouria.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/otoe_missouria.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/persian_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/persian_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/persian_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/persian_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/pid_piaroa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/pid_piaroa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/pid_piaroa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/pid_piaroa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/pingelap.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/pingelap.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/pingelap.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/pingelap.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_dualstroke.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_uk_dualstroke.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_dualstroke.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_uk_dualstroke.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_natural.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_uk_natural.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/postmodern_english_uk_natural.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_uk_natural.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_us_dualstroke.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_us_dualstroke.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/postmodern_english_us_dualstroke.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_us_dualstroke.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/postmodern_english_us_natural.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_us_natural.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/postmodern_english_us_natural.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/postmodern_english_us_natural.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/pukapuka.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/pukapuka.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/pukapuka.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/pukapuka.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/qom.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/qom.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/qom.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/qom.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/quinault.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/quinault.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/quinault.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/quinault.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/qwerty_farang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/qwerty_farang.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/qwerty_farang.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/qwerty_farang.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_aer.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_aer.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_aer.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_aer.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_arabic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_arabic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_arabic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_arabic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_balti.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_balti.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_balti.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_balti.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_brahui.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_brahui.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_brahui.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_brahui.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_brahui_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_brahui_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_brahui_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_brahui_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_burushaski.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_burushaski.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_burushaski.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_burushaski.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_dameli.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dameli.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_dameli.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dameli.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_dhatki.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dhatki.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_dhatki.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dhatki.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_dogri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dogri.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_dogri.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_dogri.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_gawar_bati.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_gawar_bati.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_gawar_bati.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_gawar_bati.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_gawri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_gawri.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_gawri.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_gawri.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_hazaragi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_hazaragi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_hazaragi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_hazaragi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_hindko.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_hindko.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_hindko.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_hindko.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_indus_kohistani.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_indus_kohistani.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_indus_kohistani.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_indus_kohistani.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_kalasha.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kalasha.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_kalasha.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kalasha.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_kashmir_shina.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kashmir_shina.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_kashmir_shina.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kashmir_shina.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_kashmiri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kashmiri.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_kashmiri.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_kashmiri.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_khowar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_khowar.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_khowar.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_khowar.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_marwari.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_marwari.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_marwari.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_marwari.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_munji.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_munji.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_munji.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_munji.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_oadki.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_oadki.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_oadki.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_oadki.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_ormuri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_ormuri.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_ormuri.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_ormuri.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_pahari.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pahari.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_pahari.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pahari.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_palula.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_palula.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_palula.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_palula.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_parkari_koli.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_parkari_koli.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_parkari_koli.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_parkari_koli.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_pashai.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pashai.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_pashai.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pashai.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_pashto.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pashto.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_pashto.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_pashto.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_saraiki.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_saraiki.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_saraiki.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_saraiki.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_shina.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_shina.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_shina.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_shina.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_sindhi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_sindhi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_sindhi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_sindhi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_torwali.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_torwali.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_torwali.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_torwali.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_urdu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_urdu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_urdu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_urdu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_ushojo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_ushojo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_ushojo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_ushojo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_uyghur.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_uyghur.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_uyghur.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_uyghur.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_wadiyara.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_wadiyara.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_wadiyara.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_wadiyara.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_wakhi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_wakhi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_wakhi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_wakhi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_western_punjabi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_western_punjabi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_western_punjabi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_western_punjabi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rac_yidgha.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_yidgha.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rac_yidgha.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rac_yidgha.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rawang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rawang.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rawang.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rawang.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rejang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rejang.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rejang.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rejang.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/remington_gail.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/remington_gail.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/remington_gail.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/remington_gail.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rohingya_arab.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rohingya_arab.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rohingya_arab.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rohingya_arab.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/rossel.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/rossel.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/rossel.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/rossel.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/runeboard.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/runeboard.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/runeboard.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/runeboard.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/russian_mnemonic_r.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/russian_mnemonic_r.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/russian_mnemonic_r.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/russian_mnemonic_r.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sabdalipi_assamese.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sabdalipi_assamese.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sabdalipi_assamese.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sabdalipi_assamese.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sahaptin_umatilla.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sahaptin_umatilla.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sahaptin_umatilla.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sahaptin_umatilla.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sahaptin_yakima.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sahaptin_yakima.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sahaptin_yakima.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sahaptin_yakima.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sanjha_punjabi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sanjha_punjabi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sanjha_punjabi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sanjha_punjabi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/santali_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/santali_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/santali_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/santali_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/saraiki.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/saraiki.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/saraiki.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/saraiki.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/satere.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/satere.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/satere.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/satere.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/shahmukhi_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/shahmukhi_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/shahmukhi_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/shahmukhi_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/shan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/shan.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/shan.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/shan.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/shaw_2layer.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/shaw_2layer.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/shaw_2layer.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/shaw_2layer.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/siddham_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/siddham_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/siddham_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/siddham_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_akha_act.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_akha_act.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_akha_act.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_akha_act.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_arabic_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_arabic_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_arabic_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_arabic_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_areare.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_areare.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_areare.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_areare.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bari.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bari.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_bari.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bari.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bengali_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bengali_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_bengali_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bengali_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bolivia.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bolivia.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_bolivia.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bolivia.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_boonkit.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_boonkit.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_boonkit.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_boonkit.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_brao.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_brao.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_brao.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_brao.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bru.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bru.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_bru.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bru.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_buang.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_buang.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_buang.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_buang.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bunong.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bunong.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_bunong.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bunong.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_busa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_busa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_busa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_busa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_bwe_karen.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bwe_karen.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_bwe_karen.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_bwe_karen.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cameroon_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cameroon_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_cameroon_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cameroon_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cameroon_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cameroon_qwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_cameroon_qwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cameroon_qwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cherokee_nation.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cherokee_nation.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_cherokee_nation.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cherokee_nation.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cheyenne.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cheyenne.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_cheyenne.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cheyenne.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_cipher_music.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cipher_music.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_cipher_music.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_cipher_music.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_devanagari_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_devanagari_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_devanagari_romanized.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_romanized.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_devanagari_romanized.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_romanized.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_devanagari_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_typewriter.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_devanagari_typewriter.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_devanagari_typewriter.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_dzongkha.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_dzongkha.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_dzongkha.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_dzongkha.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_eastern_congo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_eastern_congo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_eastern_congo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_eastern_congo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_el_ethiopian_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_el_ethiopian_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_el_ethiopian_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_el_ethiopian_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ethiopic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ethiopic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_ethiopic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ethiopic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ethiopic_power_g.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ethiopic_power_g.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_ethiopic_power_g.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ethiopic_power_g.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_euro_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_euro_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_euro_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_euro_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_extended_urdu_np.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_extended_urdu_np.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_extended_urdu_np.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_extended_urdu_np.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_greek_polytonic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_greek_polytonic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_greek_polytonic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_greek_polytonic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hawaiian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hawaiian.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_hawaiian.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hawaiian.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hebr_grek_trans.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebr_grek_trans.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_hebr_grek_trans.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebr_grek_trans.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hebrew.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebrew.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_hebrew.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebrew.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hebrew_legacy.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebrew_legacy.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_hebrew_legacy.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hebrew_legacy.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_hmd_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hmd_plrd.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_hmd_plrd.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_hmd_plrd.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_indic_roman.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_indic_roman.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_indic_roman.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_indic_roman.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ipa.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ipa.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_ipa.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ipa.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_jarai.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_jarai.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_jarai.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_jarai.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kayah_kali.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_kali.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_kayah_kali.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_kali.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kayah_latn.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_latn.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_kayah_latn.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_latn.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kayah_mymr.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_mymr.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_kayah_mymr.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kayah_mymr.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_khamti.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khamti.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_khamti.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khamti.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_khmer.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khmer.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_khmer.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khmer.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_khowar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khowar.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_khowar.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_khowar.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kmhmu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kmhmu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_kmhmu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kmhmu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_korda_jamo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korda_jamo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_korda_jamo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korda_jamo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_korda_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korda_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_korda_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korda_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_korean_morse.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korean_morse.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_korean_morse.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_korean_morse.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_kvl_kayaw.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kvl_kayaw.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_kvl_kayaw.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_kvl_kayaw.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lepcha.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lepcha.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_lepcha.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lepcha.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_limbu_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_limbu_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_limbu_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_limbu_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_limbu_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_limbu_typewriter.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_limbu_typewriter.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_limbu_typewriter.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lisu_basic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lisu_basic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_lisu_basic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lisu_basic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lisu_standard.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lisu_standard.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_lisu_standard.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lisu_standard.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_lpo_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lpo_plrd.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_lpo_plrd.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_lpo_plrd.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_madi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_madi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_madi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_madi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_makuri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_makuri.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_makuri.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_makuri.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_mali_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_mali_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_mali_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_qwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_mali_qwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_qwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_mali_qwertz.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_qwertz.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_mali_qwertz.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_mali_qwertz.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_moore.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_moore.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_moore.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_moore.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_myanmar_my3.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_myanmar_my3.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_myanmar_my3.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_myanmar_my3.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_myanmar_mywinext.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_myanmar_mywinext.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_myanmar_mywinext.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_myanmar_mywinext.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nigeria_dot.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_dot.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_nigeria_dot.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_dot.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nigeria_odd_vowels.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_odd_vowels.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_nigeria_odd_vowels.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_odd_vowels.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nigeria_underline.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_underline.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_nigeria_underline.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nigeria_underline.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nko.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nko.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_nko.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nko.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_nubian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nubian.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_nubian.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_nubian.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_pan_africa_mnemonic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_pan_africa_mnemonic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_pan_africa_mnemonic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_pan_africa_mnemonic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_pan_africa_positional.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_pan_africa_positional.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_pan_africa_positional.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_pan_africa_positional.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_philippines.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_philippines.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_philippines.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_philippines.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_sahu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_sahu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_sahu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_sahu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_bsc_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_bsc_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_bsc_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_bsc_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_cou_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_cou_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_cou_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_cou_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_dyo_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_dyo_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_dyo_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_dyo_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_gsl_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_gsl_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_gsl_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_gsl_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_krx_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_krx_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_krx_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_krx_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_ndv_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_ndv_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_ndv_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_ndv_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_sav_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_sav_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_sav_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_sav_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_snf_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_snf_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_snf_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_snf_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_srr_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_srr_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_srr_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_srr_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_senegal_wo_azerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_wo_azerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_senegal_wo_azerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_senegal_wo_azerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_sgaw_karen.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_sgaw_karen.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_sgaw_karen.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_sgaw_karen.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_shan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_shan.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_shan.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_shan.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_tai_dam.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_lao.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_lao.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_tai_dam_lao.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_lao.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_tai_dam_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tai_dam_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_typewriter.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_tai_dam_typewriter.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tai_dam_typewriter.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tawallammat.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tawallammat.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_tawallammat.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tawallammat.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tchad.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tchad.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_tchad.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tchad.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tepehuan.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tepehuan.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_tepehuan.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tepehuan.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_torwali.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_torwali.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_torwali.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_torwali.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_tunisian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tunisian.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_tunisian.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_tunisian.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_uganda_tanzania.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_uganda_tanzania.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_uganda_tanzania.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_uganda_tanzania.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_vai.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_vai.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_vai.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_vai.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_wayuu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_wayuu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_wayuu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_wayuu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ygp_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ygp_plrd.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_ygp_plrd.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ygp_plrd.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_yi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yna_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yna_plrd.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_yna_plrd.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yna_plrd.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yoruba8.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba8.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_yoruba8.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba8.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yoruba_bar.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba_bar.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_yoruba_bar.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba_bar.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yoruba_dot.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba_dot.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_yoruba_dot.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yoruba_dot.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yupik_cyrillic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yupik_cyrillic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic_ru.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yupik_cyrillic_ru.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_yupik_cyrillic_ru.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_yupik_cyrillic_ru.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sil_ywq_plrd.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ywq_plrd.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sil_ywq_plrd.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sil_ywq_plrd.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/slc_saliba.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/slc_saliba.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/slc_saliba.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/slc_saliba.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/soqotri_arabic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/soqotri_arabic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/soqotri_arabic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/soqotri_arabic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/srr_ajami_qwerty.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/srr_ajami_qwerty.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/srr_ajami_qwerty.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/srr_ajami_qwerty.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sundanese.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sundanese.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sundanese.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sundanese.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sundanese_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sundanese_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sundanese_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sundanese_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/swanalekha_malayalam.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/swanalekha_malayalam.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/swanalekha_malayalam.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/swanalekha_malayalam.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sxava.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sxava.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sxava.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sxava.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sxava_eo.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sxava_eo.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sxava_eo.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sxava_eo.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/sylheti_nagri.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/sylheti_nagri.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/sylheti_nagri.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/sylheti_nagri.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/syriac_arabic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/syriac_arabic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/syriac_arabic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/syriac_arabic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/syriac_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/syriac_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/syriac_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/syriac_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/tagbanwa_inscript.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tagbanwa_inscript.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/tagbanwa_inscript.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tagbanwa_inscript.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/taigi_poj.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/taigi_poj.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/taigi_poj.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/taigi_poj.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/tainua.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tainua.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/tainua.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tainua.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/tangsa_lakhum.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tangsa_lakhum.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/tangsa_lakhum.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tangsa_lakhum.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/tawallammat_latin.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tawallammat_latin.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/tawallammat_latin.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tawallammat_latin.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/teggargrent_lat.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/teggargrent_lat.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/teggargrent_lat.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/teggargrent_lat.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/tem_kdh.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tem_kdh.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/tem_kdh.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tem_kdh.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_anjal_paangu.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_anjal_paangu.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/thamizha_anjal_paangu.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_anjal_paangu.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_bamini.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_bamini.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/thamizha_bamini.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_bamini.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_new_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_new_typewriter.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/thamizha_new_typewriter.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_new_typewriter.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/thamizha_tamil99_ext.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_tamil99_ext.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/thamizha_tamil99_ext.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/thamizha_tamil99_ext.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/tibetan_direct_input.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tibetan_direct_input.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/tibetan_direct_input.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tibetan_direct_input.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/tibetan_ewts.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tibetan_ewts.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/tibetan_ewts.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tibetan_ewts.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/tirhuta.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tirhuta.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/tirhuta.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tirhuta.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/tlahuica.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tlahuica.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/tlahuica.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tlahuica.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/tsakonian.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tsakonian.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/tsakonian.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tsakonian.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/tuareg_tifinagh.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/tuareg_tifinagh.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/tuareg_tifinagh.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/tuareg_tifinagh.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/turkmen_cyrl.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/turkmen_cyrl.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/turkmen_cyrl.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/turkmen_cyrl.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/txo_toto.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/txo_toto.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/txo_toto.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/txo_toto.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/udi_keyboard.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/udi_keyboard.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/udi_keyboard.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/udi_keyboard.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/ukwuani.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/ukwuani.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/ukwuani.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/ukwuani.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/uma_graphic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/uma_graphic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/uma_graphic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/uma_graphic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/uma_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/uma_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/uma_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/uma_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/urdu_phonetic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/urdu_phonetic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/urdu_phonetic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/urdu_phonetic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/urdu_phonetic_crulp.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/urdu_phonetic_crulp.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/urdu_phonetic_crulp.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/urdu_phonetic_crulp.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/venetia_et_histria.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/venetia_et_histria.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/venetia_et_histria.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/venetia_et_histria.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/vm_tamil_modular.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/vm_tamil_modular.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/vm_tamil_modular.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/vm_tamil_modular.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/vm_tamil_typewriter.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/vm_tamil_typewriter.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/vm_tamil_typewriter.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/vm_tamil_typewriter.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/wakhi_anglicized.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_anglicized.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/wakhi_anglicized.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_anglicized.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/wakhi_cyrillic.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_cyrillic.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/wakhi_cyrillic.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_cyrillic.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/wakhi_standard.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_standard.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/wakhi_standard.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/wakhi_standard.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/wancho.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/wancho.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/wancho.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/wancho.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/warang_citi.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/warang_citi.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/warang_citi.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/warang_citi.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/wolofal.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/wolofal.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/wolofal.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/wolofal.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/xinaliq.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/xinaliq.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/xinaliq.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/xinaliq.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/yiddish_pasekh.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/yiddish_pasekh.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/yiddish_pasekh.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/yiddish_pasekh.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/yidgha.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/yidgha.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/yidgha.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/yidgha.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/younger_futhark_short_twig.kmx b/developer/src/kmcmplib/tests/fixtures/keyboards-repo/younger_futhark_short_twig.kmx similarity index 100% rename from developer/src/kmcmplib/tests/fixtures/younger_futhark_short_twig.kmx rename to developer/src/kmcmplib/tests/fixtures/keyboards-repo/younger_futhark_short_twig.kmx diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/compile_legacy.bat b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/compile_legacy.bat new file mode 100644 index 0000000000000000000000000000000000000000..5e8b891ea6cf163c1e4dce6c089a835c6b153e78 --- /dev/null +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/compile_legacy.bat @@ -0,0 +1,4 @@ +@echo off +echo Compiles the keyboards using the legacy kmcomp.exe +echo to use as baseline comparisons for kmcmplib +for %%d in (*.kmn) do ..\..\..\..\..\bin\kmcomp -no-compiler-version -d %%d \ No newline at end of file diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmn new file mode 100644 index 0000000000000000000000000000000000000000..2b3180c36ab4b473e74b1b3487fc8f0a61d623f6 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmn differ diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmx new file mode 100644 index 0000000000000000000000000000000000000000..13ea2a686e09c16db68ff7bb355e114b9125d76d Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k001_utf16.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmn new file mode 100644 index 0000000000000000000000000000000000000000..da6d4e70873e5762dd49a41d13721abebb5ba668 --- /dev/null +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmn @@ -0,0 +1,11 @@ +c Description: Verifies that kmcmplib can compile a UTF-8 file (without BOM) + +store(&NAME) 'k002_utf8_without_bom' +store(&VERSION) '9.0' + +begin unicode > use(main) + +group(main) using keys + ++ [K_A] > 'a' +'a' + [K_B] > 'ខ្មែរ' diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmx new file mode 100644 index 0000000000000000000000000000000000000000..0b9c9294182d9387165c98e1ffc84c8fe45452e3 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k002_utf8_without_bom.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmn new file mode 100644 index 0000000000000000000000000000000000000000..b2bccb036caad4d891ef62c5bc31c02ebaaffddd --- /dev/null +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmn @@ -0,0 +1,11 @@ +c Description: Verifies that kmcmplib can compile a UTF-8 file (with BOM) + +store(&NAME) 'k003_utf8_with_bom' +store(&VERSION) '9.0' + +begin unicode > use(main) + +group(main) using keys + ++ [K_A] > 'a' +'a' + [K_B] > 'ខ្មែរ' diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmx new file mode 100644 index 0000000000000000000000000000000000000000..ee1cf4225502e3c7944b58f301e5be649af4ec00 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k003_utf8_with_bom.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmn new file mode 100644 index 0000000000000000000000000000000000000000..50a1b31ea1c7b4d369e66dc69021be22f9cdeacb --- /dev/null +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmn @@ -0,0 +1,13 @@ +c Description: Verifies that kmcmplib can compile an ANSI file +c This has some high-ascii letters in cp1252 to ensure that +c it fails to load as 'utf8 without bom' + +store(&NAME) 'k004_ansi' +store(&VERSION) '9.0' + +begin unicode > use(main) + +group(main) using keys + ++ [K_A] > 'a' +'a' + [K_B] > 'ÀÐ' diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmx new file mode 100644 index 0000000000000000000000000000000000000000..6c41da979cb2a3ec20aadf6de0ca778fb8ce5ec1 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k004_ansi.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.bmp b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.bmp new file mode 100644 index 0000000000000000000000000000000000000000..509b89ae6df7f09a442ae60f215a6c7b3211b34f Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.bmp differ diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmn new file mode 100644 index 0000000000000000000000000000000000000000..926ccdeeafbf5bf648903d0bdc530a5e0ffa9ce9 --- /dev/null +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmn @@ -0,0 +1,12 @@ +c Description: Verifies that kmcmplib can load a bitmap without file extension + +store(&NAME) 'k005_bitmap' +store(&VERSION) '9.0' +store(&BITMAP) 'k005_bitmap' + +begin unicode > use(main) + +group(main) using keys + ++ [K_A] > 'a' +'a' + [K_B] > 'ខ្មែរ' diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmx new file mode 100644 index 0000000000000000000000000000000000000000..bc9bc9b671724050e169e59fce976a18d54a030e Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k005_bitmap.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.ico b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e1b64cf9013e6702420d97fd88b120490f33b137 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.ico differ diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmn new file mode 100644 index 0000000000000000000000000000000000000000..4ad3e61659683213ddc6b44a0454c87cc7576d11 --- /dev/null +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmn @@ -0,0 +1,12 @@ +c Description: Verifies that kmcmplib can load an icon with file extension + +store(&NAME) 'k006_icon' +store(&VERSION) '9.0' +store(&BITMAP) 'k006_icon.ico' + +begin unicode > use(main) + +group(main) using keys + ++ [K_A] > 'a' +'a' + [K_B] > 'ខ្មែរ' diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f994a1dcac9e5ae797eb88eed9cee4b0e6f85b00 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k006_icon.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmn new file mode 100644 index 0000000000000000000000000000000000000000..71d72d5120b522cd20303f3d0843e4720791230c --- /dev/null +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmn @@ -0,0 +1,12 @@ +c Description: Verifies that kmcmplib can load an includecodes file with \r\n line endings + +store(&NAME) 'k007_includecodes_r_n' +store(&VERSION) '9.0' +store(&includecodes) 'k007_includecodes_r_n.txt' + +begin unicode > use(main) + +group(main) using keys + ++ [K_A] > $LOWER_A +$LOWER_A + [K_B] > $LOWER_B diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmx new file mode 100644 index 0000000000000000000000000000000000000000..f8b4eac1b0d9103c7767e487b5109a06aed8e1ec Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.txt b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.txt new file mode 100644 index 0000000000000000000000000000000000000000..4cc3fe4351ddeb205cdd985d04648ec403065e35 --- /dev/null +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k007_includecodes_r_n.txt @@ -0,0 +1,2 @@ +0061;LOWER_A +0062;LOWER_B diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmn new file mode 100644 index 0000000000000000000000000000000000000000..ea357b4b0ef6fc8331efb7f63aca9e3fc9291652 --- /dev/null +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmn @@ -0,0 +1,12 @@ +c Description: Verifies that kmcmplib can load an includecodes file with \n line endings + +store(&NAME) 'k008_includecodes_n' +store(&VERSION) '9.0' +store(&includecodes) 'k008_includecodes_n.txt' + +begin unicode > use(main) + +group(main) using keys + ++ [K_A] > $LOWER_A +$LOWER_A + [K_B] > $LOWER_B diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmx new file mode 100644 index 0000000000000000000000000000000000000000..aa158cdd546cdde915e4a55c1db521e57d4abb04 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.kmx differ diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.txt b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.txt new file mode 100644 index 0000000000000000000000000000000000000000..4cc3fe4351ddeb205cdd985d04648ec403065e35 --- /dev/null +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k008_includecodes_n.txt @@ -0,0 +1,2 @@ +0061;LOWER_A +0062;LOWER_B diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmn b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmn new file mode 100644 index 0000000000000000000000000000000000000000..a0ad9d0d9fbc820432982d13455f5a02acda4018 --- /dev/null +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmn @@ -0,0 +1,31 @@ +c Description: Verifies that kmcmplib can deal with long lines continued with backslashes, and also comments + +store(&NAME) 'k009_long_lines' +store(&VERSION) '9.0' + +begin unicode > use(main) + +group(main) using keys + +c +c ***Note*** This file has intentional whitespace at the end of line ending in [SHIFT K_N] +c in order to verify that line continuations are working correctly +c + +store(c_key) [K_K] [K_X] [SHIFT K_K] [SHIFT K_X] [K_G] \ + [K_C] [K_Q] [SHIFT K_C] [SHIFT K_Q] [SHIFT K_J] \ + [K_D] [K_Z] [SHIFT K_D] [SHIFT K_Z] [SHIFT K_N] \ + [K_T] [K_F] [SHIFT K_T] [SHIFT K_F] [K_N] \ + [K_B] [K_P] [SHIFT K_B] [SHIFT K_P] [K_M] \ + [K_Y] [K_R] [K_L] [K_V] [K_S] [K_H] [SHIFT K_L] [SHIFT K_G] \ + [RALT K_K] [RALT K_B] +store(c_out) U+1780 U+1781 U+1782 U+1783 U+1784 \ + U+1785 U+1786 U+1787 U+1788 U+1789 \ + U+178A U+178B U+178C U+178D U+178E \ + U+178F U+1790 U+1791 U+1792 U+1793 \ + U+1794 U+1795 U+1796 U+1797 U+1798 \ + U+1799 U+179A U+179B U+179C U+179F U+17A0 U+17A1 U+17A2 \ + U+179D U+179E c deprecated, but they are used in minority languages + ++ any(c_key) > index(c_out, 1) + diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmx b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmx new file mode 100644 index 0000000000000000000000000000000000000000..4acd4cc051adc10d35c6ae842b24be5cf43f0cf4 Binary files /dev/null and b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/k009_long_lines.kmx differ diff --git a/developer/src/kmcmplib/tests/kmcompxtest.cpp b/developer/src/kmcmplib/tests/kmcompxtest.cpp index fc1e23a5148b4df8bbb8437127e4600d0523ed71..5dbaee88bc9990af9513c8ffd84882250c25d92d 100644 --- a/developer/src/kmcmplib/tests/kmcompxtest.cpp +++ b/developer/src/kmcmplib/tests/kmcompxtest.cpp @@ -13,6 +13,8 @@ #include #include #include +#include "util_filesystem.h" +#include "util_callbacks.h" #ifdef _MSC_VER #else @@ -21,29 +23,11 @@ using namespace std; -vector < int > error_vec; - #define CERR_FATAL 0x00008000 #define CERR_ERROR 0x00004000 #define CERR_WARNING 0x00002000 #define CERR_HINT 0x00001000 -int msgproc(int line, uint32_t dwMsgCode, char* szText, void* context) -{ - error_vec.push_back(dwMsgCode); - const char*t = "unknown"; - switch(dwMsgCode & 0xF000) { - case CERR_HINT: t=" hint"; break; - case CERR_WARNING: t="warning"; break; - case CERR_ERROR: t=" error"; break; - case CERR_FATAL: t=" fatal"; break; - } - printf("line %d %s %04.4x: %s\n", line, t, (unsigned int)dwMsgCode, szText); - return 1; -} - -#include "../src/filesystem.h" - int main(int argc, char *argv[]) { if(argc < 4) { @@ -71,14 +55,15 @@ int main(int argc, char *argv[]) char first5[6] = "CERR_"; char* pfirst5 = first5; - KMCMP_COMPILER_OPTIONS kcopts; - kcopts.dwSize = sizeof(KMCMP_COMPILER_OPTIONS); - kcopts.ShouldAddCompilerVersion = false; // So we can compare against existing compiled keyboards that also don't have compiler version - if(!kmcmp_SetCompilerOptions(&kcopts)) { - return __LINE__; - } + KMCMP_COMPILER_RESULT result; + KMCMP_COMPILER_OPTIONS options; + options.saveDebug = true; + options.compilerWarningsAsErrors = false; + options.warnDeprecatedCode = true; + options.shouldAddCompilerVersion = false; + options.target = CKF_KEYMAN; - if(kmcmp_CompileKeyboardFile(kmn_file, kmx_file, true, false, true, msgproc, nullptr, CKF_KEYMAN)) { + if(kmcmp_CompileKeyboard(kmn_file, options, msgproc, loadfileProc, nullptr, result)) { char* testname = strrchr( (char*) kmn_file, '/') + 1; if(strncmp(testname, pfirst5, 5) == 0){ return __LINE__; // exit code: CERR_ in Name + no Error found @@ -87,26 +72,24 @@ int main(int argc, char *argv[]) // On non-win32 platforms, we cannot get kmcmpdll.dll to build keyboards // legacy-mode, so we'll compare to a hopefully existing file that we've // been passed - FILE* fp1 = Open_File(kmx_file, "rb"); + FILE* fp1 = Open_File(kmx_file, "wb"); if(!fp1) return __LINE__; + // Write out for reference + fwrite(result.kmx, 1, result.kmxSize, fp1); + FILE* fp2 = Open_File(reference_kmx, "rb"); if(!fp2) return __LINE__; // exit code: fail if no reference kmx file in build-folder - fseek(fp1, 0, SEEK_END); - auto sz1 = ftell(fp1); - fseek(fp1, 0, SEEK_SET); fseek(fp2, 0, SEEK_END); auto sz2 = ftell(fp2); fseek(fp2, 0, SEEK_SET); - if (sz1 != sz2) return __LINE__; // exit code: size of kmx-file in build differs from size of kmx-file in source folder + if (result.kmxSize != sz2) return __LINE__; // exit code: size of kmx-file in build differs from size of kmx-file in source folder - char* buf1 = new char[sz1]; - char* buf2 = new char[sz1]; - fread(buf1, 1, sz1, fp1); - fread(buf2, 1, sz1, fp2); - return memcmp(buf1, buf2, sz1) ? __LINE__ : 0; // exit code: when contents of kmx-file in build differs from contents of kmx-file in source folder + char* buf2 = new char[result.kmxSize]; + fread(buf2, 1, result.kmxSize, fp2); + return memcmp(result.kmx, buf2, result.kmxSize) ? __LINE__ : 0; // exit code: when contents of kmx-file in build differs from contents of kmx-file in source folder // success: when contents of kmx-file in build and source folder are the same } else { /*if Errors found: check number (e.g. CERR_4061_balochi_phonetic.kmn should produce Error 4061)*/ diff --git a/developer/src/kmcmplib/tests/meson.build b/developer/src/kmcmplib/tests/meson.build index b81429806e16a3d414c2626c34fba9caa4d2bc12..a048bc8236b61803f5f16ef5f91bad7b7e56eae0 100644 --- a/developer/src/kmcmplib/tests/meson.build +++ b/developer/src/kmcmplib/tests/meson.build @@ -6,16 +6,20 @@ fs = import('fs') -tests_flags = [] +tests_links = [] + +if cpp_compiler.get_id() == 'emscripten' + tests_links += ['-lnodefs.js'] +endif input_path = meson.current_source_dir() / '../../../../common/test/keyboards/baseline' output_path = meson.current_build_dir() -kmcompxtest = executable('kmcompxtest', 'kmcompxtest.cpp', - cpp_args: defns, +kmcompxtest = executable('kmcompxtest', ['kmcompxtest.cpp','util_filesystem.cpp','util_callbacks.cpp'], + cpp_args: defns + flags, include_directories: inc, name_suffix: name_suffix, - link_args: links + tests_flags, + link_args: links + tests_links, objects: lib.extract_all_objects(), dependencies: icuuc_dep, ) @@ -76,6 +80,26 @@ foreach kbd : tests test(kbd, kmcompxtest, args: [kbd_src, kbd_obj, join_paths(input_path, kbd) + '.kmx']) endforeach +valid_keyboard_tests = [ + 'k001_utf16', + 'k002_utf8_without_bom', + 'k003_utf8_with_bom', + # 'k004_ansi', # TODO: enable ansi test when we have the icu datafiles #8884 + 'k005_bitmap', + 'k006_icon', + 'k007_includecodes_r_n', + 'k008_includecodes_n', + 'k009_long_lines', +] + +fixtures_path = meson.current_source_dir() / 'fixtures/valid-keyboards' + +foreach kbd : valid_keyboard_tests + kbd_src = join_paths(fixtures_path, kbd) + '.kmn' + kbd_obj = join_paths(output_path, kbd) + '.kmx' + test(kbd, kmcompxtest, args: [kbd_src, kbd_obj, join_paths(fixtures_path, kbd) + '.kmx']) +endforeach + # Test fixtures that come from keyboards repo -- but only for a "full" test, # which typically we run on CI no more than once a day, because it's expensive. @@ -96,7 +120,7 @@ if get_option('full_test') # kbd is going to be an absolute path kbd_obj = output_path / test_basename + '.kmx' - reference_kmx = meson.current_source_dir() / 'fixtures' / test_basename + '.kmx' + reference_kmx = meson.current_source_dir() / 'fixtures/keyboards-repo' / test_basename + '.kmx' if fs.is_file(reference_kmx) test(test_basename, kmcompxtest, args: [kbd, kbd_obj, reference_kmx]) @@ -107,22 +131,22 @@ endif # Test the API endpoints -apitest = executable('api-test', 'api-test.cpp', - cpp_args: defns, +apitest = executable('api-test', ['api-test.cpp','util_filesystem.cpp','util_callbacks.cpp'], + cpp_args: defns + flags, include_directories: inc, name_suffix: name_suffix, - link_args: links + tests_flags, + link_args: links + tests_links, objects: lib.extract_all_objects(), - dependencies: icuuc_dep, + dependencies: icuuc_dep ) -test('api-test', apitest) +test('api-test', apitest, args: [output_path / 'blank_keyboard.kmx']) usetapitest = executable('uset-api-test', 'uset-api-test.cpp', - cpp_args: defns, + cpp_args: defns + flags, include_directories: inc, name_suffix: name_suffix, - link_args: links + tests_flags, + link_args: links + tests_links, objects: lib.extract_all_objects(), dependencies: icuuc_dep, ) diff --git a/developer/src/kmcmplib/tests/prep.sh b/developer/src/kmcmplib/tests/prep.sh index 058d4a1b9b0bfdd50fe13d29bcbdff7af508bd3c..8ad9b81171f165903fe74d4b7eb47f6f39154800 100755 --- a/developer/src/kmcmplib/tests/prep.sh +++ b/developer/src/kmcmplib/tests/prep.sh @@ -39,30 +39,30 @@ if [[ ! -d "$KEYBOARDS_ROOT/.git" ]] || [[ ! -f "$KEYBOARDS_ROOT/tools/regressio fi if builder_start_action clean; then - rm -rf "$THIS_SCRIPT_PATH/fixtures" + rm -rf "$THIS_SCRIPT_PATH/fixtures/keyboards-repo" rm -f "$THIS_SCRIPT_PATH/keyboards_commit_ref.txt" builder_finish_action success clean fi if builder_start_action build; then # yes, remove existing fixtures before build so we don't end up with stale fixtures - rm -rf "$THIS_SCRIPT_PATH/fixtures" + rm -rf "$THIS_SCRIPT_PATH/fixtures/keyboards-repo" # record the revision of the keyboards repo pushd "$KEYBOARDS_ROOT" > /dev/null git rev-parse head > "$THIS_SCRIPT_PATH/keyboards_commit_ref.txt" popd > /dev/null - "$KEYBOARDS_ROOT/tools/regression.sh" --use-legacy-compiler --local "$KEYMAN_ROOT/developer/bin/" --output "$THIS_SCRIPT_PATH/fixtures/" + "$KEYBOARDS_ROOT/tools/regression.sh" --use-legacy-compiler --local "$KEYMAN_ROOT/developer/bin/" --output "$THIS_SCRIPT_PATH/fixtures/keyboards-repo/" cd "$THIS_SCRIPT_PATH" # We don't need visual keyboards - rm ./fixtures/*.kvk + rm ./fixtures/keyboards-repo/*.kvk # Nor do we need kmw outputs - rm ./fixtures/*.js + rm ./fixtures/keyboards-repo/*.js # Finally, we remove any keyboards over 500kb, as we won't automatically test them for now # TODO: consider if we want to keep those large keyboards in the future - find ./fixtures/ -size +500k -exec rm {} \+ + find ./fixtures/keyboards-repo/ -size +500k -exec rm {} \+ builder_finish_action success build fi \ No newline at end of file diff --git a/developer/src/kmcmplib/tests/uset-api-test.cpp b/developer/src/kmcmplib/tests/uset-api-test.cpp index 4fe599ca2e83a1af9d50d61368bafa4b337f3c82..61d263ad157cc365d885c463bc6bc200a893add7 100644 --- a/developer/src/kmcmplib/tests/uset-api-test.cpp +++ b/developer/src/kmcmplib/tests/uset-api-test.cpp @@ -19,30 +19,32 @@ #include "../src/compfile.h" #include -void test_kmcmp_ParseUnicodeSetProc(); +void test_kmcmp_parseUnicodeSet(); // std::vector error_vec; int main(int argc, char *argv[]) { - test_kmcmp_ParseUnicodeSetProc(); + test_kmcmp_parseUnicodeSet(); return 0; } -void test_kmcmp_ParseUnicodeSetProc() { +void test_kmcmp_parseUnicodeSet() { { // null test const auto bufsiz = 128; uint32_t buf[bufsiz]; - int rc = kmcmp_ParseUnicodeSet(u8"[]", buf, bufsiz); + uintptr_t buf_ = reinterpret_cast(buf); + int rc = kmcmp_parseUnicodeSet(u8"[]", buf_, bufsiz); assert(rc == KMCMP_USET_OK); } { // basic test const auto bufsiz = 128; uint32_t buf[bufsiz]; - int rc = kmcmp_ParseUnicodeSet(u8"[x A-C]", buf, bufsiz); + uintptr_t buf_ = reinterpret_cast(buf); + int rc = kmcmp_parseUnicodeSet(u8"[x A-C]", buf_, bufsiz); assert(rc == 2); assert(buf[0] == 0x41); assert(buf[1] == 0x43); @@ -53,7 +55,8 @@ void test_kmcmp_ParseUnicodeSetProc() { // bigger test const auto bufsiz = 128; uint32_t buf[bufsiz]; - int rc = kmcmp_ParseUnicodeSet(u8"[[🙀A-C]-[CB]]", buf, bufsiz); + uintptr_t buf_ = reinterpret_cast(buf); + int rc = kmcmp_parseUnicodeSet(u8"[[🙀A-C]-[CB]]", buf_, bufsiz); assert(rc == 2); assert(buf[0] == 0x41); assert(buf[1] == 0x41); @@ -64,35 +67,40 @@ void test_kmcmp_ParseUnicodeSetProc() { // overflow test const auto bufsiz = 1; uint32_t buf[bufsiz]; - int rc = kmcmp_ParseUnicodeSet(u8"[x A-C]", buf, bufsiz); + uintptr_t buf_ = reinterpret_cast(buf); + int rc = kmcmp_parseUnicodeSet(u8"[x A-C]", buf_, bufsiz); assert(rc == KMCMP_FATAL_OUT_OF_RANGE); } { // err test const auto bufsiz = 128; uint32_t buf[bufsiz]; - int rc = kmcmp_ParseUnicodeSet(u8"[:Adlm:]", buf, bufsiz); + uintptr_t buf_ = reinterpret_cast(buf); + int rc = kmcmp_parseUnicodeSet(u8"[:Adlm:]", buf_, bufsiz); assert(rc == KMCMP_ERROR_UNSUPPORTED_PROPERTY); } { // err test const auto bufsiz = 128; uint32_t buf[bufsiz]; - int rc = kmcmp_ParseUnicodeSet(u8"[[\\p{Mn}]&[A-Z]]", buf, bufsiz); + uintptr_t buf_ = reinterpret_cast(buf); + int rc = kmcmp_parseUnicodeSet(u8"[[\\p{Mn}]&[A-Z]]", buf_, bufsiz); assert(rc == KMCMP_ERROR_UNSUPPORTED_PROPERTY); } { // err test const auto bufsiz = 128; uint32_t buf[bufsiz]; - int rc = kmcmp_ParseUnicodeSet(u8"[abc{def}]", buf, bufsiz); + uintptr_t buf_ = reinterpret_cast(buf); + int rc = kmcmp_parseUnicodeSet(u8"[abc{def}]", buf_, bufsiz); assert(rc == KMCMP_ERROR_HAS_STRINGS); } { // err test const auto bufsiz = 128; uint32_t buf[bufsiz]; - int rc = kmcmp_ParseUnicodeSet(u8"[[]", buf, bufsiz); + uintptr_t buf_ = reinterpret_cast(buf); + int rc = kmcmp_parseUnicodeSet(u8"[[]", buf_, bufsiz); assert(rc == KMCMP_ERROR_SYNTAX_ERR); } } diff --git a/developer/src/kmcmplib/tests/util_callbacks.cpp b/developer/src/kmcmplib/tests/util_callbacks.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e610a54c09e8beb6974675d23deb0cc5199140d6 --- /dev/null +++ b/developer/src/kmcmplib/tests/util_callbacks.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include "util_filesystem.h" +#include "../src/compfile.h" +#include + +std::vector error_vec; + +int msgproc(int line, uint32_t dwMsgCode, const char* szText, void* context) { + error_vec.push_back(dwMsgCode); + const char*t = "unknown"; + switch(dwMsgCode & 0xF000) { + case CERR_HINT: t=" hint"; break; + case CERR_WARNING: t="warning"; break; + case CERR_ERROR: t=" error"; break; + case CERR_FATAL: t=" fatal"; break; + } + printf("line %d %s %04.4x: %s\n", line, t, (unsigned int)dwMsgCode, szText); + return 1; +} + +bool loadfileProc(const char* filename, const char* baseFilename, void* data, int* size, void* context) { + std::string resolvedFilename = filename; + if(baseFilename && *baseFilename && IsRelativePath(filename)) { + char* p; + if ((p = strrchr_slash((char*)baseFilename)) != nullptr) { + std::string basePath = std::string(baseFilename, (int)(p - baseFilename + 1)); + resolvedFilename = basePath; + resolvedFilename.append(filename); + } + } + + FILE* fp = Open_File(resolvedFilename.c_str(), "rb"); + if(!fp) { + return false; + } + + if(!data) { + // return size + if(fseek(fp, 0, SEEK_END) != 0) { + fclose(fp); + return false; + } + *size = ftell(fp); + if(*size == -1L) { + fclose(fp); + return false; + } + } else { + // return data + if(fread(data, 1, *size, fp) != *size) { + fclose(fp); + return false; + } + } + fclose(fp); + return true; +} \ No newline at end of file diff --git a/developer/src/kmcmplib/tests/util_callbacks.h b/developer/src/kmcmplib/tests/util_callbacks.h new file mode 100644 index 0000000000000000000000000000000000000000..345de64b9f484b568f28c1652e800ee20e7307ee --- /dev/null +++ b/developer/src/kmcmplib/tests/util_callbacks.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +int msgproc(int line, uint32_t dwMsgCode, const char* szText, void* context); +bool loadfileProc(const char* filename, const char* baseFilename, void* data, int* size, void* context); + +extern std::vector error_vec; \ No newline at end of file diff --git a/developer/src/kmcmplib/src/filesystem.cpp b/developer/src/kmcmplib/tests/util_filesystem.cpp similarity index 84% rename from developer/src/kmcmplib/src/filesystem.cpp rename to developer/src/kmcmplib/tests/util_filesystem.cpp index a343641acc128ed5537e0e1835d45a2253c29f2e..7f9edd6b367a303573e165767ca1805f13bfd558 100644 --- a/developer/src/kmcmplib/src/filesystem.cpp +++ b/developer/src/kmcmplib/tests/util_filesystem.cpp @@ -7,7 +7,7 @@ #include #include #include -#include "filesystem.h" +#include "util_filesystem.h" #ifdef _MSC_VER #include @@ -186,3 +186,42 @@ KMX_BOOL kmcmp_FileExists(const KMX_WCHAR* filename) { return FALSE; }; + + +bool IsRelativePath(KMX_CHAR const * p) { + // Relative path (returns TRUE): + // ..\...\BITMAP.BMP + // PATH\BITMAP.BMP + // BITMAP.BMP + + // Semi-absolute path (returns FALSE): + // \...\BITMAP.BMP + + // Absolute path (returns FALSE): + // C:\...\BITMAP.BMP + // \\SERVER\SHARE\...\BITMAP.BMP + + if ((*p == '\\') || (*p == '/')) return FALSE; + if (*p && *(p + 1) == ':') return FALSE; + + return TRUE; +} + +bool IsRelativePath(KMX_WCHAR const * p) { + // Relative path (returns TRUE): + // ..\...\BITMAP.BMP + // PATH\BITMAP.BMP + // BITMAP.BMP + + // Semi-absolute path (returns FALSE): + // \...\BITMAP.BMP + + // Absolute path (returns FALSE): + // C:\...\BITMAP.BMP + // \\SERVER\SHARE\...\BITMAP.BMP + + if ((*p == u'\\') || (*p == u'/'))return FALSE; + if (*p && *(p + 1) == u':') return FALSE; + + return TRUE; +} \ No newline at end of file diff --git a/developer/src/kmcmplib/src/filesystem.h b/developer/src/kmcmplib/tests/util_filesystem.h similarity index 82% rename from developer/src/kmcmplib/src/filesystem.h rename to developer/src/kmcmplib/tests/util_filesystem.h index 786b7268ac0c09bdda634296445e7e448404cc1e..4c94dae6bf4e4049d80efe8fa5d52d4b4fd7bb93 100644 --- a/developer/src/kmcmplib/src/filesystem.h +++ b/developer/src/kmcmplib/tests/util_filesystem.h @@ -1,7 +1,7 @@ #pragma once #include -#include "kmx_u16.h" +#include "../src/kmx_u16.h" // Opens files on windows and non-windows platforms. Datatypes for Filename and mode must be the same. // returns FILE* if file could be opened; FILE needs to be closed in calling function @@ -10,3 +10,6 @@ FILE* Open_File(const KMX_WCHART* Filename, const KMX_WCHART* mode); FILE* Open_File(const KMX_WCHAR* Filename, const KMX_WCHAR* mode); KMX_BOOL kmcmp_FileExists(const KMX_CHAR *filename); KMX_BOOL kmcmp_FileExists(const KMX_WCHAR *filename); + +bool IsRelativePath(KMX_CHAR const * p); +bool IsRelativePath(KMX_WCHAR const * p); diff --git a/developer/src/kmcomp/main.pas b/developer/src/kmcomp/main.pas index 5a29927a807bccd47dea671dbb6ce8d629a1f727..4aa29cf6c1ace54494285d0f9bd9c1f701d45040 100644 --- a/developer/src/kmcomp/main.pas +++ b/developer/src/kmcomp/main.pas @@ -65,7 +65,7 @@ uses UKeymanTargets; function CompileKeyboard(FInFile, FOutFile: string; FDebug, FWarnAsError: Boolean): Boolean; forward; // I4706 -function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion, FUseKmcmpLib: Boolean): Boolean; forward; +function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion: Boolean): Boolean; forward; //function CompilerMessage(line: Integer; msgcode: LongWord; text: PAnsiChar): Integer; stdcall; forward; procedure FixupPathSlashes(var path: string); forward; @@ -90,7 +90,6 @@ var FParamDistribution: Boolean; FMergingValidateIds: Boolean; FShouldAddCompilerVersion: Boolean; - FUseKmcmpLib: Boolean; FJsonSchemaPath: string; FParamSourcePath: string; FParamHelpLink: string; @@ -117,7 +116,6 @@ begin FColorMode := cmDefault; FShouldAddCompilerVersion := True; - FUseKmcmpLib := True; FParamInfile := ''; FParamOutfile := ''; @@ -193,8 +191,6 @@ begin FColorMode := cmForceNoColor else if s = '-no-compiler-version' then FShouldAddCompilerVersion := False - else if s = '-use-legacy-compiler' then - FUseKmcmpLib := False else if (s = '-help') or (s = '-h') then begin // Force help @@ -223,8 +219,6 @@ begin writeln(SKeymanDeveloperName + ' Compiler (32-bit)'); {$ENDIF} writeln('Version ' + CKeymanVersionInfo.VersionWithTag + ', ' + GetVersionCopyright); - if not FUseKmcmpLib then - writeln('Note: using legacy compiler'); end; if FError or (FParamInfile = '') then @@ -234,7 +228,7 @@ begin writeln(''); writeln('Usage: '+cmd+' [-s[s]] [-nologo] [-c] [-d] [-w] [-cfc] [-v[s|d]] [-source-path path] [-schema-path path] '); writeln(' '+spc+' [-m] infile [-m infile] [-t target] [outfile.kmx|outfile.js [error.log]]'); // I4699 - writeln(' '+spc+' [-add-help-link path] [-color|-no-color] [-no-compiler-version] [-use-legacy-compiler]'); + writeln(' '+spc+' [-add-help-link path] [-color|-no-color] [-no-compiler-version]'); writeln(' '+spc+' [-extract-keyboard-info field[,field...]]'); writeln(' infile can be a .kmn file (Keyboard Source, .kps file (Package Source), or .kpj (project)'); // I4699 // I4825 writeln(' if -v specified, can also be a .keyboard_info file'); @@ -258,7 +252,6 @@ begin writeln(' uses console mode to determine whether color should be used.'); writeln; writeln(' -no-compiler-version Don''t embed the compiler version stores, useful for regression tests.'); - writeln(' -use-legacy-compiler Use legacy compiler (will be removed in 18.0)'); writeln; writeln(' JSON .keyboard_info compile targets:'); writeln(' -v[s] validate infile against source schema'); @@ -298,7 +291,7 @@ begin TProjectLogConsole.Create(FSilent, FFullySilent, hOutfile, FColorMode); - KCSetCompilerOptions(FParamInfile, FShouldAddCompilerVersion, FUseKmcmpLib); + KCSetCompilerOptions(FParamInfile, FShouldAddCompilerVersion); if FValidateRepoChanges then FError := not TValidateRepoChanges.Execute(FParamInfile, FParamOutfile) @@ -324,7 +317,7 @@ begin ExitCode := 1; end; -function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion, FUseKmcmpLib: Boolean): Boolean; +function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion: Boolean): Boolean; var opt: TCompilerOptions; begin @@ -332,7 +325,6 @@ begin opt.dwSize := sizeof(TCompilerOptions); opt.ShouldAddCompilerVersion := FShouldAddCompilerVersion; - opt.UseKmcmpLib := FUseKmcmpLib; Result := SetCompilerOptions(@opt, @CompilerMessageW); diff --git a/developer/src/test/auto/Makefile b/developer/src/test/auto/Makefile index 635d933bd104e408c2ffe9ab6ee2b786009cecc8..ebbdf290160a8222ffcdda4a7c821c28bacc7d4b 100644 --- a/developer/src/test/auto/Makefile +++ b/developer/src/test/auto/Makefile @@ -26,7 +26,7 @@ developer-tests: \ model-ts-parser \ package-info \ kmc \ - kmc-keyboard \ + kmc-ldml \ kmc-model \ kmc-model-info \ kmc-package @@ -87,8 +87,8 @@ kmc: .virtual cd $(DEVELOPER_ROOT)\src\kmc $(MAKE) $(TARGET) -kmc-keyboard: .virtual - cd $(DEVELOPER_ROOT)\src\kmc-keyboard +kmc-ldml: .virtual + cd $(DEVELOPER_ROOT)\src\kmc-ldml $(MAKE) $(TARGET) kmc-model: .virtual diff --git a/developer/src/tike/compile/keyman32_int.pas b/developer/src/tike/compile/keyman32_int.pas deleted file mode 100644 index 37ddbe93475c8994379563db135fcb7371618ba7..0000000000000000000000000000000000000000 --- a/developer/src/tike/compile/keyman32_int.pas +++ /dev/null @@ -1,108 +0,0 @@ -unit keyman32_int; - -interface - -uses Windows, SysUtils, Forms; - -function Keyman_Initialise(Handle: HWND; FSingleApp: Boolean): Boolean; -function Keyman_Exit: Boolean; -function Keyman_ForceKeyboard(const s: string): Boolean; -function Keyman_StopForcingKeyboard: Boolean; - -implementation - -uses TikeUtils; - -type TKeyman_ForceKeyboard = function (s: PChar): Boolean; stdcall; -type TKeyman_StopForcingKeyboard = function: Boolean; stdcall; -type TKeyman_Initialise = function(h: THandle; FSingleApp: LongBool): Boolean; stdcall; -type TKeyman_Exit = function: Boolean; stdcall; - -var - FInitKeyman: Boolean = False; - FKeyman32Path: string = ''; - -function Keyman_Initialise(Handle: HWND; FSingleApp: Boolean): Boolean; -var - hkeyman: THandle; - FLoad: Boolean; - ki: TKeyman_Initialise; -begin - Result := False; - FLoad := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then - begin - hkeyman := LoadLibrary(PChar(GetKeymanInstallPath+'keyman32.dll')); - if hkeyman = 0 then Exit; - FLoad := True; - end; - ki := TKeyman_Initialise(GetProcAddress(hkeyman, 'Keyman_Initialise')); - if not Assigned(@ki) then Exit; - if not ki(Handle, FSingleApp) then - begin - if FLoad then FreeLibrary(hkeyman); - Exit; - end; - - FInitKeyman := True; - Result := True; -end; - -function Keyman_Exit: Boolean; -var - hkeyman: THandle; - ke: TKeyman_Exit; -begin - if not FInitKeyman then - begin - Result := True; - Exit; - end; - Result := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then Exit; - ke := TKeyman_Exit(GetProcAddress(hkeyman, 'Keyman_Exit')); - if not Assigned(@ke) then Exit; - if not ke then Exit; - if FInitKeyman then FreeLibrary(hkeyman); - FInitKeyman := False; - Result := True; -end; - -function Keyman_ForceKeyboard(const s: string): Boolean; -var - hkeyman: THandle; - fk: TKeyman_ForceKeyboard; -begin - Result := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then - begin - if not Keyman_Initialise(Application.MainForm.Handle, True) then Exit; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then Exit; - end; - fk := TKeyman_ForceKeyboard(GetProcAddress(hkeyman, 'Keyman_ForceKeyboard')); - if(Assigned(@fk)) then - Result := fk(PChar(s)); -end; - -function Keyman_StopForcingKeyboard: Boolean; -var - hkeyman: THandle; - sfk: TKeyman_StopForcingKeyboard; -begin - Result := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then Exit; - sfk := TKeyman_StopForcingKeyboard(GetProcAddress(hkeyman, 'Keyman_StopForcingKeyboard')); - if(Assigned(@sfk)) then - Result := sfk; - Keyman_Exit; -end; - -initialization -finalization - Keyman_Exit; -end. diff --git a/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas b/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas index 703a1e4a0b6edd891950b647e5240c2e1250f02b..2b6dea94ec580d34adc0c54f471f021683ec3b44 100644 --- a/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas +++ b/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas @@ -115,7 +115,7 @@ begin TProject.CompilerMessageFile := Self; options.dwSize := sizeof(COMPILER_OPTIONS); options.ShouldAddCompilerVersion := addVersion; - options.UseKmcmpLib := not useLegacyCompiler; + // TODO: useLegacyCompiler means we switch to kmcmpdll vs kmc if not SetCompilerOptions(@options, ProjectCompilerMessage) then begin Log(plsFatal, 'Unable to set compiler options', CERR_FATAL, 0); diff --git a/docs/build/linux-ubuntu.md b/docs/build/linux-ubuntu.md index 36583d4576d991e7d5c044732ed5e4659f0af6ae..5b576a6ce89f397ca342dda79e58a42ddf81cbea 100644 --- a/docs/build/linux-ubuntu.md +++ b/docs/build/linux-ubuntu.md @@ -100,25 +100,33 @@ docker run -it --rm -v $(pwd):/home/build/src/keyman -u root -w /home/build/src/ ## Keyman for Android -**Dependencies**: +**Dependencies:** + * [Base](#base-dependencies) * [Web](./windows#web-dependencies) -**Additional requirements**: +**Additional requirements:** + * Android SDK * [Android Studio](https://developer.android.com/studio/install#linux) * Gradle * Maven * OpenJDK 11 (for Keyman 17.0+) - +* pandoc Run Android Studio once after installation to install additional components such as emulator images and SDK updates. -**Required environment variables**: +**Required environment variable:** + +* `ANDROID_HOME` pointing to Android SDK (`$HOME/Android/Sdk`) + +**Recommended environment variable:** + * [`JAVA_HOME`](#java_home) Building: + * [Building Keyman for Android](../../android/README.md) ## Prerequisites @@ -127,20 +135,32 @@ Many dependencies are only required for specific projects. ### Base Dependencies -**Environment variables**: +**Environment variables:** +* -- ## Notes on Environment Variables ### JAVA_HOME -This environment variable tells Gradle what version of Java to use for building Keyman for Android. -OpenJDK 11 is used for master. It's recommended to set the environment variables to: +This environment variable tells Gradle what version of Java to use for building +Keyman for Android. OpenJDK 11 is used for master. + +It's recommended to set the environment variables to: + ```bash -EXPORT JAVA_HOME="[path to OpenJDK 11]" +export JAVA_HOME="[path to OpenJDK 11]" ``` Also edit `/etc/profile.d/jvm.sh` as sudo: + ```bash -EXPORT JAVA_HOME="[path to OpenJDK 11]" +export JAVA_HOME="[path to OpenJDK 11]" ``` + +**Multiple versions of Java:** If you need to build Keyman for Android 16.0 or +older versions, you can set `JAVA_HOME_11` to the OpenJDK 11 path and +`JAVA_HOME` to the OpenJDK 8 path. This will build both versions correctly +from command line. But note that you do need to update your `JAVA_HOME` env +var to the associated version before opening Android Studio and loading any +Android projects. `JAVA_HOME_11` is mostly used by CI. diff --git a/docs/linux/keyman-config.md b/docs/linux/keyman-config.md index f050fd96ad0896b2bf9a83d064884d49dc064045..cfea5d14370a0b0b660c770adb7e1df6d045920c 100644 --- a/docs/linux/keyman-config.md +++ b/docs/linux/keyman-config.md @@ -35,10 +35,7 @@ or install it with pip: pip3 install sentry-sdk ``` -Run the script `./createkeymandirs.sh` to create the directories for these -programs to install the packages to. - -Also copy and compile the GSettings schema: +Copy and compile the GSettings schema: ```bash cd keyman_config @@ -50,15 +47,16 @@ sudo glib-compile-schemas /usr/share/glib-2.0/schemas Running `km-config` requires a language tag mapping file `keyman_config/standards/lang_tags_map.py`. This file gets generated during a package -build, and also when running `make`. +build, and also when running `build.sh build`. ## Installing manually from the repo -`make && sudo make install` will install locally to `/usr/local`. +`linux/keyman-config/build.sh build && sudo linux/keyman-config/build.sh install` +will install locally to `/usr/local`. `pip3 help install` will give you more install options. -To uninstall you can run `sudo make uninstall`. +To uninstall you can run `sudo linux/keyman-config/build.sh uninstall`. ## Things to run from the command line diff --git a/docs/linux/packaging.md b/docs/linux/packaging.md index 91154934b44be5059f04bd4dca47717ea832a7bb..59f16880eb2ef028efe0620a71b1d47c5bd9a51d 100644 --- a/docs/linux/packaging.md +++ b/docs/linux/packaging.md @@ -239,7 +239,7 @@ You'll have to create the docker image. ```bash cd $KEYMAN_ROOT docker run -v $(pwd):/source -i -t -w /source --platform=linux/amd64 \ - sillsdev/jammy keyman_*.dsc /source + sillsdev/jammy jammy keyman_*.dsc ``` This will create the binary packages in `$KEYMAN_ROOT/artifacts`. diff --git a/docs/npm-packages.md b/docs/npm-packages.md index 57fbfb839f8ff1b43bc707f025502a863b04ddc0..040a6e11ccf48b052ec0cd51b925666664b53de8 100644 --- a/docs/npm-packages.md +++ b/docs/npm-packages.md @@ -13,8 +13,8 @@ Packages are **never** published from a developer's machine. * `@keymanapp/common-types` -- located at `common/web/types` * `@keymanapp/keyman-version` -- located at `common/web/keyman-version` * `@keymanapp/kmc` -- located at `developer/src/kmc` -* `@keymanapp/kmc-keyboard` -- located at `developer/src/kmc-keyboard` * `@keymanapp/kmc-kmn` -- located at `developer/src/kmc-kmn` +* `@keymanapp/kmc-ldml` -- located at `developer/src/kmc-ldml` * `@keymanapp/kmc-model` -- located at `developer/src/kmc-model` * `@keymanapp/kmc-model-info` -- located at `developer/src/kmc-model-info` * `@keymanapp/kmc-package` -- located at `developer/src/kmc-package` diff --git a/docs/settings/linux/settings.json b/docs/settings/linux/settings.json index be15b13027d8b8a133b7c310d4ab9ad1160cf6d6..891507dd2ecfc3b11a2ad1eae765934819ac0506 100644 --- a/docs/settings/linux/settings.json +++ b/docs/settings/linux/settings.json @@ -107,4 +107,5 @@ "--external-sources --source-path=resources/build/" ], "C_Cpp.errorSquiggles": "EnabledIfIncludesResolve", + "shellcheck.useWorkspaceRootAsCwd": false } diff --git a/docs/settings/linux/tasks.json b/docs/settings/linux/tasks.json index 77df789fe318c41f7099a86255d6476ce05c3658..9728cd709e4f582510b38aca95e5b78a299d9ac9 100644 --- a/docs/settings/linux/tasks.json +++ b/docs/settings/linux/tasks.json @@ -158,5 +158,100 @@ "group": "test", "detail": "unit test keyman-config" }, + { + "type": "shell", + "label": "keyman-system-service: clean", + "command": "${workspaceFolder}/linux/keyman-system-service/build.sh", + "args": [ + "--debug", + "clean" + ], + "options": { + "cwd": "${workspaceFolder}/linux/keyman-system-service" + }, + "group": "build", + "detail": "clean keyman-system-service" + }, + { + "type": "shell", + "label": "keyman-system-service: build", + "command": "${workspaceFolder}/linux/keyman-system-service/build.sh", + "args": [ + "--debug", + "build" + ], + "options": { + "cwd": "${workspaceFolder}/linux/keyman-system-service" + }, + "problemMatcher": [ + { + "owner": "cpp", + "source": "compiler", + "fileLocation": [ + "relative", + "${workspaceFolder}/linux/keyman-system-service/" + ], + "pattern": [ + { + "regexp": "^../../../(.*):(\\d+):(\\d+):\\s+(warning|error|note):\\s+(.*)(\\s+\\[.*\\])?$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] + }, + { + "owner": "cpp", + "source": "linker", + "fileLocation": "absolute", + "pattern": [ + { + "regexp": "^(/usr/bin/ld:\\s+)?(.*):(\\d+):\\s+(.*)$", + "file": 2, + "location": 3, + "message": 4 + } + ] + } + ], + "presentation": { + "clear": true + }, + "group": "build", + "detail": "build keyman-system-service" + }, + { + "type": "shell", + "label": "keyman-system-service: configure", + "command": "${workspaceFolder}/linux/keyman-system-service/build.sh", + "args": [ + "--debug", + "configure" + ], + "options": { + "cwd": "${workspaceFolder}/linux/keyman-system-service", + }, + "presentation": { + "clear": true + }, + "group": "build", + "detail": "configure keyman-system-service" + }, + { + "type": "shell", + "label": "keyman-system-service: test", + "command": "${workspaceFolder}/linux/keyman-system-service/build.sh", + "args": [ + "--debug", + "test:arch" + ], + "problemMatcher": [ + "$gcc" + ], + "group": "test", + "detail": "run tests for keyman-system-service" + }, ] } diff --git a/ios/Cartfile b/ios/Cartfile index 5ad8178ed48a4e7ad28589d41fe9ed382617a5c9..b265ef77af28f9f7d45032ba8f753477057e456f 100644 --- a/ios/Cartfile +++ b/ios/Cartfile @@ -1,5 +1,5 @@ github "marmelroy/Zip" -github "DaveWoodCom/XCGLogger" ~> 6.1.0 +github "keymanapp/dependency-XCGLogger" "master" github "devicekit/DeviceKit" ~> 5.0 github "ashleymills/Reachability.swift" -github "getsentry/sentry-cocoa" ~> 6.2.1 +github "getsentry/sentry-cocoa" ~> 8.7.0 diff --git a/ios/Cartfile.resolved b/ios/Cartfile.resolved index fe74a57322bc07d42adfbf6539596e6ad6035902..ff30a8871ddc681497e00c68287eb34808d94541 100644 --- a/ios/Cartfile.resolved +++ b/ios/Cartfile.resolved @@ -1,5 +1,5 @@ -github "DaveWoodCom/XCGLogger" "6.1.0" github "ashleymills/Reachability.swift" "v5.1.0" github "devicekit/DeviceKit" "5.0.0" -github "getsentry/sentry-cocoa" "6.2.1" +github "getsentry/sentry-cocoa" "8.7.0" +github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113" github "marmelroy/Zip" "2.1.2" diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift index 127fcd12b5bd2b4fb8d900586073e0ab94971bcc..94f988507606db6fe75025f89cc91045ecc8ffd0 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift @@ -167,7 +167,7 @@ public class SentryManager { public static func breadcrumbAndLog(crumb: Sentry.Breadcrumb, logLevel: XCGLogger.Level? = nil) { // Guarded in case a library consumer decides against initializing Sentry. if _started { - SentrySDK.addBreadcrumb(crumb: crumb) + SentrySDK.addBreadcrumb(crumb) } let level = logLevel ?? mapLoggingLevel(crumb.level) @@ -193,7 +193,7 @@ public class SentryManager { } public static func forceError() { - SentrySDK.addBreadcrumb(crumb: Sentry.Breadcrumb(level: .info, category: "Deliberate testing error")) + SentrySDK.addBreadcrumb(Sentry.Breadcrumb(level: .info, category: "Deliberate testing error")) SentrySDK.crash() } } diff --git a/ios/tools/prepRelease.sh b/ios/tools/prepRelease.sh index 1f22de45f096ec4a11cd41dc7fa14de36b423f7d..39b46722f79c973bee51d128192bfac1cc647c6f 100755 --- a/ios/tools/prepRelease.sh +++ b/ios/tools/prepRelease.sh @@ -70,17 +70,17 @@ KEYMAN_SAMPLES="samples" echo "engine dest: $KMEI_DST" -echo "Zipping ${UNI_FRAMEWORK} => ${UPLOAD_DIR}/${KMEI_DST}..." +echo "Zipping ${FRAMEWORK} => ${UPLOAD_DIR}/${KMEI_DST}..." cd "${KMEI_FRAMEWORK_BASE}" -zip -qrX ${KMEI_DST} ${FRAMEWORK} -cd $WORK_DIR +zip -qrX "${KMEI_DST}" ${FRAMEWORK} +cd "$WORK_DIR" echo "Copying Keyman Engine samples into ${UPLOAD_DIR}/${KMEI_DST_NAME}..." cp -rf "${KEYMAN_SAMPLES}" "${UPLOAD_DIR}/samples" cd "${UPLOAD_DIR}" zip -qr "${KMEI_DST_NAME}" "samples" rm -rf "samples" -cd $WORK_DIR +cd "$WORK_DIR" # # Keyman app diff --git a/linux/.gitignore b/linux/.gitignore index 478f418b608e52d3898a613528475097a4d10e4d..0abbd99c585433153b15c9eca0fd5e604ebb33d2 100644 --- a/linux/.gitignore +++ b/linux/.gitignore @@ -17,7 +17,7 @@ dist/ upload/ launchpad launchpad-* -keyman-config/build +build/ keyman-config/debian/man keyman-config/experiments/*.txt keyman-config/keyman_config/version.py diff --git a/linux/Jenkinsfile b/linux/Jenkinsfile index 15f5b27777d79d4442b63efac7d1d1650aa606f4..b70cb95d706cfc3c24ccf6e90a242f407d74ccae 100644 --- a/linux/Jenkinsfile +++ b/linux/Jenkinsfile @@ -5,7 +5,7 @@ @Library('lsdev-pipeline-library') _ keymanPackaging { - distributionsToPackage = 'focal jammy kinetic' + distributionsToPackage = 'focal jammy kinetic lunar' arches = 'amd64 i386' packagesToBuild = ['keyman'] } diff --git a/linux/build.sh b/linux/build.sh index f0aaff70d57fd0652c942c611a864975d1f87662..6c7a92069b3b11bd1f1032e88e6db28fd03b5317 100755 --- a/linux/build.sh +++ b/linux/build.sh @@ -14,7 +14,9 @@ cd "$THIS_SCRIPT_PATH" builder_describe \ "Build Keyman for Linux." \ - ":engine=ibus-keyman ibus-keyman" \ + ":config=keyman-config keyman-config" \ + ":engine=ibus-keyman ibus-keyman" \ + ":service=keyman-system-service keyman-system-service" \ "clean" \ "configure" \ "build" \ diff --git a/linux/debian/com.keyman.config.appdata.xml b/linux/debian/com.keyman.config.appdata.xml index f137be461df2923d2b9d3d197e7fd306d808e46e..9df49a37dc3a0500bbd40533c2ef608e4c3da5f0 100644 --- a/linux/debian/com.keyman.config.appdata.xml +++ b/linux/debian/com.keyman.config.appdata.xml @@ -14,8 +14,8 @@

Keyman makes it possible for you to type in over 2,000 languages on Windows, macOS, Linux, iPhone, iPad, Android tablets and phones, and even instantly in your web browser. With the -world’s most powerful keyboarding engine, intuitive and rapid text input is now possible in -your language, and for over 99% of the global population’s mother tongues! +world's most powerful keyboarding engine, intuitive and rapid text input is now possible in +your language, and for over 99% of the global population's mother tongues!

Originally created in 1993 to type Lao on Windows, Keyman is now a free and @@ -40,7 +40,7 @@ and mobile platforms. The Keyman for Linux configuration window with a keyboard welcome window. - https://keyman.com/go/linux/11.0/linux-configuration.png + https://keyman.com/go/linux/17.0/linux-configuration.png diff --git a/linux/debian/com.keyman.ibus_keyman.metainfo.xml b/linux/debian/com.keyman.ibus_keyman.metainfo.xml index eb02001da0d2a355fc4fe4dcbc5f2b97bbc54ec0..1f021d2fce346c76b90b38512af83d3cfc737b0e 100644 --- a/linux/debian/com.keyman.ibus_keyman.metainfo.xml +++ b/linux/debian/com.keyman.ibus_keyman.metainfo.xml @@ -1,39 +1,39 @@ -​ com.keyman.ibus_keyman -​ MIT + com.keyman.ibus_keyman + MIT GPL-2+ Keyman SIL International https://keyman.com/cdn/dev/img/keyman-logo.png -​ ibus-keyman -​

Keyman input method for IBus -​ -​

- This input method provides the Keyman IM engine for IBus. With this module, you can - use keyboard layouts designed for Keyman under the IBus platform. -

-

- Originally created in 1993 to type Lao on Windows, Keyman is now a free and - open source keyboarding platform which allows anyone to write a keyboard layout - for their language. Keyman is available for many platforms, including Windows, - macOS, iOS, Android, Linux and the web. -

+ ibus-keyman + Keyman input method for IBus +

- Keyboard layouts are defined with a clear and easy to understand keyboard - grammar. Keyman's contextual input model means keyboard layouts can be - intelligent and make it simple to type even the most complex languages. - Keyboard layouts are distributed through an open catalog to all major desktop - and mobile platforms. + This input method provides the Keyman IM engine for IBus. With this module, you can + use keyboard layouts designed for Keyman under the IBus platform. +

+

+ Originally created in 1993 to type Lao on Windows, Keyman is now a free and + open source keyboarding platform which allows anyone to write a keyboard layout + for their language. Keyman is available for many platforms, including Windows, + macOS, iOS, Android, Linux and the web. +

+

+ Keyboard layouts are defined with a clear and easy to understand keyboard + grammar. Keyman's contextual input model means keyboard layouts can be + intelligent and make it simple to type even the most complex languages. + Keyboard layouts are distributed through an open catalog to all major desktop + and mobile platforms. +

+

+ Input methods are typing systems allowing users to input complex languages.

-

-​ Input methods are typing systems allowing users to input complex languages. -​

-​
+
https://keyman.com/linux/ https://github.com/keymanapp/keyman/issues https://help.keyman.com/ https://community.software.sil.org/c/keyman https://donate.keyman.com/ -​ + diff --git a/linux/debian/control b/linux/debian/control index 40b2065d7f7fb9292ce55170977ec5e758471b59..624f89fc0adc2e8a0406377363f5c70d14a5531b 100644 --- a/linux/debian/control +++ b/linux/debian/control @@ -28,10 +28,12 @@ Build-Depends: python3-setuptools, meson (>= 0.53), ninja-build, + libevdev-dev, libgtk-3-dev, libicu-dev, libibus-1.0-dev (>= 1.2), libjson-glib-dev (>= 1.4.0), + libsystemd-dev, pkg-config, xserver-xephyr, xvfb, @@ -56,9 +58,9 @@ Recommends: Description: Type in your language with Keyman for Linux Keyman makes it possible for you to type in over 2,000 languages on Windows, macOS, Linux, iPhone, iPad, Android tablets and phones, and even instantly - in your web browser. With the world’s most powerful keyboarding engine, + in your web browser. With the world's most powerful keyboarding engine, intuitive and rapid text input is now possible in your language, and for - over 99% of the global population’s mother tongues! + over 99% of the global population's mother tongues! . Originally created in 1993 to type Lao on Windows, Keyman is now a free and open source keyboarding platform which allows anyone to write a keyboard @@ -160,6 +162,7 @@ Architecture: amd64 arm64 armel armhf i386 mipsel mips64el ppc64el riscv64 Depends: ibus (>= 1.3.7), libkmnkbp0-0 (= ${binary:Version}), + keyman-system-service (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends}, Provides: @@ -181,3 +184,24 @@ Description: Keyman engine for IBus . This package provides the Keyman IM engine for IBus. With this module, you can use keyboard layouts designed for Keyman under the IBus platform. + +Package: keyman-system-service +Architecture: any +Depends: + libevdev2, + ${misc:Depends}, + ${shlibs:Depends}, +Description: System service for Keyman + Originally created in 1993 to type Lao on Windows, Keyman is now a free and + open source keyboarding platform which allows anyone to write a keyboard + layout for their language. Keyman is available for many platforms, including + Windows, macOS, iOS, Android, Linux and the web. + . + Keyboard layouts are defined with a clear and easy to understand keyboard + grammar. Keyman's contextual input model means keyboard layouts can be + intelligent and make it simple to type even the most complex languages. + Keyboard layouts are distributed through an open catalog to all major desktop + and mobile platforms. + . + This package provides a DBus/systemd system service as a helper for + running Keyman with Wayland. diff --git a/linux/debian/ibus-keyman.postinst b/linux/debian/ibus-keyman.postinst index 4143effb61526534dedaf9110b5376235bc60d26..6cff5c525c0b8ae1c5c0d4c064cf9aa8210c9cd2 100644 --- a/linux/debian/ibus-keyman.postinst +++ b/linux/debian/ibus-keyman.postinst @@ -1,7 +1,7 @@ -#!/bin/sh +#!/bin/bash -# Don't call `set -e`. Even if some commands should fail, it's still -# worth running the rest of the commands. +# Exit on errors - Debian policy 10.4 +set -e case "$1" in @@ -12,7 +12,7 @@ case "$1" in if which sudo > /dev/null && which ps > /dev/null; then # check for gnome-shell as it works differently - gspid=$(ps -C gnome-shell -o pid=|head -n 1) + ! gspid=$(ps -C gnome-shell -o pid=|head -n 1) if [ "$gspid" != "" ]; then # gnome-shell has multiple ibus-daemon processes and needs exit instead of restart is_gnome_shell=1 @@ -21,7 +21,7 @@ case "$1" in fi # Restart IBus if it is running - ibuspid=$(ps -C ibus-daemon -o pid=|head -n 1) + ! ibuspid=$(ps -C ibus-daemon -o pid=|head -n 1) if [ "$ibuspid" != "" ]; then if [ "$is_gnome_shell" = "1" ]; then @@ -41,7 +41,7 @@ case "$1" in # Verify that it's running now if [ -n "$SUDO_USER" ] && id "$SUDO_USER" > /dev/null 2>/dev/null; then - ibusdaemon=$(ps --user "$SUDO_USER" -o s= -o cmd | grep --regexp="^[^ZT] \(/usr/bin/\)\?ibus-daemon .*--xim.*") + ! ibusdaemon=$(ps --user "$SUDO_USER" -o s= -o cmd | grep --regexp="^[^ZT] \(/usr/bin/\)\?ibus-daemon .*--xim.*") if [ "$ibusdaemon" = "" ]; then # otherwise try to start it for the user installing the package if [ "$is_gnome_shell" = "1" ]; then diff --git a/linux/debian/keyman-system-service.install b/linux/debian/keyman-system-service.install new file mode 100644 index 0000000000000000000000000000000000000000..f7ce93bd82b3a8f77baee2d76a13124ba317500f --- /dev/null +++ b/linux/debian/keyman-system-service.install @@ -0,0 +1,5 @@ +usr/lib/systemd/system/systemd-keyman.service +usr/libexec/keyman-system-service +usr/share/dbus-1/interfaces/com.keyman.SystemService1.System.xml +usr/share/dbus-1/system-services/com.keyman.SystemService1.service +usr/share/dbus-1/system.d/com.keyman.SystemService1.conf diff --git a/linux/debian/keyman-system-service.manpages b/linux/debian/keyman-system-service.manpages new file mode 100644 index 0000000000000000000000000000000000000000..cb4d9f0500ee07505ba9e3e6271904dda002f802 --- /dev/null +++ b/linux/debian/keyman-system-service.manpages @@ -0,0 +1 @@ +linux/keyman-system-service/man/keyman-system-service.1 diff --git a/linux/debian/keyman.install b/linux/debian/keyman.install index 8096e324aec4be58e43a6b10dc081bca41c79ad1..a036eb8a7361535fe2db5b3b2e7f1065c50656c2 100644 --- a/linux/debian/keyman.install +++ b/linux/debian/keyman.install @@ -1,4 +1,3 @@ -linux/keyman-config/com.keyman.gschema.xml usr/share/glib-2.0/schemas/ linux/keyman-config/icons/128/km-config.png usr/share/icons/hicolor/128x128/apps/ linux/keyman-config/icons/16/application-x-kmp.png usr/share/icons/hicolor/16x16/mimetypes/ linux/keyman-config/icons/24/application-x-kmp.png usr/share/icons/hicolor/24x24/mimetypes/ @@ -10,5 +9,6 @@ linux/keyman-config/icons/48/km-config.png usr/share/icons/hicolor/48x48/apps/ linux/keyman-config/icons/64/application-x-kmp.png usr/share/icons/hicolor/64x64/mimetypes/ linux/keyman-config/icons/64/km-config.png usr/share/icons/hicolor/64x64/apps/ linux/keyman-config/keyman_config/icons/* usr/share/keyman/icons/ -linux/keyman-config/km-config.desktop usr/share/applications +linux/keyman-config/resources/com.keyman.gschema.xml usr/share/glib-2.0/schemas/ +linux/keyman-config/resources/km-config.desktop usr/share/applications debian/com.keyman.config.appdata.xml usr/share/metainfo diff --git a/linux/debian/rules b/linux/debian/rules index e67ea1b5af5d2067f7b93a324564b00ee7971476..ec9178298cac3a121e8f353e8c8337d6b0ec5c2c 100755 --- a/linux/debian/rules +++ b/linux/debian/rules @@ -9,6 +9,10 @@ export PYBUILD_INSTALL_ARGS=--install-scripts=/usr/share/keyman-config/ export KEYMAN_PKG_BUILD=1 +export HOME=$(shell mktemp -d) +export XDG_DATA_HOME=$(shell mktemp -d) +export XDG_CONFIG_HOME=$(shell mktemp -d) + # xenial needs this to be explicit export LC_ALL=C.UTF-8 @@ -22,57 +26,48 @@ include /usr/share/dpkg/pkg-info.mk override_dh_auto_configure: # keymankeyboardprocessor - we need to configure and build core before # we can configure ibus-keyman! - cd core && \ - ./build.sh --no-tests configure:arch build:arch -- --wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc \ - --localstatedir=/var --libdir=lib/$(DEB_TARGET_MULTIARCH) \ - --libexecdir=lib/$(DEB_TARGET_MULTIARCH) - # ibus-keyman - cd linux/ibus-keyman && \ - ./build.sh configure -- --wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc \ - --localstatedir=/var - # keyman-config + core/build.sh --no-tests configure:arch build:arch -- \ + --wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc \ + --localstatedir=/var --libdir=lib/$(DEB_TARGET_MULTIARCH) \ + --libexecdir=lib/$(DEB_TARGET_MULTIARCH) + linux/ibus-keyman/build.sh configure -- \ + --wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc --localstatedir=/var + linux/keyman-system-service/build.sh configure -- \ + --wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc --localstatedir=/var + linux/keyman-config/build.sh configure override_dh_auto_build: - # keymankeyboardprocessor - cd core && \ - ./build.sh --no-tests build:arch - # ibus-keyman - cd linux/ibus-keyman && \ - ./build.sh build - # keyman-config + cp linux/keyman-config/resources/keyman.sharedmimeinfo debian/ + core/build.sh --no-tests build:arch + linux/ibus-keyman/build.sh build + linux/keyman-system-service/build.sh build + linux/keyman-config/build.sh build cd linux/keyman-config && \ - make man && \ sed -i -e "s/^__pkgversion__ = \"[^\"]*\"/__pkgversion__ = \"$(DEB_VERSION)\"/g" keyman_config/version.py && \ make compile-po - dh_auto_build --sourcedir=linux/keyman-config --buildsystem=pybuild $@ override_dh_auto_test: ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) - # keymankeyboardprocessor - cd core && \ - ./build.sh --no-tests test:arch - # ibus-keyman - cd linux/ibus-keyman && \ - ./build.sh test - # keyman-config - dh_auto_test --sourcedir=linux/keyman-config --buildsystem=pybuild $@ + core/build.sh --no-tests test:arch + linux/ibus-keyman/build.sh test + linux/keyman-system-service/build.sh test + linux/keyman-config/build.sh test endif override_dh_auto_install: install -d $(CURDIR)/debian/tmp - cd core && \ - DESTDIR=$(CURDIR)/debian/tmp ./build.sh --no-tests install:arch - # ibus-keyman - cd linux/ibus-keyman && \ - DESTDIR=$(CURDIR)/debian/tmp ./build.sh install + DESTDIR=$(CURDIR)/debian/tmp core/build.sh --no-tests install:arch + DESTDIR=$(CURDIR)/debian/tmp linux/ibus-keyman/build.sh install + DESTDIR=$(CURDIR)/debian/tmp linux/keyman-system-service/build.sh install # keyman-config install -d $(CURDIR)/debian/keyman/usr/share/ cp -r linux/keyman-config/locale/ $(CURDIR)/debian/keyman/usr/share/ rm $(CURDIR)/debian/keyman/usr/share/locale/*.po* + # Don't call `build.sh install` - dh_auto_install does some extra smarts dh_auto_install --sourcedir=linux/keyman-config --buildsystem=pybuild $@ # Unfortunately bash-completion 2.10 (focal) doesn't yet provide dh-sequence-bash-completion, # which we could add as build-dependency, so we'll have to explicitly call - # dh_bash_completion here... + # dh_bash_completion here instead of using `dh $@ --with-python3 --with bash-completion` dh_bash-completion -O--buildsystem=pybuild dh_python3 -O--buildsystem=pybuild diff --git a/linux/help/reference/index.md b/linux/help/reference/index.md index 1089382c55113e46bb4d2a9b44af18f940653220..7b8f704018f0f0b1b7090300f678e63592fa5f5c 100644 --- a/linux/help/reference/index.md +++ b/linux/help/reference/index.md @@ -1,8 +1,8 @@ --- -title: Keyman 16.0 for Linux Reference +title: Keyman 17.0 for Linux Reference --- -## Keyman 16.0 for Linux Overview +## Keyman 17.0 for Linux Overview Keyman for Linux makes it possible for you to type in over 2,000 languages on Linux. diff --git a/linux/ibus-keyman/build.sh b/linux/ibus-keyman/build.sh index a73a720924d9fc0312b30fac491d050078d9cca2..8a16bbb1deafe1f08024fd91c3174f5429d35068 100755 --- a/linux/ibus-keyman/build.sh +++ b/linux/ibus-keyman/build.sh @@ -37,8 +37,7 @@ cd "$THIS_SCRIPT_PATH" builder_describe_outputs \ configure "${MESON_PATH}/build.ninja" \ - build "${MESON_PATH}/src/ibus-engine-keyman" \ - build "${MESON_PATH}/tests/ibus-keyman-tests" + build "${MESON_PATH}/src/ibus-engine-keyman" if builder_start_action clean; then rm -rf "$THIS_SCRIPT_PATH/../build/" diff --git a/linux/ibus-keyman/meson.build b/linux/ibus-keyman/meson.build index e8548432d4234201853fe8bcfcce2079ddfe35fd..e4b31df7dbd59d02c6c790d8b9285d8cb42daf7b 100644 --- a/linux/ibus-keyman/meson.build +++ b/linux/ibus-keyman/meson.build @@ -9,16 +9,16 @@ project('ibus-keyman', 'c', 'cpp', cc = meson.get_compiler('c') conf = configuration_data() -ibus = dependency('ibus-1.0', version: '>= 1.2.0') -gtk = dependency('gtk+-3.0', version: '>= 2.4') -x11 = dependency('x11', version: '>= 1.6') -json_glib = dependency('json-glib-1.0', version: '>= 1.0') +ibus = dependency('ibus-1.0', version: '>= 1.2.0') +gtk = dependency('gtk+-3.0', version: '>= 2.4') +json_glib = dependency('json-glib-1.0', version: '>= 1.0') +systemd = dependency('libsystemd') core_dir = meson.current_source_dir() / '../../core' common_dir = meson.current_source_dir() / '../../common' kmnkbp_lib = cc.find_library( - 'libkmnkbp0', + 'kmnkbp0', # meson will prefix 'lib' dirs: [ core_dir / 'build/arch' / get_option('buildtype') / 'src' ] ) diff --git a/linux/ibus-keyman/src/KeymanSystemServiceClient.cpp b/linux/ibus-keyman/src/KeymanSystemServiceClient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90f459d1a21e853e0f5509756822c4292e2fd299 --- /dev/null +++ b/linux/ibus-keyman/src/KeymanSystemServiceClient.cpp @@ -0,0 +1,106 @@ +#include +#include +#include "KeymanSystemServiceClient.h" + +#define KEYMAN_BUS_NAME "com.keyman.SystemService1" +#define KEYMAN_INTERFACE_NAME "com.keyman.SystemService1.System" +#define KEYMAN_OBJECT_PATH "/com/keyman/SystemService1/System" + +extern gboolean testing; + +class KeymanSystemServiceClient { +private: + sd_bus_error *error = NULL; + sd_bus_message *msg = NULL; + sd_bus *bus = NULL; + +public: + KeymanSystemServiceClient(); + ~KeymanSystemServiceClient(); + + void SetCapsLockIndicator(guint32 capsLock); + gint32 GetCapsLockIndicator(); +}; + +KeymanSystemServiceClient::KeymanSystemServiceClient() { + int result; + if (testing) { + result = sd_bus_open_user(&bus); + } else { + result = sd_bus_default_system(&bus); + } + if (result < 0) { + g_error("%s: Can't get connection: %s", __FUNCTION__, strerror(-result)); + } +} + +KeymanSystemServiceClient::~KeymanSystemServiceClient() { + if (error) { sd_bus_error_free(error); } + if (msg) { sd_bus_message_unref(msg); } + if (bus) { sd_bus_unref(bus); } +} + +void KeymanSystemServiceClient::SetCapsLockIndicator(guint32 capsLock) { + // If Set/GetCapsLockIndicator is called more than once and previously + // failed, we will leak `error`. + assert(error == NULL); + + if (!bus) { + // we already reported the error, so just return + return; + } + + if (capsLock > 1) { + g_error("%s: Invalid value '%d' for parameter capsLock", __FUNCTION__, capsLock); + return; + } + + int result = sd_bus_call_method(bus, KEYMAN_BUS_NAME, KEYMAN_OBJECT_PATH, + KEYMAN_INTERFACE_NAME, "SetCapsLockIndicator", error, &msg, "b", capsLock); + if (result < 0) { + g_error("%s: Failed to call method SetCapsLockIndicator: %s. %s. %s.", + __FUNCTION__, strerror(-result), error->name, error->message); + return; + } +} + +gint32 KeymanSystemServiceClient::GetCapsLockIndicator() { + // If Set/GetCapsLockIndicator is called more than once and previously + // failed, we will leak `error`. + assert(error == NULL); + + if (!bus) { + // we already reported the error, so just return + return -1; + } + + int result = sd_bus_call_method(bus, KEYMAN_BUS_NAME, KEYMAN_OBJECT_PATH, + KEYMAN_INTERFACE_NAME, "GetCapsLockIndicator", error, &msg, ""); + if (result < 0) { + g_error("%s: Failed to call method GetCapsLockIndicator: %s. %s. %s.", + __FUNCTION__, strerror(-result), error->name, error->message); + return -1; + } + + guint32 capsLock = 0; + result = sd_bus_message_read(msg, "b", &capsLock); + if (result < 0) { + g_error("%s: Failed to parse response message: %s", + __FUNCTION__, strerror(-result)); + return -1; + } + + return capsLock; +} + +void set_capslock_indicator( + guint32 capsLock +) { + KeymanSystemServiceClient client; + client.SetCapsLockIndicator(capsLock); +} + +gint32 get_capslock_indicator() { + KeymanSystemServiceClient client; + return client.GetCapsLockIndicator(); +} diff --git a/linux/ibus-keyman/src/KeymanSystemServiceClient.h b/linux/ibus-keyman/src/KeymanSystemServiceClient.h new file mode 100644 index 0000000000000000000000000000000000000000..746974df5194e43fca411ef8081fdae8f6a0b9ae --- /dev/null +++ b/linux/ibus-keyman/src/KeymanSystemServiceClient.h @@ -0,0 +1,17 @@ +#ifndef __KEYMANSYSTEMSERVICECLIENT_H__ +#define __KEYMANSYSTEMSERVICECLIENT_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void set_capslock_indicator(guint32 capsLockState); +gint32 get_capslock_indicator(); + +#ifdef __cplusplus +} +#endif + +#endif // __KEYMANSYSTEMSERVICECLIENT_H__ diff --git a/linux/ibus-keyman/src/engine.c b/linux/ibus-keyman/src/engine.c index b7699f55623e46ac7f84f95697768d0ca141e401..49b9161f9a3f766786a8f60014470f4e2ba0a057 100644 --- a/linux/ibus-keyman/src/engine.c +++ b/linux/ibus-keyman/src/engine.c @@ -3,7 +3,7 @@ /* * Keyman Input Method for IBUS (The Input Bus) * - * Copyright (C) 2009-2022 SIL International + * Copyright (C) 2009-2023 SIL International * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public @@ -28,21 +28,13 @@ #include #include -#ifdef GDK_WINDOWING_X11 -#include -#include -#endif - -#ifdef GDK_WINDOWING_WAYLAND -#include -#endif - #include #include #include "config.h" #include "keymanutil.h" #include "keyman-service.h" +#include "KeymanSystemServiceClient.h" #include "engine.h" #include "keycodes.h" @@ -90,12 +82,6 @@ struct _IBusKeymanEngine { IBusLookupTable *table; IBusProperty *status_prop; IBusPropList *prop_list; -#ifdef GDK_WINDOWING_X11 - Display *xdisplay; -#endif -#ifdef GDK_WINDOWING_WAYLAND - GdkWaylandDisplay *wldisplay; -#endif commit_queue_item commit_queue[MAX_QUEUE_SIZE]; commit_queue_item *commit_item; @@ -311,29 +297,6 @@ ibus_keyman_engine_init(IBusKeymanEngine *keyman) { ibus_prop_list_append(keyman->prop_list, keyman->status_prop); keyman->state = NULL; -#ifdef GDK_WINDOWING_X11 - keyman->xdisplay = NULL; -#endif -#ifdef GDK_WINDOWING_WAYLAND - keyman->wldisplay = NULL; -#endif - - GdkDisplay *gdkDisplay = gdk_display_get_default(); -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_DISPLAY(gdkDisplay)) { - g_debug("Using X11"); - keyman->xdisplay = GDK_DISPLAY_XDISPLAY(gdkDisplay); - } else -#endif -#ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) { - g_debug("Using Wayland"); - keyman->wldisplay = GDK_WAYLAND_DISPLAY(gdkDisplay); - } else -#endif - { - g_error("unsupported GDK backend"); - } } static km_kbp_cp* get_base_layout() @@ -752,19 +715,7 @@ process_capslock_action( ) { g_message("%s: %s caps-lock", __FUNCTION__, action_item->capsLock ? "Enable" : "Disable"); -#ifdef GDK_WINDOWING_X11 - if (keyman->xdisplay) { - XkbLockModifiers(keyman->xdisplay, XkbUseCoreKbd, LockMask, action_item->capsLock ? LockMask : 0); - - XSync(keyman->xdisplay, False); - } -#endif -#ifdef GDK_WINDOWING_WAYLAND - // TODO - if (keyman->wldisplay) { - - } -#endif + set_capslock_indicator(action_item->capsLock); return TRUE; } diff --git a/linux/ibus-keyman/src/kmpdetails.c b/linux/ibus-keyman/src/kmpdetails.c index a6b7fbb18906326405f92f843fd3a84ddffeca77..eba95f4ef9752d4ce3b2c3fcd00480b068631e17 100644 --- a/linux/ibus-keyman/src/kmpdetails.c +++ b/linux/ibus-keyman/src/kmpdetails.c @@ -272,9 +272,6 @@ kmp_json_status get_keyboard_details(const char *kmp_dir, const char *id, keyboa } g_object_unref(parser); return JSON_OK; - - - } kmp_json_status get_kmp_details(const char *kmp_dir, kmp_details *details) @@ -295,7 +292,6 @@ kmp_json_status get_kmp_details(const char *kmp_dir, kmp_details *details) return JSON_FILE_NOT_EXISTS; } - json_parser_load_from_file (parser, kmp_json, &error); diff --git a/linux/ibus-keyman/src/main.c b/linux/ibus-keyman/src/main.c index f791cb45640db610194765626ed3fb36c4a307c0..4252fa93e8953ee01ca55fc732cc21db2f4b5e8c 100644 --- a/linux/ibus-keyman/src/main.c +++ b/linux/ibus-keyman/src/main.c @@ -36,12 +36,14 @@ static IBusFactory *factory = NULL; static gboolean xml = FALSE; static gboolean ibus = FALSE; static gboolean verbose = FALSE; +gboolean testing = FALSE; static const GOptionEntry entries[] = { { "xml", 'x', 0, G_OPTION_ARG_NONE, &xml, "generate xml for engines", NULL }, { "ibus", 'i', 0, G_OPTION_ARG_NONE, &ibus, "component is executed by ibus", NULL }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "verbose", NULL }, + { "testing", 0, 0, G_OPTION_ARG_NONE, &testing, "component is executed by integration testing", NULL}, { NULL }, }; diff --git a/linux/ibus-keyman/src/meson.build b/linux/ibus-keyman/src/meson.build index 29f265290d51ab76130db7f37d03c4c435cfb1c0..2e83a6510650099ea69dd3686687f137ba52ddfb 100644 --- a/linux/ibus-keyman/src/meson.build +++ b/linux/ibus-keyman/src/meson.build @@ -1,17 +1,13 @@ util_files = files( 'keymanutil.c', - 'keymanutil.h', 'kmpdetails.c', - 'kmpdetails.h', ) engine_files = files( 'main.c', 'engine.c', - 'engine.h', - 'keycodes.h', 'keyman-service.c', - 'keyman-service.h', + 'KeymanSystemServiceClient.cpp', ) include_dirs = [ @@ -20,7 +16,7 @@ include_dirs = [ include_directories(meson.current_build_dir() / '..'), ] -deps = [ibus, gtk, x11, json_glib, kmnkbp_lib] +deps = [ibus, gtk, json_glib, kmnkbp_lib, systemd] prefix = get_option('prefix') cfg = configuration_data() diff --git a/linux/ibus-keyman/src/test/meson.build b/linux/ibus-keyman/src/test/meson.build index c652d8f783f7bbe0be8aa3201bc388d725305f77..7e5cd21dbeab533bd4597326efbaf805e5ce1dc7 100644 --- a/linux/ibus-keyman/src/test/meson.build +++ b/linux/ibus-keyman/src/test/meson.build @@ -3,7 +3,7 @@ keymanutil_sources = [ util_files, ] -keymanutil_deps = [ibus, gtk, x11, json_glib, kmnkbp_lib] +keymanutil_deps = [ibus, gtk, json_glib, kmnkbp_lib] test_env = [ 'G_TEST_SRCDIR=' + meson.current_source_dir(), diff --git a/linux/ibus-keyman/src/test/run-tests.sh b/linux/ibus-keyman/src/test/run-tests.sh index 932f64b886163c707f2dd684e6b0f051a490fb5b..44a397582b45c8476d881ae3cf5fc417eb6fdf77 100755 --- a/linux/ibus-keyman/src/test/run-tests.sh +++ b/linux/ibus-keyman/src/test/run-tests.sh @@ -53,7 +53,7 @@ SCHEMA_DIR=$TEMP_DATA_DIR/glib-2.0/schemas export XDG_DATA_DIRS=$TEMP_DATA_DIR:$XDG_DATA_DIRS mkdir -p "$SCHEMA_DIR" -cp "$SRCDIR/../keyman-config/com.keyman.gschema.xml" "$SCHEMA_DIR/" +cp "$SRCDIR/../keyman-config/resources/com.keyman.gschema.xml" "$SCHEMA_DIR/" glib-compile-schemas "$SCHEMA_DIR" export GSETTINGS_BACKEND=memory diff --git a/linux/ibus-keyman/tests/KmDbusTestServer.cpp b/linux/ibus-keyman/tests/KmDbusTestServer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1d4746163d5171fa3fcbf10d58db8d099e97b961 --- /dev/null +++ b/linux/ibus-keyman/tests/KmDbusTestServer.cpp @@ -0,0 +1,146 @@ +// DBus test server. The server will start and listen on a non-standard DBus. +// It runs until the DBus Exit method gets called. +#include +#include +#include +#include + +#ifndef KEYMAN_TEST_SERVICE_PATH +#warning KEYMAN_TEST_SERVICE_PATH is undefined +#define KEYMAN_TEST_SERVICE_PATH "." +#endif + +#define KEYMAN_TESTSVC_BUS_NAME "com.keyman.ExitTestService" +#define KEYMAN_TESTSVC_INTERFACE_NAME "com.keyman.ExitTestService.Exit" +#define KEYMAN_TESTSVC_OBJECT_PATH "/com/keyman/ExitTestService/Exit" + +using namespace std; + +class KmDbusTestServer +{ +private: + GTestDBus *dbus; + sd_bus_slot *slot = NULL; + sd_bus *bus = NULL; + +public: + KmDbusTestServer(); + ~KmDbusTestServer(); + + void Loop(); +}; + +static int32_t +on_exit_method(sd_bus_message *msg, void *user_data, sd_bus_error *ret_error) { + g_debug("%s called", __FUNCTION__); + guint *exitFlag = (guint *)user_data; + *exitFlag = TRUE; + + return sd_bus_reply_method_return(msg, ""); +} + +static const sd_bus_vtable exit_test_service_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Exit", "", "", on_exit_method, 0), + SD_BUS_VTABLE_END +}; + +KmDbusTestServer::KmDbusTestServer() +{ + dbus = g_test_dbus_new(G_TEST_DBUS_NONE); + + // Add the private directory with our in-tree service files. + g_test_dbus_add_service_dir(dbus, KEYMAN_TEST_SERVICE_PATH); + + std::cout << "Add service dir to " << KEYMAN_TEST_SERVICE_PATH << std::endl; + + // Start the private D-Bus daemon + g_test_dbus_up(dbus); + + const gchar *address = g_test_dbus_get_bus_address(dbus); + + std::cout << "Test server running on: " << address << std::endl; + + ofstream file("/tmp/km-test-server.env", ios_base::out); + if (file) { + // write the address to a file which can be sourced + file << "export DBUS_SESSION_BUS_ADDRESS=" << address << std::endl; + file.close(); + } +} + +KmDbusTestServer::~KmDbusTestServer() +{ + if (bus) sd_bus_release_name(bus, KEYMAN_TESTSVC_BUS_NAME); + if (slot) sd_bus_slot_unref(slot); + if (bus) sd_bus_close_unref(bus); + + g_test_dbus_down(dbus); + g_object_unref(dbus); +} + +void KmDbusTestServer::Loop() +{ + int ret; + + ret = sd_bus_open_user(&bus); + + if (ret < 0) { + g_error("Failed to connect to system bus: %s", strerror(-ret)); + return; + } + + guint exitFlag = FALSE; + + // Install the object + ret = sd_bus_add_object_vtable(bus, &slot, KEYMAN_TESTSVC_OBJECT_PATH, + KEYMAN_TESTSVC_INTERFACE_NAME, exit_test_service_vtable, &exitFlag); + if (ret < 0) { + g_error("Failed to issue method call: %s", strerror(-ret)); + return; + } + + // Take a well-known service name so that clients can find us + ret = sd_bus_request_name(bus, KEYMAN_TESTSVC_BUS_NAME, 0); + if (ret < 0) { + g_error("Failed to acquire service name: %s", strerror(-ret)); + return; + } + + for (;;) { + // Process requests + ret = sd_bus_process(bus, NULL); + g_debug("sd_bus_process returned %d, exitFlag=%d", ret, exitFlag); + if (ret < 0) { + g_error("Failed to process bus: %s", strerror(-ret)); + return; + } + if (exitFlag) { + // `exitFlag` can be modified by the callback function + // `on_exit_method` which can be called by `sd_bus_process` + g_debug("Exiting loop"); + return; + } + + if (ret > 0) { + // we processed a request, try to process another one, right-away + continue; + } + + // Wait for the next request to process + ret = sd_bus_wait(bus, (uint64_t)-1); + g_debug("sd_bus_wait returned %d", ret); + if (ret < 0) { + g_error("Failed to wait on bus: %s", strerror(-ret)); + return; + } + } +} + +int +main(int argc, char *argv[]) { + KmDbusTestServer testServer; + testServer.Loop(); + + return 0; +} diff --git a/linux/ibus-keyman/tests/StopTestServer.cpp b/linux/ibus-keyman/tests/StopTestServer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97ce5bfbe7f8200624e02e8defa05bbb4d4ece42 --- /dev/null +++ b/linux/ibus-keyman/tests/StopTestServer.cpp @@ -0,0 +1,58 @@ +// Call the Exit method on the km-dbus-test-server. Remember to source +// `/tmp/km-test-server.env` prior to running stop-test-server in order +// to run on our non-standard DBus. +#include +#include + +using namespace std; + +class StopTestServer +{ +private: + sd_bus_error* error = NULL; + sd_bus_message* msg = NULL; + sd_bus* bus = NULL; + +public: + StopTestServer(); + ~StopTestServer(); + + int Run(); +}; + +StopTestServer::StopTestServer() +{ +} + +StopTestServer::~StopTestServer() +{ + if (error) { sd_bus_error_free(error); } + if (msg) { sd_bus_message_unref(msg); } + if (bus) { sd_bus_unref(bus); } +} + +int StopTestServer::Run() +{ + int result; + + result = sd_bus_open_user(&bus); + if (result < 0) { + std::cerr << "Can't get connection: " << strerror(-result) << std::endl; + return 1; + } + + result = sd_bus_call_method(bus, "com.keyman.ExitTestService", + "/com/keyman/ExitTestService/Exit", "com.keyman.ExitTestService.Exit", + "Exit", error, &msg, ""); + if (result < 0) { + std::cerr << "Failed to issue method call: " << strerror(-result) << std::endl; + return 1; + } + return 0; +} + +int +main(int argc, char* argv[]) { + StopTestServer stopServer; + return stopServer.Run(); +} diff --git a/linux/ibus-keyman/tests/meson.build b/linux/ibus-keyman/tests/meson.build index 8b2dea17a0e40953f4da58bcd6bf12b4e6611c08..130e3b8bce491473e0b172dd5c289f19494aebfe 100644 --- a/linux/ibus-keyman/tests/meson.build +++ b/linux/ibus-keyman/tests/meson.build @@ -1,21 +1,17 @@ test_files = [ 'ibusimcontext.c', - 'ibusimcontext.h', 'testfixture.cpp', 'testmodule.c', - 'testmodule.h', -] - -kmx_dir = [ - core_dir / 'build/arch' / get_option('buildtype') / 'tests/kmx_test_source' + '../src/KeymanSystemServiceClient.cpp', ] kmnkbp_tests_lib = cc.find_library( - 'libkmnkbp-tests', - dirs: kmx_dir + 'kmnkbp-tests', # meson will prefix 'lib' + dirs: [ core_dir / 'build/arch' / get_option('buildtype') / 'tests/kmx_test_source' ] ) -deps = [ibus, gtk, x11, json_glib, kmnkbp_lib, kmnkbp_tests_lib] +test_deps = [ibus, gtk, json_glib, kmnkbp_lib, kmnkbp_tests_lib, systemd] +dbus_deps = [gtk, systemd] test_env = [ 'G_TEST_SRCDIR=' + meson.current_source_dir(), @@ -32,10 +28,27 @@ test_include_dirs = [ test_exe = executable( 'ibus-keyman-tests', test_files, util_files, - dependencies: deps, + dependencies: test_deps, include_directories: test_include_dirs, ) +system_service_dir = meson.current_source_dir() / '../../keyman-system-service/build' / build_machine.cpu_family() / get_option('buildtype') / 'tests/services' + +dbus_test_server = executable( + 'km-dbus-test-server', + 'KmDbusTestServer.cpp', + dependencies: dbus_deps, + cpp_args: [ + '-DKEYMAN_TEST_SERVICE_PATH="' + system_service_dir + '"' + ] +) + +stop_test_server = executable( + 'stop-test-server', + 'StopTestServer.cpp', + dependencies: dbus_deps, +) + env_file = '/tmp/env.txt' pid_file = '/tmp/ibus-keyman-test-pids' @@ -84,7 +97,7 @@ test( kmxtest_files = run_command( find_tests, - kmx_dir, + [ common_dir / 'test/keyboards/baseline' ], check: true, ).stdout().split('\n') diff --git a/linux/ibus-keyman/tests/scripts/run-single-test.sh b/linux/ibus-keyman/tests/scripts/run-single-test.sh index c265c27ccdf3c9c78533dc58b37585b16fca611c..19baca7dd6f8a16d9e5c91f4ddc2e5392d9de29a 100755 --- a/linux/ibus-keyman/tests/scripts/run-single-test.sh +++ b/linux/ibus-keyman/tests/scripts/run-single-test.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash +set -eu TESTDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman/test_kmx -. "$(dirname "$0")"/test-helper.sh +. "$(dirname "$0")"/test-helper.inc.sh if [ -v KEYMAN_PKG_BUILD ]; then # During package builds we skip these tests that require to start ibus because diff --git a/linux/ibus-keyman/tests/scripts/run-tests.sh b/linux/ibus-keyman/tests/scripts/run-tests.sh index 81f32bd4efc2b62e2b84f040ffbeac7a60949d85..92cae307ed3e232b10b35b21431bbeb5c3dfa36c 100755 --- a/linux/ibus-keyman/tests/scripts/run-tests.sh +++ b/linux/ibus-keyman/tests/scripts/run-tests.sh @@ -1,9 +1,17 @@ #!/usr/bin/env bash +set -eu -TOP_SRCDIR=${top_srcdir:-$(realpath "$(dirname "$0")/..")} -TESTDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman/test_kmx +TOP_SRCDIR=${top_srcdir:-$(realpath "$(dirname "$0")/../..")} +TESTBASEDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman +TESTDIR=${TESTBASEDIR}/test_kmx +PID_FILE=/tmp/ibus-keyman-test-pids +ENV_FILE=/tmp/keyman-env.txt -. "$(dirname "$0")"/test-helper.sh +. "$(dirname "$0")"/test-helper.inc.sh + +local_cleanup() { + cleanup "$PID_FILE" +} if [ -v KEYMAN_PKG_BUILD ]; then # During package builds we skip these tests that require to start ibus because @@ -20,16 +28,6 @@ if ! which Xvfb > /dev/null || ! which Xephyr > /dev/null || ! which metacity > exit 1 fi -function cleanup() { - if [ -f "$PID_FILE" ]; then - echo - echo "# Shutting down processes..." - bash "$PID_FILE" > /dev/null 2>&1 - rm "$PID_FILE" - echo "# Finished shutdown of processes." - fi -} - function help() { echo "Usage:" echo " $0 [-k] [--tap] [--surrounding-text] [--no-surrounding-text] [--no-wayland] [--no-x11] [[--] TEST...]" @@ -54,79 +52,24 @@ function run_tests() { DISPLAY_SERVER=$1 shift - echo > "$PID_FILE" - TEMP_DATA_DIR=$(mktemp --directory) - echo "rm -rf ${TEMP_DATA_DIR} || true" >> "$PID_FILE" - - COMMON_ARCH_DIR= - [ -d "${TOP_SRCDIR}"/../../core/build/arch ] && COMMON_ARCH_DIR=${TOP_SRCDIR}/../../core/build/arch - - if [ -d "${COMMON_ARCH_DIR}"/release ]; then - COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/release - elif [ -d "${COMMON_ARCH_DIR}"/debug ]; then - COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/debug + if [ -d "$(dirname "$0")/../../../build/$(arch)/debug" ]; then + CONFIG=debug + elif [ -d "$(dirname "$0")/../../../build/$(arch)/release" ]; then + CONFIG=release else - echo "Can't find neither ${COMMON_ARCH_DIR}/release nor ${COMMON_ARCH_DIR}/debug" - exit 2 + echo "Cannot find ../../../build/$(arch)/debug or ../../../build/$(arch)/release" + exit 9 fi - # TODO: we are borrowing the KMX tests out of core; these should be shared in common - # to avoid deep links like this in future - KMX_TEST_DIR="${TOP_SRCDIR}"/../../common/test/keyboards/baseline - - if [ ! -d "$TESTDIR" ] || ! [[ $(find "${TESTDIR}/" -name \*.kmx 2>/dev/null | wc -l) -gt 0 ]]; then - if [[ $(find "${KMX_TEST_DIR}/" -name \*.kmx 2>/dev/null | wc -l) -gt 0 ]]; then - mkdir -p "$(realpath --canonicalize-missing "$TESTDIR"/..)" - ln -sf "$(realpath "${KMX_TEST_DIR}")" "$TESTDIR" - else - echo "Can't find kmx files in ${KMX_TEST_DIR}" - exit 3 - fi - fi + G_TEST_BUILDDIR="$(dirname "$0")/../../../build/$(arch)/${CONFIG}/tests" + + setup "$DISPLAY_SERVER" "$ENV_FILE" "$PID_FILE" echo "# NOTE: When the tests fail check /tmp/ibus-engine-keyman.log and /tmp/ibus-daemon.log!" echo "" - if [ "$DISPLAY_SERVER" == "wayland" ]; then - if ! can_run_wayland; then - # support for --headless got added in mutter 40.x - echo "ERROR: mutter doesn't support running headless. Can't run Wayland tests." - exit 7 - fi - echo "# Running on Wayland..." - TMPFILE=$(mktemp) - # mutter-Message: 18:56:15.422: Using Wayland display name 'wayland-1' - mutter --wayland --headless --no-x11 --virtual-monitor 1024x768 &> "$TMPFILE" & - echo "kill -9 $! || true" >> "$PID_FILE" - sleep 1s - export WAYLAND_DISPLAY - WAYLAND_DISPLAY=$(cat "$TMPFILE" | grep "Using Wayland display" | cut -d"'" -f2) - rm "$TMPFILE" - else - echo "# Starting Xvfb..." - Xvfb -screen 0 1024x768x24 :33 &> /dev/null & - echo "kill -9 $! || true" >> "$PID_FILE" - sleep 1 - echo "# Starting Xephyr..." - DISPLAY=:33 Xephyr :32 -screen 1024x768 &> /dev/null & - echo "kill -9 $! || true" >> "$PID_FILE" - sleep 1 - echo "# Starting metacity" - metacity --display=:32 &> /dev/null & - echo "kill -9 $! || true" >> "$PID_FILE" - - export DISPLAY=:32 - fi - - # Install schema to temporary directory. This removes the build dependency on the keyman package. - SCHEMA_DIR=$TEMP_DATA_DIR/glib-2.0/schemas - export XDG_DATA_DIRS=$TEMP_DATA_DIR:$XDG_DATA_DIRS - - mkdir -p "$SCHEMA_DIR" - cp "${TOP_SRCDIR}"/../keyman-config/com.keyman.gschema.xml "$SCHEMA_DIR"/ - glib-compile-schemas "$SCHEMA_DIR" - if [ $# -gt 0 ]; then + #shellcheck disable=SC2206 TESTFILES=($@) else pushd "$TESTDIR" > /dev/null || exit @@ -134,32 +77,20 @@ function run_tests() { popd > /dev/null || exit fi - export LD_LIBRARY_PATH=${COMMON_ARCH_DIR}/src:$LD_LIBRARY_PATH - - # Ubuntu 18.04 Bionic doesn't have ibus-memconf, and glib is not compiled with the keyfile - # backend enabled, so we just use the default backend. Otherwise we use the keyfile - # store which interferes less when running on a dev machine. - if [ -f /usr/libexec/ibus-memconf ]; then - export GSETTINGS_BACKEND=keyfile - IBUS_CONFIG=--config=/usr/libexec/ibus-memconf - fi - - ibus-daemon "${ARG_VERBOSE-}" --panel=disable ${IBUS_CONFIG-} &> /tmp/ibus-daemon.log & - echo "kill -9 $! || true" >> "$PID_FILE" - sleep 1s - - ../src/ibus-engine-keyman "${ARG_VERBOSE-}" &> /tmp/ibus-engine-keyman.log & - echo "kill -9 $! || true" >> "$PID_FILE" - sleep 1s - echo "# Starting tests..." + # shellcheck disable=SC1090 + source "$ENV_FILE" + echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS" + # Note: -k and --tap are consumed by the GLib testing framework - "${G_TEST_BUILDDIR:-.}"/ibus-keyman-tests "${ARG_K-}" "${ARG_TAP-}" \ - "${ARG_VERBOSE-}" "${ARG_DEBUG-}" "${ARG_SURROUNDING_TEXT-}" "${ARG_NO_SURROUNDING_TEXT-}" \ - --directory "$TESTDIR" --"${DISPLAY_SERVER}" "${TESTFILES[@]}" + #shellcheck disable=SC2068 # we want to split array elements! + #shellcheck disable=SC2086 + "${G_TEST_BUILDDIR:-../../build/$(arch)/${CONFIG}/tests}/ibus-keyman-tests" ${ARG_K-} ${ARG_TAP-} \ + ${ARG_VERBOSE-} ${ARG_DEBUG-} ${ARG_SURROUNDING_TEXT-} ${ARG_NO_SURROUNDING_TEXT-} \ + --directory "$TESTDIR" --"${DISPLAY_SERVER}" ${TESTFILES[@]} echo "# Finished tests." - cleanup + cleanup "$PID_FILE" } USE_WAYLAND=1 @@ -182,7 +113,7 @@ while (( $# )); do shift || (echo "Error: The last argument is missing a value. Exiting."; false) || exit 5 done -if ! can_run_wayland; then +if ! can_run_wayland && [ "$USE_WAYLAND" == "0" ]; then # support for --headless got added in mutter 40.x echo "# WARNING: mutter doesn't support running headless. Skipping Wayland tests." USE_WAYLAND=0 @@ -197,13 +128,18 @@ if [ "$USE_WAYLAND" == "0" ] && [ "$USE_X11" == "0" ]; then exit 6 fi +G_TEST_BUILDDIR="${G_TEST_BUILDDIR:-../../build/$(arch)/debug/tests}" +if [ ! -f "${G_TEST_BUILDDIR}/ibus-keyman-tests" ]; then + G_TEST_BUILDDIR="${G_TEST_BUILDDIR:-../../build/$(arch)/release/tests}" +fi + echo > "$PID_FILE" -trap cleanup EXIT SIGINT +trap local_cleanup EXIT SIGINT if [ "$USE_WAYLAND" == "1" ]; then - run_tests wayland "$@" + ( run_tests wayland "$@" ) fi if [ "$USE_X11" == "1" ]; then - run_tests x11 "$@" + ( run_tests x11 "$@" ) fi diff --git a/linux/ibus-keyman/tests/scripts/setup-tests.sh b/linux/ibus-keyman/tests/scripts/setup-tests.sh index 94400d40f2602b5b768c673007ffcebbde796972..9d591869fbbe46f77252d509beb2257c0efeba71 100755 --- a/linux/ibus-keyman/tests/scripts/setup-tests.sh +++ b/linux/ibus-keyman/tests/scripts/setup-tests.sh @@ -1,114 +1,6 @@ #!/usr/bin/env bash -DISPLAY_SERVER=$1 -ENV_FILE=$2 -PID_FILE=$3 +set -eu -TOP_SRCDIR=${G_TEST_SRCDIR:-$(realpath "$(dirname "$0")/..")}/.. -TOP_BINDIR=${G_TEST_BUILDDIR:-$(realpath "$(dirname "$0/..")")}/.. -TESTDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman/test_kmx +. "$(dirname "$0")/test-helper.inc.sh" -. "$(dirname "$0")"/test-helper.sh - -echo > "$ENV_FILE" - -if [ -f "$PID_FILE" ]; then - # kill previous instances - "$(dirname "$0")"/teardown-tests.sh "$PID_FILE" -fi - -echo > "$PID_FILE" -TEMP_DATA_DIR=$(mktemp --directory) -echo "rm -rf ${TEMP_DATA_DIR} || true" >> "$PID_FILE" - -COMMON_ARCH_DIR= -[ -d "${TOP_SRCDIR}"/../../core/build/arch ] && COMMON_ARCH_DIR=${TOP_SRCDIR}/../../core/build/arch - -if [ -d "${COMMON_ARCH_DIR}"/release ]; then - COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/release -elif [ -d "${COMMON_ARCH_DIR}"/debug ]; then - COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/debug -else - echo "Can't find neither ${COMMON_ARCH_DIR}/release nor ${COMMON_ARCH_DIR}/debug" - exit 2 -fi - -# TODO: we are borrowing the KMX tests out of core; these should be shared in common -# to avoid deep links like this in future -KMX_TEST_DIR="${TOP_SRCDIR}"/../../common/test/keyboards/baseline - -if [ ! -d "$TESTDIR" ] || ! [[ $(find "${TESTDIR}/" -name \*.kmx 2>/dev/null | wc -l) -gt 0 ]]; then - if [[ $(find "${KMX_TEST_DIR}/" -name \*.kmx 2>/dev/null | wc -l) -gt 0 ]]; then - mkdir -p "$(realpath --canonicalize-missing "$TESTDIR"/..)" - ln -sf "$(realpath "${KMX_TEST_DIR}")" "$TESTDIR" - else - echo "Can't find kmx files in ${KMX_TEST_DIR}" - exit 3 - fi -fi - -if [ "$DISPLAY_SERVER" == "wayland" ]; then - if ! can_run_wayland; then - # support for --headless got added in mutter 40.x - echo "ERROR: mutter doesn't support running headless. Can't run Wayland tests." - exit 7 - fi - echo "Running on Wayland..." - TMPFILE=$(mktemp) - # mutter-Message: 18:56:15.422: Using Wayland display name 'wayland-1' - mutter --wayland --headless --no-x11 --virtual-monitor 1024x768 &> "$TMPFILE" & - echo "kill -9 $! || true" >> "$PID_FILE" - sleep 1s - export WAYLAND_DISPLAY - WAYLAND_DISPLAY=$(grep "Using Wayland display" "$TMPFILE" | cut -d"'" -f2) - rm "$TMPFILE" - echo "export WAYLAND_DISPLAY=\"$WAYLAND_DISPLAY\"" >> "$ENV_FILE" -else - echo "Starting Xvfb..." - Xvfb -screen 0 1024x768x24 :33 &> /dev/null & - echo "kill -9 $! || true" >> "$PID_FILE" - sleep 1 - echo "Starting Xephyr..." - DISPLAY=:33 Xephyr :32 -screen 1024x768 &> /dev/null & - echo "kill -9 $! || true" >> "$PID_FILE" - sleep 1 - echo "Starting metacity" - metacity --display=:32 &> /dev/null & - echo "kill -9 $! || true" >> "$PID_FILE" - - export DISPLAY=:32 - echo "export DISPLAY=\"$DISPLAY\"" >> "$ENV_FILE" -fi - - # Install schema to temporary directory. This removes the build dependency on the keyman package. - SCHEMA_DIR=$TEMP_DATA_DIR/glib-2.0/schemas - export XDG_DATA_DIRS=$TEMP_DATA_DIR:$XDG_DATA_DIRS - echo "export XDG_DATA_DIRS=\"$XDG_DATA_DIRS\"" >> "$ENV_FILE" - - mkdir -p "$SCHEMA_DIR" - cp "${TOP_SRCDIR}"/../keyman-config/com.keyman.gschema.xml "$SCHEMA_DIR"/ - glib-compile-schemas "$SCHEMA_DIR" - - export LD_LIBRARY_PATH=${COMMON_ARCH_DIR}/src:$LD_LIBRARY_PATH - echo "export LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH\"" >> "$ENV_FILE" - - # Ubuntu 18.04 Bionic doesn't have ibus-memconf, and glib is not compiled with the keyfile - # backend enabled, so we just use the default backend. Otherwise we use the keyfile - # store which interferes less when running on a dev machine. - if [ -f /usr/libexec/ibus-memconf ]; then - export GSETTINGS_BACKEND=keyfile - echo "export GSETTINGS_BACKEND=\"$GSETTINGS_BACKEND\"" >> "$ENV_FILE" - IBUS_CONFIG=--config=/usr/libexec/ibus-memconf - fi - - ibus-daemon "${ARG_VERBOSE-}" --daemonize --panel=disable ${IBUS_CONFIG-} &> /tmp/ibus-daemon.log - echo "kill -9 $! || true" >> "$PID_FILE" - sleep 1s - - IBUS_ADDRESS=$(ibus address) - export IBUS_ADDRESS - - echo "export IBUS_ADDRESS=\"$IBUS_ADDRESS\"" >> "$ENV_FILE" - - "${TOP_BINDIR}"/src/ibus-engine-keyman "${ARG_VERBOSE-}" &> /tmp/ibus-engine-keyman.log & - echo "kill -9 $! || true" >> "$PID_FILE" - sleep 1s +setup "$1" "$2" "$3" diff --git a/linux/ibus-keyman/tests/scripts/teardown-tests.sh b/linux/ibus-keyman/tests/scripts/teardown-tests.sh index 489769d7218efed5bce5bd5874ce412cf5f47b05..c3b955a13a3d7aefe846d5160171c5d444ee232d 100755 --- a/linux/ibus-keyman/tests/scripts/teardown-tests.sh +++ b/linux/ibus-keyman/tests/scripts/teardown-tests.sh @@ -1,7 +1,6 @@ #!/usr/bin/env bash -PID_FILE=$1 +set -eu -echo "Shutting down processes..." -bash "$PID_FILE" > /dev/null 2>&1 -rm "$PID_FILE" -echo "Finished shutdown of processes." +. "$(dirname "$0")/test-helper.inc.sh" + +cleanup "$1" diff --git a/linux/ibus-keyman/tests/scripts/test-helper.inc.sh b/linux/ibus-keyman/tests/scripts/test-helper.inc.sh new file mode 100755 index 0000000000000000000000000000000000000000..2a3735d7084abf9b7752c29166335e893133f952 --- /dev/null +++ b/linux/ibus-keyman/tests/scripts/test-helper.inc.sh @@ -0,0 +1,229 @@ +#!/usr/bin/env bash + +function can_run_wayland() { + local MUTTER_VERSION + MUTTER_VERSION=$(mutter --version | head -1 | cut -f2 -d' ' | cut -f1 -d'.') + if (( MUTTER_VERSION < 40 )); then + return 1 + else + return 0 + fi +} + +function generate_kmpjson() { + local TESTDIR + TESTDIR="$1" + pushd "$TESTDIR" > /dev/null || exit + KMPFILE="${TESTDIR}/kmp.json" + cat <<-EOF > "$KMPFILE" + { + "system": { + "keymanDeveloperVersion": "10.0.1099.0", + "fileVersion": "7.0" + }, + "info": { + "name": { + "description": "Test Keyboards" + }, + "version": { + "description": "0.0" + } + }, + "files": [ + { + "name": "kmp.json", + "description": "Package information (JSON)" + } +EOF + for f in k_*.kmx; do + keyboard=$(basename "$f") + keyboard="${keyboard%.*}" + echo " ," >> "$KMPFILE" + cat <<-EOF >> "$KMPFILE" + { + "name": "$keyboard", + "description": "$keyboard" + } +EOF + done + cat <<-EOF >> "$KMPFILE" + ], + "keyboards": [ +EOF + FIRST=true + for f in k_*.kmx; do + keyboard=$(basename "$f") + keyboard="${keyboard%.*}" + if $FIRST; then + FIRST=false + else + echo " ," >> "$KMPFILE" + fi + cat <<-EOF >> "$KMPFILE" + { + "name": "$keyboard", + "id": "$keyboard", + "version": "0.0", + "languages": [ + { + "name": "Undetermined", + "id": "und" + } + ] + } +EOF + done + echo " ]" >> "$KMPFILE" + echo "}" >> "$KMPFILE" + popd > /dev/null || exit +} + +function link_test_keyboards() { + KMX_TEST_DIR=$1 + TESTDIR=$2 + TESTBASEDIR=$3 + + if [[ $(find "${KMX_TEST_DIR}/" -name k_\*.kmx 2>/dev/null | wc -l) -gt 0 ]]; then + mkdir -p "$(realpath --canonicalize-missing "$TESTBASEDIR")" + rm -f "$TESTDIR" + ln -sf "$(realpath "${KMX_TEST_DIR}")" "$TESTDIR" + else + echo "Can't find test kmx files in ${KMX_TEST_DIR}" + exit 3 + fi +} + +function setup() { + local DISPLAY_SERVER ENV_FILE PID_FILE TOP_SRCDIR TOP_BINDIR TESTBASEDIR TESTDIR + DISPLAY_SERVER=$1 + ENV_FILE=$2 + PID_FILE=$3 + + TOP_SRCDIR=${G_TEST_SRCDIR:-$(realpath "$(dirname "$0")/..")}/.. + TOP_BINDIR=${G_TEST_BUILDDIR:-$(realpath "$(dirname "$0/..")")}/.. + TESTBASEDIR=${XDG_DATA_HOME:-$HOME/.local/share}/keyman + TESTDIR=${TESTBASEDIR}/test_kmx + + echo > "$ENV_FILE" + + if [ -f "$PID_FILE" ]; then + # kill previous instances + "$(dirname "$0")"/teardown-tests.sh "$PID_FILE" + fi + + echo > "$PID_FILE" + TEMP_DATA_DIR=$(mktemp --directory) + echo "rm -rf ${TEMP_DATA_DIR} || true" >> "$PID_FILE" + + COMMON_ARCH_DIR= + [ -d "${TOP_SRCDIR}"/../../core/build/arch ] && COMMON_ARCH_DIR=${TOP_SRCDIR}/../../core/build/arch + + if [ -d "${COMMON_ARCH_DIR}"/release ]; then + COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/release + elif [ -d "${COMMON_ARCH_DIR}"/debug ]; then + COMMON_ARCH_DIR=${COMMON_ARCH_DIR}/debug + else + echo "Can't find neither ${COMMON_ARCH_DIR}/release nor ${COMMON_ARCH_DIR}/debug" + exit 2 + fi + + link_test_keyboards "${TOP_SRCDIR}/../../common/test/keyboards/baseline" "$TESTDIR" "$TESTBASEDIR" + + generate_kmpjson "$TESTDIR" + + # Start test dbus server + "${TOP_BINDIR}/tests/km-dbus-test-server" &> /dev/null & + sleep 1 + source /tmp/km-test-server.env + cat /tmp/km-test-server.env >> "$ENV_FILE" + cat /tmp/km-test-server.env >> "$PID_FILE" + echo "${TOP_BINDIR}/tests/stop-test-server" >> "$PID_FILE" + + if [ "$DISPLAY_SERVER" == "wayland" ]; then + if ! can_run_wayland; then + # support for --headless got added in mutter 40.x + echo "ERROR: mutter doesn't support running headless. Can't run Wayland tests." + exit 7 + fi + echo "Running on Wayland:" + echo "Starting mutter..." + TMPFILE=$(mktemp) + # mutter-Message: 18:56:15.422: Using Wayland display name 'wayland-1' + mutter --wayland --headless --no-x11 --virtual-monitor 1024x768 &> "$TMPFILE" & + echo "kill -9 $! || true" >> "$PID_FILE" + sleep 1s + export WAYLAND_DISPLAY + WAYLAND_DISPLAY=$(grep "Using Wayland display" "$TMPFILE" | cut -d"'" -f2) + rm "$TMPFILE" + echo "export WAYLAND_DISPLAY=\"$WAYLAND_DISPLAY\"" >> "$ENV_FILE" + else + echo "Running on X11:" + echo "Starting Xvfb..." + Xvfb -screen 0 1024x768x24 :33 &> /dev/null & + echo "kill -9 $! || true" >> "$PID_FILE" + sleep 1 + echo "Starting Xephyr..." + DISPLAY=:33 Xephyr :32 -screen 1024x768 &> /dev/null & + echo "kill -9 $! || true" >> "$PID_FILE" + sleep 1 + echo "Starting metacity" + metacity --display=:32 &> /dev/null & + echo "kill -9 $! || true" >> "$PID_FILE" + + export DISPLAY=:32 + echo "export DISPLAY=\"$DISPLAY\"" >> "$ENV_FILE" + fi + + # Install schema to temporary directory. This removes the build dependency on the keyman package. + SCHEMA_DIR=$TEMP_DATA_DIR/glib-2.0/schemas + export XDG_DATA_DIRS=$TEMP_DATA_DIR:${XDG_DATA_DIRS-} + echo "export XDG_DATA_DIRS=$XDG_DATA_DIRS" >> "$ENV_FILE" + + export GSETTINGS_SCHEMA_DIR=${SCHEMA_DIR} + echo "export GSETTINGS_SCHEMA_DIR=\"$SCHEMA_DIR\"" >> "$ENV_FILE" + + mkdir -p "$SCHEMA_DIR" + cp "${TOP_SRCDIR}"/../keyman-config/resources/com.keyman.gschema.xml "$SCHEMA_DIR"/ + glib-compile-schemas "$SCHEMA_DIR" + + export LD_LIBRARY_PATH=${COMMON_ARCH_DIR}/src:${LD_LIBRARY_PATH-} + echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> "$ENV_FILE" + + # Ubuntu 18.04 Bionic doesn't have ibus-memconf, and glib is not compiled with the keyfile + # backend enabled, so we just use the default backend. Otherwise we use the keyfile + # store which interferes less when running on a dev machine. + if [ -f /usr/libexec/ibus-memconf ]; then + export GSETTINGS_BACKEND=keyfile + echo "export GSETTINGS_BACKEND=\"$GSETTINGS_BACKEND\"" >> "$ENV_FILE" + IBUS_CONFIG=--config=/usr/libexec/ibus-memconf + fi + + #shellcheck disable=SC2086 + ibus-daemon ${ARG_VERBOSE-} --daemonize --panel=disable --address=unix:abstract="${TEMP_DATA_DIR}/test-ibus" ${IBUS_CONFIG-} &> /tmp/ibus-daemon.log + echo "kill -9 $! || true" >> "$PID_FILE" + sleep 1s + + IBUS_ADDRESS=$(ibus address) + export IBUS_ADDRESS + + echo "export IBUS_ADDRESS=\"$IBUS_ADDRESS\"" >> "$ENV_FILE" + + echo "# DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS" + #shellcheck disable=SC2086 + "${TOP_BINDIR}/src/ibus-engine-keyman" --testing ${ARG_VERBOSE-} &> /tmp/ibus-engine-keyman.log & + echo "kill -9 $! || true" >> "$PID_FILE" + sleep 1s +} + +function cleanup() { + local PID_FILE + PID_FILE=$1 + + if [ -f "$PID_FILE" ]; then + echo + echo "# Shutting down processes..." + bash "$PID_FILE" > /dev/null 2>&1 + rm "$PID_FILE" + echo "# Finished shutdown of processes." + fi +} diff --git a/linux/ibus-keyman/tests/scripts/test-helper.sh b/linux/ibus-keyman/tests/scripts/test-helper.sh deleted file mode 100755 index 92c8a7e5287928c2bcc853bdb047e2ef6fbfc2f8..0000000000000000000000000000000000000000 --- a/linux/ibus-keyman/tests/scripts/test-helper.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -function can_run_wayland() { - local MUTTER_VERSION - MUTTER_VERSION=$(mutter --version | head -1 | cut -f2 -d' ' | cut -f1 -d'.') - if (( MUTTER_VERSION < 40 )); then - return 1 - else - return 0 - fi -} diff --git a/linux/ibus-keyman/tests/testfixture.cpp b/linux/ibus-keyman/tests/testfixture.cpp index 3e189258bdb11e19c6ba9b69aea1982473572163..84aab933c836a7fde6518be66a77ddbef1bddada 100644 --- a/linux/ibus-keyman/tests/testfixture.cpp +++ b/linux/ibus-keyman/tests/testfixture.cpp @@ -13,17 +13,10 @@ #include "ibusimcontext.h" #include "keycodes.h" #include "keymanutil.h" +#include "KeymanSystemServiceClient.h" #include "kmx_test_source.hpp" #include "testmodule.h" -#ifdef GDK_WINDOWING_X11 -#include -#include -#endif -#ifdef GDK_WINDOWING_WAYLAND -#include -#endif - typedef struct { IBusBus *bus; GtkIMContext *context; @@ -42,12 +35,7 @@ static gboolean use_wayland = FALSE; static GdkWindow *window = NULL; static GMainLoop *thread_loop = NULL; static GTypeModule *module = NULL; -#ifdef GDK_WINDOWING_X11 -static Display *display = NULL; -#endif -#ifdef GDK_WINDOWING_WAYLAND -static GdkWaylandDisplay *wldisplay = NULL; -#endif +gboolean testing = TRUE; static void module_register(GTypeModule *module) { @@ -69,18 +57,6 @@ ibus_keyman_tests_fixture_set_up(IBusKeymanTestsFixture *fixture, gconstpointer window = gtk_widget_get_window(widget); } - GdkDisplay *gdkDisplay = gdk_display_get_default(); -#ifdef GDK_WINDOWING_X11 - if (!use_wayland && !display && GDK_IS_X11_DISPLAY(gdkDisplay)) { - display = GDK_DISPLAY_XDISPLAY(gdkDisplay); - } -#endif -#ifdef GDK_WINDOWING_WAYLAND - if (use_wayland && !wldisplay && GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) { - wldisplay = GDK_WAYLAND_DISPLAY(gdkDisplay); - } -#endif - if (!loaded) { ibus_init(); @@ -114,35 +90,12 @@ ibus_keyman_tests_fixture_tear_down(IBusKeymanTestsFixture *fixture, gconstpoint static void set_caps_lock_state(IBusKeymanTestsFixture *fixture, bool caps_lock_on) { -#ifdef GDK_WINDOWING_X11 - if (!use_wayland && display) { - XkbLockModifiers(display, XkbUseCoreKbd, LockMask, caps_lock_on ? LockMask : 0); - XSync(display, False); - } -#endif -#ifdef GDK_WINDOWING_WAYLAND - if (use_wayland && wldisplay) { - // TODO - } -#endif + set_capslock_indicator((guint32)caps_lock_on); } static bool get_caps_lock_state(IBusKeymanTestsFixture *fixture) { -#ifdef GDK_WINDOWING_X11 - if (!use_wayland && display) { - XKeyboardState state; - XGetKeyboardControl(display, &state); - return state.led_mask & 1; - } -#endif -#ifdef GDK_WINDOWING_WAYLAND - if (use_wayland && wldisplay) { - // TODO - } -#endif - - return false; + return get_capslock_indicator(); } static void @@ -493,23 +446,6 @@ main(int argc, char *argv[]) { return 2; } - if (!argWayland && !argX11) { - GdkDisplay *gdkDisplay = gdk_display_get_default(); -#ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) { - argWayland = true; - } else -#endif -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_DISPLAY(gdkDisplay)) { - argX11 = true; - } else -#endif - { - g_error("Neither X11 nor Wayland display!"); - } - } - use_wayland = argWayland; int nTests = argc - iArg; char **tests = &argv[iArg]; @@ -528,13 +464,6 @@ main(int argc, char *argv[]) { skipReason = "mnemonic keyboards are not yet supported on Linux (#3345)"; } - // Wayland related tests - #4273 - if (use_wayland && ( - strcmp(filename->str, "k_031___caps_lock") == 0 || - strcmp(filename->str, "k_032___caps_control") == 0)) { - skipReason = "capslock related keyboards are not yet working with Wayland (#4273)"; - } - if (runSurroundingTextTests) { add_test(directory, filename->str, TRUE, skipReason, use_wayland); } @@ -549,20 +478,6 @@ main(int argc, char *argv[]) { // Run tests int retVal = g_test_run(); - // Cleanup -#ifdef GDK_WINDOWING_X11 - if (!use_wayland && display) { - XCloseDisplay(display); - display = NULL; - } -#endif -#ifdef GDK_WINDOWING_WAYLAND - if (use_wayland) { - // REVIEW: do we have to call something for cleanup? - wldisplay = NULL; - } -#endif - test_module_unuse(module); return retVal; } diff --git a/linux/keyman-config/.editorconfig b/linux/keyman-config/.editorconfig index 4b530139d7b44db0fbccb2a55ff018d9c137f63b..f65775da76c6b240b5e899aba70b3e44efb76245 100644 --- a/linux/keyman-config/.editorconfig +++ b/linux/keyman-config/.editorconfig @@ -7,4 +7,7 @@ indent_size = 4 indent_style = tab [Makefile] -indent_style = tab \ No newline at end of file +indent_style = tab + +[*.sh] +indent_size = 2 diff --git a/linux/keyman-config/MANIFEST.in b/linux/keyman-config/MANIFEST.in index e27020f36c9b8a46396a1ff85d8ce50bfd8e5e3f..20705c6fc3e973a6e504395b04e1eeaeeacf9ac6 100644 --- a/linux/keyman-config/MANIFEST.in +++ b/linux/keyman-config/MANIFEST.in @@ -2,4 +2,4 @@ recursive-include keyman_config/icons * recursive-include icons * include README.md include *.bash-completion -include *.gschema.xml +include resources/*.gschema.xml diff --git a/linux/keyman-config/Makefile b/linux/keyman-config/Makefile index b18b1f133d4cd5294f4f978a1900fb72f7df59a0..f057dcad0ce8c0f81e2e82af0014c99e429a41f8 100644 --- a/linux/keyman-config/Makefile +++ b/linux/keyman-config/Makefile @@ -1,42 +1,19 @@ #!/usr/bin/make -default: clean version man langtags - python3 setup.py build - -langtags: - cd buildtools && python3 ./build-langtags.py - -install: # run as sudo - pip3 install qrcode sentry-sdk - # eventually change this to: pip3 install . - python3 setup.py install - # install icons - mkdir -p /usr/local/share/keyman/icons - cp keyman_config/icons/* /usr/local/share/keyman/icons - # install man pages - mkdir -p /usr/local/share/man/man1 - cp ../../debian/man/*.1 /usr/local/share/man/man1 - -install-temp: - mkdir -p /tmp/keyman/$(shell python3 -c 'import sys;import os;pythonver="python%d.%d" % (sys.version_info[0], sys.version_info[1]);sitedir = os.path.join("lib", pythonver, "site-packages");print(sitedir)') - # when we no longer have to support old pip version (python > 3.6) change this to: - # pip3 install --prefix /tmp/keyman . - PYTHONUSERBASE=/tmp/keyman python3 setup.py install --user - -uninstall: clean # run as sudo - rm -rf /usr/local/share/keyman/icons - rm -f /usr/local/share/man/man1/km-*.1 - pip3 uninstall keyman_config - rm -f /usr/local/bin/km-config - rm -f /usr/local/bin/km-kvk2ldml - rm -f /usr/local/bin/km-package-get - rm -f /usr/local/bin/km-package-install - rm -f /usr/local/bin/km-package-list-installed - rm -f /usr/local/bin/km-package-uninstall +default: + ./build.sh clean build + +install: + ./build.sh install + +uninstall: # run as sudo + ./build.sh uninstall clean: - -rm -rf dist make_deb build keyman_config/version.py *.egg-info __pycache__ \ - keyman_config/standards/lang_tags_map.py + ./build.sh clean + +check: + ./build.sh test devdist: version python3 setup.py egg_info -b.`TZ=UTC git log -1 --pretty=format:%cd --date=format-local:%Y%m%d%H%M` sdist @@ -86,6 +63,3 @@ $(MOFILES): %/LC_MESSAGES/keyman-config.mo: %.po msgfmt -v --check --output-file=$@ $^ compile-po: $(MOFILES) - -check: - ./run-tests.sh diff --git a/linux/keyman-config/build-help.sh b/linux/keyman-config/build-help.sh index 97f06ca3d0012649012eaa781a061f46000331ed..e05b8d284e4e90677ae22905c52de2e3fb769b9f 100755 --- a/linux/keyman-config/build-help.sh +++ b/linux/keyman-config/build-help.sh @@ -3,13 +3,11 @@ set -e set -u -## START STANDARD BUILD SCRIPT INCLUDE -# adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -## END STANDARD BUILD SCRIPT INCLUDE - THIS_DIR=$(dirname "$THIS_SCRIPT") +cd "${THIS_DIR}" + function display_usage { echo "Usage: $0 [--man] [--md] [--no-reconf]" echo " $0 --help" @@ -50,13 +48,13 @@ while [[ $# -gt 0 ]] ; do shift # past the processed argument done -if [ -z "$generate_man" -a -z "$generate_help" ]; then +if [ -z "$generate_man" ] && [ -z "$generate_help" ]; then generate_man=1 generate_help=1 fi if [ -n "$reconfigure" ]; then - pushd $THIS_DIR/.. > /dev/null + pushd "$THIS_DIR/.." > /dev/null ./scripts/reconf.sh popd > /dev/null fi diff --git a/linux/keyman-config/build.sh b/linux/keyman-config/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..4828dd5ec737389db387f8bf37275ed94aa7db7d --- /dev/null +++ b/linux/keyman-config/build.sh @@ -0,0 +1,123 @@ +#!/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/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +################################ Main script ################################ + +builder_describe \ + "Build keyman-config." \ + "clean" \ + "configure" \ + "build" \ + "test" \ + "install install artifacts" \ + "uninstall uninstall artifacts" \ + +builder_parse "$@" + +cd "$THIS_SCRIPT_PATH" + +builder_describe_outputs \ + build "/linux/keyman-config/keyman_config/standards/lang_tags_map.py" + +clean_action() { + rm -rf dist make_deb build keyman_config/version.py ./*.egg-info __pycache__ \ + keyman_config/standards/lang_tags_map.py +} + +execute_with_temp_schema() { + local TEMP_DATA_DIR SCHEMA_DIR + TEMP_DATA_DIR=$(mktemp -d) + SCHEMA_DIR="${TEMP_DATA_DIR}/glib-2.0/schemas" + export XDG_DATA_DIRS="${TEMP_DATA_DIR}":${XDG_DATA_DIRS-} + export GSETTINGS_SCHEMA_DIR="${SCHEMA_DIR}" + mkdir -p "${SCHEMA_DIR}" + cp resources/com.keyman.gschema.xml "${SCHEMA_DIR}"/ + glib-compile-schemas "${SCHEMA_DIR}" + "$@" + export XDG_DATA_DIRS=${XDG_DATA_DIRS#*:} + unset GSETTINGS_SCHEMA_DIR + rm -rf "${TEMP_DATA_DIR}" +} + +build_man_and_help_pages() { + execute_with_temp_schema ./build-help.sh --no-reconf +} + +build_action() { + builder_echo "Create version.py" + pushd keyman_config + sed \ + -e "s/_VERSION_/${VERSION}/g" \ + -e "s/_VERSIONWITHTAG_/${VERSION_WITH_TAG}/g" \ + -e "s/_VERSIONGITTAG_/${VERSION_GIT_TAG}/g" \ + -e "s/_MAJORVERSION_/${VERSION_MAJOR}/g" \ + -e "s/_RELEASEVERSION_/${VERSION_RELEASE}/g" \ + -e "s/_TIER_/${TIER}/g" \ + -e "s/_ENVIRONMENT_/${VERSION_ENVIRONMENT}/g" \ + -e "s/_UPLOADSENTRY_/${UPLOAD_SENTRY}/g" \ + version.py.in > version.py + popd + pushd buildtools + if [ -f build-langtags.py ]; then + builder_echo "Create lang_tags_map.py" + python3 ./build-langtags.py + else + builder_echo "Skip building lang_tags_map.py during package build" + fi + popd + builder_echo "Building man and help pages" + build_man_and_help_pages + builder_echo "Building keyman-config" + python3 setup.py build +} + +test_action() { + execute_with_temp_schema ./run-tests.sh +} + +install_action() { + if [ -n "${SUDO_USER:-}" ]; then + # with sudo install into /usr/local + pip3 install qrcode sentry-sdk + # eventually change this to: pip3 install . + python3 setup.py install + # install icons + mkdir -p /usr/local/share/keyman/icons + cp keyman_config/icons/* /usr/local/share/keyman/icons + # install man pages + mkdir -p /usr/local/share/man/man1 + cp ../../debian/man/*.1 /usr/local/share/man/man1 + else + # without sudo install into /tmp/keyman (or $DESTDIR) + mkdir -p "/tmp/keyman/$(python3 -c 'import sys;import os;pythonver="python%d.%d" % (sys.version_info[0], sys.version_info[1]);sitedir = os.path.join("lib", pythonver, "site-packages");print(sitedir)')" + # when we no longer have to support old pip version (python > 3.6) change this to: + # pip3 install --prefix /tmp/keyman . + PYTHONUSERBASE=${DESTDIR:-/tmp/keyman} python3 setup.py install --user + fi +} + +uninstall_action() { + # run as sudo + clean_action + rm -rf /usr/local/share/keyman/icons + rm -f /usr/local/share/man/man1/km-*.1 + pip3 uninstall keyman_config + rm -f /usr/local/bin/km-config + rm -f /usr/local/bin/km-kvk2ldml + rm -f /usr/local/bin/km-package-get + rm -f /usr/local/bin/km-package-install + rm -f /usr/local/bin/km-package-list-installed + rm -f /usr/local/bin/km-package-uninstall +} + +builder_run_action clean clean_action +builder_run_action configure # nothing to do +builder_run_action build build_action +builder_run_action test test_action +builder_run_action install install_action +builder_run_action uninstall uninstall_action diff --git a/linux/keyman-config/createkeymandirs.sh b/linux/keyman-config/createkeymandirs.sh deleted file mode 100755 index d9befe7f5d505992f4ed0383abb2a0e587a7b5fa..0000000000000000000000000000000000000000 --- a/linux/keyman-config/createkeymandirs.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -sudo mkdir -p /usr/local/share/keyman -sudo mkdir -p /usr/local/share/fonts/keyman -sudo mkdir -p /usr/local/share/doc/keyman -sudo chmod 00777 /usr/local/share/keyman -sudo chmod 00777 /usr/local/share/fonts/keyman -sudo chmod 00777 /usr/local/share/doc/keyman diff --git a/linux/keyman-config/keyman_config/__init__.py b/linux/keyman-config/keyman_config/__init__.py index e5cecf4cc82d6b6ae333dab67dd5aa615454b3e2..04098ade00fcbeeed91a9f7b5ee63070780d60af 100644 --- a/linux/keyman-config/keyman_config/__init__.py +++ b/linux/keyman-config/keyman_config/__init__.py @@ -1,20 +1,18 @@ -import getpass import gettext -import importlib -import logging -import os -import platform -import sys -from .version import __version__ -from .version import __versionwithtag__ -from .version import __versiongittag__ -from .version import __majorversion__ -from .version import __releaseversion__ -from .version import __tier__ -from .version import __pkgversion__ -from .version import __environment__ -from .version import __uploadsentry__ +from keyman_config.sentry_handling import SentryErrorHandling + +from keyman_config.version import ( + __version__, + __versionwithtag__, + __versiongittag__, + __majorversion__, + __releaseversion__, + __tier__, + __pkgversion__, + __environment__, + __uploadsentry__ +) def _(txt): @@ -24,7 +22,7 @@ def _(txt): return translation -def secure_lookup(data, key1, key2 = None): +def secure_lookup(data, key1, key2=None): """ Return data[key1][key2] while dealing with data being None or key1 or key2 not existing """ @@ -38,18 +36,10 @@ def secure_lookup(data, key1, key2 = None): return None -def before_send(event, hint): - if 'exc_info' in hint: - exc_type, exc_value, tb = hint['exc_info'] - if isinstance(exc_value, KeyboardInterrupt): - # Ignore KeyboardInterrupt exception - return None - return event - - gettext.bindtextdomain('keyman-config', '/usr/share/locale') gettext.textdomain('keyman-config') + #if __tier__ == 'alpha' or __tier__ == 'beta': // #7227 disabling: # Alpha and beta versions will work against the staging server so that they # can access new APIs etc that will only be available there. The staging @@ -60,60 +50,8 @@ gettext.textdomain('keyman-config') KeymanComUrl = 'https://keyman.com' KeymanApiUrl = 'https://api.keyman.com' + # There's no staging site for downloads KeymanDownloadsUrl = 'https://downloads.keyman.com' -if 'unittest' in sys.modules.keys(): - print('Not reporting to Sentry', file=sys.stderr) -elif os.environ.get('KEYMAN_NOSENTRY'): - print('Not reporting to Sentry because KEYMAN_NOSENTRY environment variable set', file=sys.stderr) -elif not __uploadsentry__: - print('Not reporting to Sentry because UPLOAD_SENTRY is false (%s)' % __environment__, file=sys.stderr) -else: - try: - # Try new sentry-sdk first - sentry_sdk = importlib.import_module('sentry_sdk') - from sentry_sdk import configure_scope, set_user - from sentry_sdk.integrations.logging import LoggingIntegration - HaveSentryNewSdk = True - - sentry_logging = LoggingIntegration( - level=logging.INFO, # Capture info and above as breadcrumbs - event_level=logging.CRITICAL # Send critical errors as events - ) - SentryUrl = "https://1d0edbf2d0dc411b87119b6e92e2c357@o1005580.ingest.sentry.io/5983525" - sentry_sdk.init( - dsn=SentryUrl, - environment=__environment__, - release=__versiongittag__, - integrations=[sentry_logging], - before_send=before_send - ) - set_user({'id': hash(getpass.getuser())}) - with configure_scope() as scope: - scope.set_tag("app", os.path.basename(sys.argv[0])) - scope.set_tag("pkgversion", __pkgversion__) - scope.set_tag("platform", platform.platform()) - scope.set_tag("system", platform.system()) - scope.set_tag("tier", __tier__) - except ImportError: - try: - # sentry-sdk is not available, so use older raven - raven = importlib.import_module('raven') - from raven import Client - HaveSentryNewSdk = False - - # Note, legacy raven API requires secret (https://github.com/keymanapp/keyman/pull/5787#discussion_r721457909) - SentryUrl = "https://1d0edbf2d0dc411b87119b6e92e2c357:e6d5a81ee6944fc79bd9f0cbb1f2c2a4@o1005580.ingest.sentry.io/5983525" - client = Client(SentryUrl, environment=__environment__, release=__versiongittag__) - client.user_context({'id': hash(getpass.getuser())}) - client.tags_context({ - 'app': os.path.basename(sys.argv[0]), - 'pkgversion': __pkgversion__, - 'platform': platform.platform(), - 'system': platform.system(), - 'tier': __tier__, - }) - except ImportError: - # even raven is not available. This is the case on Ubuntu 16.04. Just ignore. - print(_('Neither sentry-sdk nor raven is available. Not enabling Sentry error reporting.'), file=sys.stderr) +SentryErrorHandling().initialize_sentry() diff --git a/linux/keyman-config/keyman_config/dconf_util.py b/linux/keyman-config/keyman_config/dconf_util.py index 09b95d1815a5f42021674cba68b40a6c2f7887fa..c28ec3fd19c5aa7b6a126c020caadb3412890f3b 100644 --- a/linux/keyman-config/keyman_config/dconf_util.py +++ b/linux/keyman-config/keyman_config/dconf_util.py @@ -2,25 +2,25 @@ from gi.repository import Gio -# DConf path destkop/ibus/keyman/options -DCONF_BASE = "com.keyman.options" +# GSettings path destkop/ibus/keyman/options +GSETTINGS_BASE = "com.keyman.options" -# Utilities to get and set Keyman options in DConf: +# Utilities to get and set Keyman options in GSettings: # /desktop/ibus/keyman/options/packageID/keyboardID/options def get_child_schema(info): - settings = Gio.Settings.new(DCONF_BASE) + settings = Gio.Settings.new(GSETTINGS_BASE) path = settings.get_property('path') if not path.endswith('/'): path += '/' path += info['packageID'] + '/' + info['keyboardID'] + '/' - return Gio.Settings(DCONF_BASE + '.child', path) + return Gio.Settings(GSETTINGS_BASE + '.child', path) def get_option(info): """ - Get the Keyman keyboard options from DConf + Get the Keyman keyboard options from GSettings Convert from list of comma-separated strings into dictionary Args: @@ -41,7 +41,7 @@ def get_option(info): def set_option(info, options): """ - Store the Keyman keyboard options in DConf as a list of strings + Store the Keyman keyboard options in GSettings as a list of strings Args: info: dictionary diff --git a/linux/keyman-config/keyman_config/get_kmp.py b/linux/keyman-config/keyman_config/get_kmp.py index af7b34063d3336fd2c451c4ddddbdcf4073a1570..ed3f7ec8a444fdc178238f495f9ef29ca05f41f9 100755 --- a/linux/keyman-config/keyman_config/get_kmp.py +++ b/linux/keyman-config/keyman_config/get_kmp.py @@ -9,6 +9,7 @@ import requests import requests_cache from gi.repository import GObject +from keyman_config import _ from keyman_config import KeymanApiUrl, KeymanDownloadsUrl from keyman_config.deprecated_decorator import deprecated @@ -20,6 +21,16 @@ class InstallLocation(GObject.GEnum): Unknown = 99 +def get_install_area_string(area): + if area == InstallLocation.OS: + return _('System') + elif area == InstallLocation.Shared: + return _('Shared') + elif area == InstallLocation.User: + return _('User') + return _('Unknown') + + def get_package_download_data(packageID, weekCache=False): """ Get package download data from keyboards download api. diff --git a/linux/keyman-config/keyman_config/sentry_handling.py b/linux/keyman-config/keyman_config/sentry_handling.py new file mode 100644 index 0000000000000000000000000000000000000000..f2b2f8adc3441b83ced5dc75879f06cbea06310f --- /dev/null +++ b/linux/keyman-config/keyman_config/sentry_handling.py @@ -0,0 +1,152 @@ +#!/usr/bin/python3 +import getpass +import importlib +import logging +import os +import platform +import sys +from keyman_config.gsettings import GSettings +from keyman_config.version import ( + __version__, + __versionwithtag__, + __versiongittag__, + __majorversion__, + __releaseversion__, + __tier__, + __pkgversion__, + __environment__, + __uploadsentry__ +) + +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gio, Gtk + + +class SentryErrorHandling: + def __init__(self) -> None: + self.settings = Gio.Settings.new('com.keyman.options') + + def initialize_sentry(self): + (enabled, reason) = self.is_sentry_enabled() + if not enabled: + print(reason, file=sys.stderr) + logging.info(reason) + return (enabled, reason) + else: + try: + self._sentry_sdk_initialize() + except ImportError: + try: + self._raven_initialize() + except ImportError: + # even raven is not available. This is the case on Ubuntu 16.04. Just ignore. + print(_('Neither sentry-sdk nor raven is available. Not enabling Sentry error reporting.'), + file=sys.stderr) + logging.info('Neither sentry-sdk nor raven is available. Not enabling Sentry error reporting.') + return (False, _('Neither sentry-sdk nor raven is available. Not enabling Sentry error reporting.')) + return (True, '') + + def is_sentry_enabled(self): + if 'unittest' in sys.modules.keys(): + return (False, 'Running unit tests, not reporting to Sentry') + elif self._get_environ_nosentry(): + return (False, 'Not reporting to Sentry because KEYMAN_NOSENTRY environment variable set') + elif not __uploadsentry__: + return (False, f'Not reporting to Sentry because UPLOAD_SENTRY is false ({__environment__})') + elif not self._get_setting(): + return (False, 'Not reporting to Sentry because disabled in GSettings') + return (True, 'Reporting to Sentry') + + def is_sentry_disabled_by_variable(self): + return self._get_environ_nosentry() or not __uploadsentry__ + + def bind_checkbutton(self, button: Gtk.CheckButton): + self.settings.bind("error-reporting", button, "active", Gio.SettingsBindFlags.NO_SENSITIVITY) + self.settings.connect("changed::error-reporting", self._on_sentry_reporting_toggled) + + def set_enabled(self, enabled): + assert not self.is_sentry_disabled_by_variable() + was_enabled = self.is_sentry_enabled() + self._save_setting(enabled) + if enabled != was_enabled: + self._handle_enabled(enabled) + + def _get_environ_nosentry(self): + keyman_nosentry = os.environ.get('KEYMAN_NOSENTRY') + return keyman_nosentry and (int(keyman_nosentry) == 1) + + def _handle_enabled(self, enabled): + if enabled: + self.initialize_sentry() + else: + self._close_sentry() + + def _save_setting(self, enabled: bool): + self.settings.set_boolean('error-reporting', enabled) + + def _get_setting(self) -> bool: + return self.settings.get_boolean('error-reporting') + + def _on_sentry_reporting_toggled(self, settings, key): + self._handle_enabled(self.settings.get_boolean('error-reporting')) + + def _close_sentry(self): + from sentry_sdk import Hub + logging.info("Shutting down Sentry error reporting") + client = Hub.current.client + if client is not None: + client.close(timeout=2.0) + + def _sentry_sdk_initialize(self): + # Try new sentry-sdk first + sentry_sdk = importlib.import_module('sentry_sdk') + from sentry_sdk import configure_scope, set_user + from sentry_sdk.integrations.logging import LoggingIntegration + + sentry_logging = LoggingIntegration( + level=logging.INFO, # Capture info and above as breadcrumbs + event_level=logging.CRITICAL # Send critical errors as events + ) + SentryUrl = "https://1d0edbf2d0dc411b87119b6e92e2c357@o1005580.ingest.sentry.io/5983525" + sentry_sdk.init( + dsn=SentryUrl, + environment=__environment__, + release=__versiongittag__, + integrations=[sentry_logging], + before_send=self._before_send + ) + set_user({'id': hash(getpass.getuser())}) + with configure_scope() as scope: + scope.set_tag("app", os.path.basename(sys.argv[0])) + scope.set_tag("pkgversion", __pkgversion__) + scope.set_tag("platform", platform.platform()) + scope.set_tag("system", platform.system()) + scope.set_tag("tier", __tier__) + logging.info("Initialized Sentry error reporting") + + def _raven_initialize(self): + # sentry-sdk is not available, so use older raven + raven = importlib.import_module('raven') + from raven import Client + + # Note, legacy raven API requires secret (https://github.com/keymanapp/keyman/pull/5787#discussion_r721457909) + SentryUrl = "https://1d0edbf2d0dc411b87119b6e92e2c357:e6d5a81ee6944fc79bd9f0cbb1f2c2a4@o1005580.ingest.sentry.io/5983525" + client = Client(SentryUrl, environment=__environment__, release=__versiongittag__) + client.user_context({'id': hash(getpass.getuser())}) + client.tags_context({ + 'app': os.path.basename(sys.argv[0]), + 'pkgversion': __pkgversion__, + 'platform': platform.platform(), + 'system': platform.system(), + 'tier': __tier__, + }) + logging.info("Initialized Sentry error reporting (raven)") + + def _before_send(self, event, hint): + if 'exc_info' in hint: + exc_type, exc_value, tb = hint['exc_info'] + if isinstance(exc_value, KeyboardInterrupt): + # Ignore KeyboardInterrupt exception + return None + return event diff --git a/linux/keyman-config/keyman_config/view_installed.py b/linux/keyman-config/keyman_config/view_installed.py index 01b1a59b89b3e413619c87abd56c479667abeab2..263d665d25f8ec89ef1694185015fad5e7f2d9b4 100755 --- a/linux/keyman-config/keyman_config/view_installed.py +++ b/linux/keyman-config/keyman_config/view_installed.py @@ -19,12 +19,13 @@ from keyman_config.accelerators import bind_accelerator, init_accel from keyman_config.dbus_util import get_keyman_config_service from keyman_config.downloadkeyboard import DownloadKmpWindow from keyman_config.get_kmp import (InstallLocation, get_keyboard_dir, - get_keyman_dir) + get_keyman_dir, get_install_area_string) from keyman_config.install_window import InstallKmpWindow, find_keyman_image from keyman_config.keyboard_details import KeyboardDetailsView from keyman_config.kmpmetadata import get_fonts, parsemetadata from keyman_config.list_installed_kmp import get_installed_kmp from keyman_config.options import OptionsView +from keyman_config.sentry_handling import SentryErrorHandling from keyman_config.uninstall_kmp import uninstall_kmp from keyman_config.welcome import WelcomeView @@ -94,7 +95,7 @@ class ViewInstalledWindowBase(Gtk.Window): self.close() def run(self): - self.resize(576, 324) + self.resize(776, 424) self.connect("destroy", Gtk.main_quit) self.show_all() Gtk.main() @@ -104,26 +105,60 @@ class ViewInstalledWindow(ViewInstalledWindowBase): def __init__(self): ViewInstalledWindowBase.__init__(self) -# window is split left/right hbox -# right is ButtonBox -# possibly 2 ButtonBox in a vbox -# top one with _Remove, _About, ?_Welcome? or ?Read_Me?, _Options -# bottom one with _Download, _Install, Re_fresh, _Close -# left is GtkTreeView - does it need to be inside anything else apart from the hbox? -# with liststore which defines columns -# GdkPixbuf icon -# gchararray name -# gchararray version -# gchararray packageID (hidden) -# enum? area (user, shared, system) (icon or hidden?) -# gchararray welcomefile (hidden) (or just use area and packageID?) -# changing selected item in treeview changes what buttons are activated -# on selected_item_changed signal set the data that the buttons will use in their callbacks -# see https://developer.gnome.org/gtk3/stable/TreeWidget.html#TreeWidget + self.sentry = SentryErrorHandling() + outerHbox = Gtk.HBox() + sidebar = Gtk.StackSidebar() + stack = Gtk.Stack() + + outerHbox.pack_start(sidebar, False, False, 0) + outerHbox.pack_end(Gtk.Separator(), False, False, 0) + outerHbox.pack_end(stack, True, True, 0) + stack.set_hexpand(True) + stack.set_vexpand(True) + sidebar.set_stack(stack) + + stack.add_titled(self.add_keyboard_layouts_widget(), "KeyboardLayouts", _("Keyboard Layouts")) + stack.add_titled(self.add_options_widget(), "Options", _("Options")) + + outmostVBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + outmostVBox.pack_start(outerHbox, True, True, 0) + + bbox_bottom = Gtk.ButtonBox(spacing=12, orientation=Gtk.Orientation.HORIZONTAL) + bbox_bottom.set_layout(Gtk.ButtonBoxStyle.START) + + button = Gtk.Button.new_with_mnemonic(_("_Install keyboard...")) + button.set_tooltip_text(_("Install a keyboard from a file")) + button.connect("clicked", self.on_installfile_clicked) + bbox_bottom.add(button) + + button = Gtk.Button.new_with_mnemonic(_("_Download keyboard...")) + button.set_tooltip_text(_("Download and install a keyboard from the Keyman website")) + button.connect("clicked", self.on_download_clicked) + bbox_bottom.add(button) + + button = Gtk.Button.new_with_mnemonic(_("_Refresh")) + button.set_tooltip_text(_("Refresh keyboard list")) + button.connect("clicked", self.on_refresh_clicked) + bbox_bottom.add(button) + + button = Gtk.Button.new_with_mnemonic(_("_Close")) + button.set_tooltip_text(_("Close window")) + button.connect("clicked", self.on_close_clicked) + bind_accelerator(self.accelerators, button, 'q') + bind_accelerator(self.accelerators, button, 'w') + bbox_bottom.add(button) + + bbox_hbox = Gtk.HBox(spacing=12) + bbox_hbox.pack_start(bbox_bottom, True, True, 12) + outmostVBox.pack_end(bbox_hbox, False, True, 12) + self.add(outmostVBox) + + def add_keyboard_layouts_widget(self): hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - s = Gtk.ScrolledWindow() - hbox.pack_start(s, True, True, 0) + + scrolledWindow = Gtk.ScrolledWindow() + hbox.pack_start(scrolledWindow, True, True, 0) self.store = Gtk.ListStore( GdkPixbuf.Pixbuf, # icon @@ -131,15 +166,11 @@ class ViewInstalledWindow(ViewInstalledWindowBase): str, # version str, # packageID int, # enum InstallLocation (KmpArea is GObject version) + str, # InstallLocation area + str, # Tooltip with InstallLocation path str, # path to welcome file if it exists or None str) # path to options file if it exists or None - # add installed keyboards to the the store e.g. - # treeiter = store.append([GdkPixbuf.Pixbuf.new_from_file_at_size( - # "/usr/local/share/keyman/libtralo/libtralo.ico.png", 16, 16), \ - # "LIBTRALO", "1.6.1", \ - # "libtralo", KmpArea.SHARED, True]) - self.refresh_installed_kmp() self.tree = Gtk.TreeView(self.store) @@ -155,11 +186,16 @@ class ViewInstalledWindow(ViewInstalledWindowBase): # i18n: column header in table displaying installed keyboards column = Gtk.TreeViewColumn(_("Version"), renderer, text=2) self.tree.append_column(column) + # i18n: column header in table displaying installed keyboards + column = Gtk.TreeViewColumn(_("Area"), renderer, text=5) + self.tree.append_column(column) + + self.tree.set_tooltip_column(column=6) select = self.tree.get_selection() select.connect("changed", self.on_tree_selection_changed) - s.add(self.tree) + scrolledWindow.add(self.tree) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12) @@ -191,36 +227,34 @@ class ViewInstalledWindow(ViewInstalledWindowBase): bbox_top.add(self.options_button) vbox.pack_start(bbox_top, False, False, 12) + hbox.pack_start(vbox, False, False, 12) - bbox_bottom = Gtk.ButtonBox(spacing=12, orientation=Gtk.Orientation.VERTICAL) - bbox_bottom.set_layout(Gtk.ButtonBoxStyle.END) + return hbox - button = Gtk.Button.new_with_mnemonic(_("_Refresh")) - button.set_tooltip_text(_("Refresh keyboard list")) - button.connect("clicked", self.on_refresh_clicked) - bbox_bottom.add(button) + def add_options_widget(self): + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + label = Gtk.Label(_("General")) + label.set_padding(5, 5) + label.set_halign(Gtk.Align.START) + vbox.pack_start(label, False, False, 10) - button = Gtk.Button.new_with_mnemonic(_("_Download")) - button.set_tooltip_text(_("Download and install a keyboard from the Keyman website")) - button.connect("clicked", self.on_download_clicked) - bbox_bottom.add(button) - - button = Gtk.Button.new_with_mnemonic(_("_Install")) - button.set_tooltip_text(_("Install a keyboard from a file")) - button.connect("clicked", self.on_installfile_clicked) - bbox_bottom.add(button) + (enabled, reason) = self.sentry.is_sentry_enabled() + disabledByVariable = self.sentry.is_sentry_disabled_by_variable() - button = Gtk.Button.new_with_mnemonic(_("_Close")) - button.set_tooltip_text(_("Close window")) - button.connect("clicked", self.on_close_clicked) - bind_accelerator(self.accelerators, button, 'q') - bind_accelerator(self.accelerators, button, 'w') - bbox_bottom.add(button) + self.errorReportingButton = Gtk.CheckButton(_("Automatically report errors to keyman.com")) + self.errorReportingButton.set_active(enabled) + self.errorReportingButton.set_sensitive(not disabledByVariable) + self.sentry.bind_checkbutton(self.errorReportingButton) + vbox.pack_start(self.errorReportingButton, False, False, 0) - vbox.pack_end(bbox_bottom, False, False, 12) + if disabledByVariable: + label = Gtk.Label(reason) + label.set_halign(Gtk.Align.START) + label.set_padding(25, 0) + label.set_sensitive(False) + vbox.pack_start(label, False, False, 0) - hbox.pack_start(vbox, False, False, 12) - self.add(hbox) + return vbox def addlistitems(self, installed_kmp, store, install_area): for kmp in sorted(installed_kmp): @@ -255,6 +289,8 @@ class ViewInstalledWindow(ViewInstalledWindowBase): kmpdata['version'], kmpdata['packageID'], install_area, + get_install_area_string(install_area), + path.replace(os.path.expanduser('~'), '~'), welcome_file, options_file]) diff --git a/linux/keyman-config/km-config.bash-completion b/linux/keyman-config/km-config.bash-completion index b1f46c0076f7522383eb66c51342b9b380f30ed5..fd0eee394c77b5bc89154870136e539e5aeaa925 100644 --- a/linux/keyman-config/km-config.bash-completion +++ b/linux/keyman-config/km-config.bash-completion @@ -1,4 +1,5 @@ -#!/usr/bin/env bash +# shellcheck disable=SC2148 +# No hashbang for bash completion scripts! They are intended to be sourced, not executed. _km-config_completions() { diff --git a/linux/keyman-config/km-kvk2ldml.bash-completion b/linux/keyman-config/km-kvk2ldml.bash-completion index 3eb2950c390659f1ce058c66b06243e42713d36d..1e8231a1a1fb37044f5fba26951212c0db7292f2 100644 --- a/linux/keyman-config/km-kvk2ldml.bash-completion +++ b/linux/keyman-config/km-kvk2ldml.bash-completion @@ -1,4 +1,5 @@ -#!/usr/bin/env bash +# shellcheck disable=SC2148 +# No hashbang for bash completion scripts! They are intended to be sourced, not executed. _km-kvk2ldml_completions() { diff --git a/linux/keyman-config/km-package-get.bash-completion b/linux/keyman-config/km-package-get.bash-completion index 9bacb1b8fef29c8b7875131a0fd487a4919f0115..0fb3a4d14c76b1600d3f951ff0c44fac8af2df94 100644 --- a/linux/keyman-config/km-package-get.bash-completion +++ b/linux/keyman-config/km-package-get.bash-completion @@ -1,4 +1,5 @@ -#!/usr/bin/env bash +# shellcheck disable=SC2148 +# No hashbang for bash completion scripts! They are intended to be sourced, not executed. _km-package-get_completions() { diff --git a/linux/keyman-config/km-package-install.bash-completion b/linux/keyman-config/km-package-install.bash-completion index 1a424e8ea6f80c9bb66b97923a38562b33dd573d..06bfeb6aa5cf688303550bef2f516324cf9710bf 100644 --- a/linux/keyman-config/km-package-install.bash-completion +++ b/linux/keyman-config/km-package-install.bash-completion @@ -1,4 +1,5 @@ -#!/usr/bin/env bash +# shellcheck disable=SC2148 +# No hashbang for bash completion scripts! They are intended to be sourced, not executed. _km-package-install_completions() { diff --git a/linux/keyman-config/km-package-list-installed.bash-completion b/linux/keyman-config/km-package-list-installed.bash-completion index a3758b467eb8958d0af62dc48148249d9a69f0b5..672353b9a13a86838627bf067bf69af559cba584 100644 --- a/linux/keyman-config/km-package-list-installed.bash-completion +++ b/linux/keyman-config/km-package-list-installed.bash-completion @@ -1,4 +1,5 @@ -#!/usr/bin/env bash +# shellcheck disable=SC2148 +# No hashbang for bash completion scripts! They are intended to be sourced, not executed. _km-package-list-installed_completions() { diff --git a/linux/keyman-config/km-package-uninstall.bash-completion b/linux/keyman-config/km-package-uninstall.bash-completion index 191e709eabaf2344951ff70f1919911f8ff1b6ed..4b81c38dacc9eaa9c0ec67e19415db9b5fb56983 100644 --- a/linux/keyman-config/km-package-uninstall.bash-completion +++ b/linux/keyman-config/km-package-uninstall.bash-completion @@ -1,4 +1,5 @@ -#!/usr/bin/env bash +# shellcheck disable=SC2148 +# No hashbang for bash completion scripts! They are intended to be sourced, not executed. _km-package-uninstall_completions() { diff --git a/linux/keyman-config/com.keyman.gschema.xml b/linux/keyman-config/resources/com.keyman.gschema.xml similarity index 64% rename from linux/keyman-config/com.keyman.gschema.xml rename to linux/keyman-config/resources/com.keyman.gschema.xml index 8f05a42bf9b0c70d4bc2ee9ffdb6b3ec6716c9c3..df50060d8c44eb97e2ec4e8d4848ab23c34ef665 100644 --- a/linux/keyman-config/com.keyman.gschema.xml +++ b/linux/keyman-config/resources/com.keyman.gschema.xml @@ -1,8 +1,12 @@ - + + true + Automatic error reporting + true to enable automatic reporting of errors to Sentry + diff --git a/linux/debian/keyman.sharedmimeinfo b/linux/keyman-config/resources/keyman.sharedmimeinfo similarity index 100% rename from linux/debian/keyman.sharedmimeinfo rename to linux/keyman-config/resources/keyman.sharedmimeinfo diff --git a/linux/keyman-config/km-config.desktop b/linux/keyman-config/resources/km-config.desktop similarity index 100% rename from linux/keyman-config/km-config.desktop rename to linux/keyman-config/resources/km-config.desktop diff --git a/linux/keyman-config/run-tests.sh b/linux/keyman-config/run-tests.sh index 3b8664512adc639aa376c6e6ed4b5da8e479713c..b390ce613bc589d926da09a8ca39c28ecd7c3a19 100755 --- a/linux/keyman-config/run-tests.sh +++ b/linux/keyman-config/run-tests.sh @@ -1,6 +1,9 @@ #!/bin/bash PYTHONPATH=.:$PYTHONPATH +XDG_CONFIG_HOME=$(mktemp --directory) +export XDG_CONFIG_HOME + if [ -n "$TEAMCITY_VERSION" ]; then if ! pip3 list --format=columns | grep -q teamcity-messages; then pip3 install teamcity-messages @@ -9,3 +12,5 @@ if [ -n "$TEAMCITY_VERSION" ]; then else python3 -m unittest discover -v -s tests -p test_*.py fi + +rm -rf "$XDG_CONFIG_HOME" diff --git a/linux/keyman-config/tests/test_gnome_keyboards_util.py b/linux/keyman-config/tests/test_gnome_keyboards_util.py index b5a0cac842fff324e018f25672c1b93817ef376b..a53908cdd3296aaee7dfa26b838ac0fe29042f67 100644 --- a/linux/keyman-config/tests/test_gnome_keyboards_util.py +++ b/linux/keyman-config/tests/test_gnome_keyboards_util.py @@ -9,14 +9,14 @@ class GnomeKeyboardsUtilTests(unittest.TestCase): def setUp(self): _reset_gnome_shell() - @patch('keyman_config.os.system') + @patch('os.system') def test_IsGnomeShell_RunningGnomeShell(self, mockSystem): # Setup mockSystem.return_value = 0 # Execute/Verify self.assertEqual(is_gnome_shell(), True) - @patch('keyman_config.os.system') + @patch('os.system') def test_IsGnomeShell_NotRunningGnomeShell(self, mockSystem): # Setup mockSystem.return_value = 1 diff --git a/linux/keyman-config/tests/test_install_kmp.py b/linux/keyman-config/tests/test_install_kmp.py index 2f250550cd3ecef1ea5f1f1a88debe4a292c6178..d1e10bd645b6d9aaa450bc9fed3cc64883b92b31 100644 --- a/linux/keyman-config/tests/test_install_kmp.py +++ b/linux/keyman-config/tests/test_install_kmp.py @@ -262,13 +262,11 @@ class InstallKmpTests(unittest.TestCase): def test_InstallKmp_FutureKeymanVersion(self): # Setup - workdir = tempfile.TemporaryDirectory().name - os.makedirs(workdir) - packagedir = tempfile.TemporaryDirectory().name - os.makedirs(packagedir) - self.mockGetKeyboardDir.return_value = packagedir - kmpfile = self._createEmptyKmp(workdir) - self._createKmpJson(packagedir, ', "fileVersion": "99.0"') + workdir = tempfile.TemporaryDirectory() + packagedir = tempfile.TemporaryDirectory() + self.mockGetKeyboardDir.return_value = packagedir.name + kmpfile = self._createEmptyKmp(workdir.name) + self._createKmpJson(packagedir.name, ', "fileVersion": "99.0"') # Execute with self.assertRaises(InstallError) as context: @@ -278,22 +276,24 @@ class InstallKmpTests(unittest.TestCase): self.assertTrue('foo.kmp requires Keyman 99.0 or higher' in context.exception.message) self.mockInstallToIbus.assert_not_called() + # Teardown + workdir.cleanup() + packagedir.cleanup() + def test_InstallKmp(self): for testcase in [ {'name': 'PreviousKeymanVersion', 'fileVersion': ', "fileVersion": "7.0"'}, - {'name': 'SameKeymanVersion', 'fileVersion': ', "fileVersion": "' + __version__ + '"'}, + {'name': 'SameKeymanVersion', 'fileVersion': f', "fileVersion": "{__version__}"'}, {'name': 'NoFileVersion', 'fileVersion': ''} ]: with self.subTest(msg=testcase['name'], data=testcase['fileVersion']): # Setup self.mockInstallToIbus.reset_mock() - workdir = tempfile.TemporaryDirectory().name - os.makedirs(workdir) - packagedir = tempfile.TemporaryDirectory().name - os.makedirs(packagedir) - self.mockGetKeyboardDir.return_value = packagedir - kmpfile = self._createEmptyKmp(workdir) - self._createKmpJson(packagedir, testcase['fileVersion']) + workdir = tempfile.TemporaryDirectory() + packagedir = tempfile.TemporaryDirectory() + self.mockGetKeyboardDir.return_value = packagedir.name + kmpfile = self._createEmptyKmp(workdir.name) + self._createKmpJson(packagedir.name, testcase['fileVersion']) # Execute InstallKmp()._install_kmp(kmpfile, 'km', InstallLocation.User) @@ -301,6 +301,10 @@ class InstallKmpTests(unittest.TestCase): # Verify self.mockInstallToIbus.assert_called_once() + # Teardown + workdir.cleanup() + packagedir.cleanup() + if __name__ == '__main__': unittest.main() diff --git a/linux/keyman-config/tests/test_package_install_completion.py b/linux/keyman-config/tests/test_package_install_completion.py index 97238e3dc2e57051c63d085d1692018267841f71..61374a6c66cbc5756e22b0c42e42810adf378b03 100644 --- a/linux/keyman-config/tests/test_package_install_completion.py +++ b/linux/keyman-config/tests/test_package_install_completion.py @@ -2,7 +2,7 @@ import os import tempfile import unittest -from unittest.mock import patch, ANY +from unittest.mock import patch from importlib.machinery import SourceFileLoader from importlib.util import module_from_spec, spec_from_loader @@ -10,17 +10,11 @@ from importlib.util import module_from_spec, spec_from_loader class PackageInstallCompletionTests(unittest.TestCase): def setUp(self): - patcher1 = patch('keyman_config.install_kmp.extract_kmp') - self.mockExtractKmp = patcher1.start() - self.addCleanup(patcher1.stop) - patcher2 = patch('keyman_config.kmpmetadata.get_metadata') - self.mockGetMetadata = patcher2.start() - self.addCleanup(patcher2.stop) - + self.mockExtractKmp = self._setupMock('keyman_config.install_kmp.extract_kmp') + self.mockGetMetadata = self._setupMock('keyman_config.kmpmetadata.get_metadata') loader = SourceFileLoader('km_package_install', os.path.join(os.path.dirname( os.path.abspath(__file__)), '../km-package-install')) - spec = spec_from_loader(loader.name, loader) - if spec: + if spec := spec_from_loader(loader.name, loader): self.mod = module_from_spec(spec) loader.exec_module(self.mod) @@ -29,32 +23,43 @@ class PackageInstallCompletionTests(unittest.TestCase): self.cacheDir = os.path.join(self.tempDir.name, 'keyman') os.makedirs(self.cacheDir) + def tearDown(self): + self.tempDir.cleanup() + + def _setupMock(self, arg0): + patcher = patch(arg0) + result = patcher.start() + self.addCleanup(patcher.stop) + return result + def _list_languages_for_keyboard_impl(self, packageId): return self.mod._list_languages_for_keyboard_impl(packageId, 'someDir') def test_PackageCompletionNoLanguage(self): - open(os.path.join(self.cacheDir, 'foo'), 'w') - self.mockGetMetadata.return_value = (None, None, None, [{}], None) - result = self._list_languages_for_keyboard_impl('foo') - self.assertEqual(result, "") + with open(os.path.join(self.cacheDir, 'foo'), 'w'): + self.mockGetMetadata.return_value = (None, None, None, [{}], None) + result = self._list_languages_for_keyboard_impl('foo') + self.assertEqual(result, "") def test_PackageCompletionOneLanguage(self): - open(os.path.join(self.cacheDir, 'khmer_angkor'), 'w') - self.mockGetMetadata.return_value = ( - None, None, None, - [{'languages': [{'name': 'Central Khmer (Khmer, Cambodia)', 'id': 'km'}]}], - None) - result = self._list_languages_for_keyboard_impl('khmer_angkor') - self.assertEqual(result, "km") + with open(os.path.join(self.cacheDir, 'khmer_angkor'), 'w'): + self.mockGetMetadata.return_value = ( + None, None, None, + [{'languages': [{'name': 'Central Khmer (Khmer, Cambodia)', 'id': 'km'}]}], + None + ) + result = self._list_languages_for_keyboard_impl('khmer_angkor') + self.assertEqual(result, "km") def test_PackageCompletionMultipleLanguages(self): - open(os.path.join(self.cacheDir, 'sil_euro_latin'), 'w') - self.mockGetMetadata.return_value = ( - None, None, None, - [{'languages': [ - {'name': 'English', 'id': 'en'}, - {'name': 'French', 'id': 'fr'}, - {'name': 'German', 'id': 'de'}]}], - None) - result = self._list_languages_for_keyboard_impl('sil_euro_latin') - self.assertEqual(result, 'en\nfr\nde') + with open(os.path.join(self.cacheDir, 'sil_euro_latin'), 'w'): + self.mockGetMetadata.return_value = ( + None, None, None, + [{'languages': [ + {'name': 'English', 'id': 'en'}, + {'name': 'French', 'id': 'fr'}, + {'name': 'German', 'id': 'de'}]}], + None + ) + result = self._list_languages_for_keyboard_impl('sil_euro_latin') + self.assertEqual(result, 'en\nfr\nde') diff --git a/linux/keyman-system-service/README.md b/linux/keyman-system-service/README.md new file mode 100644 index 0000000000000000000000000000000000000000..358004af8fccd109d83ae9c69c79993c7982c06c --- /dev/null +++ b/linux/keyman-system-service/README.md @@ -0,0 +1,28 @@ +# keyman-system-service + +A DBus system service that allows to access /dev/input/* devices +to toggle capslock and perform other keyboard related actions when +running under Wayland. + +See , +, +, +. + +## Notes + +- If we need to limit access to the interface to only Keyman, we can use + [polkit](https://www.freedesktop.org/software/polkit/docs/latest/polkit.8.html) + which allows to add a rule with an action that checks for the program. + +- to see the log file + + ```bash + journalctl -f -u systemd-keyman + ``` + +- to monitor method calls + + ```bash + sudo busctl monitor com.keyman.SystemService1 + ``` diff --git a/linux/keyman-system-service/build.sh b/linux/keyman-system-service/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..9b248c51130d69f2f01a3bdf0b55023f6cb30faf --- /dev/null +++ b/linux/keyman-system-service/build.sh @@ -0,0 +1,66 @@ +#!/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/build-utils.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +################################ Main script ################################ + +builder_describe \ + "Build keyman-system-service." \ + ":service" \ + "clean" \ + "configure" \ + "build" \ + "test" \ + "install install artifacts" \ + "uninstall uninstall artifacts" + +builder_parse "$@" + +cd "$THIS_SCRIPT_PATH/" + +if builder_is_debug_build; then + MESON_TARGET=debug + export CPPFLAGS=-DG_MESSAGES_DEBUG + export CFLAGS="-O0" + export CXXFLAGS="-O0" +else + MESON_TARGET=release +fi +MESON_PATH="build/$(uname -m)/$MESON_TARGET" + +builder_describe_outputs \ + configure "${MESON_PATH}/build.ninja" \ + build "${MESON_PATH}/src/keyman-system-service" + +builder_run_action clean rm -rf "$THIS_SCRIPT_PATH/build/" + +# shellcheck disable=SC2086 +builder_run_action configure meson setup "$MESON_PATH" --werror --buildtype $MESON_TARGET "${builder_extra_params[@]}" + +if builder_start_action build; then + cd "$MESON_PATH" + ninja + builder_finish_action success build +fi + +if builder_start_action test; then + cd "$MESON_PATH" + meson test --print-errorlogs $builder_verbose + builder_finish_action success test +fi + +if builder_start_action install; then + cd "$MESON_PATH" + ninja install + builder_finish_action success install +fi + +if builder_start_action uninstall; then + cd "$MESON_PATH" + ninja uninstall + builder_finish_action success uninstall +fi diff --git a/linux/keyman-system-service/man/keyman-system-service.1 b/linux/keyman-system-service/man/keyman-system-service.1 new file mode 100644 index 0000000000000000000000000000000000000000..b506bd1607aa99442aa832d60292240f3bdae57d --- /dev/null +++ b/linux/keyman-system-service/man/keyman-system-service.1 @@ -0,0 +1,10 @@ +.TH KEYMAN-SYSTEM-SERVICE "1" "May 2023" "keyman-system-service" "User Commands" +.SH NAME +keyman-system-service \- System service for Keyman +.SH DESCRIPTION +\&keyman\-system\-service +.TP +This command should not be run directly. It will be started as a DBUS +system service by systemd. +.SH "SEE ALSO" +.BR ibus-engine-keyman(1) diff --git a/linux/keyman-system-service/meson.build b/linux/keyman-system-service/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..596ba48bcd443f4afde80d691c3ab09e23deacbe --- /dev/null +++ b/linux/keyman-system-service/meson.build @@ -0,0 +1,11 @@ +project('keyman-system-service', 'c', 'cpp', + version: run_command('cat', '../../VERSION.md', check: true).stdout().strip(), + license: 'GPL-2+', + meson_version: '>=1.0') + +evdev = dependency('libevdev', version: '>= 1.9') +systemd = dependency('libsystemd') + +subdir('resources') +subdir('src') +subdir('tests') diff --git a/linux/keyman-system-service/resources/com.keyman.SystemService1.conf b/linux/keyman-system-service/resources/com.keyman.SystemService1.conf new file mode 100644 index 0000000000000000000000000000000000000000..e2c564a94bc0e467c0f91c966d58c4766dde488d --- /dev/null +++ b/linux/keyman-system-service/resources/com.keyman.SystemService1.conf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/linux/keyman-system-service/resources/com.keyman.SystemService1.service b/linux/keyman-system-service/resources/com.keyman.SystemService1.service new file mode 100644 index 0000000000000000000000000000000000000000..3966fb1bbcb9003cffa49efa358eabd5ef591ff6 --- /dev/null +++ b/linux/keyman-system-service/resources/com.keyman.SystemService1.service @@ -0,0 +1,7 @@ +# /usr/share/dbus-1/system-services/com.keyman.SystemService1.service + +[D-BUS Service] +Name=com.keyman.SystemService1 +Exec=/bin/false +User=root +SystemdService=systemd-keyman.service diff --git a/linux/keyman-system-service/resources/meson.build b/linux/keyman-system-service/resources/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..c28cb81e585a540a0ae407509aec37ecfabc522b --- /dev/null +++ b/linux/keyman-system-service/resources/meson.build @@ -0,0 +1,3 @@ +install_data('com.keyman.SystemService1.conf', install_dir: get_option('datadir') / 'dbus-1/system.d/') +install_data('com.keyman.SystemService1.service', install_dir: get_option('datadir') / 'dbus-1/system-services/') +install_data('systemd-keyman.service', install_dir: get_option('prefix') / 'lib/systemd/system/') diff --git a/linux/keyman-system-service/resources/systemd-keyman.service b/linux/keyman-system-service/resources/systemd-keyman.service new file mode 100644 index 0000000000000000000000000000000000000000..7cb67508a89076395ba0b1b7b9e16a3f04fad3de --- /dev/null +++ b/linux/keyman-system-service/resources/systemd-keyman.service @@ -0,0 +1,51 @@ +# systemd service file +# /usr/lib/systemd/system/systemd-keyman.service +# See https://stackoverflow.com/a/31725112 + +[Unit] +Description=Keyman System Service + +[Service] +Type=dbus +BusName=com.keyman.SystemService1 +ExecStart=/usr/libexec/keyman-system-service +Restart=on-failure + +# Filesystem lockdown +ProtectSystem=strict +ProtectKernelTunables=true +ProtectControlGroups=true +ReadWritePaths=/dev/input/ +PrivateTmp=true + +# Network +PrivateNetwork=true +IPAddressDeny=any +RestrictAddressFamilies=AF_UNIX + +# Execute Mappings +MemoryDenyWriteExecute=true + +# Modules +ProtectKernelModules=true + +# Real-time +RestrictRealtime=true + +# Privilege escalation +NoNewPrivileges=true + +# Capabilities +CapabilityBoundingSet= + +# System call interfaces +LockPersonality=yes +SystemCallArchitectures=native +SystemCallFilter=@system-service + +# Namespaces +PrivateUsers=yes +RestrictNamespaces=yes + +# Locked memory +LimitMEMLOCK=0 diff --git a/linux/keyman-system-service/src/KeyboardDevice.cpp b/linux/keyman-system-service/src/KeyboardDevice.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a744a96b8282e593b5175caa3e06a3e1d551241c --- /dev/null +++ b/linux/keyman-system-service/src/KeyboardDevice.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include "KeyboardDevice.h" + +using namespace std; + +KeyboardDevice::KeyboardDevice() +{ + dev = nullptr; + fd = -1; + hasCapsLockLed = -1; + debug = false; +} + +KeyboardDevice::~KeyboardDevice() +{ + if (dev) { + libevdev_free(dev); + } + if (fd != -1) { + close(fd); + } +} + +bool KeyboardDevice::Initialize(const char* name) +{ + string path("/dev/input/"); + path.append(name); + + fd = open(path.c_str(), O_RDWR); + if (fd < 0) { + std::cerr << "Failed to open device " << path << ": " << strerror(errno) << std::endl; + return false; + } + + int rc = libevdev_new_from_fd(fd, &dev); + if (rc < 0) { + std::cerr << "Failed to init libevdev for " << path << ": " << strerror(-rc) << std::endl; + close(fd); + fd = -1; + return false; + } + + return true; +} + +bool KeyboardDevice::HasCapsLockLed() +{ + if (hasCapsLockLed < 0) { + if (!libevdev_has_event_code(dev, EV_LED, LED_CAPSL)) { + hasCapsLockLed = 0; + if (debug) { + std::cerr << "Device '" << libevdev_get_name(dev) << "' doesn't have " + << libevdev_event_code_get_name(EV_LED, LED_CAPSL) << "." << std::endl; + } + } else { + hasCapsLockLed = 1; + } + } + return hasCapsLockLed == 1; +} + +void KeyboardDevice::SetCapsLockLed(bool on) +{ + if (!HasCapsLockLed()) { + return; + } + + int rc = libevdev_kernel_set_led_value(dev, LED_CAPSL, on ? LIBEVDEV_LED_ON : LIBEVDEV_LED_OFF); + if (rc != 0) { + std::cerr << "Failed to set LED " << libevdev_event_code_get_name(EV_LED, LED_CAPSL) + << ": " << strerror(-rc) << std::endl; + } +} + +bool KeyboardDevice::GetCapsLockLed() +{ + if (!HasCapsLockLed()) { + return false; + } + + int val = libevdev_get_event_value(dev, EV_LED, LED_CAPSL); + return val; +} diff --git a/linux/keyman-system-service/src/KeyboardDevice.h b/linux/keyman-system-service/src/KeyboardDevice.h new file mode 100644 index 0000000000000000000000000000000000000000..af9ce119f339db7fb2162a2f6e4a310f9330b15d --- /dev/null +++ b/linux/keyman-system-service/src/KeyboardDevice.h @@ -0,0 +1,25 @@ +#ifndef __KEYBOARDDEVICE_H__ +#define __KEYBOARDDEVICE_H__ + +#include + +class KeyboardDevice +{ + public: + KeyboardDevice(); + virtual ~KeyboardDevice(); + + bool Initialize(const char* name); + bool HasCapsLockLed(); + void SetCapsLockLed(bool on); + bool GetCapsLockLed(); + void SetDebug(bool on) { debug = on; } + + private: + struct libevdev *dev; + int fd; + int hasCapsLockLed; + bool debug; +}; + +#endif // __KEYBOARDDEVICE_H__ diff --git a/linux/keyman-system-service/src/KeymanSystemService.cpp b/linux/keyman-system-service/src/KeymanSystemService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..deceafd0c39193c3693c41ab54bdb10330f50107 --- /dev/null +++ b/linux/keyman-system-service/src/KeymanSystemService.cpp @@ -0,0 +1,195 @@ +// This file implements a service which will run on the system dbus, +// based on the sd-bus library, see +// https://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "KeymanSystemService.h" +#include "KeyboardDevice.h" + +#define KEYMAN_BUS_NAME "com.keyman.SystemService1" +#define KEYMAN_INTERFACE_NAME "com.keyman.SystemService1.System" +#define KEYMAN_OBJECT_PATH "/com/keyman/SystemService1/System" + +using namespace std; + +static int32_t +on_set_caps_lock_indicator( + sd_bus_message *msg, + void *user_data, + sd_bus_error *ret_error +) { + int32_t ret; + uint32_t state; + + *ret_error = SD_BUS_ERROR_NULL; + + ret = sd_bus_message_read_basic(msg, 'b', &state); + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to parse parameter: %s", strerror(-ret)); + return ret; + } + + KeymanSystemService *service = static_cast(user_data); + service->SetCapsLockIndicatorOnDevices(state); + return sd_bus_reply_method_return(msg, ""); +} + +static int32_t +on_get_caps_lock_indicator( + sd_bus_message *msg, + void *user_data, + sd_bus_error *ret_error +) { + uint32_t state; + + *ret_error = SD_BUS_ERROR_NULL; + + KeymanSystemService *service = static_cast(user_data); + state = service->GetCapsLockIndicatorOnDevices(); + return sd_bus_reply_method_return(msg, "b", state); +} + +static const sd_bus_vtable system_service_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("SetCapsLockIndicator", "b", "", on_set_caps_lock_indicator, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetCapsLockIndicator", "", "b", on_get_caps_lock_indicator, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END +}; + +KeymanSystemService::KeymanSystemService() +{ + int ret; + + GetKbdDevices(); + +#ifdef KEYMAN_TESTING + ret = sd_bus_open_user(&bus); +#else + ret = sd_bus_open_system(&bus); +#endif + + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to connect to system bus: %s", strerror(-ret)); + failed = true; + return; + } + + // Install the object + ret = sd_bus_add_object_vtable(bus, + &slot, + KEYMAN_OBJECT_PATH, + KEYMAN_INTERFACE_NAME, + system_service_vtable, + this); + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to issue method call: %s", strerror(-ret)); + failed = true; + return; + } + + // Take a well-known service name so that clients can find us + ret = sd_bus_request_name(bus, KEYMAN_BUS_NAME, 0); + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to acquire service name: %s", strerror(-ret)); + failed = true; + return; + } +} + +KeymanSystemService::~KeymanSystemService() +{ + if (slot) sd_bus_slot_unref(slot); + if (bus) sd_bus_unref(bus); + + if (kbd_devices) { + for (KeyboardDevice *device : *kbd_devices) { + delete device; + } + delete kbd_devices; + } +} + +int KeymanSystemService::Loop() +{ + for (;;) { + // Process requests + int ret = sd_bus_process(bus, NULL); + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to process bus: %s", strerror(-ret)); + failed = true; + return ret; + } + if (ret > 0) { + // we processed a request, try to process another one, right-away + continue; + } + + // Wait for the next request to process + ret = sd_bus_wait(bus, (uint64_t)-1); + if (ret < 0) { + syslog(LOG_USER | LOG_NOTICE, "Failed to wait on bus: %s", strerror(-ret)); + failed = true; + return ret; + } + } + return 0; +} + +void KeymanSystemService::GetKbdDevices() { + kbd_devices = new std::list(); + + DIR *dirstream; + struct dirent *dir; + dirstream = opendir("/dev/input"); + if (dirstream) { + while ((dir = readdir(dirstream)) != NULL) { + // we're looking for a character device like `event1` + if (dir->d_type == DT_CHR && strncmp(dir->d_name, "event", 5) == 0) { + KeyboardDevice *kbdDevice = new KeyboardDevice(); + if (!kbdDevice->Initialize(dir->d_name) || !kbdDevice->HasCapsLockLed()) { + delete kbdDevice; + continue; + } + kbd_devices->push_back(kbdDevice); + } + } + closedir(dirstream); + } +} + +// Set the CapsLock indicator on all keyboard devices. +void +KeymanSystemService::SetCapsLockIndicatorOnDevices(uint32_t state) { + for (KeyboardDevice *kbdDevice : *kbd_devices) { + if (!kbdDevice->HasCapsLockLed()) { + syslog(LOG_USER | LOG_NOTICE, "%s: No kbdDevice or no CapsLockLed", __FUNCTION__); + continue; + } + syslog(LOG_USER | LOG_NOTICE, "%s: Caps Lock indicator: %s", __FUNCTION__, (state != 0 ? "ON" : "OFF")); + kbdDevice->SetCapsLockLed(state != 0); + } +} + +// Get the CapsLock indicator state from the list of keyboard devices. +// This will return true if any keyboard has the CapsLock indicator lit. +uint32_t +KeymanSystemService::GetCapsLockIndicatorOnDevices() { + bool state = false; + for (KeyboardDevice *kbdDevice : *kbd_devices) { + if (!kbdDevice->HasCapsLockLed()) { + syslog(LOG_USER | LOG_NOTICE, "%s: No kbdDevice or no CapsLockLed", __FUNCTION__); + continue; + } + state = state || kbdDevice->GetCapsLockLed(); + } + return state; +} diff --git a/linux/keyman-system-service/src/KeymanSystemService.h b/linux/keyman-system-service/src/KeymanSystemService.h new file mode 100644 index 0000000000000000000000000000000000000000..2686bfb99f95315ac28ca022ed846e9392e0444e --- /dev/null +++ b/linux/keyman-system-service/src/KeymanSystemService.h @@ -0,0 +1,33 @@ +#ifndef __KEYMANSYSTEMSERVICE_H__ +#define __KEYMANSYSTEMSERVICE_H__ + +#include +#include +#include "KeyboardDevice.h" + +using namespace std; + +class KeymanSystemService { +private: + std::list* kbd_devices = NULL; + sd_bus_slot *slot = NULL; + sd_bus *bus = NULL; + bool failed = false; + + void GetKbdDevices(); + +public: + KeymanSystemService(); + ~KeymanSystemService(); + + bool + Failed() { + return failed; + } + + int Loop(); + void SetCapsLockIndicatorOnDevices(uint32_t state); + uint32_t GetCapsLockIndicatorOnDevices(); +}; + +#endif // __KEYMANSYSTEMSERVICE_H__ diff --git a/linux/keyman-system-service/src/com.keyman.SystemService1.System.xml b/linux/keyman-system-service/src/com.keyman.SystemService1.System.xml new file mode 100644 index 0000000000000000000000000000000000000000..b75a1f23904be79d7483b5fa10cae0f03acb9b96 --- /dev/null +++ b/linux/keyman-system-service/src/com.keyman.SystemService1.System.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + diff --git a/linux/keyman-system-service/src/main.cpp b/linux/keyman-system-service/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a2bb2d0de0e11f8498c87c9882b45daf71271d5 --- /dev/null +++ b/linux/keyman-system-service/src/main.cpp @@ -0,0 +1,10 @@ +#include "KeymanSystemService.h" + +int main(int argc, char *argv[]) { + KeymanSystemService dbusService; + if (dbusService.Failed()) { + return 1; + } + + return dbusService.Loop(); +} diff --git a/linux/keyman-system-service/src/meson.build b/linux/keyman-system-service/src/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..51820ab9fa9eecf70f975c7e731b1373d34251b7 --- /dev/null +++ b/linux/keyman-system-service/src/meson.build @@ -0,0 +1,17 @@ +service_files = files( + 'KeyboardDevice.cpp', + 'KeymanSystemService.cpp', + 'main.cpp', +) + +deps = [evdev, systemd] + +exe = executable( + 'keyman-system-service', + sources: [service_files], + dependencies: deps, + install: true, + install_dir: get_option('libexecdir'), +) + +install_data('com.keyman.SystemService1.System.xml', install_dir: get_option('datadir') / 'dbus-1/interfaces') diff --git a/linux/keyman-system-service/tests/KeyboardDeviceMock.cpp b/linux/keyman-system-service/tests/KeyboardDeviceMock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dbaa6aafab99ad620c1d3566ab5c463561f653d9 --- /dev/null +++ b/linux/keyman-system-service/tests/KeyboardDeviceMock.cpp @@ -0,0 +1,45 @@ +// A KeyboardDevice mock. +#include +#include +#include +#include +#include +#include + +#ifndef KeyboardDevice +#define KeyboardDevice KeyboardDeviceMock +#endif + +#include "KeyboardDevice.h" + +using namespace std; + +KeyboardDeviceMock::KeyboardDeviceMock() { + dev = nullptr; + fd = -1; + hasCapsLockLed = 1; + debug = false; +} + +KeyboardDeviceMock::~KeyboardDeviceMock() { +} + +bool +KeyboardDeviceMock::Initialize(const char* name) { + return true; +} + +bool +KeyboardDeviceMock::HasCapsLockLed() { + return true; +} + +void +KeyboardDeviceMock::SetCapsLockLed(bool on) { + hasCapsLockLed = on ? 1 : 0; +} + +bool +KeyboardDeviceMock::GetCapsLockLed() { + return hasCapsLockLed == 1; +} diff --git a/linux/keyman-system-service/tests/README.md b/linux/keyman-system-service/tests/README.md new file mode 100644 index 0000000000000000000000000000000000000000..af77363b65794c4c80ba897b042bfa55eef68bae --- /dev/null +++ b/linux/keyman-system-service/tests/README.md @@ -0,0 +1,11 @@ +# keyman-system-service tests + +This directory defines a keyman-test-service. This is mostly identical to +keyman-system-service. The differences are: + +- it runs on the session bus instead of system bus +- KeyboardDevice (which in production interacts with evdev) is mocked + so that the tests don't require root permissions. + +The keyman-test-service gets called/used by the ibus-keyman integration +tests. diff --git a/linux/keyman-system-service/tests/meson.build b/linux/keyman-system-service/tests/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..f90e12d16a23a8992de761ae3aa9626fecb0f36d --- /dev/null +++ b/linux/keyman-system-service/tests/meson.build @@ -0,0 +1,27 @@ +test_service_files = files( + '../src/main.cpp', + '../src/KeymanSystemService.cpp', + 'KeyboardDeviceMock.cpp', +) + +deps = [evdev, systemd] + +test_c_args = [ + '-DKEYMAN_TESTING', + '-DKeyboardDevice=KeyboardDeviceMock' +] + +exe = executable( + 'keyman-test-service', + sources: [test_service_files], + c_args: test_c_args, + cpp_args: test_c_args, + dependencies: deps, + include_directories: [ '../src' ] +) + +# we currently don't have any unit tests for keyman-system-service. +# The keyman-test-service we build here gets used by the ibus-keyman +# integration tests. + +subdir('services') diff --git a/linux/keyman-system-service/tests/services/README.md b/linux/keyman-system-service/tests/services/README.md new file mode 100644 index 0000000000000000000000000000000000000000..17c6950c556659740a761d4d31f8f5cf2d76bfd7 --- /dev/null +++ b/linux/keyman-system-service/tests/services/README.md @@ -0,0 +1,5 @@ +# tests/services + +This directory contains the service file for our keyman test service as +well as some additional files for services that are required for things +to properly work in our test environment. diff --git a/linux/keyman-system-service/tests/services/at-spi-dbus-bus.service.in b/linux/keyman-system-service/tests/services/at-spi-dbus-bus.service.in new file mode 100644 index 0000000000000000000000000000000000000000..8294093a7f88a4da344a25705667c66671e9744a --- /dev/null +++ b/linux/keyman-system-service/tests/services/at-spi-dbus-bus.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.a11y.Bus +Exec=/usr/libexec/at-spi-bus-launcher diff --git a/linux/keyman-system-service/tests/services/gvfs-daemon.service.in b/linux/keyman-system-service/tests/services/gvfs-daemon.service.in new file mode 100644 index 0000000000000000000000000000000000000000..b5357bdda3aa3766b26a86e17020ddecf468e8b2 --- /dev/null +++ b/linux/keyman-system-service/tests/services/gvfs-daemon.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.gtk.vfs.Daemon +Exec=/usr/libexec/gvfsd diff --git a/linux/keyman-system-service/tests/services/keyman-test.service.in b/linux/keyman-system-service/tests/services/keyman-test.service.in new file mode 100644 index 0000000000000000000000000000000000000000..8946119f234d18af79489daf1dc480f74881a532 --- /dev/null +++ b/linux/keyman-system-service/tests/services/keyman-test.service.in @@ -0,0 +1,5 @@ +# Service definition file for keyman-test-service + +[D-BUS Service] +Name=com.keyman.SystemService1 +Exec=@installed_tests_dir@/keyman-test-service diff --git a/linux/keyman-system-service/tests/services/meson.build b/linux/keyman-system-service/tests/services/meson.build new file mode 100644 index 0000000000000000000000000000000000000000..e741aa5fbffd4f7d69f71625f5c1dedd4c40de02 --- /dev/null +++ b/linux/keyman-system-service/tests/services/meson.build @@ -0,0 +1,16 @@ +dbus_service_files = [ + 'at-spi-dbus-bus.service', + 'gvfs-daemon.service', + 'keyman-test.service', +] + +config_data = configuration_data() +config_data.set('installed_tests_dir', meson.current_build_dir() / '..') + +foreach service_file : dbus_service_files + configure_file( + input: service_file + '.in', + output: service_file, + configuration: config_data, + ) +endforeach diff --git a/linux/scripts/dist.sh b/linux/scripts/dist.sh index 7b1be441d90374e87053c328b4d973c043bbe82a..f8d63b63e353db64aba653e209034cb467080084 100755 --- a/linux/scripts/dist.sh +++ b/linux/scripts/dist.sh @@ -17,7 +17,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" BASEDIR=$(pwd) -if [ "$1" == "origdist" ]; then +if [[ ! -z ${1+x} ]] && [ "$1" == "origdist" ]; then create_origdist=1 shift fi @@ -33,6 +33,8 @@ dch keyman --newversion "${VERSION}" --force-bad-version --nomultimaint dpkg-source --tar-ignore=*~ --tar-ignore=.git --tar-ignore=.gitattributes \ --tar-ignore=.gitignore --tar-ignore=experiments --tar-ignore=debian \ --tar-ignore=.github --tar-ignore=.vscode --tar-ignore=android \ + --tar-ignore=.devcontainer \ + --tar-ignore=artifacts \ \ --tar-ignore=common/models \ --tar-ignore=common/predictive-text \ @@ -49,11 +51,15 @@ dpkg-source --tar-ignore=*~ --tar-ignore=.git --tar-ignore=.gitattributes \ --tar-ignore=linux/keyman-config/buildtools/build-langtags.py --tar-ignore=__pycache__ \ --tar-ignore=linux/help --tar-ignore=linux/Jenkinsfile \ --tar-ignore=mac --tar-ignore=node_modules --tar-ignore=oem \ - --tar-ignore=linux/build --tar-ignore=core/build \ + --tar-ignore=linux/build \ + --tar-ignore=linux/builddebs \ + --tar-ignore=linux/ibus-keyman/build \ + --tar-ignore=linux/keyman-system-service/build \ --tar-ignore=resources/devbox --tar-ignore=resources/git-hooks \ --tar-ignore=resources/scopes \ --tar-ignore=resources/build/*.lua --tar-ignore=resources/build/jq* \ --tar-ignore=results \ + --tar-ignore=tmp \ --tar-ignore=web --tar-ignore=windows --tar-ignore=keyman_1* \ --tar-ignore=dist --tar-ignore=.pbuilderrc --tar-ignore=VERSION -Zgzip -b . mv ../keyman_"${VERSION}".tar.gz linux/dist/keyman-"${VERSION}".tar.gz @@ -61,7 +67,7 @@ echo "3.0 (quilt)" > debian/source/format cd "$BASEDIR" # create orig.tar.gz -if [ -n "$create_origdist" ]; then +if [ ! -z "${create_origdist+x}" ]; then cd dist pkgvers="keyman-$VERSION" tar xfz keyman-"${VERSION}".tar.gz diff --git a/linux/scripts/install.sh b/linux/scripts/install.sh index 40f2eb8f5b09bcd7f6571fa272d660391349ebaa..7e497232fc8d6ad7fb01bb643ae6ab5cb19b7d14 100755 --- a/linux/scripts/install.sh +++ b/linux/scripts/install.sh @@ -15,7 +15,7 @@ INSTALLDIR=${INSTALLDIR:-"/usr/local"} if [[ "${SUDOINSTALL}" != "no" ]]; then if [ "$EUID" -ne 0 ] then - echo "Please run 'make un/install' with sudo" + echo "Please run 'build.sh un/install' with sudo" exit fi fi @@ -24,7 +24,7 @@ if [ -f "/usr/share/ibus/component/keyman.xml" ] && [ "${SUDOINSTALL}" == "yes" if grep -Fq "/usr/lib/ibus" /usr/share/ibus/component/keyman.xml then echo "component file is in ibus-keyman package version so move it" - echo "run 'sudo make uninstall' to put it back" + echo "run 'sudo build.sh uninstall' to put it back" mv /usr/share/ibus/component/keyman.xml /usr/share/doc/ibus-keyman/ else echo "component file is local one so overwrite it" @@ -37,10 +37,10 @@ cd "$BASEDIR" cd ibus-keyman if [[ "${SUDOINSTALL}" == "uninstall" ]]; then - echo "doing make uninstall of ibus-keyman" + echo "doing build.sh uninstall of ibus-keyman" ./build.sh uninstall else - echo "doing make install of ibus-keyman" + echo "doing build.sh install of ibus-keyman" ./build.sh install fi cd "$BASEDIR" @@ -49,20 +49,20 @@ cd keyman-config echo "SUDOINSTALL: ${SUDOINSTALL}" if [[ "${SUDOINSTALL}" == "yes" ]]; then if [ ! -d build ]; then - echo "keyman-config must be built before it is installed. Run 'make configure' if needed then 'make'" + echo "keyman-config must be built before it is installed. Run 'build.sh configure build'." exit 1 fi echo "doing sudo glib-compile-schemas for keyman-config" - cp com.keyman.gschema.xml /usr/share/glib-2.0/schemas/ + cp resources/com.keyman.gschema.xml /usr/share/glib-2.0/schemas/ glib-compile-schemas /usr/share/glib-2.0/schemas/ echo "doing sudo install of keyman-config" - make install + ./build.sh install elif [[ "${SUDOINSTALL}" == "uninstall" ]]; then echo "doing sudo uninstall of keyman-config" - make uninstall + ./build.sh uninstall else echo "doing /tmp install of keyman-config" - make install-temp + ./build.sh install fi cd "$BASEDIR" diff --git a/linux/scripts/launchpad.sh b/linux/scripts/launchpad.sh index c16d89f7d65d6a9a2b8178bd71cc020c24ab7662..30495d2b0e1567278819415fcd9edf5a4a9de098 100755 --- a/linux/scripts/launchpad.sh +++ b/linux/scripts/launchpad.sh @@ -10,24 +10,20 @@ # DIST="" only upload for this distribution # PACKAGEVERSION="" string to append to the package version. Default to "1~sil1" -set -e +set -eu ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +# shellcheck source=resources/build/build-utils.sh . "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh" ## END STANDARD BUILD SCRIPT INCLUDE -. $(dirname "$THIS_SCRIPT")/package-build.inc.sh +# shellcheck source=linux/scripts/package-build.inc.sh +. "$(dirname "$THIS_SCRIPT")/package-build.inc.sh" checkPrerequisites -if [ "${UPLOAD}" == "yes" ]; then - SIM="" -else - SIM="-s" -fi - if [ "${TIER}" == "stable" ]; then ppa="ppa:keymanapp/keyman" elif [ "${TIER}" == "beta" ]; then @@ -37,32 +33,22 @@ else fi echo "ppa: ${ppa}" -if [ "${DIST:-}" != "" ]; then - distributions="${DIST}" -else - distributions="focal jammy kinetic lunar" -fi - -if [ "${PACKAGEVERSION:-}" != "" ]; then - packageversion="${PACKAGEVERSION}" -else - packageversion="1~sil1" -fi - +distributions="${DIST:-focal jammy kinetic lunar}" +packageversion="${PACKAGEVERSION:-1~sil1}" BASEDIR=$(pwd) rm -rf launchpad mkdir -p launchpad -for proj in ${projects}; do +for proj in ${projects:-}; do downloadSource launchpad - cd "${proj}-${version}" + cd "${proj}-${version:-}" pwd - cp debian/changelog ../${proj}-changelog + cp debian/changelog "../${proj}-changelog" for dist in ${distributions}; do - cp ../${proj}-changelog debian/changelog + cp "../${proj}-changelog" debian/changelog dch -v "${version}-${packageversion}~${dist}" "source package for PPA" dch -D "${dist}" -r "" debuild -d -S -sa -Zxz diff --git a/linux/scripts/package-build.inc.sh b/linux/scripts/package-build.inc.sh index 7134f6719f6932335812441cb6d6015fcba5929e..6c249891c1633a3bcd5da6279f6fc13a316cc29a 100644 --- a/linux/scripts/package-build.inc.sh +++ b/linux/scripts/package-build.inc.sh @@ -1,14 +1,14 @@ #!/bin/bash function checkPrerequisites() { - if [ "${UPLOAD}" == "yes" ]; then + if [ "${UPLOAD:=}" == "yes" ]; then SIM="" else SIM="-s" fi # Check the tier - if [[ -z "${TIER}" ]]; then + if [[ -z "${TIER:=}" ]]; then echo "TIER.md or \${TIER} must be set to (alpha, beta, stable) to use this script" exit 1 fi @@ -18,23 +18,17 @@ function checkPrerequisites() { exit 1 fi - if [ "${PROJECT}" != "" ]; then - projects="${PROJECT}" - else - projects="keyman" - fi + # shellcheck disable=SC2034 + projects="${PROJECT:=keyman}" } function downloadSource() { local packageDir packageDir=$1 - if [ "${proj}" == "keyman" ]; then + if [ "${proj:=}" == "keyman" ]; then cd "${BASEDIR}" || exit - fi - - if [ "${proj}" == "keyman" ]; then - make clean + ./build.sh clean fi # Update tier in Debian watch files (replacing any previously set tier) and remove comment @@ -51,7 +45,7 @@ function downloadSource() { mv "${proj}"*.asc "${BASEDIR}/${packageDir}" rm "${proj}"*.debian.tar.xz cd "${BASEDIR}/${packageDir}" || exit - wget -N https://downloads.keyman.com/linux/${TIER}/${dirversion}/SHA256SUMS + wget -N "https://downloads.keyman.com/linux/${TIER}/${dirversion}/SHA256SUMS" sha256sum -c --ignore-missing SHA256SUMS |grep "${proj}" } diff --git a/linux/scripts/reconf.sh b/linux/scripts/reconf.sh index cd74571a4a1dc696934de83f04d395d4850cda05..4e4f5c1b115cbc37e02905dfcdc262ccb9941024 100755 --- a/linux/scripts/reconf.sh +++ b/linux/scripts/reconf.sh @@ -21,7 +21,7 @@ cd "$BASEDIR/ibus-keyman" ./build.sh clean configure cd "$BASEDIR/keyman-config" -make clean +./build.sh clean cd keyman_config sed \ diff --git a/mac/Keyman4MacIM/Keyman4MacIM/am.lproj/Localizable.strings b/mac/Keyman4MacIM/Keyman4MacIM/am.lproj/Localizable.strings index 06182ea47cbb20a18a09f6261771c207bc11f1fa..35de92b8d831632491abd14ea201191a9a9adfc9 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/am.lproj/Localizable.strings +++ b/mac/Keyman4MacIM/Keyman4MacIM/am.lproj/Localizable.strings @@ -68,4 +68,4 @@ "copyright-label" = "የባለቤትነት መብት፦"; /* message displayed to alert user to need grant accessibility permission */ -"privacy-alert-text" = "To function properly, Keyman requires accessibility features:\n\nGrant access in System Preferences, Security & Privacy.\nRestart your system."; +"privacy-alert-text" = "ኪይማን በአግባቡ ለመስራት የተደራሽነት ባህሪያትን ይፈልጋል፦ \n\nበስርዓት ምርጫዎች፣ ደህንነት እና ግላዊነት ውስጥ መዳረሻ ፍቀድ። \nስርዓት እንደገና አስጀምር።"; diff --git a/mac/build.sh b/mac/build.sh index 480cbd8587f9e35a24f5951bb57b9b59a4d0d69c..5b6a3f525b8e4e9296e4be223f9fa0df6fe521f6 100755 --- a/mac/build.sh +++ b/mac/build.sh @@ -132,6 +132,7 @@ QUIET=false NOTARIZE=false SKIP_BUILD=false UPLOAD_SENTRY=false +QUIET_FLAG= # Import local environment variables for build if [[ -f $(dirname "$THIS_SCRIPT")/localenv.sh ]]; then @@ -259,6 +260,7 @@ displayInfo "" \ "BUILD_ACTIONS: $BUILD_ACTIONS" \ "TEST_ACTION: $TEST_ACTION" \ "UPLOAD_SENTRY: $UPLOAD_SENTRY" \ + "QUIET_FLAG: $QUIET_FLAG" \ "" ### Validate notarization environment variables ### @@ -273,11 +275,11 @@ if $LOCALDEPLOY && ! $NOTARIZE ; then fi if $PREPRELEASE || $NOTARIZE ; then - if [ ! $DO_CODESIGN ] || [ -z "${CERTIFICATE_ID}" ]; then + if [ ! $DO_CODESIGN ] || [ -z "${CERTIFICATE_ID+x}" ]; then builder_die "Code signing must be configured for deployment. See build.sh -help for details." fi - if [ -z "${APPSTORECONNECT_PROVIDER}" ] || [ -z "${APPSTORECONNECT_USERNAME}" ] || [ -z "${APPSTORECONNECT_PASSWORD}" ]; then + if [ -z "${APPSTORECONNECT_PROVIDER+x}" ] || [ -z "${APPSTORECONNECT_USERNAME+x}" ] || [ -z "${APPSTORECONNECT_PASSWORD+x}" ]; then builder_die "Appstoreconnect Apple ID credentials must be configured in environment. See build.sh -help for details." fi fi @@ -388,7 +390,7 @@ if $DO_KEYMANIM ; then ENTITLEMENTS_FILE=Keyman.entitlements fi - if [ -z "$DEVELOPMENT_TEAM" ]; then + if [ -z "${DEVELOPMENT_TEAM+x}" ]; then DEVELOPMENT_TEAM=3YE4W86L3G fi @@ -411,7 +413,7 @@ if $DO_KEYMANIM ; then if which sentry-cli >/dev/null; then cd "$KM4MIM_BASE_PATH" - sentry-cli upload-dif "build/${CONFIGURATION}" + sentry-cli upload-dif "build/$CONFIG" else builder_die "Error: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases" fi @@ -432,7 +434,7 @@ fi if $PREPRELEASE || $NOTARIZE; then builder_heading "Notarizing app" - if [ "${CODESIGNING_SUPPRESSION}" != "" ] && [ -z "${CERTIFICATE_ID}" ]; then + if [ "${CODESIGNING_SUPPRESSION}" != "" ] && [ -z "${CERTIFICATE_ID+x}" ]; then builder_die "Notarization and signed executable is required for deployment, even locally. Specify CERTIFICATE_ID environment variable for custom certificate." else TARGET_PATH="$KM4MIM_BASE_PATH/build/$CONFIG" @@ -443,7 +445,7 @@ if $PREPRELEASE || $NOTARIZE; then # Note: get-task-allow entitlement must be *off* in our release build (to do this, don't include base entitlements in project build settings) # We may need to re-run the code signing if a custom certificate has been passed in - if [ ! -z "${CERTIFICATE_ID}" ]; then + if [ ! -z "${CERTIFICATE_ID+x}" ]; then builder_heading "Signing with custom certificate (CERTIFICATE_ID environment variable)." codesign --force --options runtime --entitlements Keyman4MacIM/Keyman.entitlements --deep --sign "${CERTIFICATE_ID}" "$TARGET_APP_PATH" fi diff --git a/oem/firstvoices/ios/Cartfile b/oem/firstvoices/ios/Cartfile index ec3127efc7f8555f418ada6a1254f76698566929..10efbc3d5a00674b34fa904f3376c63c23b95f93 100644 --- a/oem/firstvoices/ios/Cartfile +++ b/oem/firstvoices/ios/Cartfile @@ -1,4 +1,4 @@ -github "DaveWoodCom/XCGLogger" ~> 6.1.0 +github "keymanapp/dependency-XCGLogger" "master" github "devicekit/DeviceKit" ~> 5.0 github "ashleymills/Reachability.swift" -github "getsentry/sentry-cocoa" ~> 6.2.1 +github "getsentry/sentry-cocoa" ~> 8.7.0 diff --git a/oem/firstvoices/ios/Cartfile.resolved b/oem/firstvoices/ios/Cartfile.resolved index 16cb6213b6c2f2a085fd61f59c813a6f2292ec3d..5ac3a55318988538f2f6e3ebf339ac837e43effd 100644 --- a/oem/firstvoices/ios/Cartfile.resolved +++ b/oem/firstvoices/ios/Cartfile.resolved @@ -1,4 +1,4 @@ -github "DaveWoodCom/XCGLogger" "6.1.0" github "ashleymills/Reachability.swift" "v5.1.0" github "devicekit/DeviceKit" "5.0.0" -github "getsentry/sentry-cocoa" "6.2.1" +github "getsentry/sentry-cocoa" "8.7.0" +github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113" diff --git a/oem/firstvoices/ios/exportAppStore.plist b/oem/firstvoices/ios/exportAppStore.plist index 123af31a0c437e0927eddef23f03b3a850374642..c4ac972730f0715629ebd05914f11ab9f1026ca6 100644 --- a/oem/firstvoices/ios/exportAppStore.plist +++ b/oem/firstvoices/ios/exportAppStore.plist @@ -7,9 +7,9 @@ teamID D7TR486TEH signingCertificate - EFA9DE793B2A25F38270468AE62060CFF55CD634 + C4726D4C8BD4C2FE38A551B84CEDB8E4B3FD12D6 installerSigningCertificate - EFA9DE793B2A25F38270468AE62060CFF55CD634 + C4726D4C8BD4C2FE38A551B84CEDB8E4B3FD12D6 provisioningProfiles com.firstvoices.keyboards diff --git a/package-lock.json b/package-lock.json index 331c616e6d31e00c51c5c9d9e9f7a8186ccd1c44..91fce6219175c9df02c7230a940ecc14629f0ad2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,9 @@ "core/include/ldml", "developer/src/common/web/test-helpers", "developer/src/kmc-analyze", - "developer/src/kmc-keyboard", "developer/src/kmc-kmn", "developer/src/kmc-kmw", + "developer/src/kmc-ldml", "developer/src/kmc-model", "developer/src/kmc-model-info", "developer/src/kmc-package", @@ -637,9 +637,9 @@ "@keymanapp/common-types": "*", "@keymanapp/keyman-version": "*", "@keymanapp/kmc-analyze": "*", - "@keymanapp/kmc-keyboard": "*", "@keymanapp/kmc-kmn": "*", "@keymanapp/kmc-kmw": "*", + "@keymanapp/kmc-ldml": "*", "@keymanapp/kmc-model": "*", "@keymanapp/kmc-model-info": "*", "@keymanapp/kmc-package": "*", @@ -900,6 +900,7 @@ }, "developer/src/kmc-keyboard": { "name": "@keymanapp/kmc-keyboard", + "extraneous": true, "license": "MIT", "dependencies": { "@keymanapp/keyman-version": "*", @@ -924,34 +925,77 @@ "typescript": "^4.9.5" } }, - "developer/src/kmc-keyboard/node_modules/@types/mocha": { - "version": "5.2.7", + "developer/src/kmc-kmn": { + "name": "@keymanapp/kmc-kmn", + "license": "MIT", + "dependencies": { + "@keymanapp/keyman-version": "*" + }, + "devDependencies": { + "@keymanapp/developer-test-helpers": "*", + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.7", + "@types/node": "^10.14.6", + "@types/semver": "^7.3.12", + "@types/sinon": "^10.0.13", + "@types/sinon-chai": "^3.2.9", + "@types/xml2js": "^0.4.5", + "c8": "^7.12.0", + "chai": "^4.3.4", + "chalk": "^2.4.2", + "mocha": "^8.4.0", + "sinon": "^15.0.2", + "sinon-chai": "^3.7.0", + "ts-node": "^9.1.1", + "typescript": "^4.9.5" + } + }, + "developer/src/kmc-kmn/node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, - "license": "MIT" + "dependencies": { + "type-detect": "4.0.8" + } }, - "developer/src/kmc-keyboard/node_modules/@types/node": { - "version": "10.17.60", + "developer/src/kmc-kmn/node_modules/@sinonjs/samsam": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", + "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", "dev": true, - "license": "MIT" + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } }, - "developer/src/kmc-keyboard/node_modules/ajv": { - "version": "8.11.2", - "license": "MIT", + "developer/src/kmc-kmn/node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type-detect": "4.0.8" } }, - "developer/src/kmc-keyboard/node_modules/ansi-styles": { + "developer/src/kmc-kmn/node_modules/@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, + "developer/src/kmc-kmn/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true + }, + "developer/src/kmc-kmn/node_modules/ansi-styles": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -959,10 +1003,11 @@ "node": ">=4" } }, - "developer/src/kmc-keyboard/node_modules/chalk": { + "developer/src/kmc-kmn/node_modules/chalk": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -972,23 +1017,32 @@ "node": ">=4" } }, - "developer/src/kmc-keyboard/node_modules/color-convert": { + "developer/src/kmc-kmn/node_modules/color-convert": { "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, - "developer/src/kmc-keyboard/node_modules/color-name": { + "developer/src/kmc-kmn/node_modules/color-name": { "version": "1.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, - "developer/src/kmc-keyboard/node_modules/js-yaml": { + "developer/src/kmc-kmn/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "developer/src/kmc-kmn/node_modules/js-yaml": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -996,14 +1050,11 @@ "js-yaml": "bin/js-yaml.js" } }, - "developer/src/kmc-keyboard/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, - "developer/src/kmc-keyboard/node_modules/minimatch": { + "developer/src/kmc-kmn/node_modules/minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1011,10 +1062,11 @@ "node": "*" } }, - "developer/src/kmc-keyboard/node_modules/mocha": { + "developer/src/kmc-kmn/node_modules/mocha": { "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, - "license": "MIT", "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", @@ -1054,10 +1106,11 @@ "url": "https://opencollective.com/mochajs" } }, - "developer/src/kmc-keyboard/node_modules/mocha/node_modules/escape-string-regexp": { + "developer/src/kmc-kmn/node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -1065,10 +1118,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "developer/src/kmc-keyboard/node_modules/mocha/node_modules/supports-color": { + "developer/src/kmc-kmn/node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1079,15 +1133,87 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "developer/src/kmc-keyboard/node_modules/ms": { + "developer/src/kmc-kmn/node_modules/ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "developer/src/kmc-kmn/node_modules/nise": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", "dev": true, - "license": "MIT" + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "developer/src/kmc-kmn/node_modules/nise/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "developer/src/kmc-kmn/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, + "developer/src/kmc-kmn/node_modules/sinon": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.2.tgz", + "integrity": "sha512-PCVP63XZkg0/LOqQH5rEU4LILuvTFMb5tNxTHfs6VUMNnZz2XrnGSTZbAGITjzwQWbl/Bl/8hi4G3zZWjyBwHg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/samsam": "^7.0.1", + "diff": "^5.1.0", + "nise": "^5.1.4", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } }, - "developer/src/kmc-keyboard/node_modules/supports-color": { + "developer/src/kmc-kmn/node_modules/sinon/node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "developer/src/kmc-kmn/node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "developer/src/kmc-kmn/node_modules/supports-color": { "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -1095,15 +1221,16 @@ "node": ">=4" } }, - "developer/src/kmc-keyboard/node_modules/supports-color/node_modules/has-flag": { + "developer/src/kmc-kmn/node_modules/supports-color/node_modules/has-flag": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, - "developer/src/kmc-keyboard/node_modules/ts-node": { + "developer/src/kmc-kmn/node_modules/ts-node": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", @@ -1129,7 +1256,7 @@ "typescript": ">=2.7" } }, - "developer/src/kmc-keyboard/node_modules/ts-node/node_modules/diff": { + "developer/src/kmc-kmn/node_modules/ts-node/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", @@ -1138,73 +1265,59 @@ "node": ">=0.3.1" } }, - "developer/src/kmc-kmn": { - "name": "@keymanapp/kmc-kmn", + "developer/src/kmc-kmw": { + "name": "@keymanapp/kmc-kmw", "license": "MIT", "dependencies": { - "@keymanapp/keyman-version": "*" + "@keymanapp/common-types": "*", + "@keymanapp/kmc-kmn": "*", + "ajv": "^8.11.0", + "restructure": "git+https://github.com/keymanapp/dependency-restructure.git#49d129cf0916d082a7278bb09296fb89cecfcc50", + "semver": "^7.3.7", + "xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829" }, "devDependencies": { - "@keymanapp/developer-test-helpers": "*", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", "@types/semver": "^7.3.12", - "@types/sinon": "^10.0.13", - "@types/sinon-chai": "^3.2.9", "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", "chalk": "^2.4.2", "mocha": "^8.4.0", - "sinon": "^15.0.2", - "sinon-chai": "^3.7.0", "ts-node": "^9.1.1", "typescript": "^4.9.5" } }, - "developer/src/kmc-kmn/node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "developer/src/kmc-kmn/node_modules/@sinonjs/samsam": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", - "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^2.0.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "developer/src/kmc-kmn/node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "developer/src/kmc-kmn/node_modules/@types/mocha": { + "developer/src/kmc-kmw/node_modules/@types/mocha": { "version": "5.2.7", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", "dev": true }, - "developer/src/kmc-kmn/node_modules/@types/node": { + "developer/src/kmc-kmw/node_modules/@types/node": { "version": "10.17.60", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", "dev": true }, - "developer/src/kmc-kmn/node_modules/ansi-styles": { + "developer/src/kmc-kmw/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "developer/src/kmc-kmw/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", @@ -1216,7 +1329,7 @@ "node": ">=4" } }, - "developer/src/kmc-kmn/node_modules/chalk": { + "developer/src/kmc-kmw/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", @@ -1230,7 +1343,7 @@ "node": ">=4" } }, - "developer/src/kmc-kmn/node_modules/color-convert": { + "developer/src/kmc-kmw/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", @@ -1239,19 +1352,13 @@ "color-name": "1.1.3" } }, - "developer/src/kmc-kmn/node_modules/color-name": { + "developer/src/kmc-kmw/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "developer/src/kmc-kmn/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true - }, - "developer/src/kmc-kmn/node_modules/js-yaml": { + "developer/src/kmc-kmw/node_modules/js-yaml": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", @@ -1263,7 +1370,12 @@ "js-yaml": "bin/js-yaml.js" } }, - "developer/src/kmc-kmn/node_modules/minimatch": { + "developer/src/kmc-kmw/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "developer/src/kmc-kmw/node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", @@ -1275,7 +1387,7 @@ "node": "*" } }, - "developer/src/kmc-kmn/node_modules/mocha": { + "developer/src/kmc-kmw/node_modules/mocha": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", @@ -1319,7 +1431,7 @@ "url": "https://opencollective.com/mochajs" } }, - "developer/src/kmc-kmn/node_modules/mocha/node_modules/escape-string-regexp": { + "developer/src/kmc-kmw/node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", @@ -1331,7 +1443,7 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "developer/src/kmc-kmn/node_modules/mocha/node_modules/supports-color": { + "developer/src/kmc-kmw/node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", @@ -1346,83 +1458,13 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "developer/src/kmc-kmn/node_modules/ms": { + "developer/src/kmc-kmw/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "developer/src/kmc-kmn/node_modules/nise": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", - "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^2.0.0", - "@sinonjs/fake-timers": "^10.0.2", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - } - }, - "developer/src/kmc-kmn/node_modules/nise/node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "developer/src/kmc-kmn/node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "dependencies": { - "isarray": "0.0.1" - } - }, - "developer/src/kmc-kmn/node_modules/sinon": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.2.tgz", - "integrity": "sha512-PCVP63XZkg0/LOqQH5rEU4LILuvTFMb5tNxTHfs6VUMNnZz2XrnGSTZbAGITjzwQWbl/Bl/8hi4G3zZWjyBwHg==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^10.0.2", - "@sinonjs/samsam": "^7.0.1", - "diff": "^5.1.0", - "nise": "^5.1.4", - "supports-color": "^7.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "developer/src/kmc-kmn/node_modules/sinon/node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "developer/src/kmc-kmn/node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "developer/src/kmc-kmn/node_modules/supports-color": { + "developer/src/kmc-kmw/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", @@ -1434,7 +1476,7 @@ "node": ">=4" } }, - "developer/src/kmc-kmn/node_modules/supports-color/node_modules/has-flag": { + "developer/src/kmc-kmw/node_modules/supports-color/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", @@ -1443,7 +1485,7 @@ "node": ">=4" } }, - "developer/src/kmc-kmn/node_modules/ts-node": { + "developer/src/kmc-kmw/node_modules/ts-node": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", @@ -1469,7 +1511,7 @@ "typescript": ">=2.7" } }, - "developer/src/kmc-kmn/node_modules/ts-node/node_modules/diff": { + "developer/src/kmc-kmw/node_modules/ts-node/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", @@ -1478,18 +1520,19 @@ "node": ">=0.3.1" } }, - "developer/src/kmc-kmw": { - "name": "@keymanapp/kmc-kmw", + "developer/src/kmc-ldml": { + "name": "@keymanapp/kmc-ldml", "license": "MIT", "dependencies": { - "@keymanapp/common-types": "*", - "@keymanapp/kmc-kmn": "*", + "@keymanapp/keyman-version": "*", + "@keymanapp/ldml-keyboard-constants": "*", "ajv": "^8.11.0", "restructure": "git+https://github.com/keymanapp/dependency-restructure.git#49d129cf0916d082a7278bb09296fb89cecfcc50", "semver": "^7.3.7", "xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829" }, "devDependencies": { + "@keymanapp/developer-test-helpers": "*", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^10.14.6", @@ -1503,19 +1546,19 @@ "typescript": "^4.9.5" } }, - "developer/src/kmc-kmw/node_modules/@types/mocha": { + "developer/src/kmc-ldml/node_modules/@types/mocha": { "version": "5.2.7", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", "dev": true }, - "developer/src/kmc-kmw/node_modules/@types/node": { + "developer/src/kmc-ldml/node_modules/@types/node": { "version": "10.17.60", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", "dev": true }, - "developer/src/kmc-kmw/node_modules/ajv": { + "developer/src/kmc-ldml/node_modules/ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", @@ -1530,7 +1573,7 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "developer/src/kmc-kmw/node_modules/ansi-styles": { + "developer/src/kmc-ldml/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", @@ -1542,7 +1585,7 @@ "node": ">=4" } }, - "developer/src/kmc-kmw/node_modules/chalk": { + "developer/src/kmc-ldml/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", @@ -1556,7 +1599,7 @@ "node": ">=4" } }, - "developer/src/kmc-kmw/node_modules/color-convert": { + "developer/src/kmc-ldml/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", @@ -1565,13 +1608,13 @@ "color-name": "1.1.3" } }, - "developer/src/kmc-kmw/node_modules/color-name": { + "developer/src/kmc-ldml/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "developer/src/kmc-kmw/node_modules/js-yaml": { + "developer/src/kmc-ldml/node_modules/js-yaml": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", @@ -1583,12 +1626,12 @@ "js-yaml": "bin/js-yaml.js" } }, - "developer/src/kmc-kmw/node_modules/json-schema-traverse": { + "developer/src/kmc-ldml/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "developer/src/kmc-kmw/node_modules/minimatch": { + "developer/src/kmc-ldml/node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", @@ -1600,7 +1643,7 @@ "node": "*" } }, - "developer/src/kmc-kmw/node_modules/mocha": { + "developer/src/kmc-ldml/node_modules/mocha": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", @@ -1644,7 +1687,7 @@ "url": "https://opencollective.com/mochajs" } }, - "developer/src/kmc-kmw/node_modules/mocha/node_modules/escape-string-regexp": { + "developer/src/kmc-ldml/node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", @@ -1656,7 +1699,7 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "developer/src/kmc-kmw/node_modules/mocha/node_modules/supports-color": { + "developer/src/kmc-ldml/node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", @@ -1671,13 +1714,13 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "developer/src/kmc-kmw/node_modules/ms": { + "developer/src/kmc-ldml/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "developer/src/kmc-kmw/node_modules/supports-color": { + "developer/src/kmc-ldml/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", @@ -1689,7 +1732,7 @@ "node": ">=4" } }, - "developer/src/kmc-kmw/node_modules/supports-color/node_modules/has-flag": { + "developer/src/kmc-ldml/node_modules/supports-color/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", @@ -1698,7 +1741,7 @@ "node": ">=4" } }, - "developer/src/kmc-kmw/node_modules/ts-node": { + "developer/src/kmc-ldml/node_modules/ts-node": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", @@ -1724,7 +1767,7 @@ "typescript": ">=2.7" } }, - "developer/src/kmc-kmw/node_modules/ts-node/node_modules/diff": { + "developer/src/kmc-ldml/node_modules/ts-node/node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", @@ -1761,8 +1804,7 @@ "name": "@keymanapp/kmc-model-info", "license": "MIT", "dependencies": { - "@keymanapp/keyman-version": "*", - "@keymanapp/kmc-package": "*", + "@keymanapp/common-types": "*", "@keymanapp/models-types": "*" }, "devDependencies": { @@ -2763,10 +2805,6 @@ "resolved": "developer/src/kmc-analyze", "link": true }, - "node_modules/@keymanapp/kmc-keyboard": { - "resolved": "developer/src/kmc-keyboard", - "link": true - }, "node_modules/@keymanapp/kmc-kmn": { "resolved": "developer/src/kmc-kmn", "link": true @@ -2775,6 +2813,10 @@ "resolved": "developer/src/kmc-kmw", "link": true }, + "node_modules/@keymanapp/kmc-ldml": { + "resolved": "developer/src/kmc-ldml", + "link": true + }, "node_modules/@keymanapp/kmc-model": { "resolved": "developer/src/kmc-model", "link": true diff --git a/package.json b/package.json index 6a13ed2685cb47f2b98e241dde59f81846275f6e..f832cea04a0f5570f6bdbd0e66efbdcb23823a72 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,9 @@ "core/include/ldml", "developer/src/common/web/test-helpers", "developer/src/kmc-analyze", - "developer/src/kmc-keyboard", "developer/src/kmc-kmn", "developer/src/kmc-kmw", + "developer/src/kmc-ldml", "developer/src/kmc-model", "developer/src/kmc-model-info", "developer/src/kmc-package", diff --git a/resources/build/build-utils-ci.inc.sh b/resources/build/build-utils-ci.inc.sh index de36ae9e9b606556869c22f2f7492240825070f7..cab449a4636d59e5f9a388bfad84601d165e0956 100644 --- a/resources/build/build-utils-ci.inc.sh +++ b/resources/build/build-utils-ci.inc.sh @@ -100,18 +100,7 @@ function _builder_publish_npm_package() { dry_run=--dry-run fi - # We use --no-git-tag-version because our CI system controls version numbering - # and already tags releases. We also want to have the version of this match - # the release of Keyman Developer -- these two versions should be in sync. - # Because this is a large repo with multiple projects and build systems, it's - # better for us that individual build systems don't take too much ownership of - # git tagging. :) - npm version --allow-same-version --no-git-tag-version --no-commit-hooks "$VERSION_WITH_TAG" - - # Update all @keymanapp/* [*]dependencies in package.json to the current - # version-with-tag, so that the published version has precise dependencies, - # and we don't accidentally end up with either older or newer deps here - _builder_npm_set_dependency_version + _builder_write_npm_version # Note: In either case, npm publish MUST be given --access public to publish a # package in the @keymanapp scope on the public npm package index. @@ -128,18 +117,33 @@ function _builder_publish_npm_package() { fi } -# Updates all @keymanapp/* [*]dependencies in package.json to the current -# version-with-tag, so that the published version has precise dependencies, and -# we don't accidentally end up with either older or newer deps. This overwrites -# the local package.json, so it does need to be restored afterwards -function _builder_npm_set_dependency_version() { - cat package.json | "$JQ" --arg VERSION_WITH_TAG "$VERSION_WITH_TAG" \ - ' - . + - (try { dependencies: (.dependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) + - (try { devDependencies: (.devDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) + - (try { bundleDependencies: (.bundleDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) + - (try { optionalDependencies: (.optionalDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) - ' > package1.json - mv -f package1.json package.json +function _builder_write_npm_version() { + # We use --no-git-tag-version because our CI system controls version numbering + # and already tags releases. We also want to have the version of this match + # the release of Keyman Developer -- these two versions should be in sync. + # Because this is a large repo with multiple projects and build systems, it's + # better for us that individual build systems don't take too much ownership of + # git tagging. :) + if ! "$JQ" -e '.version' package.json > /dev/null; then + pushd "$KEYMAN_ROOT" > /dev/null + npm version --allow-same-version --no-git-tag-version --no-commit-hooks --workspaces "$VERSION_WITH_TAG" + popd > /dev/null + fi + + # Updates all @keymanapp/* [*]dependencies in all package.jsons to the current + # version-with-tag, so that the published version has precise dependencies, and + # we don't accidentally end up with either older or newer deps. This overwrites + # the local package.json files, so they do need to be restored afterwards + find "$KEYMAN_ROOT" -name "package.json" -not -path '*/node_modules/*' -print0 | \ + while IFS= read -r -d '' line; do + cat "$line" | "$JQ" --arg VERSION_WITH_TAG "$VERSION_WITH_TAG" \ + ' + . + + (try { dependencies: (.dependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) + + (try { devDependencies: (.devDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) + + (try { bundleDependencies: (.bundleDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) + + (try { optionalDependencies: (.optionalDependencies | to_entries | . + map(select(.key | match("@keymanapp/.*")) .value |= $VERSION_WITH_TAG) | from_entries) } catch {}) + ' > "${line}_" + mv -f "${line}_" "$line" + done } \ No newline at end of file diff --git a/resources/build/build-utils.md b/resources/build/build-utils.md index f6be8b3fc73a0fa4bba3bbe97f373ba503b21054..6c27e5e8a4bd9b3e7980d2f6278c0690f0f527dd 100644 --- a/resources/build/build-utils.md +++ b/resources/build/build-utils.md @@ -267,7 +267,7 @@ fi Each step is run separately, is started with [`builder_start_action`], and finishes with [`builder_finish_action`]. If a build step is complex, it may be worthwhile splitting it into a separate function or even a separate script -include. +include. See also [`builder_run_action`]. Use the longer form of `if ...; then` rather than the shorter `[ ... ] && ` pattern, for consistency and readability. @@ -303,10 +303,10 @@ have described outputs for them, and the outputs do not exist, and the dependency is required for one of the targets specified on the command line. The build order of dependencies is determined by the order in which -`builder_start_action` is called in the script for each action. +[`builder_start_action`] is called in the script for each action. You can also define your own internal dependencies with -`builder_describe_internal_dependency`. This allows you to define dependencies +[`builder_describe_internal_dependency`]. This allows you to define dependencies across targets. Use this judiciously; for example, Keyman Core uses this to build both x86_64 and arm64 targets, and test only the appropriate architecture on macOS. @@ -343,7 +343,7 @@ npm test -- $builder_debug ## `builder_describe` function Describes a build script, defines available parameters and their meanings. Use -together with `builder_parse` to process input parameters. +together with [`builder_parse`] to process input parameters. ### Usage @@ -391,7 +391,7 @@ builder_describe "Sample script" :engine ":proxy the proxy module" ``` There are several predefined targets. These will not be available to users of -your script unless you include them in the `builder_describe` call, but when +your script unless you include them in the [`builder_describe`] call, but when used, they have default descriptions, which can be used instead of adding your own in the call: * `:project`: `"this project"` @@ -515,15 +515,46 @@ builder_describe_internal_dependency \ ``` **Note:** actions and targets must be fully specified, and this _must_ be called -before either builder_describe_outputs or builder_parse in order for +before either [`builder_describe_outputs`] or [`builder_parse`] in order for dependencies to be resolved. -------------------------------------------------------------------------------- +## `builder_describe_outputs` function + +Defines an output file or folder expected to be present after successful +completion of an action for a target. Used to skip actions for dependency +builds. If `:target` is not provided, assumes `:project`. + +Relative paths are relative to script folder; absolute paths are relative to +repository root, not filesystem root. + +### Usage + +```bash + builder_describe_outputs action:target filename [...] +``` + +### Parameters + +* 1: `action[:target]` action and/or target associated with file +* 2: `filename` name of file or folder to check +* 3+: ... repeat previous arguments for additional outputs + +### Example + +```bash + builder_describe_outputs \ + "configure" "/node_modules" \ + "build" "build/index.js" +``` + +-------------------------------------------------------------------------------- + ## `builder_display_usage` function Prints the help for the script, constructed from the [`builder_describe`] -parameters, so must be called after `builder_describe`. +parameters, so must be called after [`builder_describe`]. ### Usage @@ -600,7 +631,7 @@ white for top-level builds and child builds. ## `builder_echo_debug` function -Wraps the `builder_echo` command with debug mode and a `[DEBUG]` prefix. +Wraps the [`builder_echo`] command with debug mode and a `[DEBUG]` prefix. ### Usage @@ -666,6 +697,7 @@ fi These last two parameters can optionally be space separated. ### Description + In normal circumstances, `builder_finish_action` will then print a corresponding message: @@ -752,7 +784,7 @@ fi ### Description The `--debug` standard option is currently handled differently to other options. -It should never be declared in `builder_describe`, because it is always +It should never be declared in [`builder_describe`], because it is always available anyway. `--debug` is automatically passed to child scripts and dependency scripts. @@ -779,6 +811,46 @@ correct quoting and escaping. -------------------------------------------------------------------------------- +## `builder_run_action` function + +Wraps [`builder_start_action'] and [`builder_finish`] commands in a shorthand +style for single-command actions. Can be used together with a local function for +multi-command actions. Do be aware that this pseudo-closure style cannot be +mixed with operators such as `<`, `>`, `&&`, `;`, `()` and so on. + +### Usage + +```bash + builder_run_action action[:target] command [command-params...] +``` + +### Parameters + +* 1: `action[:target]` name of action, and optionally also target, if target + excluded starts for all defined targets +* 2: command command to run if action is started +* 3...: command-params parameters for command + +### Example + +The following example shows a sensible pattern to use when you have a single +multi-command action and other single-command actions. If most of your actions +are multi-command, you may choose to use this approach, or stick with the +longhand form. + +```bash + function do_build() { + mkdir -p build/cjs-src + npm run build + } + + builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo + builder_run_action configure verify_npm_setup + builder_run_action build do_build +``` + +-------------------------------------------------------------------------------- + ## `builder_run_child_actions` function Executes the specified actions on or all child targets, or on the specified @@ -937,6 +1009,8 @@ Note: it is recommended that you use `$(builder_term text)` instead of [standard builder parameters]: #standard-builder-parameters [`builder_describe`]: #builderdescribe-function +[`builder_describe_outputs`]: #builderdescribeoutputs-function +[`builder_describe_internal_dependency`]: #builderdescribeinternaldependency-function [`builder_display_usage`]: #builderdisplayusage-function [`$builder_extra_params`]: #builderextraparams-variable [`builder_finish_action`]: #builderfinishaction-function @@ -947,8 +1021,9 @@ Note: it is recommended that you use `$(builder_term text)` instead of [`builder_use_color`]: #builderusecolor-function [`$builder_verbose`]: #builderverbose-variable [formatting variables]: #formatting-variables +[`builder_run_action`]: #builderrunaction-function [`builder_run_child_actions`]: #builderrunchildactions-function [`builder_echo`]: #builderecho-function [`builder_die`]: #builderdie-function [`builder_echo_debug`]: #builderechodebug-function -[`builder_is_debug_build`]: #builderisdebugbuild-function +[`builder_is_debug_build`]: #builderisdebugbuild-function \ No newline at end of file diff --git a/resources/build/build-utils.sh b/resources/build/build-utils.sh index 3cff571b218d5dd7eb9d33cca72a2e3aabe8dc2f..7ae8db2ae1e90a292f1675cd94001b2391d61181 100755 --- a/resources/build/build-utils.sh +++ b/resources/build/build-utils.sh @@ -30,7 +30,7 @@ # # Exit on command failure and when using unset variables: -# TODO #8693: set -eu +set -eu # # Prevents 'clear' on exit of mingw64 bash shell @@ -293,7 +293,7 @@ set_keyman_standard_build_path() { # For CI compatbility of building Keyman for Android 16.0 with OpenJDK 8, # this overrides JAVA_HOME for the builder script to use OpenJDK 11. set_java_home() { - if [[ ! -z {$JAVA_HOME_11+x} ]]; then + if [[ ! -z ${JAVA_HOME_11+x} ]]; then builder_echo "Setting JAVA_HOME to JAVA_HOME_11 ($JAVA_HOME_11)" export JAVA_HOME="${JAVA_HOME_11}" fi diff --git a/resources/builder.inc.sh b/resources/builder.inc.sh index 385d03816f0a9c23cc178bc3b8c0e19301921928..f9440ae83eb36f3f293a56efaee5de96bccce8ce 100755 --- a/resources/builder.inc.sh +++ b/resources/builder.inc.sh @@ -359,7 +359,7 @@ _builder_failure_trap() { # finishes. # _builder_cleanup_deps() { - if ! builder_is_dep_build && [[ ! -z ${_builder_deps_built+x} ]]; then + if ! builder_is_dep_build && ! builder_is_child_build && [[ ! -z ${_builder_deps_built+x} ]]; then if $_builder_debug_internal; then builder_echo_debug "Dependencies that were built:" cat "$_builder_deps_built" @@ -523,6 +523,48 @@ builder_has_action() { fi } +# +# Wraps builder_start_action and builder_finish action for single-command +# actions. Can be used together with a local function for multi-command actions. +# Do be aware that this pseudo-closure style cannot be mixed with operators such +# as `<`, `>`, `&&`, `;`, `()` and so on. +# +# ### Usage +# +# ```bash +# builder_run_action action[:target] command [command-params...] +# ``` +# +# ### Parameters +# +# * 1: `action[:target]` name of action, and optionally also target, if target +# excluded starts for all defined targets +# * 2: command command to run if action is started +# * 3...: command-params parameters for command +# +# ### Example +# +# ```bash +# function do_build() { +# mkdir -p build/cjs-src +# npm run build +# } +# +# builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo +# builder_run_action configure verify_npm_setup +# builder_run_action build do_build +# ``` +# +function builder_run_action() { + local action=$1 + shift + if builder_start_action $action; then + ($@) + builder_finish_action success $action + fi + return 0 +} + # # Returns `0` if the user has asked to perform action on target on the command # line, and then starts the action. Should be paired with @@ -1690,6 +1732,7 @@ builder_has_dependencies() { builder_has_module_been_built() { local module="$1" + if [[ -z ${_builder_deps_built+x} ]]; then # not in a builder context, so we assume a build is needed return 1 diff --git a/resources/locate_emscripten.inc.sh b/resources/locate_emscripten.inc.sh new file mode 100644 index 0000000000000000000000000000000000000000..a58b384089e30cc3faece91ee9476b11e0f6d485 --- /dev/null +++ b/resources/locate_emscripten.inc.sh @@ -0,0 +1,30 @@ +# shellcheck shell=bash +# no hashbang for .inc.sh + +# +# We don't want to rely on emcc.py being on the path, because Emscripten puts far +# too many things onto the path (in particular for us, node). +# +# The following comment suggests that we don't need emcc.py on the path. +# https://github.com/emscripten-core/emscripten/issues/4848#issuecomment-1097357775 +# +# So we try and locate emcc.py in common locations ourselves. The search pattern +# is: +# +# 1. Look for $EMSCRIPTEN_BASE (our primary emscripten variable), which should +# point to the folder that emcc.py is located in +# 2. Look for $EMCC which should point to the emcc.py executable +# 3. Look for emcc.py on the path +# +locate_emscripten() { + if [[ -z ${EMSCRIPTEN_BASE+x} ]]; then + if [[ -z ${EMCC+x} ]]; then + local EMCC=`which emcc.py` + [[ -z $EMCC ]] && builder_die "locate_emscripten: Could not locate emscripten (emcc.py) on the path or with \$EMCC or \$EMSCRIPTEN_BASE" + fi + [[ -x $EMCC ]] || builder_die "locate_emscripten: Variable EMCC ($EMCC) does not point to a valid executable emcc.py" + EMSCRIPTEN_BASE="$(dirname "$EMCC")" + fi + + [[ -x ${EMSCRIPTEN_BASE}/emcc.py ]] || builder_die "locate_emscripten: Variable EMSCRIPTEN_BASE ($EMSCRIPTEN_BASE) does not point to emcc.py's folder" +} \ No newline at end of file diff --git a/tsconfig.esm.json b/tsconfig.esm.json index f7c3999a4a4d5efcfb78a2947f940084364c8026..ddab85239860824c6ae3d640351d3e4086e4138f 100644 --- a/tsconfig.esm.json +++ b/tsconfig.esm.json @@ -12,12 +12,14 @@ { "path": "./developer/src/kmc/tsconfig.json" }, - { "path": "./developer/src/kmc-keyboard/test/tsconfig.json" }, - { "path": "./developer/src/kmc-keyboard/tsconfig.json" }, + // { "path": "./developer/src/kmc-analyze/test/tsconfig.json" }, + { "path": "./developer/src/kmc-analyze/tsconfig.json" }, { "path": "./developer/src/kmc-kmn/test/tsconfig.json" }, { "path": "./developer/src/kmc-kmn/tsconfig.json" }, { "path": "./developer/src/kmc-kmw/test/tsconfig.json" }, { "path": "./developer/src/kmc-kmw/tsconfig.json" }, + { "path": "./developer/src/kmc-ldml/test/tsconfig.json" }, + { "path": "./developer/src/kmc-ldml/tsconfig.json" }, { "path": "./developer/src/kmc-model/test/tsconfig.json" }, { "path": "./developer/src/kmc-model/tsconfig.json" }, //{ "path": "./developer/src/kmc-model-info/test/tsconfig.json" }, diff --git a/windows/src/desktop/kmshell/locale/am-ET/strings.xml b/windows/src/desktop/kmshell/locale/am-ET/strings.xml index 141ba5da55e5a856754c9acd319029547cf54392..20e21edee605f16cbe39b675397755799ad3e357 100644 --- a/windows/src/desktop/kmshell/locale/am-ET/strings.xml +++ b/windows/src/desktop/kmshell/locale/am-ET/strings.xml @@ -46,6 +46,10 @@ ይቅር + + ቋንቋ ይጨምሩ ወይም ያጥፉ... + + የፊደል ገበታ ሰሌዳውን ለመምረጥ ይህንን ምስል ይጫኑ @@ -64,6 +68,18 @@ እርዳታ + + + + ያጥፉ + + + + ያንቁ + + + + እንዳይሰራ ያድርጉ @@ -174,6 +190,10 @@ ቋንቋ ይጨምሩ + + ቋንቋ ይጨምሩ ወይም ያጥፉ + + የስክሪን ላይ የፊደል ገበታ፦ @@ -223,7 +243,7 @@ - የፊደል ገበታ ሰሌዳ አማራጮች + የፊደል ገበታ ሰሌዳ አማራጮች... @@ -341,7 +361,7 @@ - (ባዶ ነው) + (አማራጭ ሆትኪዪ የለም) diff --git a/windows/src/desktop/setup/locale/am-ET/strings.xml b/windows/src/desktop/setup/locale/am-ET/strings.xml index 1c4f4252d9e83752e0ab45334b23a0a88fc57d57..b67dc6851a1bf57b7359bf83b897df6ed81eb49d 100644 --- a/windows/src/desktop/setup/locale/am-ET/strings.xml +++ b/windows/src/desktop/setup/locale/am-ET/strings.xml @@ -72,9 +72,7 @@ %0:s በማውረድ ላይ %0:s በማውረድ ላይ - $APPNAME ማዋቀር ተጨማሪ አጋዥ ሪሶርሶችን ለማውረድ ከkeyman.com ጋር መገናኘት አልቻለም። - -እባክዎን የኢንተርኔት ኮኔክሽን መኖሩን ያረጋግጡ እና በፋየርዎልዎ ቅንብሮች ውስጥ በይነመረብን ለመድረስ ለ $APPNAME ማዋቀር ፈቃድ ይስጡ። - -ከቅንብር ለመውጣት (Abort)ን ይምረጡ፣ እንደገና ሪሶርሶቹን ለማውረድ እንደገና ይሞክሩ፣ ወይም ከኢንተርኔት ውጭ ለመቀጠል ችላ ይበሉት። + $APPNAME ማቀነባበሪያው ተጨማሪ አጋዥ ሪሶርሶችን ለማውረድ ከkeyman.com ጋር መገናኘት አልቻለም። + እባክዎን የኢንተርኔት ኮኔክሽን መኖሩን ያረጋግጡ እና በፋየርዎልዎ ቅንብሮች ውስጥ በይነመረብን ለመድረስ ለ $APPNAME ማቀነባበሪያውን ፈቃድ ይስጡ። + ከማቀነባበሪያው ለመውጣት (Abort) ን ይምረጡ፣ እንደገና ሪሶርሶቹን ለማውረድ ይሞክሩ፣ ወይም ከኢንተርኔት ውጭ ለመቀጠል ይሞክሩ። diff --git a/windows/src/engine/keyman32/K32_load.cpp b/windows/src/engine/keyman32/K32_load.cpp index 190cf53e79eff5ab23de777e4c8524e9a715ae98..e29e504b2a1ce49568438f1bdf9dc789eae7675d 100644 --- a/windows/src/engine/keyman32/K32_load.cpp +++ b/windows/src/engine/keyman32/K32_load.cpp @@ -45,12 +45,6 @@ BOOL GetKeyboardFileName(LPSTR kbname, LPSTR buf, int nbuf) { PKEYMAN64THREADDATA _td = ThreadGlobals(); if(!_td) return FALSE; - if(_td->ForceFileName[0]) - { - strncpy_s(buf, nbuf, _td->ForceFileName, nbuf - 1); - buf[nbuf-1] = 0; - return TRUE; - } int n = 0; RegistryReadOnly *reg = Reg_GetKeymanInstalledKeyboard(kbname); @@ -75,9 +69,9 @@ BOOL GetKeyboardFileName(LPSTR kbname, LPSTR buf, int nbuf) return n; } -BOOL LoadlpKeyboardCore(int i) +BOOL LoadlpKeyboard(int i) { - SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboardCore: Enter ---"); + SendDebugMessageFormat(0, sdmLoad, 0, "%s: Enter ---", __FUNCTION__); PKEYMAN64THREADDATA _td = ThreadGlobals(); if (!_td) return FALSE; @@ -85,7 +79,7 @@ BOOL LoadlpKeyboardCore(int i) if (_td->lpActiveKeyboard == &_td->lpKeyboards[i]) _td->lpActiveKeyboard = NULL; // I822 TSF not working if (_td->lpKeyboards[i].lpCoreKeyboardState) { - SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboardCore: a keyboard km_kbp_state exits without matching keyboard - disposing of state"); + SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: a keyboard km_kbp_state exits without matching keyboard - disposing of state"); km_kbp_state_dispose(_td->lpKeyboards[i].lpCoreKeyboardState); _td->lpKeyboards[i].lpCoreKeyboardState = NULL; } @@ -95,7 +89,7 @@ BOOL LoadlpKeyboardCore(int i) PWCHAR keyboardPath = strtowstr(buf); km_kbp_status err_status = km_kbp_keyboard_load(keyboardPath, &_td->lpKeyboards[i].lpCoreKeyboard); if (err_status != KM_KBP_STATUS_OK) { - SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboardCore: km_kbp_keyboard_load failed for %ls with error status [%d]", keyboardPath, err_status); + SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: km_kbp_keyboard_load failed for %ls with error status [%d]", keyboardPath, err_status); delete keyboardPath; return FALSE; } @@ -104,7 +98,7 @@ BOOL LoadlpKeyboardCore(int i) km_kbp_option_item *core_environment = nullptr; if(!SetupCoreEnvironment(&core_environment)) { - SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboardCore: Unable to set environment options for keyboard %ls", keyboardPath); + SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: Unable to set environment options for keyboard %ls", keyboardPath); return FALSE; } @@ -114,7 +108,7 @@ BOOL LoadlpKeyboardCore(int i) if (err_status != KM_KBP_STATUS_OK) { SendDebugMessageFormat( - 0, sdmLoad, 0, "LoadlpKeyboardCore: km_kbp_state_create failed with error status [%d]", err_status); + 0, sdmLoad, 0, "LoadlpKeyboard: km_kbp_state_create failed with error status [%d]", err_status); // Dispose of the keyboard to leave us in a consistent state ReleaseKeyboardMemoryCore(&_td->lpKeyboards[i].lpCoreKeyboard); return FALSE; @@ -122,38 +116,15 @@ BOOL LoadlpKeyboardCore(int i) // Register callback? err_status = km_kbp_keyboard_get_imx_list(_td->lpKeyboards[i].lpCoreKeyboard, &_td->lpKeyboards[i].lpIMXList); if (err_status != KM_KBP_STATUS_OK) { - SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboardCore: km_kbp_keyboard_get_imx_list failed with error status [%d]", err_status); + SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: km_kbp_keyboard_get_imx_list failed with error status [%d]", err_status); // Dispose of the keyboard to leave us in a consistent state ReleaseKeyboardMemoryCore(&_td->lpKeyboards[i].lpCoreKeyboard); return FALSE; } - LoadDLLsCore(&_td->lpKeyboards[i]); - - LoadKeyboardOptionsREGCore(&_td->lpKeyboards[i], _td->lpKeyboards[i].lpCoreKeyboardState); - - return TRUE; -} - -BOOL LoadlpKeyboard(int i) -{ - if (Globals::get_CoreIntegration()) - { - return LoadlpKeyboardCore(i); - } - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - if(_td->lpKeyboards[i].Keyboard) return TRUE; - if(_td->lpActiveKeyboard == &_td->lpKeyboards[i]) _td->lpActiveKeyboard = NULL; // I822 TSF not working - - char buf[256]; - if(!GetKeyboardFileName(_td->lpKeyboards[i].Name, buf, 255)) return FALSE; - - if(!LoadKeyboard(buf, &_td->lpKeyboards[i].Keyboard)) return FALSE; // I5136 - LoadDLLs(&_td->lpKeyboards[i]); - LoadKeyboardOptions(&_td->lpKeyboards[i]); + LoadKeyboardOptionsRegistrytoCore(&_td->lpKeyboards[i], _td->lpKeyboards[i].lpCoreKeyboardState); return TRUE; } diff --git a/windows/src/engine/keyman32/addins.cpp b/windows/src/engine/keyman32/addins.cpp deleted file mode 100644 index 6c7f5d0f285842d9a47753bc56081219c6fbd662..0000000000000000000000000000000000000000 --- a/windows/src/engine/keyman32/addins.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/* - Name: addins - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 14 Jun 2008 - - Modified Date: 14 May 2010 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 14 Jun 2008 - mcdurdin - I1488 - Fix registry handle leak - 11 Mar 2009 - mcdurdin - I1894 - Fix threading bugs introduced in I1888 - 11 Dec 2009 - mcdurdin - I934 - x64 - Initial version - 12 Mar 2010 - mcdurdin - I934 - x64 - Complete - 12 Mar 2010 - mcdurdin - I2229 - Remove hints and warnings - 04 May 2010 - mcdurdin - I2351 - Robustness - verify _td return value - 14 May 2010 - mcdurdin - I2374 - Fix crash in some situations -*/ - -#include "pch.h" - -void Addin_Release() -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return; - - //SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addin_Release: ENTER [%d]", nAddins); - if(_td->Addins) - { - for(int i = 0; i < _td->nAddins; i++) - if(_td->Addins[i].hAddin) - { - if(_td->Addins[i].Uninitialise) (*_td->Addins[i].Uninitialise)(); - FreeLibrary(_td->Addins[i].hAddin); - } - delete[] _td->Addins; - } - _td->Addins = NULL; - _td->nAddins = 0; - _td->CurrentAddin = -1; - //SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addin_Release: EXIT"); -} - -void ReadAddins(HKEY hkey) -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return; - - //SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: ReadAddins: ENTER"); - RegistryReadOnly *reg = new RegistryReadOnly(hkey); - if(reg->OpenKeyReadOnly(hkey == HKEY_CURRENT_USER ? REGSZ_KeymanAddinsCU : REGSZ_KeymanAddinsLM)) - { - int n = _td->nAddins; - char buf[128]; - while(reg->GetValueNames(buf, 128, n)) - { - Addin *a = new Addin[n+1]; - if(_td->Addins) - { - memcpy(a, _td->Addins, n * sizeof(Addin)); - delete[] _td->Addins; - } - _td->Addins = a; - _td->Addins[n].hAddin = 0; - _td->Addins[n].FocusChanged = NULL; - _td->Addins[n].Initialise = NULL; - _td->Addins[n].OutputBackspace = NULL; - _td->Addins[n].OutputChar = NULL; - _td->Addins[n].Uninitialise = NULL; - _td->Addins[n].ShouldProcess = NULL; - strcpy(_td->Addins[n].ClassName, buf); - reg->ReadString(buf, _td->Addins[n].AddinName, 260); - _td->Addins[n].Application[0] = 0; - n++; - } - _td->nAddins = n; - } - delete reg; - //SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: ReadAddins: EXIT"); -} - -void Addin_Refresh() -{ - //SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addin_Refresh: ENTER"); - Addin_Release(); - ReadAddins(HKEY_CURRENT_USER); - ReadAddins(HKEY_LOCAL_MACHINE); - //SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addin_Refresh: EXIT"); -} - -BOOL LoadAddin() -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - if(_td->CurrentAddin == -1) return FALSE; - - Addin *a = &_td->Addins[_td->CurrentAddin]; - if(!a->hAddin) - { - //SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: LoadAddin: ENTER"); - a->hAddin = LoadLibrary(a->AddinName); - if(!a->hAddin) - { - a->hAddin = 0; - a->ClassName[0] = 0; // prevent add-in attempting to load again - _td->CurrenthWnd = 0; - _td->CurrentAddin = -1; - //SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: LoadAddin: EXIT - FALSE - LoadLibrary"); - return FALSE; - } - a->OutputBackspace = (PKeymanOutputBackspace) GetProcAddress(a->hAddin, "KeymanOutputBackspace"); - a->OutputChar = (PKeymanOutputChar) GetProcAddress(a->hAddin, "KeymanOutputChar"); - a->FocusChanged = (PKeymanFocusChanged) GetProcAddress(a->hAddin, "KeymanFocusChanged"); - a->ShouldProcess = (PKeymanShouldProcess) GetProcAddress(a->hAddin, "KeymanShouldProcess"); - a->Initialise = (PKeymanInit) GetProcAddress(a->hAddin, "KeymanInitialise"); - a->Uninitialise = (PKeymanInit) GetProcAddress(a->hAddin, "KeymanUninitialise"); - - if(a->Initialise && !(*a->Initialise)()) - { - FreeLibrary(a->hAddin); - a->hAddin = 0; - a->ClassName[0] = 0; - _td->CurrenthWnd = 0; - _td->CurrentAddin = -1; - //SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: LoadAddin: EXIT - FALSE - Initialise"); - return FALSE; - } - //SendDebugMessageFormat(GetFocus(), sdmGlobal, 0, "Addins: LoadAddin: EXIT - TRUE - Loaded"); - } - return TRUE; -} - -BOOL Addin_ShouldProcessUnichar(HWND hwnd) -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ShouldProcessUnichar: ENTER"); - _td->CurrenthWnd = hwnd; - GetClassName(hwnd, _td->CurrentClassName, 128); - if(_td->CurrentAddin >= 0 && !_strcmpi(_td->CurrentClassName, _td->Addins[_td->CurrentAddin].ClassName)) - { - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ShouldProcessUnichar: EXIT - TRUE - CurrentAddin okay"); - return TRUE; - } - - for(int i = 0; i < _td->nAddins; i++) - if(!_strcmpi(_td->CurrentClassName, _td->Addins[i].ClassName)) - { - _td->CurrentAddin = i; - if(!LoadAddin()) - { - _td->CurrentAddin = -1; - _td->Addins[i].ClassName[0] = 0; // prevent add-in attempting to load again - } - else if(_td->Addins[i].ShouldProcess && !(*_td->Addins[i].ShouldProcess)(hwnd)) - _td->CurrentAddin = -1; - else - { - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ShouldProcessUnichar: EXIT - TRUE - FoundAddin"); - return TRUE; - } - } - _td->CurrentAddin = -1; - _td->CurrenthWnd = 0; - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ShouldProcessUnichar: EXIT - FALSE"); - return FALSE; -} - -BOOL Addin_ProcessUnichar(HWND hwnd, DWORD chr) -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessUnichar: ENTER"); - if(_td->CurrenthWnd != hwnd) - if(!Addin_ShouldProcessUnichar(hwnd)) - { - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessUnichar: EXIT - FALSE - Addin_ShouldProcessUnichar"); - return FALSE; - } - - if(!LoadAddin()) - { - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessUnichar: EXIT - FALSE - !LoadAddin)"); - return FALSE; - } - if(!_td->Addins[_td->CurrentAddin].OutputChar) - { - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessUnichar: EXIT - FALSE !OutputChar"); - return FALSE; - } - - BOOL b = (*_td->Addins[_td->CurrentAddin].OutputChar)(hwnd, chr); - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessUnichar: EXIT (b) == %d", b); - return b; -} - -BOOL Addin_ProcessBackspace(HWND hwnd) -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessBackspace: ENTER"); - if(_td->CurrenthWnd != hwnd) - if(!Addin_ShouldProcessUnichar(hwnd)) - { - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessBackspace: EXIT - !ShouldProcess"); - return FALSE; - } - - if(!LoadAddin()) - { - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessBackspace: EXIT - !LoadAddin"); - return FALSE; - } - if(!_td->Addins[_td->CurrentAddin].OutputBackspace) - { - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessBackspace: EXIT - !OutputBackspace"); - return FALSE; - } - - BOOL b = (*_td->Addins[_td->CurrentAddin].OutputBackspace)(hwnd); - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_ProcessBackspace: EXIT - (b) = %d", b); - return b; -} - -void Addin_FocusChanged(HWND hwnd) -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return; - - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_FocusChanged: ENTER"); - - if(_td->CurrenthWnd != hwnd) - if(!Addin_ShouldProcessUnichar(hwnd)) - { - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_FocusChanged: EXIT - !ShouldProcess"); - return; - } - - if(!LoadAddin()) - { - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_FocusChanged: EXIT - !LoadAddin"); - return; - } - - // Addin variables must be valid now - - if(!_td->Addins[_td->CurrentAddin].FocusChanged) - { - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_FocusChanged: EXIT - !FocusChanged"); - return; - } - - (*_td->Addins[_td->CurrentAddin].FocusChanged)(); - //SendDebugMessageFormat(hwnd, sdmGlobal, 0, "Addin_FocusChanged: EXIT"); -} diff --git a/windows/src/engine/keyman32/addins.h b/windows/src/engine/keyman32/addins.h deleted file mode 100644 index 71aa006e59c41ab2ec71517b3e24e76dc16e2e36..0000000000000000000000000000000000000000 --- a/windows/src/engine/keyman32/addins.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - Name: addins - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 11 Dec 2009 - - Modified Date: 11 Dec 2009 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 11 Dec 2009 - mcdurdin - I934 - x64 - Initial version -*/ -void Addin_Release(); -void Addin_Refresh(); -BOOL Addin_ShouldProcessUnichar(HWND hwnd); -BOOL Addin_ProcessUnichar(HWND hwnd, DWORD chr); -BOOL Addin_ProcessBackspace(HWND hwnd); -void Addin_FocusChanged(HWND hwnd); diff --git a/windows/src/engine/keyman32/appint/aiTIP.cpp b/windows/src/engine/keyman32/appint/aiTIP.cpp index 23240b2a067b5d517653b3b1da6f678cb7b5344f..34f3afc867f5c243898a0c0452cc2e89479859c6 100644 --- a/windows/src/engine/keyman32/appint/aiTIP.cpp +++ b/windows/src/engine/keyman32/appint/aiTIP.cpp @@ -126,7 +126,6 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM BOOL isUp = keyFlags & KF_UP ? TRUE : FALSE; BOOL extended = keyFlags & KF_EXTENDED ? TRUE : FALSE; BYTE scan = keyFlags & 0xFF; - BOOL isUsingCoreProcessor = Globals::get_CoreIntegration(); SendDebugMessageFormat(0, sdmAIDefault, 0, "TIPProcessKey: Enter VirtualKey=%s lParam=%x IsUp=%d Extended=%d Updateable=%d Preserved=%d", Debug_VirtualKey((WORD) wParam), lParam, isUp, extended, Updateable, Preserved); @@ -163,72 +162,31 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM // core processor. The core processor has the keyboard Caps Lock stores and will // queue an action 'KM_KBP_IT_CAPSLOCK'. In processing the action the Windows engine will synthesise keystrokes // to ensure caps lock is in the correct state. - if (isUsingCoreProcessor) { - if (!Preserved) { - switch (wParam) { - case VK_MENU: - case VK_CONTROL: - ProcessModifierChange((UINT)wParam, isUp, extended); - return FALSE; - case VK_NUMLOCK: + if (!Preserved) { + switch (wParam) { + case VK_MENU: + case VK_CONTROL: + ProcessModifierChange((UINT)wParam, isUp, extended); + return FALSE; + case VK_NUMLOCK: + if (!isUp) + ProcessToggleChange((UINT)wParam); // I4793 + return FALSE; + case VK_CAPITAL: if (!isUp) - ProcessToggleChange((UINT)wParam); // I4793 - return FALSE; - case VK_CAPITAL: - if (!isUp) - ProcessToggleChange((UINT)wParam); // I4793 - break; - case VK_SHIFT: - ProcessModifierChange((UINT)wParam, isUp, extended); + ProcessToggleChange((UINT)wParam); // I4793 break; - } - } else { - // Mask out Ctrl, Shift and Alt and include new modifiers // I4548 - DWORD NewShiftState = TSFShiftToShift(lParam); // I3588 - SendDebugMessageFormat( - 0, sdmGlobal, 0, "TIPProcessKey: TSFShiftToShift start with %x, include %x", LocalShiftState, NewShiftState); - *Globals::ShiftState() = (LocalShiftState & K_NOTMODIFIERFLAG) | NewShiftState; // I3588 - } - } else { // using windows processor TODO: #5442 Remove this else block - if (!Preserved) { - switch (wParam) { - case VK_CAPITAL: - if (!isUp) - ProcessToggleChange((UINT)wParam); // I4793 - if (!Updateable) { - // We only want to process the Caps Lock key event once -- - // in the first pass (!Updateable). - KeyCapsLockPress(isUp); // I4548 - } - return FALSE; - case VK_SHIFT: - if (!Updateable) { - // We only want to process the Shift key event once -- - // in the first pass (!Updateable). - KeyShiftPress(isUp); // I4548 - } - // Fall through - case VK_MENU: - case VK_CONTROL: - ProcessModifierChange((UINT)wParam, isUp, extended); - return FALSE; - case VK_NUMLOCK: - if (!isUp) - ProcessToggleChange((UINT)wParam); // I4793 - return FALSE; - } - // This would only get here if none of the above cases matched why not use default in the switch? - if (isUp) { - return FALSE; // return value ignored in this case; we only needed it for testing anyway - } - } else { - // Mask out Ctrl, Shift and Alt and include new modifiers // I4548 - DWORD NewShiftState = TSFShiftToShift(lParam); // I3588 - SendDebugMessageFormat( - 0, sdmGlobal, 0, "TIPProcessKey: TSFShiftToShift start with %x, include %x", LocalShiftState, NewShiftState); - *Globals::ShiftState() = (LocalShiftState & K_NOTMODIFIERFLAG) | NewShiftState; // I3588 + case VK_SHIFT: + ProcessModifierChange((UINT)wParam, isUp, extended); + break; } - } // TODO: #5442 Remove this else block ^^ + } else { + // Mask out Ctrl, Shift and Alt and include new modifiers // I4548 + DWORD NewShiftState = TSFShiftToShift(lParam); // I3588 + SendDebugMessageFormat( + 0, sdmGlobal, 0, "TIPProcessKey: TSFShiftToShift start with %x, include %x", LocalShiftState, NewShiftState); + *Globals::ShiftState() = (LocalShiftState & K_NOTMODIFIERFLAG) | NewShiftState; // I3588 + } _td->TIPFUpdateable = Updateable; _td->TIPFPreserved = Preserved; // I4290 @@ -239,16 +197,7 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM _td->state.vkey = (WORD) wParam; _td->state.isDown = !isUp; - if (isUsingCoreProcessor) { - _td->state.lpCoreKb = _td->lpActiveKeyboard->lpCoreKeyboard; - - } else { - _td->state.lpkb = _td->lpActiveKeyboard->Keyboard; - _td->state.startgroup = &_td->state.lpkb->dpGroupArray[_td->state.lpkb->StartGroup[BEGIN_UNICODE]]; - _td->state.NoMatches = TRUE; - _td->state.LoopTimes = 0; - _td->state.StopOutput = FALSE; - } + _td->state.lpCoreKb = _td->lpActiveKeyboard->lpCoreKeyboard; _td->state.windowunicode = TRUE; @@ -257,30 +206,8 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM _td->TIPProcessOutput = outfunc; _td->TIPGetContext = ctfunc; - AppContextWithStores *savedContext = NULL; // I4370 // I4978 - - if (!Updateable) { - // The core processor km_kbp_process_event is only called once per key stroke - // therefore there is no need to preserve context and keyboard actions - if (!isUsingCoreProcessor) { - savedContext = new AppContextWithStores(_td->lpActiveKeyboard->Keyboard->cxStoreArray); // I4370 // I4978 - _td->app->SaveContext(savedContext); - } - } - BOOL res = ProcessHook(); - if (!Updateable) { - if (!isUsingCoreProcessor) { - if (res) { // I4585 // I4370 - // Reset the context if match found - _td->app->RestoreContext(savedContext); - delete savedContext; - savedContext = NULL; - } - } - } - _td->TIPProcessOutput = NULL; _td->TIPGetContext = NULL; @@ -439,65 +366,6 @@ void AITIP::ReadContext() { } } -AppContextWithStores::AppContextWithStores(int nKeyboardOptions) : AppContext() { // I4978 - this->nKeyboardOptions = nKeyboardOptions; - KeyboardOptions = new INTKEYBOARDOPTIONS[nKeyboardOptions]; - memset(KeyboardOptions, 0, sizeof(INTKEYBOARDOPTIONS) * nKeyboardOptions); -} - -AppContextWithStores::~AppContextWithStores() { // I4978 - for(DWORD i = 0; i < nKeyboardOptions; i++) { - if(KeyboardOptions[i].Value) delete KeyboardOptions[i].Value; - } - delete KeyboardOptions; -} - -void AITIP::SaveContext(AppContextWithStores *savedContext) { // I4370 // I4978 - savedContext->CopyFrom(context); - - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td || !_td->lpActiveKeyboard || !_td->lpActiveKeyboard->Keyboard) return; - - assert(savedContext->nKeyboardOptions == _td->lpActiveKeyboard->Keyboard->cxStoreArray); - - for(DWORD i = 0; i < savedContext->nKeyboardOptions; i++) { // I4978 - if(_td->lpActiveKeyboard->KeyboardOptions[i].Value != NULL) { - savedContext->KeyboardOptions[i].Value = new WCHAR[wcslen(_td->lpActiveKeyboard->KeyboardOptions[i].Value)+1]; - wcscpy_s(savedContext->KeyboardOptions[i].Value, wcslen(_td->lpActiveKeyboard->KeyboardOptions[i].Value)+1, _td->lpActiveKeyboard->KeyboardOptions[i].Value); - } - } -} - -void AITIP::RestoreContext(AppContextWithStores *savedContext) { // I4370 // I4978 - context->CopyFrom(savedContext); - - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td || !_td->lpActiveKeyboard || !_td->lpActiveKeyboard->Keyboard) return; - LPINTKEYBOARDINFO kp = _td->lpActiveKeyboard; - - assert(savedContext->nKeyboardOptions == kp->Keyboard->cxStoreArray); - - for(DWORD i = 0; i < savedContext->nKeyboardOptions; i++) { // I4978 - if(kp->KeyboardOptions[i].Value == NULL && savedContext->KeyboardOptions[i].Value != NULL) { - // Restore the previously saved value as it was reset - kp->KeyboardOptions[i].OriginalStore = kp->Keyboard->dpStoreArray[i].dpString; - kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].Value = savedContext->KeyboardOptions[i].Value; - savedContext->KeyboardOptions[i].Value = NULL; - } else if(kp->KeyboardOptions[i].Value != NULL && savedContext->KeyboardOptions[i].Value == NULL) { - // Clear the newly saved value back to the default - delete kp->KeyboardOptions[i].Value; - kp->KeyboardOptions[i].Value = NULL; - kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].OriginalStore; - } else if(kp->KeyboardOptions[i].Value != NULL && savedContext->KeyboardOptions[i].Value != NULL && - wcscmp(kp->KeyboardOptions[i].Value, savedContext->KeyboardOptions[i].Value) != 0) { - // Restore the previously saved value as it was changed - delete kp->KeyboardOptions[i].Value; - kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].Value = savedContext->KeyboardOptions[i].Value; - savedContext->KeyboardOptions[i].Value = NULL; - } - } -} - void AITIP::CopyContext(AppContext *savedContext) { savedContext->CopyFrom(context); } @@ -722,49 +590,3 @@ void FillStoreOffsets(AIDEBUGINFO *di) } di->StoreOffsets[n] = 0xFFFF; } - -BOOL AITIP::QueueDebugInformation(int ItemType, LPGROUP Group, LPKEY Rule, PWSTR fcontext, PWSTR foutput, DWORD_PTR dwExtraFlags) -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return TRUE; - if(!_td->ForceFileName[0]) return TRUE; - - SendDebugMessageFormat(0, sdmAIDefault, 0, "AIDebugger::QueueDebugInformation ItemType=%d", ItemType); - AIDEBUGINFO di; - di.cbSize = sizeof(AIDEBUGINFO); - di.ItemType = ItemType; // int - di.Context = fcontext; // PWSTR - di.Rule = Rule; // LPKEY - di.Group = Group; // LPGROUP - di.Output = foutput; // PWSTR - di.Flags = dwExtraFlags; // DWORD - - if(di.Rule) FillStoreOffsets(&di); - - // data required - // keystroke - // context for rule - // if rule, then output of rule - // match positions for all stores in rule - - if(DebugControlled()) - SendMessage(GetDebugControlWindow(), WM_KEYMANDEBUG_RULEMATCH, ItemType, (LPARAM) &di); - - return TRUE; -} - -typedef BOOL(WINAPI *PREFRESHPRESERVEDKEYSFUNC)(BOOL Activating); - -void RefreshPreservedKeys(BOOL Activating) { -#ifdef _WIN64 - HMODULE hModule = GetModuleHandle("kmtip64"); -#else - HMODULE hModule = GetModuleHandle("kmtip"); -#endif - if (hModule != NULL) { - PREFRESHPRESERVEDKEYSFUNC pRefreshPreservedKeys = (PREFRESHPRESERVEDKEYSFUNC)GetProcAddress(hModule, "RefreshPreservedKeys"); - if (pRefreshPreservedKeys) { - pRefreshPreservedKeys(Activating); - } - } -} diff --git a/windows/src/engine/keyman32/appint/aiTIP.h b/windows/src/engine/keyman32/appint/aiTIP.h index 7ff5aff4ef2c29164363ed6860f370a65cdcea20..3c6fd9b6980b7bcf139b77dac28cace83bf134bc 100644 --- a/windows/src/engine/keyman32/appint/aiTIP.h +++ b/windows/src/engine/keyman32/appint/aiTIP.h @@ -71,11 +71,6 @@ public: BOOL DebugControlled(); - // TODO: 5442 This would be better to called SaveContextWithStores or SaveContextWithKbdOptions - // Will be removed with 5442 when removing window core - void SaveContext(AppContextWithStores *savedContext); // I4370 // I4978 - void RestoreContext(AppContextWithStores *savedContext); // I4370 // I4978 - /** * Copy the member context * @@ -106,7 +101,6 @@ public: /* Queue and sending functions */ virtual BOOL SendActions(); // I4196 - virtual BOOL QueueDebugInformation(int ItemType, LPGROUP Group, LPKEY Rule, PWSTR fcontext, PWSTR foutput, DWORD_PTR dwExtraFlags); /* TIP interactions */ diff --git a/windows/src/engine/keyman32/appint/aiWin2000Unicode.cpp b/windows/src/engine/keyman32/appint/aiWin2000Unicode.cpp index 19da87a543581223b8aaf860cda5116bdbbfc28e..a6b540fd47a1c4a9d09ee7c5cca65388ae263b7d 100644 --- a/windows/src/engine/keyman32/appint/aiWin2000Unicode.cpp +++ b/windows/src/engine/keyman32/appint/aiWin2000Unicode.cpp @@ -1,18 +1,18 @@ /* Name: AIWin2000Unicode Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 22 Jan 2007 Modified Date: 9 Aug 2015 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 22 Jan 2007 - mcdurdin - Fix for K_NPENTER 13 Jul 2007 - mcdurdin - I934 - Prep fox x64 23 Aug 2007 - mcdurdin - I719 - Fix Alt+LeftShift and Word interactions @@ -43,12 +43,6 @@ #include "pch.h" // I4128 // I4287 #include "serialkeyeventclient.h" -#define KEYMAN_MOREPOST "WM_KMMOREPOST" - -#ifndef WM_UNICHAR -#define WM_UNICHAR 0x0109 -#define UNICODE_NOCHAR 0xFFFF -#endif AIWin2000Unicode::AIWin2000Unicode() { @@ -70,13 +64,13 @@ BOOL AIWin2000Unicode::CanHandleWindow(HWND ahwnd) } BOOL AIWin2000Unicode::HandleWindow(HWND ahwnd) -{ +{ if(hwnd != ahwnd) { - hwnd = ahwnd; + hwnd = ahwnd; context->Reset(); } - return TRUE; + return TRUE; } BOOL AIWin2000Unicode::IsWindowHandled(HWND ahwnd) @@ -84,11 +78,11 @@ BOOL AIWin2000Unicode::IsWindowHandled(HWND ahwnd) return (hwnd == ahwnd); } -BOOL AIWin2000Unicode::IsUnicode() -{ +BOOL AIWin2000Unicode::IsUnicode() +{ BOOL Result = IsWindowUnicode(hwnd); SendDebugMessageFormat(0, sdmAIDefault, 0, "IsWindowUnicode=%s", Result ? "Yes" : "No"); - return Result; + return Result; } /* Context functions */ @@ -96,7 +90,7 @@ BOOL AIWin2000Unicode::IsUnicode() void AIWin2000Unicode::ReadContext() { } - + void AIWin2000Unicode::AddContext(WCHAR ch) //I2436 { context->Add(ch); @@ -121,7 +115,7 @@ void AIWin2000Unicode::SetContext(const WCHAR* buf) { return context->Set(buf); } - + BYTE SavedKbdState[256]; BOOL AIWin2000Unicode::SendActions() // I4196 @@ -137,7 +131,7 @@ BOOL AIWin2000Unicode::QueueAction(int ItemType, DWORD dwData) int result = AppIntegration::QueueAction(ItemType, dwData); //SendDebugMessageFormat(hwnd, sdmAIDefault, 0, "App::QueueAction ItemType=%d dwData=%x", ItemType, dwData); - + switch(ItemType) { case QIT_VKEYDOWN: @@ -166,27 +160,16 @@ BOOL AIWin2000Unicode::QueueAction(int ItemType, DWORD dwData) return result; } -BOOL AIWin2000Unicode::QueueDebugInformation(int ItemType, LPGROUP Group, LPKEY Rule, PWSTR fcontext, PWSTR foutput, DWORD_PTR dwExtraFlags) -{ - UNREFERENCED_PARAMETER(ItemType); - UNREFERENCED_PARAMETER(Group); - UNREFERENCED_PARAMETER(Rule); - UNREFERENCED_PARAMETER(fcontext); - UNREFERENCED_PARAMETER(foutput); - UNREFERENCED_PARAMETER(dwExtraFlags); - return TRUE; -} - // I1512 - SendInput with VK_PACKET for greater robustness BOOL AIWin2000Unicode::PostKeys() -{ +{ PKEYMAN64THREADDATA _td = ThreadGlobals(); if(!_td) { return FALSE; } - if(QueueSize == 0) + if(QueueSize == 0) { return TRUE; } @@ -208,7 +191,7 @@ BOOL AIWin2000Unicode::PostKeys() switch(Queue[n].ItemType) { case QIT_VKEYDOWN: if((Queue[n].dwData & QVK_KEYMASK) == 0x05) Queue[n].dwData = (Queue[n].dwData & QVK_FLAGMASK) | VK_RETURN; // I649 // I3438 - + /* 6.0.153.0: Fix repeat state for virtual keys */ if((Queue[n].dwData & QVK_KEYMASK) <= VK__MAX) // I3438 @@ -277,7 +260,6 @@ BOOL AIWin2000Unicode::PostKeys() break; case QIT_BACK: if(Queue[n].dwData & BK_DEADKEY) break; - if(Addin_ProcessBackspace(hwnd)) break; pInputs[i].type = INPUT_KEYBOARD; pInputs[i].ki.wVk = VK_BACK; diff --git a/windows/src/engine/keyman32/appint/aiWin2000Unicode.h b/windows/src/engine/keyman32/appint/aiWin2000Unicode.h index 73db7ac9073ceeefdbd138777f3a8ebf120122fd..1ee49ed001fef8fbb4dce79565beb835832f9800 100644 --- a/windows/src/engine/keyman32/appint/aiWin2000Unicode.h +++ b/windows/src/engine/keyman32/appint/aiWin2000Unicode.h @@ -1,18 +1,18 @@ /* Name: aiWin2000Unicode Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 27 Jan 2009 Modified Date: 23 Jun 2014 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 27 Jan 2009 - mcdurdin - I1797 - Add fallback for AIWin2000 app integration 11 Dec 2009 - mcdurdin - I934 - x64 - Initial version 24 Jun 2010 - mcdurdin - I2436 - Add space to context for AIWin2000Unicode when not matched @@ -43,7 +43,7 @@ public: virtual BOOL QueueAction(int ItemType, DWORD dwData); /* Information functions */ - + virtual BOOL CanHandleWindow(HWND ahwnd); virtual BOOL IsWindowHandled(HWND ahwnd); virtual BOOL HandleWindow(HWND ahwnd); @@ -59,9 +59,8 @@ public: virtual void SetContext(const WCHAR* buf); /* Queue and sending functions */ - + virtual BOOL SendActions(); // I4196 - virtual BOOL QueueDebugInformation(int ItemType, LPGROUP Group, LPKEY Rule, PWSTR fcontext, PWSTR foutput, DWORD_PTR dwExtraFlags); }; #endif diff --git a/windows/src/engine/keyman32/appint/appint.h b/windows/src/engine/keyman32/appint/appint.h index 3e9a4dfb4d49ed3993d173d1c508bd60ff60739f..6fe7ca7e201ef5016613ee292e3f86033ac2f643 100644 --- a/windows/src/engine/keyman32/appint/appint.h +++ b/windows/src/engine/keyman32/appint/appint.h @@ -43,22 +43,6 @@ typedef struct #define QIT_CAPSLOCK 8 #define QIT_INVALIDATECONTEXT 9 -// QueueDebugInformation ItemTypes -#define QID_BEGIN_UNICODE 0 -#define QID_BEGIN_ANSI 1 -#define QID_GROUP_ENTER 2 -#define QID_GROUP_EXIT 3 -#define QID_RULE_ENTER 4 -#define QID_RULE_EXIT 5 -#define QID_MATCH_ENTER 6 -#define QID_MATCH_EXIT 7 -#define QID_NOMATCH_ENTER 8 -#define QID_NOMATCH_EXIT 9 -#define QID_END 10 - -#define QID_FLAG_RECURSIVE_OVERFLOW 0x0001 -#define QID_FLAG_NOMATCH 0x0002 - #define QVK_EXTENDED 0x00010000 // Flag for QIT_VKEYDOWN to indicate an extended key #define QVK_KEYMASK 0x0000FFFF #define QVK_FLAGMASK 0xFFFF0000 @@ -178,15 +162,6 @@ public: }; -class AppContextWithStores : public AppContext // I4978 -{ -public: - AppContextWithStores(int nKeyboardOptions); - ~AppContextWithStores(); - DWORD nKeyboardOptions; - LPINTKEYBOARDOPTIONS KeyboardOptions; -}; - class AppIntegration:public AppActionQueue { protected: @@ -214,7 +189,6 @@ public: /* Queue and sending functions */ - virtual BOOL QueueDebugInformation(int ItemType, LPGROUP Group, LPKEY Rule, PWSTR fcontext, PWSTR foutput, DWORD_PTR dwExtraFlags) = 0; void SetCurrentShiftState(int ShiftFlags) { FShiftFlags = ShiftFlags; } virtual BOOL SendActions() = 0; // I4196 }; diff --git a/windows/src/engine/keyman32/calldll.cpp b/windows/src/engine/keyman32/calldll.cpp index 142db01747c644393d556d31100d1ec2bb3a636d..f0c623373c2e7bd1e20577f32826e31cd5092960 100644 --- a/windows/src/engine/keyman32/calldll.cpp +++ b/windows/src/engine/keyman32/calldll.cpp @@ -93,34 +93,6 @@ static LPIMDLL AddIMDLL(LPINTKEYBOARDINFO lpkbi, LPSTR kbdpath, LPSTR dllfilenam return imd; } -/* Add a dll hook function to the list of hook functions associated with a single dll */ - -static BOOL AddIMDLLHook(LPIMDLL imd, LPSTR funcname, DWORD storeno, PWCHAR *dpString) -{ - //SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHook: Enter"); - /* Get the procedure address for the function */ - - IMDLLHOOKProc dhp = (IMDLLHOOKProc) GetProcAddress(imd->hModule, funcname); - if(!dhp) return FALSE; - - /* Add the function to the list of functions in the DLL */ - - LPIMDLLHOOK hooks = new IMDLLHOOK[imd->nHooks+1]; - if(imd->nHooks > 0) - { - memcpy(hooks, imd->Hooks, sizeof(IMDLLHOOK) * imd->nHooks); - delete imd->Hooks; - } - imd->Hooks = hooks; - strncpy(imd->Hooks[imd->nHooks].name, funcname, 31); - imd->Hooks[imd->nHooks].name[31] = 0; - imd->Hooks[imd->nHooks].storeno = storeno; - imd->Hooks[imd->nHooks].function = dhp; - *dpString = (PWCHAR) &imd->Hooks[imd->nHooks++]; - //SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHook: Exit"); - return TRUE; -} - static km_kbp_action_item* kmnToCoreActionItem(int ItemType, DWORD dwData, WORD wVkey) { @@ -232,55 +204,6 @@ BOOL CallbackDLLs(LPINTKEYBOARDINFO lpkbi, PSTR cmd) return TRUE; } -/* Load the dlls associated with a keyboard */ - -BOOL LoadDLLs(LPINTKEYBOARDINFO lpkbi) -{ - char fullname[_MAX_PATH]; - //SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Enter"); - - if(lpkbi->nIMDLLs > 0) if(!UnloadDLLs(lpkbi)) return FALSE; - - if (!GetKeyboardFileName(lpkbi->Name, fullname, _MAX_PATH)) { - SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Filename not found[%s]", lpkbi->Name); - return FALSE; - } - - if (!lpkbi->Keyboard) { - SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Keyboard is null"); - return FALSE; - } - for(DWORD i = 0; i < lpkbi->Keyboard->cxStoreArray; i++) - { - LPSTORE s = &lpkbi->Keyboard->dpStoreArray[i]; - if(s->dwSystemID == TSS_CALLDEFINITION) - { - /* Break the store string into components */ - - PCHAR p = wstrtostr(s->dpString), q, r, context; - - q = strtok_s(p, ":", &context); - r = strtok_s(NULL, ":", &context); - - if(!q || !r) - { - s->dwSystemID = TSS_CALLDEFINITION_LOADFAILED; - delete[] p; - continue; - } - - LPIMDLL imd = AddIMDLL(lpkbi, fullname, q); - if(imd && AddIMDLLHook(imd, r, i, &s->dpString)) s->dwSystemID = TSS_CALLDEFINITION; - else s->dwSystemID = TSS_CALLDEFINITION_LOADFAILED; - - delete[] p; - } - } - - //SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Exit"); - return TRUE; -} - // Both Core and Window keyboard processor can use this function BOOL UnloadDLLs(LPINTKEYBOARDINFO lpkbi) { @@ -302,7 +225,7 @@ BOOL UnloadDLLs(LPINTKEYBOARDINFO lpkbi) lpkbi->IMDLLs = NULL; lpkbi->nIMDLLs = 0; - if (Globals::get_CoreIntegration() && lpkbi->lpCoreKeyboardState) { + if (lpkbi->lpCoreKeyboardState) { km_kbp_state_imx_deregister_callback(lpkbi->lpCoreKeyboardState); } return TRUE; @@ -330,26 +253,6 @@ BOOL DeactivateDLLs(LPINTKEYBOARDINFO lpkbi) return TRUE; } -void CallDLL(LPINTKEYBOARDINFO lpkbi, DWORD storenum) -{ - //SendDebugMessageFormat(0, sdmKeyboard, 0, "CallDll: Enter"); - if (!lpkbi->Keyboard) return; - if(storenum >= lpkbi->Keyboard->cxStoreArray) return; - - LPSTORE s = &lpkbi->Keyboard->dpStoreArray[storenum]; - if(s->dwSystemID != TSS_CALLDEFINITION) return; - if(s->dpString == NULL) return; - LPIMDLLHOOK imdh = (LPIMDLLHOOK) s->dpString; - - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return; - - if(_td->TIPFUpdateable) { // I4452 - (*imdh->function)(_td->state.msg.hwnd, _td->state.vkey, _td->state.charCode, Globals::get_ShiftState()); - } - //SendDebugMessageFormat(0, sdmKeyboard, 0, "CallDll: Exit"); -} - // The callback function called by the Core Keyboardprocessor extern "C" uint8_t IM_CallBackCore(km_kbp_state *km_state, uint32_t UniqueStoreNo, void *callbackObject) { //SendDebugMessageFormat(0, sdmKeyboard, 0, "IM_CallBackCore: Enter"); @@ -402,90 +305,84 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMSetOutput(PWSTR buf, DWORD backlen if (!_td->app) return FALSE; - if (!Globals::get_CoreIntegration()) { // TODO: 5442 Remove If and fix indent - while (backlen-- > 0) - _td->app->QueueAction(QIT_BACK, BK_DEFAULT); - while (*buf) - _td->app->QueueAction(QIT_CHAR, *buf++); - return TRUE; - } else { - if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) { - SendDebugMessageFormat(0, sdmKeyboard, 0, "KMSetOutputCore: no active state"); - return FALSE; - } - DWORD numActions = backlen + (DWORD)wcslen(buf); - DWORD idx = 0; - km_kbp_action_item *actionItems = new km_kbp_action_item[numActions + 1]; - - // The actions sent to the core processor need to set the expected_type - // correctly. To do this need to check the context as we process the - // backspaces. - km_kbp_context_item *citems = nullptr; - if (KM_KBP_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) { - delete[] actionItems; - return FALSE; - } - DWORD context_length = (DWORD)km_kbp_context_item_list_size(citems); - WCHAR *contextString = new WCHAR[(context_length * 3) + 1]; // *3 if every context item was a deadkey - if (!ContextItemToAppContext(citems, contextString, context_length)) { - km_kbp_context_items_dispose(citems); - delete[] contextString; - delete[] actionItems; - return FALSE; - } + if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) { + SendDebugMessageFormat(0, sdmKeyboard, 0, "KMSetOutputCore: no active state"); + return FALSE; + } + DWORD numActions = backlen + (DWORD)wcslen(buf); + DWORD idx = 0; + km_kbp_action_item *actionItems = new km_kbp_action_item[numActions + 1]; + + // The actions sent to the core processor need to set the expected_type + // correctly. To do this need to check the context as we process the + // backspaces. + km_kbp_context_item *citems = nullptr; + if (KM_KBP_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) { + delete[] actionItems; + return FALSE; + } + + DWORD context_length = (DWORD)km_kbp_context_item_list_size(citems); + WCHAR *contextString = new WCHAR[(context_length * 3) + 1]; // *3 if every context item was a deadkey + if (!ContextItemToAppContext(citems, contextString, context_length)) { km_kbp_context_items_dispose(citems); - AppContext context; - context.Set(contextString); delete[] contextString; - - while (backlen-- > 0) { - actionItems[idx].type = KM_KBP_IT_BACK; - WCHAR *CodeUnitPtr; - const int DeadKeyLength = 3; - const int SurrogateLength = 2; - const int SingleCharLength = 1; - if (context.CharIsDeadkey()) { - CodeUnitPtr = context.BufMax(DeadKeyLength); - CodeUnitPtr += 2; - actionItems[idx].backspace.expected_type = KM_KBP_BT_MARKER; - actionItems[idx].backspace.expected_value = (uintptr_t)*CodeUnitPtr; - } else if (context.CharIsSurrogatePair()) { - CodeUnitPtr = context.BufMax(SurrogateLength); - actionItems[idx].backspace.expected_type = KM_KBP_BT_CHAR; - actionItems[idx].backspace.expected_value = (DWORD)Uni_SurrogateToUTF32(*CodeUnitPtr, *(CodeUnitPtr + 1)); - } else if (!context.IsEmpty()) { - CodeUnitPtr = context.BufMax(SingleCharLength); - actionItems[idx].backspace.expected_type = KM_KBP_BT_CHAR; - actionItems[idx].backspace.expected_value = (DWORD)*CodeUnitPtr; - } else { - actionItems[idx].backspace.expected_type = KM_KBP_BT_UNKNOWN; - actionItems[idx].backspace.expected_value = 0; - } - context.Delete(); - idx++; + delete[] actionItems; + return FALSE; + } + km_kbp_context_items_dispose(citems); + AppContext context; + context.Set(contextString); + delete[] contextString; + + while (backlen-- > 0) { + actionItems[idx].type = KM_KBP_IT_BACK; + WCHAR *CodeUnitPtr; + const int DeadKeyLength = 3; + const int SurrogateLength = 2; + const int SingleCharLength = 1; + if (context.CharIsDeadkey()) { + CodeUnitPtr = context.BufMax(DeadKeyLength); + CodeUnitPtr += 2; + actionItems[idx].backspace.expected_type = KM_KBP_BT_MARKER; + actionItems[idx].backspace.expected_value = (uintptr_t)*CodeUnitPtr; + } else if (context.CharIsSurrogatePair()) { + CodeUnitPtr = context.BufMax(SurrogateLength); + actionItems[idx].backspace.expected_type = KM_KBP_BT_CHAR; + actionItems[idx].backspace.expected_value = (DWORD)Uni_SurrogateToUTF32(*CodeUnitPtr, *(CodeUnitPtr + 1)); + } else if (!context.IsEmpty()) { + CodeUnitPtr = context.BufMax(SingleCharLength); + actionItems[idx].backspace.expected_type = KM_KBP_BT_CHAR; + actionItems[idx].backspace.expected_value = (DWORD)*CodeUnitPtr; + } else { + actionItems[idx].backspace.expected_type = KM_KBP_BT_UNKNOWN; + actionItems[idx].backspace.expected_value = 0; } + context.Delete(); + idx++; + } - while (*buf) { - actionItems[idx].type = KM_KBP_IT_CHAR; - if (Uni_IsSurrogate1(*buf) && Uni_IsSurrogate2(*(buf + 1))) { - actionItems[idx].character = Uni_SurrogateToUTF32(*buf, *(buf + 1)); - buf++; - } else { - actionItems[idx].character = (DWORD)(*buf); - } + while (*buf) { + actionItems[idx].type = KM_KBP_IT_CHAR; + if (Uni_IsSurrogate1(*buf) && Uni_IsSurrogate2(*(buf + 1))) { + actionItems[idx].character = Uni_SurrogateToUTF32(*buf, *(buf + 1)); buf++; - idx++; - } - actionItems[idx].type = KM_KBP_IT_END; - if (KM_KBP_STATUS_OK != km_kbp_state_queue_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, actionItems)) { - delete[] actionItems; - return FALSE; + } else { + actionItems[idx].character = (DWORD)(*buf); } + buf++; + idx++; + } + actionItems[idx].type = KM_KBP_IT_END; + if (KM_KBP_STATUS_OK != km_kbp_state_queue_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, actionItems)) { delete[] actionItems; - //SendDebugMessageFormat(0, sdmKeyboard, 0, "KMSetOutputCore: Exit"); - return TRUE; + return FALSE; } + delete[] actionItems; + //SendDebugMessageFormat(0, sdmKeyboard, 0, "KMSetOutputCore: Exit"); + return TRUE; + } extern "C" BOOL _declspec(dllexport) WINAPI KMQueueAction(int ItemType, DWORD dwData) { @@ -496,61 +393,53 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMQueueAction(int ItemType, DWORD dw if (!_td->app) return FALSE; - if (!Globals::get_CoreIntegration()) { - return _td->app->QueueAction(ItemType, dwData); // TODO: 5442 Remove - } else { - if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) { - return FALSE; - } + if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) { + return FALSE; + } - km_kbp_action_item *actionItem = kmnToCoreActionItem(ItemType, dwData, _td->state.vkey); - km_kbp_status_codes error_status = - (km_kbp_status_codes)km_kbp_state_queue_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, actionItem); - if (error_status != KM_KBP_STATUS_OK) { - delete[] actionItem; - SendDebugMessageFormat(0, sdmKeyboard, 0, "KMQueueAction: Error core queue_action_items error status:[%lu]",error_status); - return FALSE; - } + km_kbp_action_item *actionItem = kmnToCoreActionItem(ItemType, dwData, _td->state.vkey); + km_kbp_status_codes error_status = + (km_kbp_status_codes)km_kbp_state_queue_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, actionItem); + if (error_status != KM_KBP_STATUS_OK) { delete[] actionItem; - //SendDebugMessageFormat(0, sdmKeyboard, 0, "KMQueueActionCore: Exit"); - return TRUE; + SendDebugMessageFormat(0, sdmKeyboard, 0, "KMQueueAction: Error core queue_action_items error status:[%lu]",error_status); + return FALSE; } + delete[] actionItem; + //SendDebugMessageFormat(0, sdmKeyboard, 0, "KMQueueActionCore: Exit"); + return TRUE; + } extern "C" BOOL _declspec(dllexport) WINAPI KMGetContext(PWSTR buf, DWORD len) { //SendDebugMessageFormat(0, sdmKeyboard, 0, "KMGetContext: Enter"); PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - if(!_td->app) return FALSE; - // TODO: 5442 KMGetContext is already public call (even though it is pointer) Rather then making a new KMGetContextCore - // This has been modified to check for core processor once we move to core processor the old Windows Platmform calling of - // ContextBuff can be removed - // - if(!Globals::get_CoreIntegration()){ - PWSTR q = _td->app->ContextBufMax(len); - if (!q) - return FALSE; // context buf does not exist - - wcscpy_s(buf, len + 1, q); // I3091 - return TRUE; - } else { - if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) { - return FALSE; - } - km_kbp_context_item *citems = nullptr; - if (KM_KBP_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) { - return FALSE; - } + if(!_td) { + return FALSE; + } + + if(!_td->app) { + return FALSE; + } - if (!ContextItemToAppContext(citems, buf, len)) { - km_kbp_context_items_dispose(citems); + if (!_td->lpActiveKeyboard || !_td->lpActiveKeyboard->lpCoreKeyboardState) { + return FALSE; + } + + km_kbp_context_item *citems = nullptr; + if (KM_KBP_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) { return FALSE; - } + } + + if (!ContextItemToAppContext(citems, buf, len)) { km_kbp_context_items_dispose(citems); - //SendDebugMessageFormat(0, sdmKeyboard, 0, "KMGetContext: Exit"); - return TRUE; + return FALSE; } + km_kbp_context_items_dispose(citems); + //SendDebugMessageFormat(0, sdmKeyboard, 0, "KMGetContext: Exit"); + return TRUE; + } extern "C" BOOL _declspec(dllexport) WINAPI KMDisplayIM(HWND hwnd, BOOL FShowAlways) @@ -650,8 +539,8 @@ BOOL IsIMWindow(HWND hwnd) /* Add a dll hook function to the list of hook functions associated with a single dll */ static BOOL -AddIMDLLHookCore(LPIMDLL imd, LPSTR funcname, DWORD storeno) { - //SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHookCore: Enter"); +AddIMDLLHook(LPIMDLL imd, LPSTR funcname, DWORD storeno) { + //SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHook: Enter"); /* Get the procedure address for the function */ IMDLLHOOKProc dhp = (IMDLLHOOKProc)GetProcAddress(imd->hModule, funcname); if (!dhp) @@ -670,15 +559,15 @@ AddIMDLLHookCore(LPIMDLL imd, LPSTR funcname, DWORD storeno) { imd->Hooks[imd->nHooks].storeno = storeno; imd->Hooks[imd->nHooks].function = dhp; imd->nHooks++; - //SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHookCore: Exit"); + //SendDebugMessageFormat(0, sdmKeyboard, 0, "AddIMDLLHook: Exit"); return TRUE; } /* Load the dlls associated with a keyboard */ BOOL -LoadDLLsCore(LPINTKEYBOARDINFO lpkbi) { +LoadDLLs(LPINTKEYBOARDINFO lpkbi) { char fullname[_MAX_PATH]; - //SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLsCore: Enter"); + //SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Enter"); if (lpkbi->nIMDLLs > 0) if (!UnloadDLLs(lpkbi)) @@ -694,11 +583,11 @@ LoadDLLsCore(LPINTKEYBOARDINFO lpkbi) { BOOL result = false; for (; imx_list->library_name; ++imx_list) { LPIMDLL imd = AddIMDLL(lpkbi, fullname, wstrtostr(reinterpret_cast(imx_list->library_name))); - if (imd && AddIMDLLHookCore(imd, wstrtostr(reinterpret_cast(imx_list->function_name)), imx_list->imx_id)) { + if (imd && AddIMDLLHook(imd, wstrtostr(reinterpret_cast(imx_list->function_name)), imx_list->imx_id)) { result = TRUE; } else { - SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLsCore: Error Loading Library name [%s], Function name [%s]", + SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Error Loading Library name [%s], Function name [%s]", wstrtostr(reinterpret_cast(imx_list->library_name)), wstrtostr(reinterpret_cast(imx_list->function_name))); } @@ -707,7 +596,7 @@ LoadDLLsCore(LPINTKEYBOARDINFO lpkbi) { if (result) { km_kbp_state_imx_register_callback(lpkbi->lpCoreKeyboardState, IM_CallBackCore, (void *)lpkbi); } - //SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLsCore: Exit"); + //SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Exit"); return TRUE; } diff --git a/windows/src/engine/keyman32/calldll.h b/windows/src/engine/keyman32/calldll.h index 9eb1a7ade4144e0240bad642b711e0c30740e723..6e719bde9ac29a7edf212018d5e12b766c4fb77e 100644 --- a/windows/src/engine/keyman32/calldll.h +++ b/windows/src/engine/keyman32/calldll.h @@ -19,24 +19,19 @@ #ifndef __CALLDLL_H #define __CALLDLL_H -BOOL LoadDLLs(LPINTKEYBOARDINFO lpkbi); -BOOL UnloadDLLs(LPINTKEYBOARDINFO lpkbi); -BOOL DeactivateDLLs(LPINTKEYBOARDINFO lpkbi); -BOOL ActivateDLLs(LPINTKEYBOARDINFO lpkbi); - -// TODO: 5444 This will become the only LoadDLLs function /** * Load the all the dlls used by the current keyboard * @param lpkbi The keyboard for which to load the dlls * @return BOOL True on success */ -BOOL LoadDLLsCore(LPINTKEYBOARDINFO lpkbi); +BOOL LoadDLLs(LPINTKEYBOARDINFO lpkbi); +BOOL UnloadDLLs(LPINTKEYBOARDINFO lpkbi); +BOOL DeactivateDLLs(LPINTKEYBOARDINFO lpkbi); +BOOL ActivateDLLs(LPINTKEYBOARDINFO lpkbi); BOOL IsIMWindow(HWND hwnd); -void CallDLL(LPINTKEYBOARDINFO lpkbi, DWORD storenum); - // Callback function used by the core processor to call out to 3rd Party Library functions extern "C" uint8_t IM_CallBackCore(km_kbp_state *km_state, uint32_t UniqueStoreNo, void *callbackObject); diff --git a/windows/src/engine/keyman32/capsstate.cpp b/windows/src/engine/keyman32/capsstate.cpp index c9d283cde40db020413c10e186119cdef47e049b..4f9b466e9ee85e2a73a4041613c0842c6c1c06b7 100644 --- a/windows/src/engine/keyman32/capsstate.cpp +++ b/windows/src/engine/keyman32/capsstate.cpp @@ -29,73 +29,3 @@ BOOL IsCapsLockOn(void) { return GetKeyState(VK_CAPITAL) & 1; } - -void ResetCapsLock(void) -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if (!_td) return; - if (!_td->lpActiveKeyboard) return; - if (!_td->lpActiveKeyboard->Keyboard) return; - - SendDebugMessageFormat(0, sdmGlobal, 0, "ResetCapsLock: enter"); - - if (_td->lpActiveKeyboard->Keyboard->dwFlags & KF_CAPSALWAYSOFF) - { - SendDebugMessageFormat(0, sdmGlobal, 0, "ResetCapsLock: caps lock should be always off"); - if (IsCapsLockOn()) - { - SendDebugMessageFormat(0, sdmGlobal, 0, "ResetCapsLock: caps lock is on, switching off caps lock"); - keybd_event(VK_CAPITAL, 0x3A, 0, 0); - keybd_event(VK_CAPITAL, 0x3A, 0 | KEYEVENTF_KEYUP, 0); - } - } - SendDebugMessageFormat(0, sdmGlobal, 0, "ResetCapsLock: exit"); -} - - -void KeyCapsLockPress(BOOL FIsUp) // I3284 - void // I3529 -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if (!_td) return; - if (!_td->lpActiveKeyboard) return; // pass through to window - if (!_td->lpActiveKeyboard->Keyboard) return; - - if (_td->lpActiveKeyboard->Keyboard->dwFlags & KF_CAPSONONLY) - { - SendDebugMessageFormat(0, sdmAIDefault, 0, "KeyCapsLockPress: KF_CAPSONONLY: FIsUp=%d CapsState=%d", FIsUp, IsCapsLockOn()); - if (FIsUp && !IsCapsLockOn()) // I267 - 24/11/2006 invert GetKeyState test - { - keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, 0, 0); - keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, KEYEVENTF_KEYUP, 0); - } - } - else if (_td->lpActiveKeyboard->Keyboard->dwFlags & KF_CAPSALWAYSOFF) - { - SendDebugMessageFormat(0, sdmAIDefault, 0, "KeyCapsLockPress: KF_CAPSALWAYSOFF: FIsUp=%d CapsState=%d", FIsUp, IsCapsLockOn()); - if (!FIsUp && IsCapsLockOn()) - { // I267 - 24/11/2006 invert GetKeyState test - keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, KEYEVENTF_KEYUP, 0); - keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, 0, 0); - } - } -} - - -void KeyShiftPress(BOOL FIsUp) // I3284 - void // I3529 -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if (!_td) return; - if (!_td->lpActiveKeyboard) return; // pass through to window - if (!_td->lpActiveKeyboard->Keyboard) return; - - if (_td->lpActiveKeyboard->Keyboard->dwFlags & KF_SHIFTFREESCAPS) - { - SendDebugMessageFormat(0, sdmAIDefault, 0, "KeyShiftPress: KF_SHIFTFREESCAPS: FIsUp=%d CapsState=%d", FIsUp, IsCapsLockOn()); - if (!FIsUp && IsCapsLockOn()) - { - keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, 0, 0); - keybd_event(VK_CAPITAL, SCAN_FLAG_KEYMAN_KEY_EVENT, KEYEVENTF_KEYUP, 0); - } - } -} - diff --git a/windows/src/engine/keyman32/capsstate.h b/windows/src/engine/keyman32/capsstate.h index aeb3e2900065fd6a7ece9a27f3a921ec0f5edcd5..d28ba9c0db99f240744e273b4b919cf4ead96aee 100644 --- a/windows/src/engine/keyman32/capsstate.h +++ b/windows/src/engine/keyman32/capsstate.h @@ -20,8 +20,5 @@ #define __CAPSSTATE_H BOOL IsCapsLockOn(void); -void ResetCapsLock(void); -void KeyCapsLockPress(BOOL FIsUp); -void KeyShiftPress(BOOL FIsUp); #endif diff --git a/windows/src/engine/keyman32/globals.h b/windows/src/engine/keyman32/globals.h index 6e840fce99b7c1a6d6e9e1205a51076e2e6e2f8d..320ef811de4d0af5a08a8ce43232f2e781a85b5a 100644 --- a/windows/src/engine/keyman32/globals.h +++ b/windows/src/engine/keyman32/globals.h @@ -151,7 +151,6 @@ public: static BOOL get_debug_KeymanLog(); static BOOL get_debug_ToConsole(); - static BOOL get_CoreIntegration(); static void LoadDebugSettings(); }; @@ -216,8 +215,6 @@ typedef struct tagKEYMAN64THREADDATA FInitialised, FInitialising; - char ForceFileName[MAX_PATH]; - DWORD ActiveKeymanID; /* TIP Globals */ diff --git a/windows/src/engine/keyman32/glossary.cpp b/windows/src/engine/keyman32/glossary.cpp index 2954638c1925ee01bb14d75fddcc96dee6e06124..7a6aecddc06998ef337a80a5bd3f0860a4f644c7 100644 --- a/windows/src/engine/keyman32/glossary.cpp +++ b/windows/src/engine/keyman32/glossary.cpp @@ -1,18 +1,18 @@ /* Name: glossary Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 20 Jul 2008 Modified Date: 28 May 2014 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 20 Jul 2008 - mcdurdin - I1498 - Fix keyboard switching for Shadow keyboards on Vista+ 20 Jul 2008 - mcdurdin - I1546 - Fix language switch with ids >= x80000000 20 Jul 2008 - mcdurdin - I1545 - Fix registry leak @@ -38,9 +38,9 @@ BOOL HKLIsIME(HKL hkl) // I1498 - fix keyboard switching for shadow keyboards o if( (GetVersion() & 0xFF) >= 6 ) return FALSE; if( (GetVersion() & 0x8000000) == 0x8000000 || (GetVersion() & 0xFF) == 4 ) r = GetSystemMetrics(SM_DBCSENABLED); - else + else r = GetSystemMetrics(SM_IMMENABLED); - + return r && ImmIsIME(hkl); } #pragma warning(default: 4996) @@ -99,12 +99,12 @@ DWORD HKLToKeyboardID(HKL hkl) return (DWORD) LOWORD(hkl); } - for(len=16, n = i = 0; RegEnumKeyEx(hkey, i, str, &len, 0, NULL, NULL, &ft) == ERROR_SUCCESS; - len = 16, i++, n=0) + for(len=16, n = i = 0; RegEnumKeyEx(hkey, i, str, &len, 0, NULL, NULL, &ft) == ERROR_SUCCESS; + len = 16, i++, n=0) { RegOpenKeyEx(hkey, str, NULL, KEY_READ, &hsubkey); len = 16; - if(RegQueryValueEx(hsubkey, REGSZ_LayoutID, NULL, NULL, (LPBYTE) str2, &len) == ERROR_SUCCESS) + if(RegQueryValueEx(hsubkey, REGSZ_LayoutID, NULL, NULL, (LPBYTE) str2, &len) == ERROR_SUCCESS) { n = strtoul(str2, NULL, 16); // I1546 if(n == LayoutID) @@ -120,7 +120,7 @@ DWORD HKLToKeyboardID(HKL hkl) } RegCloseKey(hkey); - + //SendDebugMessageFormat(0, sdmGlobal, 0, "HKLToKeyboardID: fails[2], return LOWORD(hkl)=%x", LOWORD(hkl)); return (DWORD) LOWORD(hkl); // should never happen } @@ -143,11 +143,11 @@ WORD HKLToLayoutNumber(HKL hkl) if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSZ_SystemKeyboardLayouts, NULL, KEY_READ, &hkey) != ERROR_SUCCESS) return 0; - for(len=16, n = i = 0; RegEnumKeyEx(hkey, i, str, &len, 0, NULL, NULL, &ft) == ERROR_SUCCESS; len = 16, i++, n=0) + for(len=16, n = i = 0; RegEnumKeyEx(hkey, i, str, &len, 0, NULL, NULL, &ft) == ERROR_SUCCESS; len = 16, i++, n=0) { RegOpenKeyEx(hkey, str, NULL, KEY_READ, &hsubkey); len = 16; - if(RegQueryValueEx(hsubkey, REGSZ_LayoutID, NULL, NULL, (LPBYTE) str2, &len) == ERROR_SUCCESS) + if(RegQueryValueEx(hsubkey, REGSZ_LayoutID, NULL, NULL, (LPBYTE) str2, &len) == ERROR_SUCCESS) { if(strtoul(str2, NULL, 16) == LayoutID) break; // strtoul - I1546 } @@ -166,37 +166,3 @@ WORD HKLToLayoutID(HKL hkl) return HIWORD(hkl) & 0x0FFF; } - -DWORD EthnologueCodeToKeymanID(DWORD EthCode) -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return KEYMANID_NONKEYMAN; - - for(int i = 0; i < _td->nKeyboards; i++) - { - if(!_td->lpKeyboards[i].Keyboard && !LoadlpKeyboard(i)) - { - SendDebugMessageFormat(0,sdmGlobal,0, - "EthnologueCodeToKeymanID: Unable to load keyboard %s", _td->lpKeyboards[i].Name); - return KEYMANID_NONKEYMAN; - } - - PWSTR ps = GetSystemStore(_td->lpKeyboards[i].Keyboard, TSS_ETHNOLOGUECODE); - if(ps) - { - SendDebugMessageFormat(0,sdmGlobal,0,"EthnologueCodeToKeymanID: %s %ws %x", _td->lpKeyboards[i].Name, ps, EthnologueStringCodeToDWord(ps)); - if(EthnologueStringCodeToDWord(ps) == EthCode) return _td->lpKeyboards[i].KeymanID; - } - } - - return KEYMANID_NONKEYMAN; -} - -DWORD EthnologueStringCodeToDWord(PWSTR EthCode) -{ - if(wcslen(EthCode) < 3 || wcslen(EthCode) > 4) return (DWORD)-1; - return (LOBYTE(EthCode[0])) | - (LOBYTE(EthCode[1]) << 8) | - (LOBYTE(EthCode[2]) << 16) | - (LOBYTE(EthCode[3]) << 24); -} diff --git a/windows/src/engine/keyman32/k32_globals.cpp b/windows/src/engine/keyman32/k32_globals.cpp index 054945b8129a72cf7479097c551f820fd477cc5b..989a787f95951a3652624d4f502d3a530e65d448 100644 --- a/windows/src/engine/keyman32/k32_globals.cpp +++ b/windows/src/engine/keyman32/k32_globals.cpp @@ -288,9 +288,6 @@ static BOOL f_debug_KeymanLog = FALSE, f_debug_ToConsole = FALSE; -static BOOL - f_CoreIntegration = TRUE; - #pragma data_seg() /***************************************************************************/ @@ -365,8 +362,6 @@ BOOL Globals::get_MnemonicDeadkeyConversionMode() { return f_MnemonicDeadkeyConv BOOL Globals::get_debug_KeymanLog() { return f_debug_KeymanLog; } BOOL Globals::get_debug_ToConsole() { return f_debug_ToConsole; } -BOOL Globals::get_CoreIntegration() { return f_CoreIntegration; } - void Globals::SetBaseKeyboardName(wchar_t *baseKeyboardName, wchar_t *baseKeyboardNameAlt) { // I4583 wcscpy_s(f_BaseKeyboardName, baseKeyboardName); wcscpy_s(f_BaseKeyboardNameAlt, baseKeyboardNameAlt); @@ -385,9 +380,6 @@ void Globals::SetBaseKeyboardFlags(char *baseKeyboard, BOOL simulateAltGr, BOOL be changed until Keyman is restarted. */ BOOL Globals::InitSettings() { - /* Check for common core vs windows core */ - f_CoreIntegration = Reg_GetDebugFlag(REGSZ_Flag_UseKeymanCore, TRUE); - SendDebugMessageFormat(0, sdmAIDefault, 0, "Globals::InitSettings - Coreintegration set in '" REGSZ_Flag_UseKeymanCore "' to %x", f_CoreIntegration); f_vk_prefix = _VK_PREFIX_DEFAULT; RegistryReadOnly reg(HKEY_LOCAL_MACHINE); if (reg.OpenKeyReadOnly(REGSZ_KeymanLM) && diff --git a/windows/src/engine/keyman32/keyboardoptions.cpp b/windows/src/engine/keyman32/keyboardoptions.cpp index 3e5edcff5bcb18307c7d9ff446fa2d2fadfeb53b..96deb57590dac21770303edbbfe91447e93da434 100644 --- a/windows/src/engine/keyman32/keyboardoptions.cpp +++ b/windows/src/engine/keyman32/keyboardoptions.cpp @@ -19,10 +19,8 @@ */ #include "pch.h" -void IntSaveKeyboardOption(LPCSTR key, LPINTKEYBOARDINFO kp, int nStoreToSave); -BOOL IntLoadKeyboardOptions(LPCSTR key, LPINTKEYBOARDINFO kp); -BOOL IntLoadKeyboardOptionsCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* const state); -void IntSaveKeyboardOptionREGCore(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value); +BOOL IntLoadKeyboardOptionsRegistrytoCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* const state); +void IntSaveKeyboardOptionCoretoRegistry(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value); static km_kbp_cp* CloneKMKBPCP(const km_kbp_cp* cp) { LPCWSTR buf = reinterpret_cast(cp); @@ -37,123 +35,12 @@ static km_kbp_cp* CloneKMKBPCPFromWSTR(LPWSTR buf) { return clone; } -void LoadKeyboardOptions(LPINTKEYBOARDINFO kp) -{ // I3594 - IntLoadKeyboardOptions(REGSZ_KeyboardOptions, kp); -} - -void LoadSharedKeyboardOptions(LPINTKEYBOARDINFO kp) -{ - if(!DebugAssert(!Globals::get_CoreIntegration(), "LoadSharedKeyboardOptions: Error called in core integration mode")) { - return; - } - // Called when another thread changes keyboard options and we are sharing keyboard settings - assert(kp != NULL); - assert(kp->Keyboard != NULL); - - if(kp->KeyboardOptions != NULL) FreeKeyboardOptions(kp); - - IntLoadKeyboardOptions(REGSZ_SharedKeyboardOptions, kp); -} - -void FreeKeyboardOptions(LPINTKEYBOARDINFO kp) -{ - // This is a cleanup routine; we don't want to precondition all calls to it - // so we do not assert - if (kp == NULL || kp->Keyboard == NULL || kp->KeyboardOptions == NULL) - return; - - for(DWORD i = 0; i < kp->Keyboard->cxStoreArray; i++) - if(kp->KeyboardOptions[i].Value) - { - kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].OriginalStore; - delete kp->KeyboardOptions[i].Value; - } - delete kp->KeyboardOptions; - kp->KeyboardOptions = NULL; -} - -void SetKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToSet, int nStoreToRead) -{ - if (!DebugAssert(!Globals::get_CoreIntegration(), "SetKeyboardOption: Error called in core integration mode")) { - return; - } - assert(kp != NULL); - assert(kp->Keyboard != NULL); - assert(kp->KeyboardOptions != NULL); - assert(nStoreToSet >= 0); - assert(nStoreToSet < (int) kp->Keyboard->cxStoreArray); - assert(nStoreToRead >= 0); - assert(nStoreToRead < (int) kp->Keyboard->cxStoreArray); - - LPSTORE sp = &kp->Keyboard->dpStoreArray[nStoreToRead]; - if(kp->KeyboardOptions[nStoreToSet].Value) - { - delete kp->KeyboardOptions[nStoreToSet].Value; - } - else - { - kp->KeyboardOptions[nStoreToSet].OriginalStore = kp->Keyboard->dpStoreArray[nStoreToSet].dpString; - } - - kp->KeyboardOptions[nStoreToSet].Value = new WCHAR[wcslen(sp->dpString)+1]; - wcscpy_s(kp->KeyboardOptions[nStoreToSet].Value, wcslen(sp->dpString)+1, sp->dpString); - kp->Keyboard->dpStoreArray[nStoreToSet].dpString = kp->KeyboardOptions[nStoreToSet].Value; -} - -void ResetKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToReset) +void SaveKeyboardOptionCoretoRegistry(LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value) { - if (!DebugAssert(!Globals::get_CoreIntegration(), "ResetKeyboardOption: Error called in core integration mode")) { - return; - } - assert(kp != NULL); - assert(kp->Keyboard != NULL); - assert(kp->KeyboardOptions != NULL); - assert(nStoreToReset >= 0); - assert(nStoreToReset < (int) kp->Keyboard->cxStoreArray); - - if(kp->KeyboardOptions[nStoreToReset].Value) - { - kp->Keyboard->dpStoreArray[nStoreToReset].dpString = kp->KeyboardOptions[nStoreToReset].OriginalStore; - delete kp->KeyboardOptions[nStoreToReset].Value; - kp->KeyboardOptions[nStoreToReset].Value = NULL; - - if(kp->Keyboard->dpStoreArray[nStoreToReset].dpName == NULL) return; - - RegistryReadOnly r(HKEY_CURRENT_USER); - if(r.OpenKeyReadOnly(REGSZ_KeymanActiveKeyboards) && r.OpenKeyReadOnly(kp->Name) && r.OpenKeyReadOnly(REGSZ_KeyboardOptions)) - { - if(r.ValueExists(kp->Keyboard->dpStoreArray[nStoreToReset].dpName)) - { - WCHAR val[256]; - if(!r.ReadString(kp->Keyboard->dpStoreArray[nStoreToReset].dpName, val, sizeof(val) / sizeof(val[0]))) return; - if(!val[0]) return; - val[255] = 0; - kp->KeyboardOptions[nStoreToReset].Value = new WCHAR[wcslen(val)+1]; - wcscpy_s(kp->KeyboardOptions[nStoreToReset].Value, wcslen(val)+1, val); - - kp->KeyboardOptions[nStoreToReset].OriginalStore = kp->Keyboard->dpStoreArray[nStoreToReset].dpString; - kp->Keyboard->dpStoreArray[nStoreToReset].dpString = kp->KeyboardOptions[nStoreToReset].Value; - } - } - } + IntSaveKeyboardOptionCoretoRegistry(REGSZ_KeyboardOptions, kp, key, value); } - -void SaveKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToSave) -{ - if (!DebugAssert(!Globals::get_CoreIntegration(), "SaveKeyboardOption: Error called in core integration mode")) { - return; - } - IntSaveKeyboardOption(REGSZ_KeyboardOptions, kp, nStoreToSave); -} - -void SaveKeyboardOptionREGCore(LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value) -{ - IntSaveKeyboardOptionREGCore(REGSZ_KeyboardOptions, kp, key, value); -} - -void IntSaveKeyboardOptionREGCore(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value) +void IntSaveKeyboardOptionCoretoRegistry(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value) { assert(REGKey != NULL); assert(kp != NULL); @@ -167,73 +54,13 @@ void IntSaveKeyboardOptionREGCore(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR k } } -BOOL IntLoadKeyboardOptions(LPCSTR key, LPINTKEYBOARDINFO kp) -{ - assert(key != NULL); - assert(kp != NULL); - assert(kp->Keyboard != NULL); - assert(kp->KeyboardOptions == NULL); - - kp->KeyboardOptions = new INTKEYBOARDOPTIONS[kp->Keyboard->cxStoreArray]; - memset(kp->KeyboardOptions, 0, sizeof(INTKEYBOARDOPTIONS) * kp->Keyboard->cxStoreArray); - RegistryReadOnly r(HKEY_CURRENT_USER); - if(r.OpenKeyReadOnly(REGSZ_KeymanActiveKeyboards) && r.OpenKeyReadOnly(kp->Name) && r.OpenKeyReadOnly(key)) - { - WCHAR buf[256]; - int n = 0; - while(r.GetValueNames(buf, sizeof(buf) / sizeof(buf[0]), n)) - { - buf[255] = 0; - WCHAR val[256]; - if(r.ReadString(buf, val, sizeof(val) / sizeof(val[0])) && val[0]) - { - val[255] = 0; - for(DWORD i = 0; i < kp->Keyboard->cxStoreArray; i++) - { - if(kp->Keyboard->dpStoreArray[i].dpName != NULL && _wcsicmp(kp->Keyboard->dpStoreArray[i].dpName, buf) == 0) - { - kp->KeyboardOptions[i].Value = new WCHAR[wcslen(val)+1]; - wcscpy_s(kp->KeyboardOptions[i].Value, wcslen(val)+1, val); - - kp->KeyboardOptions[i].OriginalStore = kp->Keyboard->dpStoreArray[i].dpString; - kp->Keyboard->dpStoreArray[i].dpString = kp->KeyboardOptions[i].Value; - - break; - } - } - } - n++; - } - return TRUE; - } - return FALSE; -} - -void IntSaveKeyboardOption(LPCSTR key, LPINTKEYBOARDINFO kp, int nStoreToSave) +void LoadKeyboardOptionsRegistrytoCore(LPINTKEYBOARDINFO kp, km_kbp_state* const state) { - assert(key != NULL); - assert(kp != NULL); - assert(kp->Keyboard != NULL); - assert(kp->KeyboardOptions != NULL); - assert(nStoreToSave >= 0); - assert(nStoreToSave < (int) kp->Keyboard->cxStoreArray); - - if(kp->Keyboard->dpStoreArray[nStoreToSave].dpName == NULL) return; - - RegistryFullAccess r(HKEY_CURRENT_USER); - if(r.OpenKey(REGSZ_KeymanActiveKeyboards, TRUE) && r.OpenKey(kp->Name, TRUE) && r.OpenKey(key, TRUE)) - { - r.WriteString(kp->Keyboard->dpStoreArray[nStoreToSave].dpName, kp->Keyboard->dpStoreArray[nStoreToSave].dpString); - } + SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadKeyboardOptionsRegistrytoCore: Enter"); + IntLoadKeyboardOptionsRegistrytoCore(REGSZ_KeyboardOptions, kp, state); } -void LoadKeyboardOptionsREGCore(LPINTKEYBOARDINFO kp, km_kbp_state* const state) -{ - SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadKeyboardOptionsREGCore: Enter"); - IntLoadKeyboardOptionsCore(REGSZ_KeyboardOptions, kp, state); -} - -BOOL IntLoadKeyboardOptionsCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* const state) +BOOL IntLoadKeyboardOptionsRegistrytoCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* const state) { assert(key != NULL); assert(kp != NULL); @@ -243,7 +70,7 @@ BOOL IntLoadKeyboardOptionsCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* km_kbp_status err_status = km_kbp_keyboard_get_attrs(kp->lpCoreKeyboard, &keyboardAttrs); if (err_status != KM_KBP_STATUS_OK) { SendDebugMessageFormat( - 0, sdmKeyboard, 0, "LoadKeyboardOptionsREGCore: km_kbp_keyboard_get_attrs failed with error status [%d]", err_status); + 0, sdmKeyboard, 0, "LoadKeyboardOptionsRegistrytoCore: km_kbp_keyboard_get_attrs failed with error status [%d]", err_status); return FALSE; } @@ -275,7 +102,7 @@ BOOL IntLoadKeyboardOptionsCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* err_status = km_kbp_state_options_update(state, keyboardOpts); if (err_status != KM_KBP_STATUS_OK) { SendDebugMessageFormat( - 0, sdmKeyboard, 0, "LoadKeyboardOptionsREGCore: km_kbp_state_options_update failed with error status [%d]", err_status); + 0, sdmKeyboard, 0, "LoadKeyboardOptionsRegistrytoCore: km_kbp_state_options_update failed with error status [%d]", err_status); } for (int i = 0; i < n; i++) { delete[] keyboardOpts[i].value; @@ -283,89 +110,3 @@ BOOL IntLoadKeyboardOptionsCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* delete[] keyboardOpts; return TRUE; } - -BOOL -UpdateKeyboardOptionsCore( - km_kbp_state* const lpCoreKeyboardState, - km_kbp_option_item *lpCoreKeyboardOptions) { - - int listSize = (int)km_kbp_options_list_size(lpCoreKeyboardOptions); - // Create a option list based on this size look up each key and store the return value in it. - // then at the end return this options list. - BOOL changed = FALSE; - km_kbp_cp const* retValue = nullptr; - for (int i = 0; i < listSize; i++) { - km_kbp_status err_status = km_kbp_state_option_lookup(lpCoreKeyboardState, lpCoreKeyboardOptions[i].scope, lpCoreKeyboardOptions[i].key, - &retValue); - if (err_status != KM_KBP_STATUS_OK) { - SendDebugMessageFormat( - 0, sdmKeyboard, 0, "UpdateKeyboardOptionsCore: km_kbp_state_option_lookup failed with error status [%d]", err_status); - continue; - } - // compare to see if changed - if (wcscmp(reinterpret_cast(retValue), reinterpret_cast(lpCoreKeyboardOptions[i].value)) != 0) { - delete lpCoreKeyboardOptions[i].value; - lpCoreKeyboardOptions[i].value = CloneKMKBPCP(retValue); - changed = TRUE; - } - } - return changed; -} - -km_kbp_option_item* -SaveKeyboardOptionsCore(LPINTKEYBOARDINFO kp) { - - // Get the list of default options to determine size of list - const km_kbp_keyboard_attrs* keyboardAttrs; - km_kbp_status err_status = km_kbp_keyboard_get_attrs(kp->lpCoreKeyboard, &keyboardAttrs); - if (err_status != KM_KBP_STATUS_OK) { - SendDebugMessageFormat( - 0, sdmKeyboard, 0, "SaveKeyboardOptionsCore: km_kbp_keyboard_get_attrs failed with error status [%d]", err_status); - return nullptr; - } - int listSize = (int)km_kbp_options_list_size(keyboardAttrs->default_options); - km_kbp_option_item* savedKeyboardOpts = new km_kbp_option_item[listSize + 1]; - km_kbp_cp const* retValue = nullptr; - - km_kbp_option_item const* kbDefaultOpts = keyboardAttrs->default_options; - for (int i = 0; i < listSize; i++, ++kbDefaultOpts) { - if (kbDefaultOpts->scope != KM_KBP_OPT_KEYBOARD) - continue; - err_status = - km_kbp_state_option_lookup(kp->lpCoreKeyboardState, KM_KBP_OPT_KEYBOARD, kbDefaultOpts->key, &retValue); - if (err_status != KM_KBP_STATUS_OK) { - SendDebugMessageFormat( - 0, sdmKeyboard, 0, "SaveKeyboardOptionsCore: km_kbp_state_option_lookup failed with error status [%d]", err_status); - continue; - } - savedKeyboardOpts[i].key = CloneKMKBPCP(kbDefaultOpts->key); - savedKeyboardOpts[i].value = CloneKMKBPCP(retValue); - savedKeyboardOpts[i].scope = KM_KBP_OPT_KEYBOARD; - } - savedKeyboardOpts[listSize] = KM_KBP_OPTIONS_END; - return savedKeyboardOpts; -} - -BOOL -RestoreKeyboardOptionsCore( - km_kbp_state* const lpCoreKeyboardState, - km_kbp_option_item* lpCoreKeyboardOptions) { - km_kbp_status err_status = km_kbp_state_options_update(lpCoreKeyboardState, lpCoreKeyboardOptions); - if (err_status != KM_KBP_STATUS_OK) { - SendDebugMessageFormat( - 0, sdmKeyboard, 0, "RestoreKeyboardOptionsCore: km_kbp_state_options_update failed with error status [%d]", err_status); - return FALSE; - } - return TRUE; -} - -void -DisposeKeyboardOptionsCore(km_kbp_option_item** lpCoreKeyboardOptions) { - size_t listSize = km_kbp_options_list_size(*lpCoreKeyboardOptions); - for (int i = 0; i < (int)listSize; i++) { - delete[] (*lpCoreKeyboardOptions)[i].key; - delete[] (*lpCoreKeyboardOptions)[i].value; - } - delete[] *lpCoreKeyboardOptions; - *lpCoreKeyboardOptions = NULL; -} diff --git a/windows/src/engine/keyman32/keyboardoptions.h b/windows/src/engine/keyman32/keyboardoptions.h index cef9ae6df1bf34a24b12546c098f2b9bc4122a2f..f55b0bcc77d5f7ebd5ea0952fccec67e6eb0d8c9 100644 --- a/windows/src/engine/keyman32/keyboardoptions.h +++ b/windows/src/engine/keyman32/keyboardoptions.h @@ -16,44 +16,6 @@ History: 25 May 2010 - mcdurdin - I1632 - Keyboard Options */ -void LoadKeyboardOptions(LPINTKEYBOARDINFO kp); -void FreeKeyboardOptions(LPINTKEYBOARDINFO kp); -void SetKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToSet, int nStoreToRead); -void ResetKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToReset); -void SaveKeyboardOption(LPINTKEYBOARDINFO kp, int nStoreToSave); -void LoadSharedKeyboardOptions(LPINTKEYBOARDINFO kp); -/** - * Updates the supplied Keyboard processor options list from the keyboard processor pointed - * to by the state pointer. - * - * @param lpCoreKeyboardState The core keyboardprocessor state which has the source options - * @param[in,out] lpCoreKeyboardOptions The core keyboard options to be updated - * @return BOOL True if one or more options were updated - */ -BOOL UpdateKeyboardOptionsCore(km_kbp_state* const lpCoreKeyboardState, km_kbp_option_item *lpCoreKeyboardOptions); - -/** - * Returns a copy of the core keyboard processors current keyboard options - * The caller is responsible for freeing the returned km_kbp_option_item's list. - * - * @param kp A pointer to the keyboard info object that contains the - * keyboardprocessor state and keyboard for the source options list. - * - * @return km_kbp_option_item* The copy of the options list or NULL if copy failed - */ -km_kbp_option_item* SaveKeyboardOptionsCore(LPINTKEYBOARDINFO kp); - -/** - * Restore the core keyboard processor options to the supplied keyboard - * list of `km_kbp_option_item`s - * - * @param lpCoreKeyboardState The state pointer for the keyboard processor - * @param lpCoreKeyboardOptions The list of `km_kbp_option_item`s to restore - * - * return BOOL TRUE when the call to update keyboard processor was successful - */ -BOOL RestoreKeyboardOptionsCore(km_kbp_state* const lpCoreKeyboardState, km_kbp_option_item* lpCoreKeyboardOptions); - /* Common core integration functions */ /** @@ -62,7 +24,7 @@ BOOL RestoreKeyboardOptionsCore(km_kbp_state* const lpCoreKeyboardState, km_kbp_ * @param kp keyboard info object with options to be updated * @param state core keyboard state used to update keyboard options */ -void LoadKeyboardOptionsREGCore(LPINTKEYBOARDINFO kp, km_kbp_state* state); +void LoadKeyboardOptionsRegistrytoCore(LPINTKEYBOARDINFO kp, km_kbp_state* state); /** * Saves the keyboard option to the windows registry @@ -71,12 +33,6 @@ void LoadKeyboardOptionsREGCore(LPINTKEYBOARDINFO kp, km_kbp_state* state); * @param key keyboard key to save * @param value keyboard option value to save */ -void SaveKeyboardOptionREGCore(LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value); +void SaveKeyboardOptionCoretoRegistry(LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value); + -/** - * Free the allocated resources belonging to a key_kbp_option_items object - * that was created on the heap most likely using SaveKeyboardOptionREGCore - * - * @param lpCoreKeyboardOptions keyboard options items to be freed - */ -void DisposeKeyboardOptionsCore(km_kbp_option_item** lpCoreKeyboardOptions); diff --git a/windows/src/engine/keyman32/keyman-engine.vcxproj b/windows/src/engine/keyman32/keyman-engine.vcxproj index 6869117fd9a41e685be646f78c54b05608259191..d2e78ef7e4fba2a9e0adc19dde994e1dbcc74d6a 100644 --- a/windows/src/engine/keyman32/keyman-engine.vcxproj +++ b/windows/src/engine/keyman32/keyman-engine.vcxproj @@ -59,7 +59,7 @@ $(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\src;$(ProjectDir)..\..\..\..\core\build\rust\x86\$(Configuration);$(LibraryPath) - $(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(IncludePath) + $(ProjectDir)..\..\..\..\common\include;$(ProjectDir)..\..\..\..\core\include;$(ProjectDir)..\..\..\..\core\build\x86\$(Configuration)\include;$(IncludePath) @@ -186,12 +186,6 @@ - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) @@ -403,4 +397,4 @@ - \ No newline at end of file + diff --git a/windows/src/engine/keyman32/keyman-engine.vcxproj.filters b/windows/src/engine/keyman32/keyman-engine.vcxproj.filters index 7cd4d906d1b5e2a358e7ada4eb3fdb55a6dd4e7c..c0eab08097cded066cd712f162636ef4f54b389f 100644 --- a/windows/src/engine/keyman32/keyman-engine.vcxproj.filters +++ b/windows/src/engine/keyman32/keyman-engine.vcxproj.filters @@ -15,9 +15,6 @@ - - Source Files - Source Files diff --git a/windows/src/engine/keyman32/keyman32.cpp b/windows/src/engine/keyman32/keyman32.cpp index ee6cd754b822b955afd021863820ec25e0cf34ab..86eda219a232766e6074c7df61e507b8794f68e7 100644 --- a/windows/src/engine/keyman32/keyman32.cpp +++ b/windows/src/engine/keyman32/keyman32.cpp @@ -492,7 +492,6 @@ extern "C" BOOL _declspec(dllexport) WINAPI Keyman_Exit(void) *Globals::Keyman_Shutdown() = TRUE; ReleaseKeyboards(TRUE); - Addin_Release(); if(!Globals::get_Keyman_Initialised()) { @@ -529,138 +528,6 @@ extern "C" BOOL _declspec(dllexport) WINAPI Keyman_RestartEngine() return TRUE; } -/*******************************************************************************************/ -/* */ -/* Keyman Keyboard Override Functions */ -/* */ -/*******************************************************************************************/ - -extern "C" BOOL _declspec(dllexport) WINAPI Keyman_StopForcingKeyboard(); - -void RefreshPreservedKeys(BOOL Activating); - -extern "C" BOOL _declspec(dllexport) WINAPI Keyman_ForceKeyboard(PCSTR FileName) -{ - SendDebugMessageFormat(0,sdmGlobal,0,"Keyman_ForceKeyboard: ENTER %s", FileName); - - Keyman_StopForcingKeyboard(); // 7.0.219.0 - - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - - strncpy(_td->ForceFileName, FileName, MAX_PATH - 1); - _td->ForceFileName[MAX_PATH-1] = 0; - - if(_td->lpActiveKeyboard) - { - DeactivateDLLs(_td->lpActiveKeyboard); - } - - _td->lpActiveKeyboard = new INTKEYBOARDINFO; - memset(_td->lpActiveKeyboard, 0, sizeof(INTKEYBOARDINFO)); // I2437 - Crash unloading keyboard due to keyboard options not init - /*_td->lpActiveKeyboard->KeymanID = 0; - _td->lpActiveKeyboard->nIMDLLs = 0; - _td->lpActiveKeyboard->IMDLLs = NULL; - _td->lpActiveKeyboard->KeyboardOptions = NULL;*/ - _splitpath_s(FileName, NULL, 0, NULL, 0, _td->lpActiveKeyboard->Name, sizeof(_td->lpActiveKeyboard->Name), NULL, 0); - // TODO: 5442 - remove if/ else as there will no longer be the old LoadKeyboard option - if (Globals::get_CoreIntegration()) { - PWCHAR keyboardPath = strtowstr(_td->ForceFileName); - km_kbp_status err_status = km_kbp_keyboard_load(keyboardPath, &_td->lpActiveKeyboard->lpCoreKeyboard); - if (err_status != KM_KBP_STATUS_OK) { - SendDebugMessageFormat(0, sdmGlobal, 0, "Keyman_ForceKeyboard Core: km_kbp_keyboard_load failed for %ls with error status [%d]", keyboardPath, err_status); // TODO: 5442 - remove word Core - delete keyboardPath; - return FALSE; - } - delete keyboardPath; - SendDebugMessageFormat(0, sdmGlobal, 0, "Keyman_ForceKeyboard Core: %s OK", FileName); // TODO: 5442 - remove word Core - - km_kbp_option_item *core_environment = nullptr; - - if(!SetupCoreEnvironment(&core_environment)) { - SendDebugMessageFormat(0, sdmLoad, 0, "Keyman_ForceKeyboard Core: Unable to set environment options for keyboard %s", FileName); // TODO: 5442 - remove word Core - return FALSE; - } - - err_status = - km_kbp_state_create(_td->lpActiveKeyboard->lpCoreKeyboard, core_environment, &_td->lpActiveKeyboard->lpCoreKeyboardState); - - DeleteCoreEnvironment(core_environment); - - if (err_status != KM_KBP_STATUS_OK) { - SendDebugMessageFormat( - 0, sdmGlobal, 0, "Keyman_ForceKeyboard Core: km_kbp_state_create failed with error status [%d]", err_status); - // Dispose of the keyboard to leave us in a consitent state - ReleaseKeyboardMemoryCore(&_td->lpActiveKeyboard->lpCoreKeyboard); - return FALSE; - } - - ResetCapsLock(); - err_status = km_kbp_keyboard_get_imx_list(_td->lpActiveKeyboard->lpCoreKeyboard, &_td->lpActiveKeyboard->lpIMXList); - if (err_status != KM_KBP_STATUS_OK) { - SendDebugMessageFormat(0, sdmLoad, 0, "Keyman_ForceKeyboard Core: km_kbp_keyboard_get_imx_list failed with error status [%d]", err_status); - // Dispose of the keyboard to leave us in a consistent state - ReleaseKeyboardMemoryCore(&_td->lpActiveKeyboard->lpCoreKeyboard); - return FALSE; - } - - LoadDLLsCore(_td->lpActiveKeyboard); - ActivateDLLs(_td->lpActiveKeyboard); - LoadKeyboardOptionsREGCore(_td->lpActiveKeyboard, _td->lpActiveKeyboard->lpCoreKeyboardState); - RefreshPreservedKeys(TRUE); - return TRUE; - } else { - if (LoadKeyboard(_td->ForceFileName, &_td->lpActiveKeyboard->Keyboard)) { - SendDebugMessageFormat(0, sdmGlobal, 0, "Keyman_ForceKeyboard: %s OK", FileName); - ResetCapsLock(); - LoadDLLs(_td->lpActiveKeyboard); - ActivateDLLs(_td->lpActiveKeyboard); - LoadKeyboardOptions(_td->lpActiveKeyboard); // I2437 - Crash unloading keyboard due to keyboard options not set - RefreshPreservedKeys(TRUE); - return TRUE; - } - } - SendDebugMessageFormat(0,sdmGlobal,0,"Keyman_ForceKeyboard: %s FAIL", FileName); - - delete _td->lpActiveKeyboard; - _td->lpActiveKeyboard = NULL; - - _td->ForceFileName[0] = 0; - return FALSE; -} - -extern "C" BOOL _declspec(dllexport) WINAPI Keyman_StopForcingKeyboard() -{ - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) - { - SetLastError(ERROR_KEYMAN_THREAD_DATA_NOT_READY); // I3173 // I3525 - return FALSE; - } - - if(!_td->lpActiveKeyboard) - { - SetLastError(ERROR_KEYMAN_KEYBOARD_NOT_ACTIVE); // I3173 // I3525 - return FALSE; - } - SendDebugMessageFormat(0,sdmGlobal,0,"Keyman_StopForcingKeyboard"); - if(_td->ForceFileName[0]) - { - SendDebugMessageFormat(0,sdmGlobal,0,"Keyman_StopForcingKeyboard: Stopping forcing"); - if(!DeactivateDLLs(_td->lpActiveKeyboard)) return FALSE; // I3173 // I3525 - if(!UnloadDLLs(_td->lpActiveKeyboard)) return FALSE; // I3173 // I3525 - _td->ForceFileName[0] = 0; - FreeKeyboardOptions(_td->lpActiveKeyboard); - ReleaseKeyboardMemory(_td->lpActiveKeyboard->Keyboard); - ReleaseStateMemoryCore(&_td->lpActiveKeyboard->lpCoreKeyboardState); - ReleaseKeyboardMemoryCore(&_td->lpActiveKeyboard->lpCoreKeyboard); - RefreshPreservedKeys(FALSE); - delete _td->lpActiveKeyboard; - _td->lpActiveKeyboard = NULL; - } - return TRUE; -} - //--------------------------------------------------------------------------------------------------------- // // Utility guff functions @@ -936,8 +803,6 @@ void RefreshKeyboards(BOOL Initialising) // Can happen when multiple top-level windows for one process - Addin_Refresh(); - SendDebugMessageFormat(0,sdmGlobal,0,"---ENTER RefreshKeyboards---"); //FInRefreshKeyboards = TRUE; @@ -1006,8 +871,6 @@ void RefreshKeyboards(BOOL Initialising) RefreshKeyboardProfiles(kp, FALSE); // Read standard profiles RefreshKeyboardProfiles(kp, TRUE); // Read transient profiles - kp->Keyboard = NULL; - SendDebugMessageFormat(0,sdmGlobal,0,"RefreshKeyboards: Added keyboard %s, %d", kp->Name, kp->KeymanID); i++; @@ -1035,13 +898,15 @@ void ReleaseKeyboards(BOOL Lock) if(!_td || !_td->lpKeyboards) return; - if(Lock) if(_td->lpActiveKeyboard && !_td->ForceFileName[0]) DeactivateDLLs(_td->lpActiveKeyboard); + if(Lock) { + if(_td->lpActiveKeyboard) { + DeactivateDLLs(_td->lpActiveKeyboard); + } + } for(int i = 0; i < _td->nKeyboards; i++) { if(Lock) UnloadDLLs(&_td->lpKeyboards[i]); - FreeKeyboardOptions(&_td->lpKeyboards[i]); - ReleaseKeyboardMemory(_td->lpKeyboards[i].Keyboard); ReleaseStateMemoryCore(&_td->lpKeyboards[i].lpCoreKeyboardState); ReleaseKeyboardMemoryCore(&_td->lpKeyboards[i].lpCoreKeyboard); if(_td->lpKeyboards[i].Profiles) delete _td->lpKeyboards[i].Profiles; // I3581 @@ -1051,7 +916,6 @@ void ReleaseKeyboards(BOOL Lock) delete _td->lpKeyboards; _td->lpKeyboards = NULL; - if(!_td->ForceFileName[0]) _td->lpActiveKeyboard = NULL; } /** diff --git a/windows/src/engine/keyman32/keyman32.def b/windows/src/engine/keyman32/keyman32.def index 84f0f24a91645bdf94c3fd1bcbee4b44f748372a..2fa0e767c550182fe27330d3bd30e6e3608e4c47 100644 --- a/windows/src/engine/keyman32/keyman32.def +++ b/windows/src/engine/keyman32/keyman32.def @@ -4,8 +4,6 @@ EXPORTS Keyman_GetInitialised Keyman_Initialise Keyman_Exit - Keyman_ForceKeyboard - Keyman_StopForcingKeyboard Keyman_GetLastActiveWindow Keyman_GetLastFocusWindow KMSetOutput diff --git a/windows/src/engine/keyman32/keyman32.vcxproj b/windows/src/engine/keyman32/keyman32.vcxproj index 0d78d2c2927fbd6d0f84100989fcb86badaebd2b..670df3c1d4197ec0a592a1aad3beff1d9e3dff6a 100644 --- a/windows/src/engine/keyman32/keyman32.vcxproj +++ b/windows/src/engine/keyman32/keyman32.vcxproj @@ -176,12 +176,6 @@ - - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) @@ -362,7 +356,6 @@ - diff --git a/windows/src/engine/keyman32/keyman32.vcxproj.filters b/windows/src/engine/keyman32/keyman32.vcxproj.filters index 8995c39a9308cfa03547f0b363de6eb9c2fc2e3f..dcf5f5d4c51c8069ebd39adeccb31c9307fb323c 100644 --- a/windows/src/engine/keyman32/keyman32.vcxproj.filters +++ b/windows/src/engine/keyman32/keyman32.vcxproj.filters @@ -15,9 +15,6 @@ - - Source Files - Source Files @@ -162,9 +159,6 @@ - - Header Files - Header Files diff --git a/windows/src/engine/keyman32/keymanengine.h b/windows/src/engine/keyman32/keymanengine.h index d56bcc1e3e14be8d34899c478ee3ccd934616487..f40aeaf3728ef1f2f09dba9305c4aab675d719dc 100644 --- a/windows/src/engine/keyman32/keymanengine.h +++ b/windows/src/engine/keyman32/keymanengine.h @@ -81,11 +81,9 @@ typedef struct tagINTKEYBOARDINFO DWORD __filler_Hotkey; DWORD __filler; // makes same as KEYBOARDINFO // I4462 char Name[256]; - LPKEYBOARD Keyboard; DWORD nIMDLLs; LPIMDLL IMDLLs; int __filler2; // makes same as KEYBOARDINFO - LPINTKEYBOARDOPTIONS KeyboardOptions; int nProfiles; LPINTKEYBOARDPROFILE Profiles; km_kbp_keyboard* lpCoreKeyboard; @@ -105,17 +103,11 @@ typedef struct tagKMSTATE { BOOL NoMatches; MSG msg; - // TODO: 5442 will remove these once windows core is deprecated - BOOL StopOutput; - int LoopTimes; - // TODO: 5442 WORD vkey; // I934 WCHAR charCode; // I4582 BOOL windowunicode; // I4287 BOOL isDown; - LPKEYBOARD lpkb; km_kbp_keyboard* lpCoreKb; // future use with IMDLL - LPGROUP startgroup; // TODO: 5442 will remove this once windows core is deprecated } KMSTATE; // I3616 @@ -129,7 +121,6 @@ LRESULT CALLBACK kmnLowLevelKeyboardProc( // I4124 _In_ LPARAM lParam ); -BOOL ReleaseKeyboardMemory(LPKEYBOARD kbd); BOOL ReleaseStateMemoryCore(km_kbp_state** state); BOOL ReleaseKeyboardMemoryCore(km_kbp_keyboard** kbd); @@ -230,8 +221,6 @@ DWORD HKLToKeyboardID(HKL hkl); WORD HKLToLanguageID(HKL hkl); WORD HKLToLayoutNumber(HKL hkl); WORD HKLToLayoutID(HKL hkl); -DWORD EthnologueCodeToKeymanID(DWORD EthCode); -DWORD EthnologueStringCodeToDWord(PWSTR EthCode); PWSTR GetSystemStore(LPKEYBOARD kb, DWORD SystemID); @@ -254,7 +243,6 @@ void keybd_shift(LPINPUT pInputs, int* n, BOOL isReset, LPBYTE const kbd); #include "keystate.h" #include "calldll.h" -#include "addins.h" #include "keymancontrol.h" #include "keyboardoptions.h" #include "kmprocessactions.h" diff --git a/windows/src/engine/keyman32/kmhook_getmessage.cpp b/windows/src/engine/keyman32/kmhook_getmessage.cpp index f6a56340f93e258059f436e092a4a2d5b37b0b45..562c455abb3cc53bbf6368915ed905150cb3ea32 100644 --- a/windows/src/engine/keyman32/kmhook_getmessage.cpp +++ b/windows/src/engine/keyman32/kmhook_getmessage.cpp @@ -248,19 +248,6 @@ LRESULT _kmnGetMessageProc(int nCode, WPARAM wParam, LPARAM lParam) return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam); } - /* - Handle WM_UNICHAR messages for RichEdit control -- should we test RichEdit version? - */ - - if(mp->message == WM_UNICHAR && Addin_ShouldProcessUnichar(mp->hwnd)) - { - if(Addin_ProcessUnichar(mp->hwnd, (DWORD) mp->wParam)) - { - mp->message = 0; - return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam); - } - } - /* Handle wm_keyman_control_internal messages */ @@ -271,7 +258,6 @@ LRESULT _kmnGetMessageProc(int nCode, WPARAM wParam, LPARAM lParam) if(_td->lpActiveKeyboard) { - _td->state.lpkb = _td->lpActiveKeyboard->Keyboard; _td->state.lpCoreKb = _td->lpActiveKeyboard->lpCoreKeyboard; } // I4412 @@ -297,20 +283,17 @@ LRESULT _kmnGetMessageProc(int nCode, WPARAM wParam, LPARAM lParam) { if(_td->app) { - BOOL isUsingCoreProcessor = Globals::get_CoreIntegration(); - - if (isUsingCoreProcessor) { - // Call the core keyboard processor to process the queued actions - if (!_td->lpActiveKeyboard) { - return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam); - } - if (KM_KBP_STATUS_OK != km_kbp_process_queued_actions(_td->lpActiveKeyboard->lpCoreKeyboardState)) { - SendDebugMessageFormat(0, sdmGlobal, 0, "_kmnGetMessageProc wm_keymanim_close process event fail"); - return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam); - } - BOOL emitKeyStroke; - ProcessActions(&emitKeyStroke); + // Call the core keyboard processor to process the queued actions + if (!_td->lpActiveKeyboard) { + return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam); } + if (KM_KBP_STATUS_OK != km_kbp_process_queued_actions(_td->lpActiveKeyboard->lpCoreKeyboardState)) { + SendDebugMessageFormat(0, sdmGlobal, 0, "_kmnGetMessageProc wm_keymanim_close process event fail"); + return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam); + } + BOOL emitKeyStroke; + ProcessActions(&emitKeyStroke); + _td->app->SetCurrentShiftState(Globals::get_ShiftState()); _td->app->SendActions(); } @@ -372,7 +355,6 @@ void ProcessWMKeyman(HWND hwnd, WPARAM wParam, LPARAM lParam) hwnd = GetFocus(); if(_td->lpActiveKeyboard) { - _td->state.lpkb = _td->lpActiveKeyboard->Keyboard; _td->state.lpCoreKb = _td->lpActiveKeyboard->lpCoreKeyboard; } @@ -384,7 +366,6 @@ void ProcessWMKeyman(HWND hwnd, WPARAM wParam, LPARAM lParam) { if(_td->app) _td->app->ResetQueue(); GetCapsAndNumlockState(); - Addin_FocusChanged(hwnd); UpdateActiveWindows(); } } diff --git a/windows/src/engine/keyman32/kmprocess.cpp b/windows/src/engine/keyman32/kmprocess.cpp index 7faac081c279526359bd4a2ba918bfd953f11bf6..64a6a48e98d3f2b5d8bdd2c5dceaf1907c16da22 100644 --- a/windows/src/engine/keyman32/kmprocess.cpp +++ b/windows/src/engine/keyman32/kmprocess.cpp @@ -119,7 +119,6 @@ Process_Event_Core(PKEYMAN64THREADDATA _td) { return TRUE; } - /* * BOOL ProcessHook(); * @@ -138,10 +137,7 @@ BOOL ProcessHook() PKEYMAN64THREADDATA _td = ThreadGlobals(); if(!_td) return FALSE; - LPGROUP gp = _td->state.startgroup; - fOutputKeystroke = FALSE; // TODO: 5442 no longer needs to be global once we use core processor - BOOL isUsingCoreProcessor = Globals::get_CoreIntegration(); // // If we are running in the debugger, don't do a second run through // @@ -160,44 +156,28 @@ BOOL ProcessHook() Debug_VirtualKey(_td->state.vkey), getcontext_debug()); } - AIDEBUGKEYINFO keyinfo; - keyinfo.shiftFlags = Globals::get_ShiftState(); - keyinfo.VirtualKey = _td->state.vkey; - keyinfo.Character = _td->state.charCode; - keyinfo.DeadKeyCharacter = 0; // I4582 - keyinfo.IsUp = !_td->state.isDown; - if(_td->app->IsUnicode()) - _td->app->QueueDebugInformation(QID_BEGIN_UNICODE, NULL, NULL, NULL, NULL, (DWORD_PTR) &keyinfo); - else - _td->app->QueueDebugInformation(QID_BEGIN_ANSI, NULL, NULL, NULL, NULL, (DWORD_PTR) &keyinfo); } - if (isUsingCoreProcessor) { // TODO: 5442 Note: Nested if will be reduced once using core only - // For applications not using the TSF kmtip calls this function twice for each keystroke, - // first to determine if we are doing processing work (TIPFUpdateable == FALSE), - // if we say yes it will call a second time to actually do the work. - // We call the core process event only once and use the core's queued actions - // on the second pass. - // For the TSF in most cases kmtip (except OnPreservedKey) will not call the non-updateable test parse. - // Therfore the core process event will need to be called before processing the actions. - - // CoreProcessEventRun would be a sufficient test however testing TIPFUpdateable defines - // the status of the keystroke processing more precisely. - if (!_td->TIPFUpdateable || !_td->CoreProcessEventRun) { - if (!Process_Event_Core(_td)) { - return FALSE; - } - } - - if (!_td->TIPFUpdateable) { - ProcessActionsNonUpdatableParse(&fOutputKeystroke); - } else { - ProcessActions(&fOutputKeystroke); + // For applications not using the TSF kmtip calls this function twice for each keystroke, + // first to determine if we are doing processing work (TIPFUpdateable == FALSE), + // if we say yes it will call a second time to actually do the work. + // We call the core process event only once and use the core's queued actions + // on the second pass. + // For the TSF in most cases kmtip (except OnPreservedKey) will not call the non-updateable test parse. + // Therfore the core process event will need to be called before processing the actions. + + // CoreProcessEventRun would be a sufficient test however testing TIPFUpdateable defines + // the status of the keystroke processing more precisely. + if (!_td->TIPFUpdateable || !_td->CoreProcessEventRun) { + if (!Process_Event_Core(_td)) { + return FALSE; } - } - else { - ProcessGroup(gp); // TODO: 5442 remove + + if (!_td->TIPFUpdateable) { + ProcessActionsNonUpdatableParse(&fOutputKeystroke); + } else { + ProcessActions(&fOutputKeystroke); } if (fOutputKeystroke && !_td->app->IsQueueEmpty()) { @@ -256,671 +236,9 @@ BOOL ProcessHook() // PWSTR contextBuf = _td->app->ContextBufMax(MAXCONTEXT); // SendDebugMessageFormat(0, sdmAIDefault, 0, "Kmprocess::ProcessHook After cxt=%s", Debug_UnicodeString(contextBuf, 1)); - _td->app->QueueDebugInformation(QID_END, NULL, NULL, NULL, NULL, 0); return !fOutputKeystroke; } -/* -* PRIVATE BOOL ProcessGroup(LPGROUP gp); -* -* Parameters: gp Pointer to group to process inside -* -* Returns: TRUE if messages are to be sent, -* and FALSE if no messages are to be sent. -* -* Called by: ProcessHook, recursive inside groups -* -* ProcessKey is where the keystroke conversion and output takes place. This routine -* has a lot of crucial code in it! -*/ - -BOOL ProcessGroup(LPGROUP gp) -{ - if (!DebugAssert(!Globals::get_CoreIntegration(), "KMPROCESS:ProcessGroup: Error called in core integration mode")) { - return FALSE; - } - DWORD i; - LPKEY kkp = NULL; - PWSTR p; - int sdmfI; - - /* - If the number of nested groups goes higher than 50, then break out - this is - a limitation of stack size. This is basically a catch-all for freaky apps that - cause message loopbacks and nasty things like that. Okay, it's really a catch all - for bugs! This means the user's system shouldn't hang. - */ - - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - - _td->app->QueueDebugInformation(QID_GROUP_ENTER, gp, NULL, NULL, NULL, 0); - - sdmfI = -1; - - for(i = 0; i < _td->state.lpkb->cxGroupArray; i++) - if(gp == &_td->state.lpkb->dpGroupArray[i]) - { - if(_td->state.msg.message == wm_keymankeydown && ShouldDebug(sdmKeyboard)) - SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, "Entering group %d of %d, context '%s'", i+1, _td->state.lpkb->cxGroupArray, getcontext_debug()); - sdmfI = i; - break; - } - - if(++_td->state.LoopTimes > 50) - { - if(_td->state.msg.message == wm_keymankeydown) SendDebugMessage(_td->state.msg.hwnd, sdmKeyboard, 0, "Aborting output: state.LoopTimes exceeded."); - _td->state.StopOutput = TRUE; - _td->app->QueueDebugInformation(QID_GROUP_EXIT, gp, NULL, NULL, NULL, QID_FLAG_RECURSIVE_OVERFLOW); - return FALSE; - } - - _td->state.NoMatches = TRUE; - - /* - The rule matching loop. - - This loop iterates through all the rules in the group that is currently being - processed. Each rule in a group can be of three different types: - 1. A virtual key rule, where the key to be matched is a virtual key - 2. A normal key rule (WM_CHAR), where the key to be matched is an Ascii char. - 3. A rule in a keyless group, where only the context is matched. - - The loop goes through and checks the rules like that. This loop could be optimized - with standard searching techniques - the ContextMatch may be difficult. - */ - - if(ShouldDebug(sdmKeyboard)) - SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, "state.vkey: %s shiftFlags: %x; charCode: %X", - Debug_VirtualKey(_td->state.vkey), Globals::get_ShiftState(), _td->state.charCode); // I4582 - - if(gp) - { - for(kkp = gp->dpKeyArray, i=0; i < gp->cxKeyArray; i++, kkp++) - { - if(!ContextMatch(kkp)) continue; - if(!gp->fUsingKeys) - { - if(kkp->dpContext[0] != 0) break; else continue; - } - - //if(kkp->Key == state.vkey) - //SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, 0, "kkp->Key: %d kkp->ShiftFlags: %x", - // kkp->Key, kkp->ShiftFlags); - - /* Keyman 6.0: support Virtual Characters */ - if(IsEquivalentShift(kkp->ShiftFlags, Globals::get_ShiftState())) - { - if(kkp->Key > VK__MAX && kkp->Key == _td->state.vkey) break; // I3438 // I4582 - else if(kkp->Key == _td->state.vkey) break; // I4169 - } - else if(kkp->ShiftFlags == 0 && kkp->Key == _td->state.charCode && _td->state.charCode != 0) break; - } - } - - if(!gp || i == gp->cxKeyArray) - { - /* - No rule was found that corresponded to the current state of the context and - keyboard. NoMatch should be checked for everything except virtual keys; and - context should also be kept. - - If the message was a virtual key, then just return without checking NoMatch. - NoMatch shouldn't be used for virtual keys because it will mean that no key - can ever get through that isn't matched - including arrows, func. keys, etc !! - Context is not kept for virtual keys being output. - */ - - if(_td->state.msg.message == wm_keymankeydown && ShouldDebug(sdmKeyboard)) SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, - "No match was found in group %d of %d", sdmfI, _td->state.lpkb->cxGroupArray); - - if(!gp || (_td->state.charCode == 0 && gp->fUsingKeys)) // I4585 - // 7.0.241.0: I1133 - Fix mismatched parentheses on state.charCode - ie. we don't want to output this letter if !gp->fUsingKeys - { - BOOL fIsBackspace = _td->state.vkey == VK_BACK && (Globals::get_ShiftState() & (LCTRLFLAG|RCTRLFLAG|LALTFLAG|RALTFLAG)) == 0; // I4128 - - if(/*_td->app->DebugControlled() &&*/ fIsBackspace) { // I4838 // I4933 - if(_td->state.msg.message == wm_keymankeydown) { // I4933 - if(!_td->app->IsLegacy()) { // I4933 - PWCHAR pdeletecontext = _td->app->ContextBuf(1); // I4933 - if(!pdeletecontext || *pdeletecontext == 0) { // I4933 - _td->app->ResetContext(); // I4933 - fOutputKeystroke = TRUE; // I4933 - return FALSE; // I4933 - } - if (Uni_IsSurrogate1(*pdeletecontext) && Uni_IsSurrogate2(*(pdeletecontext+1))) { - // 2 backspaces to delete both parts of surrogate pair - // This only needs to be done for TSF-aware apps as legacy apps - // will receive a BKSP WM_KEYDOWN event which results in deleting - // both parts in one action - _td->app->QueueAction(QIT_BACK, BK_BACKSPACE | BK_SURROGATE); - } - else { - _td->app->QueueAction(QIT_BACK, BK_BACKSPACE); - } - } - else { - _td->app->QueueAction(QIT_BACK, BK_BACKSPACE); // I4933 - } - } - } else if( (!_td->app->IsLegacy() || !fIsBackspace) && !_td->TIPFPreserved) { // I4024 // I4128 // I4287 // I4290 - SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, " ... IsLegacy = FALSE; IsTIP = TRUE"); // I4128 - if(_td->state.charCode == 0) _td->app->ResetContext(); // I3573 // I3577 // I4585 - fOutputKeystroke = TRUE; - return FALSE; - } - //fOutputKeystroke = TRUE; return FALSE; // Don't swallow keystroke // I3577 - ///SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, " ... IsLegacy = TRUE; IsTIP = TRUE"); - - /* - If the key is not a character key (white keys), or not processing, then we must init the stack - - unknown keys do things like moving position in the context, so must clear. - */ - if(fIsBackspace) // I4128 - { - /* - Must have special handling for VK_BACK: delete a character from the context stack - This only fires if the keyboard has no rule for backspace. - */ - } - else - { - //app->NoSetShift = FALSE; - - DWORD dw = _td->state.vkey; - if(dw == 0x05) dw = VK_RETURN; // I649 - VK_ENTER and K_NPENTER - - if(_td->state.msg.lParam & (1<<24)) dw |= QVK_EXTENDED; // Extended key flag // I3438 - - if(_td->state.charCode == 0) { - _td->app->ResetContext(); - } - - if(_td->TIPFPreserved) { // I4290 - if(_td->state.charCode != 0) { - _td->app->QueueAction(QIT_CHAR, _td->state.charCode); - } - } else { - if(_td->state.msg.message == wm_keymankeydown) - { - _td->app->QueueAction(QIT_VSHIFTDOWN, Globals::get_ShiftState()); // 15/05/2001 - fixing I201 -- enabled line - _td->app->QueueAction(QIT_VKEYDOWN, dw); - } - - if(_td->state.msg.message == wm_keymankeyup) { - _td->app->QueueAction(QIT_VKEYUP, dw); - _td->app->QueueAction(QIT_VSHIFTUP, Globals::get_ShiftState()); - } - } - } - } - else if (gp->dpNoMatch != NULL && *gp->dpNoMatch != 0 && _td->state.msg.message != wm_keymankeyup) - { - /* NoMatch rule found, and is a character key */ - _td->app->QueueDebugInformation(QID_NOMATCH_ENTER, gp, NULL, NULL, gp->dpNoMatch, 0); - PostString(gp->dpNoMatch, &_td->state.msg, _td->state.lpkb, NULL); - _td->app->QueueDebugInformation(QID_NOMATCH_EXIT, gp, NULL, NULL, gp->dpNoMatch, 0); - } - else if (_td->state.charCode != 0 && _td->state.charCode != 0xFFFF && _td->state.msg.message != wm_keymankeyup && gp->fUsingKeys) - { - /* No rule found, is a character key */ - // 7.0.239.0: I994 - Workaround output order issues - we will use the TSF to output all characters... - // if(app->Type1() == AIType_TIP) { fOutputKeystroke = TRUE; return FALSE; } // Don't swallow keystroke - - _td->app->QueueAction(QIT_CHAR, _td->state.charCode); - } - - _td->app->QueueDebugInformation(QID_GROUP_EXIT, gp, NULL, NULL, NULL, QID_FLAG_NOMATCH); - return TRUE; - } - - if(_td->state.msg.message == wm_keymankeyup) - return TRUE; - - SendDebugMessageFormat(_td->state.msg.hwnd, sdmKeyboard, 0, "match found in rule %d", i); - - _td->state.NoMatches = FALSE; - - /* - Save the context that will be used for output when the 'context' keyword is used. - For each deadkey, we need to add 2 characters; look in related stores as well... - */ - - assert(kkp != NULL); - - _td->miniContextIfLen = xstrlen(kkp->dpContext) - xstrlen_ignoreifopt(kkp->dpContext); - - // 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support - if(*kkp->dpContext == UC_SENTINEL && *(kkp->dpContext+1) == CODE_NUL) - wcsncpy_s(_td->miniContext, GLOBAL_ContextStackSize, _td->app->ContextBuf(xstrlen_ignoreifopt(kkp->dpContext)-1), GLOBAL_ContextStackSize); // I3162 // I3536 - else - wcsncpy_s(_td->miniContext, GLOBAL_ContextStackSize, _td->app->ContextBuf(xstrlen_ignoreifopt(kkp->dpContext)), GLOBAL_ContextStackSize); // I3162 // I3536 - - _td->miniContext[GLOBAL_ContextStackSize-1] = 0; - - _td->app->QueueDebugInformation(QID_RULE_ENTER, gp, kkp, _td->miniContext, NULL, 0); - - /* - The next section includes several optimizations that make the code a little harder - to read, but are probably worth it in the time that they save. - - If the output string doesn't have a "context" byte at the start, post backspaces - to erase the appropriate number of characters in the application. If it does have - a "context" byte at the start, then the string won't change, and no backspaces are - necessary. You could go one step further with this optimization, in PostAllKeys, - by comparing the starts of the strings to see what is same, and not backspacing - that, but it is probably not necessary. - */ - - p = kkp->dpOutput; - if(*p != UC_SENTINEL || *(p+1) != CODE_CONTEXT) { - for(PWSTR mcp = decxstr(wcschr(_td->miniContext, 0), _td->miniContext); mcp != NULL; mcp = decxstr(mcp, _td->miniContext)) { - if (*mcp == UC_SENTINEL) { - switch (*(mcp + 1)) { - case CODE_DEADKEY: _td->app->QueueAction(QIT_BACK, BK_DEADKEY); break; - case CODE_NUL: break; // 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support - } - } - else if (Uni_IsSurrogate1(*mcp) && Uni_IsSurrogate2(*(mcp + 1))) { - // 2 backspaces to delete both parts of surrogate pair - // This only needs to be done for TSF-aware apps as legacy apps - // will receive a BKSP WM_KEYDOWN event which results in deleting - // both parts in one action - _td->app->QueueAction(QIT_BACK, BK_SURROGATE); - } - else { - _td->app->QueueAction(QIT_BACK, 0); - } - } - } - else { - // otherwise, the "context" entry has to be jumped over - p += 2; - } - - /* Use PostString to post the rest of the output string. */ - - if(PostString(p, &_td->state.msg, _td->state.lpkb, NULL) == psrCheckMatches) - { - _td->app->QueueDebugInformation(QID_RULE_EXIT, gp, kkp, _td->miniContext, NULL, 0); - - if(gp->dpMatch && *gp->dpMatch) - { - _td->app->QueueDebugInformation(QID_MATCH_ENTER, gp, NULL, NULL, gp->dpMatch, 0); - PostString(gp->dpMatch, &_td->state.msg, _td->state.lpkb, NULL); - _td->app->QueueDebugInformation(QID_MATCH_EXIT, gp, NULL, NULL, gp->dpMatch, 0); - } - } - else - _td->app->QueueDebugInformation(QID_RULE_EXIT, gp, kkp, _td->miniContext, NULL, 0); - _td->app->QueueDebugInformation(QID_GROUP_EXIT, gp, NULL, NULL, NULL, 0); - - return TRUE; -} - -/* -* int PostString( LPSTR str, BOOL *useMode, LPMSG mp, -* LPKEYBOARD lpkb ); -* -* Parameters: str Pointer to string to send -* useMode Pointer to BOOL about whether a "use" command was found -* mp Pointer to MSG structure to copy in outputting messages -* lpkb Pointer to global keyboard structure -* -* Returns: 0 to continue, 1 and 2 to return. -* -* Called by: ProcessKey -* -* PostString posts a string of "context", "index", "beep", characters and virtual keys -* to the active application, via the Keyman PostKey buffer. -*/ - -int PostString(PWSTR str, LPMSG mp, LPKEYBOARD lpkb, PWSTR endstr) -{ - if (!DebugAssert(!Globals::get_CoreIntegration(), "KKMPROCESS:PostString: Error called in core integration mode")) { - return FALSE; - } - PWSTR p, q, temp; - LPSTORE s; - int n1, n2; - int i, n, shift; - BOOL FoundUse = FALSE; - - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - - // TODO: Refactor to use incxstr - for(p = str; *p && (p < endstr || !endstr); p++) - { - if(*p == UC_SENTINEL) - switch(*(++p)) - { - case CODE_EXTENDED: // Start of a virtual key section w/shift codes - p++; - - shift = *p; //(*p<<8) | *(p+1); - _td->app->QueueAction(QIT_VSHIFTDOWN, shift); - - p++; - - _td->app->QueueAction(QIT_VKEYDOWN, *p); - _td->app->QueueAction(QIT_VKEYUP, *p); - - _td->app->QueueAction(QIT_VSHIFTUP, shift); - - p++; // CODE_EXTENDEDEND - ////// CODE_EXTENDEDEND will be incremented by loop - - //app->QueueAction(QIT_VSHIFTUP, shift); - break; - - case CODE_DEADKEY: // A deadkey to be output - p++; - _td->app->QueueAction(QIT_DEADKEY, *p); - break; - case CODE_BEEP: // Sound an 'iconasterisk' beep - _td->app->QueueAction(QIT_BELL, 0); - break; - case CODE_CONTEXT: // copy the context to the output - PostString(_td->miniContext, mp, lpkb, wcschr(_td->miniContext, 0)); - break; - case CODE_CONTEXTEX: - p++; - for(q = _td->miniContext, i = _td->miniContextIfLen; *q && i < *p-1; i++, q=incxstr(q)); - if(*q) { - temp = incxstr(q); - PostString(q, mp, lpkb, temp); - } - break; - case CODE_RETURN: // stop processing and start PostAllKeys - _td->state.StopOutput = TRUE; - return psrPostMessages; - - case CODE_CALL: - p++; - CallDLL(_td->lpActiveKeyboard, *p-1); - if(_td->state.StopOutput) return psrPostMessages; - FoundUse = TRUE; - break; - case CODE_USE: // use another group - p++; - ProcessGroup(&lpkb->dpGroupArray[*p-1]); - if(_td->state.StopOutput) return psrPostMessages; - FoundUse = TRUE; - break; - case CODE_CLEARCONTEXT: - _td->app->ResetContext(); - _td->app->ReadContext(); - break; - case CODE_INDEX: - p++; - s = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[*p - 1]; - p++; - - n = _td->IndexStack[*p - 1]; - for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--); - PostString(temp, mp, lpkb, incxstr(temp)); - break; - case CODE_SETOPT: - p++; - n1 = *p - 1; - p++; - n2 = *p - 1; - SetKeyboardOption(_td->lpActiveKeyboard, n1, n2); - break; - case CODE_RESETOPT: - p++; - n1 = *p - 1; - ResetKeyboardOption(_td->lpActiveKeyboard, n1); - break; - case CODE_SAVEOPT: - p++; - n1 = *p - 1; - SaveKeyboardOption(_td->lpActiveKeyboard, n1); - break; - case CODE_IFSYSTEMSTORE: - p+=3; - break; - case CODE_SETSYSTEMSTORE: - p+=2; - break; - } - else - _td->app->QueueAction(QIT_CHAR, *p); - } - return FoundUse ? psrPostMessages : psrCheckMatches; -} - - -BOOL IsMatchingBaseLayout(PWCHAR layoutName) // I3432 -{ - BOOL bEqual = _wcsicmp(layoutName, Globals::get_BaseKeyboardName()) == 0 || // I4583 - _wcsicmp(layoutName, Globals::get_BaseKeyboardNameAlt()) == 0; // I4583 - - return bEqual; -} - -BOOL IsMatchingPlatformString(PWCHAR platform) // I3432 -{ - return - _wcsicmp(platform, L"windows") == 0 || - _wcsicmp(platform, L"desktop") == 0 || - _wcsicmp(platform, L"hardware") == 0 || - _wcsicmp(platform, L"native") == 0; -} - -BOOL IsMatchingPlatform(LPSTORE s) // I3432 -{ - PWCHAR t = new WCHAR[wcslen(s->dpString)+1]; - wcscpy_s(t, wcslen(s->dpString)+1, s->dpString); - PWCHAR context = NULL; - PWCHAR platform = wcstok_s(t, L" ", &context); - while(platform != NULL) - { - if(!IsMatchingPlatformString(platform)) - { - s->dwSystemID = TSS_PLATFORM_NOMATCH; - delete[] t; - return FALSE; - } - platform = wcstok_s(NULL, L" ", &context); - } - - s->dwSystemID = TSS_PLATFORM_MATCH; - delete[] t; - return TRUE; -} - -/* -* BOOL ContextMatch( LPKEY kkp ); -* -* Parameters: kkp Rule to compare -* -* Returns: 0 on OK, 1 on not equal -* -* Called by: ProcessKey -* -* ContextMatch compares the context of a rule with the current context. -*/ - -BOOL ContextMatch(LPKEY kkp) -{ - if (!DebugAssert(!Globals::get_CoreIntegration(), "KMPROCESS:ContextMatch: Error called in core integration mode")) { - return FALSE; - } - WORD /*i,*/ n; - PWSTR p, q, qbuf, temp; - LPWORD indexp; - LPSTORE s, t; - BOOL bEqual; - - //SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: ENTER [%d]", kkp->Line); - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - - memset(_td->IndexStack, 0, GLOBAL_ContextStackSize*sizeof(WORD)); // I3158 // I3524 - - p = kkp->dpContext; - - if(*p == 0) - { - //SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: EXIT TRUE -> no rule context"); - return TRUE; - } - - /* 11 Aug 2003 - I25(v6) - mcdurdin - test for CODE_NUL */ - - if(*p == UC_SENTINEL && *(p+1) == CODE_NUL) - { - // If context buf is longer than the context, then obviously not start of doc. - if(_td->app->ContextBuf(xstrlen_ignoreifopt(p))) return FALSE; // I2484 - Fix bug with if() following nul in same statement - p = incxstr(p); - if(*p == 0) return TRUE; - } - - for(PWCHAR pp = p; pp && *pp; pp = incxstr(pp)) - { - if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFOPT) - { - s = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(pp+2))-1]; // I2590 - t = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(pp+4))-1]; // I2590 - - bEqual = wcscmp(s->dpString, t->dpString) == 0; - if(*(pp+3) == 1 && bEqual) return FALSE; // I2590 - if(*(pp+3) == 2 && !bEqual) return FALSE; // I2590 - } - else if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFSYSTEMSTORE) // I3432 - { - DWORD dwSystemID = *(pp+2)-1; - t = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(pp+4))-1]; // I2590 - switch(dwSystemID) - { - case TSS_PLATFORM_MATCH: // Cached platform result - a matching platform - bEqual = TRUE; - break; - case TSS_PLATFORM_NOMATCH: // Cached platform result - not a matching platform - bEqual = FALSE; - break; - case TSS_PLATFORM: - bEqual = IsMatchingPlatform(t); - break; - case TSS_BASELAYOUT: - bEqual = IsMatchingBaseLayout(t->dpString); - break; - default: - { - PWCHAR ss = GetSystemStore(_td->lpActiveKeyboard->Keyboard, dwSystemID); - if(ss == NULL) return FALSE; - bEqual = wcscmp(ss, t->dpString) == 0; - } - } - - if(*(pp+3) == 1 && bEqual) return FALSE; // I2590 - if(*(pp+3) == 2 && !bEqual) return FALSE; // I2590 - } - } - - q = qbuf = _td->app->ContextBuf(xstrlen_ignoreifopt(p)); - if(!q) - { - //SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: EXIT FALSE -> context too short"); - return FALSE; // context buf is too short! - } - indexp = _td->IndexStack; - - //SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: [%d] Rule: %s", kkp->Line, format_unicode_debug(kkp->dpContext)); - //SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: [%d] Test: %s", kkp->Line, format_unicode_debug(q)); - - for(; *p && *q; p = incxstr(p)) - { - //SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: p:%x q:%x", *p, *q); - *indexp = 0; - - if(*p == UC_SENTINEL) - { - switch(*(p+1)) - { - case CODE_DEADKEY: - if(*q != UC_SENTINEL || *(q+1) != CODE_DEADKEY || *(q+2) != *(p+2)) - { - // SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: EXIT FALSE -> deadkeys don't match %x %x %x != %x %x %x", - // *p, *(p+1), *(p+2), *q, *(q+1), *(q+2)); - return FALSE; - } - break; - case CODE_ANY: - s = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(p+2))-1]; - - temp = xstrchr(s->dpString, q); - - /*SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard ,kkp->Line, "ContextMatch: CODE_ANY [%x %x %x %x %x %x %x %x %x %x] [%x %x %x] %d", - s->dpString[0], s->dpString[1], s->dpString[2], - s->dpString[3], s->dpString[4], s->dpString[5], - s->dpString[6], s->dpString[7], s->dpString[8], - s->dpString[9], - q[0], q[1], q[2], - (temp ? (INT_PTR)(temp-s->dpString) : 0));*/ - - if(temp != NULL) // I1622 - *indexp = (WORD) xstrpos(temp, s->dpString); - - //if((temp = xstrchr(s->dpString, GetSuppChar(q))) != NULL) - // *indexp = xstrpos(temp, s->dpString); - else - return FALSE; - break; - case CODE_NOTANY: - s = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(p+2))-1]; - - if((temp = xstrchr(s->dpString, q)) != NULL) // I1622 - return FALSE; - - //if((temp = xstrchr(s->dpString, GetSuppChar(q))) != NULL) - // return FALSE; - break; - case CODE_INDEX: - s = &_td->lpActiveKeyboard->Keyboard->dpStoreArray[(*(p+2))-1]; - *indexp = n = _td->IndexStack[(*(p+3))-1]; - - for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--); - if(n != 0) return FALSE; - if(xchrcmp(temp, q) != 0) return FALSE; - ////if(GetSuppChar(temp) != GetSuppChar(q)) return FALSE; // I1622 - break; - case CODE_CONTEXTEX: - // only the nth character - for(n = *(p+2) - 1, temp = qbuf; temp < q && n > 0; n--, temp = incxstr(temp)); - if(n == 0) - if(xchrcmp(temp, q) != 0) return FALSE; - //if(GetSuppChar(temp) != GetSuppChar(q)) return FALSE; - break; - case CODE_IFOPT: - case CODE_IFSYSTEMSTORE: // I3432 - indexp++; - continue; // don't increment q - default: - return FALSE; - } - } - else if(xchrcmp(p, q) != 0) //GetSuppChar(p) != GetSuppChar(q)) - { - //SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: [%d] FAIL: %s", kkp->Line, format_unicode_debug(p)); - //SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: [%d] FAIL: %s", kkp->Line, format_unicode_debug(q)); - //SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: EXIT FALSE -> chrs don't match"); - return FALSE; - } - indexp++; - q = incxstr(q); - } - - while(*p == UC_SENTINEL && (*(p+1) == CODE_IFOPT || *(p+1) == CODE_IFSYSTEMSTORE)) p = incxstr(p); // already tested // I3432 - - //SendDebugMessageFormat(state.msg.hwnd, sdmKeyboard, kkp->Line, "ContextMatch: EXIT %s -> END OF FUNCTION", - // *p == *q ? "TRUE" : "FALSE"); - return *p == *q; /*at least one must ==0 at this point*/ -} - - PWSTR strtowstr(PSTR in) { PWSTR result; @@ -933,7 +251,6 @@ PWSTR strtowstr(PSTR in) return result; } - PSTR wstrtostr(PCWSTR in) { PSTR result; diff --git a/windows/src/engine/keyman32/kmprocessactions.cpp b/windows/src/engine/keyman32/kmprocessactions.cpp index 1e07523925079b9b1426d769c2a74b8bc48dee2d..8855ad7e15ed4e4dd2603180991fb29b3810e60b 100644 --- a/windows/src/engine/keyman32/kmprocessactions.cpp +++ b/windows/src/engine/keyman32/kmprocessactions.cpp @@ -71,7 +71,7 @@ static BOOL processPersistOpt( SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessHook: Saving option to registry for keyboard [%s].", activeKeyboard->Name); LPWSTR value = new WCHAR[sizeof(actionItem->option->value) + 1]; wcscpy_s(value, sizeof(actionItem->option->value) + 1, reinterpret_cast(actionItem->option->value)); - SaveKeyboardOptionREGCore(activeKeyboard, reinterpret_cast(actionItem->option->key), value); + SaveKeyboardOptionCoretoRegistry(activeKeyboard, reinterpret_cast(actionItem->option->key), value); } } return TRUE; diff --git a/windows/src/engine/keyman32/preservedkeymap.cpp b/windows/src/engine/keyman32/preservedkeymap.cpp index cd13a82f8c885a95f3a91a87f08e62c46984e8c6..1fd803838d7aa250eee4a84789b752cf4835b55e 100644 --- a/windows/src/engine/keyman32/preservedkeymap.cpp +++ b/windows/src/engine/keyman32/preservedkeymap.cpp @@ -37,12 +37,11 @@ struct PreservedKey class PreservedKeyMap { public: - BOOL MapKeyboard(KEYBOARD *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys); /** * Updates a map of preserved keys (with GUID) that are used in keyboard rules. * Passing in NULL for pPreservedKeys will cause cPreservedKeys to be set to the number - * of preserved keys needed for the supplied pKeyboard; this should be used in creating - * pPreservedKeys list to sufficient size. When pPreservedKeys list is passed the + * of preserved keys needed for the supplied pKeyboard; this should be used in creating + * pPreservedKeys list to sufficient size. When pPreservedKeys list is passed the * cPreservedKeys will be the actual count of the number of unique pPreservedKeys * * @param pKeyboard the keyboard for which the rules will be extracted from @@ -50,14 +49,13 @@ public: * @param cPreservedKeys number of preserved keys in pPreservedKeys - or the size pPreservedKeys needs to be * @return BOOL return TRUE on success */ - BOOL MapKeyboardCore(km_kbp_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys); + BOOL MapKeyboard(km_kbp_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys); private: BOOL m_BaseKeyboardUsesAltGr; // I4592 UINT ShiftToTSFShift(UINT ShiftFlags); BOOL MapUSCharToVK(UINT *puKey, UINT *puShiftFlags); - BOOL MapKeyRule(KEY *pKey, TF_PRESERVEDKEY *pPreservedKey); - BOOL MapKeyRuleCore(km_kbp_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey); + BOOL MapKeyRule(km_kbp_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey); BOOL IsMatchingKey(PreservedKey *pKey, PreservedKey *pKeys, size_t cKeys); }; @@ -199,45 +197,8 @@ UINT PreservedKeyMap::ShiftToTSFShift(UINT ShiftFlags) return res; } -BOOL PreservedKeyMap::MapKeyRule(KEY *pKey, TF_PRESERVEDKEY *pPreservedKey) -{ - UINT ShiftFlags; - UINT Key; - - Key = pKey->Key; - ShiftFlags = pKey->ShiftFlags; - - if(Key == VK_BACK || Key == VK_RETURN || Key == VK_TAB) // I4575 - { - // - // We never map backspace, return or tab because these are the only supported virtual key outputs, - // and result in recursion. Sadly, this is an imperfect solution forced upon us by preserved key - // limitations. - // - // Other virtual key output will be blocked with this version. - return FALSE; - } - - if (Key > 255) { - // - // Touch-defined keys have a value > 255, but these should never be preserved - // - return FALSE; - } - - if(ShiftFlags == 0) - { - if(!MapUSCharToVK(&Key, &ShiftFlags)) return FALSE; - } - - pPreservedKey->uVKey = (UINT) USVKToScanCodeToLayoutVK( (WORD) Key); // I3762 - pPreservedKey->uModifiers = ShiftToTSFShift(ShiftFlags); - - return TRUE; -} - BOOL -PreservedKeyMap::MapKeyRuleCore(km_kbp_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey) { +PreservedKeyMap::MapKeyRule(km_kbp_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey) { UINT ShiftFlags; UINT Key; @@ -280,94 +241,8 @@ BOOL PreservedKeyMap::IsMatchingKey(PreservedKey *pKey, PreservedKey *pKeys, siz return FALSE; } -// TODO: 5442 - Remove once core processor verfied -BOOL PreservedKeyMap::MapKeyboard(KEYBOARD *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys) -{ - size_t cKeys = 0, n; - DWORD i, j; - GROUP *pGroup; - - m_BaseKeyboardUsesAltGr = KeyboardGivesCtrlRAltForRAlt(); // I4592 - - // This is not the same as m_BaseKeyboardUsesAltGr -- we are turning - // the simulation back on after (possibly) turning it off, for a - // consistent experience. TODO: determine if m_BaseKeyboardUseAltGr - // is still needed given we always use kbdus as base for Keyman 10+ - BOOL bSimulateAltGr = Globals::get_SimulateAltGr(); - - // We only want to translate RALT and RALT+SHIFT for Ctrl+Alt rules. - // So we exclude all our other favourite modifier keys. - const UINT RALT_MATCHING_MASK = TF_MOD_CONTROL | TF_MOD_ALT | TF_MOD_LCONTROL | TF_MOD_RCONTROL | TF_MOD_LALT | TF_MOD_RALT; - - for(i = 0; i < pKeyboard->cxGroupArray; i++) - { - if(pKeyboard->dpGroupArray[i].fUsingKeys) - { - cKeys += pKeyboard->dpGroupArray[i].cxKeyArray; - } - } - - if(cKeys == 0) - { - return FALSE; - } - - if (bSimulateAltGr) - { - // We might need twice as many preserved keys to map both LCtrl+LAlt+x and RAlt+x - cKeys *= 2; - } - - if(pPreservedKeys == NULL) - { - *cPreservedKeys = cKeys; - return TRUE; - } - - if(*cPreservedKeys < cKeys) - { - return FALSE; - } - - PreservedKey *pKeys = *pPreservedKeys; - - for(n = i = 0; i < pKeyboard->cxGroupArray; i++) - { - pGroup = &pKeyboard->dpGroupArray[i]; - if(pGroup->fUsingKeys) - { - for(j = 0; j < pGroup->cxKeyArray; j++) - { - // If we have a key rule for the key, we should preserve it - if(MapKeyRule(&pGroup->dpKeyArray[j], &pKeys[n].key)) - { - // Don't attempt to add the same preserved key twice. Bad things happen - if(!IsMatchingKey(&pKeys[n], pKeys, n)) - { - CoCreateGuid(&pKeys[n].guid); - n++; - - if (bSimulateAltGr && (pKeys[n-1].key.uModifiers & RALT_MATCHING_MASK) == TF_MOD_RALT) - { - // Do this for RALT and RALT+SHIFT only, so we've tested against that mask - // Copy the key and fix modifiers - pKeys[n].key = pKeys[n - 1].key; - pKeys[n].key.uModifiers = (pKeys[n].key.uModifiers & ~TF_MOD_RALT) | TF_MOD_LCONTROL | TF_MOD_LALT; - CoCreateGuid(&pKeys[n].guid); - n++; - } - } - } - } - } - } - - *cPreservedKeys = n; // return actual count of allocated keys, usually smaller than allocated count - return TRUE; -} - BOOL -PreservedKeyMap::MapKeyboardCore(km_kbp_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys) { +PreservedKeyMap::MapKeyboard(km_kbp_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys) { size_t cKeys = 0, cRules = 0, n = 0; DWORD i; @@ -422,7 +297,7 @@ PreservedKeyMap::MapKeyboardCore(km_kbp_keyboard *pKeyboard, PreservedKey **pPre for (i = 0; i < cRules; i++) { // If we have a key rule for the key, we should preserve it - if (MapKeyRuleCore(&kb_key_list[i], &pKeys[n].key)) { + if (MapKeyRule(&kb_key_list[i], &pKeys[n].key)) { // Don't attempt to add the same preserved key twice. Bad things happen if (!IsMatchingKey(&pKeys[n], pKeys, n)) { CoCreateGuid(&pKeys[n].guid); @@ -457,23 +332,14 @@ extern "C" __declspec(dllexport) BOOL WINAPI GetKeyboardPreservedKeys(PreservedK if (!_td) { return FALSE; } + if (!_td->lpActiveKeyboard) { return FALSE; } - // It could be an active core keyboard - if (Globals::get_CoreIntegration()) { - if (!_td->lpActiveKeyboard->lpCoreKeyboard) { - return FALSE; - } - // use api to get key rules - return pkm.MapKeyboardCore(_td->lpActiveKeyboard->lpCoreKeyboard, pPreservedKeys, cPreservedKeys); - - } else { // TODO: 5442 Remove else - if (!_td->lpActiveKeyboard->Keyboard) { - return FALSE; - } - return pkm.MapKeyboard(_td->lpActiveKeyboard->Keyboard, pPreservedKeys, cPreservedKeys); + if (!_td->lpActiveKeyboard->lpCoreKeyboard) { + return FALSE; } - + // use api to get key rules + return pkm.MapKeyboard(_td->lpActiveKeyboard->lpCoreKeyboard, pPreservedKeys, cPreservedKeys); } diff --git a/windows/src/engine/keyman32/selectkeyboard.cpp b/windows/src/engine/keyman32/selectkeyboard.cpp index ae6f366f3fa39be1181908fed1d3097f2a035816..0ca9e2e65f77ae37750053cefaac2c35549da108 100644 --- a/windows/src/engine/keyman32/selectkeyboard.cpp +++ b/windows/src/engine/keyman32/selectkeyboard.cpp @@ -53,7 +53,7 @@ // I3594 // I4220 -BOOL SelectKeyboardCore(DWORD KeymanID) +BOOL SelectKeyboard(DWORD KeymanID) { int i; HWND hwnd = GetFocus(); @@ -71,14 +71,9 @@ BOOL SelectKeyboardCore(DWORD KeymanID) __try { - if (_td->ForceFileName[0]) - { - SendDebugMessageFormat(hwnd, sdmGlobal, 0, "SelectKeyboard: Ignored due to ForceFile"); - return FALSE; // Keyboard file is force-loaded - } KMHideIM(); - + if (_td->lpActiveKeyboard) DeactivateDLLs(_td->lpActiveKeyboard); _td->lpActiveKeyboard = NULL; _td->ActiveKeymanID = KEYMANID_NONKEYMAN; @@ -101,12 +96,13 @@ BOOL SelectKeyboardCore(DWORD KeymanID) SendDebugMessageFormat(hwnd, sdmGlobal, 0, "SelectKeyboardCore: NewKeymanID: %x", _td->ActiveKeymanID); if (_td->app) _td->app->ResetContext(); - ResetCapsLock(); + + // TODO: #5822 tell the core with km_kbp_event so it can reset the capslock state SelectApplicationIntegration(); // I4287 if (_td->app && !_td->app->IsWindowHandled(hwnd)) _td->app->HandleWindow(hwnd); _td->state.windowunicode = !_td->app || _td->app->IsUnicode(); - + ActivateDLLs(_td->lpActiveKeyboard); return TRUE; @@ -130,88 +126,6 @@ BOOL SelectKeyboardCore(DWORD KeymanID) return TRUE; } -BOOL SelectKeyboard(DWORD KeymanID) -{ - if (Globals::get_CoreIntegration()) - { - return SelectKeyboardCore(KeymanID); - } - int i; - HWND hwnd = GetFocus(); - - PKEYMAN64THREADDATA _td = ThreadGlobals(); - if(!_td) return FALSE; - - SendDebugMessageFormat(hwnd,sdmGlobal,0,"ENTER SelectKeyboard-------------------------------------------"); - SendDebugMessageFormat(hwnd,sdmGlobal,0,"ENTER SelectKeyboard: Current:(HKL=%x KeymanID=%x %s) New:(ID=%x)", //lpActiveKeyboard=%s ActiveKeymanID: %x sk: %x KeymanID: %d", - GetKeyboardLayout(0), - _td->ActiveKeymanID, - _td->lpActiveKeyboard == NULL ? "NULL" : _td->lpActiveKeyboard->Name, - //_td->NextKeyboardLayout, - KeymanID); - - __try - { - if(_td->ForceFileName[0]) - { - SendDebugMessageFormat(hwnd,sdmGlobal,0,"SelectKeyboard: Ignored due to ForceFile"); - return FALSE; // Keyboard file is force-loaded - } - - KMHideIM(); - - if(_td->lpActiveKeyboard) DeactivateDLLs(_td->lpActiveKeyboard); - _td->lpActiveKeyboard = NULL; - _td->ActiveKeymanID = KEYMANID_NONKEYMAN; - - //SendDebugMessageFormat(hwnd,sdmGlobal,0,"SelectKeyboard: nKeyboards=%d", nKeyboards); - - for(i = 0; i < _td->nKeyboards; i++) - { - if(_td->lpKeyboards[i].KeymanID == KeymanID) - { - if(!_td->lpKeyboards[i].Keyboard && !LoadlpKeyboard(i)) - { - SendDebugMessageFormat(hwnd,sdmGlobal,0,"SelectKeyboard: Unable to load"); - return TRUE; - } - - _td->lpActiveKeyboard = &_td->lpKeyboards[i]; - _td->ActiveKeymanID = _td->lpActiveKeyboard->KeymanID; - - SendDebugMessageFormat(hwnd,sdmGlobal,0,"SelectKeyboard: NewKeymanID: %x", _td->ActiveKeymanID); - - if(_td->app) _td->app->ResetContext(); - ResetCapsLock(); - - SelectApplicationIntegration(); // I4287 - if(_td->app && !_td->app->IsWindowHandled(hwnd)) _td->app->HandleWindow(hwnd); - _td->state.windowunicode = !_td->app || _td->app->IsUnicode(); - - ActivateDLLs(_td->lpActiveKeyboard); - - return TRUE; - } - } - - if(IsFocusedThread()) - { - SendDebugMessageFormat(hwnd,sdmGlobal,0,"SelectKeyboard: Keyboard Not Found"); - } - } - __finally - { - SendDebugMessageFormat(hwnd,sdmGlobal,0,"EXIT SelectKeyboard: Current:(HKL=%x KeymanID=%x %s) New:(ID=%x)", //lpActiveKeyboard=%s ActiveKeymanID: %x sk: %x KeymanID: %d", - GetKeyboardLayout(0), - _td->ActiveKeymanID, - _td->lpActiveKeyboard == NULL ? "NULL" : _td->lpActiveKeyboard->Name, - KeymanID); - SendDebugMessageFormat(hwnd,sdmGlobal,0,"EXIT SelectKeyboard-------------------------------------------"); - } - return TRUE; -} - - BOOL SelectKeyboardTSF(DWORD dwIdentity, BOOL foreground) // I3933 // I3949 // I4271 { if (!foreground && IsFocusedThread()) { diff --git a/windows/src/engine/keyman32/tests/keyman-engine-tests/keyboardoptionstests.cpp b/windows/src/engine/keyman32/tests/keyman-engine-tests/keyboardoptionstests.cpp index 4a16621ee881fcec9fe8aa592edc8d51b5739c3a..795f89fe447c7029fcafc802afaeb648f6aac7be 100644 --- a/windows/src/engine/keyman32/tests/keyman-engine-tests/keyboardoptionstests.cpp +++ b/windows/src/engine/keyman32/tests/keyman-engine-tests/keyboardoptionstests.cpp @@ -5,77 +5,6 @@ #include #include -// Test UpdateKeyboardOptionsCore, also uses SaveKeyboardOptionsCore -TEST(KEYBOARDOPTIONS, UpdateKeyboardOptionsCore) { - LPINTKEYBOARDINFO kp = new INTKEYBOARDINFO; - memset(kp, 0, sizeof(INTKEYBOARDINFO)); - - km_kbp_option_item test_env_opts[] = { - {u"__test_point", u"not tiggered", KM_KBP_OPT_KEYBOARD}, {u"hello", u"-", KM_KBP_OPT_ENVIRONMENT}, KM_KBP_OPTIONS_END}; - - km_kbp_path_name dummyPath = L"dummyActions.mock"; - EXPECT_EQ(km_kbp_keyboard_load(dummyPath, &kp->lpCoreKeyboard), KM_KBP_STATUS_OK); - EXPECT_EQ(km_kbp_state_create(kp->lpCoreKeyboard, test_env_opts, &kp->lpCoreKeyboardState), KM_KBP_STATUS_OK); - - kp->lpCoreKeyboardOptions = SaveKeyboardOptionsCore(kp); - // No Change - EXPECT_FALSE(UpdateKeyboardOptionsCore(kp->lpCoreKeyboardState, kp->lpCoreKeyboardOptions)); - std::u16string value = kp->lpCoreKeyboardOptions[0].value; - std::u16string expectedValue = u"not tiggered"; - EXPECT_TRUE(value == expectedValue); - - km_kbp_option_item update_key_opts[] = {{u"__test_point", u"triggered", KM_KBP_OPT_KEYBOARD}, KM_KBP_OPTIONS_END}; - EXPECT_EQ(km_kbp_state_options_update(kp->lpCoreKeyboardState, update_key_opts), KM_KBP_STATUS_OK); - // Change value to triggered - EXPECT_TRUE(UpdateKeyboardOptionsCore(kp->lpCoreKeyboardState, kp->lpCoreKeyboardOptions)); - value = kp->lpCoreKeyboardOptions[0].value; - expectedValue = u"triggered"; - EXPECT_TRUE(value == expectedValue); - DisposeKeyboardOptionsCore(&kp->lpCoreKeyboardOptions); - ReleaseStateMemoryCore(&kp->lpCoreKeyboardState); - ReleaseKeyboardMemoryCore(&kp->lpCoreKeyboard); - delete kp; - -} - -// Test SaveKeyboardOptionsCore and RestoreKeyboardOptionsCORE -TEST(KEYBOARDOPTIONS, SaveRestoreKeyboardOptionsCore) { - - LPINTKEYBOARDINFO kp = new INTKEYBOARDINFO; - memset(kp, 0, sizeof(INTKEYBOARDINFO)); - - km_kbp_option_item test_env_opts[] = { - {u"__test_point", u"not tiggered", KM_KBP_OPT_KEYBOARD}, {u"hello", u"-", KM_KBP_OPT_ENVIRONMENT}, KM_KBP_OPTIONS_END}; - km_kbp_path_name dummyPath = L"dummyActions.mock"; - EXPECT_EQ(km_kbp_keyboard_load(dummyPath, &kp->lpCoreKeyboard), KM_KBP_STATUS_OK); - EXPECT_EQ(km_kbp_state_create(kp->lpCoreKeyboard, test_env_opts, &kp->lpCoreKeyboardState), KM_KBP_STATUS_OK); - - km_kbp_option_item *SavedKBDOptions = SaveKeyboardOptionsCore(kp); - - std::u16string value = SavedKBDOptions[0].value; - std::u16string expectedValue = u"not tiggered"; - km_kbp_option_item update_key_opts[] = {{u"__test_point", u"triggered", KM_KBP_OPT_KEYBOARD}, KM_KBP_OPTIONS_END}; - EXPECT_EQ(km_kbp_state_options_update(kp->lpCoreKeyboardState, update_key_opts), KM_KBP_STATUS_OK); - - km_kbp_option_item *NewKBDOptions = SaveKeyboardOptionsCore(kp); - value = NewKBDOptions[0].value; - expectedValue = u"triggered"; - EXPECT_TRUE(value == expectedValue); - km_kbp_cp const *retValue = nullptr; - EXPECT_TRUE(RestoreKeyboardOptionsCore(kp->lpCoreKeyboardState, SavedKBDOptions)); - EXPECT_EQ(km_kbp_state_option_lookup(kp->lpCoreKeyboardState, KM_KBP_OPT_KEYBOARD, test_env_opts[0].key, &retValue), KM_KBP_STATUS_OK); - - value = retValue; - expectedValue = u"not tiggered"; - EXPECT_TRUE(value == expectedValue); - - DisposeKeyboardOptionsCore(&NewKBDOptions); - DisposeKeyboardOptionsCore(&SavedKBDOptions); - - ReleaseStateMemoryCore(&kp->lpCoreKeyboardState); - ReleaseKeyboardMemoryCore(&kp->lpCoreKeyboard); - delete kp; -} // Test SetupCoreEnvironment and also test km_kbp_state_options_update TEST(KEYBOARDOPTIONS, SetupCoreEnvironment) { diff --git a/windows/src/engine/keyman64/keyman64.vcxproj b/windows/src/engine/keyman64/keyman64.vcxproj index d70c0872eddcfa111f582c87c43c021ccb7c9c63..15546caef5b0022e0d61e337ed041c3e481ab053 100644 --- a/windows/src/engine/keyman64/keyman64.vcxproj +++ b/windows/src/engine/keyman64/keyman64.vcxproj @@ -177,7 +177,6 @@ - @@ -279,7 +278,6 @@ - diff --git a/windows/src/engine/keyman64/keyman64.vcxproj.filters b/windows/src/engine/keyman64/keyman64.vcxproj.filters index 0b53bd96cbaf397e58d5a3bc208a73496baabbd9..cd8aeefb322143e5ecef53a8b55e11e15c3d6c88 100644 --- a/windows/src/engine/keyman64/keyman64.vcxproj.filters +++ b/windows/src/engine/keyman64/keyman64.vcxproj.filters @@ -2,7 +2,6 @@ - @@ -41,7 +40,6 @@ - diff --git a/windows/src/engine/kmtip/keys.cpp b/windows/src/engine/kmtip/keys.cpp index d5d4882ca586f84ed8c590b727b4cc0159f6bfa1..9f1552f4e30e4883a347ac8426c84720624b5bdc 100644 --- a/windows/src/engine/kmtip/keys.cpp +++ b/windows/src/engine/kmtip/keys.cpp @@ -362,36 +362,3 @@ STDMETHODIMP CKMTipTextService::OnPreservedKey(ITfContext *pContext, REFGUID rgu *pfEaten = FALSE; return S_OK; } - -BOOL CKMTipTextService::DoRefreshPreservedKeys(BOOL Activating) { - LogEnter(); - ITfKeystrokeMgr *pKeystrokeMgr; - HRESULT hr = S_OK; - - Log(L"CKMTipTextService::DoRefreshPreservedKeys"); - - if (!_pThreadMgr) { - return FALSE; - } - - if (_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr) != S_OK) { - return FALSE; - } - - _UnpreserveAltKeys(pKeystrokeMgr); - if (Activating) { - hr = _PreserveAltKeys(pKeystrokeMgr); // I3588 - } - - pKeystrokeMgr->Release(); - - return (hr == S_OK); -} - -__declspec(dllexport) BOOL WINAPI RefreshPreservedKeys(BOOL Activating) { - LogEnter(); - if (CKMTipTextService::ThreadThis) { - return CKMTipTextService::ThreadThis->DoRefreshPreservedKeys(Activating); - } - return FALSE; -} diff --git a/windows/src/engine/kmtip/kmtip.def b/windows/src/engine/kmtip/kmtip.def index 64c10743c23a56877be1792f37de6210798adb23..854f6b6ca5042a89711519a42e0b9b330eb7a98c 100644 --- a/windows/src/engine/kmtip/kmtip.def +++ b/windows/src/engine/kmtip/kmtip.def @@ -5,5 +5,4 @@ EXPORTS DllCanUnloadNow PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE - RefreshPreservedKeys Keyman_Diagnostic diff --git a/windows/src/engine/kmtip/kmtip.h b/windows/src/engine/kmtip/kmtip.h index aabe098b8b6bb11877d2aeffd95df5fe92b2a601..3ab425891df64c53f02cf7379495a65f57aa3bef 100644 --- a/windows/src/engine/kmtip/kmtip.h +++ b/windows/src/engine/kmtip/kmtip.h @@ -1,18 +1,18 @@ /* Name: kmtip Copyright: Copyright (C) SIL International. - Documentation: + Documentation: Description: CKMTipTextService declaration. Create Date: 7 Sep 2009 Modified Date: 27 Jan 2015 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 07 Sep 2009 - mcdurdin - I2095 - TSF addin is not threadsafe 01 May 2014 - mcdurdin - I4216 - V9.0 - Keyman TIP should use ITfTextInputProcessorEx 16 Jun 2014 - mcdurdin - I4274 - V9.0 - kmtip does not work if already active before KM starts @@ -88,8 +88,6 @@ public: BOOL TIPNotifyActivate(GUID *guidProfile); - BOOL DoRefreshPreservedKeys(BOOL Activating); - static __declspec(thread) CKMTipTextService *ThreadThis; private: @@ -107,7 +105,7 @@ private: void _UninitKeyman(); void _TryAndStartKeyman(); - + HRESULT _UnpreserveAltKeys(ITfKeystrokeMgr *pKeystrokeMgr); // Keyman interfaces diff --git a/windows/src/global/delphi/general/keyman32_int.pas b/windows/src/global/delphi/general/keyman32_int.pas deleted file mode 100644 index decebd7bdb754dc5b4fee74fb73e0602ab9a2d96..0000000000000000000000000000000000000000 --- a/windows/src/global/delphi/general/keyman32_int.pas +++ /dev/null @@ -1,184 +0,0 @@ -(* - Name: keyman32_int - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 1 Aug 2006 - - Modified Date: 17 Aug 2012 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 01 Aug 2006 - mcdurdin - Add GetKeymanInstallPath function - 14 Sep 2006 - mcdurdin - Retrieve Debug_Keyman32 path if assigned - 18 Mar 2011 - mcdurdin - I2825 - Debug_Keyman32 override not working correctly - 03 May 2011 - mcdurdin - I2890 - Record diagnostic data when encountering registry errors - 17 Aug 2012 - mcdurdin - I3310 - V9.0 - Unicode in Delphi fixes -*) -unit keyman32_int; - -interface - -uses Windows, SysUtils, Forms, ErrorControlledRegistry, RegistryKeys; //, util; - -function Keyman_Initialise(Handle: HWND; FSingleApp: Boolean): Boolean; -function Keyman_Exit: Boolean; -function Keyman_ForceKeyboard(const s: string): Boolean; -function Keyman_StopForcingKeyboard: Boolean; - -implementation - -uses DebugPaths, Dialogs, KLog; - -type TKeyman_ForceKeyboard = function (s: PAnsiChar): Boolean; stdcall; // I3310 -type TKeyman_StopForcingKeyboard = function: Boolean; stdcall; -type TKeyman_Initialise = function(h: THandle; FSingleApp: LongBool): Boolean; stdcall; -type TKeyman_Exit = function: Boolean; stdcall; - -var - FInitKeyman: Boolean = False; - FKeyman32Path: string = ''; - -function GetKeymanInstallPath: string; -var - RootPath: string; -begin - RootPath := ''; - with TRegistryErrorControlled.Create do // I2890 - try - RootKey := HKEY_LOCAL_MACHINE; - if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_RootPath) then - RootPath := ReadString(SRegValue_RootPath); - finally - Free; - end; - - RootPath := GetDebugPath('Debug_Keyman32Path', RootPath); // I2825 - - if RootPath = '' then - begin - RootPath := ExtractFilePath(ParamStr(0)); - end; - - Result := IncludeTrailingPathDelimiter(RootPath); - - if not FileExists(Result + 'keyman32.dll') then - raise Exception.Create( 'The executable keyman32.dll could not '+ - 'be found. You should reinstall.'); -end; - -function Keyman_Initialise(Handle: HWND; FSingleApp: Boolean): Boolean; -var - hkeyman: THandle; - FLoad: Boolean; - ki: TKeyman_Initialise; - s: string; -begin - Result := False; - FLoad := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then - begin - s := GetKeymanInstallPath; - hkeyman := LoadLibrary(PChar(s+'keyman32.dll')); - if hkeyman = 0 then - begin - KL.LogError('Keyman_Initialise: Unable to load '+s+'keyman32.dll: '+SysErrorMessage(GetLastError)); - Exit; - end; - KL.Log('Keyman_Initialise: Loaded keyman32.dll'); - FLoad := True; - end - else - KL.Log('Keyman_Initialise: Found keyman32.dll already loaded'); - ki := TKeyman_Initialise(GetProcAddress(hkeyman, 'Keyman_Initialise')); - if not Assigned(@ki) then - begin - KL.LogError('Keyman_Initialise: Unable to find Keyman_Initialise in '+s+'keyman32.dll: '+SysErrorMessage(GetLastError)); - if FLoad then FreeLibrary(hkeyman); - Exit; - end; - if not ki(Handle, FSingleApp) then - begin - KL.LogError('Keyman_Initialise: Call to Keyman_Initialise in '+s+'keyman32.dll failed: '+SysErrorMessage(GetLastError)); - if FLoad then FreeLibrary(hkeyman); - Exit; - end; - - KL.Log('Keyman_Initialise: Loaded successfully'); - FInitKeyman := True; - Result := True; -end; - -function Keyman_Exit: Boolean; -var - hkeyman: THandle; - ke: TKeyman_Exit; -begin - if not FInitKeyman then - begin - Result := True; - Exit; - end; - Result := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then Exit; - ke := TKeyman_Exit(GetProcAddress(hkeyman, 'Keyman_Exit')); - if not Assigned(@ke) then Exit; - if not ke then Exit; - if FInitKeyman then FreeLibrary(hkeyman); - FInitKeyman := False; - Result := True; -end; - -function Keyman_ForceKeyboard(const s: string): Boolean; -var - hkeyman: THandle; - fk: TKeyman_ForceKeyboard; -begin - Result := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then - begin - KL.Log('Keyman_ForceKeyboard: Attempting to load keyman32.dll'); - if not Keyman_Initialise(Application.MainForm.Handle, True) then - begin - KL.LogError('Keyman_ForceKeyboard: Unable to load keyman32.dll'); - Exit; - end; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then Exit; - end; - fk := TKeyman_ForceKeyboard(GetProcAddress(hkeyman, 'Keyman_ForceKeyboard')); - if(Assigned(@fk)) then - begin - Result := fk(PAnsiChar(AnsiString(s))); // todo: k9: unicode // I3310 - if not Result then KL.LogError('Keyman_ForceKeyboard in keyman32.dll failed: '+s) - else KL.Log('Keyman_ForceKeyboard: success'); - end - else - KL.LogError('Keyman_ForceKeyboard: failed to find Keyman_ForceKeyboard in keyman32.dll'); -end; - -function Keyman_StopForcingKeyboard: Boolean; -var - hkeyman: THandle; - sfk: TKeyman_StopForcingKeyboard; -begin - Result := False; - hkeyman := GetModuleHandle('keyman32.dll'); - if hkeyman = 0 then Exit; - sfk := TKeyman_StopForcingKeyboard(GetProcAddress(hkeyman, 'Keyman_StopForcingKeyboard')); - if(Assigned(@sfk)) then - Result := sfk; -end; - -initialization -finalization - Keyman_Exit; -end. - diff --git a/windows/src/support/Makefile b/windows/src/support/Makefile index 7f624d29f41a45544cbd88e8f5f07b8fc82696d2..56609e21e48831b4ab4ce85a28a353e6089537fb 100644 --- a/windows/src/support/Makefile +++ b/windows/src/support/Makefile @@ -3,9 +3,9 @@ # !ifdef NODELPHI -TARGETS=etl2log +TARGETS=etl2log texteditor !else -TARGETS=oskbulkrenderer etl2log +TARGETS=oskbulkrenderer etl2log texteditor !endif CLEANS=clean-support @@ -21,6 +21,10 @@ etl2log: .virtual cd $(ROOT)\src\support\etl2log $(MAKE) $(TARGET) +texteditor: .virtual + cd $(ROOT)\src\support\texteditor + $(MAKE) $(TARGET) + # ---------------------------------------------------------------------- clean-support: diff --git a/windows/src/support/texteditor/Makefile b/windows/src/support/texteditor/Makefile index a9fa1b1cd225a6f04c29a2e23ecedc7b2ac336f7..473bf57598e68dac99e4a5bab9602ee9724802bf 100644 --- a/windows/src/support/texteditor/Makefile +++ b/windows/src/support/texteditor/Makefile @@ -8,9 +8,9 @@ build: $(MSBUILD) editor.vcxproj $(MSBUILD_BUILD) /p:Platform=x86 $(MSBUILD) editor.vcxproj $(MSBUILD_BUILD) /p:Platform=x64 $(COPY) $(WIN32_TARGET_PATH)\editor32.exe $(PROGRAM)\support - $(COPY) $(WIN32_TARGET_PATH)\editor32.pdb $(PROGRAM)\support + $(COPY) $(WIN32_TARGET_PATH)\editor32.pdb $(DEBUGPATH)\support $(COPY) $(X64_TARGET_PATH)\editor64.exe $(PROGRAM)\support - $(COPY) $(X64_TARGET_PATH)\editor64.pdb $(PROGRAM)\support + $(COPY) $(X64_TARGET_PATH)\editor64.pdb $(DEBUGPATH)\support clean: def-clean $(MSBUILD) $(MSBUILD_CLEAN) editor.sln @@ -23,6 +23,12 @@ install: $(COPY) $(PROGRAM)\support\editor32.exe "$(INSTALLPATH_KEYMANENGINE)" $(COPY) $(PROGRAM)\support\editor64.exe "$(INSTALLPATH_KEYMANENGINE)" +wrap-symbols: + $(SYMSTORE) $(PROGRAM)\support\editor32.exe /t keyman-windows + $(SYMSTORE) $(PROGRAM)\support\editor64.exe /t keyman-windows + $(SYMSTORE) $(DEBUGPATH)\support\editor32.pdb /t keyman-windows + $(SYMSTORE) $(DEBUGPATH)\support\editor64.pdb /t keyman-windows + !include ..\..\Target.mak # ----------------------------------------------------------------------