diff --git a/.github/labeler.yml b/.github/labeler.yml index d831af12b66..5250e321935 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -35,9 +35,8 @@ developer/: - developer/** developer/compilers/: - - developer/src/kmcomp/** - - developer/src/kmcmpdll/** - developer/src/kmc/** + - developer/src/kmcmplib/** - developer/src/kmc-*/** developer/ide/: diff --git a/.github/multi-labeler.yml b/.github/multi-labeler.yml index b2e84bec107..88bb0e73ca6 100644 --- a/.github/multi-labeler.yml +++ b/.github/multi-labeler.yml @@ -87,15 +87,3 @@ labels: - label: 'windows/' matcher: title: '\(.*windows.*\):' - - # - # epics -- we will add/remove these as we work on new epics each release - # - - - label: 'epic-ldml' - matcher: - branch: '.*epic-ldml.*' # anywhere in the branch name, e.g. feat/epic-ldml/developer/... or feat/developer/foo-epic-ldml - - - label: 'epic-kmcompx' - matcher: - branch: '.*epic-kmcompx.*' diff --git a/.gitignore b/.gitignore index 429948be84e..ac5be21083d 100644 --- a/.gitignore +++ b/.gitignore @@ -73,7 +73,6 @@ # /developer/TIKE/ /windows/src/developer/TIKE/stock.kct -/windows/src/developer/TIKE/kmcmpdll.dll # /developer/TIKE/redist/ /windows/src/developer/TIKE/redist/Addins @@ -85,12 +84,6 @@ /windows/src/developer/inst/download.mak /windows/src/developer/inst/copydev.mak -# /developer/kmcmpdll/ -/windows/src/developer/kmcmpdll/*.lastbuildstate - -# /developer/kmcmpdll/Debug/ -/windows/src/developer/kmcmpdll/Debug/*.idb - /windows/src/developer/stock/stock.kct /windows/src/developer/uitemplates/keyman.kct @@ -186,10 +179,3 @@ lcov.info /keyman*.changes /keyman*.tar.?z -#Sabine: -# /common/test/keyboards/invalid/source/*.kmx -# /developer/src/test/auto/kmcomp/*.kmn -# /developer/src/test/auto/kmcomp/*.kvk -# /developer/src/test/auto/kmcomp/*.kvk* -# /developer/src/test/auto/kmcomp/*.txt - diff --git a/HISTORY.md b/HISTORY.md index 56e8f96c954..4ff80c05a8b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,69 @@ # Keyman Version History +## 17.0.191 alpha 2023-10-15 + +* chore: update readme for keyboard_info schema (#9746) + +## 17.0.190 alpha 2023-10-12 + +* chore(web): web build streamlining (#9743) +* chore(linux): Add code coverage reports for keyman-config and keyman-system-service (#9753) + +## 17.0.189 alpha 2023-10-11 + +* refactor(linux): Use auto cleanup (and fix some memory leaks) (#9648) +* refactor(linux): Simplify adding keyboard (#9734) + +## 17.0.188 alpha 2023-10-11 + +* chore(android,windows): Update crowdin strings for Kannada (#9737) + +## 17.0.187 alpha 2023-10-11 + +* chore(developer): convert Server to ES Modules (#9673) +* chore(common): convert hextobin to ES Modules (#9676) +* chore(common): convert resources/build/version to ES Modules (#9678) +* chore(common): keyman-version now generates only es module (#9680) +* chore(common): cleanup final Typescript non-ESM metadata (#9681) +* chore(ios): renew certificate (#9697) +* (#9687) +* feat(developer): ldml fix all remaining TODOs around markers and variables (#9688) +* fix(windows): re-enable signature check (#9695) +* fix(common): fix schema fixer (#9727) +* chore(core): rename keyboardprocessor.h to keyman_core_api.h (#9723) +* chore: rename km_kbp_ to km_core_ (#9724) +* chore(developer): fixup signcode paths for server (#9730) +* fix(linux): Explicitly initialize GTK (#9706) +* chore(linux): Improve Sentry reports (#9725) +* epic(developer): refactor package-metadata for 17.0 (#9485) +* chore: clean up a few minor discrepancies in builder.inc.sh (#9731) +* fix: path for ESM increment-version, and exit code (#9738) + +## 17.0.186 alpha 2023-10-04 + +* feat(developer): show an INFO message when warnings have failed a build (#9652) +* chore(developer): reduce duplicate words warning to hint (#9653) +* feat(developer): issue hint if package includes keyboard source files (#9658) +* chore(developer): switch on code coverage reporting for kmc (#9662) +* fix(core): clean cached ICU in core (#9668) +* feat(developer): warn if .kps includes a .js which is not touch-capable (#9667) + +## 17.0.185 alpha 2023-10-03 + +* chore(web): Add non-printing characters to the OSK (#9547) +* feat(developer): support `store(&version) '17.0'` (#9656) +* chore(developer): add test for `checkFilenameConventions == false` or unset (#9661) +* feat(developer): ldml scan codes support (#9615) + +## 17.0.184 alpha 2023-10-02 + +* fix(web): fixes toolbar refocus timing after a keyboard change (#9618) + +## 17.0.183 alpha 2023-09-29 + +* feat(web): browser-KMW support for default subkeys (#9496) +* refactor(linux): Add more tests for `keymanutil.c` ️ (#9595) + ## 17.0.182 alpha 2023-09-28 * chore(web): builds that output to web/build/publish should also clean it (#9613) diff --git a/VERSION.md b/VERSION.md index 8bf382e6d5e..5e11964caa1 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -17.0.183 \ No newline at end of file +17.0.192 \ No newline at end of file diff --git a/android/KMAPro/kMAPro/src/main/res/values-kn-rIN/strings.xml b/android/KMAPro/kMAPro/src/main/res/values-kn-rIN/strings.xml index 21ec6f216bb..3d37123cfbb 100644 --- a/android/KMAPro/kMAPro/src/main/res/values-kn-rIN/strings.xml +++ b/android/KMAPro/kMAPro/src/main/res/values-kn-rIN/strings.xml @@ -26,6 +26,7 @@ ಇಲ್ಲಿ ಟೈಪ್ ಮಾಡಲು ಪ್ರಾರಂಭಿಸಿ… + ಅಕ್ಷರದ ಗಾತ್ರ: %1$d @@ -33,6 +34,8 @@ ಅಕ್ಷರದ ಗಾತ್ರ ಹೆಚ್ಚಿಸಿ ಅಕ್ಷರದ ಗಾತ್ರ ತಗ್ಗಿಸಿ + + ಅಕ್ಷರ ಗಾತ್ರದ ಸ್ಲೈಡರ್ \nಎಲ್ಲಾ ಪಠ್ಯವನ್ನು ತೆರವುಗೊಳಿಸಲಾಗುತ್ತದೆ\n diff --git a/android/Tests/KeyboardHarness/app/src/main/assets/keyboardharness.kmp b/android/Tests/KeyboardHarness/app/src/main/assets/keyboardharness.kmp index 3a8f1cb624c..30a4f87663b 100644 Binary files a/android/Tests/KeyboardHarness/app/src/main/assets/keyboardharness.kmp and b/android/Tests/KeyboardHarness/app/src/main/assets/keyboardharness.kmp differ diff --git a/android/Tests/KeyboardHarness/app/src/main/assets/test9469.kmp b/android/Tests/KeyboardHarness/app/src/main/assets/test9469.kmp index 100cc1d70b6..8390d5dee31 100644 Binary files a/android/Tests/KeyboardHarness/app/src/main/assets/test9469.kmp and b/android/Tests/KeyboardHarness/app/src/main/assets/test9469.kmp differ diff --git a/android/Tests/KeyboardHarness/readme.md b/android/Tests/KeyboardHarness/readme.md index 1e00343bf55..898d9926382 100644 --- a/android/Tests/KeyboardHarness/readme.md +++ b/android/Tests/KeyboardHarness/readme.md @@ -10,6 +10,8 @@ and consists of these engineering keyboards: * web/testing/chirality/chirality.js * web/testing/platform/platformtest.js +test9469.kmp is another engineering keyboard to show non-printing characters on the OSK. + ### Compiling From Command Line 1. Launch a command prompt to the `android/` folder 2. Compile KMEA. This will build and copy `keyman-engine.aar` to the Samples and Test projects diff --git a/common/include/km_types.h b/common/include/km_types.h index d81f2672384..aa7fb2e5182 100644 --- a/common/include/km_types.h +++ b/common/include/km_types.h @@ -28,14 +28,14 @@ typedef uint8_t KMX_BYTE; typedef uint16_t KMX_WORD; #if defined(__cplusplus) -typedef char16_t km_kbp_cp; -typedef char32_t km_kbp_usv; +typedef char16_t km_core_cp; +typedef char32_t km_core_usv; #else -typedef uint16_t km_kbp_cp; // code point -typedef uint32_t km_kbp_usv; // Unicode Scalar Value +typedef uint16_t km_core_cp; // code point +typedef uint32_t km_core_usv; // Unicode Scalar Value #endif -typedef km_kbp_cp KMX_WCHAR; // wc, 16-bit UNICODE character +typedef km_core_cp KMX_WCHAR; // wc, 16-bit UNICODE character typedef KMX_WCHAR* PKMX_WCHAR; typedef char KMX_CHAR; @@ -60,7 +60,7 @@ typedef KMX_DWORD* PKMX_DWORD; #ifdef USE_CHAR16_T #define lpuch(x) u ## x -typedef km_kbp_cp KMX_UCHAR; +typedef km_core_cp KMX_UCHAR; #else #define lpuch(x) L ## x typedef wchar_t KMX_UCHAR; diff --git a/common/include/kmx_file.h b/common/include/kmx_file.h index b689ba685ad..243e5e0d638 100644 --- a/common/include/kmx_file.h +++ b/common/include/kmx_file.h @@ -68,9 +68,10 @@ namespace kmx { #define VERSION_150 0x00000F00 #define VERSION_160 0x00001000 +#define VERSION_170 0x00001100 #define VERSION_MIN VERSION_50 -#define VERSION_MAX VERSION_160 +#define VERSION_MAX VERSION_170 // // Backspace types diff --git a/common/include/test_assert.h b/common/include/test_assert.h index 655074ba8b2..8cc0f3b624d 100644 --- a/common/include/test_assert.h +++ b/common/include/test_assert.h @@ -30,7 +30,7 @@ #endif #define try_status(expr) { \ auto __s = (expr); \ - if (__s != KM_KBP_STATUS_OK) { \ + if (__s != KM_CORE_STATUS_OK) { \ _assert_failed(__s, u ## #expr); \ } \ } diff --git a/common/models/templates/package.json b/common/models/templates/package.json index 0bec0fc88fa..33f89cca706 100644 --- a/common/models/templates/package.json +++ b/common/models/templates/package.json @@ -21,9 +21,7 @@ "exports": { ".": "./build/obj/index.js", "./lib": { - "types": "./build/lib/index.d.ts", - "import": "./build/lib/index.mjs", - "require": "./build/lib/index.cjs" + "types": "./build/lib/index.d.ts" }, "./obj/*.js": "./build/obj/*.js" }, diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json index bd8d0157fbd..7a03dc9e8fb 100644 --- a/common/models/wordbreakers/package.json +++ b/common/models/wordbreakers/package.json @@ -19,8 +19,7 @@ "exports": { ".": "./build/obj/index.js", "./lib": { - "import": "./build/lib/index.mjs", - "require": "./build/lib/index.cjs" + "types": "./build/lib/index.d.ts" }, "./obj/*.js": "./build/obj/*.js" }, diff --git a/common/predictive-text/testing/index.html b/common/predictive-text/testing/index.html deleted file mode 100644 index 8a00094b701..00000000000 --- a/common/predictive-text/testing/index.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - LMLayer Testing - - - -

Language-Modeling Layer module testing

-

A simple basic WebWorker.

Designed as a baseline functionality test we can run against various platforms as a first-stage canary of sorts. -

An embedded WebWorker prototype.

A prototype for directly embedding a WebWorker's code within a "master" script. -

A better embedded WebWorker prototype.

A prototype for two-stage compilation of a master/slave main-script/WebWorker pair. -

Sending code via a Blob URI

A prototype for sending code over to a WebWorker via a Blob URI reference. - - diff --git a/common/predictive-text/testing/one-stage-embedded-webworker/.gitignore b/common/predictive-text/testing/one-stage-embedded-webworker/.gitignore deleted file mode 100644 index df6361deda9..00000000000 --- a/common/predictive-text/testing/one-stage-embedded-webworker/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -main.js -main.js.map \ No newline at end of file diff --git a/common/predictive-text/testing/one-stage-embedded-webworker/build.sh b/common/predictive-text/testing/one-stage-embedded-webworker/build.sh deleted file mode 100755 index f1f727b8c03..00000000000 --- a/common/predictive-text/testing/one-stage-embedded-webworker/build.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -# -# Compiles the Language Modeling Layer for common use in predictive text and autocorrective applications. -# Designed for optimal compatibility with the Keyman Suite. -# - -## START STANDARD BUILD SCRIPT INCLUDE -# adjust relative paths as necessary -THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/build-utils.sh" -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" -## END STANDARD BUILD SCRIPT INCLUDE - -display_usage ( ) { - echo "build.sh [-clean]" - echo - echo " -clean to erase pre-existing build products before a re-build" -} - -SOURCE="testing/one-stage-embedded-webworker" - -echo "Node.js + dependencies check" -npm install --no-optional - -if [ $? -ne 0 ]; then - builder_die "Build environment setup error detected! Please ensure Node.js is installed!" -fi - -# A nice, extensible method for -clean operations. Add to this as necessary. -clean ( ) { - rm -rf "./*.js" - if [ $? -ne 0 ]; then - builder_die "Failed to erase the prior build." - fi -} - -# Process command-line arguments -while [[ $# -gt 0 ]] ; do - key="$1" - case $key in - -clean) - clean - ;; - esac - shift # past the processed argument -done - -npm run tsc -- -p $SOURCE/tsconfig.json - -if [ $? -ne 0 ]; then - builder_die "Compilation failed." -fi - -echo "Typescript compilation successful." \ No newline at end of file diff --git a/common/predictive-text/testing/one-stage-embedded-webworker/index.html b/common/predictive-text/testing/one-stage-embedded-webworker/index.html deleted file mode 100644 index 244f4aa2833..00000000000 --- a/common/predictive-text/testing/one-stage-embedded-webworker/index.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - Embedded WebWorker test - - - - - - - - - -

LM Layer Testing - One-Stage Embedded WebWorkers

-

This page serves as a prototype for embedding a WebWorker's code within its "master" script - across the TypeScript transpilation boundary.

-
- - - -

Return to testing home page

- - diff --git a/common/predictive-text/testing/one-stage-embedded-webworker/main.ts b/common/predictive-text/testing/one-stage-embedded-webworker/main.ts deleted file mode 100644 index 5d15ae88b58..00000000000 --- a/common/predictive-text/testing/one-stage-embedded-webworker/main.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Useful for passing class constructors -type Workable = { - new (...args: any[]): T; - // Requires a static implementation. - onmessage(e: any): void; -}; - -class A { - a(x: number, y: number):number { - return x + y; - } -} - -var WorkerGlobals = { - counter: 0, - a: A -} - -class WorkerCore { - static WorkerGlobals = WorkerGlobals; - //static counter: number; - - static onmessage(e: any) { - WorkerGlobals.counter++; - - console.log("Message received from main page: ", e.data); - - // Forces TypeScript to interpret this line as plain JavaScript, as it uses a non-Worker definition. - // @ts-ignore - postMessage(WorkerGlobals.counter); - } -} - -function createWorkerFromClasses(globals: object, fn: Workable): Worker { - var sep = ";\n"; - let glb = "var WorkerGlobals = " + JSON.stringify(globals); - let wc = "var onmessage = " + fn.onmessage.toString(); - var blob = new Blob([glb, sep, wc], { type: 'text/javascript' }); - let url = URL.createObjectURL(blob); - - return new Worker(url); -} - -var canaryWorker = createWorkerFromClasses(WorkerGlobals, WorkerCore); - -canaryWorker.onmessage = function(e) { - var counter = e.data; // Number of times the WebWorker has been messaged. - console.log("Received message from the WebWorker: " + e.data); - - var txtFeedback = document.getElementById("txtFeedback"); - txtFeedback.value = counter + " click(s)"; -} diff --git a/common/predictive-text/testing/one-stage-embedded-webworker/tsconfig.json b/common/predictive-text/testing/one-stage-embedded-webworker/tsconfig.json deleted file mode 100644 index f1e9be86003..00000000000 --- a/common/predictive-text/testing/one-stage-embedded-webworker/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "allowJs": false, - "module": "none", - "outDir": "./", - "inlineSources": true, - "sourceMap": true, - "target": "es5" - }, - "files" : [ - "main.ts" - ] -} diff --git a/common/predictive-text/testing/post-blob-webworker/canaryWorker.js b/common/predictive-text/testing/post-blob-webworker/canaryWorker.js deleted file mode 100644 index 5c69deace8e..00000000000 --- a/common/predictive-text/testing/post-blob-webworker/canaryWorker.js +++ /dev/null @@ -1,5 +0,0 @@ -// receive a message and execute its uri -onmessage = function (e) { - let uri = e.data.uri; - importScripts(uri); -}; diff --git a/common/predictive-text/testing/post-blob-webworker/index.html b/common/predictive-text/testing/post-blob-webworker/index.html deleted file mode 100644 index a04b6be377b..00000000000 --- a/common/predictive-text/testing/post-blob-webworker/index.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - Blob URI test - - - - - - - - - -

LM Layer Testing - Sending a function to a WebWorker via a Blob URI test

-

This page sends a function over to the Worker via a Blob URI, and - expects the function to reply back. -

-
- - - -

Return to testing home page

- - diff --git a/common/predictive-text/testing/simple-webworker/canaryWorker.js b/common/predictive-text/testing/simple-webworker/canaryWorker.js deleted file mode 100644 index cf8674d3304..00000000000 --- a/common/predictive-text/testing/simple-webworker/canaryWorker.js +++ /dev/null @@ -1,9 +0,0 @@ -var counter = 0; - -onmessage = function(e) { - counter++; - - console.log("Message received from main page: ", e.data); - - postMessage(counter); -} \ No newline at end of file diff --git a/common/predictive-text/testing/simple-webworker/index.html b/common/predictive-text/testing/simple-webworker/index.html deleted file mode 100644 index adbcd30d3e6..00000000000 --- a/common/predictive-text/testing/simple-webworker/index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - Basic WebWorker test - - - - - - - - - -

LM Layer Testing - basic WebWorker canary

-

This page uses a simple WebWorker useful for ensuring functionality on various platforms.

-
- - - -

Return to testing home page

- - diff --git a/common/predictive-text/testing/two-stage-embedded-webworker/.gitignore b/common/predictive-text/testing/two-stage-embedded-webworker/.gitignore deleted file mode 100644 index 20efd1ec834..00000000000 --- a/common/predictive-text/testing/two-stage-embedded-webworker/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -**/*.js -**/*.js.map \ No newline at end of file diff --git a/common/predictive-text/testing/two-stage-embedded-webworker/build.sh b/common/predictive-text/testing/two-stage-embedded-webworker/build.sh deleted file mode 100755 index dd1854ec00b..00000000000 --- a/common/predictive-text/testing/two-stage-embedded-webworker/build.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash -# -# Compiles the Language Modeling Layer for common use in predictive text and autocorrective applications. -# Designed for optimal compatibility with the Keyman Suite. -# - -## START STANDARD BUILD SCRIPT INCLUDE -# adjust relative paths as necessary -THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" -. "${THIS_SCRIPT%/*}/../../../../resources/build/build-utils.sh" -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" -## END STANDARD BUILD SCRIPT INCLUDE - -display_usage ( ) { - echo "build.sh [-clean]" - echo - echo " -clean to erase pre-existing build products before a re-build" -} - -SOURCE="testing/two-stage-embedded-webworker" - -COMPILED_WORKER="worker.js" -EMBEDDED_WORKER="embedded_worker.js" - -echo "Node.js + dependencies check" -npm install --no-optional - -if [ $? -ne 0 ]; then - builder_die "Build environment setup error detected! Please ensure Node.js is installed!" -fi - -# A nice, extensible method for -clean operations. Add to this as necessary. -clean ( ) { - rm -rf "./*.js" - if [ $? -ne 0 ]; then - builder_die "Failed to erase the prior build." - fi -} - -# Process command-line arguments -while [[ $# -gt 0 ]] ; do - key="$1" - case $key in - -clean) - clean - ;; - esac - shift # past the processed argument -done - -npm run tsc -- -p $SOURCE/worker/tsconfig.json -if [ $? -ne 0 ]; then - builder_die "Worker compilation failed." -fi - -rm $EMBEDDED_WORKER &> /dev/null - -echo "var LMLayerWorker = function() {" >> $EMBEDDED_WORKER -cat $COMPILED_WORKER >> $EMBEDDED_WORKER -echo "" >> $EMBEDDED_WORKER -echo "}" >> $EMBEDDED_WORKER - -npm run tsc -- -p $SOURCE/tsconfig.json -if [ $? -ne 0 ]; then - builder_die "Final compilation failed." -fi - -echo "Typescript compilation successful." \ No newline at end of file diff --git a/common/predictive-text/testing/two-stage-embedded-webworker/index.html b/common/predictive-text/testing/two-stage-embedded-webworker/index.html deleted file mode 100644 index 244f4aa2833..00000000000 --- a/common/predictive-text/testing/two-stage-embedded-webworker/index.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - Embedded WebWorker test - - - - - - - - - -

LM Layer Testing - One-Stage Embedded WebWorkers

-

This page serves as a prototype for embedding a WebWorker's code within its "master" script - across the TypeScript transpilation boundary.

-
- - - -

Return to testing home page

- - diff --git a/common/predictive-text/testing/two-stage-embedded-webworker/main.ts b/common/predictive-text/testing/two-stage-embedded-webworker/main.ts deleted file mode 100644 index 9eb276d3794..00000000000 --- a/common/predictive-text/testing/two-stage-embedded-webworker/main.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Provides the final source for our compiled WebWorker within a wrapping function. -/// - -function createWorker(fn: Function): Worker { - var str_fn = fn.toString(); - - // We now unwrap our WebWorker from its function. - var str_lines = str_fn.split("\n"); - var blob_lines = [] - - for(var i=1; i < str_lines.length -1; i++) { - blob_lines.push(str_lines[i] + "\n"); - } - // Unwrapping complete. - - var blob = new Blob(blob_lines, { type: 'text/javascript' }); - let url = URL.createObjectURL(blob); - - return new Worker(url); -} - -var canaryWorker = createWorker(LMLayerWorker); - -canaryWorker.onmessage = function(e) { - var counter = e.data; // Number of times the WebWorker has been messaged. - console.log("Received message from the WebWorker: " + e.data); - - var txtFeedback = document.getElementById("txtFeedback"); - txtFeedback.value = counter + " click(s)"; -} diff --git a/common/predictive-text/testing/two-stage-embedded-webworker/tsconfig.json b/common/predictive-text/testing/two-stage-embedded-webworker/tsconfig.json deleted file mode 100644 index a25930a70e6..00000000000 --- a/common/predictive-text/testing/two-stage-embedded-webworker/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "module": "none", - "outFile": "./main.js", - "inlineSources": true, - "inlineSourceMap": true, - "target": "es5" - }, - "files" : [ - "main.ts" - ] -} diff --git a/common/predictive-text/testing/two-stage-embedded-webworker/worker/canaryWorker.ts b/common/predictive-text/testing/two-stage-embedded-webworker/worker/canaryWorker.ts deleted file mode 100644 index cf8674d3304..00000000000 --- a/common/predictive-text/testing/two-stage-embedded-webworker/worker/canaryWorker.ts +++ /dev/null @@ -1,9 +0,0 @@ -var counter = 0; - -onmessage = function(e) { - counter++; - - console.log("Message received from main page: ", e.data); - - postMessage(counter); -} \ No newline at end of file diff --git a/common/predictive-text/testing/two-stage-embedded-webworker/worker/tsconfig.json b/common/predictive-text/testing/two-stage-embedded-webworker/worker/tsconfig.json deleted file mode 100644 index ca8006ba440..00000000000 --- a/common/predictive-text/testing/two-stage-embedded-webworker/worker/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "allowJs": false, - "module": "none", - "outFile": "../worker.js", - "inlineSources": true, - "sourceMap": true, - "lib": ["webworker", "es6"], - "target": "es5" - } -} diff --git a/common/schemas/keyboard_info/README.md b/common/schemas/keyboard_info/README.md index 3222a465a8c..f41457f93e5 100644 --- a/common/schemas/keyboard_info/README.md +++ b/common/schemas/keyboard_info/README.md @@ -1,18 +1,31 @@ # keyboard_info -* **keyboard_info.source.json** -* **keyboard_info.distribution.json** +* **keyboard_info.schema.json** Documentation at https://help.keyman.com/developer/cloud/keyboard_info * Primary version: - * https://github.com/keymanapp/api.keyman.com/tree/master/schemas/keyboard_info.source - * https://github.com/keymanapp/api.keyman.com/tree/master/schemas/keyboard_info.distribution + * https://github.com/keymanapp/api.keyman.com/tree/master/schemas/keyboard_info * Synchronized copies at: * https://github.com/keymanapp/keyman/tree/master/common/schemas/keyboard_info # .keyboard_info version history +## 2023-08-11 2.0 stable +* Removed: + - `.documentationFilename` + - `.documentationFileSize` + - `.legacyId` + `.links` + `.related[].note` + `.languages[].example` + Added: + - `.languages[].examples[]` + Modified: + - `.languages[].font`, `.languages[].oskFont`: `.source` is `[string]` + - Source .keyboard_info files are no longer needed, so source vs distribution + keyboard_info distinction is removed + ## 2019-09-06 1.0.6 stable * No changes (see api.keyman.com#36 and api.keyman.com#59. Reverted in 2020-06-10.). diff --git a/common/schemas/keyboard_info/keyboard_info.distribution.json b/common/schemas/keyboard_info/keyboard_info.distribution.json deleted file mode 100644 index 3d3d4ee0c86..00000000000 --- a/common/schemas/keyboard_info/keyboard_info.distribution.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "allOf": [ - { "$ref": "/keyboard_info.source.json#/definitions/KeyboardInfo" }, - { "required": [ "id", "name", "license", "languages", "lastModifiedDate", "platformSupport" ] } - ] -} diff --git a/common/schemas/keyboard_info/keyboard_info.schema.json b/common/schemas/keyboard_info/keyboard_info.schema.json new file mode 100644 index 00000000000..75d8e0329e8 --- /dev/null +++ b/common/schemas/keyboard_info/keyboard_info.schema.json @@ -0,0 +1,105 @@ +{ + "$schema": "http://json-schema.org/schema#", + "$ref": "#/definitions/KeyboardInfo", + + "definitions": { + "KeyboardInfo": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "name": { "type": "string" }, + "authorName": { "type": "string" }, + "authorEmail": { "type": "string", "format": "email" }, + "description": { "type": "string" }, + "license": { "type": "string", "enum": ["freeware", "shareware", "commercial", "mit", "other"] }, + "languages": { "anyOf": [ + { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, + { "$ref": "#/definitions/KeyboardLanguageInfo" } + ]}, + "lastModifiedDate": { "type": "string", "format": "date-time" }, + "packageFilename": { "type": "string", "pattern": "\\.km[xp]$" }, + "packageFileSize": { "type": "number" }, + "jsFilename": { "type": "string", "pattern": "\\.js$" }, + "jsFileSize": { "type": "number" }, + "isRTL": { "type": "boolean" }, + "encodings": { "type": "array", "items": { "type": "string", "enum": ["ansi", "unicode"] } }, + "packageIncludes": { "type": "array", "items": { "type": "string", "enum": ["welcome", "documentation", "fonts", "visualKeyboard"] } }, + "version": { "type": "string" }, + "minKeymanVersion": { "type": "string", "pattern": "^\\d+\\.\\d$" }, + "helpLink": { "type": "string", "pattern": "^https://help\\.keyman\\.com/keyboard/" }, + "platformSupport": { "$ref": "#/definitions/KeyboardPlatformInfo" }, + "sourcePath": { "type": "string", "pattern": "^(release|legacy|experimental)/.+/.+$" }, + "related": { "type": "object", "patternProperties": { + ".": { "$ref": "#/definitions/KeyboardRelatedInfo" } + }, + "additionalProperties": false + }, + "deprecated": { "type": "boolean" } + }, + "required": [ "id", "name", "license", "languages", "lastModifiedDate", "platformSupport" ] + }, + + "KeyboardLanguageInfo": { + "type": "object", + "patternProperties": { + ".": { "$ref": "#/definitions/KeyboardLanguageInfoItem" } + }, + "additionalProperties": false + }, + + "KeyboardLanguageInfoItem": { + "type": "object", + "properties": { + "font": { "$ref": "#/definitions/KeyboardFontInfo" }, + "oskFont": { "$ref": "#/definitions/KeyboardFontInfo" }, + "examples": { "type": "array", "items": { "$ref": "#/definitions/KeyboardExampleInfo" } }, + "displayName": { "type": "string" }, + "languageName": { "type": "string" }, + "scriptName": { "type": "string" }, + "regionName": { "type": "string" } + }, + "required": [], + "additionalProperties": false + }, + + "KeyboardFontInfo": { + "type": "object", + "properties": { + "family": { "type": "string" }, + "source": { "type": "array", "items": { "type": "string" } } + }, + "required": ["family", "source"], + "additionalProperties": false + }, + + "KeyboardExampleInfo": { + "type": "object", + "properties": { + "keys": { "type": "string" }, + "text": { "type": "string" }, + "note": { "type": "string" } + }, + "required": ["keys", "text"], + "additionalProperties": false + }, + + "KeyboardPlatformInfo": { + "type": "object", + "patternProperties": { + "^(windows|macos|desktopWeb|ios|android|mobileWeb|linux)$": { "type": "string", "enum": ["dictionary", "full", "basic", "none"] } + }, + "required": [], + "additionalProperties": false + }, + + "KeyboardRelatedInfo": { + "type": "object", + "properties": { + "deprecates": { "type": "boolean" }, + "deprecatedBy": { "type": "boolean" } + }, + "required": [], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/common/schemas/keyboard_info/keyboard_info.source.json b/common/schemas/keyboard_info/keyboard_info.source.json deleted file mode 100644 index ac2687701ff..00000000000 --- a/common/schemas/keyboard_info/keyboard_info.source.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema#", - "$ref": "#/definitions/KeyboardInfo", - - "definitions": { - "KeyboardInfo": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "name": { "type": "string" }, - "authorName": { "type": "string" }, - "authorEmail": { "type": "string", "format": "email" }, - "description": { "type": "string" }, - "license": { "type": "string", "enum": ["freeware", "shareware", "commercial", "mit", "other"] }, - "languages": { "anyOf": [ - { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, - { "$ref": "#/definitions/KeyboardLanguageInfo" } - ]}, - "lastModifiedDate": { "type": "string", "format": "date-time" }, - "links": { "type": "array", "items": { "$ref": "#/definitions/KeyboardLinkInfo" } }, - "packageFilename": { "type": "string", "pattern": "\\.km[xp]$" }, - "packageFileSize": { "type": "number" }, - "jsFilename": { "type": "string", "pattern": "\\.js$" }, - "jsFileSize": { "type": "number" }, - "documentationFilename": { "type": "string", "pattern": "\\.(rtf|html?|pdf|txt)$" }, - "documentationFileSize": { "type": "number" }, - "isRTL": { "type": "boolean" }, - "encodings": { "type": "array", "items": { "type": "string", "enum": ["ansi", "unicode"] }, "additionalItems": false }, - "packageIncludes": { "type": "array", "items": { "type": "string", "enum": ["welcome", "documentation", "fonts", "visualKeyboard"] }, "additionalItems": false }, - "version": { "type": "string" }, - "minKeymanVersion": { "type": "string", "pattern": "^\\d+\\.\\d$" }, - "helpLink": { "type": "string", "pattern": "^https://help\\.keyman\\.com/keyboard/" }, - "platformSupport": { "$ref": "#/definitions/KeyboardPlatformInfo" }, - "legacyId": { "type": "number" }, - "sourcePath": { "type": "string", "pattern": "^(release|legacy|experimental)/.+/.+$" }, - "related": { "type": "object", "patternProperties": { - ".": { "$ref": "#/definitions/KeyboardRelatedInfo" } - }, - "additionalProperties": false - }, - "deprecated": { "type": "boolean" } - }, - "required": [ - "license", "languages" - ] - }, - - "KeyboardLanguageInfo": { - "type": "object", - "patternProperties": { - ".": { "$ref": "#/definitions/KeyboardLanguageInfoItem" } - }, - "additionalProperties": false - }, - - "KeyboardLanguageInfoItem": { - "type": "object", - "properties": { - "font": { "$ref": "#/definitions/KeyboardFontInfo" }, - "oskFont": { "$ref": "#/definitions/KeyboardFontInfo" }, - "example": { "$ref": "#/definitions/KeyboardExampleInfo" }, - "displayName": { "type": "string" }, - "languageName": { "type": "string" }, - "scriptName": { "type": "string" }, - "regionName": { "type": "string" } - }, - "required": [], - "additionalProperties": false - }, - - "KeyboardFontInfo": { - "type": "object", - "properties": { - "family": { "type": "string" }, - "source": { "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] }, - "size": { "type": "string" } - }, - "required": ["family", "source"], - "additionalProperties": false - }, - - "KeyboardExampleInfo": { - "type": "object", - "properties": { - "keys": { "anyOf": [ - { "type": "string" }, - { "type": "array", "items": { - "anyOf": [ - { "type": "string" }, - { "$ref": "#/definitions/KeyboardExampleKeyInfo" } - ] } - } - ] }, - "text": { "type": "string" }, - "note": { "type": "string" } - }, - "required": [], - "additionalProperties": false - }, - - "KeyboardExampleKeyInfo": { - "type": "object", - "properties": { - "key": { "type": "string", "enum": [ - "K_SPACE", - "K_A", "K_B", "K_C", "K_D", "K_E", "K_F", "K_G", "K_H", "K_I", "K_J", "K_K", "K_L", "K_M", - "K_N", "K_O", "K_P", "K_Q", "K_R", "K_S", "K_T", "K_U", "K_V", "K_W", "K_X", "K_Y", "K_Z", - "K_1", "K_2", "K_3", "K_4", "K_5", "K_6", "K_7", "K_8", "K_9", "K_0", - "K_BKQUOTE", "K_HYPHEN", "K_EQUAL", "K_LBRKT", "K_RBRKT", "K_BKSLASH", "K_COLON", - "K_QUOTE", "K_COMMA", "K_PERIOD", "K_SLASH", - "K_oE2", "K_BKSP", "K_TAB", "K_ENTER", "K_ESC", - "K_LEFT", "K_UP", "K_RIGHT", "K_DOWN", "K_PGUP", "K_PGDN", "K_HOME", "K_END", "K_INS", "K_DEL", - "K_F1", "K_F2", "K_F3", "K_F4", "K_F5", "K_F6", "K_F7", "K_F8", "K_F9", "K_F10", "K_F11", "K_F12", - "K_KP5", "K_NP0", "K_NP1", "K_NP2", "K_NP3", "K_NP4", "K_NP5", "K_NP6", "K_NP7", "K_NP8", "K_NP9", - "K_NPSTAR", "K_NPPLUS", "K_NPMINUS", "K_NPDOT", "K_NPSLASH", - "K_SEL", "K_PRINT", "K_EXEC", "K_HELP", "K_SEPARATOR", - "K_F13", "K_F14", "K_F15", "K_F16", "K_F17", "K_F18", "K_F19", "K_F20", "K_F21", "K_F22", "K_F23", "K_F24", - "K_KANJI?15", "K_KANJI?16", "K_KANJI?17", "K_KANJI?18", "K_KANJI?19", "K_KANJI?1C", "K_KANJI?1D", "K_KANJI?1E", "K_KANJI?1F", - "K_oE0", "K_oE1", "K_oE3", "K_oE4", "K_oE6", "K_oE9", "K_oEA", "K_oEB", "K_oEC", "K_oED", "K_oEE", "K_oEF", - "K_oF0", "K_oF1", "K_oF2", "K_oF3", "K_oF4", "K_oF5", "K_?00", "K_?05", "K_NPENTER", - "K_?06", "K_?07", "K_?0A", "K_?0B", "K_?0E", "K_?0F", "K_?1A", "K_?3A", "K_?3B", "K_?3C", "K_?3D", "K_?3E", - "K_?3F", "K_?40", "K_?5B", "K_?5C", "K_?5D", "K_?5E", "K_?5F", "K_?88", "K_?89", "K_?8A", "K_?8B", "K_?8C", - "K_?8D", "K_?8E", "K_?8F", "K_?92", "K_?94", "K_?95", "K_?96", "K_?97", "K_?98", "K_?99", "K_?9A", "K_?9B", - "K_?9C", "K_?9D", "K_?9E", "K_?9F", "K_?A0", "K_?A1", "K_?A2", "K_?A3", "K_?A4", "K_?A5", "K_?A6", "K_?A7", - "K_?A8", "K_?A9", "K_?AA", "K_?AB", "K_?AC", "K_?AD", "K_?AE", "K_?AF", "K_?B0", "K_?B1", "K_?B2", "K_?B3", - "K_?B4", "K_?B5", "K_?B6", "K_?B7", "K_?B8", "K_?B9", "K_?C1", "K_?C2", "K_?C3", "K_?C4", "K_?C5", "K_?C6", - "K_?C7", "K_?C8", "K_?C9", "K_?CA", "K_?CB", "K_?CC", "K_?CD", "K_?CE", "K_?CF", "K_?D0", "K_?D1", "K_?D2", - "K_?D3", "K_?D4", "K_?D5", "K_?D6", "K_?D7", "K_?D8", "K_?D9", "K_?DA", "K_oDF", "K_?E5", "K_?E7", "K_?E8", - "K_?F6", "K_?F7", "K_?F8", "K_?F9", "K_?FA", "K_?FB", "K_?FC", "K_?FD", "K_?FE", "K_?FF" - ] }, - "modifiers": { - "type": "array", - "items": { "type": "string", "enum": ["shift", "s", "ctrl", "c", "alt", "a", "left-ctrl", "lc", "right-ctrl", "rc", "left-alt", "la", "right-alt", "ra"] } - } - }, - "required": ["key"], - "additionalProperties": false - }, - - "KeyboardLinkInfo": { - "type": "object", - "properties": { - "name": { "type": "string" }, - "url": { "type": "string" } - }, - "required": ["name", "url"], - "additionalProperties": false - }, - - "KeyboardPlatformInfo": { - "type": "object", - "patternProperties": { - "^(windows|macos|desktopWeb|ios|android|mobileWeb|linux)$": { "type": "string", "enum": ["dictionary", "full", "basic", "none"] } - }, - "required": [], - "additionalProperties": false - }, - - "KeyboardRelatedInfo": { - "type": "object", - "properties": { - "deprecates": { "type": "boolean" }, - "deprecatedBy": { "type": "boolean" }, - "note": { "type": "string" } - }, - "required": [], - "additionalProperties": false - } - } -} \ No newline at end of file diff --git a/common/schemas/kpj/README.md b/common/schemas/kpj/README.md index 0f8e55de60d..bfca5c5bdc0 100644 --- a/common/schemas/kpj/README.md +++ b/common/schemas/kpj/README.md @@ -6,6 +6,9 @@ always '1.0'. It will be required for version 2.0 and later of the format. **Note:** An additional schema file, kpj-9.0.schema.json, for supporting legacy versions of .kpj, from Keyman Developer 9.0 and earlier, is now available. +## 2023-08-07 2.0.1 +* Add Options/SkipMetadataFiles, defaults to True for 1.0 projects. + ## 2023-02-27 2.0 * Version 2.0 makes 'Files' optional (internally, Files/File will be ignored, deleted on load and populated from folder structure). Adds Options/SourcePath, diff --git a/common/schemas/kpj/kpj.schema.json b/common/schemas/kpj/kpj.schema.json index 77721345f28..8a59fa40c54 100644 --- a/common/schemas/kpj/kpj.schema.json +++ b/common/schemas/kpj/kpj.schema.json @@ -45,6 +45,10 @@ "type": "string", "pattern": "^(True|False)$" }, + "SkipMetadataFiles": { + "type": "string", + "pattern": "^(True|False)$" + }, "ProjectType": { "type": "string", "pattern": "^(keyboard|lexicalmodel)$" diff --git a/common/schemas/kps/kps.xsd b/common/schemas/kps/kps.xsd index e6ee424e28d..292f0c8fc6a 100644 --- a/common/schemas/kps/kps.xsd +++ b/common/schemas/kps/kps.xsd @@ -31,6 +31,8 @@ + + @@ -74,6 +76,7 @@ + @@ -96,6 +99,19 @@ + + + + + + + + + + + + + @@ -110,6 +126,9 @@ + + + @@ -135,6 +154,7 @@ + @@ -149,8 +169,6 @@ - - @@ -189,6 +207,12 @@ + + + + + + @@ -209,4 +233,27 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmn b/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmn index a66fac420a9..07e37ee681b 100644 --- a/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmn +++ b/common/test/keyboards/baseline/k_017___space_mnemonic_kbd.kmn @@ -4,8 +4,7 @@ c keys: [K_A][K_B][K_SPACE][K_C][K_SPACE][K_D][K_SPACE][K_D][K_E] c expected: XYZ store(&VERSION) '9.0' -NAME "Alt key tests" -HOTKEY "[CTRL SHIFT K_A]" +store(&HOTKEY) "[CTRL SHIFT K_A]" store(&mnemoniclayout) "1" begin Unicode > use(main) diff --git a/common/test/keyboards/baseline/k_018___nul_testing.kmn b/common/test/keyboards/baseline/k_018___nul_testing.kmn index bc6e41206d5..20da4d4af70 100644 --- a/common/test/keyboards/baseline/k_018___nul_testing.kmn +++ b/common/test/keyboards/baseline/k_018___nul_testing.kmn @@ -3,11 +3,10 @@ c Description: Tests the processing of nul in LHS of rules c keys: [K_A][K_A] c expected: OKa -VERSION 9.0 -NAME "Nul test" -HOTKEY "[CTRL SHIFT K_N]" +store(&VERSION) "9.0" +store(&HOTKEY) "[CTRL SHIFT K_N]" -begin unicode > use(main) +begin unicode > use(main) group(Main) using keys nul + 'a' > 'OK' diff --git a/common/test/keyboards/build.sh b/common/test/keyboards/build.sh index 43562ffb13a..af6a9702d5f 100755 --- a/common/test/keyboards/build.sh +++ b/common/test/keyboards/build.sh @@ -1,225 +1,43 @@ #!/usr/bin/env bash - -set -e -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" -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" ## END STANDARD BUILD SCRIPT INCLUDE -THIS_DIR="$(dirname "$THIS_SCRIPT")" - -display_usage() { - echo "usage: build.sh [build options] [targets]" - echo - echo "Build options:" - echo " --clean, -c Clean instead of build" - echo " --debug, -d Debug build" - echo " --silent, -s Suppress information messages" - echo " --keyboard, -k Build only keyboards (not packages)" - echo " --kmcomp path Specify path to kmcomp.exe, defaults" - echo " to windows/bin/developer/kmcomp.exe" - echo " --zip-source Create zip file for source of each" - echo " keyboard for artifact tests" - echo " --index Build index.html for artifact tests" - echo - echo "Targets (all unless specified):" - - for d in "$THIS_DIR/"*/; do - d="$(basename "$d")" - if [ "$d" != "invalid" ]; then - echo " $d" - fi - done - echo - exit 0 -} +cd "$THIS_SCRIPT_PATH" -QUIET=false -DEBUG=false -CLEAN=false -KEYBOARDS_ONLY=false -CUSTOM_KMCOMP= -INDEX=false -ZIPSOURCE=false -KMCOMP="$KEYMAN_ROOT/developer/bin/kmcomp.exe" -TARGETS=() - -# Parse args -shopt -s nocasematch - -while [[ $# -gt 0 ]] ; do - key="$1" - case $key in - --help|-h|-\?) - display_usage - ;; - --clean|-c) - CLEAN=true - ;; - --debug|-d) - DEBUG=true - ;; - --silent|-s) - QUIET=true - ;; - --keyboard|-k) - KEYBOARDS_ONLY=true - ;; - --zip-source) - ZIPSOURCE=true - ;; - --index) - INDEX=true - ;; - --kmcomp) - shift - KMCOMP="$1" - CUSTOM_KMCOMP=true - ;; - *) - TARGETS+=("$key") - esac - shift +targets=() +# Build list of available targets from subfolders, if none specified +for d in */; do + d="$(basename "$d")" + if [ "$d" != "invalid" ] && [ "$d" != "issue" ]; then + targets+=(":$d") + fi done -# TODO: while this is intended to be cross platform, we -# don't currently have a binary version of kmcomp available -# during Linux and macOS builds, so that will need to be -# manually sourced. -KMCOMP_LAUNCHER= - -if ! $CUSTOM_KMCOMP; then - case "${OSTYPE}" in - "cygwin") - ;; - "msys") - ;; - "darwin"*) - # For Catalina (10.15) onwards, must use wine64 - base_macos_ver=10.15 - macos_ver=$(sw_vers -productVersion) - if verlt "$macos_ver" "$base_macos_ver"; then - KMCOMP_LAUNCHER=wine - else - # On Catalina, and later versions: - # wine-4.12.1 works; wine-5.0, wine-5.7 do not. - # retrieve these from: - # `brew tap gcenx/wine && brew install --cask --no-quarantine wine-crossover` - # may also need to `sudo spctl --master-disable` - KMCOMP_LAUNCHER=wine64 - KMCOMP="$(dirname $KMCOMP)/kmcomp.x64.exe" - fi - ;; - *) - KMCOMP_LAUNCHER=wine - ;; - esac -fi +KMC="$KEYMAN_ROOT/developer/src/kmc/build/src/kmc.js" -# Build list of available targets from subfolders, if none specified -if [ ${#TARGETS[@]} == 0 ]; then - for d in "$THIS_DIR/"*/; do - d="$(basename "$d")" - if [ "$d" != "invalid" ] && [ "$d" != "issue" ]; then - TARGETS+=("$d") - fi - done -fi - -if ! $QUIET; then - displayInfo "" \ - "CLEAN: $CLEAN" \ - "DEBUG: $DEBUG" \ - "QUIET: $QUIET" \ - "KEYBOARDS_ONLY: $KEYBOARDS_ONLY" \ - "TARGETS: ${TARGETS[@]}" \ - "ZIPSOURCE: $ZIPSOURCE" \ - "INDEX: $INDEX" \ - "" -fi - -clean() { - local kpj="$1.kpj" ss= s= - if $QUIET; then - ss=-ss - s=-s - fi - pushd "$1" > /dev/null - if [ -f build.sh ]; then - ./build.sh -c $s - else - $KMCOMP_LAUNCHER "$KMCOMP" -c $ss "$kpj" - fi - popd > /dev/null -} +builder_describe "Test Keyboards" \ + clean configure build \ + ${targets[@]} \ + "--index Build index.html for artifact tests" \ + "--zip-source Create zip file for source of each keyboard for artifact tests" \ + "--kmc=KMC Specify path to kmc, defaults to developer/src/kmc/build/" \ + "--silent,-s Suppress information messages" -build() { - local kpj="$1.kpj" d= t= ss= target= s= k= - if $KEYBOARDS_ONLY; then - k=-k - t=-t - target="$1.kmn" - fi - if $DEBUG; then - d=-d - fi - if $QUIET; then - s=-s - ss=-ss - fi - # -w - treat warnings as errors, we'll force this - # -cfc - check filename conventions - pushd "$1" > /dev/null - if [ -f build.sh ]; then - ./build.sh $d $k $s - else - $KMCOMP_LAUNCHER "$KMCOMP" $d $ss -w -cfc "$kpj" $t "$target" - fi - popd > /dev/null -} +builder_parse "$@" -zipsource() { +function zipsource() { local target="$1" pushd "$1" > /dev/null 7z a -r -x!build -x"!$target.kpj.user" "${target}_source.zip" . popd > /dev/null } -### - -for TARGET in "${TARGETS[@]}"; do - if $CLEAN; then - if ! $QUIET; then - echo - builder_heading "Cleaning target $TARGET" - echo - fi - clean "$TARGET" - else - if ! $QUIET; then - echo - builder_heading "Building target $TARGET" - echo - fi - build "$TARGET" - - if $ZIPSOURCE; then - zipsource "$TARGET" - fi - fi -done - -### - -if $INDEX; then - if $CLEAN; then - rm -f "$THIS_DIR/index.html" - else - cat << EOF > "$THIS_DIR/index.html" +function build_index() { + local active_targets=($*) + cat << EOF > index.html @@ -231,19 +49,47 @@ if $INDEX; then
    EOF - for TARGET in "${TARGETS[@]}"; do - if $ZIPSOURCE; then - echo "
  • $TARGET.kmp (source)
  • " >> "$THIS_DIR/index.html" - else - echo "
  • $TARGET.kmp
  • " >> "$THIS_DIR/index.html" - fi - done + for TARGET in "${active_targets[@]}"; do + if builder_has_option --zip-source; then + echo "
  • $TARGET.kmp (source)
  • " >> index.html + else + echo "
  • $TARGET.kmp
  • " >> index.html + fi + done - cat << 'EOF' >> "$THIS_DIR/index.html" + cat << EOF >> index.html
EOF +} + +### + +function build() { + local active_targets=() + for TARGET in "${targets[@]}"; do + if builder_has_action build$TARGET; then + active_targets+=(${TARGET#:}) + fi + done + + local ss= + if builder_has_option --silent; then + ss="--log-level silent" fi -fi -exit 0 + + node "$KMC" build $builder_debug $ss -w "${active_targets[@]}" + + if builder_has_option --zip-source; then + for TARGET in "${active_targets[@]}"; do + zipsource "$TARGET" + done + fi + + if builder_has_option --index; then + build_index "${active_targets[@]}" + fi +} + +builder_run_action build build \ No newline at end of file diff --git a/common/test/keyboards/caps_lock_layer_3620/caps_lock_layer_3620.keyboard_info b/common/test/keyboards/caps_lock_layer_3620/caps_lock_layer_3620.keyboard_info deleted file mode 100644 index 7cabc2dfde2..00000000000 --- a/common/test/keyboards/caps_lock_layer_3620/caps_lock_layer_3620.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "en" - ], - "description": "Caps Lock Layer 3620 generated from template" -} diff --git a/common/test/keyboards/caps_lock_layer_3620/caps_lock_layer_3620.kpj b/common/test/keyboards/caps_lock_layer_3620/caps_lock_layer_3620.kpj index 57877e2346b..7868db1fad5 100644 --- a/common/test/keyboards/caps_lock_layer_3620/caps_lock_layer_3620.kpj +++ b/common/test/keyboards/caps_lock_layer_3620/caps_lock_layer_3620.kpj @@ -51,13 +51,6 @@ .md - - id_fff8738ee01fad8282ccd871d1b1d7ec - caps_lock_layer_3620.keyboard_info - caps_lock_layer_3620.keyboard_info - - .keyboard_info - id_6beb4ed096b4f0d03913a0cf0dc1c10b caps_lock_layer_3620.kmx diff --git a/common/test/keyboards/full_caps_3620_3621/full_caps_3620_3621.keyboard_info b/common/test/keyboards/full_caps_3620_3621/full_caps_3620_3621.keyboard_info deleted file mode 100644 index 3bcee188375..00000000000 --- a/common/test/keyboards/full_caps_3620_3621/full_caps_3620_3621.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "en" - ], - "description": "full_caps_3620_3621 generated from template" -} diff --git a/common/test/keyboards/full_caps_3620_3621/full_caps_3620_3621.kpj b/common/test/keyboards/full_caps_3620_3621/full_caps_3620_3621.kpj index 96cb56925cd..b7aad105733 100644 --- a/common/test/keyboards/full_caps_3620_3621/full_caps_3620_3621.kpj +++ b/common/test/keyboards/full_caps_3620_3621/full_caps_3620_3621.kpj @@ -51,13 +51,6 @@ .md - - id_da795226173614238dd92754311e5f15 - full_caps_3620_3621.keyboard_info - full_caps_3620_3621.keyboard_info - - .keyboard_info - id_3087832d59de33f9903fc21ba620b576 full_caps_3620_3621.kmx diff --git a/common/test/keyboards/obolo_chwerty_6347/obolo_chwerty_6347.keyboard_info b/common/test/keyboards/obolo_chwerty_6347/obolo_chwerty_6347.keyboard_info deleted file mode 100644 index 1513b8bb473..00000000000 --- a/common/test/keyboards/obolo_chwerty_6347/obolo_chwerty_6347.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "ann-Latn" - ], - "description": "Obolo Chwerty Keyboard: A keyboard layout for the Obolo language of Nigeria. It covers the need of every dialect in that language" -} diff --git a/common/test/keyboards/obolo_chwerty_6347/obolo_chwerty_6347.kpj b/common/test/keyboards/obolo_chwerty_6347/obolo_chwerty_6347.kpj index 2205ec839ed..228ef013817 100644 --- a/common/test/keyboards/obolo_chwerty_6347/obolo_chwerty_6347.kpj +++ b/common/test/keyboards/obolo_chwerty_6347/obolo_chwerty_6347.kpj @@ -52,13 +52,6 @@ .md - - id_fa9ac62e52c7671ac61602e3583305fd - obolo_chwerty_6347.keyboard_info - obolo_chwerty_6347.keyboard_info - - .keyboard_info - id_8ec79e2ed4a2ffef6c8f0645d5ff022f obolo_chwerty_6347.ico diff --git a/common/test/keyboards/obolo_chwerty_6347/source/obolo_chwerty_6347.kps b/common/test/keyboards/obolo_chwerty_6347/source/obolo_chwerty_6347.kps index 9e3f5a437ab..ebf22548312 100644 --- a/common/test/keyboards/obolo_chwerty_6347/source/obolo_chwerty_6347.kps +++ b/common/test/keyboards/obolo_chwerty_6347/source/obolo_chwerty_6347.kps @@ -113,7 +113,7 @@ obolo_chwerty_6347 1.2.1 - Obolo (Andoni) + Obolo (Andoni) diff --git a/common/test/keyboards/obolo_chwerty_6351/obolo_chwerty_6351.keyboard_info b/common/test/keyboards/obolo_chwerty_6351/obolo_chwerty_6351.keyboard_info deleted file mode 100644 index 1513b8bb473..00000000000 --- a/common/test/keyboards/obolo_chwerty_6351/obolo_chwerty_6351.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "ann-Latn" - ], - "description": "Obolo Chwerty Keyboard: A keyboard layout for the Obolo language of Nigeria. It covers the need of every dialect in that language" -} diff --git a/common/test/keyboards/obolo_chwerty_6351/obolo_chwerty_6351.kpj b/common/test/keyboards/obolo_chwerty_6351/obolo_chwerty_6351.kpj index cffa8ad084e..3e3e5defd93 100644 --- a/common/test/keyboards/obolo_chwerty_6351/obolo_chwerty_6351.kpj +++ b/common/test/keyboards/obolo_chwerty_6351/obolo_chwerty_6351.kpj @@ -52,13 +52,6 @@ .md - - id_fa9ac62e52c7671ac61602e3583305fd - obolo_chwerty_6351.keyboard_info - obolo_chwerty_6351.keyboard_info - - .keyboard_info - id_425d4d61d84287d1991f48eb0158c78c obolo_chwerty_6351.ico diff --git a/common/test/keyboards/obolo_chwerty_6351/source/obolo_chwerty_6351.kps b/common/test/keyboards/obolo_chwerty_6351/source/obolo_chwerty_6351.kps index 11e8de9c3bf..df9a3b81cc6 100644 --- a/common/test/keyboards/obolo_chwerty_6351/source/obolo_chwerty_6351.kps +++ b/common/test/keyboards/obolo_chwerty_6351/source/obolo_chwerty_6351.kps @@ -113,7 +113,7 @@ obolo_chwerty_6351 1.2.1 - Obolo (Andoni) + Obolo (Andoni) diff --git a/common/test/keyboards/start_of_sentence_3621/start_of_sentence_3621.keyboard_info b/common/test/keyboards/start_of_sentence_3621/start_of_sentence_3621.keyboard_info deleted file mode 100644 index 70b79803fa4..00000000000 --- a/common/test/keyboards/start_of_sentence_3621/start_of_sentence_3621.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "en" - ], - "description": "start_of_sentence_3621 generated from template" -} diff --git a/common/test/keyboards/start_of_sentence_3621/start_of_sentence_3621.kpj b/common/test/keyboards/start_of_sentence_3621/start_of_sentence_3621.kpj index 064471c9506..af45b2b3716 100644 --- a/common/test/keyboards/start_of_sentence_3621/start_of_sentence_3621.kpj +++ b/common/test/keyboards/start_of_sentence_3621/start_of_sentence_3621.kpj @@ -51,13 +51,6 @@ .md - - id_31c9e544d5ff29ebb5ee3006e39f5624 - start_of_sentence_3621.keyboard_info - start_of_sentence_3621.keyboard_info - - .keyboard_info - id_8b42366ff2333c0b6d85e2d23cf930d7 start_of_sentence_3621.kmx diff --git a/common/test/keyboards/u_xxxx_yyyy_2858/u_xxxx_yyyy_2858.keyboard_info b/common/test/keyboards/u_xxxx_yyyy_2858/u_xxxx_yyyy_2858.keyboard_info deleted file mode 100644 index 03c280d3093..00000000000 --- a/common/test/keyboards/u_xxxx_yyyy_2858/u_xxxx_yyyy_2858.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - - ], - "description": "U_xxxx_yyyy_2858 generated from template" -} diff --git a/common/test/keyboards/u_xxxx_yyyy_2858/u_xxxx_yyyy_2858.kpj b/common/test/keyboards/u_xxxx_yyyy_2858/u_xxxx_yyyy_2858.kpj index f2b44703b1e..1c576713382 100644 --- a/common/test/keyboards/u_xxxx_yyyy_2858/u_xxxx_yyyy_2858.kpj +++ b/common/test/keyboards/u_xxxx_yyyy_2858/u_xxxx_yyyy_2858.kpj @@ -51,13 +51,6 @@ .md - - id_10a5cd9d0c42cd03b9838916f6f0bd3d - u_xxxx_yyyy_2858.keyboard_info - u_xxxx_yyyy_2858.keyboard_info - - .keyboard_info - id_a1200478ad69b7f058fdce369fb1d960 u_xxxx_yyyy_2858.kmx diff --git a/common/test/keyboards/web_context_tests/web_context_tests.kpj b/common/test/keyboards/web_context_tests/web_context_tests.kpj index 05146c1a3f4..1ec5a1c65f6 100644 --- a/common/test/keyboards/web_context_tests/web_context_tests.kpj +++ b/common/test/keyboards/web_context_tests/web_context_tests.kpj @@ -51,13 +51,6 @@ .md - - id_41e1ad22438964b0b5b84605df1c667d - web_context_tests.keyboard_info - web_context_tests.keyboard_info - - .keyboard_info - id_fa6acb8dd8884fe0c6c6262bb4477d27 web_context_tests.kmx diff --git a/common/tools/hextobin/build.sh b/common/tools/hextobin/build.sh index 702e6610f01..8df4f9b83a3 100755 --- a/common/tools/hextobin/build.sh +++ b/common/tools/hextobin/build.sh @@ -1,11 +1,4 @@ #!/usr/bin/env bash -# -# Builds hextobin.js -# - -# 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]}")" @@ -22,21 +15,10 @@ cd "$THIS_SCRIPT_PATH" builder_describe "Build hextobin" clean configure build builder_describe_outputs \ configure /node_modules \ - build build/index.js -builder_parse "$@" + build /common/tools/hextobin/build/index.js -if builder_start_action clean; then - npm run clean - 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 - npm run build - builder_finish_action success build -fi +builder_parse "$@" +builder_run_action clean rm -rf build/ node_modules/ +builder_run_action configure verify_npm_setup +builder_run_action build tsc --build diff --git a/common/tools/hextobin/hextobin.ts b/common/tools/hextobin/hextobin.ts index 503b18d4b1a..6bf56c79533 100644 --- a/common/tools/hextobin/hextobin.ts +++ b/common/tools/hextobin/hextobin.ts @@ -1,11 +1,11 @@ #!/usr/bin/env node -import * as fs from 'fs'; -import * as rd from 'readline'; -import * as program from 'commander'; +import { Command } from 'commander'; +import hextobin from './index.js'; let inputFilename: string = ""; let outputFilename: string = ""; +const program = new Command(); program .description( `Will convert the input file which is a hex dump to the output file. Hex dump can contain @@ -43,6 +43,4 @@ function exitDueToUsageError(message: string): never { return process.exit(64); // SysExits.EX_USAGE } -import hextobin from './index'; - hextobin(inputFilename, outputFilename, {silent: false}); diff --git a/common/tools/hextobin/package.json b/common/tools/hextobin/package.json index 155f8297597..2ebb0e9f4e1 100644 --- a/common/tools/hextobin/package.json +++ b/common/tools/hextobin/package.json @@ -1,6 +1,8 @@ { "name": "@keymanapp/hextobin", "description": "hextobin conversion tool", + "type": "module", + "private": true, "keywords": [ "keyman", "hex", @@ -14,14 +16,6 @@ "bin": { "hextobin": "build/hextobin.js" }, - "scripts": { - "clean": "tsc -b --clean", - "build": "tsc -b" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/keymanapp/keyman.git" - }, "devDependencies": { "@types/node": "^18.7.18" } diff --git a/common/tools/hextobin/tsconfig.json b/common/tools/hextobin/tsconfig.json index e32831fe737..55e3f66308e 100644 --- a/common/tools/hextobin/tsconfig.json +++ b/common/tools/hextobin/tsconfig.json @@ -1,19 +1,9 @@ { - "extends": "../../../tsconfig-base.json", - "compilerOptions": { - "composite": true, - "declaration": true, - "target": "ES2020", - "module": "CommonJS", - "moduleResolution": "node", - "rootDir": ".", - "outDir": "build/", - }, - "exclude": [ - "node_modules" - ], - "files": [ - "index.ts", - "hextobin.ts" - ] + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": ".", + "outDir": "build/" + }, + "exclude": ["node_modules"], + "files": ["index.ts", "hextobin.ts"] } diff --git a/common/tools/sourcemap-path-remapper/src/convert-source-map.d.ts b/common/tools/sourcemap-path-remapper/src/convert-source-map.d.ts new file mode 100644 index 00000000000..15a4da3c643 --- /dev/null +++ b/common/tools/sourcemap-path-remapper/src/convert-source-map.d.ts @@ -0,0 +1 @@ +declare module 'convert-source-map'; \ No newline at end of file diff --git a/common/tools/sourcemap-path-remapper/tsconfig.json b/common/tools/sourcemap-path-remapper/tsconfig.json index f9b531b311a..e207097f614 100644 --- a/common/tools/sourcemap-path-remapper/tsconfig.json +++ b/common/tools/sourcemap-path-remapper/tsconfig.json @@ -1,12 +1,11 @@ { - "extends": "../../../tsconfig-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "allowJs": false, "allowSyntheticDefaultImports": true, "declaration": true, - "module": "es6", - "moduleResolution": "node", + "sourceMap": false, "inlineSourceMap": true, "inlineSources": true, "sourceRoot": "/common/tools/sourcemap-path-remapper/src", diff --git a/common/web/es-bundling/tsconfig.json b/common/web/es-bundling/tsconfig.json index d2ab738acfb..a5149b51c6f 100644 --- a/common/web/es-bundling/tsconfig.json +++ b/common/web/es-bundling/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.esm-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "baseUrl": "./", "outDir": "build/", diff --git a/common/web/keyboard-processor/src/keyboards/activeLayout.ts b/common/web/keyboard-processor/src/keyboards/activeLayout.ts index e6d2ab9f157..9050d393a19 100644 --- a/common/web/keyboard-processor/src/keyboards/activeLayout.ts +++ b/common/web/keyboard-processor/src/keyboards/activeLayout.ts @@ -48,6 +48,12 @@ export class ActiveKey implements LayoutKey { _baseKeyEvent: KeyEvent; isMnemonic: boolean = false; + /** + * Only available on subkeys, but we don't distinguish between base keys and subkeys + * at this level yet in KMW. + */ + default?: boolean; + proportionalPad: number; proportionalX: number; proportionalWidth: number; diff --git a/common/web/keyboard-processor/src/keyboards/defaultLayouts.ts b/common/web/keyboard-processor/src/keyboards/defaultLayouts.ts index 8d187ac8544..7e0153d0a52 100644 --- a/common/web/keyboard-processor/src/keyboards/defaultLayouts.ts +++ b/common/web/keyboard-processor/src/keyboards/defaultLayouts.ts @@ -22,6 +22,7 @@ export type LayoutKey = { "nextlayer"?: string, "pad"?: string | number, "sk"?: LayoutKey[] + "default"?: boolean } export type LayoutRow = { diff --git a/common/web/keyman-version/build-bundler.js b/common/web/keyman-version/build-bundler.js deleted file mode 100644 index a5f26a44df9..00000000000 --- a/common/web/keyman-version/build-bundler.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Bundles @keymanapp/keyman-version as a single-file CommonJS module for components - * still in need of it. - */ - -import esbuild from 'esbuild'; - -// Bundles to a compact ESModule -esbuild.buildSync({ - entryPoints: ['build/version.inc.js'], - bundle: true, - sourcemap: true, - //minify: true, // No need to minify a module. - //keepNames: true, - format: "cjs", - // Sets 'common/web' as a root folder for module resolution; - // this allows the keyman-version import to resolve. - nodePaths: ['..'], - outfile: "build/version.inc.cjs", - tsconfig: 'tsconfig.json', - target: "es5" -}); \ No newline at end of file diff --git a/common/web/keyman-version/build.sh b/common/web/keyman-version/build.sh index 939709732c2..d4a07e4c762 100755 --- a/common/web/keyman-version/build.sh +++ b/common/web/keyman-version/build.sh @@ -1,11 +1,4 @@ #!/usr/bin/env bash -# -# Builds the include script for the current Keyman version. -# - -# 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]}")" @@ -13,6 +6,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" ## END STANDARD BUILD SCRIPT INCLUDE . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh" # This script runs from its own folder cd "$THIS_SCRIPT_PATH" @@ -25,8 +19,7 @@ builder_describe "Build the include script for current Keyman version" \ build \ "pack build a local .tgz pack for testing" \ "publish publish to npm" \ - test \ - --dry-run + test builder_describe_outputs \ configure "/node_modules" \ @@ -34,21 +27,7 @@ builder_describe_outputs \ builder_parse "$@" - -if builder_start_action configure; then - verify_npm_setup - builder_finish_action success configure -fi - -if builder_start_action clean; then - npm run clean - rm -f ./version.inc.ts - rm -f ./keyman-version.mts - rm -rf build - builder_finish_action success clean -fi - -if builder_start_action build; then +function do_build() { # Generate version.inc.ts echo " // Generated by common/web/keyman-version/build.sh @@ -72,22 +51,11 @@ export class KEYMAN_VERSION { export default KEYMAN_VERSION; " > ./version.inc.ts - tsc -b $builder_verbose - # kmlmc (the lexical model compiler) relies on a Node-based import, but after some of the earlier - # ES-modularization work, our main output's an ES module. Fortunately, esbuild can provide an easy stopgap. - - # Generates a CommonJS variant (in case other modules still need it). - node ./build-bundler.js - - builder_finish_action success build -fi + tsc --build $builder_verbose +} -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 +builder_run_action clean rm -rf version.inc.ts keyman-version.mts build/ +builder_run_action configure verify_npm_setup +builder_run_action build do_build +builder_run_action publish builder_publish_to_npm +builder_run_action pack builder_publish_to_pack diff --git a/common/web/keyman-version/package.json b/common/web/keyman-version/package.json index 0ec9c1322be..ded749528c4 100644 --- a/common/web/keyman-version/package.json +++ b/common/web/keyman-version/package.json @@ -1,26 +1,15 @@ { "name": "@keymanapp/keyman-version", "description": "Keyman global version data", - "main": "./build/version.inc.cjs", "exports": { - ".": { - "import": "./build/version.inc.js", - "require": "./build/version.inc.cjs" - }, - "./build/version.inc.cjs": "./build/version.inc.cjs" + ".": "./build/version.inc.js" }, "files": [ "/build/" ], - "scripts": { - "build": "echo 'Building @keymanapp/keyman-version' && tsc -b", - "clean": "tsc -b --clean", - "tsc": "tsc" - }, "license": "MIT", "type": "module", "devDependencies": { - "@types/node": "^20.4.1", "typescript": "^4.9.5" } } diff --git a/common/web/keyman-version/tsconfig.json b/common/web/keyman-version/tsconfig.json index 8618c6dd922..a5808c432ee 100644 --- a/common/web/keyman-version/tsconfig.json +++ b/common/web/keyman-version/tsconfig.json @@ -1,17 +1,12 @@ { - "extends": "../../../tsconfig-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { - "allowJs": false, - "declaration": true, - "module": "es6", - "sourceMap": true, "outDir": "./build", - "lib": ["es6"], "target": "es5", - "downlevelIteration": true, "rootDir": "." }, + "include": [ "version.inc.ts" ] diff --git a/common/web/lm-message-types/tsconfig.json b/common/web/lm-message-types/tsconfig.json index 124def350d2..f9d427f0bfc 100644 --- a/common/web/lm-message-types/tsconfig.json +++ b/common/web/lm-message-types/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "declaration": true, "module": "none", diff --git a/common/web/sentry-manager/src/tsconfig.json b/common/web/sentry-manager/src/tsconfig.json index 490c315dc90..1b438c93c4b 100644 --- a/common/web/sentry-manager/src/tsconfig.json +++ b/common/web/sentry-manager/src/tsconfig.json @@ -1,13 +1,22 @@ { - "extends": "../../../../tsconfig-base.json", + "extends": "../../../../tsconfig.base.json", "compilerOptions": { + + // TODO: These override /tsconfig.base.json settings, and so should be removed if possible, + // but existing code in web/ breaks some of these settinsg + "noImplicitThis": false, + "noImplicitReturns": false, + "noImplicitAny": false, + "strictFunctionTypes": false, + "noUnusedLocals": false, + + "allowJs": true, "allowSyntheticDefaultImports": true, "baseUrl": "./", "inlineSources": true, "lib": ["es6", "dom"], "module": "es6", - "moduleResolution": "Node16", "outDir": "../build/obj", "rootDir": "./", "sourceMap": true, diff --git a/common/web/types/.eslintrc.cjs b/common/web/types/.eslintrc.cjs index 21618ea66ff..98db7f812e5 100644 --- a/common/web/types/.eslintrc.cjs +++ b/common/web/types/.eslintrc.cjs @@ -8,6 +8,8 @@ module.exports = { "coverage/*", "node_modules/*", "test/fixtures/*", + "tools/*", + "src/schemas/*" ], overrides: [ { diff --git a/common/web/types/.gitignore b/common/web/types/.gitignore index 35512aa7852..2f943a2f4e9 100644 --- a/common/web/types/.gitignore +++ b/common/web/types/.gitignore @@ -1 +1,2 @@ -src/schemas/ \ No newline at end of file +src/schemas/ +obj/ \ No newline at end of file diff --git a/common/web/types/build.sh b/common/web/types/build.sh index f0c73b0e077..85c448024ed 100755 --- a/common/web/types/build.sh +++ b/common/web/types/build.sh @@ -37,18 +37,38 @@ function compile_schemas() { "$KEYMAN_ROOT/common/schemas/displaymap/displaymap.schema.json" "$KEYMAN_ROOT/common/schemas/keyman-touch-layout/keyman-touch-layout.spec.json" "$KEYMAN_ROOT/common/schemas/keyman-touch-layout/keyman-touch-layout.clean.spec.json" + "$KEYMAN_ROOT/common/schemas/keyboard_info/keyboard_info.schema.json" ) + rm -rf "$THIS_SCRIPT_PATH/obj/schemas" + mkdir -p "$THIS_SCRIPT_PATH/obj/schemas" rm -rf "$THIS_SCRIPT_PATH/src/schemas" mkdir -p "$THIS_SCRIPT_PATH/src/schemas" cp "${schemas[@]}" "$THIS_SCRIPT_PATH/src/schemas/" # TODO: use https://github.com/tc39/proposal-json-modules instead of this once it stablises for schema in "${schemas[@]}"; do - local fn="$THIS_SCRIPT_PATH/src/schemas/$(basename "$schema" .json)" + local schema_base="$(basename "$schema" .json)" + local fn="$THIS_SCRIPT_PATH/src/schemas/$schema_base" + local out="$THIS_SCRIPT_PATH/obj/schemas/$schema_base.validator.cjs" + + # emit a .ts wrapper for the schema file + + builder_echo "Compiling schema $schema_base.json" echo 'export default ' > "$fn.ts" cat "$fn.json" >> "$fn.ts" + + # emit a compiled validator for the schema file + + # While would seem obvious to just run 'ajv' directly here, somewhere node + # is picking up the wrong path for the build and breaking the formats + # imports. So it is essential to use `npm run` at this point, even though it + # is painfully slower, at least until we figure out the path discrepancy. + npm run build:schema -- -c ./tools/formats.cjs -s "$fn.json" --strict-types false -o "$out" done + + # the validators now need to be compiled to esm + node tools/schema-bundler.js } function copy_cldr_imports() { diff --git a/common/web/types/package.json b/common/web/types/package.json index 83a403aeedc..fd59cffa97d 100644 --- a/common/web/types/package.json +++ b/common/web/types/package.json @@ -16,6 +16,7 @@ ], "scripts": { "build": "tsc -b", + "build:schema": "ajv compile", "lint": "eslint .", "test": "npm run lint && cd test && tsc -b && cd .. && c8 --skip-full --reporter=lcov --reporter=text mocha", "prepublishOnly": "npm run build" @@ -27,7 +28,6 @@ }, "dependencies": { "@keymanapp/keyman-version": "*", - "ajv": "^8.11.0", "restructure": "git+https://github.com/keymanapp/dependency-restructure.git#7a188a1e26f8f36a175d95b67ffece8702363dfc", "semver": "^7.5.2", "xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829" @@ -39,6 +39,9 @@ "@types/node": "^20.4.1", "@types/semver": "^7.3.12", "@types/xml2js": "^0.4.5", + "ajv": "^8.12.0", + "ajv-cli": "^5.0.0", + "ajv-formats": "^2.1.1", "c8": "^7.12.0", "chai": "^4.3.4", "chalk": "^2.4.2", @@ -74,6 +77,7 @@ "src/ldml-keyboard/unicodeset-parser-api.ts", "src/keyman-touch-layout/keyman-touch-layout-file-writer.ts", "src/osk/osk.ts", + "src/schemas/*", "test/" ] } diff --git a/common/web/types/src/consts/virtual-key-constants.ts b/common/web/types/src/consts/virtual-key-constants.ts index f93155f1ff5..3347660f015 100644 --- a/common/web/types/src/consts/virtual-key-constants.ts +++ b/common/web/types/src/consts/virtual-key-constants.ts @@ -122,8 +122,8 @@ export const USVirtualKeyCodes = { */ K_oE2:226, K_OE2:226, - k_oC1:193, // ISO B11, ABNT-2 key to left of right shift, not on US keyboard - k_OC1:193, + K_oC1:193, // ISO B11, ABNT-2 key to left of right shift, not on US keyboard + K_OC1:193, 'K_?C1':193, 'k_?C1':193, K_oDF:0xDF, @@ -144,125 +144,90 @@ export const USVirtualKeyCodes = { const k = USVirtualKeyCodes; -export type KeyMap = number[][]; +/** Map a CLDR scancode to a US VKey ala USVirtualKeyCodes */ +export const CLDRScanToUSVirtualKeyCodes = { + 0x02: k.K_1, + 0x03: k.K_2, + 0x04: k.K_3, + 0x05: k.K_4, + 0x06: k.K_5, + 0x07: k.K_6, + 0x08: k.K_7, + 0x09: k.K_8, + 0x0A: k.K_9, + 0x0B: k.K_0, + 0x0C: k.K_HYPHEN, + 0x0D: k.K_EQUAL, -export const USVirtualKeyMap: KeyMap = [ - // ` 1 2 3 4 5 6 7 8 9 0 - = [bksp] - [ k.K_BKQUOTE, k.K_1, k.K_2, k.K_3, k.K_4, k.K_5, k.K_6, k.K_7, k.K_8, k.K_9, k.K_0, k.K_HYPHEN, k.K_EQUAL ], - // [tab] Q W E R T Y U I O P [ ] \ - [ k.K_Q, k.K_W, k.K_E, k.K_R, k.K_T, k.K_Y, k.K_U, k.K_I, k.K_O, k.K_P, k.K_LBRKT, k.K_RBRKT, k.K_BKSLASH ], - // [caps] A S D F G H J K L ; ' [enter] - [ k.K_A, k.K_S, k.K_D, k.K_F, k.K_G, k.K_H, k.K_J, k.K_K, k.K_L, k.K_COLON, k.K_QUOTE ], - // [shift] Z X C V B N M , . / [shift] *=oE2 - [ k.K_Z, k.K_X, k.K_C, k.K_V, k.K_B, k.K_N, k.K_M, k.K_COMMA, k.K_PERIOD, k.K_SLASH ], - // space - [ k.K_SPACE ], -]; + 0x10: k.K_Q, + 0x11: k.K_W, + 0x12: k.K_E, + 0x13: k.K_R, + 0x14: k.K_T, + 0x15: k.K_Y, + 0x16: k.K_U, + 0x17: k.K_I, + 0x18: k.K_O, + 0x19: k.K_P, + 0x1A: k.K_LBRKT, + 0x1B: k.K_RBRKT, -export const ISOVirtualKeyMap: KeyMap = [ - // ` 1 2 3 4 5 6 7 8 9 0 - = [bksp] - [ k.K_BKQUOTE, k.K_1, k.K_2, k.K_3, k.K_4, k.K_5, k.K_6, k.K_7, k.K_8, k.K_9, k.K_0, k.K_HYPHEN, k.K_EQUAL ], - // [tab] Q W E R T Y U I O P [ ] - [ k.K_Q, k.K_W, k.K_E, k.K_R, k.K_T, k.K_Y, k.K_U, k.K_I, k.K_O, k.K_P, k.K_LBRKT, k.K_RBRKT ], - // [caps] A S D F G H J K L ; ' \ [enter] - [ k.K_A, k.K_S, k.K_D, k.K_F, k.K_G, k.K_H, k.K_J, k.K_K, k.K_L, k.K_COLON, k.K_QUOTE, k.K_BKSLASH ], - // [shift] * Z X C V B N M , . / [shift] *=oE2 - [ k.K_oE2, k.K_Z, k.K_X, k.K_C, k.K_V, k.K_B, k.K_N, k.K_M, k.K_COMMA, k.K_PERIOD, k.K_SLASH ], - // space - [ k.K_SPACE ], -]; + 0x1E: k.K_A, + 0x1F: k.K_S, + 0x20: k.K_D, + 0x21: k.K_F, + 0x22: k.K_G, + 0x23: k.K_H, + 0x24: k.K_J, + 0x25: k.K_K, + 0x26: k.K_L, + 0x27: k.K_COLON, + 0x28: k.K_QUOTE, + 0x29: k.K_BKQUOTE, -export const JISVirtualKeyMap: KeyMap = [ - // [Hankaku/Zenkaku] 1 2 3 4 5 6 7 8 9 0 - ^ ¥ [bksp] - [ k.K_1, k.K_2, k.K_3, k.K_4, k.K_5, k.K_6, k.K_7, k.K_8, k.K_9, k.K_0, k.K_HYPHEN, k.K_EQUAL, k.K_BKSLASH /* YEN */ ], - // [tab] Q W E R T Y U I O P @«`» [ [enter] - [ k.K_Q, k.K_W, k.K_E, k.K_R, k.K_T, k.K_Y, k.K_U, k.K_I, k.K_O, k.K_P, k.K_BKQUOTE, k.K_LBRKT ], - // [caps] A S D F G H J K L ; : ] [enter] - [ k.K_A, k.K_S, k.K_D, k.K_F, k.K_G, k.K_H, k.K_J, k.K_K, k.K_L, k.K_COLON, k.K_QUOTE, k.K_RBRKT ], - // [shift] Z X C V B N M , . / _ [shift] - [ k.K_Z, k.K_X, k.K_C, k.K_V, k.K_B, k.K_N, k.K_M, k.K_COMMA, k.K_PERIOD, k.K_SLASH, k.K_oE2 /* ろ */ ], - // space - [ k.K_SPACE ], -]; + 0x2B: k.K_BKSLASH, + 0x2C: k.K_Z, + 0x2D: k.K_X, + 0x2E: k.K_C, + 0x2F: k.K_V, + 0x30: k.K_B, + 0x31: k.K_N, + 0x32: k.K_M, + 0x33: k.K_COMMA, + 0x34: k.K_PERIOD, + 0x35: k.K_SLASH, -export const ABNT2VirtualKeyMap: KeyMap = [ - // ' 1 2 3 4 5 6 7 8 9 0 - = [bksp] - [ k.K_BKQUOTE, k.K_1, k.K_2, k.K_3, k.K_4, k.K_5, k.K_6, k.K_7, k.K_8, k.K_9, k.K_0, k.K_HYPHEN, k.K_EQUAL ], - // [tab] Q W E R T Y U I O P ´ [ - [ k.K_Q, k.K_W, k.K_E, k.K_R, k.K_T, k.K_Y, k.K_U, k.K_I, k.K_O, k.K_P, k.K_LBRKT, k.K_RBRKT ], - // [caps] A S D F G H J K L ç ~ ] [enter] - [ k.K_A, k.K_S, k.K_D, k.K_F, k.K_G, k.K_H, k.K_J, k.K_K, k.K_L, k.K_COLON, k.K_QUOTE, k.K_BKSLASH ], - // [shift] \ Z X C V B N M , . ; / [shift] - [ k.K_oE2, k.K_Z, k.K_X, k.K_C, k.K_V, k.K_B, k.K_N, k.K_M, k.K_COMMA, k.K_PERIOD, k.K_SLASH, k.k_oC1 /* ABNT2 */ ], - // space - [ k.K_SPACE ], -]; + 0x39: k.K_SPACE, -/** - * Map from a hardware constant to a keymap - * For the 'key' see constants.layr_list_hardware_map - */ -export const HardwareToKeymap: Map = new Map( - [ - ["us", USVirtualKeyMap], - ["iso", ISOVirtualKeyMap], - ["jis", JISVirtualKeyMap], - ["abnt2", ABNT2VirtualKeyMap], - ] -); + 0x56: k.K_oE2, // << Same as 0x7D; found on iso, abnt2 + 0x73: k.K_oC1, + 0x7D: k.K_oE2, // << Same as 0x56; found on jis + +}; + +export type KeyMap = number[][]; /** - * Maps LDML VKey Names from CLDR VKey Enum in TR35 to Keyman virtual key codes + * Convert a scan code numerical KeyMap to VKeys + * @param scans keymap to convert + * @param badScans output: set of not-found scancodes + * @returns */ -export const LdmlVkeyNames: Record = { - 'SPACE': k.K_SPACE, // 0x20, // A03 - '0': k.K_0, // 0x30, // E10 - '1': k.K_1, // 0x31, // E01 - '2': k.K_2, // 0x32, // E02 - '3': k.K_3, // 0x33, // E03 - '4': k.K_4, // 0x34, // E04 - '5': k.K_5, // 0x35, // E05 - '6': k.K_6, // 0x36, // E06 - '7': k.K_7, // 0x37, // E07 - '8': k.K_8, // 0x38, // E08 - '9': k.K_9, // 0x39, // E09 - 'A': k.K_A, // 0x41, // C01 - 'B': k.K_B, // 0x42, // B05 - 'C': k.K_C, // 0x43, // B03 - 'D': k.K_D, // 0x44, // C03 - 'E': k.K_E, // 0x45, // D03 - 'F': k.K_F, // 0x46, // C04 - 'G': k.K_G, // 0x47, // C05 - 'H': k.K_H, // 0x48, // C06 - 'I': k.K_I, // 0x49, // D08 - 'J': k.K_J, // 0x4A, // C07 - 'K': k.K_K, // 0x4B, // C08 - 'L': k.K_L, // 0x4C, // C09 - 'M': k.K_M, // 0x4D, // B07 - 'N': k.K_N, // 0x4E, // B06 - 'O': k.K_O, // 0x4F, // D09 - 'P': k.K_P, // 0x50, // D10 - 'Q': k.K_Q, // 0x51, // D01 - 'R': k.K_R, // 0x52, // D04 - 'S': k.K_S, // 0x53, // C02 - 'T': k.K_T, // 0x54, // D05 - 'U': k.K_U, // 0x55, // D07 - 'V': k.K_V, // 0x56, // B05 - 'W': k.K_W, // 0x57, // D02 - 'X': k.K_X, // 0x58, // B02 - 'Y': k.K_Y, // 0x59, // D06 - 'Z': k.K_Z, // 0x5A, // B01 - 'SEMICOLON': k.K_COLON, // 0xBA, // C10 - 'EQUAL': k.K_EQUAL, // 0xBB, // E12 - 'COMMA': k.K_COMMA, // 0xBC, // B08 - 'HYPHEN': k.K_HYPHEN, // 0xBD, // E11 - 'PERIOD': k.K_PERIOD, // 0xBE, // B09 - 'SLASH': k.K_SLASH, // 0xBF, // B10 - 'GRAVE': k.K_BKQUOTE, // 0xC0, // E00 - 'LBRACKET': k.K_LBRKT, // 0xDB, // D11 - 'BACKSLASH': k.K_BKSLASH, // 0xDC, // D13 - 'RBRACKET': k.K_RBRKT, // 0xDD, // D12 - 'QUOTE': k.K_QUOTE, // 0xDE, // C11 - 'LESS-THAN': k.K_oE2, // 0xE2, // B00 102nd key on European layouts, right of left shift. - 'ABNT2': k.k_oC1, // 0xC1, // B11 Extra key, left of right-shift, ABNT2 -}; +export function CLDRScanToKeyMap(scans: KeyMap, badScans?: Set): KeyMap { + return scans.map((row) => row.map((scan) => CLDRScanToVkey(scan, badScans))); +} + +/** Convert one scan code to vkey, or undefined */ +export function CLDRScanToVkey(scan: number, badScans?: Set): number { + /** typescript fun to index the scan table */ + function hasScanCode(key: PropertyKey): key is keyof typeof CLDRScanToUSVirtualKeyCodes { + return key in CLDRScanToUSVirtualKeyCodes; + } + if (hasScanCode(scan)) { + return CLDRScanToUSVirtualKeyCodes[scan]; + } else { + badScans?.add(scan); + return undefined; + } +} + diff --git a/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file-reader.ts b/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file-reader.ts index f2dc0a5c7d9..bb5d8c2df17 100644 --- a/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file-reader.ts +++ b/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file-reader.ts @@ -1,7 +1,5 @@ -import { default as AjvModule } from 'ajv'; -const Ajv = AjvModule.default; // The actual expected Ajv type. import { TouchLayoutFile } from "./keyman-touch-layout-file.js"; -import Schemas from '../../src/schemas.js'; +import SchemaValidators from '../schema-validators.js'; export class TouchLayoutFileReader { public read(source: Uint8Array): TouchLayoutFile { @@ -69,11 +67,10 @@ export class TouchLayoutFileReader { } public validate(source: TouchLayoutFile): void { - const ajv = new Ajv(); - if(!ajv.validate(Schemas.touchLayoutClean, source)) + if(!SchemaValidators.touchLayoutClean(source)) /* c8 ignore next 3 */ { - throw new Error(ajv.errorsText()); + throw new Error((SchemaValidators.touchLayoutClean).errors); } } diff --git a/common/web/types/src/kmx/element-string.ts b/common/web/types/src/kmx/element-string.ts index ed64a498ff8..ebf4d398e55 100644 --- a/common/web/types/src/kmx/element-string.ts +++ b/common/web/types/src/kmx/element-string.ts @@ -31,11 +31,11 @@ export class ElementString extends Array { * @param source if a string array, does not get reinterpreted as UnicodeSet. This is used with vars, etc. Or pass `["str"]` for an explicit 1-element elem. * If it is a string, will be interpreted per reorder element rules. */ - constructor(sections: DependencySections, source: string | string[], order?: string, tertiary?: string, tertiary_base?: string, prebase?: string) { - super(); - //TODO-LDML: full UnicodeSet and parsing + static fromStrings(sections: DependencySections, source: string | string[], order?: string, tertiary?: string, tertiary_base?: string, prebase?: string) : ElementString { + // the returned array + const array = new ElementString(); if(!source) { - return; + return array; } let items : ElementSegment[]; @@ -80,16 +80,16 @@ export class ElementString extends Array { throw Error(`Could not parse uset ${item.segment}`); } elem.uset = sections.uset.allocUset(uset, sections); - elem.value = sections.strs.allocString('', true); // no string + elem.value = sections.strs.allocString('', {singleOk: true}); // no string } else if (item.type === ElementType.codepoint || item.type === ElementType.escaped || item.type === ElementType.string) { // some kind of a string let str = item.segment; if (item.type === ElementType.escaped && !MATCH_HEX_ESCAPE.test(str)) { str = unescapeOneQuadString(str); // TODO-LDML: any other escape forms here? - elem.value = sections.strs.allocString(str, true); + elem.value = sections.strs.allocString(str, { singleOk: true }); } else { - elem.value = sections.strs.allocAndUnescapeString(str, true); + elem.value = sections.strs.allocString(str, { unescape: true, singleOk: true }); } // Now did we end up with one char or no? if (elem.value.isOneChar) { @@ -104,8 +104,9 @@ export class ElementString extends Array { (ElemElementFlags.type & typeFlag) | (tertiary_bases?.[i] == '1' /* TODO-LDML: or 'true'? */ ? ElemElementFlags.tertiary_base : 0) | (prebases?.[i] == '1' /* TODO-LDML: or 'true'? */ ? ElemElementFlags.prebase : 0); - this.push(elem); + array.push(elem); }; + return array; } isEqual(a: ElementString): boolean { if (a.length != this.length) { diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts index 057158f1865..c0038246e27 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/build-layr.ts @@ -13,7 +13,7 @@ import { BUILDER_SECTION } from "./builder-section.js"; * List of layers, the element */ interface BUILDER_LAYR_LIST { - hardware: number; // hardware indicator + hardware: BUILDER_STR_REF; // hardware or 'touch' layer: number; // index of first layer in the list, in the count: number; // number of layer entries in the list minDeviceWidth: number; // width in millimeters @@ -82,7 +82,7 @@ export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l layr.lists = kmxplus.layr.lists.map((list) => { const blist: BUILDER_LAYR_LIST = { - hardware: list.hardware, + hardware: build_strs_index(sect_strs, list.hardware), layer: null, // to be set below _layers: list.layers, count: list.layers.length, @@ -92,6 +92,7 @@ export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l }); // now sort the lists layr.lists.sort((a, b) => { + // sort by string # if (a.hardware < b.hardware) { return -1; } else if (a.hardware > b.hardware) { diff --git a/common/web/types/src/kmx/kmx-plus-builder/build-vkey.ts b/common/web/types/src/kmx/kmx-plus-builder/build-vkey.ts deleted file mode 100644 index 0a4863beabd..00000000000 --- a/common/web/types/src/kmx/kmx-plus-builder/build-vkey.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { constants } from '@keymanapp/ldml-keyboard-constants'; -import { KMXPlusData } from "../kmx-plus.js"; -import { BUILDER_SECTION } from './builder-section.js'; - -/* ------------------------------------------------------------------ - * vkey section - ------------------------------------------------------------------ */ - -/** - * Builder for the 'vkey' section - */ -interface BUILDER_VKEY_ITEM { - vkey: number; - target: number; -}; - -export interface BUILDER_VKEY extends BUILDER_SECTION { - count: number; - items: BUILDER_VKEY_ITEM[]; -}; - -export function build_vkey(kmxplus: KMXPlusData): BUILDER_VKEY { - if(!kmxplus.vkey.vkeys.length) { - return null; - } - - let vkey: BUILDER_VKEY = { - ident: constants.hex_section_id(constants.section.vkey), - size: constants.length_vkey + constants.length_vkey_item * kmxplus.vkey.vkeys.length, - _offset: 0, - count: kmxplus.vkey.vkeys.length, - items: [] - }; - - for(let item of kmxplus.vkey.vkeys) { - vkey.items.push({ - vkey: item.vkey, - target: item.target - }); - } - - return vkey; -} diff --git a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts index 3cc58c9fe75..e77c60fbe7e 100644 --- a/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts +++ b/common/web/types/src/kmx/kmx-plus-builder/kmx-plus-builder.ts @@ -15,7 +15,6 @@ import { BUILDER_STRS, build_strs } from './build-strs.js'; import { BUILDER_TRAN, build_tran } from './build-tran.js'; import { BUILDER_USET, build_uset } from './build-uset.js'; import { BUILDER_VARS, build_vars } from './build-vars.js'; -import { BUILDER_VKEY, build_vkey } from './build-vkey.js'; type BUILDER_BKSP = BUILDER_TRAN; // type BUILDER_FINL = BUILDER_TRAN; @@ -36,7 +35,6 @@ type SectionBuilders = { tran?: BUILDER_TRAN; uset?: BUILDER_USET; vars?: BUILDER_VARS; - vkey?: BUILDER_VKEY; }; export default class KMXPlusBuilder { @@ -73,7 +71,6 @@ export default class KMXPlusBuilder { this.emitSection(file, this.file.COMP_PLUS_TRAN, this.sect.tran); this.emitSection(file, this.file.COMP_PLUS_USET, this.sect.uset); this.emitSection(file, this.file.COMP_PLUS_VARS, this.sect.vars); - this.emitSection(file, this.file.COMP_PLUS_VKEY, this.sect.vkey); return file; } @@ -100,7 +97,6 @@ export default class KMXPlusBuilder { this.sect.tran = build_tran(this.file.kmxplus.tran, this.sect.strs, this.sect.elem); this.sect.uset = build_uset(this.file.kmxplus, this.sect.strs); this.sect.vars = build_vars(this.file.kmxplus, this.sect.strs, this.sect.elem, this.sect.list); - this.sect.vkey = build_vkey(this.file.kmxplus); // Finalize the sect (index) section @@ -138,7 +134,6 @@ export default class KMXPlusBuilder { offset = this.finalize_sect_item(this.sect.tran, offset); offset = this.finalize_sect_item(this.sect.uset, offset); offset = this.finalize_sect_item(this.sect.vars, offset); - offset = this.finalize_sect_item(this.sect.vkey, offset); this.sect.sect.total = offset; } diff --git a/common/web/types/src/kmx/kmx-plus.ts b/common/web/types/src/kmx/kmx-plus.ts index eba1324b857..1060e50fba8 100644 --- a/common/web/types/src/kmx/kmx-plus.ts +++ b/common/web/types/src/kmx/kmx-plus.ts @@ -35,14 +35,14 @@ export class Elem extends Section { strings: ElementString[] = []; constructor(sections: DependencySections) { super(); - this.strings.push(new ElementString(sections, '')); // C7043: null element string + this.strings.push(ElementString.fromStrings(sections, '')); // C7043: null element string } /** * @param source if a string array, does not get reinterpreted as UnicodeSet. This is used with vars, etc. Or pass `["str"]` for an explicit 1-element elem. * If it is a string, will be interpreted per reorder element ruls. */ allocElementString(sections: DependencySections, source: string | string[], order?: string, tertiary?: string, tertiary_base?: string, prebase?: string): ElementString { - let s = new ElementString(sections, source, order, tertiary, tertiary_base, prebase); + let s = ElementString.fromStrings(sections, source, order, tertiary, tertiary_base, prebase); let result = this.strings.find(item => item.isEqual(s)); if(result === undefined) { result = s; @@ -143,45 +143,67 @@ export class CharStrsItem extends StrsItem { } }; +/** class for string manipulation options. These are in order of the pipeline. */ +export interface StrsOptions { + /** apply string variables (requires sections) */ + stringVariables?: boolean; + /** apply markers (requires sections) */ + markers?: boolean; + /** unescape with unescapeString */ + unescape?: boolean; + /** string can be stored as a single CharStrsItem, not in strs table. */ + singleOk?: boolean; +}; + export class Strs extends Section { strings: StrsItem[] = [ new StrsItem('') ]; // C7043: The null string is always requierd /** * Allocate a StrsItem given the string, unescaping if necessary. * @param s escaped string - * @param singleOk if true, allocate a CharStrsItem (not in strs table) if single-char capable. - * @returns - */ - allocAndUnescapeString(s?: string, singleOk?: boolean): StrsItem { - return this.allocString(unescapeString(s), singleOk); - } - /** - * Allocate a StrsItem given the string. - * @param s string - * @param singleOk if true, allocate a CharStrsItem (not in strs table) if single-char capable. - * @returns + * @param opts options for allocation + * @param sections other sections, if needed + * @returns StrsItem */ - allocString(s?: string, singleOk?: boolean): StrsItem { - if(s === undefined || s === null) { - // undefined or null are always equivalent to empty string, see C7043 - s = ''; - } - - if(typeof s !== 'string') { - throw new Error('alloc_string: s must be a string, undefined, or null.'); - } + allocString(s?: string, opts?: StrsOptions, sections?: DependencySections): StrsItem { + // Run the string processing pipeline + s = Strs.processString(s, opts, sections); - // if it's a single char, don't push it into the list - if (singleOk && isOneChar(s)) { + // if it's a single char, don't push it into the strs table + if (opts?.singleOk && isOneChar(s)) { return new CharStrsItem(s); } + // default: look to see if the string is already present let result = this.strings.find(item => item.value === s); if(result === undefined) { + // only add if not already present result = new StrsItem(s); this.strings.push(result); } return result; } + + /** process everything according to opts */ + static processString(s: string, opts: StrsOptions, sections: DependencySections) { + s = s ?? ''; + // type check everything else + if (typeof s !== 'string') { + throw new Error('alloc_string: s must be a string, undefined, or null.'); + } + // substitute variables + if (opts?.stringVariables) { + s = sections.vars.substituteStrings(s, sections); + } + // substitute markers + if (opts?.markers) { + s = sections.vars.substituteMarkerString(s); + } + // unescape \u{…} + if (opts?.unescape) { + s = unescapeString(s); + } + return s; + } }; /** @@ -234,6 +256,7 @@ export class Vars extends Section { }); } substituteStrings(str: string, sections: DependencySections): string { + if (!str) return str; return str.replaceAll(VariableParser.STRING_REFERENCE, (_entire, id) => { const val = this.findStringVariableValue(id); if (val === null) { @@ -293,8 +316,8 @@ export class Vars extends Section { return v[0]; } } - substituteMarkerString(s : string) : string { - return MarkerParser.toSentinelString(s, this.markers); + substituteMarkerString(s : string, forMatch? : boolean) : string { + return MarkerParser.toSentinelString(s, this.markers, forMatch); } }; @@ -309,7 +332,7 @@ export class VarsItem extends Section { constructor(id: string, value: string, sections: DependencySections) { super(); this.id = sections.strs.allocString(id); - this.value = sections.strs.allocAndUnescapeString(value); + this.value = sections.strs.allocString(value, {unescape: true}); } valid() : boolean { @@ -404,17 +427,6 @@ export class Bksp extends Tran { } }; -// 'vkey' - -export class VkeyItem { - vkey: number; - target: number; -} - -export class Vkey extends Section { - vkeys: VkeyItem[] = []; -}; - // 'disp' export class DispItem { to: StrsItem; @@ -433,7 +445,7 @@ export class Disp extends Section { * In-memory `` */ export class LayrList { - hardware: number; + hardware: StrsItem; layers: LayrEntry[] = []; minDeviceWidth: number; // millimeters }; @@ -510,19 +522,14 @@ export class List extends Section { * Allocate a list from a space-separated list of items. * Note that passing undefined or null or `''` will * end up being the same as the empty list `[]` - * @param strs Strs section for allocation * @param s space-separated list of items + * @param opts string options + * @param sections sections * @returns a List object */ - allocListFromSpaces(strs: Strs, s?: string): ListItem { + allocListFromSpaces(s: string, opts: StrsOptions, sections: DependencySections): ListItem { s = s ?? ''; - return this.allocList(strs, s.split(' ')); - } - allocListFromEscapedSpaces(strs: Strs, s?: string): ListItem { - if(s === undefined || s === null) { - s = ''; - } - return this.allocList(strs, s.split(' ').map(unescapeString)); + return this.allocList(s.split(' '), opts, sections); } /** * Return a List object referring to the string list. @@ -532,7 +539,7 @@ export class List extends Section { * @param s string list to allocate * @returns */ - allocList(strs: Strs, s?: string[]): ListItem { + allocList(s: string[], opts: StrsOptions, sections: DependencySections): ListItem { // Special case the 'null' list for [] or [''] if (!s || (s.length === 1 && s[0] === '')) { return this.lists[0]; @@ -540,14 +547,14 @@ export class List extends Section { let result = this.lists.find(item => item.isEqual(s)); if(result === undefined) { // allocate a new ListItem - result = new ListItem(strs, s); + result = ListItem.fromStrings(s, opts, sections); this.lists.push(result); } return result; } constructor(strs: Strs) { super(); - this.lists.push(new ListItem(strs, [])); // C7043: null element string + this.lists.push(ListItem.fromStrings([], {}, { strs })); // C7043: null element string } lists: ListItem[] = []; }; @@ -569,7 +576,6 @@ export interface KMXPlusData { tran?: Tran; uset?: Uset; // uset is ignored in-memory vars?: Vars; - vkey?: Vkey; }; export class KMXPlusFile extends KMXFile { @@ -720,7 +726,7 @@ export class KMXPlusFile extends KMXFile { }); this.COMP_PLUS_LAYR_LIST = new r.Struct({ - hardware: r.uint32le, //enum + hardware: STR_REF, // str: hardware name layer: r.uint32le, // index into layers count: r.uint32le, minDeviceWidth: r.uint32le, // integer: millimeters diff --git a/common/web/types/src/kmx/kmx.ts b/common/web/types/src/kmx/kmx.ts index 8a2c3e47aaa..6102a69cf55 100644 --- a/common/web/types/src/kmx/kmx.ts +++ b/common/web/types/src/kmx/kmx.ts @@ -144,9 +144,10 @@ export class KMXFile { public static readonly VERSION_150 = 0x00000F00; public static readonly VERSION_160 = 0x00001000; + public static readonly VERSION_170 = 0x00001100; public static readonly VERSION_MIN = this.VERSION_50; - public static readonly VERSION_MAX = this.VERSION_160; + public static readonly VERSION_MAX = this.VERSION_170; // // Backspace types diff --git a/common/web/types/src/kmx/string-list.ts b/common/web/types/src/kmx/string-list.ts index f9f4b46e912..411c940acf1 100644 --- a/common/web/types/src/kmx/string-list.ts +++ b/common/web/types/src/kmx/string-list.ts @@ -1,5 +1,5 @@ import { OrderedStringList } from 'src/ldml-keyboard/pattern-parser.js'; -import { Strs, StrsItem } from './kmx-plus.js'; +import { DependencySections, StrsItem, StrsOptions } from './kmx-plus.js'; /** * A single entry in a ListItem. @@ -27,20 +27,22 @@ export class ListItem extends Array implements OrderedStringList { /** * Construct a new list from an array of strings. * Use List. This is meant to be called by the List.allocString*() functions. - * @param strs the Strs section is needed to construct this object. * @param source array of strings + * @param opts string handling options + * @param sections the Strs section is needed to construct this object, and other sections may + * be needed depending on the options * @returns */ - constructor(strs: Strs, source: Array) { - super(); - if(!source) { - return; + static fromStrings(source: Array, opts: StrsOptions, sections: DependencySections) : ListItem { + const a = new ListItem(); + if (!source) { + return a; } - for (const str of source) { - let index = new ListIndex(strs.allocString(str)); - this.push(index); + let index = new ListIndex(sections.strs.allocString(str, opts, sections)); + a.push(index); } + return a; } getItemOrder(item: string): number { return this.findIndex(({value}) => value.value === item); @@ -72,12 +74,13 @@ export class ListItem extends Array implements OrderedStringList { return 0; } } - /** for debugging, print as single string */ + /** for debugging and tests, print as single string */ toString(): string { return this.toStringArray().join(' '); } - /** for debugging, map to string array */ + /** for debugging and tests, map to string array */ toStringArray(): string[] { - return this.map(v => v.value.value); + // TODO-LDML: this crashes: return this.map(v => v.toString()); + return Array.from(this.values()).map(v => v.toString()); } }; diff --git a/common/web/types/src/kpj/keyman-developer-project.ts b/common/web/types/src/kpj/keyman-developer-project.ts index ce9f78cf591..a6814a5302f 100644 --- a/common/web/types/src/kpj/keyman-developer-project.ts +++ b/common/web/types/src/kpj/keyman-developer-project.ts @@ -1,5 +1,5 @@ // -// Version 1.0 of Keyman Developer Project .kpj file +// Version 1.0 and 2.0 of Keyman Developer Project .kpj file // import { KeymanFileTypes } from '../main.js'; @@ -9,11 +9,17 @@ export class KeymanDeveloperProject { options: KeymanDeveloperProjectOptions; files: KeymanDeveloperProjectFile[]; projectPath: string = ''; + readonly projectFile: KeymanDeveloperProjectFile; - constructor(private projectFilename: string, version: KeymanDeveloperProjectVersion, private callbacks: CompilerCallbacks) { - this.projectPath = this.callbacks.path.dirname(this.projectFilename); + get projectFilename() { + return this._projectFilename; + } + + constructor(private _projectFilename: string, version: KeymanDeveloperProjectVersion, private callbacks: CompilerCallbacks) { + this.projectPath = this.callbacks.path.dirname(this._projectFilename); this.options = new KeymanDeveloperProjectOptions(version); this.files = []; + this.projectFile = new KeymanDeveloperProjectFile20(_projectFilename, callbacks); } /** * Adds .kmn, .xml, .kps to project based on options.sourcePath @@ -38,8 +44,6 @@ export class KeymanDeveloperProject { this.files.push(file); } } - - this.addMetadataFile(); } public isKeyboardProject() { @@ -50,26 +54,32 @@ export class KeymanDeveloperProject { return !!this.files.find(file => file.fileType == KeymanFileTypes.Source.Model); } - public addMetadataFile() { - const ext = this.isLexicalModelProject() ? KeymanFileTypes.Source.ModelInfo : KeymanFileTypes.Source.KeyboardInfo; + private resolveProjectPath(p: string): string { + // Replace placeholders in the target path + return p.replace('$PROJECTPATH', this.projectPath); + } + + getOutputFilePath(type: KeymanFileTypes.Binary) { + // Roughly corresponds to Delphi TProject.GetTargetFileName + let p = this.options.version == '1.0' ? + this.options.buildPath || '$SOURCEPATH' : + this.options.buildPath; - if(this.files.find(file => KeymanFileTypes.filenameIs(file.filename, ext))) { - return; + // Replace placeholders in the target path + if(this.options.version == '1.0') { + // TODO: do we need to support $VERSION? + // $SOURCEPATH only supported in 1.0 projects + p = p.replace('$SOURCEPATH', this.callbacks.path.dirname(this._projectFilename)); } - const infoFile = - this.callbacks.path.join(this.projectPath, - this.callbacks.path.basename(this.projectFilename, KeymanFileTypes.Source.Project) + ext); - this.files.push(new KeymanDeveloperProjectFile20(infoFile, this.callbacks)); - } + p = this.resolveProjectPath(p); - private resolveProjectPath(p: string): string { - // Replace placeholders in the target path - return p.replace('$PROJECTPATH', this.projectPath); + const f = this.callbacks.path.basename(this._projectFilename, KeymanFileTypes.Source.Project) + type; + return this.callbacks.path.normalize(this.callbacks.path.join(p, f)); } resolveInputFilePath(file: KeymanDeveloperProjectFile): string { - return this.callbacks.resolveFilename(this.projectFilename, file.filePath); + return this.callbacks.resolveFilename(this._projectFilename, file.filePath); } resolveOutputFilePath(file: KeymanDeveloperProjectFile, sourceExt: string, targetExt: string): string { @@ -108,6 +118,11 @@ export class KeymanDeveloperProjectOptions { compilerWarningsAsErrors: boolean = false; warnDeprecatedCode: boolean = true; checkFilenameConventions: boolean = false; // missing option defaults to False + /** + * Skip building .keyboard_info and .model_info files, for example in + * unit tests or for legacy keyboards + */ + skipMetadataFiles: boolean; projectType: KeymanDeveloperProjectType = KeymanDeveloperProjectType.Keyboard; readonly version: KeymanDeveloperProjectVersion; constructor(version: KeymanDeveloperProjectVersion) { @@ -116,10 +131,12 @@ export class KeymanDeveloperProjectOptions { case "1.0": this.buildPath = ''; this.sourcePath = ''; + this.skipMetadataFiles = true; break; case "2.0": this.buildPath = '$PROJECTPATH/build'; this.sourcePath = '$PROJECTPATH/source'; + this.skipMetadataFiles = false; break; default: throw new Error('Invalid version'); diff --git a/common/web/types/src/kpj/kpj-file-reader.ts b/common/web/types/src/kpj/kpj-file-reader.ts index 2c8589b5c14..cffa2069c64 100644 --- a/common/web/types/src/kpj/kpj-file-reader.ts +++ b/common/web/types/src/kpj/kpj-file-reader.ts @@ -1,11 +1,9 @@ import * as xml2js from 'xml2js'; import { KPJFile, KPJFileProject } from './kpj-file.js'; -import { default as AjvModule } from 'ajv'; -const Ajv = AjvModule.default; // The actual expected Ajv type. import { boxXmlArray } from '../util/util.js'; import { KeymanDeveloperProject, KeymanDeveloperProjectFile10, KeymanDeveloperProjectType } from './keyman-developer-project.js'; import { CompilerCallbacks } from '../util/compiler-interfaces.js'; -import Schemas from '../schemas.js'; +import SchemaValidators from '../schema-validators.js'; export class KPJFileReader { constructor(private callbacks: CompilerCallbacks) { @@ -35,13 +33,11 @@ export class KPJFileReader { } public validate(source: KPJFile): void { - const ajv = new Ajv(); - if(!ajv.validate(Schemas.kpj, source)) { - const ajvLegacy = new Ajv(); - if(!ajvLegacy.validate(Schemas.kpj90, source)) { + if(!SchemaValidators.kpj(source)) { + if(!SchemaValidators.kpj90(source)) { // If the legacy schema also does not validate, then we will only report // the errors against the modern schema - throw new Error(ajv.errorsText()); + throw new Error((SchemaValidators.kpj).errors); } } } @@ -62,8 +58,10 @@ export class KPJFileReader { if(result.options.version == '2.0') { result.options.buildPath = (project.Options?.BuildPath || result.options.buildPath).replace(/\\/g, '/'); result.options.sourcePath = (project.Options?.SourcePath || result.options.sourcePath).replace(/\\/g, '/'); + result.options.skipMetadataFiles = this.boolFromString(project.Options?.SkipMetadataFiles, false); } else { result.options.buildPath = (project.Options?.BuildPath || '').replace(/\\/g, '/'); + result.options.skipMetadataFiles = this.boolFromString(project.Options?.SkipMetadataFiles, true); } result.options.checkFilenameConventions = this.boolFromString(project.Options?.CheckFilenameConventions, false); result.options.compilerWarningsAsErrors = this.boolFromString(project.Options?.CompilerWarningsAsErrors, false); @@ -75,7 +73,6 @@ export class KPJFileReader { if(result.options.version == '1.0') { this.transformFilesVersion10(project, result); - result.addMetadataFile(); } else { result.populateFiles(); } diff --git a/common/web/types/src/kpj/kpj-file.ts b/common/web/types/src/kpj/kpj-file.ts index 317f24d013c..1c7da704378 100644 --- a/common/web/types/src/kpj/kpj-file.ts +++ b/common/web/types/src/kpj/kpj-file.ts @@ -20,6 +20,7 @@ export interface KPJFileOptions { SourcePath?: string; // default '' in 1.0, '$PROJECTPATH/source' in 2.0 CompilerWarningsAsErrors?: string; // default False WarnDeprecatedCode?: string; // default True + SkipMetadataFiles?: string; // default True for 1.0, False for 2.0 CheckFilenameConventions?: string; // default False ProjectType?: 'keyboard' | 'lexicalmodel'; // default 'keyboard' Version?: '1.0' | '2.0'; // default 1.0 diff --git a/common/web/types/src/kvk/kvk-file.ts b/common/web/types/src/kvk/kvk-file.ts index ca3f522e72d..ff99adca5df 100644 --- a/common/web/types/src/kvk/kvk-file.ts +++ b/common/web/types/src/kvk/kvk-file.ts @@ -49,6 +49,7 @@ export interface BUILDER_KVK_KEY { export const BUILDER_KVK_KEY_Size = 9; // size of fixed elements of BUILDER_KVK_KEY export const enum BUILDER_KVK_HEADER_FLAGS { + kvkhNone = 0x00, // no flags kvkh102 = 0x01, kvkhDisplayUnderlying = 0x02, kvkhUseUnderlying = 0x04, @@ -122,4 +123,4 @@ export default class KVKFile { }); } -}; \ No newline at end of file +}; diff --git a/common/web/types/src/kvk/kvks-file-reader.ts b/common/web/types/src/kvk/kvks-file-reader.ts index c9808d0b934..70967df0538 100644 --- a/common/web/types/src/kvk/kvks-file-reader.ts +++ b/common/web/types/src/kvk/kvks-file-reader.ts @@ -1,12 +1,10 @@ import * as xml2js from 'xml2js'; import KVKSourceFile from './kvks-file.js'; -import { default as AjvModule } from 'ajv'; -const Ajv = AjvModule.default; // The actual expected Ajv type. import { boxXmlArray } from '../util/util.js'; import { DEFAULT_KVK_FONT, VisualKeyboard, VisualKeyboardHeaderFlags, VisualKeyboardKey, VisualKeyboardKeyFlags, VisualKeyboardLegalShiftStates, VisualKeyboardShiftState } from './visual-keyboard.js'; import { USVirtualKeyCodes } from '../consts/virtual-key-constants.js'; import { BUILDER_KVK_HEADER_VERSION, KVK_HEADER_IDENTIFIER_BYTES } from './kvk-file.js'; -import Schemas from '../schemas.js'; +import SchemaValidators from '../schema-validators.js'; export default class KVKSFileReader { @@ -85,9 +83,8 @@ export default class KVKSFileReader { } public validate(source: KVKSourceFile): void { - const ajv = new Ajv(); - if(!ajv.validate(Schemas.kvks, source)) { - throw new Error(ajv.errorsText()); + if(!SchemaValidators.kvks(source)) { + throw new Error((SchemaValidators.kvks).errorsText()); } } 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 3fdd9a74025..5e7c1ab2d30 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 @@ -1,13 +1,11 @@ import * as xml2js from 'xml2js'; -import { LDMLKeyboardXMLSourceFile, LKImport } from './ldml-keyboard-xml.js'; -import { default as AjvModule } from 'ajv'; -const Ajv = AjvModule.default; // The actual expected Ajv type. +import { LDMLKeyboardXMLSourceFile, LKImport, ImportStatus } from './ldml-keyboard-xml.js'; import { boxXmlArray } from '../util/util.js'; import { CompilerCallbacks } from '../util/compiler-interfaces.js'; import { constants } from '@keymanapp/ldml-keyboard-constants'; import { CommonTypesMessages } from '../util/common-events.js'; import { LDMLKeyboardTestDataXMLSourceFile, LKTTest, LKTTests } from './ldml-keyboard-testdata-xml.js'; -import Schemas from '../schemas.js'; +import SchemaValidators from '../schema-validators.js'; interface NameAndProps { '$'?: any; // content @@ -49,11 +47,18 @@ export class LDMLKeyboardXMLSourceFileReader { if (!source.keyboard3.keys.import) { source.keyboard3.keys.import = []; } + if (!source.keyboard3.forms) { + source.keyboard3.forms = { + form: [], + }; + } + if (!source.keyboard3.forms.import) { + source.keyboard3.forms.import = []; + } } boxXmlArray(source?.keyboard3, 'layers'); boxXmlArray(source?.keyboard3?.displays, 'display'); boxXmlArray(source?.keyboard3?.names, 'name'); - boxXmlArray(source?.keyboard3?.vkeys, 'vkey'); boxXmlArray(source?.keyboard3?.keys, 'key'); boxXmlArray(source?.keyboard3?.keys, 'flicks'); boxXmlArray(source?.keyboard3?.locales, 'locale'); @@ -68,6 +73,12 @@ export class LDMLKeyboardXMLSourceFileReader { } } } + if(source?.keyboard3?.forms?.form) { + boxXmlArray(source?.keyboard3?.forms, 'form'); + for(let form of source?.keyboard3?.forms?.form) { + boxXmlArray(form, 'scanCodes'); + } + } if(source?.keyboard3?.keys?.flicks) { for(let flicks of source?.keyboard3?.keys?.flicks) { boxXmlArray(flicks, 'flick'); @@ -149,7 +160,15 @@ export class LDMLKeyboardXMLSourceFileReader { if (!this.resolveOneImport(obj, subtag, { base: constants.cldr_import_base, path: constants.cldr_implied_keys_import - })) { + }, true)) { + return false; + } + } else if (subtag === 'forms') { + // + if (!this.resolveOneImport(obj, subtag, { + base: constants.cldr_import_base, + path: constants.cldr_implied_forms_import + }, true)) { return false; } } @@ -160,9 +179,10 @@ export class LDMLKeyboardXMLSourceFileReader { * @param obj the object being imported into * @param subtag obj's element tag, e.g. `keys` * @param asImport the import structure + * @param implied true if it is an implied import * @returns true on success, false on failure */ - private resolveOneImport(obj: any, subtag: string, asImport: LKImport) : boolean { + private resolveOneImport(obj: any, subtag: string, asImport: LKImport, implied? : boolean) : boolean { const { base, path } = asImport; if (base !== constants.cldr_import_base) { this.callbacks.reportMessage(CommonTypesMessages.Error_ImportInvalidBase({base, path, subtag})); @@ -189,12 +209,20 @@ export class LDMLKeyboardXMLSourceFileReader { // pull all children of importXml[subtag] into obj for (const subsubtag of Object.keys(importRootNode).reverse()) { // e.g. const subsubval = importRootNode[subsubtag]; + const basePath = `${base}/${path}`; if (!Array.isArray(subsubval)) { // This is somewhat of an internal error, indicating that a non-mergeable XML file was imported // Not exercisable with the standard LDML imports. this.callbacks.reportMessage(CommonTypesMessages.Error_ImportMergeFail({base, path, subtag, subsubtag})); return false; } + // Mark all children as an import + subsubval.forEach(o => o[ImportStatus.import] = basePath); + if (implied) { + // mark all children as an implied import + subsubval.forEach(o => o[ImportStatus.impliedImport] = basePath); + } + if (!obj[subsubtag]) { obj[subsubtag] = []; // start with empty array } @@ -207,9 +235,8 @@ export class LDMLKeyboardXMLSourceFileReader { * @returns true if valid, false if invalid */ public validate(source: LDMLKeyboardXMLSourceFile | LDMLKeyboardTestDataXMLSourceFile): boolean { - const ajv = new Ajv(); - if(!ajv.validate(Schemas.ldmlKeyboard3, source)) { - for (let err of ajv.errors) { + if(!SchemaValidators.ldmlKeyboard3(source)) { + for (let err of (SchemaValidators.ldmlKeyboard3).errors) { this.callbacks.reportMessage(CommonTypesMessages.Error_SchemaValidationError({ instancePath: err.instancePath, keyword: err.keyword, diff --git a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts index 629b8e8aa24..83720312dd8 100644 --- a/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts +++ b/common/web/types/src/ldml-keyboard/ldml-keyboard-xml.ts @@ -12,7 +12,7 @@ export interface LDMLKeyboardXMLSourceFile { * -- the root element. */ keyboard3: LKKeyboard; -} +}; export interface LKKeyboard { locale?: string; @@ -24,9 +24,9 @@ export interface LKKeyboard { names?: LKNames; settings?: LKSettings; keys?: LKKeys; + forms?: LKForms; displays?: LKDisplays; layers?: LKLayers[]; - vkeys?: LKVkeys; variables?: LKVariables; transforms?: LKTransforms[]; }; @@ -128,15 +128,6 @@ export interface LKRow { keys?: string; }; -export interface LKVkeys { - vkey?: LKVkey[]; -}; - -export interface LKVkey { - from?: string; - to?: string; -}; - export interface LKVariables { string?: LKString[]; set?: LKSet[]; @@ -193,3 +184,36 @@ export interface LKDisplays { display?: LKDisplay[]; displayOptions?: LKDisplayOptions; }; + +export interface LKForms { + form?: LKForm[]; +}; + +export interface LKForm { + id?: string; + scanCodes?: LKScanCodes[]; +}; + +export interface LKScanCodes { + codes?: string; +}; + +/** + * Utilities for determining the import status of items + */ +export class ImportStatus { + /** item came in via implied (spec based) import, such as keys-Latn-implied.xml */ + static impliedImport = Symbol('LDML implied import'); + /** item came in via import */ + static import = Symbol('LDML import'); + + /** @returns true if the object was loaded through an implied import */ + static isImpliedImport(o : any) : boolean { + return o && !!o[ImportStatus.impliedImport]; + } + /** @returns true if the object was loaded through an explicit import */ + static isImport(o : any) : boolean { + return o && !!o[ImportStatus.import]; + } +}; + diff --git a/common/web/types/src/ldml-keyboard/pattern-parser.ts b/common/web/types/src/ldml-keyboard/pattern-parser.ts index f6013248d26..b0d6fb07962 100644 --- a/common/web/types/src/ldml-keyboard/pattern-parser.ts +++ b/common/web/types/src/ldml-keyboard/pattern-parser.ts @@ -65,6 +65,15 @@ export class MarkerParser { /** Max count of markers */ public static readonly MAX_MARKER_COUNT = constants.marker_max_count; + private static anyMarkerMatch() : string { + const start = (`0000` + (this.MIN_MARKER_INDEX).toString(16)).slice(-4); + const end = (`0000` + (this.MAX_MARKER_INDEX).toString(16)).slice(-4); + return `${this.SENTINEL}${this.MARKER_CODE}[\\u${start}-\\u${end}]`; + } + + /** Expression that matches any marker */ + public static readonly ANY_MARKER_MATCH = MarkerParser.anyMarkerMatch(); + /** * Pattern for matching a marker reference, OR the special marker \m{.} */ @@ -91,10 +100,13 @@ export class MarkerParser { } /** @returns all marker strings as sentinel values */ - public static toSentinelString(s: string, markers?: OrderedStringList) : string { + public static toSentinelString(s: string, markers?: OrderedStringList, forMatch?: boolean) : string { if (!s) return s; return s.replaceAll(this.REFERENCE, (sub, arg) => { if (arg === MarkerParser.ANY_MARKER_ID) { + if (forMatch) { + return this.ANY_MARKER_MATCH; + } return MarkerParser.markerOutput(MarkerParser.ANY_MARKER_INDEX); } if (!markers) { @@ -103,10 +115,10 @@ export class MarkerParser { const order = markers.getItemOrder(arg); if (order === -1) { throw RangeError(`Internal Error: Could not find marker \\m{${arg}}`); - } else if(order >= MarkerParser.MAX_MARKER_INDEX) { + } else if(order > MarkerParser.MAX_MARKER_INDEX) { throw RangeError(`Internal Error: marker \\m{${arg}} has out of range index ${order}`); } else { - return MarkerParser.markerOutput(order+1); + return MarkerParser.markerOutput(order + 1); } }); } diff --git a/common/web/types/src/main.ts b/common/web/types/src/main.ts index 430b88cdc3d..108a2d57c29 100644 --- a/common/web/types/src/main.ts +++ b/common/web/types/src/main.ts @@ -25,7 +25,9 @@ export { defaultCompilerOptions, CompilerBaseOptions, CompilerCallbacks, Compile CompilerErrorSeverity, CompilerPathCallbacks, CompilerFileSystemCallbacks, CompilerCallbackOptions, CompilerError, CompilerMessageSpec, compilerErrorSeverity, CompilerErrorMask, CompilerFileCallbacks, compilerErrorSeverityName, compilerExceptionToString, compilerErrorFormatCode, - compilerLogLevelToSeverity, CompilerLogLevel, compilerEventFormat, ALL_COMPILER_LOG_LEVELS } from './util/compiler-interfaces.js'; + compilerLogLevelToSeverity, CompilerLogLevel, compilerEventFormat, ALL_COMPILER_LOG_LEVELS, + ALL_COMPILER_LOG_FORMATS, CompilerLogFormat, + } from './util/compiler-interfaces.js'; export { CommonTypesMessages } from './util/common-events.js'; export * as TouchLayout from './keyman-touch-layout/keyman-touch-layout-file.js'; @@ -45,4 +47,5 @@ export * as KeymanFileTypes from './util/file-types.js'; export * as Osk from './osk/osk.js'; -export * as Schemas from './schemas.js'; \ No newline at end of file +export * as Schemas from './schemas.js'; +export * as SchemaValidators from './schema-validators.js'; \ No newline at end of file diff --git a/common/web/types/src/osk/osk.ts b/common/web/types/src/osk/osk.ts index 620712baeb2..9c7d1bf04c4 100644 --- a/common/web/types/src/osk/osk.ts +++ b/common/web/types/src/osk/osk.ts @@ -1,8 +1,6 @@ import { TouchLayoutFile, TouchLayoutFlick, TouchLayoutKey, TouchLayoutPlatform, TouchLayoutSubKey } from "src/keyman-touch-layout/keyman-touch-layout-file.js"; import { VisualKeyboard } from "../kvk/visual-keyboard.js"; -import { default as AjvModule } from 'ajv'; -import Schemas from "../schemas.js"; -const Ajv = AjvModule.default; // The actual expected Ajv type. +import SchemaValidators from "../schema-validators.js"; export interface StringRefUsage { filename: string; @@ -24,11 +22,10 @@ export interface StringResult { export type PuaMap = {[index:string]: string}; export function parseMapping(mapping: any) { - const ajv = new Ajv(); - if(!ajv.validate(Schemas.displayMap, mapping)) + if(!SchemaValidators.displayMap(mapping)) /* c8 ignore next 3 */ { - throw new Error(ajv.errorsText()); + throw new Error((SchemaValidators.displayMap).errorsText()); } let map: PuaMap = {}; diff --git a/common/web/types/src/package/kmp-json-file.ts b/common/web/types/src/package/kmp-json-file.ts index 913bc98f06e..ae88b5ba3e9 100644 --- a/common/web/types/src/package/kmp-json-file.ts +++ b/common/web/types/src/package/kmp-json-file.ts @@ -6,6 +6,7 @@ export interface KmpJsonFile { lexicalModels?: KmpJsonFileLexicalModel[]; startMenu?: KmpJsonFileStartMenu; keyboards?: KmpJsonFileKeyboard[]; + relatedPackages?: KmpJsonRelatedPackage[]; } export interface KmpJsonFileSystem { @@ -16,6 +17,8 @@ export interface KmpJsonFileSystem { export interface KmpJsonFileOptions { readmeFile?: string; graphicFile?: string; + licenseFile?: string; + welcomeFile?: string; executeProgram?: string; msiFilename?: string; msiOptions?: string; @@ -27,6 +30,11 @@ export interface KmpJsonFileInfo { name?: KmpJsonFileInfoItem; copyright?: KmpJsonFileInfoItem; author?: KmpJsonFileInfoItem; + /** + * A Markdown description of the keyboard, intended for use in websites + * referencing the keyboard + */ + description?: KmpJsonFileInfoItem; } export interface KmpJsonFileInfoItem { @@ -59,6 +67,15 @@ export interface KmpJsonFileKeyboard { displayFont?: string; rtl?: boolean; languages?: KmpJsonFileLanguage[]; + examples?: KmpJsonFileExample[]; + /** + * array of web font alternatives for OSK. should be same font data as oskFont + */ + webOskFonts?: string[]; + /** + * array of web font alternatives for display. should be same font data as displayFont + */ + webDisplayFonts?: string[]; } export interface KmpJsonFileStartMenu { @@ -74,3 +91,37 @@ export interface KmpJsonFileStartMenuItem { icon?: string; location?: string; } + +export interface KmpJsonFileExample { + /** + * BCP 47 identifier for the example + */ + id: string; + /** + * A space-separated list of keys. + * - modifiers indicated with "+" + * - spacebar is "space" + * - plus key is "shift+=" or "plus" on US English (all other punctuation as per key cap). + * - Hardware modifiers are: "shift", "ctrl", "alt", "left-ctrl", + * "right-ctrl", "left-alt", "right-alt" + * - Key caps should generally be their character for desktop (Latin script + * case insensitive), or the actual key cap for touch + * - Caps Lock should be indicated with "caps-on", "caps-off" + * + * e.g. "shift+a b right-alt+c space plus z z z" represents something like: "Ab{AltGr+C} +zzz" + */ + keys: string; + /** + * The text that would be generated by typing those keys + */ + text?: string; + /** + * A short description of what the text means or represents + */ + note?: string; +} + +export interface KmpJsonRelatedPackage { + id: string; + relationship: "deprecates" | "related"; +} diff --git a/common/web/types/src/package/kps-file.ts b/common/web/types/src/package/kps-file.ts index 7ff77d09846..7c7ba12c879 100644 --- a/common/web/types/src/package/kps-file.ts +++ b/common/web/types/src/package/kps-file.ts @@ -17,40 +17,44 @@ export interface KpsPackage { /** * -- the root element. */ - package: KpsFile; + Package: KpsFile; } export interface KpsFile { - system: KpsFileSystem; - options: KpsFileOptions; - info?: KpsFileInfo; - files?: KpsFileContentFiles; - keyboards?: KpsFileKeyboards; - lexicalModels?: KpsFileLexicalModels; - startMenu?: KpsFileStartMenu; - strings?: KpsFileStrings; + System: KpsFileSystem; + Options: KpsFileOptions; + Info?: KpsFileInfo; + Files?: KpsFileContentFiles; + Keyboards?: KpsFileKeyboards; + LexicalModels?: KpsFileLexicalModels; + StartMenu?: KpsFileStartMenu; + Strings?: KpsFileStrings; + RelatedPackages?: KpsFileRelatedPackages; } export interface KpsFileSystem { - keymanDeveloperVersion: string; - fileVersion: string; + KeymanDeveloperVersion: string; + FileVersion: string; } export interface KpsFileOptions { - followKeyboardVersion?: string; - readMeFile?: string; - graphicFile?: string; - executeProgram?: string; - msiFileName?: string; - msiOptions?: string; + FollowKeyboardVersion?: string; + ReadMeFile?: string; + GraphicFile?: string; + LicenseFile?: string; + WelcomeFile?: string; + ExecuteProgram?: string; + MsiFileName?: string; + MsiOptions?: string; } export interface KpsFileInfo { - name?: KpsFileInfoItem; - copyright?: KpsFileInfoItem; - author?: KpsFileInfoItem; - webSite?: KpsFileInfoItem; - version?: KpsFileInfoItem; + Name?: KpsFileInfoItem; + Copyright?: KpsFileInfoItem; + Author?: KpsFileInfoItem; + WebSite?: KpsFileInfoItem; + Version?: KpsFileInfoItem; + Description?: KpsFileInfoItem; } export interface KpsFileInfoItem { @@ -59,28 +63,31 @@ export interface KpsFileInfoItem { } export interface KpsFileContentFiles { - file: KpsFileContentFile[] | KpsFileContentFile; + File: KpsFileContentFile[] | KpsFileContentFile; } export interface KpsFileContentFile { - name: string; - description: string; - copyLocation: string; - fileType: string; + Name: string; + /** @deprecated */ + Description: string; + /** @deprecated */ + CopyLocation: string; + /** @deprecated */ + FileType: string; } export interface KpsFileLexicalModel { - name: string; - iD: string; - languages: KpsFileLanguages; + Name: string; + ID: string; + Languages: KpsFileLanguages; } export interface KpsFileLexicalModels { - lexicalModel: KpsFileLexicalModel[] | KpsFileLexicalModel; + LexicalModel: KpsFileLexicalModel[] | KpsFileLexicalModel; } export interface KpsFileLanguages { - language: KpsFileLanguage[] | KpsFileLanguage; + Language: KpsFileLanguage[] | KpsFileLanguage; } export interface KpsFileLanguage { @@ -88,39 +95,116 @@ export interface KpsFileLanguage { $: { ID: string } } +export interface KpsFileRelatedPackages { + RelatedPackage: KpsFileRelatedPackage | KpsFileRelatedPackage[]; +} + +export interface KpsFileRelatedPackage { + $: { + ID: string; + /** + * relationship between this package and the related package, "related" is default if not specified + */ + Relationship?: "deprecates"; + } +} + export interface KpsFileKeyboard { - name: string; /// the descriptive name of the keyboard - iD: string; /// the keyboard identifier, equal to the basename of the keyboard file sans extension - version: string; - oSKFont?: string; - displayFont?: string; - rTL?: string; - languages?: KpsFileLanguages; + Name: string; /// the descriptive name of the keyboard + ID: string; /// the keyboard identifier, equal to the basename of the keyboard file sans extension + Version: string; + OSKFont?: string; + DisplayFont?: string; + RTL?: string; + Languages?: KpsFileLanguages; + Examples?: KpsFileLanguageExamples; + /** + * array of web font alternatives for OSK. should be same font data as oskFont + */ + WebOSKFonts?: KpsFileFonts; + /** + * array of web font alternatives for display. should be same font data as displayFont + */ + WebDisplayFonts?: KpsFileFonts; +} + +export interface KpsFileFonts { + Font: KpsFileFont[] | KpsFileFont; +} + +export interface KpsFileFont { + $: { + Filename: string; + } } export interface KpsFileKeyboards { - keyboard: KpsFileKeyboard[] | KpsFileKeyboard; + Keyboard: KpsFileKeyboard[] | KpsFileKeyboard; } export interface KpsFileStartMenu { - folder?: string; - addUninstallEntry?: string; - items?: KpsFileStartMenuItems; + Folder?: string; + AddUninstallEntry?: string; + Items?: KpsFileStartMenuItems; } export interface KpsFileStartMenuItem { - name: string; - fileName: string; - arguments?: string; - icon?: string; - location?: string; + Name: string; + FileName: string; + Arguments?: string; + Icon?: string; + Location?: string; } export interface KpsFileStartMenuItems { - item: KpsFileStartMenuItem[] | KpsFileStartMenuItem; + Item: KpsFileStartMenuItem[] | KpsFileStartMenuItem; } export interface KpsFileStrings { - //TODO: validate this structure - string: string[] | string; + String: KpsFileString[] | KpsFileString; +} + +export interface KpsFileString { + $: { + Name: string; + Value: string; + } +} + +export interface KpsFileLanguageExamples { + Example: KpsFileLanguageExample | KpsFileLanguageExample[]; +} + +/** + * An example key sequence intended to demonstrate how the keyboard works + */ +export interface KpsFileLanguageExample { + $: { + /** + * BCP 47 identifier for the example + */ + ID: string; + /** + * A space-separated list of keys. + * - modifiers indicated with "+" + * - spacebar is "space" + * - plus key is "shift+=" or "plus" on US English (all other punctuation as per key cap). + * - Hardware modifiers are: "shift", "ctrl", "alt", "left-ctrl", + * "right-ctrl", "left-alt", "right-alt" + * - Key caps should generally be their character for desktop (Latin script + * case insensitive), or the actual key cap for touch + * - Caps Lock should be indicated with "caps-on", "caps-off" + * + * e.g. "shift+a b right-alt+c space plus z z z" represents something like: "Ab{AltGr+C} +zzz" + */ + Keys: string; + /** + * The text that would be generated by typing those keys + */ + Text?: string; + /** + * A short description of what the text means or represents + */ + Note?: string; + } } diff --git a/common/web/types/src/schema-validators.ts b/common/web/types/src/schema-validators.ts new file mode 100644 index 00000000000..07bcc032f16 --- /dev/null +++ b/common/web/types/src/schema-validators.ts @@ -0,0 +1,25 @@ +import kpj from './schemas/kpj.schema.validator.mjs'; +import kpj90 from './schemas/kpj-9.0.schema.validator.mjs'; +import kvks from './schemas/kvks.schema.validator.mjs'; +import ldmlKeyboard3 from './schemas/ldml-keyboard3.schema.validator.mjs'; +import ldmlKeyboardTest3 from './schemas/ldml-keyboardtest3.schema.validator.mjs'; +import displayMap from './schemas/displaymap.schema.validator.mjs'; +import touchLayoutClean from './schemas/keyman-touch-layout.clean.spec.validator.mjs'; +import touchLayout from './schemas/keyman-touch-layout.spec.validator.mjs'; +import keyboard_info from './schemas/keyboard_info.schema.validator.mjs'; + +// How to use: https://ajv.js.org/standalone.html#using-the-validation-function-s +// See also existing uses (search for `SchemaValidators`) for examples. +const SchemaValidators = { + kpj, + kpj90, + kvks, + ldmlKeyboard3, + ldmlKeyboardTest3, + displayMap, + touchLayoutClean, + touchLayout, + keyboard_info, +}; + +export default SchemaValidators; diff --git a/common/web/types/src/schemas.ts b/common/web/types/src/schemas.ts index 3d1ce0cbf12..8140f3c0702 100644 --- a/common/web/types/src/schemas.ts +++ b/common/web/types/src/schemas.ts @@ -7,6 +7,7 @@ import ldmlKeyboardTest3 from './schemas/ldml-keyboardtest3.schema.js'; import displayMap from './schemas/displaymap.schema.js'; import touchLayoutClean from './schemas/keyman-touch-layout.clean.spec.js'; import touchLayout from './schemas/keyman-touch-layout.spec.js'; +import keyboard_info from './schemas/keyboard_info.schema.js'; const Schemas = { kpj, @@ -17,6 +18,7 @@ const Schemas = { displayMap, touchLayoutClean, touchLayout, + keyboard_info, }; export default Schemas; diff --git a/common/web/types/src/util/compiler-interfaces.ts b/common/web/types/src/util/compiler-interfaces.ts index 3283fb25981..b27a170ab34 100644 --- a/common/web/types/src/util/compiler-interfaces.ts +++ b/common/web/types/src/util/compiler-interfaces.ts @@ -74,11 +74,20 @@ export class CompilerError { * @param filename * @returns */ - static formatFilename(filename: string): string { + static formatFilename(filename: string, options?: { + fullPath?: boolean, + forwardSlashes?: boolean + }): string { if(!filename) { return ''; } + if(options?.fullPath) { + return options?.forwardSlashes ? + filename.replaceAll(/\\/g, '/') : + filename.replaceAll(/\//g, '\\'); + } + let x = filename.lastIndexOf('/'); if(x < 0) { x = filename.lastIndexOf('\\'); @@ -239,6 +248,7 @@ export interface CompilerFileSystemCallbacks { export interface CompilerCallbackOptions { logLevel?: CompilerLogLevel; + logFormat?: CompilerLogFormat; color?: boolean; // null or undefined == use console default compilerWarningsAsErrors?: boolean; }; @@ -355,6 +365,10 @@ export interface CompilerBaseOptions { * all messages are still reported to the internal log) */ logLevel?: CompilerLogLevel; + /** + * Format of output for log to console + */ + logFormat?: CompilerLogFormat; /** * Optional output file for activities that generate output */ @@ -390,6 +404,7 @@ export interface CompilerOptions extends CompilerBaseOptions { export const defaultCompilerOptions: CompilerOptions = { logLevel: 'info', + logFormat: 'formatted', // outFile: (undefined) saveDebug: false, shouldAddCompilerVersion: true, @@ -443,3 +458,11 @@ export const compilerLogLevelToSeverity: {[index in CompilerLogLevel]: number} = 'info': CompilerErrorSeverity.Info, 'debug': CompilerErrorSeverity.Info }; + +export const ALL_COMPILER_LOG_FORMATS = [ + 'tsv', + 'formatted' +] as const; + +type CompilerLogFormatTuple = typeof ALL_COMPILER_LOG_FORMATS; +export type CompilerLogFormat = CompilerLogFormatTuple[number]; diff --git a/common/web/types/src/util/file-types.ts b/common/web/types/src/util/file-types.ts index ebad759638a..e3d3bd54523 100644 --- a/common/web/types/src/util/file-types.ts +++ b/common/web/types/src/util/file-types.ts @@ -10,9 +10,7 @@ export const enum Source { LdmlKeyboard = '.xml', // Warning, also other possible uses Package = '.kps', VisualKeyboard = '.kvks', - TouchLayout = '.keyman-touch-layout', - KeyboardInfo = '.keyboard_info', - ModelInfo = '.model_info', + TouchLayout = '.keyman-touch-layout' }; /** @@ -26,9 +24,7 @@ export const ALL_SOURCE: ReadonlyArray = [ Source.LdmlKeyboard, Source.Package, Source.VisualKeyboard, - Source.TouchLayout, - Source.KeyboardInfo, - Source.ModelInfo, + Source.TouchLayout ] as const; /** diff --git a/common/web/types/test/helpers/index.ts b/common/web/types/test/helpers/index.ts index e92b9656bfa..9b2be1d6056 100644 --- a/common/web/types/test/helpers/index.ts +++ b/common/web/types/test/helpers/index.ts @@ -1,5 +1,5 @@ -import path from "path"; -import fs from "fs"; +import * as path from "path"; +import * as fs from "fs"; import { fileURLToPath } from "url"; /** 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 2c3a5c383b9..12c1e7a134e 100644 --- a/common/web/types/test/kpj/test-kpj-file-reader.ts +++ b/common/web/types/test/kpj/test-kpj-file-reader.ts @@ -23,6 +23,7 @@ describe('kpj-file-reader', function () { assert.equal(kpj.KeymanDeveloperProject.Options.CompilerWarningsAsErrors, 'True'); assert.equal(kpj.KeymanDeveloperProject.Options.ProjectType, 'keyboard'); assert.equal(kpj.KeymanDeveloperProject.Options.WarnDeprecatedCode, 'True'); + assert.isUndefined(kpj.KeymanDeveloperProject.Options.SkipMetadataFiles); // because this is a 1.0 version file assert.isUndefined(kpj.KeymanDeveloperProject.Options.Version); assert.lengthOf(kpj.KeymanDeveloperProject.Files.File, 21); @@ -73,9 +74,10 @@ describe('kpj-file-reader', function () { assert.isTrue(project.options.compilerWarningsAsErrors); assert.equal(project.options.projectType, KeymanDeveloperProjectType.Keyboard); assert.isTrue(project.options.warnDeprecatedCode); + assert.isTrue(project.options.skipMetadataFiles); assert.equal(project.options.version, '1.0'); - assert.lengthOf(project.files, 3); + assert.lengthOf(project.files, 2); let f: KeymanDeveloperProjectFile10 = project.files[0]; assert.equal(f.id, 'id_f347675c33d2e6b1c705c787fad4941a'); diff --git a/common/web/types/test/ldml-keyboard/test-ldml-keyboard-xml-reader.ts b/common/web/types/test/ldml-keyboard/test-ldml-keyboard-xml-reader.ts index 4da7a006636..43429676a0a 100644 --- a/common/web/types/test/ldml-keyboard/test-ldml-keyboard-xml-reader.ts +++ b/common/web/types/test/ldml-keyboard/test-ldml-keyboard-xml-reader.ts @@ -1,8 +1,9 @@ -import { LKKey } from './../../src/ldml-keyboard/ldml-keyboard-xml.js'; +import { LKKey, ImportStatus } from './../../src/ldml-keyboard/ldml-keyboard-xml.js'; import 'mocha'; import {assert} from 'chai'; import { CommonTypesMessages } from '../../src/util/common-events.js'; import { testReaderCases } from '../helpers/reader-callback-test.js'; +import { CLDRScanToVkey, CLDRScanToKeyMap, USVirtualKeyCodes } from '../../src/consts/virtual-key-constants.js'; function pluckKeysFromKeybag(keys: LKKey[], ids: string[]) { return keys.filter(({id}) => ids.indexOf(id) !== -1); @@ -40,6 +41,9 @@ describe('ldml keyboard xml reader tests', function () { {id: 'b', to: 'b'}, {id: 'c', to: 'c'}, ]); + // all of the keys are implied imports here + assert.isTrue(ImportStatus.isImpliedImport(source?.keyboard3?.keys.key.find(({id}) => id === 'a'))); + assert.isTrue(ImportStatus.isImport(source?.keyboard3?.keys.key.find(({id}) => id === 'a'))); }, }, { @@ -81,6 +85,15 @@ describe('ldml keyboard xml reader tests', function () { {id: 'zz', to: 'zz'}, // new key {id: 'hash', to: '##'}, // override ]); + // 'a' is an implied import + assert.isTrue(ImportStatus.isImpliedImport(k.find(({id}) => id === 'a'))); + assert.isTrue(ImportStatus.isImport(k.find(({id}) => id === 'a'))); + // 'hash' is an import but not implied + assert.isFalse(ImportStatus.isImpliedImport(k.find(({id}) => id === 'hash'))); + assert.isTrue(ImportStatus.isImport(k.find(({id}) => id === 'hash'))); + // 'zz' is not imported + assert.isFalse(ImportStatus.isImpliedImport(k.find(({id}) => id === 'zz'))); + assert.isFalse(ImportStatus.isImport(k.find(({id}) => id === 'zz'))); }, }, { @@ -129,3 +142,41 @@ describe('ldml keyboard xml reader tests', function () { }, ]); }); + +describe('check scan code routines', () => { + it('should be able to detect bad single scancodes', () => { + const badScans = new Set(); + const ckey = CLDRScanToVkey(0xFF, badScans); + assert.isUndefined(ckey, `expected undefined 0xFF`); + assert.equal(badScans.size, 1); + assert.sameDeepMembers(Array.from(badScans.values()), [0xFF]); + }); + it('should be able to detect bad list scancodes', () => { + const badScans = new Set(); + const ckeys = CLDRScanToKeyMap([[0x02, 0xFF]], badScans); + assert.sameDeepMembers(ckeys, [[49, undefined]]); + assert.equal(badScans.size, 1); + assert.sameDeepMembers(Array.from(badScans.values()), [0xFF]); + }); + it('CLDRScanToUSVirtualKeyCodes should be 1:1', () => { + const vkeyToScan = new Map(); + // check all scancodes + for (let scan = 0; scan <= 0xFF; scan++) { + const vkey = CLDRScanToVkey(scan); + if (vkey === undefined) { + // not mapped, which is OK + continue; + } + if (scan === 0x56 || scan === 0x7D) { + // These both can map to this scancode + assert.equal(vkey, USVirtualKeyCodes.K_oE2); + } else { + // don't check those exceptions + assert.isFalse(vkeyToScan.has(vkey), + `vkey ${vkey} mapped from more than one scancode: ${Number(vkeyToScan.get(vkey)).toString(16)} and ${Number(scan).toString(16)}`); + } + // do make sure nothing else maps to that vkey + vkeyToScan.set(vkey, scan); + } + }); +}); diff --git a/common/web/types/test/ldml-keyboard/test-pattern-parser.ts b/common/web/types/test/ldml-keyboard/test-pattern-parser.ts index e7f06a1c557..0274f2a5c27 100644 --- a/common/web/types/test/ldml-keyboard/test-pattern-parser.ts +++ b/common/web/types/test/ldml-keyboard/test-pattern-parser.ts @@ -75,6 +75,7 @@ describe('Test of Pattern Parsers', () => { 'a': 0, 'b': 1, 'c': 2, + 'zz': MarkerParser.MAX_MARKER_INDEX - 1, // this is an ordering, so needs to be -1 'zzz': 0x2FFFFF, }; const o = m[item]; @@ -103,6 +104,21 @@ describe('Test of Pattern Parsers', () => { markers ) ); + // verify the matching behavior of these + assert.isTrue(new RegExp(MarkerParser.toSentinelString(`^Q\\m{a}$`, markers, true), 'u') + .test(MarkerParser.toSentinelString(`Q\\m{a}`, markers, false)), `Q\\m{a} did not match`); + assert.isFalse(new RegExp(MarkerParser.toSentinelString(`^Q\\m{a}$`, markers, true), 'u') + .test(MarkerParser.toSentinelString(`Q\\m{b}`, markers, false)), `Q\\m{a} should not match Q\\m{b}`); + assert.isTrue(new RegExp(MarkerParser.toSentinelString(`^Q\\m{.}$`, markers, true), 'u') + .test(MarkerParser.toSentinelString(`Q\\m{a}`, markers, false)), `Q\\m{.} did not match Q\\m{a}`); + assert.isTrue(new RegExp(MarkerParser.toSentinelString(`^Q\\m{.}$`, markers, true), 'u') + .test(MarkerParser.toSentinelString(`Q\\m{zz}`, markers, false)), `Q\\m{.} did not match Q\\m{zz} (max marker)`); + assert.isFalse(new RegExp(MarkerParser.toSentinelString(`^Q\\m{.}$`, markers, true), 'u') + .test(MarkerParser.toSentinelString(`\\m{a}`, markers, false)), `Q\\m{.} did not match \\m{a}`); + assert.isTrue(new RegExp(MarkerParser.toSentinelString(`^\\m{.}$`, markers, true), 'u') + .test(MarkerParser.toSentinelString(`\\m{a}`, markers, false)), `\\m{.} did not match \\m{a}`); + assert.isFalse(new RegExp(MarkerParser.toSentinelString(`^\\m{.}$`, markers, true), 'u') + .test(MarkerParser.toSentinelString(`\\m{a}\\m{b}`, markers, false)), `\\m{.} did not match \\m{a}\\m{b}`); }); it('should match some marker constants', () => { assert.equal(constants.uc_sentinel, KMXFile.UC_SENTINEL); diff --git a/common/web/types/test/tsconfig.json b/common/web/types/test/tsconfig.json index c522129c99a..b034236bd75 100644 --- a/common/web/types/test/tsconfig.json +++ b/common/web/types/test/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../tsconfig.esm-base.json", + "extends": "../../../../tsconfig.base.json", "compilerOptions": { "rootDir": ".", @@ -7,7 +7,7 @@ "outDir": "../build/test", "baseUrl": ".", "strictNullChecks": false, // TODO: get rid of this as some point - "allowSyntheticDefaultImports": true // for ajv + "allowSyntheticDefaultImports": true }, "include": [ "**/test-*.ts", diff --git a/common/web/types/tools/formats.cjs b/common/web/types/tools/formats.cjs new file mode 100644 index 00000000000..82eca97ab2f --- /dev/null +++ b/common/web/types/tools/formats.cjs @@ -0,0 +1,10 @@ +/* + * This somewhat peculiar function is used in `build.sh configure` when + * precompiling the validators and makes it possible to use the extended formats + * in ajv-formats. + */ +function formats(ajv) { + require("ajv-formats")(ajv); +} + +module.exports = formats; diff --git a/common/web/types/tools/schema-bundler.js b/common/web/types/tools/schema-bundler.js new file mode 100644 index 00000000000..995befaec1f --- /dev/null +++ b/common/web/types/tools/schema-bundler.js @@ -0,0 +1,27 @@ +/* + * Bundle schema validation files (from .cjs) and make them available as ES modules + */ + +import esbuild from 'esbuild'; + +await esbuild.build({ + entryPoints: [ + 'obj/schemas/kpj.schema.validator.cjs', + 'obj/schemas/kpj-9.0.schema.validator.cjs', + 'obj/schemas/kvks.schema.validator.cjs', + 'obj/schemas/ldml-keyboard3.schema.validator.cjs', + 'obj/schemas/ldml-keyboardtest3.schema.validator.cjs', + 'obj/schemas/displaymap.schema.validator.cjs', + 'obj/schemas/keyman-touch-layout.clean.spec.validator.cjs', + 'obj/schemas/keyman-touch-layout.spec.validator.cjs', + 'obj/schemas/keyboard_info.schema.validator.cjs', + ], + bundle: true, + format: 'esm', + target: 'es2022', + outdir: 'src/schemas/', + sourcemap: false, + + // We want a .mjs extension to force node into ESM module mode + outExtension: { '.js': '.mjs' }, +}); diff --git a/common/web/types/tsconfig.json b/common/web/types/tsconfig.json index 5059030c0f8..be41a26729e 100644 --- a/common/web/types/tsconfig.json +++ b/common/web/types/tsconfig.json @@ -1,15 +1,15 @@ { - "extends": "../../../tsconfig.esm-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "build/src/", "rootDir": "src/", "baseUrl": ".", - "allowSyntheticDefaultImports": true, // for ajv - "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, }, "include": [ - "src/**/*.ts" + "src/**/*.ts", + "src/schemas/*.mjs", // Import the validators ], "references": [ { "path": "../keyman-version" }, diff --git a/common/web/utils/package.json b/common/web/utils/package.json index 210ac4b6be5..3f5729ec6a8 100644 --- a/common/web/utils/package.json +++ b/common/web/utils/package.json @@ -23,13 +23,13 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/resources-gosh": "*", "@keymanapp/keyman-version": "*", + "@keymanapp/resources-gosh": "*", + "@types/node": "^14.0.5", "c8": "^7.12.0", "chai": "^4.3.4", "mocha": "^10.0.0", "mocha-teamcity-reporter": "^4.0.0", - "@types/node": "^14.0.5", "typescript": "^4.9.5" }, "type": "module", diff --git a/common/windows/cpp/include/legacy_kmx_file.h b/common/windows/cpp/include/legacy_kmx_file.h index 6be4b3b9861..9b8f6a59fb1 100644 --- a/common/windows/cpp/include/legacy_kmx_file.h +++ b/common/windows/cpp/include/legacy_kmx_file.h @@ -74,8 +74,9 @@ #define VERSION_140 0x00000E00 #define VERSION_150 0x00000F00 #define VERSION_160 0x00001000 +#define VERSION_170 0x00001100 #define VERSION_MIN VERSION_50 -#define VERSION_MAX VERSION_160 +#define VERSION_MAX VERSION_170 /* Special flag for WM_CHAR/WM_KEY???/WM_SYSKEY???: says that key has been diff --git a/common/windows/delphi/ext/sentry/test/Makefile b/common/windows/delphi/ext/sentry/test/Makefile index 63fc664b011..b2e92bb8964 100644 --- a/common/windows/delphi/ext/sentry/test/Makefile +++ b/common/windows/delphi/ext/sentry/test/Makefile @@ -4,7 +4,8 @@ !include ..\..\..\Defines.mak -build: dirs # version.res manifest.res +build: dirs +# version.res manifest.res $(DELPHI_MSBUILD) SentryClientTest.dproj "/p:Platform=Win32" $(DELPHI_MSBUILD) SentryClientVclTest.dproj "/p:Platform=Win32" diff --git a/common/windows/delphi/general/utilfiletypes.pas b/common/windows/delphi/general/utilfiletypes.pas index bf1f09fbf40..56a718bcd06 100644 --- a/common/windows/delphi/general/utilfiletypes.pas +++ b/common/windows/delphi/general/utilfiletypes.pas @@ -62,14 +62,15 @@ TKMFileTypeInfo = record Ext_LexicalModelPackageSource = '.model.kps'; Ext_LexicalModelProject = '.model.kpj'; -const ExtFileTypes: array[0..13] of TKMFileTypeInfo = ( +const ExtFileTypes: array[0..14] of TKMFileTypeInfo = ( (Ext: Ext_KeymanSource; FileType: ftKeymanSource), (Ext: Ext_PackageSource; FileType: ftPackageSource), (Ext: Ext_KeymanFile; FileType: ftKeymanFile), (Ext: Ext_PackageFile; FileType: ftPackageFile), (Ext: '.ttf'; FileType: ftFont), (Ext: '.otf'; FileType: ftFont), - (Ext: '.fon'; FileType: ftFont), + (Ext: '.woff'; FileType: ftFont), + (Ext: '.woff2'; FileType: ftFont), (Ext: '.ttc'; FileType: ftFont), (Ext: Ext_VisualKeyboard; FileType: ftVisualKeyboard), (Ext: Ext_VisualKeyboardSource; FileType: ftVisualKeyboardSource), diff --git a/common/windows/delphi/keyboards/kmxfileconsts.pas b/common/windows/delphi/keyboards/kmxfileconsts.pas index e08feb65641..966dada8827 100644 --- a/common/windows/delphi/keyboards/kmxfileconsts.pas +++ b/common/windows/delphi/keyboards/kmxfileconsts.pas @@ -99,9 +99,10 @@ interface VERSION_140 = $00000E00; VERSION_150 = $00000F00; VERSION_160 = $00001000; + VERSION_170 = $00001100; VERSION_MIN = VERSION_50; - VERSION_MAX = VERSION_160; + VERSION_MAX = VERSION_170; VERSION_MASK_MINOR = $00FF; VERSION_MASK_MAJOR = $FF00; diff --git a/common/windows/delphi/packages/PackageInfo.pas b/common/windows/delphi/packages/PackageInfo.pas index e468a9ceb74..202fa9b4e7f 100644 --- a/common/windows/delphi/packages/PackageInfo.pas +++ b/common/windows/delphi/packages/PackageInfo.pas @@ -41,6 +41,7 @@ interface System.Generics.Collections, System.IniFiles, System.JSON, + System.StrUtils, System.Sysutils, Winapi.Windows, Xml.XMLDoc, @@ -55,7 +56,15 @@ function GetJsonValueString(o: TJSONObject; const n: string): string; function GetJsonValueBool(o: TJSONObject; const n: string): Boolean; type - TPackageInfoEntryType = (pietName, pietVersion, pietCopyright, pietAuthor, pietWebsite, pietOther); + TPackageInfoEntryType = ( + pietName, + pietVersion, + pietCopyright, + pietAuthor, + pietWebsite, + pietDescription, + pietOther + ); const PackageInfo_Name = 'Name'; @@ -63,9 +72,17 @@ function GetJsonValueBool(o: TJSONObject; const n: string): Boolean; PackageInfo_Copyright = 'Copyright'; PackageInfo_Author = 'Author'; PackageInfo_Website = 'Website'; - - PackageInfoEntryTypeNames: array[TPackageInfoEntryType] of WideString = - (PackageInfo_Name, PackageInfo_Version, PackageInfo_Copyright, PackageInfo_Author, PackageInfo_Website, ''); + PackageInfo_Description = 'Description'; + + PackageInfoEntryTypeNames: array[TPackageInfoEntryType] of string = ( + PackageInfo_Name, + PackageInfo_Version, + PackageInfo_Copyright, + PackageInfo_Author, + PackageInfo_Website, + PackageInfo_Description, + '' + ); type EPackageInfo = class(Exception); @@ -122,6 +139,7 @@ TPackageObjectList = class(TObjectList) TPackageContentFile = class; TPackageContentFileList = class; + TPackageContentFileReferenceList = class; { Package Options } @@ -131,6 +149,8 @@ TPackageOptions = class(TPackageBaseObject) FExecuteProgram: WideString; FReadmeFile: TPackageContentFile; FGraphicFile: TPackageContentFile; + FLicenseFile: TPackageContentFile; + FWelcomeFile: TPackageContentFile; FLoadLegacy: Boolean; procedure SetReadmeFile(const Value: TPackageContentFile); procedure SetExecuteProgram(Value: WideString); @@ -140,6 +160,12 @@ TPackageOptions = class(TPackageBaseObject) EventType: TPackageNotifyEventType; var FAllow: Boolean); procedure ReadmeRemoved(Sender: TObject; EventType: TPackageNotifyEventType; var FAllow: Boolean); + procedure SetLicenseFile(const Value: TPackageContentFile); + procedure LicenseRemoved(Sender: TObject; + EventType: TPackageNotifyEventType; var FAllow: Boolean); + procedure SetWelcomeFile(const Value: TPackageContentFile); + procedure WelcomeRemoved(Sender: TObject; + EventType: TPackageNotifyEventType; var FAllow: Boolean); public constructor Create(APackage: TPackage); override; destructor Destroy; override; @@ -155,6 +181,8 @@ TPackageOptions = class(TPackageBaseObject) property ExecuteProgram: WideString read FExecuteProgram write SetExecuteProgram; property ReadmeFile: TPackageContentFile read FReadmeFile write SetReadmeFile; property GraphicFile: TPackageContentFile read FGraphicFile write SetGraphicFile; + property LicenseFile: TPackageContentFile read FLicenseFile write SetLicenseFile; + property WelcomeFile: TPackageContentFile read FWelcomeFile write SetWelcomeFile; end; { Package Information } @@ -282,6 +310,8 @@ TPackageKeyboard = class; TPackageKeyboardList = class; TPackageKeyboardLanguage = class; TPackageKeyboardLanguageList = class; + TPackageKeyboardExample = class; + TPackageKeyboardExampleList = class; TPackageKeyboard = class(TPackageBaseObject) private @@ -291,14 +321,17 @@ TPackageKeyboard = class(TPackageBaseObject) FRTL: Boolean; FDisplayFont: TPackageContentFile; FLanguages: TPackageKeyboardLanguageList; + FExamples: TPackageKeyboardExampleList; FVersion: string; FMinKeymanVersion: string; + FWebOSKFonts: TPackageContentFileReferenceList; + FWebDisplayFonts: TPackageContentFileReferenceList; procedure SetDisplayFont(const Value: TPackageContentFile); procedure SetOSKFont(const Value: TPackageContentFile); - procedure DisplayFontRemoved(Sender: TObject; - EventType: TPackageNotifyEventType; var FAllow: Boolean); - procedure OSKFontRemoved(Sender: TObject; + procedure FontRemoved(Sender: TObject; EventType: TPackageNotifyEventType; var FAllow: Boolean); + procedure FontListNotify(Sender: TObject; const Item: TPackageContentFile; + Action: TCollectionNotification); public constructor Create(APackage: TPackage); override; destructor Destroy; override; @@ -308,12 +341,26 @@ TPackageKeyboard = class(TPackageBaseObject) property RTL: Boolean read FRTL write FRTL; property Version: string read FVersion write FVersion; property Languages: TPackageKeyboardLanguageList read FLanguages; + property Examples: TPackageKeyboardExampleList read FExamples; property OSKFont: TPackageContentFile read FOSKFont write SetOSKFont; property DisplayFont: TPackageContentFile read FDisplayFont write SetDisplayFont; + property WebOSKFonts: TPackageContentFileReferenceList read FWebOSKFonts; + property WebDisplayFonts: TPackageContentFileReferenceList read FWebDisplayFonts; // The following properties are used only in memory and never streamed in or out property MinKeymanVersion: string read FMinKeymanVersion write FMinKeymanVersion; end; + TPackageContentFileReferenceList = class(TPackageObjectList) + public + constructor Create(APackage: TPackage); + procedure Assign(Source: TPackageContentFileReferenceList); virtual; + procedure LoadXML(ARoot: IXMLNode); virtual; + procedure SaveXML(ARoot: IXMLNode); virtual; + procedure LoadJSON(ARoot: TJSONArray); virtual; + procedure SaveJSON(ARoot: TJSONArray); virtual; + function GetAsString: string; + end; + TPackageKeyboardList = class(TPackageObjectList) public procedure Assign(Source: TPackageKeyboardList); virtual; @@ -341,6 +388,23 @@ TPackageKeyboardLanguageList = class(TPackageObjectList) + public + procedure LoadJSON(ARoot: TJSONObject); virtual; + procedure SaveJSON(ARoot: TJSONObject); virtual; + procedure LoadXML(ARoot: IXMLNode); virtual; + procedure SaveXML(ARoot: IXMLNode); virtual; + function ContainsID(const id: string): Boolean; + function IndexOfID(const id: string; from: Integer = 0): Integer; + end; + TPackageLexicalModel = class(TPackageBaseObject) private FName: string; @@ -366,6 +430,24 @@ TPackageLexicalModelList = class(TPackageObjectList) function ItemByID(id: string): TPackageLexicalModel; end; + TPackageRelatedPackage = class(TPackageBaseObject) + private + FID: string; + FRelationship: string; + public + procedure Assign(Source: TPackageRelatedPackage); virtual; + property ID: string read FID write FID; + property Relationship: string read FRelationship write FRelationship; + end; + + TPackageRelatedPackageList = class(TPackageObjectList) + procedure Assign(Source: TPackageRelatedPackageList); virtual; + procedure LoadXML(ARoot: IXMLNode); virtual; + procedure SaveXML(ARoot: IXMLNode); virtual; + procedure LoadJSON(ARoot: TJSONObject); virtual; + procedure SaveJSON(ARoot: TJSONObject); virtual; + end; + { TPackage } TPackage = class @@ -390,6 +472,7 @@ TPackage = class Info: TPackageInfoEntryList; Keyboards: TPackageKeyboardList; LexicalModels: TPackageLexicalModelList; + RelatedPackages: TPackageRelatedPackageList; property FileName: WideString read FFileName write FFileName; procedure Assign(Source: TPackage); virtual; @@ -417,6 +500,9 @@ TPackage = class const PackageStartMenuEntryLocationName: array[TPackageStartMenuEntryLocation] of WideString = ('Start Menu', 'Desktop'); //, 'Quick Launch Toolbar'); +const + S_RelatedPackage_Deprecates = 'deprecates'; + implementation uses @@ -433,13 +519,12 @@ implementation SPackageInfoTooNew = 'The package file is version %s. This version can only read version '+SKeymanVersion+' and older files.'; SReadmeNotOwnedCorrectly = 'The readme file ''%s'' referred to is not part of the package.'; SGraphicNotOwnedCorrectly = 'The graphic file ''%s'' referred to is not part of the package.'; + SLicenseNotOwnedCorrectly = 'The license file ''%s'' referred to is not part of the package.'; + SWelcomeNotOwnedCorrectly = 'The welcome file ''%s'' referred to is not part of the package.'; SFileNotOwnedCorrectly = 'The file ''%s'' referred to is not part of the package.'; SDisplayFontNotOwnedCorrectly = 'The display font file ''%s'' referred to is not part of the package.'; SOSKFontNotOwnedCorrectly = 'The OSK font file ''%s'' referred to is not part of the package.'; - - - const SXML_PackageKeyboards = 'Keyboards'; SXML_PackageKeyboard = 'Keyboard'; @@ -450,11 +535,23 @@ implementation SXML_PackageKeyboard_OSKFont = 'OSKFont'; SXML_PackageKeyboard_DisplayFont = 'DisplayFont'; SXML_PackageKeyboard_Languages = 'Languages'; + SXML_PackageKeyboard_Examples = 'Examples'; SXML_PackageKeyboard_Language = 'Language'; SXML_PackageKeyboard_Language_ID = 'ID'; SXML_PackageKeyboard_Language_Name = 'Name'; + SXML_PackageKeyboard_Example = 'Example'; + SXML_PackageKeyboard_Example_ID = 'ID'; + SXML_PackageKeyboard_Example_Keys = 'Keys'; + SXML_PackageKeyboard_Example_Text = 'Text'; + SXML_PackageKeyboard_Example_Note = 'Note'; + + SXML_PackageKeyboard_WebOskFonts = 'WebOSKFonts'; + SXML_PackageKeyboard_WebDisplayFonts = 'WebDisplayFonts'; + SXML_PackageKeyboardFont = 'Font'; + SXML_PackageKeyboardFont_Filename = 'Filename'; + SXML_PackageLexicalModels = 'LexicalModels'; SXML_PackageLexicalModel = 'LexicalModel'; SXML_PackageLexicalModel_Name = 'Name'; @@ -462,6 +559,11 @@ implementation SXML_PackageLexicalModel_RTL = 'RTL'; SXML_PackageLexicalModel_Languages = 'Languages'; + SXML_PackageRelatedPackages = 'RelatedPackages'; + SXML_PackageRelatedPackage = 'RelatedPackage'; + SXML_PackageRelatedPackage_ID = 'ID'; + SXML_PackageRelatedPackage_Relationship = 'Relationship'; + const SJSON_System = 'system'; SJSON_System_KeymanDeveloperVersion = 'keymanDeveloperVersion'; @@ -471,6 +573,8 @@ implementation SJSON_Options_ExecuteProgram = 'executeProgram'; SJSON_Options_ReadMeFile = 'readmeFile'; SJSON_Options_GraphicFile = 'graphicFile'; + SJSON_Options_LicenseFile = 'licenseFile'; + SJSON_Options_WelcomeFile = 'welcomeFile'; SJSON_Registry = 'registry'; SJSON_Registry_Root = 'root'; @@ -493,14 +597,22 @@ implementation SJSON_Info__Description = 'description'; SJSON_Info__URL = 'url'; - SJSON_Info_Website = 'website'; - SJSON_Info_Version = 'version'; SJSON_Info_Name = 'name'; + SJSON_Info_Version = 'version'; SJSON_Info_Copyright = 'copyright'; SJSON_Info_Author = 'author'; - - SJSON_PackageInfoEntryTypeNames: array[TPackageInfoEntryType] of string = - (SJSON_Info_Name, SJSON_Info_Version, SJSON_Info_Copyright, SJSON_Info_Author, SJSON_Info_Website, ''); + SJSON_Info_Website = 'website'; + SJSON_Info_Description = 'description'; + + SJSON_PackageInfoEntryTypeNames: array[TPackageInfoEntryType] of string = ( + SJSON_Info_Name, + SJSON_Info_Version, + SJSON_Info_Copyright, + SJSON_Info_Author, + SJSON_Info_Website, + SJSON_Info_Description, + '' + ); SJSON_Files = 'files'; SJSON_Files_Name = 'name'; @@ -519,15 +631,28 @@ implementation SJSON_Keyboard_Language_ID = 'id'; SJSON_Keyboard_Language_Name = 'name'; + SJSON_Keyboard_Examples = 'examples'; + SJSON_Keyboard_Example_ID = 'id'; + SJSON_Keyboard_Example_Keys = 'keys'; + SJSON_Keyboard_Example_Text = 'text'; + SJSON_Keyboard_Example_Note = 'note'; + + SJSON_Keyboard_WebOSKFonts = 'webOskFonts'; + SJSON_Keyboard_WebDisplayFonts = 'webDisplayFonts'; + SJSON_LexicalModels = 'lexicalModels'; SJSON_LexicalModel_Name = 'name'; SJSON_LexicalModel_ID = 'id'; SJSON_LexicalModel_RTL = 'rtl'; SJSON_LexicalModel_Languages = 'languages'; + SJSON_RelatedPackages = 'relatedPackages'; + SJSON_RelatedPackage_ID = 'id'; + SJSON_RelatedPackage_Relationship = 'relationship'; + function XmlVarToStr(v: OleVariant): string; begin - Result := Trim(VarToStr(v)); + Result := ReplaceStr(ReplaceStr(Trim(VarToStr(v)), #$D#$A, #$A), #$A, #$D#$A); end; {------------------------------------------------------------------------------- @@ -544,6 +669,12 @@ procedure TPackageOptions.Assign(Source: TPackageOptions); if Assigned(Source.GraphicFile) then GraphicFile := Package.Files.FromFileName(Source.GraphicFile.FileName) else GraphicFile := nil; + if Assigned(Source.LicenseFile) + then LicenseFile := Package.Files.FromFileName(Source.LicenseFile.FileName) + else LicenseFile := nil; + if Assigned(Source.WelcomeFile) + then WelcomeFile := Package.Files.FromFileName(Source.WelcomeFile.FileName) + else WelcomeFile := nil; end; constructor TPackageOptions.Create(APackage: TPackage); @@ -557,6 +688,8 @@ destructor TPackageOptions.Destroy; begin ReadmeFile := nil; GraphicFile := nil; + LicenseFile := nil; + WelcomeFile := nil; inherited Destroy; end; @@ -570,14 +703,28 @@ procedure TPackageOptions.GraphicRemoved(Sender: TObject; EventType: TPackageNot FGraphicFile := nil; end; +procedure TPackageOptions.LicenseRemoved(Sender: TObject; EventType: TPackageNotifyEventType; var FAllow: Boolean); +begin + FLicenseFile := nil; +end; + +procedure TPackageOptions.WelcomeRemoved(Sender: TObject; EventType: TPackageNotifyEventType; var FAllow: Boolean); +begin + FWelcomeFile := nil; +end; + procedure TPackageOptions.LoadXML(ARoot: IXMLNode); begin FileVersion := XmlVarToStr(ARoot.ChildNodes['System'].ChildNodes['FileVersion'].NodeValue); ExecuteProgram := XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['ExecuteProgram'].NodeValue); ReadmeFile := Package.Files.FromFileName(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['ReadMeFile'].NodeValue)); GraphicFile := Package.Files.FromFileName(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['GraphicFile'].NodeValue)); + LicenseFile := Package.Files.FromFileName(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['LicenseFile'].NodeValue)); + WelcomeFile := Package.Files.FromFileName(XmlVarToStr(ARoot.ChildNodes['Options'].ChildNodes['WelcomeFile'].NodeValue)); if Assigned(ReadmeFile) then ReadmeFile.AddNotifyObject(ReadmeRemoved); if Assigned(GraphicFile) then GraphicFile.AddNotifyObject(GraphicRemoved); + if Assigned(LicenseFile) then LicenseFile.AddNotifyObject(LicenseRemoved); + if Assigned(WelcomeFile) then WelcomeFile.AddNotifyObject(WelcomeRemoved); end; procedure TPackageOptions.SaveXML(ARoot: IXMLNode); @@ -588,6 +735,10 @@ procedure TPackageOptions.SaveXML(ARoot: IXMLNode); ARoot.ChildNodes['Options'].ChildNodes['ReadMeFile'].NodeValue := ReadmeFile.RelativeFileName; if Assigned(GraphicFile) then ARoot.ChildNodes['Options'].ChildNodes['GraphicFile'].NodeValue := GraphicFile.RelativeFileName; + if Assigned(LicenseFile) then + ARoot.ChildNodes['Options'].ChildNodes['LicenseFile'].NodeValue := LicenseFile.RelativeFileName; + if Assigned(WelcomeFile) then + ARoot.ChildNodes['Options'].ChildNodes['WelcomeFile'].NodeValue := WelcomeFile.RelativeFileName; end; procedure TPackageOptions.LoadIni(AIni: TIniFile); @@ -598,6 +749,8 @@ procedure TPackageOptions.LoadIni(AIni: TIniFile); GraphicFile := Package.Files.FromFileName(AIni.ReadString('Package', 'GraphicFile', '')); if Assigned(ReadmeFile) then ReadmeFile.AddNotifyObject(ReadmeRemoved); if Assigned(GraphicFile) then GraphicFile.AddNotifyObject(GraphicRemoved); + // LicenseFile not supported in ini + // WelcomeFile not supported in ini end; procedure TPackageOptions.LoadJSON(ARoot: TJSONObject); @@ -611,8 +764,12 @@ procedure TPackageOptions.LoadJSON(ARoot: TJSONObject); ExecuteProgram := GetJsonValueString(FOptions, SJSON_Options_ExecuteProgram); ReadmeFile := Package.Files.FromFileName(GetJsonValueString(FOptions, SJSON_Options_ReadMeFile)); GraphicFile := Package.Files.FromFileName(GetJsonValueString(FOptions, SJSON_Options_GraphicFile)); + LicenseFile := Package.Files.FromFileName(GetJsonValueString(FOptions, SJSON_Options_LicenseFile)); + WelcomeFile := Package.Files.FromFileName(GetJsonValueString(FOptions, SJSON_Options_WelcomeFile)); if Assigned(ReadmeFile) then ReadmeFile.AddNotifyObject(ReadmeRemoved); if Assigned(GraphicFile) then GraphicFile.AddNotifyObject(GraphicRemoved); + if Assigned(LicenseFile) then LicenseFile.AddNotifyObject(LicenseRemoved); + if Assigned(WelcomeFile) then WelcomeFile.AddNotifyObject(WelcomeRemoved); end; procedure TPackageOptions.SaveIni(AIni: TIniFile); @@ -623,6 +780,8 @@ procedure TPackageOptions.SaveIni(AIni: TIniFile); AIni.WriteString('Package', 'ReadMeFile', ReadmeFile.RelativeFileName); if Assigned(GraphicFile) then AIni.WriteString('Package', 'GraphicFile', GraphicFile.RelativeFileName); + // licenseFile not supported in ini + // welcomeFile not supported in ini end; procedure TPackageOptions.SaveJSON(ARoot: TJSONObject); @@ -642,6 +801,10 @@ procedure TPackageOptions.SaveJSON(ARoot: TJSONObject); FOptions.AddPair(SJSON_Options_ReadMeFile, ReadmeFile.RelativeFileName); if Assigned(GraphicFile) then FOptions.AddPair(SJSON_Options_GraphicFile, GraphicFile.RelativeFileName); + if Assigned(LicenseFile) then + FOptions.AddPair(SJSON_Options_LicenseFile, LicenseFile.RelativeFileName); + if Assigned(WelcomeFile) then + FOptions.AddPair(SJSON_Options_WelcomeFile, WelcomeFile.RelativeFileName); end; procedure TPackageOptions.SetExecuteProgram(Value: WideString); @@ -669,6 +832,32 @@ procedure TPackageOptions.SetGraphicFile(const Value: TPackageContentFile); end; end; +procedure TPackageOptions.SetLicenseFile(const Value: TPackageContentFile); +begin + if Assigned(FLicenseFile) then FLicenseFile.RemoveNotifyObject(LicenseRemoved); + if not Assigned(Value) then + FLicenseFile := nil + else + begin + if Value.Package <> Package then raise EPackageInfo.CreateFmt(SLicenseNotOwnedCorrectly, [Value]); + FLicenseFile := Value; + FLicenseFile.AddNotifyObject(LicenseRemoved); + end; +end; + +procedure TPackageOptions.SetWelcomeFile(const Value: TPackageContentFile); +begin + if Assigned(FWelcomeFile) then FWelcomeFile.RemoveNotifyObject(WelcomeRemoved); + if not Assigned(Value) then + FWelcomeFile := nil + else + begin + if Value.Package <> Package then raise EPackageInfo.CreateFmt(SWelcomeNotOwnedCorrectly, [Value]); + FWelcomeFile := Value; + FWelcomeFile.AddNotifyObject(WelcomeRemoved); + end; +end; + procedure TPackageOptions.SetReadmeFile(const Value: TPackageContentFile); begin if Assigned(FReadmeFile) then FReadmeFile.RemoveNotifyObject(ReadmeRemoved); @@ -1420,6 +1609,7 @@ procedure TPackage.Assign(Source: TPackage); Info.Assign(Source.Info); Keyboards.Assign(Source.Keyboards); LexicalModels.Assign(Source.LexicalModels); + RelatedPackages.Assign(Source.RelatedPackages); end; constructor TPackage.Create; @@ -1432,6 +1622,7 @@ constructor TPackage.Create; if not Assigned(Info) then Info := TPackageInfoEntryList.Create(Self); if not Assigned(Keyboards) then Keyboards := TPackageKeyboardList.Create(Self); if not Assigned(LexicalModels) then LexicalModels := TPackageLexicalModelList.Create(Self); + if not Assigned(RelatedPackages) then RelatedPackages := TPackageRelatedPackageList.Create(Self); end; destructor TPackage.Destroy; @@ -1442,6 +1633,7 @@ destructor TPackage.Destroy; Info.Free; Keyboards.Free; LexicalModels.Free; + RelatedPackages.Free; inherited Destroy; end; @@ -1475,6 +1667,7 @@ procedure TPackage.DoLoadIni(ini: TIniFile); Options.LoadIni(ini); Keyboards.LoadIni(ini); //LexicalModels not supported in ini + //RelatedPackages not supported in ini end; procedure TPackage.LoadJSON; @@ -1593,8 +1786,9 @@ procedure TPackage.DoLoadJSON(ARoot: TJSONObject); Files.LoadJSON(ARoot); Options.LoadJSON(ARoot); Keyboards.LoadJSON(ARoot); - if Options.FileVersion = SKeymanVersion120 then + if CompareVersions(Options.FileVersion, SKeymanVersion120) >= 0 then LexicalModels.LoadJSON(ARoot); + RelatedPackages.LoadJSON(ARoot); end; procedure TPackage.DoLoadXML(ARoot: IXMLNode); @@ -1613,8 +1807,9 @@ procedure TPackage.DoLoadXML(ARoot: IXMLNode); Files.LoadXML(ARoot); Options.LoadXML(ARoot); Keyboards.LoadXML(ARoot); - if FVersion = SKeymanVersion120 then + if CompareVersions(FVersion, SKeymanVersion120) >= 0 then LexicalModels.LoadXML(ARoot); + RelatedPackages.LoadXML(ARoot); end; procedure TPackage.DoSaveIni(ini: TIniFile); @@ -1626,6 +1821,7 @@ procedure TPackage.DoSaveIni(ini: TIniFile); Files.SaveIni(ini); Keyboards.SaveIni(ini); // Lexical models not supported in ini + // RelatedPackages not supported in ini end; procedure TPackage.FixupFileVersion; @@ -1647,6 +1843,7 @@ procedure TPackage.DoSaveJSON(ARoot: TJSONObject); Keyboards.SaveJSON(ARoot); if LexicalModels.Count > 0 then LexicalModels.SaveJSON(ARoot); + RelatedPackages.SaveJSON(ARoot); end; procedure TPackage.DoSaveXML(ARoot: IXMLNode); @@ -1660,6 +1857,7 @@ procedure TPackage.DoSaveXML(ARoot: IXMLNode); Keyboards.SaveXML(ARoot); if LexicalModels.Count > 0 then LexicalModels.SaveXML(ARoot); + RelatedPackages.SaveXML(ARoot); end; procedure TPackage.SaveIni; @@ -1822,6 +2020,7 @@ procedure TPackageKeyboard.Assign(Source: TPackageKeyboard); var i: Integer; FLanguage: TPackageKeyboardLanguage; + FExample: TPackageKeyboardExample; begin FName := Source.Name; FID := Source.ID; @@ -1842,18 +2041,35 @@ procedure TPackageKeyboard.Assign(Source: TPackageKeyboard); FLanguage.Name := Source.Languages[i].Name; FLanguages.Add(FLanguage); end; + FExamples.Clear; + for i := 0 to Source.Examples.Count - 1 do + begin + FExample := TPackageKeyboardExample.Create(Package); + FExample.ID := Source.Examples[i].ID; + FExample.Keys := Source.Examples[i].Keys; + FExample.Text := Source.Examples[i].Text; + FExample.Note := Source.Examples[i].Note; + FExamples.Add(FExample); + end; + + FWebOSKFonts.Clear; + FWebOSKFonts.Assign(Source.WebOSKFonts); + + FWebDisplayFonts.Clear; + FWebDisplayFonts.Assign(Source.WebDisplayFonts); end; procedure TPackageKeyboard.SetDisplayFont(const Value: TPackageContentFile); begin - if Assigned(FDisplayFont) then FDisplayFont.RemoveNotifyObject(DisplayFontRemoved); + if Assigned(FDisplayFont) then + FDisplayFont.RemoveNotifyObject(FontRemoved); if not Assigned(Value) then FDisplayFont := nil else begin if Value.Package <> Package then raise EPackageInfo.CreateFmt(SDisplayFontNotOwnedCorrectly, [Value]); FDisplayFont := Value; - FDisplayFont.AddNotifyObject(DisplayFontRemoved); + FDisplayFont.AddNotifyObject(FontRemoved); end; end; @@ -1861,39 +2077,68 @@ constructor TPackageKeyboard.Create(APackage: TPackage); begin inherited Create(APackage); FLanguages := TPackageKeyboardLanguageList.Create(APackage); + FExamples := TPackageKeyboardExampleList.Create(APackage); + FWebOSKFonts := TPackageContentFileReferenceList.Create(APackage); + FWebOSKFonts.OnNotify := FontListNotify; + FWebDisplayFonts := TPackageContentFileReferenceList.Create(APackage); + FWebDisplayFonts.OnNotify := FontListNotify; end; destructor TPackageKeyboard.Destroy; begin + if Assigned(FDisplayFont) then + FDisplayFont.RemoveNotifyObject(FontRemoved); + FDisplayFont := nil; + + if Assigned(FOSKFont) then + FOSKFont.RemoveNotifyObject(FontRemoved); + FOSKFont := nil; + FreeAndNil(FLanguages); + FreeAndNil(FExamples); + FreeAndNil(FWebOSKFonts); + FreeAndNil(FWebDisplayFonts); inherited Destroy; end; -procedure TPackageKeyboard.DisplayFontRemoved(Sender: TObject; +procedure TPackageKeyboard.FontListNotify(Sender: TObject; + const Item: TPackageContentFile; Action: TCollectionNotification); +begin + if Action = cnRemoved then + Item.RemoveNotifyObject(FontRemoved) + else if Action = cnAdded then + begin + Assert(Item.Package = Self.Package); + Item.AddNotifyObject(FontRemoved); + end; +end; + +procedure TPackageKeyboard.FontRemoved(Sender: TObject; EventType: TPackageNotifyEventType; var FAllow: Boolean); begin - FDisplayFont := nil; + // For all EventTypes + if Sender = FDisplayFont then + FDisplayFont := nil; + if Sender = FOSKFont then + FOSKFont := nil; + FWebDisplayFonts.Remove(Sender as TPackageContentFile); + FWebOSKFonts.Remove(Sender as TPackageContentFile); end; procedure TPackageKeyboard.SetOSKFont(const Value: TPackageContentFile); begin - if Assigned(FOSKFont) then FOSKFont.RemoveNotifyObject(OSKFontRemoved); + if Assigned(FOSKFont) then + FOSKFont.RemoveNotifyObject(FontRemoved); if not Assigned(Value) then FOSKFont := nil else begin if Value.Package <> Package then raise EPackageInfo.CreateFmt(SOSKFontNotOwnedCorrectly, [Value]); FOSKFont := Value; - FOSKFont.AddNotifyObject(OSKFontRemoved); + FOSKFont.AddNotifyObject(FontRemoved); end; end; -procedure TPackageKeyboard.OSKFontRemoved(Sender: TObject; - EventType: TPackageNotifyEventType; var FAllow: Boolean); -begin - FOSKFont := nil; -end; - { TPackageKeyboardList } procedure TPackageKeyboardList.Assign(Source: TPackageKeyboardList); @@ -1958,6 +2203,8 @@ procedure TPackageKeyboardList.LoadIni(AIni: TIniFile); keyboard.Languages.Add(FLanguage); end; + // web fonts not supported in ini + Add(keyboard); end; finally @@ -1969,7 +2216,7 @@ procedure TPackageKeyboardList.LoadJSON(ARoot: TJSONObject); var keyboard: TPackageKeyboard; i: Integer; - ANode: TJSONArray; + ASubNode, ANode: TJSONArray; AKeyboard: TJSONObject; begin Clear; @@ -1990,6 +2237,16 @@ procedure TPackageKeyboardList.LoadJSON(ARoot: TJSONObject); keyboard.OSKFont := Package.Files.FromFileNameEx(GetJsonValueString(AKeyboard, SJSON_Keyboard_OSKFont)); keyboard.DisplayFont := Package.Files.FromFileNameEx(GetJsonValueString(AKeyboard, SJSON_Keyboard_DisplayFont)); keyboard.Languages.LoadJSON(AKeyboard); + keyboard.Examples.LoadJSON(AKeyboard); + + ASubNode := AKeyboard.GetValue(SJSON_Keyboard_WebOSKFonts) as TJSONArray; + if Assigned(ASubNode) then + keyboard.WebOSKFonts.LoadJSON(ASubNode); + + ASubNode := AKeyboard.GetValue(SJSON_Keyboard_WebDisplayFonts) as TJSONArray; + if Assigned(ASubNode) then + keyboard.WebDisplayFonts.LoadJSON(ASubNode); + Add(keyboard); end; end; @@ -1998,7 +2255,7 @@ procedure TPackageKeyboardList.LoadXML(ARoot: IXMLNode); var keyboard: TPackageKeyboard; i: Integer; - AKeyboard, ANode: IXMLNode; + AKeyboard, ANode, ASubNode: IXMLNode; begin Clear; @@ -2016,6 +2273,20 @@ procedure TPackageKeyboardList.LoadXML(ARoot: IXMLNode); keyboard.DisplayFont := Package.Files.FromFileNameEx(XmlVarToStr(AKeyboard.ChildValues[SXML_PackageKeyboard_DisplayFont])); keyboard.Languages.LoadXML(AKeyboard); + keyboard.Examples.LoadXML(AKeyboard); + + ASubNode := AKeyboard.ChildNodes[SXML_PackageKeyboard_WebOSKFonts]; + if Assigned(ASubNode) then + begin + keyboard.WebOSKFonts.LoadXML(ASubNode); + end; + + ASubNode := AKeyboard.ChildNodes[SXML_PackageKeyboard_WebDisplayFonts]; + if Assigned(ASubNode) then + begin + keyboard.WebDisplayFonts.LoadXML(ASubNode); + end; + Add(keyboard); end; end; @@ -2041,6 +2312,8 @@ procedure TPackageKeyboardList.SaveIni(AIni: TIniFile); begin AIni.WriteString(FSectionName, SXML_PackageKeyboard_Language+IntToStr(j), Items[i].Languages[j].ID+','+Items[i].Languages[j].Name); end; + + // web fonts not supported in ini end; end; @@ -2048,7 +2321,7 @@ procedure TPackageKeyboardList.SaveJSON(ARoot: TJSONObject); var i: Integer; AKeyboard: TJSONObject; - AKeyboards: TJSONArray; + AFonts, AKeyboards: TJSONArray; begin if Count = 0 then Exit; @@ -2071,13 +2344,27 @@ procedure TPackageKeyboardList.SaveJSON(ARoot: TJSONObject); AKeyboard.AddPair(SJSON_Keyboard_DisplayFont, Items[i].DisplayFont.RelativeFileName); Items[i].Languages.SaveJSON(AKeyboard); + Items[i].Examples.SaveJSON(AKeyboard); + if Items[i].WebOSKFonts.Count > 0 then + begin + AFonts := TJSONArray.Create; + Items[i].WebOSKFonts.SaveJSON(AFonts); + AKeyboard.AddPair(SJSON_Keyboard_WebOSKFonts, AFonts); + end; + + if Items[i].WebDisplayFonts.Count > 0 then + begin + AFonts := TJSONArray.Create; + Items[i].WebDisplayFonts.SaveJSON(AFonts); + AKeyboard.AddPair(SJSON_Keyboard_WebDisplayFonts, AFonts); + end; end; end; procedure TPackageKeyboardList.SaveXML(ARoot: IXMLNode); var i: Integer; - AKeyboard, ANode: IXMLNode; + AFonts, AKeyboard, ANode: IXMLNode; begin ANode := ARoot.AddChild(SXML_PackageKeyboards); for i := 0 to Count - 1 do @@ -2095,6 +2382,18 @@ procedure TPackageKeyboardList.SaveXML(ARoot: IXMLNode); AKeyboard.ChildNodes[SXML_PackageKeyboard_DisplayFont].NodeValue := Items[i].DisplayFont.RelativeFileName; Items[i].Languages.SaveXML(AKeyboard); + Items[i].Examples.SaveXML(AKeyboard); + if Items[i].WebOSKFonts.Count > 0 then + begin + AFonts := AKeyboard.AddChild(SXML_PackageKeyboard_WebOskFonts); + Items[i].WebOSKFonts.SaveXML(AFonts); + end; + + if Items[i].WebDisplayFonts.Count > 0 then + begin + AFonts := AKeyboard.AddChild(SXML_PackageKeyboard_WebDisplayFonts); + Items[i].WebDisplayFonts.SaveXML(AFonts); + end; end; end; @@ -2353,5 +2652,307 @@ procedure TPackageKeyboardLanguageList.SaveXML(ARoot: IXMLNode); end; end; +{ TPackageKeyboardExampleList } + +function TPackageKeyboardExampleList.ContainsID(const id: string): Boolean; +begin + Result := IndexOfID(id) >= 0; +end; + +function TPackageKeyboardExampleList.IndexOfID(const id: string; + from: Integer): Integer; +var + i: Integer; +begin + for i := from to Count - 1 do + if SameText(Items[i].ID, id) then + Exit(i); + Result := -1; +end; + +procedure TPackageKeyboardExampleList.LoadJSON(ARoot: TJSONObject); +var + j: Integer; + AExample: TJSONObject; + FExample: TPackageKeyboardExample; + AExamples: TJSONArray; +begin + AExamples := ARoot.Values[SJSON_Keyboard_Examples] as TJSONArray; + if not Assigned(AExamples) then + Exit; + + for j := 0 to AExamples.Count - 1 do + begin + AExample := AExamples.Items[j] as TJSONObject; + + FExample := TPackageKeyboardExample.Create(Package); + FExample.ID := GetJsonValueString(AExample, SJSON_Keyboard_Example_ID); + FExample.Keys := GetJsonValueString(AExample, SJSON_Keyboard_Example_Keys); + FExample.Text := GetJsonValueString(AExample, SJSON_Keyboard_Example_Text); + FExample.Note := GetJsonValueString(AExample, SJSON_Keyboard_Example_Note); + Self.Add(FExample); + end; +end; + +procedure TPackageKeyboardExampleList.LoadXML(ARoot: IXMLNode); +var + j: Integer; + AExamples, AExample: IXMLNode; + FExample: TPackageKeyboardExample; +begin + AExamples := ARoot.ChildNodes[SXML_PackageKeyboard_Examples]; + if not Assigned(AExamples) then + Exit; + + for j := 0 to AExamples.ChildNodes.Count - 1 do + begin + AExample := AExamples.ChildNodes[j]; + + FExample := TPackageKeyboardExample.Create(Package); + FExample.ID := VarToStr(AExample.Attributes[SXML_PackageKeyboard_Example_ID]); + FExample.Keys := VarToStr(AExample.Attributes[SXML_PackageKeyboard_Example_Keys]); + FExample.Text := VarToStr(AExample.Attributes[SXML_PackageKeyboard_Example_Text]); + FExample.Note := VarToStr(AExample.Attributes[SXML_PackageKeyboard_Example_Note]); + Self.Add(FExample); + end; +end; + +procedure TPackageKeyboardExampleList.SaveJSON(ARoot: TJSONObject); +var + AExamples: TJSONArray; + j: Integer; + AExample: TJSONObject; +begin + AExamples := TJSONArray.Create; + ARoot.AddPair(SJSON_Keyboard_Examples, AExamples); + for j := 0 to Count - 1 do + begin + AExample := TJSONObject.Create; + AExamples.Add(AExample); + AExample.AddPair(SJSON_Keyboard_Example_ID, Items[j].ID); + AExample.AddPair(SJSON_Keyboard_Example_Keys, Items[j].Keys); + AExample.AddPair(SJSON_Keyboard_Example_Text, Items[j].Text); + AExample.AddPair(SJSON_Keyboard_Example_Note, Items[j].Note); + end; +end; + +procedure TPackageKeyboardExampleList.SaveXML(ARoot: IXMLNode); +var + AExamples: IXMLNode; + j: Integer; + AExample: IXMLNode; +begin + AExamples := ARoot.AddChild(SXML_PackageKeyboard_Examples); + for j := 0 to Count - 1 do + begin + AExample := AExamples.AddChild(SXML_PackageKeyboard_Example); + AExample.Attributes[SXML_PackageKeyboard_Example_ID] := Items[j].ID; + AExample.Attributes[SXML_PackageKeyboard_Example_Keys] := Items[j].Keys; + AExample.Attributes[SXML_PackageKeyboard_Example_Text] := Items[j].Text; + AExample.Attributes[SXML_PackageKeyboard_Example_Note] := Items[j].Note; + end; +end; + +{ TPackageRelatedPackage } + +procedure TPackageRelatedPackage.Assign(Source: TPackageRelatedPackage); +begin + FID := Source.ID; + FRelationship := Source.Relationship; +end; + +{ TPackageRelatedPackageList } + +procedure TPackageRelatedPackageList.Assign(Source: TPackageRelatedPackageList); +var + i: Integer; + rp: TPackageRelatedPackage; +begin + Clear; + for i := 0 to Source.Count - 1 do + begin + rp := TPackageRelatedPackage.Create(Package); + rp.Assign(Source[i]); + Add(rp); + end; +end; + +procedure TPackageRelatedPackageList.LoadJSON(ARoot: TJSONObject); +var + rp: TPackageRelatedPackage; + i: Integer; + ANode: TJSONArray; + ARelatedPackage: TJSONObject; +begin + Clear; + + ANode := ARoot.Values[SJSON_RelatedPackages] as TJSONArray; + if not Assigned(ANode) then + Exit; + + for i := 0 to ANode.Count - 1 do + begin + ARelatedPackage := ANode.Items[i] as TJSONObject; + + rp := TPackageRelatedPackage.Create(Package); + rp.ID := GetJsonValueString(ARelatedPackage,SJSON_RelatedPackage_ID); + rp.Relationship := GetJsonValueString(ARelatedPackage, SJSON_RelatedPackage_Relationship); + if rp.Relationship <> 'deprecates' then + rp.Relationship := ''; + + Add(rp); + end; +end; + +procedure TPackageRelatedPackageList.LoadXML(ARoot: IXMLNode); +var + rp: TPackageRelatedPackage; + i: Integer; + ARelatedPackage, ANode: IXMLNode; +begin + Clear; + + ANode := ARoot.ChildNodes[SXML_PackageRelatedPackages]; + for i := 0 to ANode.ChildNodes.Count - 1 do + begin + ARelatedPackage := ANode.ChildNodes[i]; + + rp := TPackageRelatedPackage.Create(Package); + rp.ID := XmlVarToStr(ARelatedPackage.Attributes[SXML_PackageRelatedPackage_ID]); + rp.Relationship := XmlVarToStr(ARelatedPackage.Attributes[SXML_PackageRelatedPackage_Relationship]); + if rp.Relationship <> 'deprecates' then + rp.Relationship := ''; + + Add(rp); + end; +end; + +procedure TPackageRelatedPackageList.SaveJSON(ARoot: TJSONObject); +var + i: Integer; + ARelatedPackage: TJSONObject; + ARelatedPackages: TJSONArray; +begin + if Count = 0 then + Exit; + + ARelatedPackages := TJSONArray.Create; + ARoot.AddPair(SJSON_RelatedPackages, ARelatedPackages); + + for i := 0 to Count - 1 do + begin + ARelatedPackage := TJSONObject.Create; + ARelatedPackages.Add(ARelatedPackage); + + ARelatedPackage.AddPair(SJSON_RelatedPackage_ID, Items[i].ID); + // Relationship field required for kmp.json + if Items[i].Relationship = '' + then ARelatedPackage.AddPair(SJSON_RelatedPackage_Relationship, 'related') + else ARelatedPackage.AddPair(SJSON_RelatedPackage_Relationship, Items[i].Relationship); + end; +end; + +procedure TPackageRelatedPackageList.SaveXML(ARoot: IXMLNode); +var + i: Integer; + ARelatedPackage, ANode: IXMLNode; +begin + ANode := ARoot.AddChild(SXML_PackageRelatedPackages); + for i := 0 to Count - 1 do + begin + ARelatedPackage := ANode.AddChild(SXML_PackageRelatedPackage); + + ARelatedPackage.Attributes[SXML_PackageRelatedPackage_ID] := Items[i].ID; + // Relationship field optional for .kps + if Items[i].Relationship <> '' then + ARelatedPackage.Attributes[SXML_PackageRelatedPackage_Relationship] := Items[i].Relationship; + end; +end; + +{ TPackageContentFileReferenceList } + +procedure TPackageContentFileReferenceList.Assign(Source: TPackageContentFileReferenceList); +var + i: Integer; + f: TPackageContentFile; +begin + Clear; + for i := 0 to Source.Count - 1 do + begin + f := Package.Files.FromFileNameEx(Source[i].FileName); + if Assigned(f) then + Add(f); + end; +end; + +constructor TPackageContentFileReferenceList.Create(APackage: TPackage); +begin + inherited Create(APackage); + OwnsObjects := False; +end; + +function TPackageContentFileReferenceList.GetAsString: string; +var + f: TPackageContentFile; +begin + Result := ''; + for f in Self do + begin + Result := Result + ', ' + ExtractFileName(f.FileName); + end; + System.Delete(Result, 1, 2); +end; + +procedure TPackageContentFileReferenceList.LoadJSON(ARoot: TJSONArray); +var + i: Integer; + f: TPackageContentFile; +begin + for i := 0 to ARoot.Count - 1 do + begin + f := Package.Files.FromFileNameEx(ARoot.Items[i].Value); + if Assigned(f) then + Add(f); + end; +end; + +procedure TPackageContentFileReferenceList.LoadXML(ARoot: IXMLNode); +var + i: Integer; + ANode: IXMLNode; + f: TPackageContentFile; +begin + Clear; + for i := 0 to ARoot.ChildNodes.Count - 1 do + begin + ANode := ARoot.ChildNodes[i]; + f := Package.Files.FromFileNameEx(ANode.Attributes[SXML_PackageKeyboardFont_Filename]); + if Assigned(f) then + Add(f); + end; +end; + +procedure TPackageContentFileReferenceList.SaveJSON(ARoot: TJSONArray); +var + i: Integer; +begin + for i := 0 to Count - 1 do + begin + ARoot.Add(Items[i].FileName); + end; +end; + +procedure TPackageContentFileReferenceList.SaveXML(ARoot: IXMLNode); +var + i: Integer; + ANode: IXMLNode; +begin + for i := 0 to Count - 1 do + begin + ANode := ARoot.AddChild(SXML_PackageKeyboardFont); + ANode.Attributes[SXML_PackageKeyboardFont_Filename] := Items[i].FileName; + end; +end; + end. diff --git a/common/windows/delphi/tools/Makefile b/common/windows/delphi/tools/Makefile index a9c1151473a..2ea11ab2372 100644 --- a/common/windows/delphi/tools/Makefile +++ b/common/windows/delphi/tools/Makefile @@ -7,7 +7,7 @@ NOTARGET_SIGNCODE=yes !ifdef NODELPHI TARGETS=.virtual !else -TARGETS=build_standards_data buildunidata devtools sentrytool test-klog +TARGETS=build_standards_data buildunidata devtools sentrytool test-klog verify_signatures !endif CLEANS=clean-tools @@ -28,6 +28,10 @@ buildunidata: .virtual cd $(COMMON_ROOT)\tools\buildunidata $(MAKE) $(TARGET) +verify_signatures: .virtual + cd $(COMMON_ROOT)\tools\verify_signatures + $(MAKE) $(TARGET) + devtools: .virtual !ifdef NODELPHI echo Skipping devtools diff --git a/common/windows/delphi/tools/verify_signatures/Makefile b/common/windows/delphi/tools/verify_signatures/Makefile new file mode 100644 index 00000000000..0f67747665e --- /dev/null +++ b/common/windows/delphi/tools/verify_signatures/Makefile @@ -0,0 +1,13 @@ +# +# test for signatures and version information being correct in ?install directory or ?bin directory +# + +!include ..\..\Defines.mak + +build: + $(DELPHI_MSBUILD) verify_signatures.dproj + copy sigcheck.bin $(WIN32_TARGET_PATH)\sigcheck.exe + +clean: def-clean + +!include ..\..\Target.mak diff --git a/windows/src/test/test_i3633/sigcheck.bin b/common/windows/delphi/tools/verify_signatures/sigcheck.bin similarity index 100% rename from windows/src/test/test_i3633/sigcheck.bin rename to common/windows/delphi/tools/verify_signatures/sigcheck.bin diff --git a/windows/src/test/test_i3633/verify.dpr b/common/windows/delphi/tools/verify_signatures/verify_signatures.dpr similarity index 86% rename from windows/src/test/test_i3633/verify.dpr rename to common/windows/delphi/tools/verify_signatures/verify_signatures.dpr index b3707d84617..49478ac3edf 100644 --- a/windows/src/test/test_i3633/verify.dpr +++ b/common/windows/delphi/tools/verify_signatures/verify_signatures.dpr @@ -1,4 +1,4 @@ -program verify; +program verify_signatures; {$APPTYPE CONSOLE} @@ -36,6 +36,7 @@ begin Delete(s,1,1); end; + // TODO: sign these files and move the check down to the EndsWith node test if str[0].ToLower.Contains('sentry.') or str[0].ToLower.Contains('crashpad_handler') or str[0].ToLower.Contains('keymanmc') then // We don't verify sentry.dll or sentry.x64.dll or crashpad_handler.exe because they're not our files // We don't verify keymanmc.dll because it has no version resources, as it is mc-generated @@ -62,6 +63,14 @@ begin end; end; + if str[0].ToLower.EndsWith('node') then + begin + // It's one of the node addons -- in developer server at time of writing + // we don't have a version resource but we have signed it, so treat it + // as valid + Exit(''); + end; + if str[3] <> 'SIL International' then begin Exit('File has wrong company name'); @@ -90,7 +99,7 @@ begin if (ParamStr(1) = '-?') or (ParamCount < 1) then begin - writeln('verify [-d] VERSION.md: Verify the output of sigcheck to ensure all executables are signed and have proper version.'); + writeln('verify_signatures [-d] VERSION.md: Verify the output of sigcheck to ensure all executables are signed and have proper version.'); writeln(' -d: Check the timestamp on the signature is less than 2 days old.'); writeln(' VERSION.md: path to the version to verify against'); Halt(2); diff --git a/windows/src/test/test_i3633/verify.dproj b/common/windows/delphi/tools/verify_signatures/verify_signatures.dproj similarity index 99% rename from windows/src/test/test_i3633/verify.dproj rename to common/windows/delphi/tools/verify_signatures/verify_signatures.dproj index e23aeb3a4d8..323353c79b9 100644 --- a/windows/src/test/test_i3633/verify.dproj +++ b/common/windows/delphi/tools/verify_signatures/verify_signatures.dproj @@ -3,7 +3,7 @@ {A6DCA558-8DD0-4FFF-8DAE-F305AB8D2AF4} 18.8 None - verify.dpr + verify_signatures.dpr True Debug Win32 @@ -45,7 +45,7 @@ true - verify + verify_signatures $(BDS)\bin\delphi_PROJECTICNS.icns $(BDS)\bin\delphi_PROJECTICON.ico bindcompfmx;fmx;rtl;dbrtl;DbxClientDriver;bindcomp;inetdb;DBXInterBaseDriver;xmlrtl;DbxCommonDriver;DBXMySQLDriver;dbxcds;soaprtl;bindengine;CustomIPTransport;dsnap;fmxase;inet;fmxobj;inetdbxpress;fmxdae;dbexpress;$(DCC_UsePackage) @@ -161,7 +161,7 @@ - verify.dpr + verify_signatures.dpr Microsoft Office 2000 Sample Automation Server Wrapper Components diff --git a/developer/src/test/auto/keyboard-package-versions/KeyboardPackageVersionsTestSuite.res b/common/windows/delphi/tools/verify_signatures/verify_signatures.res similarity index 100% rename from developer/src/test/auto/keyboard-package-versions/KeyboardPackageVersionsTestSuite.res rename to common/windows/delphi/tools/verify_signatures/verify_signatures.res diff --git a/core/commands.inc.sh b/core/commands.inc.sh index 6916b61a9d9..2d3bccc4c08 100644 --- a/core/commands.inc.sh +++ b/core/commands.inc.sh @@ -11,7 +11,18 @@ do_clean() { # clean: note build/ will be left, but build// should be gone local target=$1 builder_start_action clean:$target || return 0 + rm -rf "$MESON_PATH" + + # Removes ICU cached components so it will be re-downloaded and built. Note: + # we could use `git clean`, but this clarifies exactly what is deleted, and + # but we try not to use git commands in build scripts, to maintain clear + # responsibility + rm -rf \ + "$THIS_SCRIPT_PATH/subprojects/icu" \ + "$THIS_SCRIPT_PATH/subprojects/*.tgz" \ + "$THIS_SCRIPT_PATH/subprojects/*.zip" + builder_finish_action success clean:$target } diff --git a/core/doc/BUILDING.md b/core/doc/BUILDING.md index ae066550465..5dfd46f9d61 100644 --- a/core/doc/BUILDING.md +++ b/core/doc/BUILDING.md @@ -8,10 +8,3 @@ See [build configuration](../../docs/build/index.md) for details on how to confi On all platforms, use `build.sh`. * See `./build.sh --help` for more details - -## Note on kmcomp - -kmcomp is the command-line compiler from Keyman Developer, available from - or in this repo in -`/windows/src/developer/kmcomp`. The compiler is currently available as a -Windows PE executable only, but it does run under WINE. diff --git a/core/doc/hotdoc.json b/core/doc/hotdoc.json index 4ece50d0f52..946c62beac0 100644 --- a/core/doc/hotdoc.json +++ b/core/doc/hotdoc.json @@ -4,7 +4,7 @@ "sitemap": "@doc_dir@/sitemap.txt", "index": "@doc_dir@/markdown_files/index.md", "c_sources": [ - "@include_dir@/keyman/keyboardprocessor.h" + "@include_dir@/keyman/keyman_core_api.h" ], "c_include_directories": [ "@include_dir@", diff --git a/core/doc/introspection.schema b/core/doc/introspection.schema index ff10c7c9695..3b7684e0e9d 100644 --- a/core/doc/introspection.schema +++ b/core/doc/introspection.schema @@ -47,7 +47,7 @@ } }, "properties": { - "$schema": { "const": "keyman/keyboardprocessor/doc/introspection.schema" }, + "$schema": { "const": "keyman/core/doc/introspection.schema" }, "keyboard": { "$ref": "#/definitions/keyboard" }, "options": { "type": "object", diff --git a/core/doc/markdown_files/index.md b/core/doc/markdown_files/index.md index 98870c356dc..d389857c5f6 100644 --- a/core/doc/markdown_files/index.md +++ b/core/doc/markdown_files/index.md @@ -46,7 +46,7 @@ A virtual key board event and modifier map recevied from the Platform layer to b processed with the state object for this Client application. - __Virtual Key:__ A code based on the US English layout, with values matching the Windows -virtual key codes. See `keyboardprocessor_vkeys.h` for definitions. +virtual key codes. See `keyman_core_api_vkeys.h` for definitions. - __Modifier Key:__ The set of Control, Shift, Alt, Caps Lock keys. On some platforms these may have other names (e.g. Alt is called Option on macOS); other platform-specific @@ -58,4 +58,4 @@ Caps Lock. ### Namespace -All calls, types and enums are prefixed with the namespace identifier `km_kbp_` +All calls, types and enums are prefixed with the namespace identifier `km_core_` diff --git a/core/doc/meson.build b/core/doc/meson.build index 01fcb5c98ad..6039e2e2dda 100644 --- a/core/doc/meson.build +++ b/core/doc/meson.build @@ -18,7 +18,7 @@ if hotdoc.found() output: 'hotdoc.json', configuration: cfg) deps = files( - '../include/keyman/keyboardprocessor.h', + '../include/keyman/keyman_core_api.h', '../src/jsonpp.hpp', '../src/utfcodec.hpp' ) diff --git a/core/include/keyman/keyboardprocessor_consts.h b/core/include/keyman/keyboardprocessor_consts.h deleted file mode 100644 index 1d0936610b3..00000000000 --- a/core/include/keyman/keyboardprocessor_consts.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -// Defined environment options for KMX processor -#define KM_KBP_KMX_ENV_PLATFORM u"platform" -#define KM_KBP_KMX_ENV_BASELAYOUT u"baseLayout" -#define KM_KBP_KMX_ENV_BASELAYOUTALT u"baseLayoutAlt" -#define KM_KBP_KMX_ENV_SIMULATEALTGR u"simulateAltgr" -#define KM_KBP_KMX_ENV_CAPSLOCK u"capsLock" -#define KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT u"baseLayoutGivesCtrlRAltForRAlt" diff --git a/core/include/keyman/keyboardprocessor_version.h.in b/core/include/keyman/keyboardprocessor_version.h.in deleted file mode 100644 index d434ae4e485..00000000000 --- a/core/include/keyman/keyboardprocessor_version.h.in +++ /dev/null @@ -1,15 +0,0 @@ -#define km_kbp_version_stringify(x) km_kbp_version_to_string(x) -#define km_kbp_version_to_string(x) #x - -// Product versioning - -#define KM_KBP_VERSION_MAJOR @majorver@ -#define KM_KBP_VERSION_MINOR @minorver@ -#define KM_KBP_VERSION_PATCH @patchver@ -#define KM_KBP_VERSION_STRING km_kbp_version_stringify(@majorver@ ## . ## @minorver@ ## . ## @patchver@ ## .0) - -// API versioning - -#define KM_KBP_LIB_CURRENT @lib_curr@ -#define KM_KBP_LIB_AGE @lib_age@ -#define KM_KBP_LIB_REVISION @lib_rev@ diff --git a/core/include/keyman/keyboardprocessor_vkeys.h b/core/include/keyman/keyboardprocessor_vkeys.h deleted file mode 100644 index e8d8122bda2..00000000000 --- a/core/include/keyman/keyboardprocessor_vkeys.h +++ /dev/null @@ -1,312 +0,0 @@ -/* - Copyright: © 2018 SIL International. - Description: API declarations for modifier keys, handy access masks and - Keyman VKEY names. These follow the same keytop->code - associations as the Windows API. This is a separate header to - maintain readability of the primary API header. - Create Date: 17 Oct 2018 - Authors: Tim Eves (TSE) - History: 17 Oct 2018 - TSE - Moved & refactored km_kbp_modifier_state - from keyboardprocessor.h. - - Added VKey and mask definitions. - 6 Oct 2018 - TSE - Move into keyman folder. - -*/ - -#pragma once - -enum km_kbp_modifier_state { - KM_KBP_MODIFIER_LCTRL = 1 << 0, - KM_KBP_MODIFIER_RCTRL = 1 << 1, - KM_KBP_MODIFIER_LALT = 1 << 2, - KM_KBP_MODIFIER_RALT = 1 << 3, - KM_KBP_MODIFIER_SHIFT = 1 << 4, - KM_KBP_MODIFIER_CTRL = 1 << 5, - KM_KBP_MODIFIER_ALT = 1 << 6, - /* - KM_KBP_MODIFIER_META = 1 << 7, // Either Meta-key flag (tentative). Not usable by keyboards currently - // Used internally (currently, only by KMW) to ensure Meta-key - // shortcuts safely bypass rules - // Meta key = Command key on macOS, Windows key on Windows - */ - KM_KBP_MODIFIER_CAPS = 1 << 8, - KM_KBP_MODIFIER_NOCAPS = 1 << 9, - /* - KM_KBP_MODIFIER_NUMLOCK = 1 << 10, - KM_KBP_MODIFIER_NONUMLOCK = 1 << 11, - KM_KBP_MODIFIER_SCROLLOCK = 1 << 12, - KM_KBP_MODIFIER_NOSCROLLOCK = 1 << 13, - KM_KBP_MODIFIER_VIRTUALKEY = 1 << 14, - */ -}; - -enum km_kbp_modifier_mask { - KM_KBP_MODIFIER_MASK_ALL = 0x7f, - KM_KBP_MODIFIER_MASK_ALT_GR_SIM = KM_KBP_MODIFIER_LCTRL|KM_KBP_MODIFIER_LALT, - KM_KBP_MODIFIER_MASK_CHIRAL = 0x1f, - KM_KBP_MODIFIER_MASK_IS_CHIRAL = 0x0f, - KM_KBP_MODIFIER_MASK_NON_CHIRAL = 0x7f, - KM_KBP_MODIFIER_MASK_CAPS = 0x0300, -/*KM_KBP_MODIFIER_MASK_NUMLOCK = 0x0C00, - KM_KBP_MODIFIER_MASK_SCROLLLOCK = 0x3000,*/ -}; - -// These are Windows API VKEYs, using Keyman VKEY names. -enum km_kpb_virtual_key { - KM_KBP_VKEY__00, - KM_KBP_VKEY_LBUTTON, - KM_KBP_VKEY_RBUTTON, - KM_KBP_VKEY_CANCEL, - KM_KBP_VKEY_MBUTTON, - KM_KBP_VKEY__05, - KM_KBP_VKEY__06, - KM_KBP_VKEY__07, - KM_KBP_VKEY_BKSP, - KM_KBP_VKEY_TAB, - KM_KBP_VKEY__0A, - KM_KBP_VKEY__0B, - KM_KBP_VKEY_KP5, - KM_KBP_VKEY_ENTER, - KM_KBP_VKEY__0E, - KM_KBP_VKEY__0F, - KM_KBP_VKEY_SHIFT, - KM_KBP_VKEY_CONTROL, - KM_KBP_VKEY_ALT, - KM_KBP_VKEY_PAUSE, - KM_KBP_VKEY_CAPS, - KM_KBP_VKEY__15, - KM_KBP_VKEY__16, - KM_KBP_VKEY__17, - KM_KBP_VKEY__18, - KM_KBP_VKEY__19, - KM_KBP_VKEY__1A, - KM_KBP_VKEY_ESC, - KM_KBP_VKEY__1C, - KM_KBP_VKEY__1D, - KM_KBP_VKEY__1E, - KM_KBP_VKEY__1F, - KM_KBP_VKEY_SPACE, - KM_KBP_VKEY_PGUP, - KM_KBP_VKEY_PGDN, - KM_KBP_VKEY_END, - KM_KBP_VKEY_HOME, - KM_KBP_VKEY_LEFT, - KM_KBP_VKEY_UP, - KM_KBP_VKEY_RIGHT, - KM_KBP_VKEY_DOWN, - KM_KBP_VKEY_SEL, - KM_KBP_VKEY_PRINT, - KM_KBP_VKEY_EXEC, - KM_KBP_VKEY_PRTSCN, - KM_KBP_VKEY_INS, - KM_KBP_VKEY_DEL, - KM_KBP_VKEY_HELP, - KM_KBP_VKEY_0, - KM_KBP_VKEY_1, - KM_KBP_VKEY_2, - KM_KBP_VKEY_3, - KM_KBP_VKEY_4, - KM_KBP_VKEY_5, - KM_KBP_VKEY_6, - KM_KBP_VKEY_7, - KM_KBP_VKEY_8, - KM_KBP_VKEY_9, - KM_KBP_VKEY__3A, - KM_KBP_VKEY__3B, - KM_KBP_VKEY__3C, - KM_KBP_VKEY__3D, - KM_KBP_VKEY__3E, - KM_KBP_VKEY__3F, - KM_KBP_VKEY__40, - KM_KBP_VKEY_A, - KM_KBP_VKEY_B, - KM_KBP_VKEY_C, - KM_KBP_VKEY_D, - KM_KBP_VKEY_E, - KM_KBP_VKEY_F, - KM_KBP_VKEY_G, - KM_KBP_VKEY_H, - KM_KBP_VKEY_I, - KM_KBP_VKEY_J, - KM_KBP_VKEY_K, - KM_KBP_VKEY_L, - KM_KBP_VKEY_M, - KM_KBP_VKEY_N, - KM_KBP_VKEY_O, - KM_KBP_VKEY_P, - KM_KBP_VKEY_Q, - KM_KBP_VKEY_R, - KM_KBP_VKEY_S, - KM_KBP_VKEY_T, - KM_KBP_VKEY_U, - KM_KBP_VKEY_V, - KM_KBP_VKEY_W, - KM_KBP_VKEY_X, - KM_KBP_VKEY_Y, - KM_KBP_VKEY_Z, - KM_KBP_VKEY__5B, - KM_KBP_VKEY__5C, - KM_KBP_VKEY__5D, - KM_KBP_VKEY__5E, - KM_KBP_VKEY__5F, - KM_KBP_VKEY_NP0, - KM_KBP_VKEY_NP1, - KM_KBP_VKEY_NP2, - KM_KBP_VKEY_NP3, - KM_KBP_VKEY_NP4, - KM_KBP_VKEY_NP5, - KM_KBP_VKEY_NP6, - KM_KBP_VKEY_NP7, - KM_KBP_VKEY_NP8, - KM_KBP_VKEY_NP9, - KM_KBP_VKEY_NPSTAR, - KM_KBP_VKEY_NPPLUS, - KM_KBP_VKEY_SEPARATOR, - KM_KBP_VKEY_NPMINUS, - KM_KBP_VKEY_NPDOT, - KM_KBP_VKEY_NPSLASH, - KM_KBP_VKEY_F1, - KM_KBP_VKEY_F2, - KM_KBP_VKEY_F3, - KM_KBP_VKEY_F4, - KM_KBP_VKEY_F5, - KM_KBP_VKEY_F6, - KM_KBP_VKEY_F7, - KM_KBP_VKEY_F8, - KM_KBP_VKEY_F9, - KM_KBP_VKEY_F10, - KM_KBP_VKEY_F11, - KM_KBP_VKEY_F12, - KM_KBP_VKEY_F13, - KM_KBP_VKEY_F14, - KM_KBP_VKEY_F15, - KM_KBP_VKEY_F16, - KM_KBP_VKEY_F17, - KM_KBP_VKEY_F18, - KM_KBP_VKEY_F19, - KM_KBP_VKEY_F20, - KM_KBP_VKEY_F21, - KM_KBP_VKEY_F22, - KM_KBP_VKEY_F23, - KM_KBP_VKEY_F24, - KM_KBP_VKEY__88, - KM_KBP_VKEY__89, - KM_KBP_VKEY__8A, - KM_KBP_VKEY__8B, - KM_KBP_VKEY__8C, - KM_KBP_VKEY__8D, - KM_KBP_VKEY__8E, - KM_KBP_VKEY__8F, - KM_KBP_VKEY_NUMLOCK, - KM_KBP_VKEY_SCROLL, - KM_KBP_VKEY__92, - KM_KBP_VKEY__93, - KM_KBP_VKEY__94, - KM_KBP_VKEY__95, - KM_KBP_VKEY__96, - KM_KBP_VKEY__97, - KM_KBP_VKEY__98, - KM_KBP_VKEY__99, - KM_KBP_VKEY__9A, - KM_KBP_VKEY__9B, - KM_KBP_VKEY__9C, - KM_KBP_VKEY__9D, - KM_KBP_VKEY__9E, - KM_KBP_VKEY__9F, - KM_KBP_VKEY__A0, - KM_KBP_VKEY__A1, - KM_KBP_VKEY__A2, - KM_KBP_VKEY__A3, - KM_KBP_VKEY__A4, - KM_KBP_VKEY__A5, - KM_KBP_VKEY__A6, - KM_KBP_VKEY__A7, - KM_KBP_VKEY__A8, - KM_KBP_VKEY__A9, - KM_KBP_VKEY__AA, - KM_KBP_VKEY__AB, - KM_KBP_VKEY__AC, - KM_KBP_VKEY__AD, - KM_KBP_VKEY__AE, - KM_KBP_VKEY__AF, - KM_KBP_VKEY__B0, - KM_KBP_VKEY__B1, - KM_KBP_VKEY__B2, - KM_KBP_VKEY__B3, - KM_KBP_VKEY__B4, - KM_KBP_VKEY__B5, - KM_KBP_VKEY__B6, - KM_KBP_VKEY__B7, - KM_KBP_VKEY__B8, - KM_KBP_VKEY__B9, - KM_KBP_VKEY_COLON, - KM_KBP_VKEY_EQUAL, - KM_KBP_VKEY_COMMA, - KM_KBP_VKEY_HYPHEN, - KM_KBP_VKEY_PERIOD, - KM_KBP_VKEY_SLASH, - KM_KBP_VKEY_BKQUOTE, - KM_KBP_VKEY__C1, - KM_KBP_VKEY__C2, - KM_KBP_VKEY__C3, - KM_KBP_VKEY__C4, - KM_KBP_VKEY__C5, - KM_KBP_VKEY__C6, - KM_KBP_VKEY__C7, - KM_KBP_VKEY__C8, - KM_KBP_VKEY__C9, - KM_KBP_VKEY__CA, - KM_KBP_VKEY__CB, - KM_KBP_VKEY__CC, - KM_KBP_VKEY__CD, - KM_KBP_VKEY__CE, - KM_KBP_VKEY__CF, - KM_KBP_VKEY__D0, - KM_KBP_VKEY__D1, - KM_KBP_VKEY__D2, - KM_KBP_VKEY__D3, - KM_KBP_VKEY__D4, - KM_KBP_VKEY__D5, - KM_KBP_VKEY__D6, - KM_KBP_VKEY__D7, - KM_KBP_VKEY__D8, - KM_KBP_VKEY__D9, - KM_KBP_VKEY__DA, - KM_KBP_VKEY_LBRKT, - KM_KBP_VKEY_BKSLASH, - KM_KBP_VKEY_RBRKT, - KM_KBP_VKEY_QUOTE, - KM_KBP_VKEY_oDF, - KM_KBP_VKEY_oE0, - KM_KBP_VKEY_oE1, - KM_KBP_VKEY_oE2, // 102nd key on European layouts - KM_KBP_VKEY_oE3, - KM_KBP_VKEY_oE4, - KM_KBP_VKEY__E5, - KM_KBP_VKEY_oE6, - KM_KBP_VKEY__E7, - KM_KBP_VKEY__E8, - KM_KBP_VKEY_oE9, - KM_KBP_VKEY_oEA, - KM_KBP_VKEY_oEB, - KM_KBP_VKEY_oEC, - KM_KBP_VKEY_oED, - KM_KBP_VKEY_oEE, - KM_KBP_VKEY_oEF, - KM_KBP_VKEY_oF0, - KM_KBP_VKEY_oF1, - KM_KBP_VKEY_oF2, - KM_KBP_VKEY_oF3, - KM_KBP_VKEY_oF4, - KM_KBP_VKEY_oF5, - KM_KBP_VKEY__F6, - KM_KBP_VKEY__F7, - KM_KBP_VKEY__F8, - KM_KBP_VKEY__F9, - KM_KBP_VKEY__FA, - KM_KBP_VKEY__FB, - KM_KBP_VKEY__FC, - KM_KBP_VKEY__FD, - KM_KBP_VKEY__FE, - KM_KBP_VKEY__FF, -}; diff --git a/core/include/keyman/keyboardprocessor.h b/core/include/keyman/keyman_core_api.h similarity index 58% rename from core/include/keyman/keyboardprocessor.h rename to core/include/keyman/keyman_core_api.h index 429a8ae6cbf..86358edeebd 100644 --- a/core/include/keyman/keyboardprocessor.h +++ b/core/include/keyman/keyman_core_api.h @@ -57,7 +57,7 @@ A virtual key event and modifier map received from the Platform layer to be processed with the state object for this Client application. - __Virtual Key:__ A code based on the US English layout, with values matching the Windows -virtual key codes. See `keyboardprocessor_vkeys.h` for definitions. +virtual key codes. See `keyman_core_api_vkeys.h` for definitions. - __Modifier Key:__ The set of Control, Shift, Alt, Caps Lock keys. On some platforms these may have other names (e.g. Alt is called Option on macOS); other platform-specific @@ -67,25 +67,25 @@ Caps Lock. ## API ### Namespace -All calls, types and enums are prefixed with the namespace identifier `km_kbp_` +All calls, types and enums are prefixed with the namespace identifier `km_core_` ### API idioms Almost all calls marshalling variable length aggregate data in or out of an API object take the form: -> km_kbp_status *fn_name*(object_ref, buffer_ptr, size_ptr) +> km_core_status *fn_name*(object_ref, buffer_ptr, size_ptr) where the buffer is nullable and all other arguments are required (will result -in an `KM_KBP_STATUS_INVALID_ARGUMENT` status being returned if nulled). When +in an `KM_CORE_STATUS_INVALID_ARGUMENT` status being returned if nulled). When `buffer` is `nullptr` or `0` the function will place the size of the required buffer in the variable pointed to by `size_ptr`. Calls which result in the allocation of resources, regardless of resulting ownership, are of the form: -> km_kbp_status *fn_name*(object_ref, out_ptr) +> km_core_status *fn_name*(object_ref, out_ptr) where `out_ptr` is a valid pointer to a caller allocated variable to hold the resulting ouput. This is often a reference to a created object. All arguments -are required (will result in an `KM_KBP_STATUS_INVALID_ARGUMENT` status being +are required (will result in an `KM_CORE_STATUS_INVALID_ARGUMENT` status being returned if nulled). For accessors to fixed size attributes of an object these will take the form: @@ -100,9 +100,9 @@ nothing in that event. */ #include #include -#include -#include -#include +#include +#include +#include #if defined(__cplusplus) extern "C" @@ -110,24 +110,24 @@ extern "C" #endif // Basic types // -typedef uint16_t km_kbp_virtual_key; // A virtual key code. -typedef uint32_t km_kbp_status; // Status return code. +typedef uint16_t km_core_virtual_key; // A virtual key code. +typedef uint32_t km_core_status; // Status return code. // Opaque object types. // -typedef struct km_kbp_context km_kbp_context; -typedef struct km_kbp_keyboard km_kbp_keyboard; -typedef struct km_kbp_state km_kbp_state; -typedef struct km_kbp_options km_kbp_options; +typedef struct km_core_context km_core_context; +typedef struct km_core_keyboard km_core_keyboard; +typedef struct km_core_state km_core_state; +typedef struct km_core_options km_core_options; // Forward declarations // -typedef struct km_kbp_option_item km_kbp_option_item; +typedef struct km_core_option_item km_core_option_item; // Callback function used to to access Input Method eXtension library functions // from Keyman Core // -typedef uint8_t (*km_kbp_keyboard_imx_platform)(km_kbp_state*, uint32_t, void*); +typedef uint8_t (*km_core_keyboard_imx_platform)(km_core_state*, uint32_t, void*); /*``` ### Error Handling @@ -137,21 +137,21 @@ value is an error). Any functions that can fail will always return a status value and all results are returned via outparams passed to the function. ```c */ -enum km_kbp_status_codes { - KM_KBP_STATUS_OK = 0, - KM_KBP_STATUS_NO_MEM = 1, - KM_KBP_STATUS_IO_ERROR = 2, - KM_KBP_STATUS_INVALID_ARGUMENT = 3, - KM_KBP_STATUS_KEY_ERROR = 4, - KM_KBP_STATUS_INSUFFICENT_BUFFER = 5, - KM_KBP_STATUS_INVALID_UTF = 6, - KM_KBP_STATUS_INVALID_KEYBOARD = 7, - KM_KBP_STATUS_OS_ERROR = 0x80000000 +enum km_core_status_codes { + KM_CORE_STATUS_OK = 0, + KM_CORE_STATUS_NO_MEM = 1, + KM_CORE_STATUS_IO_ERROR = 2, + KM_CORE_STATUS_INVALID_ARGUMENT = 3, + KM_CORE_STATUS_KEY_ERROR = 4, + KM_CORE_STATUS_INSUFFICENT_BUFFER = 5, + KM_CORE_STATUS_INVALID_UTF = 6, + KM_CORE_STATUS_INVALID_KEYBOARD = 7, + KM_CORE_STATUS_OS_ERROR = 0x80000000 }; /* ``` -The final status code KM_KBP_STATUS_OS_ERROR is intended to allow encapsulating +The final status code KM_CORE_STATUS_OS_ERROR is intended to allow encapsulating a platform error code; the remaining 31 low bits are the error code returned by the OS for cases where the failure mode is platform specific. For HRESULT codes this only permits failure codes to be passed. @@ -176,79 +176,79 @@ Contexts are always owned by their state. They may be set to a list of context_items or interrogated for their current list of context items. ```c */ -enum km_kbp_context_type { - KM_KBP_CT_END, - KM_KBP_CT_CHAR, - KM_KBP_CT_MARKER +enum km_core_context_type { + KM_CORE_CT_END, + KM_CORE_CT_CHAR, + KM_CORE_CT_MARKER }; typedef struct { uint8_t type; uint8_t _reserved[3]; union { - km_kbp_usv character; + km_core_usv character; uint32_t marker; }; -} km_kbp_context_item; +} km_core_context_item; -#define KM_KBP_CONTEXT_ITEM_END {KM_KBP_CT_END, {0,}, {0,}} +#define KM_CORE_CONTEXT_ITEM_END {KM_CORE_CT_END, {0,}, {0,}} /* ``` -### `km_kbp_context_items_from_utf16` +### `km_core_context_items_from_utf16` ##### Description: -Convert a UTF16 encoded Unicode string into an array of `km_kbp_context_item` +Convert a UTF16 encoded Unicode string into an array of `km_core_context_item` structures. Allocates memory as needed. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. -- `KM_KBP_STATUS_NO_MEM`: In the event not enough memory can be allocated for the +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_NO_MEM`: In the event not enough memory can be allocated for the output buffer. -- `KM_KBP_STATUS_INVALID_UTF`: In the event the UTF16 string cannot be decoded +- `KM_CORE_STATUS_INVALID_UTF`: In the event the UTF16 string cannot be decoded because it contains unpaired surrogate codeunits. ##### Parameters: - __text__: a pointer to a null terminated array of utf16 encoded data. - __out_ptr__: a pointer to the result variable: - A pointer to the start of the `km_kbp_context_item` array containing the + A pointer to the start of the `km_core_context_item` array containing the representation of the input string. - Terminated with a type of `KM_KBP_CT_END`. Must be disposed of with - `km_kbp_context_items_dispose`. + Terminated with a type of `KM_CORE_CT_END`. Must be disposed of with + `km_core_context_items_dispose`. ```c */ KMN_API -km_kbp_status -km_kbp_context_items_from_utf16(km_kbp_cp const *text, - km_kbp_context_item **out_ptr); +km_core_status +km_core_context_items_from_utf16(km_core_cp const *text, + km_core_context_item **out_ptr); /* ``` -### `km_kbp_context_items_from_utf8` +### `km_core_context_items_from_utf8` ##### Description: -Convert an UTF8 encoded Unicode string into an array of `km_kbp_context_item` +Convert an UTF8 encoded Unicode string into an array of `km_core_context_item` structures. Allocates memory as needed. ##### Status: -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. -- `KM_KBP_STATUS_NO_MEM`: In the event it cannot allocate enough memory for the +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_NO_MEM`: In the event it cannot allocate enough memory for the output buffer. -- `KM_KBP_STATUS_INVALID_UTF`: In the event the UTF8 string cannot be +- `KM_CORE_STATUS_INVALID_UTF`: In the event the UTF8 string cannot be decoded. ##### Parameters: - __text__: a pointer to a null terminated array of utf8 encoded data. - __out_ptr__: a pointer to the result variable: - A pointer to the start of the `km_kbp_context_item` array containing the + A pointer to the start of the `km_core_context_item` array containing the representation of the input string. - Terminated with a type of `KM_KBP_CT_END`. + Terminated with a type of `KM_CORE_CT_END`. ```c */ KMN_API -km_kbp_status -km_kbp_context_items_from_utf8(char const *text, - km_kbp_context_item **out_ptr); +km_core_status +km_core_context_items_from_utf8(char const *text, + km_core_context_item **out_ptr); /* ``` -### `km_kbp_context_items_to_utf16` +### `km_core_context_items_to_utf16` ##### Description: Convert a context item array into a UTF-16 encoded string placing it into the supplied buffer of specified size, and return the number of code units @@ -256,14 +256,14 @@ actually used in the conversion. If null is passed as the buffer the number codeunits required is returned. This will strip markers from the context during the conversion. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. -- `KM_KBP_STATUS_INSUFFICENT_BUFFER`: If the buffer is not large enough. +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_INSUFFICENT_BUFFER`: If the buffer is not large enough. `buf_size` will contain the space required. The contents of the buffer are undefined. ##### Parameters: -- __context_items__: A pointer to the start of an array `km_kbp_context_item`. - Must be terminated with a type of `KM_KBP_CT_END`. +- __context_items__: A pointer to the start of an array `km_core_context_item`. + Must be terminated with a type of `KM_CORE_CT_END`. - __buf__: A pointer to the buffer to place the UTF-16 string into. May be null to request size calculation. - __buf_size__: a pointer to the result variable: @@ -273,14 +273,14 @@ context during the conversion. ```c */ KMN_API -km_kbp_status -km_kbp_context_items_to_utf16(km_kbp_context_item const *item, - km_kbp_cp *buf, +km_core_status +km_core_context_items_to_utf16(km_core_context_item const *item, + km_core_cp *buf, size_t *buf_size); /* ``` -### `km_kbp_context_items_to_utf8` +### `km_core_context_items_to_utf8` ##### Description: Convert a context item array into a UTF-8 encoded string placing it into the supplied buffer of specified size, and return the number of code units @@ -288,14 +288,14 @@ actually used in the conversion. If null is passed as the buffer the number codeunits required is returned. This will strip markers from the context during the conversion. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. -- `KM_KBP_STATUS_INSUFFICENT_BUFFER`: If the buffer is not large enough. +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_INSUFFICENT_BUFFER`: If the buffer is not large enough. `buf_size` will contain the space required. The contents of the buffer are undefined. ##### Parameters: -- __context_items__: A pointer to the start of an array `km_kbp_context_item`. - Must be terminated with a type of `KM_KBP_CT_END`. +- __context_items__: A pointer to the start of an array `km_core_context_item`. + Must be terminated with a type of `KM_CORE_CT_END`. - __buf__: A pointer to the buffer to place the UTF-8 string into. May be null to request size calculation. - __buf_size__: a pointer to the result variable: @@ -305,79 +305,79 @@ context during the conversion. ```c */ KMN_API -km_kbp_status -km_kbp_context_items_to_utf8(km_kbp_context_item const *item, +km_core_status +km_core_context_items_to_utf8(km_core_context_item const *item, char *buf, size_t *buf_size); /* ``` -### `km_kbp_context_items_dispose` +### `km_core_context_items_dispose` ##### Description: -Free the allocated memory belonging to a `km_kbp_context_item` array previously -returned by `km_kbp_context_items_from_utf16` or `km_kbp_context_get` +Free the allocated memory belonging to a `km_core_context_item` array previously +returned by `km_core_context_items_from_utf16` or `km_core_context_get` ##### Parameters: -- __context_items__: A pointer to the start of the `km_kbp_context_item` array +- __context_items__: A pointer to the start of the `km_core_context_item` array to be disposed of. ```c */ KMN_API void -km_kbp_context_items_dispose(km_kbp_context_item *context_items); +km_core_context_items_dispose(km_core_context_item *context_items); /* ``` -### `km_kbp_context_set` +### `km_core_context_set` ##### Description: Replace the contents of the current context with a new sequence of -`km_kbp_context_item` entries. +`km_core_context_item` entries. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. -- `KM_KBP_STATUS_NO_MEM`: In the event not enough memory can be allocated to +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_NO_MEM`: In the event not enough memory can be allocated to grow the context buffer internally. ##### Parameters: - __context__: A pointer to an opaque context object -- __context_items__: A pointer to the start of the `km_kbp_context_item` +- __context_items__: A pointer to the start of the `km_core_context_item` array containing the new context. It must be terminated with an item - of type `KM_KBP_CT_END`. + of type `KM_CORE_CT_END`. ```c */ KMN_API -km_kbp_status -km_kbp_context_set(km_kbp_context *context, - km_kbp_context_item const *context_items); +km_core_status +km_core_context_set(km_core_context *context, + km_core_context_item const *context_items); /* ``` -### `km_kbp_context_get` +### `km_core_context_get` ##### Description: Copies all items in the context into a new array and returns the new array. -This must be disposed of by caller using `km_kbp_context_items_dispose`. +This must be disposed of by caller using `km_core_context_items_dispose`. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. -- `KM_KBP_STATUS_NO_MEM`: In the event not enough memory can be allocated for the +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_NO_MEM`: In the event not enough memory can be allocated for the output buffer. ##### Parameters: -- __context_items__: A pointer to the start of an array `km_kbp_context_item`. +- __context_items__: A pointer to the start of an array `km_core_context_item`. - __out__: a pointer to the result variable: - A pointer to the start of the `km_kbp_context_item` array containing a - copy of the context. Terminated with a type of `KM_KBP_CT_END`. Must be - disposed of with `km_kbp_context_items_dispose`. + A pointer to the start of the `km_core_context_item` array containing a + copy of the context. Terminated with a type of `KM_CORE_CT_END`. Must be + disposed of with `km_core_context_items_dispose`. ```c */ KMN_API -km_kbp_status -km_kbp_context_get(km_kbp_context const *context_items, - km_kbp_context_item **out); +km_core_status +km_core_context_get(km_core_context const *context_items, + km_core_context_item **out); /* ``` -### `km_kbp_context_clear` +### `km_core_context_clear` ##### Description: Removes all context_items from the internal array. If `context` is null, has no effect. @@ -388,11 +388,11 @@ null, has no effect. */ KMN_API void -km_kbp_context_clear(km_kbp_context *); +km_core_context_clear(km_core_context *); /* ``` -### `km_kbp_context_length` +### `km_core_context_length` ##### Description: Return the number of items in the context. ##### Return: @@ -405,75 +405,75 @@ pointer. */ KMN_API size_t -km_kbp_context_length(km_kbp_context *); +km_core_context_length(km_core_context *); /* ``` -### `km_kbp_context_append` +### `km_core_context_append` ##### Description: Add more items to the end (insertion point) of the context. If these exceed the maximum context length the same number of items will be dropped from the beginning of the context. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. -- `KM_KBP_STATUS_NO_MEM`: In the event not enough memory can be allocated to +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_NO_MEM`: In the event not enough memory can be allocated to grow the context buffer internally. ##### Parameters: - __context__: A pointer to an opaque context object. -- __context_items__: A pointer to the start of the `KM_KBP_CT_END` terminated - array of `km_kbp_context_item` to append. +- __context_items__: A pointer to the start of the `KM_CORE_CT_END` terminated + array of `km_core_context_item` to append. ```c */ KMN_API -km_kbp_status -km_kbp_context_append(km_kbp_context *context, - km_kbp_context_item const *context_items); +km_core_status +km_core_context_append(km_core_context *context, + km_core_context_item const *context_items); /* ``` -### `km_kbp_context_shrink` +### `km_core_context_shrink` ##### Description: Remove a specified number of items from the end of the context, optionally add up to the same number of the supplied items to the front of the context. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. -- `KM_KBP_STATUS_NO_MEM`: in the event it cannot allocated enough memory to grow +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_NO_MEM`: in the event it cannot allocated enough memory to grow the context internally. ##### Parameters: - __context__: A pointer to an opaque context object. - __num__: The number of items to remove from the end of context. -- __context_items__: Pointer to the start of the `KM_KBP_CT_END` terminated - array of `km_kbp_context_item` to add to the front. Up to `num` items will +- __context_items__: Pointer to the start of the `KM_CORE_CT_END` terminated + array of `km_core_context_item` to add to the front. Up to `num` items will be prepended. This may be null if not required. ```c */ KMN_API -km_kbp_status -km_kbp_context_shrink(km_kbp_context *context, +km_core_status +km_core_context_shrink(km_core_context *context, size_t num, - km_kbp_context_item const *prefix); + km_core_context_item const *prefix); /* ``` -### `km_kbp_context_item_list_size` +### `km_core_context_item_list_size` ##### Description: -Return the length of a terminated `km_kbp_context_item` array. +Return the length of a terminated `km_core_context_item` array. ##### Return: The number of items in the list, not including terminating item, or 0 if `context_items` is null. ##### Parameters: -- __context_items__: A pointer to a `KM_KBP_CT_END` terminated array of - `km_kbp_context_item` values. +- __context_items__: A pointer to a `KM_CORE_CT_END` terminated array of + `km_core_context_item` values. ```c */ KMN_API size_t -km_kbp_context_item_list_size(km_kbp_context_item const *context_items); +km_core_context_item_list_size(km_core_context_item const *context_items); /* ``` @@ -486,15 +486,15 @@ other actions. */ typedef struct { - uint8_t expected_type; // km_kbp_backspace_type + uint8_t expected_type; // km_core_backspace_type uintptr_t expected_value; // used mainly in unit tests -} km_kbp_backspace_item; +} km_core_backspace_item; -enum km_kbp_backspace_type { - KM_KBP_BT_UNKNOWN = 0, // Used at beginning of context; user-initiated backspace - KM_KBP_BT_CHAR = 1, // Deleting a character prior to insertion point - KM_KBP_BT_MARKER = 2, // Deleting a marker prior to insertion point - KM_KBP_BT_MAX_TYPE_ID +enum km_core_backspace_type { + KM_CORE_BT_UNKNOWN = 0, // Used at beginning of context; user-initiated backspace + KM_CORE_BT_CHAR = 1, // Deleting a character prior to insertion point + KM_CORE_BT_MARKER = 2, // Deleting a marker prior to insertion point + KM_CORE_BT_MAX_TYPE_ID }; typedef struct { @@ -502,28 +502,28 @@ typedef struct { uint8_t _reserved[sizeof(void*)-sizeof(uint8_t)]; union { uintptr_t marker; // MARKER type - km_kbp_option_item const * option; // OPT types - km_kbp_usv character; // CHAR type + km_core_option_item const * option; // OPT types + km_core_usv character; // CHAR type uint8_t capsLock; // CAPSLOCK type, 1 to turn on, 0 to turn off - km_kbp_backspace_item backspace; // BACKSPACE type + km_core_backspace_item backspace; // BACKSPACE type }; -} km_kbp_action_item; - -enum km_kbp_action_type { - KM_KBP_IT_END = 0, // Marks end of action items list. - KM_KBP_IT_CHAR = 1, // A Unicode character has been generated. - KM_KBP_IT_MARKER = 2, // Correlates to kmn's "deadkey" markers. - KM_KBP_IT_ALERT = 3, // The keyboard has triggered a alert/beep/bell. - KM_KBP_IT_BACK = 4, // Delete the codepoint preceding the insertion point. - KM_KBP_IT_PERSIST_OPT = 5, // The indicated option needs to be stored. - KM_KBP_IT_EMIT_KEYSTROKE = 6, // Emit the current keystroke to the application - KM_KBP_IT_INVALIDATE_CONTEXT = 7, +} km_core_action_item; + +enum km_core_action_type { + KM_CORE_IT_END = 0, // Marks end of action items list. + KM_CORE_IT_CHAR = 1, // A Unicode character has been generated. + KM_CORE_IT_MARKER = 2, // Correlates to kmn's "deadkey" markers. + KM_CORE_IT_ALERT = 3, // The keyboard has triggered a alert/beep/bell. + KM_CORE_IT_BACK = 4, // Delete the codepoint preceding the insertion point. + KM_CORE_IT_PERSIST_OPT = 5, // The indicated option needs to be stored. + KM_CORE_IT_EMIT_KEYSTROKE = 6, // Emit the current keystroke to the application + KM_CORE_IT_INVALIDATE_CONTEXT = 7, // The processor requests that the context buffer be cleared; // for applications where context is cached, this clears the context; // for applications where context is read from the focused text store, // the context is just re-read and markers flushed. - KM_KBP_IT_CAPSLOCK = 8, // Enable or disable capsLock - KM_KBP_IT_MAX_TYPE_ID + KM_CORE_IT_CAPSLOCK = 8, // Enable or disable capsLock + KM_CORE_IT_MAX_TYPE_ID }; @@ -533,9 +533,9 @@ enum km_kbp_action_type { A state’s default options are set from the keyboard at creation time and the environment. The Platform layer is then is expected to apply any persisted options it is maintaining. Options are passed into and out of API functions as -simple C arrays of `km_kbp_option_item` terminated with a `KM_KBP_OPTIONS_END` +simple C arrays of `km_core_option_item` terminated with a `KM_CORE_OPTIONS_END` sentinel value. A state's options are exposed and manipulatable via the -`km_kbp_options` API. All option values are of type C string. +`km_core_options` API. All option values are of type C string. During processing when the Platform layer finds a PERSIST action type it should store the updated option in the appropriate place, based on its scope. @@ -545,55 +545,55 @@ value. ```c */ -enum km_kbp_option_scope { - KM_KBP_OPT_UNKNOWN = 0, - KM_KBP_OPT_KEYBOARD = 1, - KM_KBP_OPT_ENVIRONMENT = 2, - KM_KBP_OPT_MAX_SCOPES +enum km_core_option_scope { + KM_CORE_OPT_UNKNOWN = 0, + KM_CORE_OPT_KEYBOARD = 1, + KM_CORE_OPT_ENVIRONMENT = 2, + KM_CORE_OPT_MAX_SCOPES }; -struct km_kbp_option_item { - km_kbp_cp const * key; - km_kbp_cp const * value; +struct km_core_option_item { + km_core_cp const * key; + km_core_cp const * value; uint8_t scope; // Scope which an option belongs to. }; -#define KM_KBP_OPTIONS_END { 0, 0, 0 } +#define KM_CORE_OPTIONS_END { 0, 0, 0 } /* ``` -### `km_kbp_options_list_size` +### `km_core_options_list_size` ##### Description: -Return the length of a terminated `km_kbp_option_item` array (options +Return the length of a terminated `km_core_option_item` array (options list). ##### Return: The number of items in the list, not including terminating item, or 0 if `opts` is null. ##### Parameters: -- __opts__: A pointer to a `KM_KBP_OPTIONS_END` terminated array of - `km_kbp_option_item` values. +- __opts__: A pointer to a `KM_CORE_OPTIONS_END` terminated array of + `km_core_option_item` values. ```c */ KMN_API size_t -km_kbp_options_list_size(km_kbp_option_item const *opts); +km_core_options_list_size(km_core_option_item const *opts); /* ``` -### `km_kbp_state_option_lookup` +### `km_core_state_option_lookup` ##### Description: Lookup an option based on its key, in an options list. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null, or +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null, or if the scope is invalid. -- `KM_KBP_STATUS_KEY_ERROR`: The key cannot be found. +- `KM_CORE_STATUS_KEY_ERROR`: The key cannot be found. ##### Parameters: - __state__: An opaque pointer to a state object. - __scope__: Which key-value store to interrogate. -- __key__: A UTF-16 string that matches the key in the target `km_kbp_option_item`. +- __key__: A UTF-16 string that matches the key in the target `km_core_option_item`. - __value__: A pointer to the result variable: A pointer to a UTF-16 string value owned by the state or keyboard object at the time of the call. This pointer is only valid *until* the next call to any @@ -601,47 +601,47 @@ Lookup an option based on its key, in an options list. ```c */ KMN_API -km_kbp_status -km_kbp_state_option_lookup(km_kbp_state const *state, +km_core_status +km_core_state_option_lookup(km_core_state const *state, uint8_t scope, - km_kbp_cp const *key, - km_kbp_cp const **value); + km_core_cp const *key, + km_core_cp const **value); /* ``` -### `km_kbp_state_options_update` +### `km_core_state_options_update` ##### Description: -Adds or updates one or more options from a list of `km_kbp_option_item`s. +Adds or updates one or more options from a list of `km_core_option_item`s. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. -- `KM_KBP_STATUS_NO_MEM`: In the event an internal memory allocation fails. -- `KM_KBP_STATUS_KEY_ERROR`: The key cannot be found. +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_NO_MEM`: In the event an internal memory allocation fails. +- `KM_CORE_STATUS_KEY_ERROR`: The key cannot be found. ##### Parameters: - __state__: An opaque pointer to a state object. -- __new_opts__: An array of `km_kbp_option_item` objects to update or add. Must be - terminated with `KM_KBP_OPTIONS_END`. +- __new_opts__: An array of `km_core_option_item` objects to update or add. Must be + terminated with `KM_CORE_OPTIONS_END`. ```c */ KMN_API -km_kbp_status -km_kbp_state_options_update(km_kbp_state *state, - km_kbp_option_item const *new_opts); +km_core_status +km_core_state_options_update(km_core_state *state, + km_core_option_item const *new_opts); /* ``` -### `km_kbp_state_options_to_json` +### `km_core_state_options_to_json` ##### Description: -Export the contents of a `km_kbp_options` array to a JSON formatted document and +Export the contents of a `km_core_options` array to a JSON formatted document and place it in the supplied buffer, reporting how much space was used. If null is passed as the buffer the number of bytes required is returned in `space`. If there is insufficent space to hold the document the contents of the buffer is undefined. The returned buffer uses UTF-8 encoding. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. -- `KM_KBP_STATUS_NO_MEM`: In the event an internal memory allocation fails. +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_NO_MEM`: In the event an internal memory allocation fails. ##### Parameters: - __state__: An opaque pointer to a state object. - __buf__: A pointer to the buffer to place the C string containing the JSON @@ -653,8 +653,8 @@ null. On return it will hold how many bytes were used. ```c */ KMN_API -km_kbp_status -km_kbp_state_options_to_json(km_kbp_state const *state, +km_core_status +km_core_state_options_to_json(km_core_state const *state, char *buf, size_t *space); @@ -669,63 +669,63 @@ of state objects. ```c */ typedef struct { - km_kbp_cp const * version_string; // Processor specific version string. - km_kbp_cp const * id; // Keyman keyboard ID string. - km_kbp_path_name folder_path; // Path to the unpacked folder containing + km_core_cp const * version_string; // Processor specific version string. + km_core_cp const * id; // Keyman keyboard ID string. + km_core_path_name folder_path; // Path to the unpacked folder containing // the keyboard and associated resources. - km_kbp_option_item const * default_options; -} km_kbp_keyboard_attrs; + km_core_option_item const * default_options; +} km_core_keyboard_attrs; typedef struct { - km_kbp_virtual_key key; + km_core_virtual_key key; uint32_t modifier_flag; -} km_kbp_keyboard_key; +} km_core_keyboard_key; -#define KM_KBP_KEYBOARD_KEY_LIST_END { 0, 0 } +#define KM_CORE_KEYBOARD_KEY_LIST_END { 0, 0 } typedef struct { - km_kbp_cp const * library_name; - km_kbp_cp const * function_name; + km_core_cp const * library_name; + km_core_cp const * function_name; uint32_t imx_id; // unique identifier used to call this function -} km_kbp_keyboard_imx; +} km_core_keyboard_imx; -#define KM_KBP_KEYBOARD_IMX_END { 0, 0, 0 } +#define KM_CORE_KEYBOARD_IMX_END { 0, 0, 0 } /* ``` -### `km_kbp_keyboard_load` +### `km_core_keyboard_load` ##### Description: Parse and load keyboard from the supplied path and a pointer to the loaded keyboard into the out paramter. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_NO_MEM`: In the event an internal memory allocation fails. -- `KM_KBP_STATUS_IO_ERROR`: +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_NO_MEM`: In the event an internal memory allocation fails. +- `KM_CORE_STATUS_IO_ERROR`: In the event the keyboard file is unparseable for any reason -- `KM_KBP_STATUS_INVALID_ARGUMENT`: +- `KM_CORE_STATUS_INVALID_ARGUMENT`: In the event the file doesn't exist or is inaccesible or `keyboard` is null. -- `KM_KBP_STATUS_OS_ERROR`: Bit 31 (high bit) set, bits 0-30 are an OS-specific +- `KM_CORE_STATUS_OS_ERROR`: Bit 31 (high bit) set, bits 0-30 are an OS-specific error code. ##### Parameters: - __kb_path__: On Windows, a UTF-16 string; on other platforms, a C string: contains a valid path to the keyboard file. - __keyboard__: A pointer to result variable: A pointer to the opaque keyboard object returned by the Processor. This - memory must be freed with a call to `km_kbp_keyboard_dispose`. + memory must be freed with a call to `km_core_keyboard_dispose`. ```c */ KMN_API -km_kbp_status -km_kbp_keyboard_load(km_kbp_path_name kb_path, - km_kbp_keyboard **keyboard); +km_core_status +km_core_keyboard_load(km_core_path_name kb_path, + km_core_keyboard **keyboard); /* ``` -### `km_kbp_keyboard_dispose` +### `km_core_keyboard_dispose` ##### Description: Free the allocated memory belonging to an opaque keyboard object previously -returned by `km_kbp_keyboard_load`. +returned by `km_core_keyboard_load`. ##### Parameters: - __keyboard__: A pointer to the opaque keyboard object to be disposed of. @@ -734,57 +734,57 @@ returned by `km_kbp_keyboard_load`. */ KMN_API void -km_kbp_keyboard_dispose(km_kbp_keyboard *keyboard); +km_core_keyboard_dispose(km_core_keyboard *keyboard); /* ``` -### `km_kbp_keyboard_get_attrs` +### `km_core_keyboard_get_attrs` ##### Description: Returns the const internal attributes of the keyboard. This structure is valid for the lifetime of the opaque keyboard object. Do not modify the returned data. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. ##### Parameters: - __keyboard__: A pointer to the opaque keyboard object to be queried. - __out__: A pointer to the result: - A pointer to a `km_kbp_keyboard_attrs` structure. + A pointer to a `km_core_keyboard_attrs` structure. ```c */ KMN_API -km_kbp_status -km_kbp_keyboard_get_attrs(km_kbp_keyboard const *keyboard, - km_kbp_keyboard_attrs const **out); +km_core_status +km_core_keyboard_get_attrs(km_core_keyboard const *keyboard, + km_core_keyboard_attrs const **out); /* ``` -### `km_kbp_keyboard_get_key_list` +### `km_core_keyboard_get_key_list` ##### Description: Returns the unordered full set of modifier+virtual keys that are handled by the keyboard. The matching dispose call needs to be called to free the memory. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null. ##### Parameters: - __keyboard__: A pointer to the opaque keyboard object to be queried. -- __out__: A pointer to an array of `km_kbp_keyboard_key` structures, - terminated by `KM_KBP_KEYBOARD_KEY_LIST_END`. +- __out__: A pointer to an array of `km_core_keyboard_key` structures, + terminated by `KM_CORE_KEYBOARD_KEY_LIST_END`. ```c */ KMN_API -km_kbp_status -km_kbp_keyboard_get_key_list(km_kbp_keyboard const *keyboard, - km_kbp_keyboard_key **out); +km_core_status +km_core_keyboard_get_key_list(km_core_keyboard const *keyboard, + km_core_keyboard_key **out); /** ``` -### `km_kbp_keyboard_key_list_dispose` +### `km_core_keyboard_key_list_dispose` ##### Description: Free the allocated memory belonging to a keyboard key list previously -returned by `km_kbp_keyboard_get_key_list`. +returned by `km_core_keyboard_get_key_list`. ##### Parameters: - __key_list__: A pointer to the keyboard key list to be disposed of. @@ -792,46 +792,46 @@ returned by `km_kbp_keyboard_get_key_list`. ```c */ KMN_API -void km_kbp_keyboard_key_list_dispose(km_kbp_keyboard_key *key_list); +void km_core_keyboard_key_list_dispose(km_core_keyboard_key *key_list); /** - * km_kbp_keyboard_get_imx_list: + * km_core_keyboard_get_imx_list: * * Returns: the list of IMX libraries and function names that are referenced by * the keyboard.The matching dispose call needs to be called to free the memory. */ KMN_API -km_kbp_status km_kbp_keyboard_get_imx_list(km_kbp_keyboard const *keyboard, km_kbp_keyboard_imx **imx_list); +km_core_status km_core_keyboard_get_imx_list(km_core_keyboard const *keyboard, km_core_keyboard_imx **imx_list); /** - * km_kbp_keyboard_imx_list_dispose: + * km_core_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); +void km_core_keyboard_imx_list_dispose(km_core_keyboard_imx *imx_list); /** - * km_kbp_state_imx_register_callback: + * km_core_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); +void km_core_state_imx_register_callback(km_core_state *state, km_core_keyboard_imx_platform imx_callback, void *callback_object); /** - * km_kbp_state_imx_deregister_callback: + * km_core_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); +void km_core_state_imx_deregister_callback(km_core_state *state); /* ``` @@ -844,45 +844,45 @@ and dynamic options ("option stores" in kmn format). /* ``` -### `km_kbp_state_create` +### `km_core_state_create` ##### Description: Create a keyboard processor state object, maintaining state for the keyboard in the environment passed. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_NO_MEM`: +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_NO_MEM`: In the event memory is unavailable to allocate a state object. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: +- `KM_CORE_STATUS_INVALID_ARGUMENT`: In the event the `keyboard` or `out` pointer are null. ##### Parameters: - __keyboard__: A pointer to the opaque keyboard object this object will hold state for. - __env__: -The array of `km_kbp_option_item` key/value pairs used to initialise the -environment, terminated by `KM_KBP_OPTIONS_END`. +The array of `km_core_option_item` key/value pairs used to initialise the +environment, terminated by `KM_CORE_OPTIONS_END`. - __out__: A pointer to result variable: A pointer to the opaque state object returned by the Processor, initalised to maintain state for `keyboard`. -This must be disposed of by a call to `km_kbp_state_dispose`. +This must be disposed of by a call to `km_core_state_dispose`. ```c */ KMN_API -km_kbp_status -km_kbp_state_create(km_kbp_keyboard *keyboard, - km_kbp_option_item const *env, - km_kbp_state **out); +km_core_status +km_core_state_create(km_core_keyboard *keyboard, + km_core_option_item const *env, + km_core_state **out); /* ``` -### `km_kbp_state_clone` +### `km_core_state_clone` ##### Description: Clone an existing opaque state object. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_NO_MEM`: +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_NO_MEM`: In the event memory is unavailable to allocate a state object. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: +- `KM_CORE_STATUS_INVALID_ARGUMENT`: In the event the `state` or `out` pointer are null. ##### Parameters: - __state__: @@ -890,22 +890,22 @@ A pointer to the opaque statea object to be cloned. - __out__: A pointer to result variable: A pointer to the opaque state object returned by the Processor, cloned from the existing object `state`. This -must be disposed of by a call to `km_kbp_state_dispose`. +must be disposed of by a call to `km_core_state_dispose`. ```c */ KMN_API -km_kbp_status -km_kbp_state_clone(km_kbp_state const *state, - km_kbp_state **out); +km_core_status +km_core_state_clone(km_core_state const *state, + km_core_state **out); /* ``` -### `km_kbp_state_dispose` +### `km_core_state_dispose` ##### Description: -Free the allocated resources belonging to a `km_kbp_state` object previously -returned by `km_kbp_state_create` or `km_kbp_state_clone`. After this all -pointers previously returned by any km_kbp_state family of calls will become +Free the allocated resources belonging to a `km_core_state` object previously +returned by `km_core_state_create` or `km_core_state_clone`. After this all +pointers previously returned by any km_core_state family of calls will become invalid. ##### Parameters: - __state__: A pointer to the opaque state object to be disposed. @@ -914,11 +914,11 @@ invalid. */ KMN_API void -km_kbp_state_dispose(km_kbp_state *state); +km_core_state_dispose(km_core_state *state); /* ``` -### `km_kbp_state_context` +### `km_core_state_context` ##### Description: Get access to the state object's context. ##### Return: @@ -930,8 +930,8 @@ of the state object. If null is passed in, then null is returned. ```c */ KMN_API -km_kbp_context * -km_kbp_state_context(km_kbp_state *state); +km_core_context * +km_core_state_context(km_core_state *state); /* @@ -942,68 +942,68 @@ Get access to the state object's keyboard processor's intermediate context. This is used during an IMX callback, part way through processing a keystroke. ##### Return: A pointer to an context item array. Must be disposed of by a call -to `km_kbp_context_items_dispose`. +to `km_core_context_items_dispose`. ##### Parameters: - __state__: A pointer to the opaque state object to be queried. ```c */ KMN_API -km_kbp_status -kbp_state_get_intermediate_context(km_kbp_state *state, km_kbp_context_item ** context_items); +km_core_status +kbp_state_get_intermediate_context(km_core_state *state, km_core_context_item ** context_items); /* ``` -### `km_kbp_state_action_items` +### `km_core_state_action_items` ##### Description: Get the list of action items generated by the last call to -`km_kbp_process_event`. +`km_core_process_event`. ##### Return: -A pointer to a `km_kbp_action_item` list, of `*num_items` in length. This data +A pointer to a `km_core_action_item` list, of `*num_items` in length. This data becomes invalid when the state object is destroyed, or after a call to -`km_kbp_process_event`. Do not modify the contents of this data. The returned -array is terminated with a `KM_KBP_IT_END` entry. +`km_core_process_event`. Do not modify the contents of this data. The returned +array is terminated with a `KM_CORE_IT_END` entry. ##### Parameters: -- __state__: A pointer to the opaque `km_kbp_state` object to be queried. +- __state__: A pointer to the opaque `km_core_state` object to be queried. - __num_items__: A pointer to a result variable: The number of items in the action item list -including the `KM_KBP_IT_END` terminator. May be null if not that +including the `KM_CORE_IT_END` terminator. May be null if not that information is required. ```c */ KMN_API -km_kbp_action_item const * -km_kbp_state_action_items(km_kbp_state const *state, +km_core_action_item const * +km_core_state_action_items(km_core_state const *state, size_t *num_items); /* ``` -### `km_kbp_state_queue_action_items` +### `km_core_state_queue_action_items` ##### Description: Queue actions for the current keyboard processor state; normally -used in IMX callbacks called during `km_kbp_process_event`. +used in IMX callbacks called during `km_core_process_event`. ##### Return: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_INVALID_ARGUMENT`: In the event the `state` or `action_items` pointer are null. ##### Parameters: -- __state__: A pointer to the opaque `km_kbp_state` object to be queried. -- __action_items__: The action items to be added to the keyboardprocessor - queue. Must be terminated with a `KM_KBP_IT_END` entry. +- __state__: A pointer to the opaque `km_core_state` object to be queried. +- __action_items__: The action items to be added to the core + queue. Must be terminated with a `KM_CORE_IT_END` entry. ```c */ KMN_API -km_kbp_status -km_kbp_state_queue_action_items(km_kbp_state *state, - km_kbp_action_item const *action_items); +km_core_status +km_core_state_queue_action_items(km_core_state *state, + km_core_action_item const *action_items); /* ``` ### `km_kpb_state_to_json` ##### Description: -Export the internal state of a `km_kbp_state` object to a JSON format document +Export the internal state of a `km_core_state` object to a JSON format document and place it in the supplied buffer, reporting how much space was used. If null is passed as the buffer the number of bytes required is returned. If there is insufficent space to hold the document, the contents of the buffer is undefined. @@ -1014,8 +1014,8 @@ versioned is not part of this API and is intended solely for use in diagnostics or by development and debugging tools which are aware of processor implementation details. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_NO_MEM`: In the event an internal memory allocation fails. +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_NO_MEM`: In the event an internal memory allocation fails. ##### Parameters: - __state__: An pointer to an opaque state object. - __buf__: A pointer to the buffer to place the C string containing the JSON @@ -1027,8 +1027,8 @@ null. On return it will hold how many bytes were used. ```c */ KMN_API -km_kbp_status -km_kbp_state_to_json(km_kbp_state const *state, +km_core_status +km_core_state_to_json(km_core_state const *state, char *buf, size_t *space); @@ -1045,44 +1045,44 @@ typedef struct { uint16_t technology; // A bit field specifiying which Keyboard // technologies the engine supports. char const *vendor; // Implementor of the processor. -} km_kbp_attr; +} km_core_attr; -enum km_kbp_tech_value { - KM_KBP_TECH_UNSPECIFIED = 0, - KM_KBP_TECH_MOCK = 1 << 0, - KM_KBP_TECH_KMX = 1 << 1, - KM_KBP_TECH_LDML = 1 << 2 +enum km_core_tech_value { + KM_CORE_TECH_UNSPECIFIED = 0, + KM_CORE_TECH_MOCK = 1 << 0, + KM_CORE_TECH_KMX = 1 << 1, + KM_CORE_TECH_LDML = 1 << 2 }; /** - * km_kbp_event_flags: + * km_core_event_flags: * - * Bit flags to be used with the event_flags parameter of km_kbp_process_event + * Bit flags to be used with the event_flags parameter of km_core_process_event */ -enum km_kbp_event_flags { - KM_KBP_EVENT_FLAG_DEFAULT = 0, // default value: hardware - KM_KBP_EVENT_FLAG_TOUCH = 1, // set if the event is touch, otherwise hardware +enum km_core_event_flags { + KM_CORE_EVENT_FLAG_DEFAULT = 0, // default value: hardware + KM_CORE_EVENT_FLAG_TOUCH = 1, // set if the event is touch, otherwise hardware }; /* ``` -### `km_kbp_get_engine_attrs` +### `km_core_get_engine_attrs` ##### Description: Get access processors attributes describing version and technology implemented. ##### Return: -A pointer to a `km_kbp_attr` structure. Do not modify the contents of this +A pointer to a `km_core_attr` structure. Do not modify the contents of this structure. ##### Parameters: -- __state__: An opaque pointer to an `km_kbp_state`. +- __state__: An opaque pointer to an `km_core_state`. ```c */ KMN_API -km_kbp_attr const * -km_kbp_get_engine_attrs(km_kbp_state const *state); +km_core_attr const * +km_core_get_engine_attrs(km_core_state const *state); /* ``` -### `km_kbp_process_event` +### `km_core_process_event` ##### Description: Run the keyboard on an opaque state object with the provided virtual key and modifer key state. Updates the state object as appropriate and fills out its action list. @@ -1090,10 +1090,10 @@ key state. Updates the state object as appropriate and fills out its action list The action list will be cleared at the start of this call; options and context in the state may also be modified. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_NO_MEM`: +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_NO_MEM`: In the event memory is unavailable to allocate internal buffers. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: +- `KM_CORE_STATUS_INVALID_ARGUMENT`: In the event the `state` pointer is null or an invalid virtual key or modifier state is passed. @@ -1102,35 +1102,35 @@ state is passed. - __vk__: A virtual key to be processed. - __modifier_state__: The combinations of modifier keys set at the time key `vk` was pressed, bitmask -from the `km_kbp_modifier_state` enum. -- __event_flags__: Event level flags, see km_kbp_event_flags +from the `km_core_modifier_state` enum. +- __event_flags__: Event level flags, see km_core_event_flags ```c */ KMN_API -km_kbp_status -km_kbp_process_event(km_kbp_state *state, - km_kbp_virtual_key vk, +km_core_status +km_core_process_event(km_core_state *state, + km_core_virtual_key vk, uint16_t modifier_state, uint8_t is_key_down, uint16_t event_flags); /* ``` -### `km_kbp_process_queued_actions` +### `km_core_process_queued_actions` ##### Description: Process the keyboard processors queued actions for the opaque state object. Updates the state object as appropriate and fills out its action list. -The client can add actions externally via the `km_kbp_state_queue_action_items` and +The client can add actions externally via the `km_core_state_queue_action_items` and then request the processing of the actions with this method. The state action list will be cleared at the start of this call; options and context in the state may also be modified. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_NO_MEM`: +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_NO_MEM`: In the event memory is unavailable to allocate internal buffers. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: +- `KM_CORE_STATUS_INVALID_ARGUMENT`: In the event the `state` pointer is null ##### Parameters: @@ -1139,20 +1139,20 @@ In the event the `state` pointer is null ```c */ KMN_API -km_kbp_status -km_kbp_process_queued_actions(km_kbp_state *state); +km_core_status +km_core_process_queued_actions(km_core_state *state); /* ``` -### `km_kbp_event` +### `km_core_event` ##### Description: Tell the keyboard processor that an external event has occurred, such as a keyboard being activated through the language switching UI. ##### Return status: -- `KM_KBP_STATUS_OK`: On success. -- `KM_KBP_STATUS_NO_MEM`: +- `KM_CORE_STATUS_OK`: On success. +- `KM_CORE_STATUS_NO_MEM`: In the event memory is unavailable to allocate internal buffers. -- `KM_KBP_STATUS_INVALID_ARGUMENT`: +- `KM_CORE_STATUS_INVALID_ARGUMENT`: In the event the `state` pointer is null or an invalid event or data is passed. The keyboard processor may generate actions which should be processed by the @@ -1163,24 +1163,24 @@ the state may also be modified. ##### Parameters: - __state__: A pointer to the opaque state object. -- __event__: The event to be processed, from km_kbp_event_code enumeration +- __event__: The event to be processed, from km_core_event_code enumeration - __data__: Additional event-specific data. Currently unused, must be nullptr. ```c */ KMN_API -km_kbp_status -km_kbp_event( - km_kbp_state *state, +km_core_status +km_core_event( + km_core_state *state, uint32_t event, void* data ); -enum km_kbp_event_code { +enum km_core_event_code { // A keyboard has been activated by the user. The processor may use this // event, for example, to switch caps lock state or provide other UX. - KM_KBP_EVENT_KEYBOARD_ACTIVATED = 1, - //future: KM_KBP_EVENT_KEYBOARD_DEACTIVATED = 2, + KM_CORE_EVENT_KEYBOARD_ACTIVATED = 1, + //future: KM_CORE_EVENT_KEYBOARD_DEACTIVATED = 2, }; #if defined(__cplusplus) diff --git a/core/include/keyman/keyboardprocessor_bits.h b/core/include/keyman/keyman_core_api_bits.h similarity index 96% rename from core/include/keyman/keyboardprocessor_bits.h rename to core/include/keyman/keyman_core_api_bits.h index 03892a62afb..be2886d0583 100644 --- a/core/include/keyman/keyboardprocessor_bits.h +++ b/core/include/keyman/keyman_core_api_bits.h @@ -27,7 +27,7 @@ #endif #if defined _WIN32 || defined __CYGWIN__ - typedef wchar_t const * km_kbp_path_name; + typedef wchar_t const * km_core_path_name; #define _KM_KBP_PATH_SEPARATOR (L'\\') #define _KM_KBP_EXT_SEPARATOR (L'.') #if defined __GNUC__ // These three will be redefined for Windows @@ -43,7 +43,7 @@ #define _kmn_import_flag dllimport #define _kmn_static_flag #else - typedef char const * km_kbp_path_name; + typedef char const * km_core_path_name; #define _KM_KBP_PATH_SEPARATOR ('/') #define _KM_KBP_EXT_SEPARATOR ('.') #endif diff --git a/core/include/keyman/keyman_core_api_consts.h b/core/include/keyman/keyman_core_api_consts.h new file mode 100644 index 00000000000..67d72edb3d1 --- /dev/null +++ b/core/include/keyman/keyman_core_api_consts.h @@ -0,0 +1,9 @@ +#pragma once + +// Defined environment options for KMX processor +#define KM_CORE_KMX_ENV_PLATFORM u"platform" +#define KM_CORE_KMX_ENV_BASELAYOUT u"baseLayout" +#define KM_CORE_KMX_ENV_BASELAYOUTALT u"baseLayoutAlt" +#define KM_CORE_KMX_ENV_SIMULATEALTGR u"simulateAltgr" +#define KM_CORE_KMX_ENV_CAPSLOCK u"capsLock" +#define KM_CORE_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT u"baseLayoutGivesCtrlRAltForRAlt" diff --git a/core/include/keyman/keyboardprocessor_debug.h b/core/include/keyman/keyman_core_api_debug.h similarity index 60% rename from core/include/keyman/keyboardprocessor_debug.h rename to core/include/keyman/keyman_core_api_debug.h index 380b8ee3012..8f80481d2c5 100644 --- a/core/include/keyman/keyboardprocessor_debug.h +++ b/core/include/keyman/keyman_core_api_debug.h @@ -15,8 +15,8 @@ #include #include -#include -#include +#include +#include #if defined(__cplusplus) extern "C" @@ -24,7 +24,7 @@ extern "C" #endif /** - * The maximum size of context in km_kbp_cp units for a single debug + * The maximum size of context in km_core_cp units for a single debug * event. This is taken from MAXCONTEXT in keyman32 (Windows) and is purely * a convenience value. We can increase it if there is a demonstrated need. */ @@ -42,31 +42,31 @@ extern "C" * These modifier flags are used internally in the kmx engine, so will be * exposed only in debugging modifier states. */ -#define KM_KBP_MODIFIER_VIRTUALKEY 0x4000 -#define KM_KBP_MODIFIER_VIRTUALCHARKEY 0x8000 +#define KM_CORE_MODIFIER_VIRTUALKEY 0x4000 +#define KM_CORE_MODIFIER_VIRTUALCHARKEY 0x8000 /** * Input key event data. The `character` member is derived from * a US English key event for vk + modifier_state, and is 0 if * the vk + modifier_state do not generate a character. * - * Used only in event type KM_KBP_DEBUG_BEGIN. + * Used only in event type KM_CORE_DEBUG_BEGIN. */ typedef struct { uint16_t vk; uint16_t modifier_state; char16_t character; -} km_kbp_state_debug_key_info; +} km_core_state_debug_key_info; /** * Option event data. * - * Used only in event type KM_KBP_DEBUG_SET_OPTION. + * Used only in event type KM_CORE_DEBUG_SET_OPTION. */ typedef struct { void *store; // LPSTORE - km_kbp_cp value[DEBUG_MAX_CONTEXT]; // value to be saved into the store -} km_kbp_state_debug_kmx_option_info; + km_core_cp value[DEBUG_MAX_CONTEXT]; // value to be saved into the store +} km_core_state_debug_kmx_option_info; /** * KMX processor data for each event. kmx_base.h defines the types that are @@ -76,11 +76,11 @@ typedef struct { * The context value here will be an intermediate value, and may differ * from event to event as the context can be rewritten for each rule match. * - * Used in all event types except KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_END. + * Used in all event types except KM_CORE_DEBUG_BEGIN, KM_CORE_DEBUG_END. */ typedef struct { - km_kbp_cp context[DEBUG_MAX_CONTEXT]; // The context matched by the rule (? may not need this?) // TODO: rename to context_matched + km_core_cp context[DEBUG_MAX_CONTEXT]; // The context matched by the rule (? may not need this?) // TODO: rename to context_matched void *group; // LPGROUP void *rule; // LPKEY uint16_t store_offsets[DEBUG_STORE_OFFSETS_SIZE]; // pairs--store, char position, terminated by 0xFFFF // TODO use a better structure here @@ -89,8 +89,8 @@ typedef struct { /// the debugger; the debugger uses this to determine when to /// execute the actions when single-stepping. uint16_t first_action; - km_kbp_state_debug_kmx_option_info option; -} km_kbp_state_debug_kmx_info; + km_core_state_debug_kmx_option_info option; +} km_core_state_debug_kmx_info; /** * A single debug event. @@ -98,38 +98,38 @@ typedef struct { typedef struct { uint32_t type; // 32 bits is better optimized than 8 bits uint32_t flags; - km_kbp_state_debug_key_info key_info; - km_kbp_state_debug_kmx_info kmx_info; -} km_kbp_state_debug_item; + km_core_state_debug_key_info key_info; + km_core_state_debug_kmx_info kmx_info; +} km_core_state_debug_item; /** * A single debug event. */ -enum km_kbp_debug_type { - KM_KBP_DEBUG_BEGIN = 0, - //KM_KBP_DEBUG_BEGIN_ANSI = 1, // not supported; instead rewrite ansi keyboards to Unicode with mcompile - KM_KBP_DEBUG_GROUP_ENTER = 2, - KM_KBP_DEBUG_GROUP_EXIT = 3, - KM_KBP_DEBUG_RULE_ENTER = 4, - KM_KBP_DEBUG_RULE_EXIT = 5, - KM_KBP_DEBUG_MATCH_ENTER = 6, - KM_KBP_DEBUG_MATCH_EXIT = 7, - KM_KBP_DEBUG_NOMATCH_ENTER = 8, - KM_KBP_DEBUG_NOMATCH_EXIT = 9, - KM_KBP_DEBUG_END = 10, - KM_KBP_DEBUG_SET_OPTION = 11, +enum km_core_debug_type { + KM_CORE_DEBUG_BEGIN = 0, + //KM_CORE_DEBUG_BEGIN_ANSI = 1, // not supported; instead rewrite ansi keyboards to Unicode with mcompile + KM_CORE_DEBUG_GROUP_ENTER = 2, + KM_CORE_DEBUG_GROUP_EXIT = 3, + KM_CORE_DEBUG_RULE_ENTER = 4, + KM_CORE_DEBUG_RULE_EXIT = 5, + KM_CORE_DEBUG_MATCH_ENTER = 6, + KM_CORE_DEBUG_MATCH_EXIT = 7, + KM_CORE_DEBUG_NOMATCH_ENTER = 8, + KM_CORE_DEBUG_NOMATCH_EXIT = 9, + KM_CORE_DEBUG_END = 10, + KM_CORE_DEBUG_SET_OPTION = 11, }; -// Flags for KM_KBP_DEBUG_GROUP_EXIT -#define KM_KBP_DEBUG_FLAG_RECURSIVE_OVERFLOW 0x0001 -#define KM_KBP_DEBUG_FLAG_NOMATCH 0x0002 +// Flags for KM_CORE_DEBUG_GROUP_EXIT +#define KM_CORE_DEBUG_FLAG_RECURSIVE_OVERFLOW 0x0001 +#define KM_CORE_DEBUG_FLAG_NOMATCH 0x0002 -// Flags for KM_KBP_DEBUG_BEGIN +// Flags for KM_CORE_DEBUG_BEGIN // TODO: do we need this at all? -#define KM_KBP_DEBUG_FLAG_UNICODE 0x0001 // Always set +#define KM_CORE_DEBUG_FLAG_UNICODE 0x0001 // Always set -// Flags for KM_KBP_DEBUG_END -#define KM_KBP_DEBUG_FLAG_OUTPUTKEYSTROKE 0x0001 +// Flags for KM_CORE_DEBUG_END +#define KM_CORE_DEBUG_FLAG_OUTPUTKEYSTROKE 0x0001 /** * Enable or disable debug tracing @@ -137,11 +137,11 @@ enum km_kbp_debug_type { * @param state Pointer to initialized state * @param value Set to 1 to enable debugging, 0 to disable * - * @returns KM_KBP_STATUS_OK on success + * @returns KM_CORE_STATUS_OK on success */ KMN_API -km_kbp_status -km_kbp_state_debug_set(km_kbp_state *state, int value); +km_core_status +km_core_state_debug_set(km_core_state *state, int value); /** * Get current debug tracing status @@ -152,7 +152,7 @@ km_kbp_state_debug_set(km_kbp_state *state, int value); */ KMN_API uint8_t -km_kbp_state_debug_get(km_kbp_state const *state); +km_core_state_debug_get(km_core_state const *state); /** * Read current debug trace log @@ -162,11 +162,11 @@ km_kbp_state_debug_get(km_kbp_state const *state); * nullptr if not required. * * @returns pointer to read only array of debug item events, - * with last entry guaranteed to be KM_KBP_DEBUG_END. + * with last entry guaranteed to be KM_CORE_DEBUG_END. */ KMN_API -km_kbp_state_debug_item const * -km_kbp_state_debug_items(km_kbp_state const *state, size_t *num_items); +km_core_state_debug_item const * +km_core_state_debug_items(km_core_state const *state, size_t *num_items); #if defined(__cplusplus) } // extern "C" diff --git a/core/include/keyman/keyman_core_api_version.h.in b/core/include/keyman/keyman_core_api_version.h.in new file mode 100644 index 00000000000..064a1391547 --- /dev/null +++ b/core/include/keyman/keyman_core_api_version.h.in @@ -0,0 +1,15 @@ +#define km_core_version_stringify(x) km_core_version_to_string(x) +#define km_core_version_to_string(x) #x + +// Product versioning + +#define KM_CORE_VERSION_MAJOR @majorver@ +#define KM_CORE_VERSION_MINOR @minorver@ +#define KM_CORE_VERSION_PATCH @patchver@ +#define KM_CORE_VERSION_STRING km_core_version_stringify(@majorver@ ## . ## @minorver@ ## . ## @patchver@ ## .0) + +// API versioning + +#define KM_CORE_LIB_CURRENT @lib_curr@ +#define KM_CORE_LIB_AGE @lib_age@ +#define KM_CORE_LIB_REVISION @lib_rev@ diff --git a/core/include/keyman/keyman_core_api_vkeys.h b/core/include/keyman/keyman_core_api_vkeys.h new file mode 100644 index 00000000000..7e4a6d181aa --- /dev/null +++ b/core/include/keyman/keyman_core_api_vkeys.h @@ -0,0 +1,312 @@ +/* + Copyright: © 2018 SIL International. + Description: API declarations for modifier keys, handy access masks and + Keyman VKEY names. These follow the same keytop->code + associations as the Windows API. This is a separate header to + maintain readability of the primary API header. + Create Date: 17 Oct 2018 + Authors: Tim Eves (TSE) + History: 17 Oct 2018 - TSE - Moved & refactored km_core_modifier_state + from keyman_core_api.h. + - Added VKey and mask definitions. + 6 Oct 2018 - TSE - Move into keyman folder. + +*/ + +#pragma once + +enum km_core_modifier_state { + KM_CORE_MODIFIER_LCTRL = 1 << 0, + KM_CORE_MODIFIER_RCTRL = 1 << 1, + KM_CORE_MODIFIER_LALT = 1 << 2, + KM_CORE_MODIFIER_RALT = 1 << 3, + KM_CORE_MODIFIER_SHIFT = 1 << 4, + KM_CORE_MODIFIER_CTRL = 1 << 5, + KM_CORE_MODIFIER_ALT = 1 << 6, + /* + KM_CORE_MODIFIER_META = 1 << 7, // Either Meta-key flag (tentative). Not usable by keyboards currently + // Used internally (currently, only by KMW) to ensure Meta-key + // shortcuts safely bypass rules + // Meta key = Command key on macOS, Windows key on Windows + */ + KM_CORE_MODIFIER_CAPS = 1 << 8, + KM_CORE_MODIFIER_NOCAPS = 1 << 9, + /* + KM_CORE_MODIFIER_NUMLOCK = 1 << 10, + KM_CORE_MODIFIER_NONUMLOCK = 1 << 11, + KM_CORE_MODIFIER_SCROLLOCK = 1 << 12, + KM_CORE_MODIFIER_NOSCROLLOCK = 1 << 13, + KM_CORE_MODIFIER_VIRTUALKEY = 1 << 14, + */ +}; + +enum km_core_modifier_mask { + KM_CORE_MODIFIER_MASK_ALL = 0x7f, + KM_CORE_MODIFIER_MASK_ALT_GR_SIM = KM_CORE_MODIFIER_LCTRL|KM_CORE_MODIFIER_LALT, + KM_CORE_MODIFIER_MASK_CHIRAL = 0x1f, + KM_CORE_MODIFIER_MASK_IS_CHIRAL = 0x0f, + KM_CORE_MODIFIER_MASK_NON_CHIRAL = 0x7f, + KM_CORE_MODIFIER_MASK_CAPS = 0x0300, +/*KM_CORE_MODIFIER_MASK_NUMLOCK = 0x0C00, + KM_CORE_MODIFIER_MASK_SCROLLLOCK = 0x3000,*/ +}; + +// These are Windows API VKEYs, using Keyman VKEY names. +enum km_kpb_virtual_key { + KM_CORE_VKEY__00, + KM_CORE_VKEY_LBUTTON, + KM_CORE_VKEY_RBUTTON, + KM_CORE_VKEY_CANCEL, + KM_CORE_VKEY_MBUTTON, + KM_CORE_VKEY__05, + KM_CORE_VKEY__06, + KM_CORE_VKEY__07, + KM_CORE_VKEY_BKSP, + KM_CORE_VKEY_TAB, + KM_CORE_VKEY__0A, + KM_CORE_VKEY__0B, + KM_CORE_VKEY_KP5, + KM_CORE_VKEY_ENTER, + KM_CORE_VKEY__0E, + KM_CORE_VKEY__0F, + KM_CORE_VKEY_SHIFT, + KM_CORE_VKEY_CONTROL, + KM_CORE_VKEY_ALT, + KM_CORE_VKEY_PAUSE, + KM_CORE_VKEY_CAPS, + KM_CORE_VKEY__15, + KM_CORE_VKEY__16, + KM_CORE_VKEY__17, + KM_CORE_VKEY__18, + KM_CORE_VKEY__19, + KM_CORE_VKEY__1A, + KM_CORE_VKEY_ESC, + KM_CORE_VKEY__1C, + KM_CORE_VKEY__1D, + KM_CORE_VKEY__1E, + KM_CORE_VKEY__1F, + KM_CORE_VKEY_SPACE, + KM_CORE_VKEY_PGUP, + KM_CORE_VKEY_PGDN, + KM_CORE_VKEY_END, + KM_CORE_VKEY_HOME, + KM_CORE_VKEY_LEFT, + KM_CORE_VKEY_UP, + KM_CORE_VKEY_RIGHT, + KM_CORE_VKEY_DOWN, + KM_CORE_VKEY_SEL, + KM_CORE_VKEY_PRINT, + KM_CORE_VKEY_EXEC, + KM_CORE_VKEY_PRTSCN, + KM_CORE_VKEY_INS, + KM_CORE_VKEY_DEL, + KM_CORE_VKEY_HELP, + KM_CORE_VKEY_0, + KM_CORE_VKEY_1, + KM_CORE_VKEY_2, + KM_CORE_VKEY_3, + KM_CORE_VKEY_4, + KM_CORE_VKEY_5, + KM_CORE_VKEY_6, + KM_CORE_VKEY_7, + KM_CORE_VKEY_8, + KM_CORE_VKEY_9, + KM_CORE_VKEY__3A, + KM_CORE_VKEY__3B, + KM_CORE_VKEY__3C, + KM_CORE_VKEY__3D, + KM_CORE_VKEY__3E, + KM_CORE_VKEY__3F, + KM_CORE_VKEY__40, + KM_CORE_VKEY_A, + KM_CORE_VKEY_B, + KM_CORE_VKEY_C, + KM_CORE_VKEY_D, + KM_CORE_VKEY_E, + KM_CORE_VKEY_F, + KM_CORE_VKEY_G, + KM_CORE_VKEY_H, + KM_CORE_VKEY_I, + KM_CORE_VKEY_J, + KM_CORE_VKEY_K, + KM_CORE_VKEY_L, + KM_CORE_VKEY_M, + KM_CORE_VKEY_N, + KM_CORE_VKEY_O, + KM_CORE_VKEY_P, + KM_CORE_VKEY_Q, + KM_CORE_VKEY_R, + KM_CORE_VKEY_S, + KM_CORE_VKEY_T, + KM_CORE_VKEY_U, + KM_CORE_VKEY_V, + KM_CORE_VKEY_W, + KM_CORE_VKEY_X, + KM_CORE_VKEY_Y, + KM_CORE_VKEY_Z, + KM_CORE_VKEY__5B, + KM_CORE_VKEY__5C, + KM_CORE_VKEY__5D, + KM_CORE_VKEY__5E, + KM_CORE_VKEY__5F, + KM_CORE_VKEY_NP0, + KM_CORE_VKEY_NP1, + KM_CORE_VKEY_NP2, + KM_CORE_VKEY_NP3, + KM_CORE_VKEY_NP4, + KM_CORE_VKEY_NP5, + KM_CORE_VKEY_NP6, + KM_CORE_VKEY_NP7, + KM_CORE_VKEY_NP8, + KM_CORE_VKEY_NP9, + KM_CORE_VKEY_NPSTAR, + KM_CORE_VKEY_NPPLUS, + KM_CORE_VKEY_SEPARATOR, + KM_CORE_VKEY_NPMINUS, + KM_CORE_VKEY_NPDOT, + KM_CORE_VKEY_NPSLASH, + KM_CORE_VKEY_F1, + KM_CORE_VKEY_F2, + KM_CORE_VKEY_F3, + KM_CORE_VKEY_F4, + KM_CORE_VKEY_F5, + KM_CORE_VKEY_F6, + KM_CORE_VKEY_F7, + KM_CORE_VKEY_F8, + KM_CORE_VKEY_F9, + KM_CORE_VKEY_F10, + KM_CORE_VKEY_F11, + KM_CORE_VKEY_F12, + KM_CORE_VKEY_F13, + KM_CORE_VKEY_F14, + KM_CORE_VKEY_F15, + KM_CORE_VKEY_F16, + KM_CORE_VKEY_F17, + KM_CORE_VKEY_F18, + KM_CORE_VKEY_F19, + KM_CORE_VKEY_F20, + KM_CORE_VKEY_F21, + KM_CORE_VKEY_F22, + KM_CORE_VKEY_F23, + KM_CORE_VKEY_F24, + KM_CORE_VKEY__88, + KM_CORE_VKEY__89, + KM_CORE_VKEY__8A, + KM_CORE_VKEY__8B, + KM_CORE_VKEY__8C, + KM_CORE_VKEY__8D, + KM_CORE_VKEY__8E, + KM_CORE_VKEY__8F, + KM_CORE_VKEY_NUMLOCK, + KM_CORE_VKEY_SCROLL, + KM_CORE_VKEY__92, + KM_CORE_VKEY__93, + KM_CORE_VKEY__94, + KM_CORE_VKEY__95, + KM_CORE_VKEY__96, + KM_CORE_VKEY__97, + KM_CORE_VKEY__98, + KM_CORE_VKEY__99, + KM_CORE_VKEY__9A, + KM_CORE_VKEY__9B, + KM_CORE_VKEY__9C, + KM_CORE_VKEY__9D, + KM_CORE_VKEY__9E, + KM_CORE_VKEY__9F, + KM_CORE_VKEY__A0, + KM_CORE_VKEY__A1, + KM_CORE_VKEY__A2, + KM_CORE_VKEY__A3, + KM_CORE_VKEY__A4, + KM_CORE_VKEY__A5, + KM_CORE_VKEY__A6, + KM_CORE_VKEY__A7, + KM_CORE_VKEY__A8, + KM_CORE_VKEY__A9, + KM_CORE_VKEY__AA, + KM_CORE_VKEY__AB, + KM_CORE_VKEY__AC, + KM_CORE_VKEY__AD, + KM_CORE_VKEY__AE, + KM_CORE_VKEY__AF, + KM_CORE_VKEY__B0, + KM_CORE_VKEY__B1, + KM_CORE_VKEY__B2, + KM_CORE_VKEY__B3, + KM_CORE_VKEY__B4, + KM_CORE_VKEY__B5, + KM_CORE_VKEY__B6, + KM_CORE_VKEY__B7, + KM_CORE_VKEY__B8, + KM_CORE_VKEY__B9, + KM_CORE_VKEY_COLON, + KM_CORE_VKEY_EQUAL, + KM_CORE_VKEY_COMMA, + KM_CORE_VKEY_HYPHEN, + KM_CORE_VKEY_PERIOD, + KM_CORE_VKEY_SLASH, + KM_CORE_VKEY_BKQUOTE, + KM_CORE_VKEY__C1, + KM_CORE_VKEY__C2, + KM_CORE_VKEY__C3, + KM_CORE_VKEY__C4, + KM_CORE_VKEY__C5, + KM_CORE_VKEY__C6, + KM_CORE_VKEY__C7, + KM_CORE_VKEY__C8, + KM_CORE_VKEY__C9, + KM_CORE_VKEY__CA, + KM_CORE_VKEY__CB, + KM_CORE_VKEY__CC, + KM_CORE_VKEY__CD, + KM_CORE_VKEY__CE, + KM_CORE_VKEY__CF, + KM_CORE_VKEY__D0, + KM_CORE_VKEY__D1, + KM_CORE_VKEY__D2, + KM_CORE_VKEY__D3, + KM_CORE_VKEY__D4, + KM_CORE_VKEY__D5, + KM_CORE_VKEY__D6, + KM_CORE_VKEY__D7, + KM_CORE_VKEY__D8, + KM_CORE_VKEY__D9, + KM_CORE_VKEY__DA, + KM_CORE_VKEY_LBRKT, + KM_CORE_VKEY_BKSLASH, + KM_CORE_VKEY_RBRKT, + KM_CORE_VKEY_QUOTE, + KM_CORE_VKEY_oDF, + KM_CORE_VKEY_oE0, + KM_CORE_VKEY_oE1, + KM_CORE_VKEY_oE2, // 102nd key on European layouts + KM_CORE_VKEY_oE3, + KM_CORE_VKEY_oE4, + KM_CORE_VKEY__E5, + KM_CORE_VKEY_oE6, + KM_CORE_VKEY__E7, + KM_CORE_VKEY__E8, + KM_CORE_VKEY_oE9, + KM_CORE_VKEY_oEA, + KM_CORE_VKEY_oEB, + KM_CORE_VKEY_oEC, + KM_CORE_VKEY_oED, + KM_CORE_VKEY_oEE, + KM_CORE_VKEY_oEF, + KM_CORE_VKEY_oF0, + KM_CORE_VKEY_oF1, + KM_CORE_VKEY_oF2, + KM_CORE_VKEY_oF3, + KM_CORE_VKEY_oF4, + KM_CORE_VKEY_oF5, + KM_CORE_VKEY__F6, + KM_CORE_VKEY__F7, + KM_CORE_VKEY__F8, + KM_CORE_VKEY__F9, + KM_CORE_VKEY__FA, + KM_CORE_VKEY__FB, + KM_CORE_VKEY__FC, + KM_CORE_VKEY__FD, + KM_CORE_VKEY__FE, + KM_CORE_VKEY__FF, +}; diff --git a/core/include/keyman/meson.build b/core/include/keyman/meson.build index 7da255502de..8ef7d1a9f26 100644 --- a/core/include/keyman/meson.build +++ b/core/include/keyman/meson.build @@ -19,16 +19,16 @@ cfg.set('patchver', project_ver[2]) configure_file( configuration: cfg, - input: 'keyboardprocessor_version.h.in', - output: 'keyboardprocessor_version.h', + input: 'keyman_core_api_version.h.in', + output: 'keyman_core_api_version.h', ) -install_headers('keyboardprocessor.h', - 'keyboardprocessor_bits.h', - 'keyboardprocessor_consts.h', - 'keyboardprocessor_debug.h', - 'keyboardprocessor_vkeys.h', - join_paths(meson.current_build_dir(), 'keyboardprocessor_version.h'), +install_headers('keyman_core_api.h', + 'keyman_core_api_bits.h', + 'keyman_core_api_consts.h', + 'keyman_core_api_debug.h', + 'keyman_core_api_vkeys.h', + join_paths(meson.current_build_dir(), 'keyman_core_api_version.h'), '../../../common/include/km_types.h', '../../../common/include/kmx_file.h', subdir: 'keyman') diff --git a/core/include/ldml/keyboardprocessor_ldml.h b/core/include/ldml/keyboardprocessor_ldml.h index ae244ffbe75..45837b63855 100644 --- a/core/include/ldml/keyboardprocessor_ldml.h +++ b/core/include/ldml/keyboardprocessor_ldml.h @@ -16,6 +16,7 @@ #pragma once #define LDML_BKSP_FLAGS_ERROR 0x1 +#define LDML_CLDR_IMPLIED_FORMS_IMPORT "techpreview/scanCodes-implied.xml" #define LDML_CLDR_IMPLIED_KEYS_IMPORT "techpreview/keys-Latn-implied.xml" #define LDML_CLDR_IMPORT_BASE "cldr" #define LDML_CLDR_VERSION_LATEST "techpreview" @@ -45,11 +46,7 @@ #define LDML_KEYS_MOD_CTRLR 0x2 #define LDML_KEYS_MOD_NONE 0x0 #define LDML_KEYS_MOD_SHIFT 0x10 -#define LDML_LAYR_LIST_HARDWARE_ABNT2 0x1 -#define LDML_LAYR_LIST_HARDWARE_ISO 0x2 -#define LDML_LAYR_LIST_HARDWARE_JIS 0x3 -#define LDML_LAYR_LIST_HARDWARE_TOUCH 0x0 -#define LDML_LAYR_LIST_HARDWARE_US 0x4 +#define LDML_LAYR_LIST_HARDWARE_TOUCH "touch" #define LDML_LENGTH_BKSP 0xC #define LDML_LENGTH_BKSP_ITEM 0x10 #define LDML_LENGTH_DISP 0x10 @@ -91,8 +88,6 @@ #define LDML_LENGTH_USET_USET 0xC #define LDML_LENGTH_VARS 0x10 #define LDML_LENGTH_VARS_ITEM 0x10 -#define LDML_LENGTH_VKEY 0xC -#define LDML_LENGTH_VKEY_ITEM 0x8 #define LDML_MARKER_ANY_INDEX 0xD7FF #define LDML_MARKER_CODE 0x8 #define LDML_MARKER_MAX_COUNT 0xD7FE @@ -129,8 +124,6 @@ #define LDML_SECTIONNAME_USET "uset" #define LDML_SECTIONID_VARS 0x73726176 /* "vars" */ #define LDML_SECTIONNAME_VARS "vars" -#define LDML_SECTIONID_VKEY 0x79656B76 /* "vkey" */ -#define LDML_SECTIONNAME_VKEY "vkey" #define LDML_TRAN_FLAGS_ERROR 0x1 #define LDML_TRAN_GROUP_TYPE_REORDER 0x1 #define LDML_TRAN_GROUP_TYPE_TRANSFORM 0x0 diff --git a/core/include/ldml/keyboardprocessor_ldml.ts b/core/include/ldml/keyboardprocessor_ldml.ts index ed9ad899ed3..4e9aaedbb13 100644 --- a/core/include/ldml/keyboardprocessor_ldml.ts +++ b/core/include/ldml/keyboardprocessor_ldml.ts @@ -37,8 +37,7 @@ export type SectionIdent = 'strs' | 'tran' | 'uset' | - 'vars' | - 'vkey'; + 'vars'; type SectionMap = { @@ -74,6 +73,10 @@ class Constants { * implied keys file */ readonly cldr_implied_keys_import = `${this.cldr_version_techpreview}/keys-Latn-implied.xml`; + /** + * implied scancodes file + */ + readonly cldr_implied_forms_import = `${this.cldr_version_techpreview}/scanCodes-implied.xml`; /** * Length of a raw section header, in bytes */ @@ -350,35 +353,7 @@ class Constants { /** * for the 'hardware' field indicating a touch keyboard, non-hardware */ - readonly layr_list_hardware_touch = 0; - /** - * for the 'hardware' field indicating an abnt2 layout - */ - readonly layr_list_hardware_abnt2 = 1; - /** - * for the 'hardware' field indicating an iso layout - */ - readonly layr_list_hardware_iso = 2; - /** - * for the 'hardware' field indicating a jis layout - */ - readonly layr_list_hardware_jis = 3; - /** - * for the 'hardware' field indicating a us layout - */ - readonly layr_list_hardware_us = 4; - /** - * Convenience map of layr_list_hardware field values - */ - readonly layr_list_hardware_map: Map = new Map( - [ - ["touch", this.layr_list_hardware_touch], - ["abnt2", this.layr_list_hardware_abnt2], - ["iso", this.layr_list_hardware_iso], - ["jis", this.layr_list_hardware_jis], - ["us", this.layr_list_hardware_us], - ] - ); + readonly layr_list_hardware_touch = 'touch'; /** * Length of each layer entry in the 'layr' section variable part */ @@ -504,19 +479,6 @@ class Constants { */ readonly tran_group_type_reorder = 1; - /* ------------------------------------------------------------------ - * vkey section - ------------------------------------------------------------------ */ - - /** - * Minimum length of the 'vkey' section not including variable parts - */ - readonly length_vkey = 12; - /** - * Length of each item in the 'vkey' section variable part - */ - readonly length_vkey_item = 8; - /* ------------------------------------------------------------------ * vars section * ------------------------------------------------------------------ */ @@ -581,7 +543,6 @@ class Constants { tran: 'tran', uset: 'uset', vars: 'vars', - vkey: 'vkey', }; /** diff --git a/core/include/ldml/tsconfig.build.json b/core/include/ldml/tsconfig.build.json index 17c809a857a..fd0f003272c 100644 --- a/core/include/ldml/tsconfig.build.json +++ b/core/include/ldml/tsconfig.build.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.esm-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "composite": true, "declaration": true, diff --git a/core/include/ldml/tsconfig.json b/core/include/ldml/tsconfig.json index b89c96a0181..fa4135b0212 100644 --- a/core/include/ldml/tsconfig.json +++ b/core/include/ldml/tsconfig.json @@ -1,10 +1,8 @@ { - "extends": "../../../tsconfig-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "composite": true, "declaration": true, - "module": "es2020", - "moduleResolution": "node", "rootDir": ".", "outDir": "build/", }, diff --git a/core/src/context.hpp b/core/src/context.hpp index b344e0657cd..08edc861634 100644 --- a/core/src/context.hpp +++ b/core/src/context.hpp @@ -3,13 +3,13 @@ Description: Internal context class and adaptor class for the API. Create Date: 2 Oct 2018 Authors: Tim Eves (TSE) - History: 2 Oct 2018 - TSE - Refactored out of km_kbp_context_api.cpp + History: 2 Oct 2018 - TSE - Refactored out of km_core_context_api.cpp */ #pragma once #include #include -#include +#include // Forward declarations class json; @@ -20,32 +20,32 @@ namespace kbp // This will likely be replaced with a class implementing a more space // efficient data structure such as a ring buffer or bounded queue. -class context: public std::list +class context: public std::list { public: - void push_character(km_kbp_usv); + void push_character(km_core_usv); void push_marker(uint32_t); }; inline -void context::push_character(km_kbp_usv usv) { - emplace_back(km_kbp_context_item { KM_KBP_CT_CHAR, {0,}, {usv} }); +void context::push_character(km_core_usv usv) { + emplace_back(km_core_context_item { KM_CORE_CT_CHAR, {0,}, {usv} }); } inline void context::push_marker(uint32_t marker) { - emplace_back(km_kbp_context_item { KM_KBP_CT_MARKER, {0,}, {marker} }); + emplace_back(km_core_context_item { KM_CORE_CT_MARKER, {0,}, {marker} }); } } // namespace kbp } // namespace km json & operator << (json &, km::kbp::context const &); -json & operator << (json &, km_kbp_context_item const &); +json & operator << (json &, km_core_context_item const &); -struct km_kbp_context : public km::kbp::context +struct km_core_context : public km::kbp::context { }; diff --git a/core/src/debug.hpp b/core/src/debug.hpp index 8069cfbc532..935fad155ca 100644 --- a/core/src/debug.hpp +++ b/core/src/debug.hpp @@ -9,20 +9,20 @@ #include #include -#include -#include +#include +#include namespace km { namespace kbp { -class debug_items : public std::vector +class debug_items : public std::vector { private: bool _is_enabled; public: template debug_items(Args&&... args); - void push_begin(km_kbp_state_debug_key_info *key_info, uint32_t flags); + void push_begin(km_core_state_debug_key_info *key_info, uint32_t flags); void push_end(uint16_t first_action, uint32_t flags); void assert_push_entry(); bool is_enabled() const noexcept; @@ -31,29 +31,29 @@ class debug_items : public std::vector template debug_items::debug_items(Args&&... args) -: std::vector(std::forward(args)...) +: std::vector(std::forward(args)...) { // Ensure the debug_items list is terminated in case the client calls - // km_kbp_state_debug_items before they call process_event. + // km_core_state_debug_items before they call process_event. _is_enabled = false; push_end(0, 0); } inline void debug_items::assert_push_entry() { - assert(empty() || (!empty() && back().type != KM_KBP_DEBUG_END)); + assert(empty() || (!empty() && back().type != KM_CORE_DEBUG_END)); } inline -void debug_items::push_begin(km_kbp_state_debug_key_info *key_info, uint32_t flags) { +void debug_items::push_begin(km_core_state_debug_key_info *key_info, uint32_t flags) { assert_push_entry(); - emplace_back(km_kbp_state_debug_item{ KM_KBP_DEBUG_BEGIN, flags, {*key_info, }, { }}); + emplace_back(km_core_state_debug_item{ KM_CORE_DEBUG_BEGIN, flags, {*key_info, }, { }}); } inline void debug_items::push_end(uint16_t first_action, uint32_t flags) { assert_push_entry(); - emplace_back(km_kbp_state_debug_item{ KM_KBP_DEBUG_END, flags, { }, { u"", nullptr, nullptr, { }, first_action, {} } }); + emplace_back(km_core_state_debug_item{ KM_CORE_DEBUG_END, flags, { }, { u"", nullptr, nullptr, { }, first_action, {} } }); } inline diff --git a/core/src/debuglog.h b/core/src/debuglog.h index 16fba464285..1ecbaca6124 100644 --- a/core/src/debuglog.h +++ b/core/src/debuglog.h @@ -1,6 +1,6 @@ /* Debugging */ -#include +#include namespace km { namespace kbp { diff --git a/core/src/keyboard.cpp b/core/src/keyboard.cpp index 977da597d31..47693b9b664 100644 --- a/core/src/keyboard.cpp +++ b/core/src/keyboard.cpp @@ -3,7 +3,7 @@ Description: Internal keyboard class and adaptor class for the API. Create Date: 2 Oct 2018 Authors: Tim Eves (TSE) - History: 7 Oct 2018 - TSE - Refactored out of km_kbp_keyboard_api.cpp + History: 7 Oct 2018 - TSE - Refactored out of km_core_keyboard_api.cpp */ #include "keyboard.hpp" #include "jsonpp.hpp" diff --git a/core/src/keyboard.hpp b/core/src/keyboard.hpp index 57f8128c781..97329b7e761 100644 --- a/core/src/keyboard.hpp +++ b/core/src/keyboard.hpp @@ -3,7 +3,7 @@ Description: Internal keyboard class and adaptor class for the API. Create Date: 2 Oct 2018 Authors: Tim Eves (TSE) - History: 2 Oct 2018 - TSE - Refactored out of km_kbp_keyboard_api.cpp + History: 2 Oct 2018 - TSE - Refactored out of km_core_keyboard_api.cpp */ #pragma once @@ -11,7 +11,7 @@ #include #include -#include +#include #include "option.hpp" #include "path.hpp" @@ -22,7 +22,7 @@ class json; namespace km { namespace kbp { - class keyboard_attributes : public km_kbp_keyboard_attrs + class keyboard_attributes : public km_core_keyboard_attrs { std::u16string _keyboard_id; std::u16string _version_string; @@ -36,7 +36,7 @@ namespace kbp using path_type = decltype(_folder_path); keyboard_attributes() - : km_kbp_keyboard_attrs {nullptr, nullptr, nullptr, nullptr} {} + : km_core_keyboard_attrs {nullptr, nullptr, nullptr, nullptr} {} keyboard_attributes(keyboard_attributes const &) = delete; keyboard_attributes(keyboard_attributes &&); diff --git a/core/src/km_kbp_context_api.cpp b/core/src/km_core_context_api.cpp similarity index 54% rename from core/src/km_kbp_context_api.cpp rename to core/src/km_core_context_api.cpp index a9111f66c45..3655af4e964 100644 --- a/core/src/km_kbp_context_api.cpp +++ b/core/src/km_core_context_api.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include "context.hpp" #include "jsonpp.hpp" @@ -20,44 +20,44 @@ namespace { template - km_kbp_status + km_core_status _context_items_from(typename U::codeunit_t const *text, - km_kbp_context_item **out_ptr) + km_core_context_item **out_ptr) { assert(text); assert(out_ptr); - if (!text || !out_ptr) return KM_KBP_STATUS_INVALID_ARGUMENT; + if (!text || !out_ptr) return KM_CORE_STATUS_INVALID_ARGUMENT; *out_ptr = nullptr; try { - std::vector res; + std::vector res; for (auto i = typename U::const_iterator(text); *i; ++i) { - if(i.error()) return KM_KBP_STATUS_INVALID_UTF; - res.emplace_back(km_kbp_context_item {KM_KBP_CT_CHAR, {0,}, {*i}}); + if(i.error()) return KM_CORE_STATUS_INVALID_UTF; + res.emplace_back(km_core_context_item {KM_CORE_CT_CHAR, {0,}, {*i}}); } // Terminate the context_items array. - res.emplace_back(km_kbp_context_item KM_KBP_CONTEXT_ITEM_END); + res.emplace_back(km_core_context_item KM_CORE_CONTEXT_ITEM_END); - *out_ptr = new km_kbp_context_item[res.size()]; + *out_ptr = new km_core_context_item[res.size()]; std::move(res.begin(), res.end(), *out_ptr); } catch (std::bad_alloc &) { - return KM_KBP_STATUS_NO_MEM; + return KM_CORE_STATUS_NO_MEM; } - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } template - km_kbp_status _context_items_to(km_kbp_context_item const *ci, + km_core_status _context_items_to(km_core_context_item const *ci, typename U::codeunit_t *buf, size_t * sz_ptr) { assert(ci); assert(sz_ptr); - if (!ci || !sz_ptr) return KM_KBP_STATUS_INVALID_ARGUMENT; + if (!ci || !sz_ptr) return KM_CORE_STATUS_INVALID_ARGUMENT; auto const buf_size = *sz_ptr; if (buf && buf_size > 0) @@ -65,9 +65,9 @@ namespace { auto i = typename U::iterator(buf); auto const e = decltype(i)(buf + buf_size - 1); - for (;i != e && ci->type != KM_KBP_CT_END && !i.error(); ++ci) + for (;i != e && ci->type != KM_CORE_CT_END && !i.error(); ++ci) { - if (ci->type == KM_KBP_CT_CHAR) + if (ci->type == KM_CORE_CT_CHAR) { *i = ci->character; ++i; } @@ -77,13 +77,13 @@ namespace { *sz_ptr = buf_size - (e - i); // Skip over any final markers - they are execluded from context - while(ci->type == KM_KBP_CT_MARKER) { + while(ci->type == KM_CORE_CT_MARKER) { ci++; } - return ci->type == KM_KBP_CT_END - ? KM_KBP_STATUS_OK - : KM_KBP_STATUS_INSUFFICENT_BUFFER; + return ci->type == KM_CORE_CT_END + ? KM_CORE_STATUS_OK + : KM_CORE_STATUS_INSUFFICENT_BUFFER; } else { @@ -92,38 +92,38 @@ namespace { do { - if (ci->type == KM_KBP_CT_CHAR) + if (ci->type == KM_CORE_CT_CHAR) { int8_t l = 4; U::codec::put(cps, ci->character, l); n += l; } } - while(ci++->type != KM_KBP_CT_END); + while(ci++->type != KM_CORE_CT_END); *sz_ptr = n+1; - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } } } -km_kbp_status -km_kbp_context_items_from_utf16(km_kbp_cp const *text, - km_kbp_context_item **out_ptr) +km_core_status +km_core_context_items_from_utf16(km_core_cp const *text, + km_core_context_item **out_ptr) { return _context_items_from(reinterpret_cast(text), out_ptr); } -km_kbp_status -km_kbp_context_items_from_utf8(char const *text, - km_kbp_context_item **out_ptr) +km_core_status +km_core_context_items_from_utf8(char const *text, + km_core_context_item **out_ptr) { return _context_items_from(reinterpret_cast(text), out_ptr); } -km_kbp_status km_kbp_context_items_to_utf8(km_kbp_context_item const *ci, +km_core_status km_core_context_items_to_utf8(km_core_context_item const *ci, char *buf, size_t * sz_ptr) { return _context_items_to(ci, @@ -132,8 +132,8 @@ km_kbp_status km_kbp_context_items_to_utf8(km_kbp_context_item const *ci, } -km_kbp_status km_kbp_context_items_to_utf16(km_kbp_context_item const *ci, - km_kbp_cp *buf, size_t * sz_ptr) +km_core_status km_core_context_items_to_utf16(km_core_context_item const *ci, + km_core_cp *buf, size_t * sz_ptr) { return _context_items_to(ci, reinterpret_cast(buf), @@ -141,41 +141,41 @@ km_kbp_status km_kbp_context_items_to_utf16(km_kbp_context_item const *ci, } -void km_kbp_context_items_dispose(km_kbp_context_item *ci) +void km_core_context_items_dispose(km_core_context_item *ci) { delete [] ci; } -km_kbp_status km_kbp_context_set(km_kbp_context *ctxt, km_kbp_context_item const *ci) +km_core_status km_core_context_set(km_core_context *ctxt, km_core_context_item const *ci) { - km_kbp_context_clear(ctxt); - return km_kbp_context_append(ctxt, ci); + km_core_context_clear(ctxt); + return km_core_context_append(ctxt, ci); } -km_kbp_status km_kbp_context_get(km_kbp_context const *ctxt, - km_kbp_context_item **out_ptr) +km_core_status km_core_context_get(km_core_context const *ctxt, + km_core_context_item **out_ptr) { assert(ctxt); assert(out_ptr); - if (!ctxt || !out_ptr) return KM_KBP_STATUS_INVALID_ARGUMENT; + if (!ctxt || !out_ptr) return KM_CORE_STATUS_INVALID_ARGUMENT; try { - *out_ptr = new km_kbp_context_item[ctxt->size() + 1]; + *out_ptr = new km_core_context_item[ctxt->size() + 1]; } catch (std::bad_alloc &) { - return KM_KBP_STATUS_NO_MEM; + return KM_CORE_STATUS_NO_MEM; } std::copy(ctxt->begin(), ctxt->end(), *out_ptr); - (*out_ptr)[ctxt->size()].type = KM_KBP_CT_END; + (*out_ptr)[ctxt->size()].type = KM_CORE_CT_END; - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } -void km_kbp_context_clear(km_kbp_context *ctxt) +void km_core_context_clear(km_core_context *ctxt) { assert(ctxt); if (ctxt) @@ -185,38 +185,38 @@ void km_kbp_context_clear(km_kbp_context *ctxt) } -size_t km_kbp_context_length(km_kbp_context *ctxt) +size_t km_core_context_length(km_core_context *ctxt) { assert(ctxt); return ctxt ? ctxt->size() : 0; } -km_kbp_status km_kbp_context_append(km_kbp_context *ctxt, - km_kbp_context_item const *ci) +km_core_status km_core_context_append(km_core_context *ctxt, + km_core_context_item const *ci) { assert(ctxt); assert(ci); - if (!ctxt || !ci) return KM_KBP_STATUS_INVALID_ARGUMENT; + if (!ctxt || !ci) return KM_CORE_STATUS_INVALID_ARGUMENT; try { - for (;ci->type != KM_KBP_CT_END; ++ci) + for (;ci->type != KM_CORE_CT_END; ++ci) { ctxt->emplace_back(*ci); } } catch (std::bad_alloc &) { - return KM_KBP_STATUS_NO_MEM; + return KM_CORE_STATUS_NO_MEM; } - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } -km_kbp_status km_kbp_context_shrink(km_kbp_context *ctxt, size_t num, - km_kbp_context_item const * ci) +km_core_status km_core_context_shrink(km_core_context *ctxt, size_t num, + km_core_context_item const * ci) { assert(ctxt); - if (!ctxt) return KM_KBP_STATUS_INVALID_ARGUMENT; + if (!ctxt) return KM_CORE_STATUS_INVALID_ARGUMENT; try { @@ -225,21 +225,21 @@ km_kbp_status km_kbp_context_shrink(km_kbp_context *ctxt, size_t num, if (ci) { auto const ip = ctxt->begin(); - while(num-- && ci->type != KM_KBP_CT_END) + while(num-- && ci->type != KM_CORE_CT_END) { ctxt->emplace(ip, *ci); ci++; } } } catch (std::bad_alloc &) { - return KM_KBP_STATUS_NO_MEM; + return KM_CORE_STATUS_NO_MEM; } - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } size_t -km_kbp_context_item_list_size(km_kbp_context_item const *context_items) +km_core_context_item_list_size(km_core_context_item const *context_items) { assert(context_items); if (!context_items) return 0; @@ -258,18 +258,18 @@ json & operator << (json & j, km::kbp::context const & ctxt) { return j << json::close; } -json & operator << (json & j, km_kbp_context_item const & i) +json & operator << (json & j, km_core_context_item const & i) { utf8::codeunit_t cps[7] = {0,}; // 6 bytes for maximal UTF-8 char (e.g. U+10FFFF) + nul terminator int8_t l = 4; switch (i.type) { - case KM_KBP_CT_CHAR: + case KM_CORE_CT_CHAR: utf8::codec::put(cps, i.character, l); j << json::string(&cps[0]); break; - case KM_KBP_CT_MARKER: + case KM_CORE_CT_MARKER: j << json::integer_u(i.marker); break; default: diff --git a/core/src/km_kbp_debug_api.cpp b/core/src/km_core_debug_api.cpp similarity index 69% rename from core/src/km_kbp_debug_api.cpp rename to core/src/km_core_debug_api.cpp index 0871df17ae5..df324c222e6 100644 --- a/core/src/km_kbp_debug_api.cpp +++ b/core/src/km_core_debug_api.cpp @@ -8,29 +8,29 @@ #include #include -#include +#include #include "processor.hpp" #include "state.hpp" using namespace km::kbp; -km_kbp_status -km_kbp_state_debug_set( - km_kbp_state *state, +km_core_status +km_core_state_debug_set( + km_core_state *state, int value ) { assert(state); if(!state) { - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; } state->debug_items().set_enabled(value ? true : false); - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } uint8_t -km_kbp_state_debug_get( - km_kbp_state const *state +km_core_state_debug_get( + km_core_state const *state ) { assert(state); if(!state) { @@ -39,9 +39,9 @@ km_kbp_state_debug_get( return state->debug_items().is_enabled() ? 1 : 0; } -km_kbp_state_debug_item const * -km_kbp_state_debug_items( - km_kbp_state const *state, +km_core_state_debug_item const * +km_core_state_debug_items( + km_core_state const *state, size_t *num_items ) { assert(state && state->debug_items().size() > 0); @@ -58,6 +58,6 @@ km_kbp_state_debug_items( // Process events will ensure that the debug vector is always well // terminated - assert(state->debug_items().back().type == KM_KBP_DEBUG_END); + assert(state->debug_items().back().type == KM_CORE_DEBUG_END); return state->debug_items().data(); } diff --git a/core/src/km_kbp_keyboard_api.cpp b/core/src/km_core_keyboard_api.cpp similarity index 63% rename from core/src/km_kbp_keyboard_api.cpp rename to core/src/km_core_keyboard_api.cpp index 13f3cafc45c..632a2beb28d 100644 --- a/core/src/km_kbp_keyboard_api.cpp +++ b/core/src/km_core_keyboard_api.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include "keyboard.hpp" #include "processor.hpp" #include "kmx/kmx_processor.hpp" @@ -43,85 +43,85 @@ namespace } -km_kbp_status -km_kbp_keyboard_load(km_kbp_path_name kb_path, km_kbp_keyboard **keyboard) +km_core_status +km_core_keyboard_load(km_core_path_name kb_path, km_core_keyboard **keyboard) { assert(keyboard); if (!keyboard) - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; try { abstract_processor *kp = processor_factory(kb_path); - km_kbp_status status = kp->validate(); - if (status != KM_KBP_STATUS_OK) { + km_core_status status = kp->validate(); + if (status != KM_CORE_STATUS_OK) { delete kp; return status; } - *keyboard = static_cast(kp); + *keyboard = static_cast(kp); } catch (std::bad_alloc &) { - return KM_KBP_STATUS_NO_MEM; + return KM_CORE_STATUS_NO_MEM; } - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } void -km_kbp_keyboard_dispose(km_kbp_keyboard *keyboard) +km_core_keyboard_dispose(km_core_keyboard *keyboard) { delete keyboard; } -km_kbp_status -km_kbp_keyboard_get_attrs(km_kbp_keyboard const *keyboard, - km_kbp_keyboard_attrs const **out) +km_core_status +km_core_keyboard_get_attrs(km_core_keyboard const *keyboard, + km_core_keyboard_attrs const **out) { assert(keyboard); assert(out); if (!keyboard || !out) - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; *out = &keyboard->keyboard(); - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } -km_kbp_status -km_kbp_keyboard_get_key_list(km_kbp_keyboard const *keyboard, - km_kbp_keyboard_key **out) +km_core_status +km_core_keyboard_get_key_list(km_core_keyboard const *keyboard, + km_core_keyboard_key **out) { assert(keyboard); assert(out); if (!keyboard || !out) - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; *out = keyboard->get_key_list(); - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } -void km_kbp_keyboard_key_list_dispose(km_kbp_keyboard_key *key_list) +void km_core_keyboard_key_list_dispose(km_core_keyboard_key *key_list) { delete[] key_list; } -km_kbp_status km_kbp_keyboard_get_imx_list( - km_kbp_keyboard const *keyboard, - km_kbp_keyboard_imx** imx_list +km_core_status km_core_keyboard_get_imx_list( + km_core_keyboard const *keyboard, + km_core_keyboard_imx** imx_list ) { assert(keyboard); assert(imx_list); if (!keyboard || !imx_list) { - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; } *imx_list = keyboard->get_imx_list(); - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } -void km_kbp_keyboard_imx_list_dispose(km_kbp_keyboard_imx *imx_list) +void km_core_keyboard_imx_list_dispose(km_core_keyboard_imx *imx_list) { if(!imx_list) { return; } - km_kbp_keyboard_imx *imx_rule_it = imx_list; + km_core_keyboard_imx *imx_rule_it = imx_list; for (; imx_rule_it->library_name; ++imx_rule_it) { delete [] imx_rule_it->library_name; // from u16dup delete [] imx_rule_it->function_name; // from u16dup diff --git a/core/src/km_kbp_options_api.cpp b/core/src/km_core_options_api.cpp similarity index 56% rename from core/src/km_kbp_options_api.cpp rename to core/src/km_core_options_api.cpp index 7dc48790987..3cb0745fa90 100644 --- a/core/src/km_kbp_options_api.cpp +++ b/core/src/km_core_options_api.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include "processor.hpp" #include "jsonpp.hpp" @@ -19,7 +19,7 @@ size_t -km_kbp_options_list_size(km_kbp_option_item const *opts) +km_core_options_list_size(km_core_option_item const *opts) { assert(opts); if (!opts) return 0; @@ -33,31 +33,31 @@ km_kbp_options_list_size(km_kbp_option_item const *opts) } -km_kbp_status -km_kbp_state_option_lookup(km_kbp_state const *state, - uint8_t scope, km_kbp_cp const *key, - km_kbp_cp const **value_out) +km_core_status +km_core_state_option_lookup(km_core_state const *state, + uint8_t scope, km_core_cp const *key, + km_core_cp const **value_out) { assert(state); assert(key); assert(value_out); - if (!state || !key || !value_out) return KM_KBP_STATUS_INVALID_ARGUMENT; + if (!state || !key || !value_out) return KM_CORE_STATUS_INVALID_ARGUMENT; - if (scope == KM_KBP_OPT_UNKNOWN || scope > KM_KBP_OPT_MAX_SCOPES) - return KM_KBP_STATUS_INVALID_ARGUMENT; + if (scope == KM_CORE_OPT_UNKNOWN || scope > KM_CORE_OPT_MAX_SCOPES) + return KM_CORE_STATUS_INVALID_ARGUMENT; auto & processor = state->processor(); - *value_out = processor.lookup_option(km_kbp_option_scope(scope), key); - if (!*value_out) return KM_KBP_STATUS_KEY_ERROR; + *value_out = processor.lookup_option(km_core_option_scope(scope), key); + if (!*value_out) return KM_CORE_STATUS_KEY_ERROR; - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } -km_kbp_status -km_kbp_state_options_update(km_kbp_state *state, km_kbp_option_item const *opt) +km_core_status +km_core_state_options_update(km_core_state *state, km_core_option_item const *opt) { assert(state); assert(opt); - if (!state|| !opt) return KM_KBP_STATUS_INVALID_ARGUMENT; + if (!state|| !opt) return KM_CORE_STATUS_INVALID_ARGUMENT; auto & processor = state->processor(); @@ -65,32 +65,32 @@ km_kbp_state_options_update(km_kbp_state *state, km_kbp_option_item const *opt) { for (;opt->key; ++opt) { - if (opt->scope == KM_KBP_OPT_UNKNOWN || opt->scope > KM_KBP_OPT_MAX_SCOPES) - return KM_KBP_STATUS_INVALID_ARGUMENT; + if (opt->scope == KM_CORE_OPT_UNKNOWN || opt->scope > KM_CORE_OPT_MAX_SCOPES) + return KM_CORE_STATUS_INVALID_ARGUMENT; if (processor.update_option( - km_kbp_option_scope(opt->scope), + km_core_option_scope(opt->scope), opt->key, opt->value).empty()) - return KM_KBP_STATUS_KEY_ERROR; + return KM_CORE_STATUS_KEY_ERROR; } } catch (std::bad_alloc &) { - return KM_KBP_STATUS_NO_MEM; + return KM_CORE_STATUS_NO_MEM; } - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } // This function doesn't need to use the json pretty printer for such a simple // list of key:value pairs but it's a good introduction to it. -km_kbp_status -km_kbp_state_options_to_json(km_kbp_state const *state, char *buf, size_t *space) +km_core_status +km_core_state_options_to_json(km_core_state const *state, char *buf, size_t *space) { assert(state); assert(space); if (!state || !space) - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; std::stringstream _buf; json jo(_buf); @@ -103,7 +103,7 @@ km_kbp_state_options_to_json(km_kbp_state const *state, char *buf, size_t *space catch (std::bad_alloc &) { *space = 0; - return KM_KBP_STATUS_NO_MEM; + return KM_CORE_STATUS_NO_MEM; } // Fetch the finished doc and copy it to the buffer if there enough space. @@ -116,5 +116,5 @@ km_kbp_state_options_to_json(km_kbp_state const *state, char *buf, size_t *space // Return space needed/used. *space = doc.size()+1; - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } diff --git a/core/src/km_kbp_processevent_api.cpp b/core/src/km_core_processevent_api.cpp similarity index 62% rename from core/src/km_kbp_processevent_api.cpp rename to core/src/km_core_processevent_api.cpp index 69eba06a41c..5295d39af7b 100644 --- a/core/src/km_kbp_processevent_api.cpp +++ b/core/src/km_core_processevent_api.cpp @@ -8,63 +8,63 @@ History: 17 Oct 2018 - TSE - Initial implementation. */ -#include +#include #include "processor.hpp" #include "state.hpp" -km_kbp_status -km_kbp_event( - km_kbp_state *state, +km_core_status +km_core_event( + km_core_state *state, uint32_t event, void* data ) { assert(state != nullptr); if(state == nullptr) { - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; } - // event: KM_KBP_EVENT_KEYBOARD_ACTIVATED; data should be nullptr - // future event: KM_KBP_EVENT_KEYBOARD_DEACTIVATED + // event: KM_CORE_EVENT_KEYBOARD_ACTIVATED; data should be nullptr + // future event: KM_CORE_EVENT_KEYBOARD_DEACTIVATED switch(event) { - case KM_KBP_EVENT_KEYBOARD_ACTIVATED: + case KM_CORE_EVENT_KEYBOARD_ACTIVATED: assert(data == nullptr); if(data != nullptr) { - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; } break; default: - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; } return state->processor().external_event(state, event, data); } -km_kbp_status -km_kbp_process_event(km_kbp_state *state, - km_kbp_virtual_key vk, +km_core_status +km_core_process_event(km_core_state *state, + km_core_virtual_key vk, uint16_t modifier_state, uint8_t is_key_down, uint16_t event_flags) { assert(state != nullptr); if(state == nullptr) { - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; } return state->processor().process_event(state, vk, modifier_state, is_key_down, event_flags); } -km_kbp_status -km_kbp_process_queued_actions( - km_kbp_state *state +km_core_status +km_core_process_queued_actions( + km_core_state *state ) { assert(state != nullptr); if(state == nullptr) { - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; } return state->processor().process_queued_actions(state); } -km_kbp_attr const * -km_kbp_get_engine_attrs(km_kbp_state const *state) +km_core_attr const * +km_core_get_engine_attrs(km_core_state const *state) { assert(state != nullptr); if(state == nullptr) { diff --git a/core/src/km_kbp_state_api.cpp b/core/src/km_core_state_api.cpp similarity index 59% rename from core/src/km_kbp_state_api.cpp rename to core/src/km_core_state_api.cpp index b966688950f..fdafd9a5b72 100644 --- a/core/src/km_kbp_state_api.cpp +++ b/core/src/km_core_state_api.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include "jsonpp.hpp" #include "processor.hpp" @@ -23,68 +23,68 @@ using namespace km::kbp; // Forward declarations class context; -km_kbp_status km_kbp_state_create(km_kbp_keyboard * keyboard, - km_kbp_option_item const *env, - km_kbp_state ** out) +km_core_status km_core_state_create(km_core_keyboard * keyboard, + km_core_option_item const *env, + km_core_state ** out) { assert(keyboard); assert(env); assert(out); if (!keyboard || !env || !out) - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; try { - *out = new km_kbp_state(static_cast(*keyboard), env); + *out = new km_core_state(static_cast(*keyboard), env); } catch (std::bad_alloc &) { - return KM_KBP_STATUS_NO_MEM; + return KM_CORE_STATUS_NO_MEM; } - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } -km_kbp_status km_kbp_state_clone(km_kbp_state const *state, - km_kbp_state ** out) +km_core_status km_core_state_clone(km_core_state const *state, + km_core_state ** out) { assert(state); assert(out); if (!state || !out) - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; - *out = new km_kbp_state(*state); - return KM_KBP_STATUS_OK; + *out = new km_core_state(*state); + return KM_CORE_STATUS_OK; } -void km_kbp_state_dispose(km_kbp_state *state) +void km_core_state_dispose(km_core_state *state) { delete state; } -km_kbp_context *km_kbp_state_context(km_kbp_state *state) +km_core_context *km_core_state_context(km_core_state *state) { assert(state); if (!state) return nullptr; - return static_cast(&state->context()); + return static_cast(&state->context()); } -km_kbp_status kbp_state_get_intermediate_context( - km_kbp_state *state, - km_kbp_context_item ** context_items +km_core_status kbp_state_get_intermediate_context( + km_core_state *state, + km_core_context_item ** context_items ) { assert(state); assert(context_items); if (!state || !context_items) { - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; } auto & processor = state->processor(); *context_items = processor.get_intermediate_context(); - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } -km_kbp_action_item const * km_kbp_state_action_items(km_kbp_state const *state, +km_core_action_item const * km_core_state_action_items(km_core_state const *state, size_t *num_items) { assert(state && state->actions().size() > 0); @@ -95,33 +95,33 @@ km_kbp_action_item const * km_kbp_state_action_items(km_kbp_state const *state, // Process events will ensure that the actions vector is always well // teminated - assert(state->actions().back().type == KM_KBP_IT_END); + assert(state->actions().back().type == KM_CORE_IT_END); return state->actions().data(); } -km_kbp_status km_kbp_state_queue_action_items( - km_kbp_state *state, - km_kbp_action_item const *action_items +km_core_status km_core_state_queue_action_items( + km_core_state *state, + km_core_action_item const *action_items ) { assert(state); assert(action_items); if (!state|| !action_items) { - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; } auto & processor = state->processor(); - for (; action_items->type != KM_KBP_IT_END; ++action_items) { - if (action_items->type >= KM_KBP_IT_MAX_TYPE_ID) { - return KM_KBP_STATUS_INVALID_ARGUMENT; + for (; action_items->type != KM_CORE_IT_END; ++action_items) { + if (action_items->type >= KM_CORE_IT_MAX_TYPE_ID) { + return KM_CORE_STATUS_INVALID_ARGUMENT; } if (!processor.queue_action(state, action_items)) { - return KM_KBP_STATUS_KEY_ERROR; + return KM_CORE_STATUS_KEY_ERROR; } } - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } namespace { char const * action_item_name_lut[] = { @@ -146,10 +146,10 @@ namespace { } -json & operator << (json & j, km_kbp_action_item const &act) +json & operator << (json & j, km_core_action_item const &act) { j << json::flat << json::object; - if (act.type >= KM_KBP_IT_MAX_TYPE_ID) + if (act.type >= KM_CORE_IT_MAX_TYPE_ID) { j << "invalid" << json::null << json::close; return j; @@ -158,16 +158,16 @@ json & operator << (json & j, km_kbp_action_item const &act) j << action_item_name_lut[act.type]; switch (act.type) { - case KM_KBP_IT_END: - case KM_KBP_IT_ALERT: - case KM_KBP_IT_BACK: + case KM_CORE_IT_END: + case KM_CORE_IT_ALERT: + case KM_CORE_IT_BACK: j << json::null; break; - case KM_KBP_IT_CHAR: - case KM_KBP_IT_MARKER: - j << km_kbp_context_item {act.type, {0,}, {act.character}}; // TODO: is act.type correct here? it may map okay but this is bad practice to mix constants across types. Similarly using act.character instead of act.type + case KM_CORE_IT_CHAR: + case KM_CORE_IT_MARKER: + j << km_core_context_item {act.type, {0,}, {act.character}}; // TODO: is act.type correct here? it may map okay but this is bad practice to mix constants across types. Similarly using act.character instead of act.type break; - case KM_KBP_IT_PERSIST_OPT: + case KM_CORE_IT_PERSIST_OPT: j << json::object << scope_names_lut[act.option->scope] << json::flat << json::object @@ -187,7 +187,7 @@ json & operator << (json & j, actions const & acts) j << json::array; for (auto & act: acts) { - if (act.type != KM_KBP_IT_END) + if (act.type != KM_CORE_IT_END) { j << act; } @@ -197,13 +197,13 @@ json & operator << (json & j, actions const & acts) } -km_kbp_status km_kbp_state_to_json(km_kbp_state const *state, +km_core_status km_core_state_to_json(km_core_state const *state, char *buf, size_t *space) { assert(state); if (!state) - return KM_KBP_STATUS_INVALID_ARGUMENT; + return KM_CORE_STATUS_INVALID_ARGUMENT; std::stringstream _buf; json jo(_buf); @@ -212,7 +212,7 @@ km_kbp_status km_kbp_state_to_json(km_kbp_state const *state, { // Pretty print the document. jo << json::object - << "$schema" << "keyman/keyboardprocessor/doc/introspection.schema" + << "$schema" << "keyman/core/doc/introspection.schema" << "keyboard" << state->processor().keyboard() // << "options" << state->options() TODO: Fix << "context" << state->context() @@ -222,7 +222,7 @@ km_kbp_status km_kbp_state_to_json(km_kbp_state const *state, catch (std::bad_alloc &) { *space = 0; - return KM_KBP_STATUS_NO_MEM; + return KM_CORE_STATUS_NO_MEM; } // Fetch the finished doc and copy it to the buffer if there enough space. @@ -235,13 +235,13 @@ km_kbp_status km_kbp_state_to_json(km_kbp_state const *state, // Return space needed/used. *space = doc.size()+1; - return KM_KBP_STATUS_OK; + return KM_CORE_STATUS_OK; } -void km_kbp_state_imx_register_callback( - km_kbp_state *state, - km_kbp_keyboard_imx_platform imx_callback, +void km_core_state_imx_register_callback( + km_core_state *state, + km_core_keyboard_imx_platform imx_callback, void *callback_object ) { assert(state); @@ -251,7 +251,7 @@ void km_kbp_state_imx_register_callback( state->imx_register_callback(imx_callback, callback_object); } -void km_kbp_state_imx_deregister_callback(km_kbp_state *state) +void km_core_state_imx_deregister_callback(km_core_state *state) { assert(state); if (!state) { diff --git a/core/src/kmx/kmx_base.h b/core/src/kmx/kmx_base.h index 7fc335cfc46..77cf53c209b 100644 --- a/core/src/kmx/kmx_base.h +++ b/core/src/kmx/kmx_base.h @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include #if defined(_WIN32) || defined(_WIN64) #define snprintf _snprintf diff --git a/core/src/kmx/kmx_capslock.cpp b/core/src/kmx/kmx_capslock.cpp index e125ccd4cf1..f14745bf326 100644 --- a/core/src/kmx/kmx_capslock.cpp +++ b/core/src/kmx/kmx_capslock.cpp @@ -2,7 +2,7 @@ Copyright: Copyright (C) 2003-2018 SIL International. Authors: mcdurdin */ -#include +#include #include using namespace km::kbp; diff --git a/core/src/kmx/kmx_consts.cpp b/core/src/kmx/kmx_consts.cpp index 26457469498..a0e1ff660ab 100644 --- a/core/src/kmx/kmx_consts.cpp +++ b/core/src/kmx/kmx_consts.cpp @@ -6,22 +6,22 @@ namespace kbp { namespace kmx { const struct char_to_vkey s_char_to_vkey[] = { - {KM_KBP_VKEY_SPACE, 0, 0}, // + {KM_CORE_VKEY_SPACE, 0, 0}, // {'1', 1, 0}, // ! - {KM_KBP_VKEY_QUOTE, 1, 0}, // " + {KM_CORE_VKEY_QUOTE, 1, 0}, // " {'3', 1, 0}, // # {'4', 1, 0}, // $ {'5', 1, 0}, // % {'7', 1, 0}, // & - {KM_KBP_VKEY_QUOTE, 0, 0}, // ' + {KM_CORE_VKEY_QUOTE, 0, 0}, // ' {'9', 1, 0}, // ( {'0', 1, 0}, // ) {'8', 1, 0}, // * - {KM_KBP_VKEY_EQUAL, 1, 0}, // + - {KM_KBP_VKEY_COMMA, 0, 0}, // , - {KM_KBP_VKEY_HYPHEN, 0, 0}, // - - {KM_KBP_VKEY_PERIOD, 0, 0}, // . - {KM_KBP_VKEY_SLASH, 0, 0}, // / + {KM_CORE_VKEY_EQUAL, 1, 0}, // + + {KM_CORE_VKEY_COMMA, 0, 0}, // , + {KM_CORE_VKEY_HYPHEN, 0, 0}, // - + {KM_CORE_VKEY_PERIOD, 0, 0}, // . + {KM_CORE_VKEY_SLASH, 0, 0}, // / {'0', 0, 0}, {'1', 0, 0}, {'2', 0, 0}, @@ -32,12 +32,12 @@ const struct char_to_vkey s_char_to_vkey[] = { {'7', 0, 0}, {'8', 0, 0}, {'9', 0, 0}, - {KM_KBP_VKEY_COLON, 1, 0}, // : - {KM_KBP_VKEY_COLON, 0, 0}, // ; - {KM_KBP_VKEY_COMMA, 1, 0}, // < - {KM_KBP_VKEY_EQUAL, 0, 0}, // = - {KM_KBP_VKEY_PERIOD, 1, 0}, // > - {KM_KBP_VKEY_SLASH, 1, 0}, // ? + {KM_CORE_VKEY_COLON, 1, 0}, // : + {KM_CORE_VKEY_COLON, 0, 0}, // ; + {KM_CORE_VKEY_COMMA, 1, 0}, // < + {KM_CORE_VKEY_EQUAL, 0, 0}, // = + {KM_CORE_VKEY_PERIOD, 1, 0}, // > + {KM_CORE_VKEY_SLASH, 1, 0}, // ? {'2', 1, 0}, // @ {'A', 1, 1}, {'B', 1, 1}, @@ -65,12 +65,12 @@ const struct char_to_vkey s_char_to_vkey[] = { {'X', 1, 1}, {'Y', 1, 1}, {'Z', 1, 1}, - {KM_KBP_VKEY_LBRKT, 0, 0}, - {KM_KBP_VKEY_BKSLASH, 0, 0}, - {KM_KBP_VKEY_RBRKT, 0, 0}, + {KM_CORE_VKEY_LBRKT, 0, 0}, + {KM_CORE_VKEY_BKSLASH, 0, 0}, + {KM_CORE_VKEY_RBRKT, 0, 0}, {'6', 1, 0}, - {KM_KBP_VKEY_HYPHEN, 1, 0}, - {KM_KBP_VKEY_BKQUOTE, 0, 0}, + {KM_CORE_VKEY_HYPHEN, 1, 0}, + {KM_CORE_VKEY_BKQUOTE, 0, 0}, {'A', 0, 1}, {'B', 0, 1}, {'C', 0, 1}, @@ -97,10 +97,10 @@ const struct char_to_vkey s_char_to_vkey[] = { {'X', 0, 1}, {'Y', 0, 1}, {'Z', 0, 1}, - {KM_KBP_VKEY_LBRKT, 1, 0}, - {KM_KBP_VKEY_BKSLASH, 1, 0}, - {KM_KBP_VKEY_RBRKT, 1, 0}, - {KM_KBP_VKEY_BKQUOTE, 1, 0}, + {KM_CORE_VKEY_LBRKT, 1, 0}, + {KM_CORE_VKEY_BKSLASH, 1, 0}, + {KM_CORE_VKEY_RBRKT, 1, 0}, + {KM_CORE_VKEY_BKQUOTE, 1, 0}, {0, 0, 0} }; diff --git a/core/src/kmx/kmx_debugger.cpp b/core/src/kmx/kmx_debugger.cpp index c217086acdb..84280280deb 100644 --- a/core/src/kmx/kmx_debugger.cpp +++ b/core/src/kmx/kmx_debugger.cpp @@ -21,7 +21,7 @@ void KMX_DebugItems::push_item( PKMX_WORD index_stack ) { _items->assert_push_entry(); - km_kbp_state_debug_item item = {type, flags, {}, {}}; + km_core_state_debug_item item = {type, flags, {}, {}}; item.kmx_info.rule = key; if(item.kmx_info.rule && index_stack) { this->fill_store_offsets(&item.kmx_info, index_stack); @@ -43,7 +43,7 @@ void KMX_DebugItems::push_set_option( KMX_WCHAR const * value ) { _items->assert_push_entry(); - km_kbp_state_debug_item item = {KM_KBP_DEBUG_SET_OPTION, 0, {}, {}}; + km_core_state_debug_item item = {KM_CORE_DEBUG_SET_OPTION, 0, {}, {}}; item.kmx_info.first_action = first_action; item.kmx_info.rule = nullptr; item.kmx_info.group = nullptr; @@ -57,11 +57,11 @@ void KMX_DebugItems::push_set_option( _items->emplace_back(item); } -void KMX_DebugItems::fill_store_offsets(km_kbp_state_debug_kmx_info *info, PKMX_WORD index_stack) { +void KMX_DebugItems::fill_store_offsets(km_core_state_debug_kmx_info *info, PKMX_WORD index_stack) { int i, n; - km_kbp_cp *p; + km_core_cp *p; // TODO turn this into a struct rather than interwoven values for(i = n = 0, p = static_cast(info->rule)->dpContext; p && *p; p = incxstr(p), i++) { diff --git a/core/src/kmx/kmx_debugger.h b/core/src/kmx/kmx_debugger.h index 20006e291c7..45b9b8d3110 100644 --- a/core/src/kmx/kmx_debugger.h +++ b/core/src/kmx/kmx_debugger.h @@ -31,12 +31,12 @@ class KMX_DebugItems PKMX_WORD index_stack = nullptr ); void fill_store_offsets( - km_kbp_state_debug_kmx_info *info, + km_core_state_debug_kmx_info *info, PKMX_WORD index_stack ); public: KMX_DebugItems(debug_items *items); - void push_begin(km_kbp_state_debug_key_info *key_info, uint32_t flags); + void push_begin(km_core_state_debug_key_info *key_info, uint32_t flags); void push_end(uint16_t first_action, uint32_t flags); void push_group_enter(uint16_t first_action, LPGROUP group); void push_group_exit(uint16_t first_action, uint32_t flags, LPGROUP group); @@ -73,10 +73,10 @@ KMX_DebugItems::KMX_DebugItems(debug_items *items) { inline void KMX_DebugItems::push_begin( - km_kbp_state_debug_key_info *key_info, + km_core_state_debug_key_info *key_info, uint32_t flags ) { - // As the vector always starts with a single KM_KBP_DEBUG_END entry, + // As the vector always starts with a single KM_CORE_DEBUG_END entry, // we clear that and any other potential events first before starting a new event sequence _items->clear(); _items->push_begin(key_info, flags); @@ -92,32 +92,32 @@ KMX_DebugItems::push_end( inline void KMX_DebugItems::push_group_enter(uint16_t first_action, LPGROUP group) { - push_item(KM_KBP_DEBUG_GROUP_ENTER, first_action, 0, group); + push_item(KM_CORE_DEBUG_GROUP_ENTER, first_action, 0, group); } inline void KMX_DebugItems::push_group_exit(uint16_t first_action, uint32_t flags, LPGROUP group) { - push_item(KM_KBP_DEBUG_GROUP_EXIT, first_action, flags, group); + push_item(KM_CORE_DEBUG_GROUP_EXIT, first_action, flags, group); } inline void KMX_DebugItems::push_nomatch_enter(uint16_t first_action, LPGROUP group) { - push_item(KM_KBP_DEBUG_NOMATCH_ENTER, first_action, 0, group); + push_item(KM_CORE_DEBUG_NOMATCH_ENTER, first_action, 0, group); } inline void KMX_DebugItems::push_nomatch_exit(uint16_t first_action, LPGROUP group) { - push_item(KM_KBP_DEBUG_NOMATCH_EXIT, first_action, 0, group); + push_item(KM_CORE_DEBUG_NOMATCH_EXIT, first_action, 0, group); } inline void KMX_DebugItems::push_match_enter(uint16_t first_action, LPGROUP group) { - push_item(KM_KBP_DEBUG_MATCH_ENTER, first_action, 0, group); + push_item(KM_CORE_DEBUG_MATCH_ENTER, first_action, 0, group); } inline void KMX_DebugItems::push_match_exit(uint16_t first_action, LPGROUP group) { - push_item(KM_KBP_DEBUG_MATCH_EXIT, first_action, 0, group); + push_item(KM_CORE_DEBUG_MATCH_EXIT, first_action, 0, group); } inline void @@ -128,7 +128,7 @@ KMX_DebugItems::push_rule_enter( PKMX_WCHAR context, PKMX_WORD index_stack ) { - push_item(KM_KBP_DEBUG_RULE_ENTER, first_action, 0, group, key, context, index_stack); + push_item(KM_CORE_DEBUG_RULE_ENTER, first_action, 0, group, key, context, index_stack); } inline void @@ -139,7 +139,7 @@ KMX_DebugItems::push_rule_exit( PKMX_WCHAR context, PKMX_WORD index_stack ) { - push_item(KM_KBP_DEBUG_RULE_EXIT, first_action, 0, group, key, context, index_stack); + push_item(KM_CORE_DEBUG_RULE_EXIT, first_action, 0, group, key, context, index_stack); } } // namespace kmx diff --git a/core/src/kmx/kmx_environment.cpp b/core/src/kmx/kmx_environment.cpp index 4d3d3c54903..5d6e367e141 100644 --- a/core/src/kmx/kmx_environment.cpp +++ b/core/src/kmx/kmx_environment.cpp @@ -3,7 +3,7 @@ Authors: mcdurdin */ #include -#include +#include #include #include @@ -11,7 +11,7 @@ using namespace km::kbp; using namespace kmx; namespace { - km_kbp_cp const + km_core_cp const *DEFAULT_PLATFORM = u"windows hardware desktop native", *DEFAULT_BASELAYOUT = u"kbdus.dll", *DEFAULT_BASELAYOUTALT = u"en-US", @@ -20,11 +20,11 @@ namespace { } KMX_Environment::KMX_Environment() { - Set(KM_KBP_KMX_ENV_PLATFORM, DEFAULT_PLATFORM); - Set(KM_KBP_KMX_ENV_BASELAYOUT, DEFAULT_BASELAYOUT); - Set(KM_KBP_KMX_ENV_BASELAYOUTALT, DEFAULT_BASELAYOUTALT); - Set(KM_KBP_KMX_ENV_SIMULATEALTGR, DEFAULT_SIMULATEALTGR); - Set(KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT, DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT); + Set(KM_CORE_KMX_ENV_PLATFORM, DEFAULT_PLATFORM); + Set(KM_CORE_KMX_ENV_BASELAYOUT, DEFAULT_BASELAYOUT); + Set(KM_CORE_KMX_ENV_BASELAYOUTALT, DEFAULT_BASELAYOUTALT); + Set(KM_CORE_KMX_ENV_SIMULATEALTGR, DEFAULT_SIMULATEALTGR); + Set(KM_CORE_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT, DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT); } @@ -32,19 +32,19 @@ char16_t const * KMX_Environment::LookUp(std::u16string const & key) const { assert(!key.empty()); if (!key.empty()) return nullptr; - if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_PLATFORM)) { + if (!u16icmp(key.c_str(), KM_CORE_KMX_ENV_PLATFORM)) { return _platform.c_str(); } - else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUT)) { + else if (!u16icmp(key.c_str(), KM_CORE_KMX_ENV_BASELAYOUT)) { return _baseLayout.c_str(); } - else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUTALT)) { + else if (!u16icmp(key.c_str(), KM_CORE_KMX_ENV_BASELAYOUTALT)) { return _baseLayoutAlt.c_str(); } - else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_SIMULATEALTGR)) { + else if (!u16icmp(key.c_str(), KM_CORE_KMX_ENV_SIMULATEALTGR)) { return _simulateAltGr ? u"1" : u"0"; } - else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT)) { + else if (!u16icmp(key.c_str(), KM_CORE_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT)) { return _baseLayoutGivesCtrlRAltForRAlt ? u"1" : u"0"; } else { @@ -57,19 +57,19 @@ char16_t const * KMX_Environment::LookUp(std::u16string const & key) const { void KMX_Environment::Set(std::u16string const & key, std::u16string const & value) { assert(!key.empty()); - if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_PLATFORM)) { + if (!u16icmp(key.c_str(), KM_CORE_KMX_ENV_PLATFORM)) { _platform = value; } - else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUT)) { + else if (!u16icmp(key.c_str(), KM_CORE_KMX_ENV_BASELAYOUT)) { _baseLayout = value; } - else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUTALT)) { + else if (!u16icmp(key.c_str(), KM_CORE_KMX_ENV_BASELAYOUTALT)) { _baseLayoutAlt = value; } - else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_SIMULATEALTGR)) { + else if (!u16icmp(key.c_str(), KM_CORE_KMX_ENV_SIMULATEALTGR)) { _simulateAltGr = value == u"1"; } - else if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT)) { + else if (!u16icmp(key.c_str(), KM_CORE_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT)) { _baseLayoutGivesCtrlRAltForRAlt = value == u"1"; } else { diff --git a/core/src/kmx/kmx_environment.h b/core/src/kmx/kmx_environment.h index 4d934e0d104..d15b3c587bc 100644 --- a/core/src/kmx/kmx_environment.h +++ b/core/src/kmx/kmx_environment.h @@ -15,8 +15,8 @@ class KMX_Environment { std::u16string _platform; void InitOption( std::vector + + + diff --git a/core/tests/unit/ldml/keyboards/meson.build b/core/tests/unit/ldml/keyboards/meson.build index 4bce343bd30..eea0b519ac6 100644 --- a/core/tests/unit/ldml/keyboards/meson.build +++ b/core/tests/unit/ldml/keyboards/meson.build @@ -51,7 +51,7 @@ kmc_root = join_paths(meson.source_root(),'..','developer','src','kmc') ldml_root = join_paths(meson.source_root(),'..','resources','standards-data','ldml-keyboards','techpreview') ldml_data = join_paths(ldml_root, '3.0') ldml_testdata = join_paths(ldml_root, 'test') -kmc_cmd = [node, kmc_root] +kmc_cmd = [node, '--enable-source-maps', kmc_root] # Build all keyboards in output folder @@ -71,7 +71,7 @@ endforeach foreach kbd : tests_with_testdata configure_file( - command: kmc_cmd + ['build-test-data', '@INPUT@', '--out-file', '@OUTPUT@'], + command: kmc_cmd + ['build', 'ldml-test-data', '@INPUT@', '--out-file', '@OUTPUT@'], input: kbd + '-test.xml', output: kbd + '-test.json', ) @@ -90,7 +90,7 @@ foreach kbd : tests_from_cldr output: kbd + '.kmx', ) configure_file( - command: kmc_cmd + ['build-test-data', '@INPUT@', '--out-file', '@OUTPUT@'], + command: kmc_cmd + ['build', 'ldml-test-data', '@INPUT@', '--out-file', '@OUTPUT@'], input: join_paths(ldml_testdata, kbd + '-test.xml'), output: kbd + '-test.json', ) diff --git a/core/tests/unit/ldml/ldml.cpp b/core/tests/unit/ldml/ldml.cpp index bf78d009365..31a4cded731 100644 --- a/core/tests/unit/ldml/ldml.cpp +++ b/core/tests/unit/ldml/ldml.cpp @@ -32,9 +32,9 @@ namespace { bool g_beep_found = false; -km_kbp_option_item test_env_opts[] = +km_core_option_item test_env_opts[] = { - KM_KBP_OPTIONS_END + KM_CORE_OPTIONS_END }; std::string @@ -56,22 +56,22 @@ string_to_hex(const std::u16string &input) { void apply_action( - km_kbp_state const *, - km_kbp_action_item const &act, + km_core_state const *, + km_core_action_item const &act, std::u16string &text_store, - std::vector &context, + std::vector &context, km::tests::LdmlTestSource &test_source) { switch (act.type) { - case KM_KBP_IT_END: + case KM_CORE_IT_END: assert(false); break; - case KM_KBP_IT_ALERT: + case KM_CORE_IT_ALERT: g_beep_found = true; // std::cout << "beep" << std::endl; break; - case KM_KBP_IT_CHAR: - context.push_back(km_kbp_context_item{ - KM_KBP_CT_CHAR, + case KM_CORE_IT_CHAR: + context.push_back(km_core_context_item{ + KM_CORE_CT_CHAR, { 0, }, @@ -85,29 +85,29 @@ apply_action( } // std::cout << "char(" << act.character << ") size=" << cp->size() << std::endl; break; - case KM_KBP_IT_MARKER: + case KM_CORE_IT_MARKER: // std::cout << "deadkey(" << act.marker << ")" << std::endl; - context.push_back(km_kbp_context_item{ - KM_KBP_CT_MARKER, + context.push_back(km_core_context_item{ + KM_CORE_CT_MARKER, { 0, }, {(uint32_t)act.marker}}); break; - case KM_KBP_IT_BACK: + case KM_CORE_IT_BACK: // It is valid for a backspace to be received with an empty text store // as the user can press backspace with no text in the store and Keyman // will pass that back to the client, as the client may do additional // processing at start of a text store, e.g. delete from a previous cell // in a table. Or, if Keyman has a cached context, then there may be // additional text in the text store that Keyman can't see. - if (act.backspace.expected_type == KM_KBP_BT_MARKER) { + if (act.backspace.expected_type == KM_CORE_BT_MARKER) { assert(!context.empty()); - assert(context.back().type == KM_KBP_CT_MARKER); + assert(context.back().type == KM_CORE_CT_MARKER); context.pop_back(); } else if (text_store.length() > 0) { assert(!context.empty() && !text_store.empty()); - km_kbp_usv ch = text_store.back(); + km_core_usv ch = text_store.back(); text_store.pop_back(); if (text_store.length() > 0 && Uni_IsSurrogate2(ch)) { auto ch1 = text_store.back(); @@ -120,20 +120,20 @@ apply_action( } assert(ch == act.backspace.expected_value); - assert(context.back().type == KM_KBP_CT_CHAR); + assert(context.back().type == KM_CORE_CT_CHAR); assert(context.back().character == ch); context.pop_back(); } break; - case KM_KBP_IT_PERSIST_OPT: + case KM_CORE_IT_PERSIST_OPT: break; - case KM_KBP_IT_INVALIDATE_CONTEXT: + case KM_CORE_IT_INVALIDATE_CONTEXT: std::cout << "action: context invalidated (markers cleared)" << std::endl; break; - case KM_KBP_IT_EMIT_KEYSTROKE: + case KM_CORE_IT_EMIT_KEYSTROKE: std::cout << "action: emit keystroke" << std::endl; break; - case KM_KBP_IT_CAPSLOCK: + case KM_CORE_IT_CAPSLOCK: std::cout << "action: capsLock " << act.capsLock << std::endl; test_source.set_caps_lock_on(act.capsLock); break; @@ -147,25 +147,25 @@ apply_action( * verify the current context */ void -verify_context(std::u16string& text_store, km_kbp_state* &test_state, std::vector &test_context) { +verify_context(std::u16string& text_store, km_core_state* &test_state, std::vector &test_context) { // Compare context and text store at each step - should be identical size_t n = 0; - km_kbp_context_item* citems = nullptr; - try_status(km_kbp_context_get(km_kbp_state_context(test_state), &citems)); - try_status(km_kbp_context_items_to_utf16(citems, nullptr, &n)); - km_kbp_cp *buf = new km_kbp_cp[n]; - try_status(km_kbp_context_items_to_utf16(citems, buf, &n)); + km_core_context_item* citems = nullptr; + try_status(km_core_context_get(km_core_state_context(test_state), &citems)); + try_status(km_core_context_items_to_utf16(citems, nullptr, &n)); + km_core_cp *buf = new km_core_cp[n]; + try_status(km_core_context_items_to_utf16(citems, buf, &n)); std::cout << "context : " << string_to_hex(buf) << " [" << buf << "]" << std::endl; // Verify that both our local test_context and the core's test_state.context have // not diverged auto ci = citems; - for (auto test_ci = test_context.begin(); ci->type != KM_KBP_CT_END || test_ci != test_context.end(); ci++, test_ci++) { - assert(ci->type != KM_KBP_CT_END && test_ci != test_context.end()); // Verify that both lists are same length + for (auto test_ci = test_context.begin(); ci->type != KM_CORE_CT_END || test_ci != test_context.end(); ci++, test_ci++) { + assert(ci->type != KM_CORE_CT_END && test_ci != test_context.end()); // Verify that both lists are same length assert(test_ci->type == ci->type && test_ci->marker == ci->marker); } - km_kbp_context_items_dispose(citems); + km_core_context_items_dispose(citems); if (text_store != buf) { std::cerr << "text store has diverged from buf" << std::endl; std::cerr << "text store: " << string_to_hex(text_store) << " [" << text_store << "]" << std::endl; @@ -177,31 +177,31 @@ verify_context(std::u16string& text_store, km_kbp_state* &test_state, std::vecto int run_test(const km::kbp::path &source, const km::kbp::path &compiled, km::tests::LdmlTestSource& test_source) { - km_kbp_keyboard * test_kb = nullptr; - km_kbp_state * test_state = nullptr; + km_core_keyboard * test_kb = nullptr; + km_core_state * test_state = nullptr; - const km_kbp_status expect_load_status = test_source.get_expected_load_status(); - assert_equal(km_kbp_keyboard_load(compiled.c_str(), &test_kb), expect_load_status); + const km_core_status expect_load_status = test_source.get_expected_load_status(); + assert_equal(km_core_keyboard_load(compiled.c_str(), &test_kb), expect_load_status); - if (expect_load_status != KM_KBP_STATUS_OK) { + if (expect_load_status != KM_CORE_STATUS_OK) { std::cout << "Keyboard was expected to be invalid, so exiting " << std::endl; return 0; } // Setup state, environment - try_status(km_kbp_state_create(test_kb, test_env_opts, &test_state)); + try_status(km_core_state_create(test_kb, test_env_opts, &test_state)); // Setup context - km_kbp_context_item *citems = nullptr; - try_status(km_kbp_context_items_from_utf16(test_source.get_context().c_str(), &citems)); - try_status(km_kbp_context_set(km_kbp_state_context(test_state), citems)); + km_core_context_item *citems = nullptr; + try_status(km_core_context_items_from_utf16(test_source.get_context().c_str(), &citems)); + try_status(km_core_context_set(km_core_state_context(test_state), citems)); // Make a copy of the setup context for the test - std::vector test_context; - for(km_kbp_context_item *ci = citems; ci->type != KM_KBP_CT_END; ci++) { + std::vector test_context; + for(km_core_context_item *ci = citems; ci->type != KM_CORE_CT_END; ci++) { test_context.emplace_back(*ci); } - km_kbp_context_items_dispose(citems); + km_core_context_items_dispose(citems); // Setup baseline text store std::u16string text_store = test_source.get_context(); @@ -220,15 +220,15 @@ run_test(const km::kbp::path &source, const km::kbp::path &compiled, km::tests:: // we mimic that in the tests. We assume caps lock state is // updated on key_down before the processor receives the // event. - if (p.vk == KM_KBP_VKEY_CAPS) { + if (p.vk == KM_CORE_VKEY_CAPS) { test_source.toggle_caps_lock_state(); } for (auto key_down = 1; key_down >= 0; key_down--) { // expected error only applies to key down - try_status(km_kbp_process_event(test_state, p.vk, p.modifier_state | test_source.caps_lock_state(), key_down, KM_KBP_EVENT_FLAG_DEFAULT)); // TODO-LDML: for now. Should send touch and hardware events. + try_status(km_core_process_event(test_state, p.vk, p.modifier_state | test_source.caps_lock_state(), key_down, KM_CORE_EVENT_FLAG_DEFAULT)); // TODO-LDML: for now. Should send touch and hardware events. - for (auto act = km_kbp_state_action_items(test_state, nullptr); act->type != KM_KBP_IT_END; act++) { + for (auto act = km_core_state_action_items(test_state, nullptr); act->type != KM_CORE_IT_END; act++) { apply_action(test_state, *act, text_store, test_context, test_source); } } @@ -238,14 +238,14 @@ run_test(const km::kbp::path &source, const km::kbp::path &compiled, km::tests:: std::cerr << "TODO-LDML: note, LDML_ACTION_EMIT_STRING is NOT going through keyboard, transforms etc." << std::endl; text_store.append(action.string); // TODO-LDML: not going through keyboard // Now, update context? - km_kbp_context_item *nitems = nullptr; - try_status(km_kbp_context_items_from_utf16(action.string.c_str(), &nitems)); - try_status(km_kbp_context_append(km_kbp_state_context(test_state), nitems)); + km_core_context_item *nitems = nullptr; + try_status(km_core_context_items_from_utf16(action.string.c_str(), &nitems)); + try_status(km_core_context_append(km_core_state_context(test_state), nitems)); // update the test_context also. - for (km_kbp_context_item *ci = nitems; ci->type != KM_KBP_CT_END; ci++) { + for (km_core_context_item *ci = nitems; ci->type != KM_CORE_CT_END; ci++) { test_context.emplace_back(*ci); } - km_kbp_context_items_dispose(nitems); + km_core_context_items_dispose(nitems); verify_context(text_store, test_state, test_context); } else if (action.type == km::tests::LDML_ACTION_CHECK_EXPECTED) { @@ -275,18 +275,18 @@ run_test(const km::kbp::path &source, const km::kbp::path &compiled, km::tests:: // Verify that both our local test_context and the core's test_state.context have // not diverged - try_status(km_kbp_context_get(km_kbp_state_context(test_state), &citems)); + try_status(km_core_context_get(km_core_state_context(test_state), &citems)); auto ci = citems; - for(auto test_ci = test_context.begin(); ci->type != KM_KBP_CT_END || test_ci != test_context.end(); ci++, test_ci++) { - assert(ci->type != KM_KBP_CT_END && test_ci != test_context.end()); // Verify that both lists are same length + for(auto test_ci = test_context.begin(); ci->type != KM_CORE_CT_END || test_ci != test_context.end(); ci++, test_ci++) { + assert(ci->type != KM_CORE_CT_END && test_ci != test_context.end()); // Verify that both lists are same length assert(test_ci->type == ci->type && test_ci->marker == ci->marker); } - km_kbp_context_items_dispose(citems); + km_core_context_items_dispose(citems); // Destroy them - km_kbp_state_dispose(test_state); - km_kbp_keyboard_dispose(test_kb); + km_core_state_dispose(test_state); + km_core_keyboard_dispose(test_kb); return 0; } diff --git a/core/tests/unit/ldml/ldml_test_source.cpp b/core/tests/unit/ldml/ldml_test_source.cpp index 33a7e1a3d86..1a5905d1db3 100644 --- a/core/tests/unit/ldml/ldml_test_source.cpp +++ b/core/tests/unit/ldml/ldml_test_source.cpp @@ -66,8 +66,8 @@ LdmlTestSource::~LdmlTestSource() { } -km_kbp_status LdmlTestSource::get_expected_load_status() { - return KM_KBP_STATUS_OK; +km_core_status LdmlTestSource::get_expected_load_status() { + return KM_CORE_STATUS_OK; } bool LdmlTestSource::get_expected_beep() const { @@ -108,7 +108,7 @@ LdmlTestSource::parse_source_string(std::string const &s) { for (auto p = s.begin(); p != s.end(); p++) { if (*p == '\\') { p++; - km_kbp_usv v; + km_core_usv v; bool had_open_curly = false; assert(p != s.end()); if (*p == 'u' || *p == 'U') { @@ -126,10 +126,10 @@ LdmlTestSource::parse_source_string(std::string const &s) { assert(v >= 0x0001 && v <= 0x10FFFF); p += n - 1; if (v < 0x10000) { - t += km_kbp_cp(v); + t += km_core_cp(v); } else { - t += km_kbp_cp(Uni_UTF32ToSurrogate1(v)); - t += km_kbp_cp(Uni_UTF32ToSurrogate2(v)); + t += km_core_cp(Uni_UTF32ToSurrogate1(v)); + t += km_core_cp(Uni_UTF32ToSurrogate2(v)); } if (had_open_curly) { p++; @@ -157,7 +157,7 @@ LdmlTestSource::parse_u8_source_string(std::string const &u8s) { for (auto p = s.begin(); p != s.end(); p++) { if (*p == '\\') { p++; - km_kbp_usv v; + km_core_usv v; bool had_open_curly = false; assert(p != s.end()); if (*p == 'u' || *p == 'U') { @@ -177,10 +177,10 @@ LdmlTestSource::parse_u8_source_string(std::string const &u8s) { assert(v >= 0x0001 && v <= 0x10FFFF); p += n - 1; if (v < 0x10000) { - t += km_kbp_cp(v); + t += km_core_cp(v); } else { - t += km_kbp_cp(Uni_UTF32ToSurrogate1(v)); - t += km_kbp_cp(Uni_UTF32ToSurrogate2(v)); + t += km_core_cp(Uni_UTF32ToSurrogate1(v)); + t += km_core_cp(Uni_UTF32ToSurrogate2(v)); } if (had_open_curly) { p++; @@ -257,9 +257,9 @@ LdmlEmbeddedTestSource::load_source( const km::kbp::path &path ) { return 0; } -km_kbp_status +km_core_status LdmlEmbeddedTestSource::get_expected_load_status() { - return expected_error ? KM_KBP_STATUS_INVALID_KEYBOARD : KM_KBP_STATUS_OK; + return expected_error ? KM_CORE_STATUS_INVALID_KEYBOARD : KM_CORE_STATUS_OK; } const std::u16string& @@ -273,7 +273,7 @@ bool LdmlEmbeddedTestSource::get_expected_beep() const { int LdmlTestSource::caps_lock_state() { - return _caps_lock_on ? KM_KBP_MODIFIER_CAPS : 0; + return _caps_lock_on ? KM_CORE_MODIFIER_CAPS : 0; } void @@ -291,7 +291,7 @@ LdmlTestSource::char_to_event(char ch) { assert(ch >= 32); return { km::kbp::kmx::s_char_to_vkey[(int)ch - 32].vk, - (uint16_t)(km::kbp::kmx::s_char_to_vkey[(int)ch - 32].shifted ? KM_KBP_MODIFIER_SHIFT : 0)}; + (uint16_t)(km::kbp::kmx::s_char_to_vkey[(int)ch - 32].shifted ? KM_CORE_MODIFIER_SHIFT : 0)}; } uint16_t @@ -312,7 +312,7 @@ LdmlEmbeddedTestSource::vkey_to_event(std::string const &vk_event) { std::stringstream f(vk_event); std::string s; uint16_t modifier_state = 0; - km_kbp_virtual_key vk = 0; + km_core_virtual_key vk = 0; while (std::getline(f, s, ' ')) { uint16_t modifier = get_modifier(s); if (modifier != 0) { @@ -442,7 +442,7 @@ void LdmlJsonTestSource::set_key_from_id(key_event& k, const std::u16string& id) auto *kmap = kmxplus->key2Helper.getKmap(kmapIndex); assert(kmap != nullptr); if (kmap->key == keyIndex) { - k = {(km_kbp_virtual_key)kmap->vkey, (uint16_t)kmap->mod}; + k = {(km_core_virtual_key)kmap->vkey, (uint16_t)kmap->mod}; return; } } @@ -561,7 +561,7 @@ LdmlJsonRepertoireTestSource::next_action(ldml_action &fillin) { } // we have already excluded strings, so just get the codepoint - const km_kbp_usv ch = iterator->getCodepoint(); + const km_core_usv ch = iterator->getCodepoint(); // as string for debugging. // const icu::UnicodeString& str = iterator->getString(); @@ -606,7 +606,7 @@ LdmlJsonRepertoireTestSource::next_action(ldml_action &fillin) { auto *kmap = kmxplus->key2Helper.getKmap(kmapIndex); assert(kmap != nullptr); if (kmap->key == keyIndex) { - fillin.k = {(km_kbp_virtual_key)kmap->vkey, (uint16_t)kmap->mod}; + fillin.k = {(km_core_virtual_key)kmap->vkey, (uint16_t)kmap->mod}; std::cout << "found vkey " << fillin.k.vk << ":" << fillin.k.modifier_state << std::endl; fillin.type = LDML_ACTION_KEY_EVENT; return; diff --git a/core/tests/unit/ldml/ldml_test_source.hpp b/core/tests/unit/ldml/ldml_test_source.hpp index 340f0bd0e90..f0770d96548 100644 --- a/core/tests/unit/ldml/ldml_test_source.hpp +++ b/core/tests/unit/ldml/ldml_test_source.hpp @@ -12,7 +12,7 @@ namespace km { namespace tests { struct key_event { - km_kbp_virtual_key vk; + km_core_virtual_key vk; uint16_t modifier_state; }; @@ -58,7 +58,7 @@ class LdmlTestSource { virtual int caps_lock_state(); virtual void toggle_caps_lock_state(); virtual void set_caps_lock_on(bool caps_lock_on); - virtual km_kbp_status get_expected_load_status(); + virtual km_core_status get_expected_load_status(); virtual const std::u16string &get_context() const = 0; virtual bool get_expected_beep() const; @@ -104,7 +104,7 @@ class LdmlEmbeddedTestSource : public LdmlTestSource { */ int load_source(const km::kbp::path &path); - virtual km_kbp_status get_expected_load_status(); + virtual km_core_status get_expected_load_status(); virtual const std::u16string &get_context() const; virtual bool get_expected_beep() const; diff --git a/core/tests/unit/ldml/ldml_test_utils.cpp b/core/tests/unit/ldml/ldml_test_utils.cpp index 57c71dce375..f12017f183b 100644 --- a/core/tests/unit/ldml/ldml_test_utils.cpp +++ b/core/tests/unit/ldml/ldml_test_utils.cpp @@ -13,7 +13,7 @@ namespace km { namespace tests { -km_kbp_virtual_key +km_core_virtual_key get_vk(std::string const &vk) { for (int i = 1; i < 256; i++) { if (vk == km::kbp::kmx::s_key_names[i]) { diff --git a/core/tests/unit/ldml/ldml_test_utils.hpp b/core/tests/unit/ldml/ldml_test_utils.hpp index 8b19edf8d33..1d876365bbb 100644 --- a/core/tests/unit/ldml/ldml_test_utils.hpp +++ b/core/tests/unit/ldml/ldml_test_utils.hpp @@ -13,12 +13,12 @@ #include // for char to vk mapping tables #include // for surrogate pair macros #include -#include +#include namespace km { namespace tests { -km_kbp_virtual_key get_vk(std::string const &vk); +km_core_virtual_key get_vk(std::string const &vk); } // namespace tests } // namespace km diff --git a/developer/build.sh b/developer/build.sh index 2315cd3b34c..47cc05f533b 100755 --- a/developer/build.sh +++ b/developer/build.sh @@ -27,6 +27,7 @@ builder_describe \ configure \ build \ test \ + ":utils=src/common/web/utils Developer utils" \ ":kmcmplib=src/kmcmplib Compiler - .kmn compiler" \ ":kmc-analyze=src/kmc-analyze Compiler - Analysis Tools" \ ":kmc-keyboard-info=src/kmc-keyboard-info Compiler - .keyboard_info Module" \ diff --git a/developer/src/.gitignore b/developer/src/.gitignore index 5e1280d2cad..31b79db664b 100644 --- a/developer/src/.gitignore +++ b/developer/src/.gitignore @@ -35,7 +35,6 @@ inst/kmc.wxs # /developer/TIKE/ tike/stock.kct -tike/kmcmpdll.dll # /developer/TIKE/redist/ tike/redist/Addins @@ -47,9 +46,6 @@ inst/*.wixpdb inst/download.mak inst/copydev.mak -# /developer/kmcmpdll/ -kmcmpdll/*.lastbuildstate - stock/stock.kct uitemplates/keyman.kct @@ -76,8 +72,6 @@ tike/xml/kmw/resource # These files are generated during build tike/icons.res -kmcmpdll/Compiler.res -kmcomp/icons.res tds_file.txt # These are built from corresponding manifest.in and version.in diff --git a/developer/src/Makefile b/developer/src/Makefile index 0c6c81ece6b..c62127437e3 100644 --- a/developer/src/Makefile +++ b/developer/src/Makefile @@ -3,13 +3,16 @@ # !ifdef NODELPHI -TARGETS=build-tools kmcmplib kmcmpdll kmanalyze kmdecomp server kmc +DELPHI_TARGETS= !else -TARGETS=build-tools kmcmplib kmcmpdll kmcomp kmanalyze kmconvert tike samples setup inst kmdecomp server kmc +# note: inst is not really a delphi target but depends on delphi +DELPHI_TARGETS=kmconvert tike setup inst !endif +TARGETS=build-tools kmcmplib kmanalyze $(DELPHI_TARGETS) kmdecomp samples server kmc + EXCLUDEPATHDEFINES=1 -MANIFESTS=kmcomp tike setup +MANIFESTS=tike setup CLEANS=clean-developer EXCLUDEPATHDEFINES=1 @@ -33,18 +36,10 @@ kmcmplib: global-versions .virtual cd $(DEVELOPER_ROOT)\src\kmcmplib $(MAKE) $(TARGET) -kmcmpdll: kmcmplib .virtual - cd $(DEVELOPER_ROOT)\src\kmcmpdll - $(MAKE) $(TARGET) - kmdecomp: .virtual cd $(DEVELOPER_ROOT)\src\kmdecomp $(MAKE) $(TARGET) -kmcomp: kmcmpdll - cd $(DEVELOPER_ROOT)\src\kmcomp - $(MAKE) $(TARGET) - kmanalyze: .virtual cd $(DEVELOPER_ROOT)\src\kmanalyze $(MAKE) $(TARGET) @@ -53,7 +48,7 @@ kmconvert: .virtual cd $(DEVELOPER_ROOT)\src\kmconvert $(MAKE) $(TARGET) -tike: kmcmpdll redist +tike: redist cd $(DEVELOPER_ROOT)\src\tike $(MAKE) $(TARGET) @@ -153,6 +148,8 @@ do-build-release: $(MAKE) build-release-dependencies $(MAKE) "SIGNCODE_BUILD=SIGNCODE_BUILD" build test $(MAKE) signcode + cd $(DEVELOPER_ROOT)\src\tools\verify_signatures + $(MAKE) test cd $(DEVELOPER_ROOT)\src\inst $(MAKE) diff --git a/developer/src/README.md b/developer/src/README.md index 95b33c36f44..5d923dd4646 100644 --- a/developer/src/README.md +++ b/developer/src/README.md @@ -8,7 +8,6 @@ We do not have 100% clean separation between Keyman Developer and Keyman for Windows. Shared units are intended to live in common, however the following projects will need to be updated in the future. -* buildpkg in Windows depends on various compiler units in multiple folders. * kmbrowserhost in Windows is included as part of Keyman Developer. # Folders @@ -37,22 +36,6 @@ a full node.js install. A keyboard source analysis and automated regression test tool. Testing for logical errors of a keyboard. -## src/kmcmpdll - kmcmpdll.dll & kmcmpdll.x64.dll - -This is the main source code for the keyboard compiler, packaged as a dynamic -linked library for Windows. The consumers will call 'CompileKeyboardFile'. - -### src/kmcmpdll/kcframe - kcframe.exe & kcframe.x64.exe - -Located under kmcmpdll folder this is used for debugging in Visual Studio. A -command line program compiler for a keyboard file 'kmn' into a binary format -'kmx'. Has hardcoded default arguments. - -## src/kmcomp - kmcomp.exe & kmcomp.x64.exe - -The main command line compiler for a keyboard. It compiles keyboards (.kmn), -packages (.kps), and projects (.kpj). - ## src/kmconvert - kmconvert.exe & kmconvert.x64.exe KMConvert converts keyboard layouts between different formats. KMConvert will @@ -68,31 +51,40 @@ details. ## src/kmc -node-based next generation compiler, hosts kmc, kmlmi, kmlmc, kmlmp +node-based next generation compiler, hosts kmc, (and legacy kmlmc, kmlmp) + +### src/kmc-analyze - Analysis tools + +File analysis tools for Keyman files. + +### src/kmc-keyboard-info - Keyboard Info Compiler + +Builds .keyboard_info files for use on the Keyman Cloud keyboard repository +at https://github.com/keymanapp/keyboards. Command line access through kmc. + +### src/kmc-kmn - Keyboard Compiler + +Builds .kmx files from .kmn. Command line access through kmc. ### src/kmc-ldml - LDML Keyboard Compiler -Next Generation keyboard compiler package - LDML keyboards only at present. -Command line access through kmc. +Next Generation keyboard compiler - LDML keyboards. Command line access through +kmc. ### src/kmc-model - Lexical Model Compiler -The Lexical Model Compiler, kmlmc, runs on nodeJS on all supported desktop -platforms. Command line access through kmc/kmlmc. +The Lexical Model Compiler, runs on nodeJS on all supported desktop platforms. +Command line access through kmc. -### src/kmc-package - Package Compiler +### src/kmc-model-info - Model Info Compiler -The package compiler is broadly compatible with the kmcomp .kps package -compiler. However at this stage it is only tested with lexical models, and use -with keyboards (either .js or .kmx) is not tested or supported. It is likely in -the future that the kmcomp .kps compiler will be deprecated in favour of this -one. Command line access through kmc/kmlmp. +Builds .model_info files for use on the Keyman Cloud lexical model repository at +https://github.com/keymanapp/lexical-models. Command line access through kmc. -### src/kmc-model-info - Model Info Compiler +### src/kmc-package - Package Compiler -Merges .model_info files for use on the Keyman Cloud lexical model repository at -https://github.com/keymanapp/lexical-models. Command line access through -kmc/kmlmi. +Compiles .kps packages into .kmp files. Works with both lexical model packages +and keyboard packages. Command line access through kmc. ## src/samples diff --git a/developer/src/common/delphi/compiler/CompileErrorCodes.pas b/developer/src/common/delphi/compiler/CompileErrorCodes.pas index 6c9cb9a11eb..557c5276a06 100644 --- a/developer/src/common/delphi/compiler/CompileErrorCodes.pas +++ b/developer/src/common/delphi/compiler/CompileErrorCodes.pas @@ -228,8 +228,6 @@ interface CWARN_KeyShouldIncludeNCaps = $20AD; CHINT_UnreachableRule = $10AE; - CHINT_FilenameHasDifferingCase = $10AF; - CWARN_MissingFile = $20B0; implementation diff --git a/developer/src/common/delphi/compiler/CompilePackage.pas b/developer/src/common/delphi/compiler/CompilePackage.pas deleted file mode 100644 index c7c5e071235..00000000000 --- a/developer/src/common/delphi/compiler/CompilePackage.pas +++ /dev/null @@ -1,540 +0,0 @@ -(* - Name: CompilePackage - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 20 Jun 2006 - - Modified Date: 11 May 2015 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 20 Jun 2006 - mcdurdin - Initial version - 04 Dec 2006 - mcdurdin - Add message for missing files when compiling package - 04 Jun 2007 - mcdurdin - Remove unused KeymanPath - 20 Jun 2007 - mcdurdin - Remove unused ActivationManager reference - 19 Nov 2007 - mcdurdin - I1157 - const string parameters - 04 May 2015 - mcdurdin - I4688 - V9.0 - Add build path to project settings - 04 May 2015 - mcdurdin - I4690 - V9.0 - Pull keyboard version into package version when adding a keyboard - 11 May 2015 - mcdurdin - I4706 - V9.0 - Update compile logging for silent and warning-as-error cleanness -*) -unit CompilePackage; - -interface - -uses - kpsfile, - kmpinffile, - PackageInfo, - Keyman.Developer.System.Project.ProjectLog; - -function DoCompilePackage(pack: TKPSFile; AMessageEvent: TCompilePackageMessageEvent; ASilent, ACheckFilenameConventions: Boolean; const AOutputFileName: string): Boolean; // I4688 - -implementation - -uses - Winapi.Windows, - System.Classes, - System.Generics.Collections, - System.SysUtils, - System.IniFiles, - System.Zip, - - VisualKeyboard, - utilfiletypes, - ErrorControlledRegistry, - RegistryKeys, - kmxfile, - KeymanDeveloperOptions, - KeymanVersion, - - Keyman.System.KeyboardUtils, - Keyman.System.LexicalModelUtils, - Keyman.System.CanonicalLanguageCodeUtils, - Keyman.System.PackageInfoRefreshKeyboards, - Keyman.System.PackageInfoRefreshLexicalModels, - - RedistFiles, - TempFileManager, - VersionINfo; - -type - TCompilePackage = class - private - FSilent: Boolean; - FOnMessage: TCompilePackageMessageEvent; - FTempPath: string; - //FRuntimeSourcePath: string; - pack: TKPSFile; - - FOutputFileName: string; - FTempFiles: TTempFiles; - FCheckFilenameConventions: Boolean; - - procedure FatalMessage(const msg: string); - procedure WriteMessage(AState: TProjectLogState; const msg: string); // I4706 - - function BuildKMP: Boolean; - - constructor Create(APack: TKPSFile; AMessageEvent: TCompilePackageMessageEvent; ASilent, ACheckFilenameConventions: Boolean; const AOutputFileName: string); // I4688 - destructor Destroy; override; - function Compile: Boolean; - procedure CheckForDangerousFiles; - procedure CheckKeyboardVersions; - procedure CheckKeyboardLanguages; - procedure CheckFilenameConventions; - function CheckLexicalModels: Boolean; - procedure CheckForDuplicatedLanguages(const resourceType, id: string; languages: TPackageKeyboardLanguageList); - end; - -function DoCompilePackage(pack: TKPSFile; AMessageEvent: TCompilePackageMessageEvent; ASilent, ACheckFilenameConventions: Boolean; const AOutputFileName: string): Boolean; // I4688 -begin - with TCompilePackage.Create(pack, AMessageEvent, ASilent, ACheckFilenameConventions, AOutputFileName) do // I4688 - try - Result := Compile; - finally - Free; - end; -end; - - -constructor TCompilePackage.Create(APack: TKPSFile; AMessageEvent: TCompilePackageMessageEvent; ASilent, ACheckFilenameConventions: Boolean; const AOutputFileName: string); // I4688 -begin - inherited Create; - pack := APack; - FOnMessage := AMessageEvent; - FSilent := ASilent; - FOutputFileName := AOutputFileName; - FTempFiles := TTempFiles.Create; - FCheckFilenameConventions := ACheckFilenameConventions; -end; - -destructor TCompilePackage.Destroy; -begin - FreeAndNil(FTempFiles); - inherited Destroy; -end; - -procedure TCompilePackage.CheckFilenameConventions; -var - i: Integer; -begin - if not FCheckFilenameConventions then - Exit; - - if pack.LexicalModels.Count > 0 then - begin - if not TLexicalModelUtils.DoesPackageFilenameFollowLexicalModelConventions(pack.FileName) then - WriteMessage(plsWarning, Format(TKeyboardUtils.SPackageNameDoesNotFollowLexicalModelConventions_Message, [ExtractFileName(pack.FileName)])); - end - else - begin - if not TKeyboardUtils.DoesKeyboardFilenameFollowConventions(pack.FileName) then - WriteMessage(plsWarning, Format(TKeyboardUtils.SKeyboardNameDoesNotFollowConventions_Message, [ExtractFileName(pack.FileName)])); - end; - - for i := 0 to pack.Files.Count - 1 do - begin - if not TKeyboardUtils.DoesFilenameFollowConventions(pack.Files[i].FileName) then - WriteMessage(plsWarning, Format(TKeyboardUtils.SFilenameDoesNotFollowConventions_Message, [ExtractFileName(pack.Files[i].FileName)])); - end; -end; - -function TCompilePackage.CheckLexicalModels: Boolean; -var - model: TPackageLexicalModel; -begin - if pack.LexicalModels.Count > 0 then - begin - if pack.Keyboards.Count > 0 then - begin - FatalMessage('The package contains both lexical models and keyboards, which is not permitted.'); - Exit(False); - end; - end; - - for model in pack.LexicalModels do - begin - CheckForDuplicatedLanguages('model', model.id, model.Languages); - end; - - - Exit(True); -end; - -function TCompilePackage.Compile: Boolean; -var - buf: array[0..260] of Char; - n: Integer; - b: Boolean; - f: TSearchRec; -begin - Result := False; - - if pack.FileName = '' then - begin - FatalMessage('You need to save the package before building.'); - Exit; - end; - - WriteMessage(plsInfo, 'Compiling package ' + ExtractFileName(pack.FileName) + '...'); - - if Trim(pack.Info.Desc[PackageInfo_Name]) = '' then - begin - FatalMessage('You need to fill in the package name before building.'); - Exit; - end; - - CheckFilenameConventions; - CheckForDangerousFiles; - CheckKeyboardVersions; - CheckKeyboardLanguages; - if not CheckLexicalModels then Exit; - - GetTempPath(260, buf); - FTempPath := buf; - - GetTempFileName(PChar(FTempPath), 'kmn', 0, buf); - FTempPath := buf + '.dir'; - b := CreateDir(FTempPath); - if not b then - begin - FatalMessage('Unable to create temp folder '+FTempPath); - if FileExists(buf) then DeleteFile(buf); - Exit; - end; - - try - Result := BuildKMP; - finally - n := FindFirst(FTempPath + '\*.*', 0, f); - if n = 0 then - begin - while n = 0 do - begin - DeleteFile(FTempPath + '\' + f.Name); - n := FindNext(f); - end; - FindClose(f); - end; - RemoveDirectory(PChar(FTempPath)); - if FileExists(buf) then DeleteFile(buf); - end; -end; - - -function TCompilePackage.BuildKMP: Boolean; -var - kmpinf: TKMPInfFile; - psf: TPackageContentFile; - FPackageVersion: string; - i: Integer; - f: TTempFile; -begin - Result := False; - - kmpinf := TKMPInfFile.Create; - try - { Create KMP.INF and KMP.JSON } - - kmpinf.Assign(pack); - - // Add keyboard information to the package 'for free' - // Note: this does not get us very far for mobile keyboards as - // they still require the .js to be added by the developer at this stage. - // But it ensures that all keyboards in the package are listed in the - // {Keyboards} section - - with TPackageInfoRefreshKeyboards.Create(kmpinf) do - try - OnError := Self.FOnMessage; - if not Execute then - begin - WriteMessage(plsError, 'The package build was not successful.'); - Exit; - end; - finally - Free; - end; - - with TPackageInfoRefreshLexicalModels.Create(kmpinf) do - try - OnError := Self.FOnMessage; - if not Execute then - begin - WriteMessage(plsError, 'The package build was not successful.'); - Exit; - end; - finally - Free; - end; - - // - // Update the package version to the current compiled - // keyboard version. - // - - if pack.KPSOptions.FollowKeyboardVersion then - begin - if kmpinf.Keyboards.Count = 0 then - begin - FatalMessage('The option "Follow Keyboard Version" is set but there are no keyboards in the package.'); - Exit; - end; - - FPackageVersion := kmpinf.Keyboards[0].Version; - for i := 1 to kmpinf.Keyboards.Count - 1 do - if kmpinf.Keyboards[i].Version <> FPackageVersion then - begin - FatalMessage( - 'The option "Follow Keyboard Version" is set but the package contains more than one keyboard, '+ - 'and the keyboards have mismatching versions.'); - Exit; - end; - - kmpinf.Info.Desc[PackageInfo_Version] := FPackageVersion; - end; - - kmpinf.RemoveFilePaths; - - // - // Find the minimum package version based on keyboard version - // See also MergeKeyboardInfo.pas - // - - if kmpinf.LexicalModels.Count = 0 then - begin - kmpinf.Options.FileVersion := SKeymanVersion70; - - for i := 0 to kmpinf.Keyboards.Count - 1 do - begin - if CompareVersions(kmpinf.Options.FileVersion, kmpinf.Keyboards[i].MinKeymanVersion) > 0 then - begin - // Keyman for Windows 14 and earlier only accepted version 7.0 for keyboard - // packages, so we must not write any other version in order to allow - // earlier versions of Keyman to load the package. - if CompareVersions(kmpinf.Keyboards[i].MinKeymanVersion, SKeymanVersion150) <= 0 then - begin - kmpinf.Options.FileVersion := kmpinf.Keyboards[i].MinKeymanVersion; - end; - end; - end; - - psf := TPackageContentFile.Create(kmpinf); - psf.FileName := 'kmp.inf'; - psf.Description := 'Package information'; - psf.CopyLocation := pfclPackage; - - kmpinf.Files.Add(psf); - end - else - begin - // TODO: min model version is always 12.0 currently - // but we may need to support this in future - kmpinf.Options.FileVersion := SKeymanVersion120; - end; - - // - // Prepare and save - // - - psf := TPackageContentFile.Create(kmpinf); - psf.FileName := 'kmp.json'; - psf.Description := 'Package information (JSON)'; - psf.CopyLocation := pfclPackage; - - kmpinf.Files.Add(psf); - - kmpinf.FileName := FTempPath + '\kmp.inf'; - kmpinf.SaveIni; - - kmpinf.FileName := FTempPath + '\kmp.json'; - kmpinf.SaveJSON; - finally - kmpinf.Free; - end; - - try - ForceDirectories(ExtractFileDir(FOutputFilename)); - if FileExists(FOutputFilename) then - DeleteFile(FOutputFilename); - - { Create output file } - - try - - if FileExists(FOutputFilename) then DeleteFile(FOutputFilename); - - with TZipFile.Create do - try - Open(FOutputFilename, TZipMode.zmWrite); - Add(FTempPath + '\kmp.inf'); - Add(FTempPath + '\kmp.json'); - for i := 0 to pack.Files.Count - 1 do - begin - if not FileExists(pack.Files[i].FileName) then - WriteMessage(plsWarning, 'File '+pack.Files[i].FileName+' does not exist.') - else - begin - // When compiling the package, save the kvk keyboard into binary for delivery - if pack.Files[i].FileType = ftVisualKeyboard then - begin - f := TTempFileManager.Get('.kvk'); - FTempFiles.Add(f); - with TVisualKeyboard.Create do - try - try - LoadFromFile(pack.Files[i].FileName); - except - on E:EVisualKeyboardLoader do - begin - WriteMessage(plsError, pack.Files[i].FileName+' is invalid: '+E.Message); - Continue; - end; - end; - SaveToFile(f.Name, kvksfBinary); - finally - Free; - end; - Add(f.Name, ExtractFileName(pack.Files[i].FileName)); - end - else - Add(pack.Files[i].FileName); - end; - end; - if FileCount < pack.Files.Count + 2 then - WriteMessage(plsError, 'The build was not successful. Some files were skipped.') - else - begin - Result := True; - end; - Close; - finally - Free; - end; - except - on E:EZipException do - begin - WriteMessage(plsError, E.Message); - end; - end; - finally - if not Result then - if FileExists(FOutputFilename) then DeleteFile(FOutputFilename); - end; -end; - -procedure TCompilePackage.FatalMessage(const msg: string); -begin - FOnMessage(Self, msg, plsFatal); // I4706 -end; - -procedure TCompilePackage.WriteMessage(AState: TProjectLogState; const msg: string); // I4706 -begin - FOnMessage(Self, msg, AState); // I4706 -end; - -const - SKKeymanRedistFileShouldNotBeInPackage = 'The Keyman system file ''%0:s'' should not be compiled into the package. Use a redistributable installer instead.'; - SKDocFilesDangerous = 'Microsoft Word .doc or .docx files (''%0:s'') are not portable. You should instead use HTML or PDF format.'; - -procedure TCompilePackage.CheckForDangerousFiles; -var - i, j: Integer; - s: string; -begin - for i := 0 to pack.Files.Count - 1 do - begin - s := LowerCase(ExtractFileName(pack.Files[i].FileName)); - for j := Low(CRedistFiles) to High(CRedistFiles) do - if s = CRedistFiles[j].FileName then - WriteMessage(plsWarning, Format(SKKeymanRedistFileShouldNotBeInPackage, [s])); - for j := Low(CRuntimeFiles) to High(CRuntimeFiles) do - if s = CRuntimeFiles[j].FileName then - WriteMessage(plsWarning, Format(SKKeymanRedistFileShouldNotBeInPackage, [s])); - if (ExtractFileExt(s) = '.doc') or (ExtractFileExt(s) = '.docx') then WriteMessage(plsWarning, Format(SKDocFilesDangerous, [s])); - end; -end; - -const - SKKeyboardPackageVersionMismatch = 'The keyboard %0:s has version %1:s, which differs from the package version %2:s.'; - -procedure TCompilePackage.CheckKeyboardVersions; // I4690 -var - n, i: Integer; - ki: TKeyboardInfo; -begin - n := 0; - - // The version information is checked separately if we use - // 'follow keyboard version' - if pack.KPSOptions.FollowKeyboardVersion then - Exit; - - for i := 0 to pack.Files.Count - 1 do - if pack.Files[i].FileType = ftKeymanFile then Inc(n); - - // We only test for keyboard <> package version if 1 keyboard in package - // For multi-keyboard packages, it isn't sensible to do the same - if n <> 1 then - Exit; - - for i := 0 to pack.Files.Count - 1 do - begin - if pack.Files[i].FileType <> ftKeymanFile then Continue; - if not FileExists(pack.Files[i].FileName) then Continue; - - GetKeyboardInfo(pack.Files[i].FileName, True, ki); - if ki.KeyboardVersion <> pack.Info.Desc[PackageInfo_Version] then - WriteMessage(plsWarning, Format(SKKeyboardPackageVersionMismatch, - [ExtractFileName(pack.Files[i].FileName), ki.KeyboardVersion, pack.Info.Desc[PackageInfo_Version]])); - ki.MemoryDump.Free; - end; -end; - -const - SKKeyboardPackageLanguageNonCanonical = 'The keyboard %0:s has a non-canonical language tag "%1:s" (%2:s), should be "%3:s".'; - SKKeyboardShouldHaveAtLeastOneLanguage = 'The keyboard %0:s has no language tags. It should have at least one language tag.'; - SKPackageShouldNotRepeatLanguages = 'The %0:s %1:s has a repeated language "%2:s".'; - -procedure TCompilePackage.CheckKeyboardLanguages; -var - k: TPackageKeyboard; -begin - for k in pack.Keyboards do - begin - if k.Languages.Count = 0 then - WriteMessage(plsWarning, Format(SKKeyboardShouldHaveAtLeastOneLanguage, [k.ID])); - CheckForDuplicatedLanguages('keyboard', k.ID, k.Languages); - end; -end; - -procedure TCompilePackage.CheckForDuplicatedLanguages(const resourceType, id: string; languages: TPackageKeyboardLanguageList); -var - tags: TDictionary; - lang: TPackageKeyboardLanguage; -begin - tags := TDictionary.Create; - try - for lang in languages do - begin - if tags.ContainsKey(lang.ID.ToLower) then - begin - WriteMessage(plsWarning, Format(SKPackageShouldNotRepeatLanguages, [resourceType, id, lang.ID])); - end - else - begin - tags.Add(lang.ID.ToLower, 0); - end; - end; - finally - tags.Free; - end; -end; - -end. - diff --git a/developer/src/common/delphi/compiler/CompilePackageInstaller.pas b/developer/src/common/delphi/compiler/CompilePackageInstaller.pas deleted file mode 100644 index aae46e6732e..00000000000 --- a/developer/src/common/delphi/compiler/CompilePackageInstaller.pas +++ /dev/null @@ -1,446 +0,0 @@ -(* - Name: CompilePackageInstaller - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 4 Jun 2007 - - Modified Date: 6 Jun 2015 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 04 Jun 2007 - mcdurdin - Initial version - 05 Jun 2007 - mcdurdin - I817 - Fix Unicode .inf file (not available on 9x) - 19 Jun 2007 - mcdurdin - I817 - Use Unicode .inf file again - 20 Jun 2007 - mcdurdin - Support compiling package installer from a .kmp file - 23 Aug 2007 - mcdurdin - Support building an exe installer without any embedded kmp files - 19 Nov 2007 - mcdurdin - I1157 - const string parameters - 28 Jul 2008 - mcdurdin - I1558 - Embed graphic file into setup.exe - 28 Jul 2008 - mcdurdin - I1559 - Support custom setup.exe strings - 05 Aug 2008 - mcdurdin - Don't crash if FPack is not assigned - 30 Dec 2010 - mcdurdin - I2562 - EULA as part of setup bootstrapper - 04 Nov 2011 - mcdurdin - I3126 - Add flag to allow install with full msi UI - 04 May 2012 - mcdurdin - I3306 - V9.0 - Remove TntControls + Win9x support - 08 Jun 2012 - mcdurdin - I3337 - V9.0 - Review of input/output for Unicode - 06 Feb 2012 - mcdurdin - I2971 - TIKE maintains an open handle to .msi when compiling package installer - 04 Nov 2012 - mcdurdin - I3543 - V9.0 - Merge of I2971 - TIKE maintains an open handle to .msi when compiling package installer - 23 Feb 2015 - mcdurdin - I4598 - V9.0 - Keyman installer does not show EULA when bundled with a keyboard - 04 May 2015 - mcdurdin - I4688 - V9.0 - Add build path to project settings - 11 May 2015 - mcdurdin - I4706 - V9.0 - Update compile logging for silent and warning-as-error cleanness - 06 Jun 2015 - mcdurdin - I4741 - Package installer compiler references wrong path for compiled package file -*) -unit CompilePackageInstaller; // I3306 - -interface - -uses Windows, kpsfile, kmpinffile, PackageInfo, CompilePackage, - Keyman.Developer.System.Project.Projectlog, - jwaMsi, jwaMsiQuery, SysUtils; - -function DoCompilePackageInstaller(pack: TKPSFile; FMessageEvent: TCompilePackageMessageEvent; FSilent: Boolean; - AInstallerMSI, AOutputFilename, ARedistSetupPath: string; - AUpdateInstaller: Boolean; ABuildPackage: Boolean; ALicense, ATitleImage, AAppName: string; - AStartDisabled, AStartWithConfiguration: Boolean): Boolean; // I4598 // I4688 -function DoCompileMSIInstaller(FMessageEvent: TCompilePackageMessageEvent; FSilent: Boolean; - AInstallerMSI, AOutputFilename, ARedistSetupPath, ALicense, ATitleImage, AAppName: string; - AStartDisabled, AStartWithConfiguration: Boolean): Boolean; // I2562 - -implementation - -uses - System.Classes, - System.Zip, - RedistFiles, - utildir, - utilsystem; - -type - ECompilePackageInstaller = class(Exception); - - TCompilePackageInstaller = class - private - FOnMessage: TCompilePackageMessageEvent; - FPack: TKPSFile; - FSilent: Boolean; - FVersion: WideString; - FTempPath: WideString; - FProductName: WideString; - FRedistSetupPath: string; - FInstallerMSI: WideString; - FUpdateInstaller: Boolean; - FBuildPackage: Boolean; - FOutputFilename: string; - FLicense: string; // I2562 - FTitleImage: string; - FAppName: string; - FStartDisabled: Boolean; - FStartWithConfiguration: Boolean; - procedure FatalMessage(msg: string); - procedure WriteMessage(msg: string); - function GetMSIOnlineProductID: Boolean; - public - constructor Create(APack: TKPSFile; ASilent: Boolean; AInstallerMSI: string; AUpdateInstaller, ABuildPackage: Boolean; AOutputFilename, ARedistSetupPath, ALicense, ATitleImage, AAppName: string; AStartDisabled, AStartWithConfiguration: Boolean); // I2562 - procedure Run; - property OnMessage: TCompilePackageMessageEvent read FOnMessage write FOnMessage; - end; - -function DoCompilePackageInstaller(pack: TKPSFile; FMessageEvent: TCompilePackageMessageEvent; FSilent: Boolean; - AInstallerMSI, AOutputFilename, ARedistSetupPath: string; AUpdateInstaller: Boolean; ABuildPackage: Boolean; - ALicense, ATitleImage, AAppName: string; AStartDisabled, AStartWithConfiguration: Boolean): Boolean; // I4598 // I4688 -begin - with TCompilePackageInstaller.Create(pack, FSilent, AInstallerMSI, AUpdateInstaller, ABuildPackage, - AOutputFilename, ARedistSetupPath, ALicense, ATitleImage, AAppName, - AStartDisabled, AStartWithConfiguration) do // I2562 // I4598 // I4688 - try - OnMessage := FMessageEvent; - Run; - Result := True; - finally - Free; - end; -end; - -function DoCompileMSIInstaller(FMessageEvent: TCompilePackageMessageEvent; FSilent: Boolean; AInstallerMSI: string; - AOutputFilename, ARedistSetupPath, ALicense, ATitleImage, AAppName: string; - AStartDisabled, AStartWithConfiguration: Boolean): Boolean; // I2562 -begin - with TCompilePackageInstaller.Create(nil, FSilent, AInstallerMSI, False, False, AOutputFileName, ARedistSetupPath, - ALicense, ATitleImage, AAppName, AStartDisabled, AStartWithConfiguration) do // I2562 - try - OnMessage := FMessageEvent; - Run; - Result := True; - finally - Free; - end; -end; - -{ TCompilePackageInstaller } - -constructor TCompilePackageInstaller.Create(APack: TKPSFile; ASilent: Boolean; AInstallerMSI: string; AUpdateInstaller, ABuildPackage: Boolean; AOutputFileName, ARedistSetupPath, ALicense, ATitleImage, AAppName: string; AStartDisabled, AStartWithConfiguration: Boolean); // I2562 -begin - inherited Create; - FPack := APack; - FSilent := ASilent; - FInstallerMSI := AInstallerMSI; - FUpdateInstaller := AUpdateInstaller; - FBuildPackage := ABuildPackage; - FOutputFilename := AOutputFilename; - FRedistSetupPath := ARedistSetupPath; - FLicense := ALicense; // I2562 - FTitleImage := ATitleImage; - FAppName := AAppName; - FStartDisabled := AStartDisabled; - FStartWithConfiguration := AStartWithConfiguration; - - if (FLicense <> '') and not FileExists(FLicense) then - raise ECompilePackageInstaller.CreateFmt('License file %s could not be found.', [FLicense]); - - if (FTitleImage <> '') and not FileExists(FTitleImage) then - raise ECompilePackageInstaller.CreateFmt('Title iamge file %s could not be found.', [FTitleImage]); - - if not Assigned(FPack) then - begin - if (FInstallerMSI = '') or (FUpdateInstaller) or (FBuildPackage) or (FOutputFilename = '') then - raise ECompilePackageInstaller.Create('Invalid arguments - if Pack is nil then FUpdateInstaller, FBuildPackage must be false and FInstallerMSI and FOutputFilename must not be empty.'); - end - else if FOutputFilename = '' then // I4688 - raise ECompilePackageInstaller.Create('Invalid arguments - if Pack is not nil then FOutputFilename must not be empty.'); -end; - -procedure TCompilePackageInstaller.FatalMessage(msg: string); -begin - FOnMessage(Self, msg, plsFatal); // I4706 -end; - -function TCompilePackageInstaller.GetMSIOnlineProductID: Boolean; -var - hDatabase: MSIHANDLE; - - procedure CheckResult(v: UINT); - begin - if v <> ERROR_SUCCESS then RaiseLastOSError; - end; - - function GetMSIProperty(PropertyName: WideString): WideString; - const - SQLPropertyQuery: WideString = 'SELECT Value FROM Property WHERE Property = ''%0:s'''; - var - hView, hRecord: MSIHANDLE; - buf: array[0..260] of WideChar; - sz: Cardinal; - FResult: Cardinal; - begin - CheckResult(MsiDatabaseOpenViewW(hDatabase, PWideChar(WideFormat(SQLPropertyQuery, [PropertyName])), hView)); - - try - CheckResult(MsiViewExecute(hView, 0)); - try - hRecord := 0; - FResult := MsiViewFetch(hView, hRecord); // I2971 // I3543 - try - case FResult of - ERROR_NO_MORE_ITEMS: - begin - Result := ''; - end; - ERROR_SUCCESS: - begin - sz := 260; - CheckResult(MsiRecordGetStringW(hRecord, 1, buf, sz)); - Result := Copy(buf,1,sz); - end; - else - RaiseLastOSError; - end; - finally - if hRecord <> 0 then MsiCloseHandle(hRecord); // I2971 // I3543 - end; - finally - MsiViewClose(hView); - end; - finally - MsiCloseHandle(hView); // I2971 // I3543 - end; - end; - -begin - Result := False; - CheckResult(MsiOpenDatabaseW(PWideChar(FInstallerMSI), nil, hDatabase)); - - try - FVersion := GetMSIProperty('ProductVersion'); - if FVersion = '' then - begin - FatalMessage('No ProductVersion found in the MSI file.'); - Exit; - end; - - FProductName := GetMSIProperty('ProductName'); - if FProductName = '' then - begin - FatalMessage('No ProductName found in the MSI file.'); - Exit; - end; - - Result := True; - finally - MsiCloseHandle(hDatabase); - end; -end; - -procedure TCompilePackageInstaller.Run; -var - FDestFileName: WideString; - fs: TFileStream; - s: WideString; - i: Integer; - FMSIOptions: WideString; - FRedistSetupFile, FPackageOutputFileName: string; // I3126 -begin - { Check that the .msi is setup for the package } - - FTempPath := CreateTempPath; - try - try - if Assigned(FPack) then - begin - if (FPack.FileName = '') and FBuildPackage then - begin - FatalMessage('You need to save the package before building.'); - Exit; - end; - - WriteMessage('Compiling package ' + ExtractFileName(FPack.FileName) + '...'); - - if FPack.Info.Desc['Name'] = '' then - begin - FatalMessage('You need to fill in the package name before building.'); - Exit; - end; - - if FInstallerMSI <> '' then - FPack.KPSOptions.MSIFileName := ExpandFileName(FInstallerMSI); - - FInstallerMSI := FPack.KPSOptions.MSIFileName; - FMSIOptions := FPack.KPSOptions.MSIOptions; // I3126 - -// FOutputFileName := FPack.InstallerFileName; - end - else - begin - WriteMessage('Compiling installer '+ExtractFileName(FOutputFileName)+'...'); - FInstallerMSI := ExpandFileName(FInstallerMSI); - FMSIOptions := ''; // I3126 - end; - - if not FileExists(FInstallerMSI) then - begin - FatalMessage('The installer could not be built because the MSI file was missing.'); - Exit; - end; - - if (FInstallerMSI <> '') and FUpdateInstaller and Assigned(FPack) then - FPack.SaveXML; - - if FRedistSetupPath = '' then - begin - FRedistSetupPath := GetRedistSetupPath; - WriteMessage('Redist path not specified, loading default ('+FRedistSetupPath+')'); - end; - - if not FileExists(FRedistSetupPath + 'setup.exe') and - not FileExists(FRedistSetupPath + 'setup-redist.exe') then - begin - FatalMessage('Neither setup.exe nor setup-redist.exe are present in redist ('+FRedistSetupPath+').'); - Exit; - end; - - { Check the product ID for the package } - if not GetMSIOnlineProductID then Exit; - - { TODO: if the package consists of *only* kmp files, then just add those to the installer } - - { Build the package } - if Assigned(FPack) then - begin - FPackageOutputFileName := ExtractFilePath(FOutputFilename) + ChangeFileExt(ExtractFileName(FPack.FileName), '.kmp'); // I4741 - if FBuildPackage then - begin - if not DoCompilePackage(FPack, FOnMessage, True, False, FPackageOutputFileName) then Exit; - end - else if not FileExists(FPackageOutputFileName) then - begin - FatalMessage('The package file '+FPackageOutputFileName+' does not exist'); - Exit; - end; - - { Test if the keyboards in the package need to be encrypted for the installer } - - FDestFileName := FPackageOutputFileName; - end - else - FDestFileName := ''; - - { Build setup.inf } - with TStringList.Create do - try - s := '[Setup]'#13#10 + - 'Version=' + FVersion + #13#10 + - 'MSIFileName='+ExtractFileName(FInstallerMSI) + #13#10 + - 'MSIOptions='+FMSIOptions + #13#10; // I3126 - - if FAppName <> '' then - s := s + 'AppName='+FAppName + #13#10; - - if FLicense <> '' then // I2562 - s := s + 'License='+ExtractFileName(FLicense) + #13#10; - - if FTitleImage <> '' then - s := s + 'TitleImage='+ExtractFileName(FTitleImage) + #13#10; - - - if Assigned(FPack) and (FPack.KPSOptions.GraphicFile <> nil) and - SameText(ExtractFileExt(FPack.KPSOptions.GraphicFile.FileName), '.bmp') then - begin - s := s + 'BitmapFileName='+ExtractFileName(FPack.KPSOptions.GraphicFile.FileName); - end; - - if FStartDisabled then - s := s + 'StartDisabled=True'#13#10; - - if FStartWithConfiguration then - s := s + 'StartWithConfiguration=True'#13#10; - - s := s + #13#10 + - '[Packages]'#13#10; - - if Assigned(FPack) then - s := s + ExtractFileName(FDestFileName)+'='+FPack.Info.Desc[PackageInfo_Name]; - - { Iterate through the strings } - - if Assigned(FPack) then - begin - s := s + #13#10#13#10'[Strings]'#13#10; - for i := 0 to FPack.Strings.Count - 1 do - s := s + FPack.Strings[i] + #13#10; - end; - - Text := s; - - SaveToFile(FTempPath + '\setup.inf', TEncoding.UTF8); // We want UTF-8 so Title can be Unicode // I3337 - finally - Free; - end; - - { Build zip file } - - try - with TZipFile.Create do - try - Open(FTempPath + '\setup.zip', TZipMode.zmWrite); - Add(FTempPath + '\setup.inf'); - Add(FInstallerMSI); - if FLicense <> '' then Add(FLicense); // I2562 - if FTitleImage <> '' then Add(FTitleImage); - if Assigned(FPack) and (FPack.KPSOptions.GraphicFile <> nil) and - SameText(ExtractFileExt(FPack.KPSOptions.GraphicFile.FileName), '.bmp') then - Add(FPack.KPSOptions.GraphicFile.FileName); - if FDestFileName <> '' then Add(FDestFileName); - Close; - finally - Free; - end; - except - on E:EZipException do - begin - FatalMessage(E.Message); - Exit; - end; - end; - - { Create the self-extracting archive } - - with TFileStream.Create(FOutputFileName, fmCreate) do - try - // A separate version of setup.exe which doesn't include a digital signature - // is included here, so that it can be bundled with a zip and the result signed. - if FileExists(FRedistSetupPath + 'setup-redist.exe') - then FRedistSetupFile := FRedistSetupPath + 'setup-redist.exe' - else FRedistSetupFile := FRedistSetupPath + 'setup.exe'; - fs := TFileStream.Create(FRedistSetupFile, fmOpenRead or fmShareDenyWrite); - try - CopyFrom(fs, 0); - finally - fs.Free; - end; - - fs := TFileStream.Create(FTempPath + '\setup.zip', fmOpenRead or fmShareDenyWrite); - try - CopyFrom(fs, 0); - finally - fs.Free; - end; - finally - Free; - end; - except - on E:EOSError do - FatalMessage(E.Message); - end; - finally - DeleteTempPath(FTempPath); - end; -end; - -procedure TCompilePackageInstaller.WriteMessage(msg: string); -begin - FOnMessage(Self, msg, plsInfo); // I4706 -end; - -end. diff --git a/developer/src/common/delphi/compiler/Keyman.Developer.System.ValidateKpsFile.pas b/developer/src/common/delphi/compiler/Keyman.Developer.System.ValidateKpsFile.pas deleted file mode 100644 index 991fe1249f8..00000000000 --- a/developer/src/common/delphi/compiler/Keyman.Developer.System.ValidateKpsFile.pas +++ /dev/null @@ -1,70 +0,0 @@ -unit Keyman.Developer.System.ValidateKpsFile; - -interface - -uses - Keyman.Developer.System.Project.ProjectLog; - -type - TValidateKpsFile = class - private - FOnLogEvent: TProjectLogObjectEvent; - FKpsFile: string; - FKpsXsdPath: string; - constructor Create(const AKpsFile, AKpsXsdPath: string); - function Validate: Boolean; - property OnLogEvent: TProjectLogObjectEvent read FOnLogEvent write FOnLogEvent; - public - class function Execute(const KpsFile, KpsXsdPath: string; FCallback: TProjectLogObjectEvent): Boolean; - end; - -implementation - -uses - Winapi.MsXml; - -{ TValidateKpsFile } - -constructor TValidateKpsFile.Create(const AKpsFile, AKpsXsdPath: string); -begin - inherited Create; - FKpsFile := AKpsFile; - FKpsXsdPath := AKpsXsdPath; -end; - -class function TValidateKpsFile.Execute(const KpsFile, KpsXsdPath: string; - FCallback: TProjectLogObjectEvent): Boolean; -var - v: TValidateKpsFile; -begin - v := TValidateKpsFile.Create(KpsFile, KpsXsdPath); - try - v.OnLogEvent := FCallback; - Result := v.Validate; - finally - v.Free; - end; -end; - -function TValidateKpsFile.Validate: Boolean; -var - xml, xsd: IXMLDOMDocument2; - cache: IXMLDOMSchemaCollection; -begin - xsd := CoDOMDocument60.Create; - xsd.Async := False; - xsd.load(FKpsXsdPath); - - cache := CoXMLSchemaCache60.Create; - cache.add('', xsd); - - xml := CoDOMDocument60.Create; - xml.async := False; - xml.schemas := cache; - - Result := xml.load(FKpsFile); - if not Result then - FOnLogEvent(plsError, FKpsFile, 'Validation error: '+xml.parseError.reason, 1, xml.parseError.line); -end; - -end. diff --git a/developer/src/common/delphi/compiler/compile.pas b/developer/src/common/delphi/compiler/compile.pas index 0124150ef62..4dcf1e191f7 100644 --- a/developer/src/common/delphi/compiler/compile.pas +++ b/developer/src/common/delphi/compiler/compile.pas @@ -32,16 +32,8 @@ interface uses Winapi.Windows, - kmxfileconsts; -const -{$IFDEF WIN64} - kmcmpdll_lib = 'kmcmpdll.x64.dll'; -{$ELSE} - kmcmpdll_lib = 'kmcmpdll.dll'; -{$ENDIF} - {$IFDEF WIN64} {$A16} {$ENDIF} @@ -137,13 +129,13 @@ FILE_KEYBOARD = record {$ENDIF} type - TCompilerCallback = function( line: Integer; msgcode: LongWord; text: PAnsiChar): Integer; stdcall; // I3310 TCompilerCallbackW = function( line: Integer; msgcode: LongWord; const text: string): Integer; // not available to C++ in this form const CKF_KEYMAN = 0; CKF_KEYMANWEB = 1; +// TODO: REMOVE THESE: const CERR_FATAL = $00008000; CERR_ERROR = $00004000; @@ -167,176 +159,12 @@ FILE_KEYBOARD = record FILE_DEADKEY_SIZE = 160; {$ENDIF} -function CompileKeyboardFile(kmnFile, kmxFile: PChar; FSaveDebug, CompilerWarningsAsErrors, WarnDeprecatedCode: BOOL; CallBack: TCompilerCallback): Integer; cdecl; // I4865 // I4866 -function CompileKeyboardFileToBuffer(kmnFile: PChar; buf: PFILE_KEYBOARD; CompilerWarningsAsErrors, WarnDeprecatedCode: BOOL; CallBack: TCompilerCallback; Target: Integer): Integer; cdecl; // I4865 // I4866 -function Compiler_Diagnostic(mode: Integer): Integer; -procedure Compiler_Diagnostic_Console(mode: Integer); - -type - TCompilerOptions = record - dwSize: DWORD; - ShouldAddCompilerVersion: BOOL; - end; - - COMPILER_OPTIONS = TCompilerOptions; - PCOMPILER_OPTIONS = ^COMPILER_OPTIONS; - -function SetCompilerOptions(options: PCOMPILER_OPTIONS; CallBack: TCompilerCallback): BOOL; cdecl; - -// -// For unit tests, point to a known-current version of kmcmpdll.dll -// -var - FUnitTestKMCmpDllPath: string = ''; - implementation uses - System.SysUtils, - - RegistryKeys, - RedistFiles; - -var - HKMCmpDll: THandle = 0; - -type - TCompileKeyboardFile = function (kmnFile, kmxFile: PAnsiChar; FSaveDebug, CompilerWarningsAsErrors, WarnDeprecatedCode: BOOL; // I3310 // I4865 // I4866 - CallBack: TCompilerCallback): Integer; cdecl; // TODO: K9: Convert to Unicode - - TCompileKeyboardFileToBuffer = function (kmnFile: PAnsiChar; buf: PFILE_KEYBOARD; // I3310 - CompilerWarningsAsErrors, WarnDeprecatedCode: BOOL; // I4865 // I4866 - CallBack: TCompilerCallback; Target: Integer): Integer; cdecl; // TODO: K9: Convert to Unicode - - TSetCompilerOptions = function (options: PCOMPILER_OPTIONS): BOOL; cdecl; - -function LoadCompiler(CallBack: TCompilerCallback = nil): Boolean; -var - s: string; -begin - if HKMCmpDll = 0 then - begin - s := FUnitTestKMCmpDllPath; - if s = '' then - begin - s := GetDebugKMCmpDllPath; - if (s <> '') and not FileExists(s + kmcmpdll_lib) then // I4770 - s := ''; - if s = '' then - begin - try - s := GetDeveloperRootPath; - except - s := ''; - end; - if s = '' then s := ExtractFilePath(ParamStr(0)); - end; - end; - - HKMCmpDll := LoadLibrary(PChar(s+kmcmpdll_lib)); - if HKMCmpDll = 0 then - begin - if Assigned(Callback) then - Callback(0, $8000, PAnsiChar(AnsiString('Could not load the compiler library '+s+kmcmpdll_lib+'. '+ // I4706 - 'Check that '+kmcmpdll_lib+' is in the program directory '+ - 'and that it is not corrupt.'#13#10+'Windows error message: '+SysErrorMessage(GetLastError)))); - Exit(False); - end; - end; - Result := True; -end; - -function SetCompilerOptions(options: PCOMPILER_OPTIONS; CallBack: TCompilerCallback): BOOL; -var - sco: TSetCompilerOptions; -begin - if not LoadCompiler(Callback) then - Exit(False); - - @sco := GetProcAddress(HKMCmpDll, 'SetCompilerOptions'); - if not Assigned(@sco) then - begin - Callback(0, $8000, PAnsiChar(AnsiString('Could not access the compiler. Check that '+kmcmpdll_lib+' is in the program directory '+ // I4706 - 'and that it is not corrupt.'#13#10+'Windows error message: '+SysErrorMessage(GetLastError)))); - Exit(False); - end; - - Result := sco(options); -end; - -function CompileKeyboardFile(kmnFile, kmxFile: PChar; FSaveDebug, CompilerWarningsAsErrors, WarnDeprecatedCode: BOOL; CallBack: TCompilerCallback): Integer; // I4865 // I4866 -var - ckf: TCompileKeyboardFile; -begin - //Result := 0; - - if not LoadCompiler(Callback) then Exit(-1); - - @ckf := GetProcAddress(HKMCmpDll, 'CompileKeyboardFile'); - if not Assigned(@ckf) then - begin - Callback(0, $8000, PAnsiChar(AnsiString('Could not access the compiler. Check that '+kmcmpdll_lib+' is in the program directory '+ // I4706 - 'and that it is not corrupt.'#13#10+'Windows error message: '+SysErrorMessage(GetLastError)))); - Result := -1; - Exit; - end; - - Result := ckf(PAnsiChar(AnsiString(kmnFile)), PAnsiChar(AnsiString(kmxFile)), FSaveDebug, CompilerWarningsAsErrors, WarnDeprecatedCode, Callback); // I3310 // I4865 // I4866 -end; - -function CompileKeyboardFileToBuffer(kmnFile: PChar; buf: PFILE_KEYBOARD; CompilerWarningsAsErrors, WarnDeprecatedCode: BOOL; CallBack: TCompilerCallback; Target: Integer): Integer; // I4865 // I4866 -var - ckf: TCompileKeyboardFileToBuffer; -begin - //Result := 0; - - if not LoadCompiler(Callback) then Exit(-1); - - @ckf := GetProcAddress(HKMCmpDll, 'CompileKeyboardFileToBuffer'); - if not Assigned(@ckf) then - begin - Callback(0, $8000, PAnsiChar(AnsiString('Could not access the compiler. Check that '+kmcmpdll_lib+' is in the program directory '+ // I4706 - 'and that it is not corrupt.'#13#10+'Windows error message: '+SysErrorMessage(GetLastError)))); - Result := -1; - Exit; - end; - - Result := ckf(PAnsiChar(AnsiString(kmnFile)), buf, CompilerWarningsAsErrors, WarnDeprecatedCode, Callback, Target); // I3310 // I4865 // I4866 -end; - -type - TKeyman_Diagnostic = procedure(mode: Integer); cdecl; - -function Compiler_Diagnostic(mode: Integer): Integer; -var - keyman_diagnostic: TKeyman_Diagnostic; -begin - if not LoadCompiler then Exit(-1); - - @keyman_diagnostic := GetProcAddress(HKMCmpDll, 'Keyman_Diagnostic'); - if Assigned(@keyman_diagnostic) then - begin - keyman_diagnostic(mode); - Exit(0); - end; - - Result := 2; -end; - -procedure Compiler_Diagnostic_Console(mode: Integer); -begin - case compile.Compiler_Diagnostic(0) of - 0: writeln('Should not have got here'); - 1: writeln('Test failed: could not find kmcmpdll.dll'); - 2: writeln('Test failed: could not find keyman_diagnostic in kmcmpdll.dll'); - else writeln('This should not be possible'); - end; -end; + System.SysUtils; initialization - // We want to early load the compiler because we need it loaded for - // sentry symbolication: https://github.com/getsentry/sentry-native/issues/213 - LoadCompiler; try Assert(sizeof(FILE_KEYBOARD) = FILE_KEYBOARD_SIZE, 'Assertion failure: sizeof(FILE_KEYBOARD) = FILE_KEYBOARD_SIZE'); Assert(sizeof(FILE_GROUP) = FILE_GROUP_SIZE, 'Assertion failure: sizeof(FILE_GROUP) = FILE_GROUP_SIZE'); @@ -352,8 +180,4 @@ initialization raise; end; end; -finalization - if HKMCmpDll > 0 then - FreeLibrary(HKMCmpDll); - HKMCmpDll := 0; end. diff --git a/developer/src/common/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas b/developer/src/common/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas index a3e07f03b23..e877e84f75a 100644 --- a/developer/src/common/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas +++ b/developer/src/common/delphi/lexicalmodels/Keyman.Developer.System.LexicalModelCompile.pas @@ -3,95 +3,24 @@ interface uses - Keyman.Developer.System.Project.ProjectFile, - Keyman.Developer.System.Project.ProjectLog, - Keyman.Developer.System.KeymanDeveloperPaths; + Keyman.Developer.System.Project.ProjectFile; function CompileModelFile(ProjectFile: TProjectFile; infile, outfile: string; debug: Boolean): Boolean; implementation uses - System.Classes, - System.RegularExpressions, - System.SysUtils, - compile, - utilexecute; + Keyman.Developer.System.KmcWrapper; function CompileModelFile(ProjectFile: TProjectFile; infile, outfile: string; debug: Boolean): Boolean; var - s: TStringList; - logtext, cmdline: string; - ec: Integer; - line: string; - messageLine: TRegEx; - m: TMatch; - state: TProjectLogState; - msgFilename: string; - msgText: string; - msgCode: Integer; - msgLine: Integer; - msgType: string; + w: TKmcWrapper; begin - ec := 0; - logtext := ''; - - cmdline := Format('"%s" "%s" -o "%s"', [TKeymanDeveloperPaths.LexicalModelCompilerPath, infile, outfile]); - Result := TUtilExecute.Console(cmdline, ExtractFileDir(infile), logtext, ec); - - logtext := UTF8ToString(AnsiString(logtext)); - - if not Result then - begin - ProjectFile.Project.Log(plsError, infile, - Format('Compiler failed to start with error %d: %s', [GetLastError, SysErrorMessage(GetLastError)]), CERR_ERROR, 0); - end; - - Result := Result and (ec = 0); - - // Format of messages emitted from kmlmc, see errors.ts:printLogs: - // ' (): : ' - messageLine := TRegEx.Create('^(?:(.+) \((\d+)\): )?(Hint|Warning|Error|Fatal Error): ([a-fA-F0-9]+) (.+)$'); - s := TStringList.Create; + w := TKmcWrapper.Create; try - s.Text := logtext; - for line in s do - begin - m := messageLine.Match(line); - if m.Success then - begin - msgFilename := m.Groups[1].Value; - msgLine := StrToIntDef(m.Groups[2].Value, 0); - msgType := m.Groups[3].Value; - msgCode := StrToInt('$'+m.Groups[4].Value); - msgText := m.Groups[5].Value; - if msgType = 'Hint' then - state := plsHint - else if msgType = 'Warning' then - begin - state := plsWarning; - if ProjectFile.Project.Options.CompilerWarningsAsErrors then - Result := False; - end - else if msgType = 'Error' then - begin - state := plsError; - Result := False; - end - else if msgType = 'Fatal Error' then - begin - state := plsFatal; - Result := False; - end - else - state := plsInfo; - ProjectFile.Project.Log(state, msgFilename, msgText, msgCode, msgLine); - end - else - ProjectFile.Project.Log(plsInfo, infile, line, 0, 0); - end; + Result := w.Compile(ProjectFile, infile, outfile, debug); finally - s.Free; + w.Free; end; end; diff --git a/developer/src/common/include/kmn_compiler_errors.h b/developer/src/common/include/kmn_compiler_errors.h index 13ba4a261a5..9944c61304e 100644 --- a/developer/src/common/include/kmn_compiler_errors.h +++ b/developer/src/common/include/kmn_compiler_errors.h @@ -239,8 +239,6 @@ #define CWARN_KeyShouldIncludeNCaps 0x000020AD #define CHINT_UnreachableRule 0x000010AE -#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/tsconfig.json b/developer/src/common/web/test-helpers/tsconfig.json index 47646be3a61..d7089c703f2 100644 --- a/developer/src/common/web/test-helpers/tsconfig.json +++ b/developer/src/common/web/test-helpers/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../../tsconfig.esm-base.json", + "extends": "../../../../../tsconfig.base.json", "compilerOptions": { "rootDir": ".", diff --git a/developer/src/common/web/utils/.gitignore b/developer/src/common/web/utils/.gitignore new file mode 100644 index 00000000000..d16386367f7 --- /dev/null +++ b/developer/src/common/web/utils/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/developer/src/common/web/utils/build.sh b/developer/src/common/web/utils/build.sh new file mode 100755 index 00000000000..d0da84b56fe --- /dev/null +++ b/developer/src/common/web/utils/build.sh @@ -0,0 +1,36 @@ +#!/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 + +cd "$THIS_SCRIPT_PATH" + +. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" + +builder_describe "Build Keyman Developer web utility module" \ + "@/common/web/types" \ + "clean" \ + "configure" \ + "build" \ + "test" + +builder_describe_outputs \ + configure /node_modules \ + build /developer/src/common/web/utils/build/index.js + +builder_parse "$@" + +#------------------------------------------------------------------------------------------------------------------- + +builder_run_action clean rm -rf ./build/ +builder_run_action configure verify_npm_setup +builder_run_action build tsc --build + +if builder_start_action test; then + eslint . + tsc --build test + c8 --reporter=lcov --reporter=text --exclude-after-remap mocha + builder_finish_action success test +fi diff --git a/developer/src/common/web/utils/index.ts b/developer/src/common/web/utils/index.ts new file mode 100644 index 00000000000..f18f2e90de5 --- /dev/null +++ b/developer/src/common/web/utils/index.ts @@ -0,0 +1 @@ +export { validateMITLicense } from './src/validate-mit-license.js'; \ No newline at end of file diff --git a/developer/src/common/web/utils/package.json b/developer/src/common/web/utils/package.json new file mode 100644 index 00000000000..b09b667e714 --- /dev/null +++ b/developer/src/common/web/utils/package.json @@ -0,0 +1,32 @@ +{ + "name": "@keymanapp/developer-utils", + "private": true, + "description": "Keyman Developer utilities", + "type": "module", + "exports": { + ".": "./build/index.js" + }, + "files": [ + "/build/" + ], + "devDependencies": { + "@types/node": "^20.4.1", + "ts-node": "^9.1.1", + "typescript": "^4.9.5", + "c8": "^7.12.0", + "chai": "^4.3.4", + "mocha": "^8.4.0" + }, + "scripts": { + "build": "tsc -b" + }, + "dependencies": { + "@keymanapp/common-types": "*" + }, + "mocha": { + "spec": "build/test/**/test-*.js", + "require": [ + "source-map-support/register" + ] + } +} diff --git a/developer/src/common/web/utils/src/validate-mit-license.ts b/developer/src/common/web/utils/src/validate-mit-license.ts new file mode 100644 index 00000000000..756abbeb567 --- /dev/null +++ b/developer/src/common/web/utils/src/validate-mit-license.ts @@ -0,0 +1,69 @@ +/** + * Returns an error string for license issues, or null if no issues found + * @param license + * @returns + */ +export function validateMITLicense(license: string) { + // NOTE: this function returns error messages which will not be easily + // localizable. Given this is only for use with the keyboards and + // lexical-models repositories, suggest we don't worry about this + + // split input text into paragraphs, trimming whitespace + const text = license.split('\n').map(line => line.trim()).join('\n').split(/\n\s*\n/); + + // MIT license, cleanup whitespace + const clauses = [ + `The MIT License (MIT)`, + + `Copyright ...`, // Copyright is tested separately below + + `Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions:`, + + `The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software.`, + + `THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE.` + ] + .map(clause => clause.split('\n').map(line => line.trim()).join(' ')); + + if(text.length < clauses.length) { + // not enough clauses in the file + return 'License is missing clauses from MIT license'; + } + + if(text.length > clauses.length) { + // too many clauses in the file + return 'License contains extra text'; + } + + for(let i = 0; i < clauses.length; i++) { + if(i == 1) { + // Clause 1 is the only one that can differ, and just with + // a copyright holder + if(!text[i].match(/^Copyright/)) { + // No Copyright clause + return `Clause 2 does not start with 'Copyright'`; + } + } else { + const t0 = text[i].replaceAll(/\s+/g, ' ').trim(); + const c0 = clauses[i].replaceAll(/\s+/g, ' ').trim(); + if(t0 != c0) { + // Clause does not match + return `Clause ${i+1} differs from MIT license`; + } + } + } + + return null; +} diff --git a/developer/src/common/web/utils/test/fixtures/license/LICENSE-changed-clause.md b/developer/src/common/web/utils/test/fixtures/license/LICENSE-changed-clause.md new file mode 100644 index 00000000000..fc4e6d75a36 --- /dev/null +++ b/developer/src/common/web/utils/test/fixtures/license/LICENSE-changed-clause.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2022 Me + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/developer/src/common/web/utils/test/fixtures/license/LICENSE-missing-copyright.md b/developer/src/common/web/utils/test/fixtures/license/LICENSE-missing-copyright.md new file mode 100644 index 00000000000..9f044869ef0 --- /dev/null +++ b/developer/src/common/web/utils/test/fixtures/license/LICENSE-missing-copyright.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +(c) 2015-2022 Me + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/developer/src/common/web/utils/test/fixtures/license/LICENSE-missing-title.md b/developer/src/common/web/utils/test/fixtures/license/LICENSE-missing-title.md new file mode 100644 index 00000000000..2e535c2b5a1 --- /dev/null +++ b/developer/src/common/web/utils/test/fixtures/license/LICENSE-missing-title.md @@ -0,0 +1,21 @@ +The Almost MIT License (AMIT) + +Copyright (c) 2015-2022 Me + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/developer/src/common/web/utils/test/fixtures/license/LICENSE-too-long.md b/developer/src/common/web/utils/test/fixtures/license/LICENSE-too-long.md new file mode 100644 index 00000000000..ad11d55dd09 --- /dev/null +++ b/developer/src/common/web/utils/test/fixtures/license/LICENSE-too-long.md @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) 2015-2022 Me + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +You must build a golden statue of me and place it in a prominent place for +each and every copy of the Software that you make. \ No newline at end of file diff --git a/developer/src/common/web/utils/test/fixtures/license/LICENSE-too-short.md b/developer/src/common/web/utils/test/fixtures/license/LICENSE-too-short.md new file mode 100644 index 00000000000..9debae50748 --- /dev/null +++ b/developer/src/common/web/utils/test/fixtures/license/LICENSE-too-short.md @@ -0,0 +1,18 @@ +The MIT License (MIT) + +Copyright (c) 2015-2022 Me + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/developer/src/common/web/utils/test/fixtures/license/LICENSE.md b/developer/src/common/web/utils/test/fixtures/license/LICENSE.md new file mode 100644 index 00000000000..0a8c9054319 --- /dev/null +++ b/developer/src/common/web/utils/test/fixtures/license/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2022 SIL International + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/developer/src/common/web/utils/test/helpers/index.ts b/developer/src/common/web/utils/test/helpers/index.ts new file mode 100644 index 00000000000..9968fbfea6d --- /dev/null +++ b/developer/src/common/web/utils/test/helpers/index.ts @@ -0,0 +1,18 @@ +/** + * 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/common/web/utils/test/test-license.ts b/developer/src/common/web/utils/test/test-license.ts new file mode 100644 index 00000000000..653f89ba69c --- /dev/null +++ b/developer/src/common/web/utils/test/test-license.ts @@ -0,0 +1,23 @@ +import * as fs from 'fs'; +import { assert } from 'chai'; +import 'mocha'; +import { makePathToFixture } from './helpers/index.js'; +import { validateMITLicense } from '../src/validate-mit-license.js'; + +function verifyLicenseFile(filename: string) { + return validateMITLicense(fs.readFileSync(makePathToFixture('license', filename), 'utf-8')); +} + +describe('validate-mit-license', function () { + it('should accept a valid license', function() { + assert.isNull(verifyLicenseFile('LICENSE.md')); + + }); + it('should catch invalid licenses', function() { + assert.equal(verifyLicenseFile('LICENSE-changed-clause.md'), 'Clause 3 differs from MIT license'); + assert.equal(verifyLicenseFile('LICENSE-missing-copyright.md'), "Clause 2 does not start with 'Copyright'"); + assert.equal(verifyLicenseFile('LICENSE-missing-title.md'), 'Clause 1 differs from MIT license'); + assert.equal(verifyLicenseFile('LICENSE-too-long.md'), 'License contains extra text'); + assert.equal(verifyLicenseFile('LICENSE-too-short.md'), 'License is missing clauses from MIT license'); + }); +}); diff --git a/developer/src/common/web/utils/test/tsconfig.json b/developer/src/common/web/utils/test/tsconfig.json new file mode 100644 index 00000000000..a28eeb03ab2 --- /dev/null +++ b/developer/src/common/web/utils/test/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../../kmc/tsconfig.kmc-base.json", + + "compilerOptions": { + "rootDir": ".", + "rootDirs": ["./", "../src/"], + "outDir": "../build/test", + "esModuleInterop": true, + "moduleResolution": "node16", + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "paths": { + "@keymanapp/developer-test-helpers": ["../../test-helpers/index"], + }, + }, + "include": [ + "**/test-*.ts", + "helpers/*.ts", + ], + "references": [ + { "path": "../" }, + { "path": "../../test-helpers/" }, + ] +} \ No newline at end of file diff --git a/developer/src/common/web/utils/tsconfig.json b/developer/src/common/web/utils/tsconfig.json new file mode 100644 index 00000000000..b509561362d --- /dev/null +++ b/developer/src/common/web/utils/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../../tsconfig.base.json", + + "compilerOptions": { + "rootDir": ".", + "outDir": "./build/", + "baseUrl": ".", + "paths": { + "@keymanapp/common-types": ["../../../../../common/web/types/src/main"], + }, + }, + "include": [ + "./*.ts", "src/validate-mit-license.ts", + ], + "references": [ + { "path": "../../../../../common/web/types/" }, + ] +} \ No newline at end of file diff --git a/developer/src/developer.groupproj b/developer/src/developer.groupproj index 9427b18ff45..906c009209c 100644 --- a/developer/src/developer.groupproj +++ b/developer/src/developer.groupproj @@ -12,9 +12,6 @@ - - - Default.Personality.12 @@ -50,23 +47,14 @@ - - - - - - - - - - + - + - + diff --git a/developer/src/developer.sln b/developer/src/developer.sln index 33dbf505129..3766c311c4f 100644 --- a/developer/src/developer.sln +++ b/developer/src/developer.sln @@ -1,17 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31313.79 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1259 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmdecomp", "kmdecomp\kmdecomp.vcxproj", "{963D608A-6689-469C-AE42-F56696DD42CC}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmcmpdll", "kmcmpdll\kmcmpdll.vcxproj", "{7E26FE08-721E-424B-9DA0-4A0DCA88A86E}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmanalyze", "kmanalyze\kmanalyze.vcxproj", "{4EAE6DFD-E18A-493D-A00F-4846A2593F56}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IMSample", "samples\imsample\IMSample.vcxproj", "{0C9DB8F9-B788-8782-A97D-4F8294AA0B4E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kcframe", "kmcmpdll\kcframe\kcframe.vcxproj", "{E92CC897-8228-4728-8110-028088D7A99C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imsample", "samples\imsample\IMSample.vcxproj", "{0C9DB8F9-B788-8782-A97D-4F8294AA0B4E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,14 +23,6 @@ Global {963D608A-6689-469C-AE42-F56696DD42CC}.Release|x64.ActiveCfg = Release|Win32 {963D608A-6689-469C-AE42-F56696DD42CC}.Release|x86.ActiveCfg = Release|Win32 {963D608A-6689-469C-AE42-F56696DD42CC}.Release|x86.Build.0 = Release|Win32 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Debug|x64.ActiveCfg = Debug|x64 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Debug|x64.Build.0 = Debug|x64 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Debug|x86.ActiveCfg = Debug|Win32 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Debug|x86.Build.0 = Debug|Win32 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Release|x64.ActiveCfg = Release|x64 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Release|x64.Build.0 = Release|x64 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Release|x86.ActiveCfg = Release|Win32 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Release|x86.Build.0 = Release|Win32 {4EAE6DFD-E18A-493D-A00F-4846A2593F56}.Debug|x64.ActiveCfg = Debug|x64 {4EAE6DFD-E18A-493D-A00F-4846A2593F56}.Debug|x64.Build.0 = Debug|x64 {4EAE6DFD-E18A-493D-A00F-4846A2593F56}.Debug|x86.ActiveCfg = Debug|Win32 @@ -49,14 +37,6 @@ Global {0C9DB8F9-B788-8782-A97D-4F8294AA0B4E}.Release|x64.ActiveCfg = Release|Win32 {0C9DB8F9-B788-8782-A97D-4F8294AA0B4E}.Release|x86.ActiveCfg = Release|Win32 {0C9DB8F9-B788-8782-A97D-4F8294AA0B4E}.Release|x86.Build.0 = Release|Win32 - {E92CC897-8228-4728-8110-028088D7A99C}.Debug|x64.ActiveCfg = Debug|x64 - {E92CC897-8228-4728-8110-028088D7A99C}.Debug|x64.Build.0 = Debug|x64 - {E92CC897-8228-4728-8110-028088D7A99C}.Debug|x86.ActiveCfg = Debug|Win32 - {E92CC897-8228-4728-8110-028088D7A99C}.Debug|x86.Build.0 = Debug|Win32 - {E92CC897-8228-4728-8110-028088D7A99C}.Release|x64.ActiveCfg = Release|x64 - {E92CC897-8228-4728-8110-028088D7A99C}.Release|x64.Build.0 = Release|x64 - {E92CC897-8228-4728-8110-028088D7A99C}.Release|x86.ActiveCfg = Release|Win32 - {E92CC897-8228-4728-8110-028088D7A99C}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/developer/src/inst/download.in.mak b/developer/src/inst/download.in.mak index eeadd67ac0a..81d8bb83ab1 100644 --- a/developer/src/inst/download.in.mak +++ b/developer/src/inst/download.in.mak @@ -22,10 +22,13 @@ KEYMAN_WIX_TEMP_SERVER=$(TEMP)\keyman_wix_build\Server KEYMAN_WIX_KMDEV_SERVER=$(DEVELOPER_ROOT)\bin\server KEYMAN_DEVELOPER_TEMPLATES_ROOT=$(DEVELOPER_ROOT)\src\kmconvert\data -copykmdev: makeinstaller make-kmcomp-install-zip +copykmdev: makeinstaller make-kmc-install-zip -mkdir $(DEVELOPER_ROOT)\release\$Version copy /Y $(DEVELOPER_ROOT)\src\inst\keymandeveloper.msi $(DEVELOPER_ROOT)\release\$Version\keymandeveloper.msi copy /Y $(DEVELOPER_ROOT)\src\inst\keymandeveloper-$Version.exe $(DEVELOPER_ROOT)\release\$Version\keymandeveloper-$Version.exe + $(SIGCHECK) $(DEVELOPER_ROOT)\release\$Version\* > sig1 + $(VERIFY_SIGNATURES) < sig1 + -del sig1 test-releaseexists: if exist $(DEVELOPER_ROOT)\release\$Version\keymandeveloper*.msi echo. & echo Release $Version already exists. Delete it or update VERSION.md and try again & exit 1 @@ -105,21 +108,20 @@ makeinstaller: $(SIGNCODE) /d "Keyman Developer" keymandeveloper-$Version.exe # -# Zip the files we distribute as part of the standalone kmcomp distro into release\$Version\kmcomp-$Version.zip +# Zip the files we distribute as part of the standalone kmc distro into release\$Version\kmcomp-$Version.zip # -KMCOMP_ZIP=$(DEVELOPER_ROOT)\release\$Version\kmcomp-$Version.zip +# TODO: rename this to keyman-developer-cli-$Version.zip +KMC_ZIP=$(DEVELOPER_ROOT)\release\$Version\kmcomp-$Version.zip -make-kmcomp-install-zip: copy-schemas +make-kmc-install-zip: copy-schemas cd $(DEVELOPER_ROOT)\bin - $(WZZIP) -bd -bb0 $(KMCOMP_ZIP) \ - kmcomp.exe kmcmpdll.dll \ - kmcomp.x64.exe kmcmpdll.x64.dll \ + $(WZZIP) -bd -bb0 $(KMC_ZIP) \ kmconvert.exe \ sentry.dll sentry.x64.dll \ kmdecomp.exe \ - keyboard_info.source.json keyboard_info.distribution.json \ + keyboard_info.schema.json \ keyman-touch-layout.spec.json keyman-touch-layout.clean.spec.json \ xml\layoutbuilder\*.keyman-touch-layout \ projects\* \ @@ -130,8 +132,7 @@ make-kmcomp-install-zip: copy-schemas # ldml-keyboard3.schema.json ldml-keyboardtest3.schema.json \ copy-schemas: - copy $(KEYMAN_ROOT)\common\schemas\keyboard_info\keyboard_info.source.json $(DEVELOPER_ROOT)\bin - copy $(KEYMAN_ROOT)\common\schemas\keyboard_info\keyboard_info.distribution.json $(DEVELOPER_ROOT)\bin + copy $(KEYMAN_ROOT)\common\schemas\keyboard_info\keyboard_info.schema.json $(DEVELOPER_ROOT)\bin copy $(KEYMAN_ROOT)\common\schemas\keyman-touch-layout\keyman-touch-layout.spec.json $(DEVELOPER_ROOT)\bin copy $(KEYMAN_ROOT)\common\schemas\keyman-touch-layout\keyman-touch-layout.clean.spec.json $(DEVELOPER_ROOT)\bin copy $(KEYMAN_ROOT)\common\schemas\displaymap\displaymap.schema.json $(DEVELOPER_ROOT)\bin diff --git a/developer/src/inst/kmdev.wxs b/developer/src/inst/kmdev.wxs index d43183fd27c..86f62cc82c5 100644 --- a/developer/src/inst/kmdev.wxs +++ b/developer/src/inst/kmdev.wxs @@ -159,36 +159,16 @@ - - - - - - - - - - - - - - - - - - - - - + @@ -257,10 +237,6 @@ - - - - diff --git a/developer/src/inst/node/kmlmi.cmd b/developer/src/inst/node/kmlmi.cmd deleted file mode 100644 index e674fe4d2c0..00000000000 --- a/developer/src/inst/node/kmlmi.cmd +++ /dev/null @@ -1,4 +0,0 @@ -@rem This script avoids path dependencies for node for distribution -@rem with Keyman Developer. When used on platforms other than Windows, -@rem node can be used directly with the compiler (`npm link` will setup). -@"%~dp0\node.js\node.exe" "%~dp0\kmc\kmlmi.mjs" %* diff --git a/developer/src/kmc-analyze/build.sh b/developer/src/kmc-analyze/build.sh index 8da7157d5dc..4d48b304313 100755 --- a/developer/src/kmc-analyze/build.sh +++ b/developer/src/kmc-analyze/build.sh @@ -29,7 +29,7 @@ function do_test() { # TODO: enable tests # cd test && tsc --build && cd .. && mocha # TODO: enable c8 (disabled because no coverage at present) - # c8 --reporter=lcov --reporter=text mocha + # c8 --reporter=lcov --reporter=text --exclude-after-remap mocha } builder_run_action clean rm -rf ./build/ diff --git a/developer/src/kmc-analyze/package.json b/developer/src/kmc-analyze/package.json index d60afa08488..807f70f0cea 100644 --- a/developer/src/kmc-analyze/package.json +++ b/developer/src/kmc-analyze/package.json @@ -30,7 +30,6 @@ "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", - "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", "chalk": "^2.4.2", diff --git a/developer/src/kmc-analyze/src/util/get-osk-from-kmn-file.ts b/developer/src/kmc-analyze/src/util/get-osk-from-kmn-file.ts index 686dcbd4f90..2df34e9dae0 100644 --- a/developer/src/kmc-analyze/src/util/get-osk-from-kmn-file.ts +++ b/developer/src/kmc-analyze/src/util/get-osk-from-kmn-file.ts @@ -14,9 +14,7 @@ export async function getOskFromKmnFile(callbacks: CompilerCallbacks, filename: return null; } - // Note, output filename here is just to provide path data, - // as nothing is written to disk - let result = kmnCompiler.runCompiler(filename, filename + '.tmp', { + let result = kmnCompiler.runCompiler(filename, { shouldAddCompilerVersion: false, saveDebug: false, }); diff --git a/developer/src/kmc-analyze/tsconfig.json b/developer/src/kmc-analyze/tsconfig.json index 3a9149e1213..987abd3cc3c 100644 --- a/developer/src/kmc-analyze/tsconfig.json +++ b/developer/src/kmc-analyze/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.esm-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "build/src/", diff --git a/developer/src/kmc-keyboard-info/build.sh b/developer/src/kmc-keyboard-info/build.sh index 345d0e00a95..fb35c450197 100755 --- a/developer/src/kmc-keyboard-info/build.sh +++ b/developer/src/kmc-keyboard-info/build.sh @@ -11,6 +11,7 @@ cd "$THIS_SCRIPT_PATH" builder_describe "Build Keyman kmc keyboard-info Compiler module" \ "@/common/web/types" \ + "@/developer/src/common/web/utils" \ "clean" \ "configure" \ "build" \ @@ -43,7 +44,7 @@ builder_run_action build tsc --build if builder_start_action test; then eslint . tsc --build test - c8 --reporter=lcov --reporter=text mocha + c8 --reporter=lcov --reporter=text --exclude-after-remap mocha builder_finish_action success test fi diff --git a/developer/src/kmc-keyboard-info/package.json b/developer/src/kmc-keyboard-info/package.json index 3a2bc226b1f..7ca8db1f626 100644 --- a/developer/src/kmc-keyboard-info/package.json +++ b/developer/src/kmc-keyboard-info/package.json @@ -24,10 +24,15 @@ "url": "https://github.com/keymanapp/keyman/issues" }, "dependencies": { - "@keymanapp/common-types": "*" + "@keymanapp/common-types": "*", + "@keymanapp/developer-utils": "*", + "@keymanapp/kmc-package": "*" }, + "bundleDependencies": [ + "@keymanapp/developer-utils" + ], "devDependencies": { - "@types/chai": "^4.1.7", + "@types/chai": "^4.3.5", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", "c8": "^7.12.0", diff --git a/developer/src/kmc-keyboard-info/src/index.ts b/developer/src/kmc-keyboard-info/src/index.ts index ca9e8150d9c..611e7ea9a97 100644 --- a/developer/src/kmc-keyboard-info/src/index.ts +++ b/developer/src/kmc-keyboard-info/src/index.ts @@ -4,15 +4,21 @@ */ import { minKeymanVersion } from "./min-keyman-version.js"; -import { KeyboardInfoFile, KeyboardInfoFileIncludes, KeyboardInfoFileLanguage, KeyboardInfoFilePlatform } from "./keyboard-info-file.js"; +import { KeyboardInfoFile, KeyboardInfoFileIncludes, KeyboardInfoFilePlatform } from "./keyboard-info-file.js"; import { KeymanFileTypes, CompilerCallbacks, KmpJsonFile, KmxFileReader, KMX, KeymanTargets } from "@keymanapp/common-types"; import { KeyboardInfoCompilerMessages } from "./messages.js"; import langtags from "./imports/langtags.js"; +import { validateMITLicense } from "@keymanapp/developer-utils"; +import { KmpCompiler } from "@keymanapp/kmc-package"; + +import { SchemaValidators } from "@keymanapp/common-types"; const regionNames = new Intl.DisplayNames(['en'], { type: "region" }); const scriptNames = new Intl.DisplayNames(['en'], { type: "script" }); const langtagsByTag = {}; +const HelpRoot = 'https://help.keyman.com/keyboard/'; + /** * Build a dictionary of language tags from langtags.json */ @@ -36,28 +42,24 @@ function init(): void { } export interface KeyboardInfoSources { - /** The identifier for the keyboard */ - keyboard_id: string; - - /** The data from the .kps file, transformed to kmp.json */ - kmpJsonData: KmpJsonFile.KmpJsonFile; - - /** The path in the keymanapp/keyboards repo where this keyboard may be found (optional) */ - sourcePath?: string; - - /** The full URL to the keyboard help, starting with https://help.keyman.com/keyboard/ (optional) */ - helpLink?: string; + /** The path in the keymanapp/keyboards repo where this keyboard may be found */ + sourcePath: string; /** The compiled keyboard filename and relative path (.js only) */ - keyboardFileNameJs?: string; + jsFilename?: string; /** The compiled package filename and relative path (.kmp) */ - kmpFileName: string; + kmpFilename: string; /** The source package filename and relative path (.kps) */ - kpsFileName: string; + kpsFilename: string; + + /** Last modification date for files in the project folder 'YYYY-MM-DDThh:mm:ssZ' */ + lastCommitDate?: string; + + /** Return an error if project does not meet requirements of keyboards repository */ + forPublishing: boolean; }; -/* c8 ignore stop */ export class KeyboardInfoCompiler { constructor(private callbacks: CompilerCallbacks) { @@ -65,56 +67,42 @@ export class KeyboardInfoCompiler { } /** - * Merges source .keyboard_info file with metadata from the keyboard and package source file. + * Builds a .keyboard_info file with metadata from the keyboard and package source file. * This function is intended for use within the keyboards repository. While many of the * parameters could be deduced from each other, they are specified here to reduce the * number of places the filenames are constructed. * For full documentation, see: * https://help.keyman.com/developer/cloud/keyboard_info/ * - * @param sourceKeyboardInfoFileName Path for the source .keyboard_info file * @param sources Details on files from which to extract metadata */ - public writeMergedKeyboardInfoFile( - sourceKeyboardInfoFileName: string, + public writeKeyboardInfoFile( sources: KeyboardInfoSources ): Uint8Array { - let keyboard_info: KeyboardInfoFile = null; - if(this.callbacks.fs.existsSync(sourceKeyboardInfoFileName)) { - const dataInput = this.callbacks.loadFile(sourceKeyboardInfoFileName); - if(!dataInput) { - this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_FileDoesNotExist({filename: sourceKeyboardInfoFileName})); - return null; - } + // TODO(lowpri): work from .kpj and nothing else as input. Blocked because + // .kpj work is largely in kmc at present, so that would need to move to + // a separate module. - try { - const jsonInput = new TextDecoder('utf-8', {fatal: true}).decode(dataInput); - keyboard_info = JSON.parse(jsonInput); - } catch(e) { - this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_FileIsNotValid({filename: sourceKeyboardInfoFileName, e})); - return null; - } - } else { - // TODO: this code pathway is still under development and won't yet be - // called by kmc. Building metadata without a source .keyboard_info file - // is on the roadmap but more data is needed in the .kps to complete this. - if(!sources.kmpFileName) { - // We can't build any metadata without a .kmp file - return null; - } - keyboard_info = { - // Only two fields that won't be automatically filled if file is not - // present - encodings: ['unicode'], - license: 'mit' - }; + const kmpCompiler = new KmpCompiler(this.callbacks); + const kmpJsonData = kmpCompiler.transformKpsToKmpObject(sources.kpsFilename); + if(!kmpJsonData) { + // Errors will have been emitted by KmpCompiler + return null; } + if(!sources.kmpFilename) { + // We can't build any metadata without a .kmp file + this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_CannotBuildWithoutKmpFile()); + return null; + } + + const keyboard_info: KeyboardInfoFile = {}; + let jsFile: string = null; - if(sources.keyboardFileNameJs) { - jsFile = this.loadJsFile(sources.keyboardFileNameJs); + if(sources.jsFilename) { + jsFile = this.loadJsFile(sources.jsFilename); if(!jsFile) { return null; } @@ -123,29 +111,51 @@ export class KeyboardInfoCompiler { const kmxFiles: { filename: string, data: KMX.KEYBOARD - }[] = this.loadKmxFiles(sources.kpsFileName, sources.kmpJsonData); + }[] = this.loadKmxFiles(sources.kpsFilename, kmpJsonData); // - // Build merged .keyboard_info file - // https://api.keyman.com/schemas/keyboard_info.source.json and - // https://api.keyman.com/schemas/keyboard_info.distribution.json - // https://help.keyman.com/developer/cloud/keyboard_info/1.0 + // Build .keyboard_info file + // https://api.keyman.com/schemas/keyboard_info.schema.json + // https://help.keyman.com/developer/cloud/keyboard_info/2.0 // - keyboard_info.isRTL = keyboard_info.isRTL ?? !!jsFile?.match(/this\.KRTL=1/); - if(!keyboard_info.isRTL) { - delete keyboard_info.isRTL; + keyboard_info.id = this.callbacks.path.basename(sources.kmpFilename, '.kmp'); + keyboard_info.name = kmpJsonData.info.name.description; + + // License + + if(sources.forPublishing) { + // We will only verify the license if asked to do so, so that all keyboard + // projects can be built even if license is not present. Keyboards + // repository will always verify license + if(!kmpJsonData.options?.licenseFile) { + this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_NoLicenseFound()); + return null; + } + + if(!this.isLicenseMIT(this.callbacks.resolveFilename(sources.kpsFilename, kmpJsonData.options.licenseFile))) { + return null; + } } - this.setField(keyboard_info, 'id', sources.keyboard_id); + // Even if license is not verified, we set the .keyboard_info license to + // 'mit' to meet the schema requirements. The .keyboard_info file is only + // used by the keyboards repository, so this is a fair assumption to make. + keyboard_info.license = 'mit'; - this.setField(keyboard_info, 'name', sources.kmpJsonData.info.name.description); + // isRTL - const author = sources.kmpJsonData.info.author; - if(author && (author.description || author.url)) { - this.setField(keyboard_info, 'authorName', author?.description); + if(jsFile?.match(/this\.KRTL=1/)) { + keyboard_info.isRTL = true; + } - if (author?.url) { + // author + + const author = kmpJsonData.info.author; + if(author?.description || author?.url) { + keyboard_info.authorName = author.description; + + if (author.url) { // we strip the mailto: from the .kps file for the .keyboard_info const match = author.url.match(/^(mailto\:)?(.+)$/); /* c8 ignore next 3 */ @@ -154,51 +164,47 @@ export class KeyboardInfoCompiler { return null; } - const email = match[2]; - this.setField(keyboard_info, 'authorEmail', email, false); + keyboard_info.authorEmail = match[2]; } } + // description + + if(kmpJsonData.info.description?.description) { + keyboard_info.description = kmpJsonData.info.description.description.trim(); + } + // extract the language identifiers from the language metadata arrays for // each of the keyboards in the kmp.json file, and merge into a single array // of identifiers in the .keyboard_info file. - this.fillLanguages(keyboard_info, sources.kmpJsonData); + this.fillLanguages(keyboard_info, kmpJsonData); - this.setField(keyboard_info, 'lastModifiedDate', (new Date).toISOString(), false); + // If a last commit date is not given, then just use the current time + keyboard_info.lastModifiedDate = sources.lastCommitDate ?? (new Date).toISOString(); - if(sources.kmpFileName) { - this.setField(keyboard_info, 'packageFilename', this.callbacks.path.basename(sources.kmpFileName)); + keyboard_info.packageFilename = this.callbacks.path.basename(sources.kmpFilename); - // Always overwrite with actual file size - keyboard_info.packageFileSize = this.callbacks.fileSize(sources.kmpFileName); - if(keyboard_info.packageFileSize === undefined) { - this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_FileDoesNotExist({filename:sources.kmpFileName})); - return null; - } - } else { - // TODO: warn if set in .keyboard_info? - delete keyboard_info.packageFilename; - delete keyboard_info.packageFileSize; + // Always overwrite with actual file size + keyboard_info.packageFileSize = this.callbacks.fileSize(sources.kmpFilename); + if(keyboard_info.packageFileSize === undefined) { + this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_FileDoesNotExist({filename:sources.kmpFilename})); + return null; } - if(sources.keyboardFileNameJs) { - this.setField(keyboard_info, 'jsFilename', this.callbacks.path.basename(sources.keyboardFileNameJs)); + if(sources.jsFilename) { + keyboard_info.jsFilename = this.callbacks.path.basename(sources.jsFilename); // Always overwrite with actual file size - keyboard_info.jsFileSize = this.callbacks.fileSize(sources.keyboardFileNameJs); + keyboard_info.jsFileSize = this.callbacks.fileSize(sources.jsFilename); if(keyboard_info.jsFileSize === undefined) { - this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_FileDoesNotExist({filename:sources.keyboardFileNameJs})); + this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_FileDoesNotExist({filename:sources.jsFilename})); return null; } - } else { - // TODO: warn if set in .keyboard_info? - delete keyboard_info.jsFilename; - delete keyboard_info.jsFileSize; } const includes = new Set(); keyboard_info.packageIncludes = []; - for(const file of sources.kmpJsonData.files) { + for(const file of kmpJsonData.files) { if(file.name.match(/\.(otf|ttf|ttc)$/)) { includes.add('fonts'); } else if(file.name.match(/welcome\.htm$/)) { @@ -211,20 +217,8 @@ export class KeyboardInfoCompiler { } keyboard_info.packageIncludes = [...includes]; - // Always overwrite source data - if(sources.kmpFileName) { - this.setField(keyboard_info, 'version', sources.kmpJsonData.info.version.description); - } else { - const m = jsFile?.match(/this\.KBVER\s*=\s*(['"])([^'"]+)(\1)/); - if(m) { - this.setField(keyboard_info, 'version', m[2]); - } else { - keyboard_info.version = '1.0'; - } - } + keyboard_info.version = kmpJsonData.info.version.description; - // The minimum Keyman version detected in the package file may be manually - // set higher by the developer let minVersion = minKeymanVersion; const m = jsFile?.match(/this.KMINVER\s*=\s*(['"])(.*?)\1/); if(m) { @@ -260,7 +254,7 @@ export class KeyboardInfoCompiler { // and if the .js is in the package, that it is mobile native as well, // because the targets metadata is not available in the .js. platforms.add('mobileWeb').add('desktopWeb'); - if(sources.kmpJsonData.files.find(file => file.name.match(/\.js$/))) { + if(kmpJsonData.files.find(file => file.name.match(/\.js$/))) { platforms.add('android').add('ios'); } } @@ -285,20 +279,31 @@ export class KeyboardInfoCompiler { keyboard_info.platformSupport[platform] = 'full'; } - // minKeymanVersion - if(parseFloat(keyboard_info.minKeymanVersion ?? minKeymanVersion) < parseFloat(minVersion)) { - this.setField(keyboard_info, 'minKeymanVersion', minVersion, false); + keyboard_info.minKeymanVersion = minVersion; + keyboard_info.sourcePath = sources.sourcePath; + keyboard_info.helpLink = HelpRoot + keyboard_info.id; + + // Related packages + if(kmpJsonData.relatedPackages?.length) { + keyboard_info.related = {}; + for(const p of kmpJsonData.relatedPackages) { + keyboard_info.related[p.id] = { + deprecates: p.relationship == 'deprecates' + }; + } } - if(sources.sourcePath) { - this.setField(keyboard_info, 'sourcePath', sources.sourcePath); - } + const jsonOutput = JSON.stringify(keyboard_info, null, 2); - if(sources.helpLink) { - this.setField(keyboard_info, 'helpLink', sources.helpLink); + if(!SchemaValidators.default.keyboard_info(keyboard_info)) { + // This is an internal fatal error; we should not be capable of producing + // invalid output, so it is best to throw and die + throw new Error(JSON.stringify({ + keyboard_info: keyboard_info, + error: SchemaValidators.default.keyboard_info.errors + }, null, 2)); } - const jsonOutput = JSON.stringify(keyboard_info, null, 2); return new TextEncoder().encode(jsonOutput); } @@ -324,20 +329,30 @@ export class KeyboardInfoCompiler { return ((version & 0xFF00) >> 8).toString() + '.' + (version & 0xFF).toString(); } - private setField(keyboard_info: KeyboardInfoFile, field: keyof KeyboardInfoFile, expected: unknown, warn: boolean = true) { - /* c8 ignore next 4 */ - if (keyboard_info[field] && keyboard_info[field] !== expected && expected !== undefined) { - if (warn ?? true) { - this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Warn_MetadataFieldInconsistent({ - field, value: keyboard_info[field], expected - })); - } + private isLicenseMIT(filename: string) { + const data = this.callbacks.loadFile(filename); + if(!data) { + this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_LicenseFileDoesNotExist({filename})); + return false; + } + let license = null; + try { + license = new TextDecoder().decode(data); + } catch(e) { + this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_LicenseFileIsDamaged({filename})); + return false; + } + if(!license) { + this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_LicenseFileIsDamaged({filename})); + return false; } - // TypeScript gets upset with this assignment, because it cannot deduce - // the exact type of keyboard_info[field] -- there are many possibilities! - // So we assert that it's unknown so that TypeScript can chill. - ( keyboard_info[field]) = keyboard_info[field] || expected; + const message = validateMITLicense(license); + if(message != null) { + this.callbacks.reportMessage(KeyboardInfoCompilerMessages.Error_LicenseIsNotValid({filename, message})); + return false; + } + return true; } private loadKmxFiles(kpsFilename: string, kmpJsonData: KmpJsonFile.KmpJsonFile) { @@ -362,20 +377,62 @@ export class KeyboardInfoCompiler { } private fillLanguages(keyboard_info: KeyboardInfoFile, kmpJsonData: KmpJsonFile.KmpJsonFile) { - keyboard_info.languages = keyboard_info.languages || - kmpJsonData.keyboards.reduce((a, e) => [].concat(a, e.languages.map((f) => f.id)), []); + // Collapse language data from multiple keyboards + const languages = + kmpJsonData.keyboards.reduce((a, e) => [].concat(a, (e.languages ?? []).map((f) => f.id)), []); + const examples: KmpJsonFile.KmpJsonFileExample[] = + kmpJsonData.keyboards.reduce((a, e) => [].concat(a, e.examples ?? []), []); // Transform array into object - if(Array.isArray(keyboard_info.languages)) { - const languages: {[index:string]: KeyboardInfoFileLanguage} = {}; - for(const language of keyboard_info.languages) { - languages[language] = {}; - } - keyboard_info.languages = languages; + keyboard_info.languages = {}; + for(const language of languages) { + keyboard_info.languages[language] = {}; } + const fontSource = kmpJsonData.keyboards.map(e => e.displayFont).concat(...kmpJsonData.keyboards.map(e => e.webDisplayFonts ?? [])); + const oskFontSource = kmpJsonData.keyboards.map(e => e.oskFont).concat(...kmpJsonData.keyboards.map(e => e.webOskFonts ?? [])); + for(const bcp47 of Object.keys(keyboard_info.languages)) { const language = keyboard_info.languages[bcp47]; + + // + // Add examples + // + language.examples = []; + for(const example of examples) { + if(example.id == bcp47) { + language.examples.push({ + // we don't copy over example.id + keys: example.keys, + note: example.note, + text: example.text + }); + } + } + + // + // Add fonts -- which are duplicated for each language; we'll mark this as a future + // optimization, but it's another keyboard_info breaking change so don't want to + // do it right now. + // + + if(fontSource.length) { + language.font = { + family: keyboard_info.id + ' Keyman Display Font', + source: fontSource + }; + } + + if(oskFontSource.length) { + language.oskFont = { + family: keyboard_info.id + ' Keyman OSK Font', + source: oskFontSource + }; + } + + // + // Add locale description + // const locale = new Intl.Locale(bcp47); // DisplayNames.prototype.of will throw a RangeError if it doesn't understand // the format of the bcp47 tag. This happens with Node 18.14.1, for example, with: @@ -407,5 +464,5 @@ export class KeyboardInfoCompiler { ); } } +} -} \ No newline at end of file diff --git a/developer/src/kmc-keyboard-info/src/keyboard-info-file.ts b/developer/src/kmc-keyboard-info/src/keyboard-info-file.ts index b221452bc4e..75e98e4a366 100644 --- a/developer/src/kmc-keyboard-info/src/keyboard-info-file.ts +++ b/developer/src/kmc-keyboard-info/src/keyboard-info-file.ts @@ -12,41 +12,31 @@ export interface KeyboardInfoFile { license?: "freeware" | "shareware" | "commercial" | "mit" | "other"; languages?: string[] | {[bcp47: string]: KeyboardInfoFileLanguage}; lastModifiedDate?: string; - links?: KeyboardInfoFileLink[]; packageFilename?: string; packageFileSize?: number; jsFilename?: string; jsFileSize?: number; - documentationFilename?: string; - documentationFileSize?: number; isRTL?: boolean; - encodings: KeyboardInfoFileEncodings[]; + encodings?: KeyboardInfoFileEncodings[]; packageIncludes?: KeyboardInfoFileIncludes[]; version?: string; minKeymanVersion?: string; helpLink?: string; platformSupport?: {[id in KeyboardInfoFilePlatform]?: KeyboardInfoFilePlatformSupport}; - legacyId?: number; sourcePath?: string; related?: {[id: string]: KeyboardInfoFileRelated}; deprecated?: boolean; } -export interface KeyboardInfoFileLink { - name: string; - url: string; -} - export interface KeyboardInfoFileRelated { - deprecates?: string; - deprecatedBy?: string; - note?: string; + deprecates?: boolean; + deprecatedBy?: boolean; } export interface KeyboardInfoFileLanguage { font?: KeyboardInfoFileLanguageFont; oskFont?: KeyboardInfoFileLanguageFont; - example?: KeyboardInfoFileExample; + examples?: KeyboardInfoFileExample[]; displayName?: string; languageName?: string; scriptName?: string; @@ -55,49 +45,24 @@ export interface KeyboardInfoFileLanguage { export interface KeyboardInfoFileLanguageFont { family?: string; - source?: string | string[]; - size?: string; + source?: string[]; } export interface KeyboardInfoFileExample { - keys?: string | KeyboardInfoFileExampleKey[]; + /** + * A space-separated list of keys. + * - modifiers indicated with "+" + * - spacebar is "space" + * - plus key is "shift+=" or "plus" on US English (all other punctuation as per key cap). + * - Hardware modifiers are: "shift", "ctrl", "alt", "left-ctrl", + * "right-ctrl", "left-alt", "right-alt" + * - Key caps should generally be their character for desktop (Latin script + * case insensitive), or the actual key cap for touch + * - Caps Lock should be indicated with "caps-on", "caps-off" + * + * e.g. "shift+a b right-alt+c space plus z z z" represents something like: "Ab{AltGr+C} +zzz" + */ + keys?: string; text?: string; note?: string; } - -export type KeyboardInfoFileExampleKeyId = - "K_SPACE" | - "K_A" | "K_B" | "K_C" | "K_D" | "K_E" | "K_F" | "K_G" | "K_H" | "K_I" | "K_J" | "K_K" | "K_L" | "K_M" | - "K_N" | "K_O" | "K_P" | "K_Q" | "K_R" | "K_S" | "K_T" | "K_U" | "K_V" | "K_W" | "K_X" | "K_Y" | "K_Z" | - "K_1" | "K_2" | "K_3" | "K_4" | "K_5" | "K_6" | "K_7" | "K_8" | "K_9" | "K_0" | - "K_BKQUOTE" | "K_HYPHEN" | "K_EQUAL" | "K_LBRKT" | "K_RBRKT" | "K_BKSLASH" | "K_COLON" | - "K_QUOTE" | "K_COMMA" | "K_PERIOD" | "K_SLASH" | - "K_oE2" | "K_BKSP" | "K_TAB" | "K_ENTER" | "K_ESC" | - "K_LEFT" | "K_UP" | "K_RIGHT" | "K_DOWN" | "K_PGUP" | "K_PGDN" | "K_HOME" | "K_END" | "K_INS" | "K_DEL" | - "K_F1" | "K_F2" | "K_F3" | "K_F4" | "K_F5" | "K_F6" | "K_F7" | "K_F8" | "K_F9" | "K_F10" | "K_F11" | "K_F12" | - "K_KP5" | "K_NP0" | "K_NP1" | "K_NP2" | "K_NP3" | "K_NP4" | "K_NP5" | "K_NP6" | "K_NP7" | "K_NP8" | "K_NP9" | - "K_NPSTAR" | "K_NPPLUS" | "K_NPMINUS" | "K_NPDOT" | "K_NPSLASH" | - "K_SEL" | "K_PRINT" | "K_EXEC" | "K_HELP" | "K_SEPARATOR" | - "K_F13" | "K_F14" | "K_F15" | "K_F16" | "K_F17" | "K_F18" | "K_F19" | "K_F20" | "K_F21" | "K_F22" | "K_F23" | "K_F24" | - "K_KANJI?15" | "K_KANJI?16" | "K_KANJI?17" | "K_KANJI?18" | "K_KANJI?19" | "K_KANJI?1C" | "K_KANJI?1D" | "K_KANJI?1E" | "K_KANJI?1F" | - "K_oE0" | "K_oE1" | "K_oE3" | "K_oE4" | "K_oE6" | "K_oE9" | "K_oEA" | "K_oEB" | "K_oEC" | "K_oED" | "K_oEE" | "K_oEF" | - "K_oF0" | "K_oF1" | "K_oF2" | "K_oF3" | "K_oF4" | "K_oF5" | "K_?00" | "K_?05" | "K_NPENTER" | - "K_?06" | "K_?07" | "K_?0A" | "K_?0B" | "K_?0E" | "K_?0F" | "K_?1A" | "K_?3A" | "K_?3B" | "K_?3C" | "K_?3D" | "K_?3E" | - "K_?3F" | "K_?40" | "K_?5B" | "K_?5C" | "K_?5D" | "K_?5E" | "K_?5F" | "K_?88" | "K_?89" | "K_?8A" | "K_?8B" | "K_?8C" | - "K_?8D" | "K_?8E" | "K_?8F" | "K_?92" | "K_?94" | "K_?95" | "K_?96" | "K_?97" | "K_?98" | "K_?99" | "K_?9A" | "K_?9B" | - "K_?9C" | "K_?9D" | "K_?9E" | "K_?9F" | "K_?A0" | "K_?A1" | "K_?A2" | "K_?A3" | "K_?A4" | "K_?A5" | "K_?A6" | "K_?A7" | - "K_?A8" | "K_?A9" | "K_?AA" | "K_?AB" | "K_?AC" | "K_?AD" | "K_?AE" | "K_?AF" | "K_?B0" | "K_?B1" | "K_?B2" | "K_?B3" | - "K_?B4" | "K_?B5" | "K_?B6" | "K_?B7" | "K_?B8" | "K_?B9" | "K_?C1" | "K_?C2" | "K_?C3" | "K_?C4" | "K_?C5" | "K_?C6" | - "K_?C7" | "K_?C8" | "K_?C9" | "K_?CA" | "K_?CB" | "K_?CC" | "K_?CD" | "K_?CE" | "K_?CF" | "K_?D0" | "K_?D1" | "K_?D2" | - "K_?D3" | "K_?D4" | "K_?D5" | "K_?D6" | "K_?D7" | "K_?D8" | "K_?D9" | "K_?DA" | "K_oDF" | "K_?E5" | "K_?E7" | "K_?E8" | - "K_?F6" | "K_?F7" | "K_?F8" | "K_?F9" | "K_?FA" | "K_?FB" | "K_?FC" | "K_?FD" | "K_?FE" | "K_?FF"; - -export type KeyboardInfoFileExampleKeyModifier = - "shift" | "s" | "ctrl" | "c" | "alt" | "a" | "left-ctrl" | "lc" | - "right-ctrl" | "rc" | "left-alt" | "la" | "right-alt" | "ra"; - -export interface KeyboardInfoFileExampleKey { - key?: KeyboardInfoFileExampleKeyId; - modifiers?: KeyboardInfoFileExampleKeyModifier[]; -} - diff --git a/developer/src/kmc-keyboard-info/src/messages.ts b/developer/src/kmc-keyboard-info/src/messages.ts index e72fd2f0e92..fb2000fca4f 100644 --- a/developer/src/kmc-keyboard-info/src/messages.ts +++ b/developer/src/kmc-keyboard-info/src/messages.ts @@ -2,7 +2,7 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m const Namespace = CompilerErrorNamespace.KeyboardInfoCompiler; // 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; @@ -26,5 +26,36 @@ export class KeyboardInfoCompilerMessages { `Invalid author email: ${o.email}`); static ERROR_InvalidAuthorEmail = SevError | 0x0005; + static Error_LicenseFileDoesNotExist = (o:{filename:string}) => m(this.ERROR_LicenseFileIsMissing, + `License file ${o.filename} does not exist.`); + static ERROR_LicenseFileIsMissing = SevError | 0x0006; + + static Error_LicenseFileIsDamaged = (o:{filename:string}) => m(this.ERROR_LicenseFileIsDamaged, + `License file ${o.filename} could not be loaded or decoded.`); + static ERROR_LicenseFileIsDamaged = SevError | 0x0007; + + static Error_LicenseIsNotValid = (o:{filename:string,message:string}) => m(this.ERROR_LicenseIsNotValid, + `An error was encountered parsing license file ${o.filename}: ${o.message}.`); + static ERROR_LicenseIsNotValid = SevError | 0x0008; + + static Error_CannotBuildWithoutKmpFile = () => m(this.ERROR_CannotBuildWithoutKmpFile, + `Compiling the .keyboard_info file requires a .kmp file for metadata.`); + static ERROR_CannotBuildWithoutKmpFile = SevError | 0x0009; + + static Error_NoLicenseFound = () => m(this.ERROR_NoLicenseFound, + `No license for the keyboard was found. MIT license is required for publication to Keyman keyboards repository.`); + static ERROR_NoLicenseFound = SevError | 0x000A; + + static Hint_OutputValidation = (o:{message: any}) => m(this.HINT_OutputValidation, + `Validating output: ${o.message}.`); + static HINT_OutputValidation = SevHint | 0x000B; + + static Warn_OutputValidation = (o:{message: any}) => m(this.WARN_OutputValidation, + `Validating output: ${o.message}.`); + static WARN_OutputValidation = SevWarn | 0x000C; + + static Error_OutputValidation = (o:{message: any}) => m(this.ERROR_OutputValidation, + `Validating output: ${o.message}.`); + static ERROR_OutputValidation = SevError | 0x000D; } diff --git a/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/LICENSE.md b/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/LICENSE.md new file mode 100644 index 00000000000..0a8c9054319 --- /dev/null +++ b/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2022 SIL International + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/build/khmer_angkor.keyboard_info b/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/build/khmer_angkor.keyboard_info index 20b86397d4b..7d85bbfb6cf 100644 --- a/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/build/khmer_angkor.keyboard_info +++ b/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/build/khmer_angkor.keyboard_info @@ -3,22 +3,22 @@ "languages": { "km": { "oskFont": { - "family": "Khmer Busra Kbd", + "family": "khmer_angkor Keyman OSK Font", "source": [ "khmer_busra_kbd.ttf" ] }, "font": { - "family": "Khmer Mondulkiri", + "family": "khmer_angkor Keyman Display Font", "source": [ "Mondulkiri-R.ttf" ] }, - "example": { - "keys": "xjmEr", + "examples": [{ + "keys": "x j m E r", "text": "\u1781\u17D2\u1798\u17C2\u179A", "note": "Name of language" - }, + }], "displayName": "Khmer", "languageName": "Khmer" } diff --git a/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/khmer_angkor.keyboard_info b/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/khmer_angkor.keyboard_info deleted file mode 100644 index 18a02278670..00000000000 --- a/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/khmer_angkor.keyboard_info +++ /dev/null @@ -1,30 +0,0 @@ -{ - "license": "mit", - "languages": { - "km": { - "oskFont": { - "family": "Khmer Busra Kbd", - "source": [ - "khmer_busra_kbd.ttf" - ] - }, - "font": { - "family": "Khmer Mondulkiri", - "source": [ - "Mondulkiri-R.ttf" - ] - }, - "example": { - "keys": "xjmEr", - "text": "ខ្មែរ", - "note": "Name of language" - } - } - }, - "description": "

Khmer Unicode keyboard layout based on the NiDA keyboard layout. Automatically corrects many common keying errors.

", - "related": { - "khmer10": { - "deprecates": true - } - } -} \ No newline at end of file diff --git a/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/source/khmer_angkor.kps b/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/source/khmer_angkor.kps index 5e27ab92a70..0e646475d59 100644 --- a/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/source/khmer_angkor.kps +++ b/developer/src/kmc-keyboard-info/test/fixtures/khmer_angkor/source/khmer_angkor.kps @@ -10,6 +10,7 @@ splash.gif + ..\LICENSE.md @@ -22,8 +23,18 @@ Makara Sok https://keyman.com/keyboards/khmer_angkor + Khmer Unicode keyboard layout based on the NiDA keyboard layout. Automatically corrects many common keying errors. + + + + + ..\LICENSE.md + File LICENSE.md + 0 + .md + ..\build\khmer_angkor.js File khmer_angkor.js @@ -143,6 +154,9 @@ Central Khmer (Khmer, Cambodia) + + + diff --git a/developer/src/kmc-keyboard-info/test/test-keyboard-info-compiler.ts b/developer/src/kmc-keyboard-info/test/test-keyboard-info-compiler.ts index 6ab3f1e2c4f..a0fdb14ca21 100644 --- a/developer/src/kmc-keyboard-info/test/test-keyboard-info-compiler.ts +++ b/developer/src/kmc-keyboard-info/test/test-keyboard-info-compiler.ts @@ -4,7 +4,6 @@ import 'mocha'; import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; import { makePathToFixture } from './helpers/index.js'; import { KeyboardInfoCompiler } from '../src/index.js'; -import { KmpCompiler } from '@keymanapp/kmc-package'; const callbacks = new TestCompilerCallbacks(); @@ -14,25 +13,28 @@ beforeEach(function() { describe('keyboard-info-compiler', function () { it('compile a .keyboard_info file correctly', function() { - const path = makePathToFixture('khmer_angkor', 'khmer_angkor.keyboard_info'); - const jsFileName = makePathToFixture('khmer_angkor', 'build', 'khmer_angkor.js'); - const kpsFileName = makePathToFixture('khmer_angkor', 'source', 'khmer_angkor.kps'); - const kmpFileName = makePathToFixture('khmer_angkor', 'build', 'khmer_angkor.kmp'); + const jsFilename = makePathToFixture('khmer_angkor', 'build', 'khmer_angkor.js'); + const kpsFilename = makePathToFixture('khmer_angkor', 'source', 'khmer_angkor.kps'); + const kmpFilename = makePathToFixture('khmer_angkor', 'build', 'khmer_angkor.kmp'); const buildKeyboardInfoFilename = makePathToFixture('khmer_angkor', 'build', 'khmer_angkor.keyboard_info'); - const kmpCompiler = new KmpCompiler(callbacks); - const kmpJsonData = kmpCompiler.transformKpsToKmpObject(kpsFileName); - const compiler = new KeyboardInfoCompiler(callbacks); - const data = compiler.writeMergedKeyboardInfoFile(path, { - kmpFileName, - kmpJsonData, - keyboard_id: 'khmer_angkor', - sourcePath: 'release/k/khmer_angkor', - kpsFileName, - helpLink: 'https://help.keyman.com/keyboard/khmer_angkor', - keyboardFileNameJs: jsFileName, - }); + let data = null; + try { + data = compiler.writeKeyboardInfoFile({ + kmpFilename, + sourcePath: 'release/k/khmer_angkor', + kpsFilename, + jsFilename: jsFilename, + forPublishing: true, + }); + } catch(e) { + callbacks.printMessages(); + throw e; + } + if(data == null) { + callbacks.printMessages(); + } assert.isNotNull(data); const actual = JSON.parse(new TextDecoder().decode(data)); @@ -44,8 +46,4 @@ describe('keyboard-info-compiler', function () { assert.deepEqual(actual, expected); }); - - it('compile a .keyboard_info file correctly when no source .keyboard_info exists', function() { - this.skip(); // TODO: support keyboard_info when no source file exists (determine license from LICENSE.md) - }); }); diff --git a/developer/src/kmc-keyboard-info/test/tsconfig.json b/developer/src/kmc-keyboard-info/test/tsconfig.json index bf96b7b1509..6d48ad864d2 100644 --- a/developer/src/kmc-keyboard-info/test/tsconfig.json +++ b/developer/src/kmc-keyboard-info/test/tsconfig.json @@ -6,7 +6,6 @@ "rootDirs": ["./", "../src/"], "outDir": "../build/test", "esModuleInterop": true, - "moduleResolution": "node16", "allowSyntheticDefaultImports": true, "baseUrl": ".", "paths": { diff --git a/developer/src/kmc-keyboard-info/tsconfig.json b/developer/src/kmc-keyboard-info/tsconfig.json index 85630292e88..bf1ad29bd95 100644 --- a/developer/src/kmc-keyboard-info/tsconfig.json +++ b/developer/src/kmc-keyboard-info/tsconfig.json @@ -12,6 +12,8 @@ "noImplicitAny": false, "paths": { "@keymanapp/common-types": ["../../../common/web/types/src/main"], + "@keymanapp/kmc-package": ["../kmc-package/source"], + "@keymanapp/developer-utils": ["../common/web/utils/index"], } }, "include": [ @@ -20,5 +22,7 @@ ], "references": [ { "path": "../../../common/web/types" }, + { "path": "../kmc-package/" }, + { "path": "../common/web/utils/" }, ] } diff --git a/developer/src/kmc-kmn/build.sh b/developer/src/kmc-kmn/build.sh index a69dec76466..7766610ac7a 100755 --- a/developer/src/kmc-kmn/build.sh +++ b/developer/src/kmc-kmn/build.sh @@ -70,7 +70,7 @@ if builder_start_action test; then copy_deps tsc --build test/ npm run lint - c8 --reporter=lcov --reporter=text mocha "${builder_extra_params[@]}" + c8 --reporter=lcov --reporter=text --exclude-after-remap mocha "${builder_extra_params[@]}" builder_finish_action success test fi diff --git a/developer/src/kmc-kmn/package.json b/developer/src/kmc-kmn/package.json index cdd1f982196..5802c859055 100644 --- a/developer/src/kmc-kmn/package.json +++ b/developer/src/kmc-kmn/package.json @@ -38,7 +38,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", diff --git a/developer/src/kmc-kmn/src/compiler/compiler.ts b/developer/src/kmc-kmn/src/compiler/compiler.ts index 6f5f9b331a7..e052f32b680 100644 --- a/developer/src/kmc-kmn/src/compiler/compiler.ts +++ b/developer/src/kmc-kmn/src/compiler/compiler.ts @@ -140,9 +140,8 @@ export class KmnCompiler implements UnicodeSetParser { return true; } - // TODO: use outFile from options - public run(infile: string, outfile: string, options?: KmnCompilerOptions): boolean { - let result = this.runCompiler(infile, outfile, options); + public run(infile: string, options?: KmnCompilerOptions): boolean { + let result = this.runCompiler(infile, options); if(result) { if(result.kmx) { this.callbacks.fs.writeFileSync(result.kmx.filename, result.kmx.data); @@ -235,7 +234,7 @@ export class KmnCompiler implements UnicodeSetParser { return new Uint8Array(new Uint8Array(Module.HEAP8.buffer, offset, size)); } - public runCompiler(infile: string, outfile: string, options: KmnCompilerOptions): CompilerResult { + public runCompiler(infile: string, options: KmnCompilerOptions): CompilerResult { if(!this.verifyInitialized()) { /* c8 ignore next 2 */ return null; @@ -243,6 +242,8 @@ export class KmnCompiler implements UnicodeSetParser { options = {...baseOptions, ...options}; + options.outFile = options.outFile ?? infile.replace(/\.kmn$/i, '.kmx'); + (globalThis as any)[this.callbackID] = { message: this.compilerMessageCallback, loadFile: this.loadFileCallback @@ -267,7 +268,7 @@ export class KmnCompiler implements UnicodeSetParser { if(result.extra.targets & COMPILETARGETS_KMX) { result.kmx = { - filename: outfile, + filename: options.outFile, data: this.copyWasmBuffer(wasm_result.kmx, wasm_result.kmxSize) }; } @@ -282,7 +283,7 @@ export class KmnCompiler implements UnicodeSetParser { } if(result.extra.kvksFilename) { - result.kvk = this.runKvkCompiler(result.extra.kvksFilename, infile, outfile, result.displayMap); + result.kvk = this.runKvkCompiler(result.extra.kvksFilename, infile, options.outFile, result.displayMap); if(!result.kvk) { return null; } @@ -302,7 +303,7 @@ export class KmnCompiler implements UnicodeSetParser { kmw_result.displayMap = result.displayMap; // we can safely re-use the kmx compile displayMap const web_kmx = this.copyWasmBuffer(wasm_result.kmx, wasm_result.kmxSize); - result.js = this.runWebCompiler(infile, outfile, web_kmx, result.kvk?.data, kmw_result, options); + result.js = this.runWebCompiler(infile, options.outFile, web_kmx, result.kvk?.data, kmw_result, options); if(!result.js) { return null; } diff --git a/developer/src/kmc-kmn/test/fixtures/features/version_160.kmn b/developer/src/kmc-kmn/test/fixtures/features/version_160.kmn new file mode 100644 index 00000000000..43ba17e6af7 --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/features/version_160.kmn @@ -0,0 +1,8 @@ +c Description: Verifies that kmcmplib can compile a v16.0 keyboard + +store(&NAME) 'version_160' +store(&VERSION) '16.0' + +begin unicode > use(main) + +group(main) using keys diff --git a/developer/src/kmc-kmn/test/fixtures/features/version_170.kmn b/developer/src/kmc-kmn/test/fixtures/features/version_170.kmn new file mode 100644 index 00000000000..b9779d0734b --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/features/version_170.kmn @@ -0,0 +1,8 @@ +c Description: Verifies that kmcmplib can compile a v17.0 keyboard + +store(&NAME) 'version_170' +store(&VERSION) '17.0' + +begin unicode > use(main) + +group(main) using keys diff --git a/developer/src/kmc-kmn/test/kmw/test-kmw-compiler.ts b/developer/src/kmc-kmn/test/kmw/test-kmw-compiler.ts index cb9e101c491..42aa489a911 100644 --- a/developer/src/kmc-kmn/test/kmw/test-kmw-compiler.ts +++ b/developer/src/kmc-kmn/test/kmw/test-kmw-compiler.ts @@ -19,7 +19,6 @@ const debug=false; const generateTestFilenames = (id: string) => ({ fixture: fixturesDir + id + KeymanFileTypes.Binary.WebKeyboard, source: fixturesDir + id + KeymanFileTypes.Source.KeymanKeyboard, - intermediate: fixturesDir + id + KeymanFileTypes.Binary.Keyboard, binary: fixturesDir + id + '.test' + KeymanFileTypes.Binary.WebKeyboard }); @@ -67,7 +66,7 @@ describe('KeymanWeb Compiler', function() { function run_test_keyboard(kmnCompiler: KmnCompiler, id: string): { result: CompilerResult, actualCode: string, actual: ETLResult, expectedCode: string, expected: ETLResult } { const filenames = generateTestFilenames(id); - let result = kmnCompiler.runCompiler(filenames.source, filenames.intermediate, { + let result = kmnCompiler.runCompiler(filenames.source, { shouldAddCompilerVersion: false, saveDebug: true, }); diff --git a/developer/src/kmc-kmn/test/test-compiler.ts b/developer/src/kmc-kmn/test/test-compiler.ts index 9f97560679d..c69ae89a95a 100644 --- a/developer/src/kmc-kmn/test/test-compiler.ts +++ b/developer/src/kmc-kmn/test/test-compiler.ts @@ -38,12 +38,12 @@ describe('Compiler class', function() { const fixtureName = baselineDir + 'k_000___null_keyboard.kmx'; const infile = baselineDir + 'k_000___null_keyboard.kmn'; - const outfile = __dirname + '/k_000___null_keyboard.kmx'; + const outFile = __dirname + '/k_000___null_keyboard.kmx'; - assert(compiler.run(infile, outfile, {saveDebug: true, shouldAddCompilerVersion: false})); + assert(compiler.run(infile, {saveDebug: true, outFile, shouldAddCompilerVersion: false})); - assert(fs.existsSync(outfile)); - const outfileData = fs.readFileSync(outfile); + assert(fs.existsSync(outFile)); + const outfileData = fs.readFileSync(outFile); const fixtureData = fs.readFileSync(fixtureName); assert.equal(outfileData.byteLength, fixtureData.byteLength); assert.deepEqual(outfileData, fixtureData); @@ -62,12 +62,12 @@ describe('Compiler class', function() { if(file.match(/\.kmx$/)) { const fixtureName = baselineDir + file; const infile = baselineDir + file.replace(/x$/, 'n'); - const outfile = __dirname + '/' + file; + const outFile = __dirname + '/' + file; - assert(compiler.run(infile, outfile, {saveDebug: true, shouldAddCompilerVersion: false})); + assert(compiler.run(infile, {saveDebug: true, outFile, shouldAddCompilerVersion: false})); - assert(fs.existsSync(outfile)); - const outfileData = fs.readFileSync(outfile); + assert(fs.existsSync(outFile)); + const outfileData = fs.readFileSync(outFile); const fixtureData = fs.readFileSync(fixtureName); assert.equal(outfileData.byteLength, fixtureData.byteLength); assert.deepEqual(outfileData, fixtureData); @@ -89,7 +89,7 @@ describe('Compiler class', function() { const resultingKmxfile = __dirname + '/caps_lock_layer_3620.kmx'; const resultingKvkfile = __dirname + '/caps_lock_layer_3620.kvk'; - assert.isTrue(compiler.run(infile, resultingKmxfile, {saveDebug: true, shouldAddCompilerVersion: false})); + assert.isTrue(compiler.run(infile, {saveDebug: true, shouldAddCompilerVersion: false})); assert.isTrue(fs.existsSync(resultingKmxfile)); assert.isTrue(fs.existsSync(resultingKvkfile)); diff --git a/developer/src/kmc-kmn/test/test-features.ts b/developer/src/kmc-kmn/test/test-features.ts new file mode 100644 index 00000000000..aa3b050a9fd --- /dev/null +++ b/developer/src/kmc-kmn/test/test-features.ts @@ -0,0 +1,44 @@ +import 'mocha'; +import { assert } from 'chai'; +import { KmnCompiler } from '../src/main.js'; +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; +import { makePathToFixture } from './helpers/index.js'; +import { KMX, KmxFileReader } from '@keymanapp/common-types'; + +describe('Keyboard compiler features', async function() { + let compiler: KmnCompiler = null; + let callbacks: TestCompilerCallbacks = null; + + this.beforeAll(async function() { + compiler = new KmnCompiler(); + callbacks = new TestCompilerCallbacks(); + assert(await compiler.init(callbacks)); + assert(compiler.verifyInitialized()); + }); + + beforeEach(function() { + callbacks.clear(); + }); + + // Test each Keyman file version target + + const versions = [ + // TODO(lowpri): we should add a test for each version + also test automatic feature detection + ['16.0', '160', KMX.KMXFile.VERSION_160], + ['17.0', '170', KMX.KMXFile.VERSION_170], + ]; + + for(const v of versions) { + it(`should build a version ${v[0]} keyboard`, function() { + const fixtureName = makePathToFixture('features', `version_${v[1]}.kmn`); + + const result = compiler.runCompiler(fixtureName, `version_${v[1]}.kmx`, {saveDebug: true}); + if(result === null) callbacks.printMessages(); + assert.isNotNull(result); + + const reader = new KmxFileReader(); + const keyboard = reader.read(result.kmx.data); + assert.equal(keyboard.fileVersion, v[2]); + }); + } +}); diff --git a/developer/src/kmc-kmn/test/test-messages.ts b/developer/src/kmc-kmn/test/test-messages.ts index 88edc9b8f09..72624f9599c 100644 --- a/developer/src/kmc-kmn/test/test-messages.ts +++ b/developer/src/kmc-kmn/test/test-messages.ts @@ -1,11 +1,10 @@ import 'mocha'; -import path from 'path'; import { assert } from 'chai'; import { CompilerMessages } from '../src/compiler/messages.js'; import { TestCompilerCallbacks, verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers'; import { makePathToFixture } from './helpers/index.js'; import { KmnCompiler } from '../src/main.js'; -import { CompilerErrorNamespace, KeymanFileTypes } from '@keymanapp/common-types'; +import { CompilerErrorNamespace } from '@keymanapp/common-types'; describe('CompilerMessages', function () { const callbacks = new TestCompilerCallbacks(); @@ -28,10 +27,9 @@ describe('CompilerMessages', function () { assert(compiler.verifyInitialized()); const kmnPath = makePathToFixture(...fixture); - const outfile = path.basename(kmnPath, KeymanFileTypes.Source.KeymanKeyboard) + KeymanFileTypes.Binary.Keyboard; // Note: throwing away compile results (just to memory) - compiler.runCompiler(kmnPath, outfile, {saveDebug: true, shouldAddCompilerVersion: false}); + compiler.runCompiler(kmnPath, {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)); diff --git a/developer/src/kmc-kmn/tsconfig.json b/developer/src/kmc-kmn/tsconfig.json index 04683902eae..4210b750af0 100644 --- a/developer/src/kmc-kmn/tsconfig.json +++ b/developer/src/kmc-kmn/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../tsconfig.esm-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "build/src/", diff --git a/developer/src/kmc-ldml/package.json b/developer/src/kmc-ldml/package.json index b140a5c552e..88173c2902e 100644 --- a/developer/src/kmc-ldml/package.json +++ b/developer/src/kmc-ldml/package.json @@ -29,10 +29,8 @@ "@keymanapp/keyman-version": "*", "@keymanapp/kmc-kmn": "*", "@keymanapp/ldml-keyboard-constants": "*", - "ajv": "^8.11.0", "restructure": "git+https://github.com/keymanapp/dependency-restructure.git#7a188a1e26f8f36a175d95b67ffece8702363dfc", - "semver": "^7.5.2", - "xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829" + "semver": "^7.5.2" }, "devDependencies": { "@keymanapp/developer-test-helpers": "*", @@ -40,7 +38,6 @@ "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", "@types/semver": "^7.3.12", - "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", "chalk": "^2.4.2", diff --git a/developer/src/kmc-ldml/src/compiler/compiler.ts b/developer/src/kmc-ldml/src/compiler/compiler.ts index ad0340ed820..c29aa9fa938 100644 --- a/developer/src/kmc-ldml/src/compiler/compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/compiler.ts @@ -8,7 +8,6 @@ import { LayrCompiler } from './layr.js'; import { LocaCompiler } from './loca.js'; import { MetaCompiler } from './meta.js'; import { NameCompiler } from './name.js'; -import { VkeyCompiler } from './vkey.js'; import { VarsCompiler } from './vars.js'; import { StrsCompiler, ElemCompiler, ListCompiler, UsetCompiler } from './empty-compiler.js'; @@ -38,7 +37,6 @@ export const SECTION_COMPILERS = [ MetaCompiler, NameCompiler, TranCompiler, - VkeyCompiler, ]; export class LdmlKeyboardCompiler { diff --git a/developer/src/kmc-ldml/src/compiler/disp.ts b/developer/src/kmc-ldml/src/compiler/disp.ts index 77c9609876a..ade215b1fa7 100644 --- a/developer/src/kmc-ldml/src/compiler/disp.ts +++ b/developer/src/kmc-ldml/src/compiler/disp.ts @@ -57,15 +57,20 @@ export class DispCompiler extends SectionCompiler { let result = new Disp(); // displayOptions - result.baseCharacter = sections.strs.allocAndUnescapeString(this.keyboard3.displays?.displayOptions?.baseCharacter); - - // TODO-LDML: substitute variables! + result.baseCharacter = sections.strs.allocString(this.keyboard3.displays?.displayOptions?.baseCharacter, {unescape: true}); // displays result.disps = this.keyboard3.displays?.display.map(display => ({ - to: sections.strs.allocAndUnescapeString(sections.vars.substituteMarkerString(display.to)), + to: sections.strs.allocString(display.to, { + stringVariables: true, + markers: true, + unescape: true, + }, sections), id: sections.strs.allocString(display.id), // not escaped, not substituted - display: sections.strs.allocAndUnescapeString(display.display), + display: sections.strs.allocString(display.display, { + stringVariables: true, + unescape: true, + }, sections), })) || []; // TODO-LDML: need coverage for the [] result.disps.sort((a: DispItem, b: DispItem) => { diff --git a/developer/src/kmc-ldml/src/compiler/keys.ts b/developer/src/kmc-ldml/src/compiler/keys.ts index 652b584d0ff..a09ebbf2b23 100644 --- a/developer/src/kmc-ldml/src/compiler/keys.ts +++ b/developer/src/kmc-ldml/src/compiler/keys.ts @@ -36,6 +36,15 @@ export class KeysCompiler extends SectionCompiler { public validate() { let valid = true; + // There's no 'form' compiler. + // We validate this here so that someone checks it. + this.keyboard3.forms?.form?.forEach((form) => { + if (!LDMLKeyboard.ImportStatus.isImpliedImport(form)) { + // If it's not an implied import, give a warning. + this.callbacks.reportMessage(CompilerMessages.Warn_CustomForm({ id: form.id })); + } + }); + // general key-level validation here, only of used keys const usedKeys = allUsedKeyIdsInLayers(this.keyboard3?.layers); const uniqueKeys = calculateUniqueKeys([...this.keyboard3.keys?.key]); @@ -121,18 +130,21 @@ export class KeysCompiler extends SectionCompiler { for (let lkflick of lkflicks.flick) { let flags = 0; - const to = sections.strs.allocAndUnescapeString(lkflick.to, true); + const to = sections.strs.allocString(lkflick.to, { + stringVariables: true, markers: true, unescape: true, singleOk: true + }, sections); if (!to.isOneChar) { flags |= constants.keys_flick_flags_extend; } let directions: ListItem = sections.list.allocListFromSpaces( - sections.strs, - lkflick.directions - ); + lkflick.directions, + { + stringVariables: true, markers: true, unescape: true + }, + sections); flicks.flicks.push({ directions, flags, - // TODO-LDML: markers,variables to, }); } @@ -159,25 +171,44 @@ export class KeysCompiler extends SectionCompiler { flags |= constants.keys_key_flags_notransform; } const id = sections.strs.allocString(key.id); - const longPress: ListItem = sections.list.allocListFromEscapedSpaces( - sections.strs, - // TODO-LDML: markers,variables - key.longPress - ); - const longPressDefault = sections.strs.allocAndUnescapeString( - // TODO-LDML: variables - sections.vars.substituteMarkerString(key.longPressDefault), - ); - const multiTap: ListItem = sections.list.allocListFromEscapedSpaces( - sections.strs, - // TODO-LDML: markers,variables - key.multiTap - ); + const longPress: ListItem = sections.list.allocListFromSpaces( + key.longPress, { + stringVariables: true, + markers: true, + unescape: true, + }, + sections); + + const longPressDefault = sections.strs.allocString(key.longPressDefault, + { + stringVariables: true, + markers: true, + unescape: true, + }, + sections); + + const multiTap: ListItem = sections.list.allocListFromSpaces( + key.multiTap, + { + stringVariables: true, + markers: true, + unescape: true, + }, + sections); const keySwitch = sections.strs.allocString(key.switch); // 'switch' is a reserved word + const toRaw = key.to; - // TODO-LDML: variables - let toCooked = sections.vars.substituteMarkerString(toRaw); - const to = sections.strs.allocAndUnescapeString(toCooked, true); + + let toCooked = sections.vars.substituteStrings(toRaw, sections); + toCooked = sections.vars.substituteMarkerString(toCooked); + const to = sections.strs.allocString(key.to, + { + stringVariables: true, + markers: true, + unescape: true, + singleOk: true + }, + sections); if (!to.isOneChar) { flags |= constants.keys_key_flags_extend; } @@ -196,6 +227,26 @@ export class KeysCompiler extends SectionCompiler { } } + private getKeymapFromForm(hardware : string, badScans?: Set) : Constants.KeyMap { + return KeysCompiler.getKeymapFromForms(this.keyboard3?.forms.form, hardware, badScans); + } + + public static getKeymapFromForms(forms: LDMLKeyboard.LKForm[], hardware: string, badScans?: Set): Constants.KeyMap { + // seach in reverse form because of overrides + const ldmlForm = [...forms].reverse().find((f) => f.id === hardware); + if (!ldmlForm) { + return undefined; + } + return KeysCompiler.getKeymapFromScancodes(ldmlForm, badScans); + } + + public static getKeymapFromScancodes(ldmlForm: LDMLKeyboard.LKForm, badScans?: Set) { + const { scanCodes } = ldmlForm; + const ldmlScan = scanCodes.map(o => o.codes.split(" ").map(n => Number.parseInt(n, 16))); + const ldmlVkey = Constants.CLDRScanToKeyMap(ldmlScan, badScans); + return ldmlVkey; + } + /** * TODO-LDML: from old 'keys' * Validate for purpose of kmap @@ -217,14 +268,21 @@ export class KeysCompiler extends SectionCompiler { valid = false; } - const keymap = Constants.HardwareToKeymap.get(hardware); - /* c8 ignore next 5 */ + const badScans = new Set(); + const keymap = this.getKeymapFromForm(hardware, badScans); if (!keymap) { - // not reached due to XML validation this.callbacks.reportMessage( CompilerMessages.Error_InvalidHardware({ form: hardware }) ); valid = false; + return valid; + } else if (badScans.size !== 0) { + const codes = Array.from(badScans.values()).map(n => Number(n).toString(16)).sort(); + this.callbacks.reportMessage( + CompilerMessages.Error_InvalidScanCode({ form: hardware, codes }) + ); + valid = false; + return valid; } const uniqueKeys = calculateUniqueKeys([...this.keyboard3.keys?.key]); @@ -287,7 +345,7 @@ export class KeysCompiler extends SectionCompiler { hardware: string ): Keys { const mod = translateLayerAttrToModifier(layer); - const keymap = Constants.HardwareToKeymap.get(hardware); + const keymap = this.getKeymapFromForm(hardware); let y = -1; for (let row of layer.row) { diff --git a/developer/src/kmc-ldml/src/compiler/layr.ts b/developer/src/kmc-ldml/src/compiler/layr.ts index 281ca8d5db9..0559c57f6df 100644 --- a/developer/src/kmc-ldml/src/compiler/layr.ts +++ b/developer/src/kmc-ldml/src/compiler/layr.ts @@ -35,11 +35,6 @@ export class LayrCompiler extends SectionCompiler { if (hardwareLayers > 1) { valid = false; this.callbacks.reportMessage(CompilerMessages.Error_ExcessHardware({form})); - } else if (!constants.layr_list_hardware_map.get(form)) { - /* c8 ignore next 4 */ - // not reached due to XML validation - valid = false; - this.callbacks.reportMessage(CompilerMessages.Error_InvalidHardware({form})); } } layers.layer.forEach((layer) => { @@ -63,7 +58,7 @@ export class LayrCompiler extends SectionCompiler { const sect = new Layr(); sect.lists = this.keyboard3.layers.map((layers) => { - const hardware = constants.layr_list_hardware_map.get(layers.form); + const hardware = sections.strs.allocString(layers.form); // Already validated in validate const list: LayrList = { hardware, diff --git a/developer/src/kmc-ldml/src/compiler/messages.ts b/developer/src/kmc-ldml/src/compiler/messages.ts index 804ec1d02dd..e22a9f62812 100644 --- a/developer/src/kmc-ldml/src/compiler/messages.ts +++ b/developer/src/kmc-ldml/src/compiler/messages.ts @@ -1,8 +1,8 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m } from "@keymanapp/common-types"; -const SevInfo = CompilerErrorSeverity.Info | CompilerErrorNamespace.LdmlKeyboardCompiler; +// const SevInfo = CompilerErrorSeverity.Info | CompilerErrorNamespace.LdmlKeyboardCompiler; const SevHint = CompilerErrorSeverity.Hint | CompilerErrorNamespace.LdmlKeyboardCompiler; -// const SevWarn = CompilerErrorSeverity.Warn | CompilerErrorNamespace.LdmlKeyboardCompiler; +const SevWarn = CompilerErrorSeverity.Warn | CompilerErrorNamespace.LdmlKeyboardCompiler; const SevError = CompilerErrorSeverity.Error | CompilerErrorNamespace.LdmlKeyboardCompiler; const SevFatal = CompilerErrorSeverity.Fatal | CompilerErrorNamespace.LdmlKeyboardCompiler; @@ -35,21 +35,16 @@ export class CompilerMessages { m(this.HINT_LocaleIsNotMinimalAndClean, `Locale '${o.sourceLocale}' is not minimal or correctly formatted and should be '${o.locale}'`); static HINT_LocaleIsNotMinimalAndClean = SevHint | 0x0008; - static Hint_VkeyIsNotValid = (o:{vkey: string}) => - m(this.HINT_VkeyIsNotValid, `Virtual key '${o.vkey}' is not found in the CLDR VKey Enum table.`); - static HINT_VkeyIsNotValid = SevHint | 0x0009; + static Error_InvalidScanCode = (o:{form?: string, codes?: string[]}) => + m(this.ERROR_InvalidScanCode, `Form '${o.form}' has invalid/unknown scancodes '${o.codes?.join(' ')}'`); + static ERROR_InvalidScanCode = SevError | 0x0009; - static Hint_VkeyIsRedundant = (o:{vkey: string}) => - m(this.HINT_VkeyIsRedundant, `Virtual key '${o.vkey}' is mapped to itself, which is redundant.`); - static HINT_VkeyIsRedundant = SevHint | 0x000A; + static Warn_CustomForm = (o:{id: string}) => + m(this.WARN_CustomForm, `Custom
element. Key layout may not be as expected.`); + static WARN_CustomForm = SevWarn | 0x000A; - static Error_VkeyIsRepeated = (o:{vkey: string}) => - m(this.ERROR_VkeyIsRepeated, `Virtual key '${o.vkey}' has more than one vkey entry.`); - static ERROR_VkeyIsRepeated = SevError | 0x000B; - - static Info_MultipleVkeysHaveSameTarget = (o:{vkey: string}) => - m(this.INFO_MultipleVkeysHaveSameTarget, `Target virtual key '${o.vkey}' has multiple source mappings, which may be an error.`); - static INFO_MultipleVkeysHaveSameTarget = SevInfo | 0x000C; + // 0x000B - available + // 0x000C - available static Error_InvalidVersion = (o:{version: string}) => m(this.ERROR_InvalidVersion, `Version number '${o.version}' must be a semantic version format string.`); @@ -148,6 +143,6 @@ export class CompilerMessages { static Error_DisplayNeedsToOrId = (o:{to?: string, id?: string}) => m(this.ERROR_DisplayNeedsToOrId, `display ${CompilerMessages.toOrId(o)} needs to= or id=, but not both`); static ERROR_DisplayNeedsToOrId = SevError | 0x0022; - } + diff --git a/developer/src/kmc-ldml/src/compiler/tran.ts b/developer/src/kmc-ldml/src/compiler/tran.ts index 9f39e302ef3..89a9d61f1cf 100644 --- a/developer/src/kmc-ldml/src/compiler/tran.ts +++ b/developer/src/kmc-ldml/src/compiler/tran.ts @@ -121,12 +121,11 @@ export class TransformCompiler m !== MarkerParser.ANY_MARKER_ID).sort(); - result.markers = sections.list.allocList(sections.strs, allMarkers); + result.markers = sections.list.allocList(allMarkers, {}, sections); return result.valid() ? result : null; } diff --git a/developer/src/kmc-ldml/src/compiler/visual-keyboard-compiler.ts b/developer/src/kmc-ldml/src/compiler/visual-keyboard-compiler.ts index c45326ff984..1ad0a4ea1c5 100644 --- a/developer/src/kmc-ldml/src/compiler/visual-keyboard-compiler.ts +++ b/developer/src/kmc-ldml/src/compiler/visual-keyboard-compiler.ts @@ -1,4 +1,5 @@ -import { Constants, VisualKeyboard, LDMLKeyboard } from "@keymanapp/common-types"; +import { VisualKeyboard, LDMLKeyboard } from "@keymanapp/common-types"; +import { KeysCompiler } from "./keys.js"; export class LdmlKeyboardVisualKeyboardCompiler { public compile(source: LDMLKeyboard.LDMLKeyboardXMLSourceFile): VisualKeyboard.VisualKeyboard { @@ -14,8 +15,9 @@ export class LdmlKeyboardVisualKeyboardCompiler { result.header.unicodeFont = {...VisualKeyboard.DEFAULT_KVK_FONT}; for(let layers of source.keyboard3.layers) { + const hardware = layers.form; for(let layer of layers.layer) { - this.compileHardwareLayer(source, result, layer); + this.compileHardwareLayer(source, result, layer, hardware); } } return result; @@ -24,9 +26,16 @@ export class LdmlKeyboardVisualKeyboardCompiler { private compileHardwareLayer( source: LDMLKeyboard.LDMLKeyboardXMLSourceFile, vk: VisualKeyboard.VisualKeyboard, - layer: LDMLKeyboard.LKLayer + layer: LDMLKeyboard.LKLayer, + hardware: string, ) { - // TODO-LDML: consider consolidation with keys.ts? + if (hardware === 'touch') { + hardware = 'us'; // TODO-LDML: US Only. Do something different here? + } + const keymap = KeysCompiler.getKeymapFromForms(source.keyboard3?.forms?.form, hardware); + if (!keymap) { + throw Error(`Internal error: could not find keymap for form ${hardware}`); + } const shift = this.translateLayerIdToVisualKeyboardShift(layer.id); let y = -1; @@ -48,7 +57,7 @@ export class LdmlKeyboardVisualKeyboardCompiler { flags: VisualKeyboard.VisualKeyboardKeyFlags.kvkkUnicode, shift: shift, text: keydef.to, // TODO-LDML: displays - vkey: Constants.USVirtualKeyMap[y][x] // TODO-LDML: #7965 US-only + vkey: keymap[y][x], }); } } diff --git a/developer/src/kmc-ldml/src/compiler/vkey.ts b/developer/src/kmc-ldml/src/compiler/vkey.ts deleted file mode 100644 index 1c4c69403be..00000000000 --- a/developer/src/kmc-ldml/src/compiler/vkey.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { KMXPlus, Constants } from '@keymanapp/common-types'; -import { CompilerMessages } from "./messages.js"; -import { SectionCompiler } from "./section-compiler.js"; - -import Vkey = KMXPlus.Vkey; -import LdmlVkeyNames = Constants.LdmlVkeyNames; - -export class VkeyCompiler extends SectionCompiler { - - public get id() { - return constants.section.vkey; - } - - public validate(): boolean { - let valid = true; - if(this.keyboard3.vkeys) { - let from: string[] = [], to: string[] = []; - - this.keyboard3.vkeys.vkey.forEach(vk => { - if(LdmlVkeyNames[vk.from] === undefined) { - // TODO-LDML: When we do #7135 this may need to change back to an error. - this.callbacks.reportMessage(CompilerMessages.Hint_VkeyIsNotValid({vkey: vk.from})); - return; - } - - if(LdmlVkeyNames[vk.to] === undefined) { - // TODO-LDML: When we do #7135 this may need to change back to an error. - this.callbacks.reportMessage(CompilerMessages.Hint_VkeyIsNotValid({vkey: vk.to})); - return; - } - - if(vk.from == vk.to) { - this.callbacks.reportMessage(CompilerMessages.Hint_VkeyIsRedundant({vkey: vk.from})); - } - - if(from.find(svk => svk == vk.from)) { - this.callbacks.reportMessage(CompilerMessages.Error_VkeyIsRepeated({vkey: vk.from})); - valid = false; - } - from.push(vk.from); - - if(to.find(svk => svk == vk.to)) { - this.callbacks.reportMessage(CompilerMessages.Info_MultipleVkeysHaveSameTarget({vkey: vk.to})); - } - to.push(vk.to); - }); - } - return valid; - } - - public compile(): Vkey { - let result = new Vkey(); - if(!this.keyboard3.vkeys) { - /* c8 ignore next 2 */ - return result; // not hit due to boxing - } - - result.vkeys = this.keyboard3.vkeys?.vkey.map(vk => { - return { - vkey: LdmlVkeyNames[vk.from], - target: LdmlVkeyNames[vk.to] - }; - }); - // Sort according to vkey binary order, per C7043 - result.vkeys.sort((a,b) => a.vkey - b.vkey); - return result; - } -} diff --git a/developer/src/kmc-ldml/test/fixtures/basic.txt b/developer/src/kmc-ldml/test/fixtures/basic.txt index 74280b3ecf8..0cd2fa77c17 100644 --- a/developer/src/kmc-ldml/test/fixtures/basic.txt +++ b/developer/src/kmc-ldml/test/fixtures/basic.txt @@ -137,9 +137,6 @@ block(sectitems) 76 61 72 73 diff(sect,vars) - 76 6b 65 79 - diff(sect,vkey) - block(endsect) # ---------------------------------------------------------------------------------------------------- @@ -305,7 +302,7 @@ block(layr) # struct COMP_KMXPLUS_LAYR { 01 00 00 00 # KMX_DWORD rowCount 02 00 00 00 # KMX_DWORD keyCount # list 0 - 04 00 00 00 # KMX_DWORD hardware = 'us' + index(strNull,strUs,2) # KMXPLUS_STR hardware = 'us' 00 00 00 00 # KMX_DWORD layer; 01 00 00 00 # count 7B 00 00 00 # KMX_DWORD minDeviceWidth; // 123 @@ -426,6 +423,7 @@ block(strs) # struct COMP_KMXPLUS_STRS { diff(strs,strAuthor) sizeof(strAuthor,2) diff(strs,strConformsTo) sizeof(strConformsTo,2) diff(strs,strThat) sizeof(strThat,2) + diff(strs,strUs) sizeof(strUs,2) diff(strs,strVse) sizeof(strVse,2) diff(strs,strVst) sizeof(strVst,2) diff(strs,strVus) sizeof(strVus,2) @@ -463,6 +461,7 @@ block(strs) # struct COMP_KMXPLUS_STRS { block(strConformsTo) 74 00 65 00 63 00 68 00 70 00 72 00 65 00 76 00 69 00 65 00 77 00 block(x) 00 00 # 'techpreview' block(strThat) 74 00 68 00 61 00 74 00 block(x) 00 00 # 'that' + block(strUs) 75 00 73 00 block(x) 00 00 # 'us' (layout) block(strVse) 76 00 73 00 65 00 block(x) 00 00 # 'vse' block(strVst) 76 00 73 00 74 00 block(x) 00 00 # 'vst' block(strVus) 76 00 75 00 73 00 block(x) 00 00 # 'vus' @@ -579,13 +578,4 @@ block(vars) # struct COMP_KMXPLUS_VARS { 00 00 00 00 # KMXPLUS_ELEM elem block(varsEnd) -block(vkey) # struct COMP_KMXPLUS_VKEY { - 76 6b 65 79 # KMX_DWORD header.ident; // 0000 Section name - vkey - sizeof(vkey) # KMX_DWORD header.size; // 0004 Section length - 02 00 00 00 # KMX_DWORD count; // 0008 Number of vkey maps - - 41 00 00 00 51 00 00 00 # KMX_DWORD vkey; KMX_DWORD target; // K_A => K_Q - 51 00 00 00 41 00 00 00 # KMX_DWORD vkey; KMX_DWORD target; // K_Q => K_A - # }; - block(eof) # end of file diff --git a/developer/src/kmc-ldml/test/fixtures/basic.xml b/developer/src/kmc-ldml/test/fixtures/basic.xml index 9c45f5e07fc..144bcb49247 100644 --- a/developer/src/kmc-ldml/test/fixtures/basic.xml +++ b/developer/src/kmc-ldml/test/fixtures/basic.xml @@ -14,11 +14,6 @@ - - - - - diff --git a/developer/src/kmc-ldml/test/fixtures/sections/disp/maximal.xml b/developer/src/kmc-ldml/test/fixtures/sections/disp/maximal.xml index 1a698edddbb..862c9310043 100644 --- a/developer/src/kmc-ldml/test/fixtures/sections/disp/maximal.xml +++ b/developer/src/kmc-ldml/test/fixtures/sections/disp/maximal.xml @@ -9,7 +9,11 @@ - + + + + + diff --git a/developer/src/kmc-ldml/test/fixtures/sections/keys/maximal.xml b/developer/src/kmc-ldml/test/fixtures/sections/keys/maximal.xml index 6c9b983fbd6..40f0be95ba1 100644 --- a/developer/src/kmc-ldml/test/fixtures/sections/keys/maximal.xml +++ b/developer/src/kmc-ldml/test/fixtures/sections/keys/maximal.xml @@ -12,13 +12,12 @@ - + - + - @@ -34,4 +33,9 @@ + + + + + diff --git a/developer/src/kmc-ldml/test/fixtures/sections/layr/error-custom-us-form.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/error-custom-us-form.xml new file mode 100644 index 00000000000..2c92c0d8608 --- /dev/null +++ b/developer/src/kmc-ldml/test/fixtures/sections/layr/error-custom-us-form.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/developer/src/kmc-ldml/test/fixtures/sections/layr/error-custom-zzz-form.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/error-custom-zzz-form.xml new file mode 100644 index 00000000000..0ab10f3d032 --- /dev/null +++ b/developer/src/kmc-ldml/test/fixtures/sections/layr/error-custom-zzz-form.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
diff --git a/developer/src/kmc-ldml/test/fixtures/sections/layr/warn-custom-us-form.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/warn-custom-us-form.xml new file mode 100644 index 00000000000..d3a0573b21d --- /dev/null +++ b/developer/src/kmc-ldml/test/fixtures/sections/layr/warn-custom-us-form.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
diff --git a/developer/src/kmc-ldml/test/fixtures/sections/layr/warn-custom-zzz-form.xml b/developer/src/kmc-ldml/test/fixtures/sections/layr/warn-custom-zzz-form.xml new file mode 100644 index 00000000000..502b254135b --- /dev/null +++ b/developer/src/kmc-ldml/test/fixtures/sections/layr/warn-custom-zzz-form.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + +
+ + +
+ + + + + +
diff --git a/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-from-vkey.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-from-vkey.xml deleted file mode 100644 index 9ed1d1b63f1..00000000000 --- a/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-from-vkey.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-repeated-vkey.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-repeated-vkey.xml deleted file mode 100644 index 36deceb4d86..00000000000 --- a/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-repeated-vkey.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-to-vkey.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-to-vkey.xml deleted file mode 100644 index 58b573adc24..00000000000 --- a/developer/src/kmc-ldml/test/fixtures/sections/vkey/invalid-to-vkey.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/developer/src/kmc-ldml/test/fixtures/sections/vkey/minimal.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/minimal.xml deleted file mode 100644 index 4af6dd30320..00000000000 --- a/developer/src/kmc-ldml/test/fixtures/sections/vkey/minimal.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/developer/src/kmc-ldml/test/fixtures/sections/vkey/redundant.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/redundant.xml deleted file mode 100644 index 612d12c0146..00000000000 --- a/developer/src/kmc-ldml/test/fixtures/sections/vkey/redundant.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/developer/src/kmc-ldml/test/fixtures/sections/vkey/same-target.xml b/developer/src/kmc-ldml/test/fixtures/sections/vkey/same-target.xml deleted file mode 100644 index 9dd34420eb3..00000000000 --- a/developer/src/kmc-ldml/test/fixtures/sections/vkey/same-target.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/developer/src/kmc-ldml/test/test-compiler-e2e.ts b/developer/src/kmc-ldml/test/test-compiler-e2e.ts index 41acd7c81d3..343f6cc7ac6 100644 --- a/developer/src/kmc-ldml/test/test-compiler-e2e.ts +++ b/developer/src/kmc-ldml/test/test-compiler-e2e.ts @@ -1,12 +1,10 @@ import 'mocha'; import {assert} from 'chai'; -import x_hextobin from '@keymanapp/hextobin'; +import hextobin from '@keymanapp/hextobin'; import { KMXBuilder } from '@keymanapp/common-types'; import {checkMessages, compileKeyboard, compilerTestCallbacks, compilerTestOptions, makePathToFixture} from './helpers/index.js'; import { LdmlKeyboardCompiler } from '../src/compiler/compiler.js'; -const hextobin = (x_hextobin as any).default; - describe('compiler-tests', function() { this.slow(500); // 0.5 sec -- json schema validation takes a while diff --git a/developer/src/kmc-ldml/test/test-keys.ts b/developer/src/kmc-ldml/test/test-keys.ts index 9429b339791..5a085a0d0fb 100644 --- a/developer/src/kmc-ldml/test/test-keys.ts +++ b/developer/src/kmc-ldml/test/test-keys.ts @@ -38,6 +38,10 @@ describe('keys', function () { assert.equal(compilerTestCallbacks.messages.length, 0); assert.equal(keys.keys.length, 4); + const [w] = keys.keys.filter(({ id }) => id.value === 'w'); + assert.ok(w); + assert.equal(w.to.value, 'w', 'substituted key value'); + const [q] = keys.keys.filter(({ id }) => id.value === 'q'); assert.ok(q); assert.isFalse(!!(q.flags & constants.keys_key_flags_gap)); @@ -57,7 +61,7 @@ describe('keys', function () { const [flick0_ne_sw] = flick0.flicks.filter(({ directions }) => directions && directions.isEqual('ne sw'.split(' '))); assert.ok(flick0_ne_sw); - assert.equal(flick0_ne_sw.to?.value, 'ê'); + assert.equal(flick0_ne_sw.to?.value, 'ê'); // via variable }, }, { @@ -129,11 +133,11 @@ describe('keys', function () { const MARKER_1 = MarkerParser.markerOutput(1); assert.equal(ww.to.value, MARKER_1); assert.equal(ww.longPressDefault.value, MARKER_1); - // TODO-LDML: assert.equal(ww.longPress[0].value.value, MARKER_1); - // TODO-LDML: assert.equal(ww.multiTap[0].value.value, MARKER_1); + assert.equal(ww.longPress[0].value.value, MARKER_1); + assert.equal(ww.multiTap[0].value.value, MARKER_1); const [flickw] = keys.flicks?.filter(({id}) => id.value === 'flickw'); assert.ok(flickw); - // TODO-LDML: assert.equal(flickw.flicks[0].to.value, MARKER_1); + assert.equal(flickw.flicks[0].to.value, MARKER_1); }, }, ]); @@ -241,6 +245,90 @@ describe('keys.kmap', function () { CompilerMessages.Error_MissingFlicks({flicks:'an-undefined-flick-id',id:'Q'}), ] }, + { + subpath: 'sections/layr/invalid-invalid-form.xml', + errors: [CompilerMessages.Error_InvalidHardware({ + form: 'holographic', + }),], + }, + { + // warning on custom form + subpath: 'sections/layr/warn-custom-us-form.xml', + warnings: [ + CompilerMessages.Warn_CustomForm({id: "us"}), + ], + callback: (sect, subpath, callbacks) => { + const keys = sect as Keys; + assert.isNotNull(keys); + assert.equal(compilerTestCallbacks.messages.length, 0); + assert.equal(keys.keys.length, 3); + assert.sameDeepMembers(keys.kmap, [ + { + vkey: K.K_K, + key: 'one', + mod: constants.keys_mod_none, + }, + { + vkey: K.K_E, + key: 'two', + mod: constants.keys_mod_none, + }, + { + vkey: K.K_Y, + key: 'three', + mod: constants.keys_mod_none, + }, + ]); + }, + }, + { + // warning on a custom unknown form - but no error! + subpath: 'sections/layr/warn-custom-zzz-form.xml', + warnings: [ + CompilerMessages.Warn_CustomForm({id: "zzz"}), + ], + callback: (sect, subpath, callbacks) => { + const keys = sect as Keys; + assert.isNotNull(keys); + assert.equal(compilerTestCallbacks.messages.length, 0); + assert.equal(keys.keys.length, 3); + assert.sameDeepMembers(keys.kmap, [ + { + vkey: K.K_K, + key: 'one', + mod: constants.keys_mod_none, + }, + { + vkey: K.K_E, + key: 'two', + mod: constants.keys_mod_none, + }, + { + vkey: K.K_Y, + key: 'three', + mod: constants.keys_mod_none, + }, + ]); + }, + }, + { + subpath: 'sections/layr/error-custom-us-form.xml', + warnings: [ + CompilerMessages.Warn_CustomForm({id: "us"}), + ], + errors: [ + CompilerMessages.Error_InvalidScanCode({ form: "us", codes: ['ff'] }), + ], + }, + { + subpath: 'sections/layr/error-custom-zzz-form.xml', + warnings: [ + CompilerMessages.Warn_CustomForm({id: "zzz"}), + ], + errors: [ + CompilerMessages.Error_InvalidScanCode({ form: "zzz", codes: ['ff'] }), + ], + }, ]); it('should reject layouts with too many hardware rows', async function() { diff --git a/developer/src/kmc-ldml/test/test-layr.ts b/developer/src/kmc-ldml/test/test-layr.ts index 86c7e2729bd..126935c33b5 100644 --- a/developer/src/kmc-ldml/test/test-layr.ts +++ b/developer/src/kmc-ldml/test/test-layr.ts @@ -2,7 +2,7 @@ import 'mocha'; import { assert } from 'chai'; import { LayrCompiler } from '../src/compiler/layr.js'; import { CompilerMessages } from '../src/compiler/messages.js'; -import { compilerTestCallbacks, loadSectionFixture, testCompilationCases } from './helpers/index.js'; +import { compilerTestCallbacks, testCompilationCases } from './helpers/index.js'; import { KMXPlus } from '@keymanapp/common-types'; import { constants } from '@keymanapp/ldml-keyboard-constants'; @@ -20,37 +20,38 @@ function allKeysOk(row : LayrRow, str : string, msg? : string) { describe('layr', function () { this.slow(500); // 0.5 sec -- json schema validation takes a while - // reuse the keys minimal file - it('should compile minimal keys data', async function () { - let layr = await loadSectionFixture(LayrCompiler, 'sections/keys/minimal.xml', compilerTestCallbacks) as Layr; - assert.ok(layr); - assert.equal(compilerTestCallbacks.messages.length, 0); - - assert.equal(layr.lists?.length, 1); - const list0 = layr.lists[0]; - assert.ok(list0); - assert.equal(list0.layers.length, 1); - assert.equal(list0.hardware, constants.layr_list_hardware_us); - const layer0 = list0.layers[0]; - assert.ok(layer0); - assert.equal(layer0.rows.length, 1); - const row0 = layer0.rows[0]; - assert.ok(row0); - assert.equal(row0.keys.length, 2); + testCompilationCases(LayrCompiler, [ + { + subpath: 'sections/keys/minimal.xml', + callback(sect) { + const layr = sect; + assert.ok(layr); + assert.equal(compilerTestCallbacks.messages.length, 0); - assert.equal(layer0.id.value, 'base'); - assert.equal(layer0.mod, constants.keys_mod_none); - assert.equal(row0.keys[0]?.value, 'grave'); - }); + assert.equal(layr.lists?.length, 1); + const list0 = layr.lists[0]; + assert.ok(list0); + assert.equal(list0.layers.length, 1); + assert.equal(list0.hardware.value, 'us'); + const layer0 = list0.layers[0]; + assert.ok(layer0); + assert.equal(layer0.rows.length, 1); + const row0 = layer0.rows[0]; + assert.ok(row0); + assert.equal(row0.keys.length, 2); - testCompilationCases(LayrCompiler, [ + assert.equal(layer0.id.value, 'base'); + assert.equal(layer0.mod, constants.keys_mod_none); + assert.equal(row0.keys[0]?.value, 'grave'); + }, + }, { subpath: 'sections/keys/maximal.xml', callback(sect) { const layr = sect; assert.equal(layr.lists?.length, 2); - const listHardware = layr.lists.find(v => v.hardware === constants.layr_list_hardware_iso); + const listHardware = layr.lists.find(v => v.hardware.value === 'iso'); assert.ok(listHardware); assert.equal(listHardware.minDeviceWidth, 0); assert.equal(listHardware.layers.length, 2); @@ -72,7 +73,7 @@ describe('layr', function () { assert.equal(hardware1row0.keys.length, 2); allKeysOk(hardware1row0,'q w', 'hardware1row0'); - const listTouch = layr.lists.find(v => v.hardware === constants.layr_list_hardware_touch); + const listTouch = layr.lists.find(v => v.hardware.value === constants.layr_list_hardware_touch); assert.ok(listTouch); assert.equal(listTouch.minDeviceWidth, 300); assert.equal(listTouch.layers.length, 1); @@ -104,23 +105,10 @@ describe('layr', function () { subpath: 'sections/layr/invalid-multi-hardware.xml', errors: [CompilerMessages.Error_ExcessHardware({ form: 'iso' })], }, - { - subpath: 'sections/layr/invalid-invalid-form.xml', - errors: [CompilerMessages.Error_InvalidHardware({ - form: 'holographic', - }),], - }, { // missing layer element subpath: 'sections/layr/invalid-missing-layer.xml', errors: [CompilerMessages.Error_MustBeAtLeastOneLayerElement()], }, - { - // missing layers element completely - subpath: 'sections/layr/invalid-missing-layer.xml', - errors: [ - CompilerMessages.Error_MustBeAtLeastOneLayerElement(), - ], - } ]); }); diff --git a/developer/src/kmc-ldml/test/test-vars.ts b/developer/src/kmc-ldml/test/test-vars.ts index 5bdd5e65f01..4b053c30d23 100644 --- a/developer/src/kmc-ldml/test/test-vars.ts +++ b/developer/src/kmc-ldml/test/test-vars.ts @@ -203,8 +203,9 @@ describe('vars', function () { callback(sect) { const vars = sect; assert.ok(vars.markers); - assert.sameDeepOrderedMembers(vars.markers.toStringArray(), - ['m','x']); + // assert.sameDeepOrderedMembers(vars.markers.toStringArray(), + // ['m','x']); + assert.equal(vars.markers.toString(), 'm x'); }, }, { diff --git a/developer/src/kmc-ldml/test/test-virtual-key-constants.ts b/developer/src/kmc-ldml/test/test-virtual-key-constants.ts deleted file mode 100644 index 3ad5d3662cc..00000000000 --- a/developer/src/kmc-ldml/test/test-virtual-key-constants.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { expect } from 'chai'; -import { Constants } from '@keymanapp/common-types'; - -import LdmlVkeyNames = Constants.LdmlVkeyNames; - -describe('virtual key constants', function () { - - it('should map the CLDR VKey Enum values to the right constants', function () { - - // These are copied-and-pasted from the table in TR35 - // We want to check that our constants match the ones in that table! - const CLDRVKeyEnumValues: Record = { - 'SPACE': 0x20, - '0': 0x30, - '1': 0x31, - '2': 0x32, - '3': 0x33, - '4': 0x34, - '5': 0x35, - '6': 0x36, - '7': 0x37, - '8': 0x38, - '9': 0x39, - 'A': 0x41, - 'B': 0x42, - 'C': 0x43, - 'D': 0x44, - 'E': 0x45, - 'F': 0x46, - 'G': 0x47, - 'H': 0x48, - 'I': 0x49, - 'J': 0x4A, - 'K': 0x4B, - 'L': 0x4C, - 'M': 0x4D, - 'N': 0x4E, - 'O': 0x4F, - 'P': 0x50, - 'Q': 0x51, - 'R': 0x52, - 'S': 0x53, - 'T': 0x54, - 'U': 0x55, - 'V': 0x56, - 'W': 0x57, - 'X': 0x58, - 'Y': 0x59, - 'Z': 0x5A, - 'SEMICOLON': 0xBA, - 'EQUAL': 0xBB, - 'COMMA': 0xBC, - 'HYPHEN': 0xBD, - 'PERIOD': 0xBE, - 'SLASH': 0xBF, - 'GRAVE': 0xC0, - 'LBRACKET': 0xDB, - 'BACKSLASH': 0xDC, - 'RBRACKET': 0xDD, - 'QUOTE': 0xDE, - 'LESS-THAN': 0xE2, - 'ABNT2': 0xC1 - }; - - const keys = Object.keys(CLDRVKeyEnumValues); - for (let key of keys) { - expect(CLDRVKeyEnumValues[key]).to.be.equal(LdmlVkeyNames[key]); - } - }); -}); diff --git a/developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts b/developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts index 87d4d914c9e..133f4286c85 100644 --- a/developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts +++ b/developer/src/kmc-ldml/test/test-visual-keyboard-compiler-e2e.ts @@ -1,11 +1,9 @@ import 'mocha'; import {assert} from 'chai'; -import x_hextobin from '@keymanapp/hextobin'; +import hextobin from '@keymanapp/hextobin'; import { KvkFileWriter } from '@keymanapp/common-types'; import {checkMessages, compilerTestOptions, compileVisualKeyboard, makePathToFixture} from './helpers/index.js'; -const hextobin = (x_hextobin as any).default; - describe('visual-keyboard-compiler', function() { this.slow(500); // 0.5 sec -- json schema validation takes a while diff --git a/developer/src/kmc-ldml/test/test-vkey.ts b/developer/src/kmc-ldml/test/test-vkey.ts deleted file mode 100644 index dec24125e2a..00000000000 --- a/developer/src/kmc-ldml/test/test-vkey.ts +++ /dev/null @@ -1,62 +0,0 @@ -import 'mocha'; -import { assert } from 'chai'; -import { VkeyCompiler } from '../src/compiler/vkey.js'; -import { compilerTestCallbacks, loadSectionFixture } from './helpers/index.js'; -import { KMXPlus, Constants } from '@keymanapp/common-types'; -import { CompilerMessages } from '../src/compiler/messages.js'; - -import Vkey = KMXPlus.Vkey; -import USVirtualKeyCodes = Constants.USVirtualKeyCodes; - -describe('vkey compiler', function () { - this.slow(500); // 0.5 sec -- json schema validation takes a while - - it('should compile minimal vkey data', async function() { - let vkey = await loadSectionFixture(VkeyCompiler, 'sections/vkey/minimal.xml', compilerTestCallbacks) as Vkey; - assert.equal(compilerTestCallbacks.messages.length, 0); - - assert.equal(vkey.vkeys.length, 4); - // Note, final order is sorted by `vkey` member - assert.deepEqual(vkey.vkeys[0], {vkey: USVirtualKeyCodes.K_A, target: USVirtualKeyCodes.K_Q}); - assert.deepEqual(vkey.vkeys[1], {vkey: USVirtualKeyCodes.K_Q, target: USVirtualKeyCodes.K_A}); - assert.deepEqual(vkey.vkeys[2], {vkey: USVirtualKeyCodes.K_W, target: USVirtualKeyCodes.K_Z}); - assert.deepEqual(vkey.vkeys[3], {vkey: USVirtualKeyCodes.K_Z, target: USVirtualKeyCodes.K_W}); - }); - - it('should hint on redundant data', async function() { - let vkey = await loadSectionFixture(VkeyCompiler, 'sections/vkey/redundant.xml', compilerTestCallbacks) as Vkey; - assert.isNotNull(vkey); - assert.equal(compilerTestCallbacks.messages.length, 1); - assert.deepEqual(compilerTestCallbacks.messages[0], CompilerMessages.Hint_VkeyIsRedundant({vkey: 'A'})); - }); - - it('should report an info message if same target found', async function() { - let vkey = await loadSectionFixture(VkeyCompiler, 'sections/vkey/same-target.xml', compilerTestCallbacks) as Vkey; - assert.isNotNull(vkey); - assert.equal(compilerTestCallbacks.messages.length, 1); - assert.deepEqual(compilerTestCallbacks.messages[0], CompilerMessages.Info_MultipleVkeysHaveSameTarget({vkey: 'Q'})); - }); - - it('should hint on invalid "from" vkey', async function() { - let vkey = await loadSectionFixture(VkeyCompiler, 'sections/vkey/invalid-from-vkey.xml', compilerTestCallbacks) as Vkey; - assert.isNotNull(vkey); - assert.equal(compilerTestCallbacks.messages.length, 2); - assert.deepEqual(compilerTestCallbacks.messages[0], CompilerMessages.Hint_VkeyIsNotValid({vkey: 'q'})); - assert.deepEqual(compilerTestCallbacks.messages[1], CompilerMessages.Hint_VkeyIsNotValid({vkey: 'HYFEN'})); - }); - - it('should hint on invalid "to" vkey', async function() { - let vkey = await loadSectionFixture(VkeyCompiler, 'sections/vkey/invalid-to-vkey.xml', compilerTestCallbacks) as Vkey; - assert.isNotNull(vkey); - assert.equal(compilerTestCallbacks.messages.length, 1); - assert.deepEqual(compilerTestCallbacks.messages[0], CompilerMessages.Hint_VkeyIsNotValid({vkey: 'A-ACUTE'})); - }); - - it('should error on repeated vkeys', async function() { - let vkey = await loadSectionFixture(VkeyCompiler, 'sections/vkey/invalid-repeated-vkey.xml', compilerTestCallbacks) as Vkey; - assert.isNull(vkey); - assert.equal(compilerTestCallbacks.messages.length, 1); - assert.deepEqual(compilerTestCallbacks.messages[0], CompilerMessages.Error_VkeyIsRepeated({vkey: 'A'})); - }); -}); - diff --git a/developer/src/kmc-ldml/tsconfig.json b/developer/src/kmc-ldml/tsconfig.json index c9ec649b40f..09c2df59b7e 100644 --- a/developer/src/kmc-ldml/tsconfig.json +++ b/developer/src/kmc-ldml/tsconfig.json @@ -1,11 +1,10 @@ { - "extends": "../../../tsconfig.esm-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "build/src/", "rootDir": "src/", "baseUrl": ".", - "allowSyntheticDefaultImports": true, // for ajv "paths": { // "@keymanapp/keyman-version": ["../../../common/web/keyman-version/keyman-version.mts"], diff --git a/developer/src/kmc-model-info/build.sh b/developer/src/kmc-model-info/build.sh index 994c6590196..68dfc78fc29 100755 --- a/developer/src/kmc-model-info/build.sh +++ b/developer/src/kmc-model-info/build.sh @@ -52,7 +52,8 @@ fi if builder_start_action test; then eslint . tsc --build test - c8 --reporter=lcov --reporter=text mocha + c8 --reporter=lcov --reporter=text --exclude-after-remap --lines 80 mocha + # TODO: remove --lines 80 and improve coverage builder_finish_action success test fi diff --git a/developer/src/kmc-model-info/package.json b/developer/src/kmc-model-info/package.json index 624be5ff807..a953062acb3 100644 --- a/developer/src/kmc-model-info/package.json +++ b/developer/src/kmc-model-info/package.json @@ -32,13 +32,16 @@ }, "dependencies": { "@keymanapp/common-types": "*", - "@keymanapp/models-types": "*" + "@keymanapp/models-types": "*", + "@keymanapp/developer-utils": "*" }, + "bundleDependencies": [ + "@keymanapp/developer-utils" + ], "devDependencies": { "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", - "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", "chalk": "^2.4.2", diff --git a/developer/src/kmc-model-info/src/messages.ts b/developer/src/kmc-model-info/src/messages.ts index a4d61e8b232..03dce78613a 100644 --- a/developer/src/kmc-model-info/src/messages.ts +++ b/developer/src/kmc-model-info/src/messages.ts @@ -26,5 +26,20 @@ export class ModelInfoCompilerMessages { `Invalid author email: ${o.email}`); static ERROR_InvalidAuthorEmail = SevError | 0x0005; + static Error_LicenseFileDoesNotExist = (o:{filename:string}) => m(this.ERROR_LicenseFileIsMissing, + `License file ${o.filename} does not exist.`); + static ERROR_LicenseFileIsMissing = SevError | 0x0006; + + static Error_LicenseFileIsDamaged = (o:{filename:string}) => m(this.ERROR_LicenseFileIsDamaged, + `License file ${o.filename} could not be loaded or decoded.`); + static ERROR_LicenseFileIsDamaged = SevError | 0x0007; + + static Error_LicenseIsNotValid = (o:{filename:string,message:string}) => m(this.ERROR_LicenseIsNotValid, + `An error was encountered parsing license file ${o.filename}: ${o.message}.`); + static ERROR_LicenseIsNotValid = SevError | 0x0008; + + static Error_NoLicenseFound = () => m(this.ERROR_NoLicenseFound, + `No license for the model was found. MIT license is required for publication to Keyman lexical-models repository.`); + static ERROR_NoLicenseFound = SevError | 0x0009; } 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 32730dd034f..8a7101e86fe 100644 --- a/developer/src/kmc-model-info/src/model-info-compiler.ts +++ b/developer/src/kmc-model-info/src/model-info-compiler.ts @@ -1,5 +1,5 @@ /** - * Merges a source .model_info file with metadata extracted from .kps file and + * Builds a source .model_info file with metadata extracted from .kps file and * compiled files to produce a comprehensive .model_info file. */ @@ -7,157 +7,206 @@ import { minKeymanVersion } from "./min-keyman-version.js"; import { ModelInfoFile } from "./model-info-file.js"; import { CompilerCallbacks, KmpJsonFile } from "@keymanapp/common-types"; import { ModelInfoCompilerMessages } from "./messages.js"; +import { validateMITLicense } from "@keymanapp/developer-utils"; + +const HelpRoot = 'https://help.keyman.com/model/'; /* c8 ignore start */ -export class ModelInfoOptions { +export class ModelInfoSources { /** The identifier for the model */ model_id: string; /** The data from the .kps file, transformed to kmp.json */ kmpJsonData: KmpJsonFile.KmpJsonFile; - /** The path in the keymanapp/lexical-models repo where this model may be found (optional) */ - sourcePath?: string; + /** The path in the keymanapp/lexical-models repo where this model may be found */ + sourcePath: string; /** The compiled model filename and relative path (.js) */ modelFileName: string; /** The compiled package filename and relative path (.kmp) */ kmpFileName: string; + + /** The source package filename and relative path (.kps) */ + kpsFilename: string; + + /** Last modification date for files in the project folder 'YYYY-MM-DDThh:mm:ssZ' */ + lastCommitDate?: string; + + /** Return an error if project does not meet requirements of lexical-models repository */ + forPublishing: boolean; }; /* c8 ignore stop */ -/** - * Merges source .model_info file with metadata from the model and package source file. - * This function is intended for use within the lexical-models repository. While many of the - * parameters could be deduced from each other, they are specified here to reduce the - * number of places the filenames are constructed. - * - * @param sourceModelInfoFileName Path for the source .model_info file - * @param options Details on files from which to extract additional metadata - */ -export function writeMergedModelMetadataFile( - sourceModelInfoFileName: string, - callbacks: CompilerCallbacks, - options: ModelInfoOptions - ): Uint8Array { - - /* - * Model info looks like this: - * - * { - * "name": "Example Template Model" - * "license": "mit", - * "version": "1.0.0", - * "languages": ["en"], - * "authorName": "Example Author", - * "authorEmail": "nobody@example.com", - * "description": "Example wordlist model" - * } - * - * For full documentation, see: - * https://help.keyman.com/developer/cloud/model_info/1.0/ - */ - const dataInput = callbacks.loadFile(sourceModelInfoFileName); - if(!dataInput) { - callbacks.reportMessage(ModelInfoCompilerMessages.Error_FileDoesNotExist({filename: sourceModelInfoFileName})); - return null; +export class ModelInfoCompiler { + constructor(private callbacks: CompilerCallbacks) { } - let model_info: ModelInfoFile = null; - try { - const jsonInput = new TextDecoder('utf-8', {fatal: true}).decode(dataInput); - model_info = JSON.parse(jsonInput); - } catch(e) { - callbacks.reportMessage(ModelInfoCompilerMessages.Error_FileIsNotValid({filename: sourceModelInfoFileName, e})); - return null; - } + /** + * Builds .model_info file with metadata from the model and package source file. + * This function is intended for use within the lexical-models repository. While many of the + * parameters could be deduced from each other, they are specified here to reduce the + * number of places the filenames are constructed. + * + * @param sources Details on files from which to extract additional metadata + */ + writeModelMetadataFile( + sources: ModelInfoSources + ): Uint8Array { - // - // Build merged .model_info file - // https://api.keyman.com/schemas/model_info.source.json and - // https://api.keyman.com/schemas/model_info.distribution.json - // https://help.keyman.com/developer/cloud/model_info/1.0 - // - - function setModelMetadata(field: keyof ModelInfoFile, expected: unknown, warn: boolean = true) { - /* c8 ignore next 4 */ - if (model_info[field] && model_info[field] !== expected) { - if (warn ?? true) { - callbacks.reportMessage(ModelInfoCompilerMessages.Warn_MetadataFieldInconsistent({ - field, value:model_info[field], expected - })); + /* + * Model info looks like this: + * + * { + * "name": "Example Template Model" + * "license": "mit", + * "version": "1.0.0", + * "languages": ["en"], + * "authorName": "Example Author", + * "authorEmail": "nobody@example.com", + * "description": "Example wordlist model" + * } + * + * For full documentation, see: + * https://help.keyman.com/developer/cloud/model_info/1.0/ + */ + + let jsFile: string = null; + + if(sources.modelFileName) { + jsFile = this.loadJsFile(sources.modelFileName); + if(!jsFile) { + return null; } - } - // TypeScript gets upset with this assignment, because it cannot deduce - // the exact type of model_info[field] -- there are many possibilities! - // So we assert that it's unknown so that TypeScript can chill. - ( model_info[field]) = model_info[field] || expected; - } - // - // Merge model info file -- some fields have "special" behaviours -- see below - // - setModelMetadata('id', options.model_id); + let model_info: ModelInfoFile = { + languages: [], + }; + + // + // Build .model_info file -- some fields have "special" behaviours -- see below + // https://api.keyman.com/schemas/model_info.source.json and + // https://api.keyman.com/schemas/model_info.distribution.json + // https://help.keyman.com/developer/cloud/model_info/1.0 + // - setModelMetadata('name', options.kmpJsonData.info.name.description); + model_info.id = sources.model_id; + model_info.name = sources.kmpJsonData.info.name.description; - let author = options.kmpJsonData.info.author; - setModelMetadata('authorName', author?.description); + // License - if (author?.url) { - // we strip the mailto: from the .kps file for the .model_info - let match = author.url.match(/^(mailto\:)?(.+)$/); - /* c8 ignore next 3 */ - if (match === null) { - callbacks.reportMessage(ModelInfoCompilerMessages.Error_InvalidAuthorEmail({email:author.url})); + if(!sources.kmpJsonData.options?.licenseFile) { + this.callbacks.reportMessage(ModelInfoCompilerMessages.Error_NoLicenseFound()); return null; } - let email = match[2]; - setModelMetadata('authorEmail', email, false); - } + if(!this.isLicenseMIT(this.callbacks.resolveFilename(sources.kpsFilename, sources.kmpJsonData.options.licenseFile))) { + return null; + } - // extract the language identifiers from the language metadata - // arrays for each of the lexical models in the kmp.json file, - // and merge into a single array of identifiers in the - // .model_info file. + model_info.license = 'mit'; - model_info.languages = model_info.languages || options.kmpJsonData.lexicalModels.reduce((a, e) => [].concat(a, e.languages.map((f) => f.id)), []); + const author = sources.kmpJsonData.info.author; + model_info.authorName = author?.description ?? ''; - setModelMetadata('lastModifiedDate', (new Date).toISOString()); - setModelMetadata('packageFilename', callbacks.path.basename(options.kmpFileName)); + if (author?.url) { + // we strip the mailto: from the .kps file for the .model_info + const match = author.url.match(/^(mailto\:)?(.+)$/); + /* c8 ignore next 3 */ + if (match === null) { + this.callbacks.reportMessage(ModelInfoCompilerMessages.Error_InvalidAuthorEmail({email:author.url})); + return null; + } - // Always overwrite with actual file size - model_info.packageFileSize = callbacks.fileSize(options.kmpFileName); - if(model_info.packageFileSize === undefined) { - callbacks.reportMessage(ModelInfoCompilerMessages.Error_FileDoesNotExist({filename:options.kmpFileName})); - return null; - } + model_info.authorEmail = match[2]; + } - setModelMetadata('jsFilename', callbacks.path.basename(options.modelFileName)); + // description - // Always overwrite with actual file size - model_info.jsFileSize = callbacks.fileSize(options.modelFileName); - if(model_info.jsFileSize === undefined) { - callbacks.reportMessage(ModelInfoCompilerMessages.Error_FileDoesNotExist({filename:options.modelFileName})); - return null; - } + if(sources.kmpJsonData.info.description?.description) { + model_info.description = sources.kmpJsonData.info.description.description.trim(); + } - // Always overwrite source data - model_info.packageIncludes = options.kmpJsonData.files.filter((e) => !!e.name.match(/.[ot]tf$/i)).length ? ['fonts'] : []; + // isRTL -- this is a little bit of a heuristic from a compiled .js + // which may need modification if compilers change - setModelMetadata('version', options.kmpJsonData.info.version.description); + if(jsFile?.match(/("?)isRTL("?):\s*true/)) { + model_info.isRTL = true; + } - // The minimum Keyman version detected in the package file may be manually set higher by the developer - setModelMetadata('minKeymanVersion', minKeymanVersion, false); + // extract the language identifiers from the language metadata + // arrays for each of the lexical models in the kmp.json file, + // and merge into a single array of identifiers in the + // .model_info file. - if(options.sourcePath) { - setModelMetadata('sourcePath', options.sourcePath); + model_info.languages = sources.kmpJsonData.lexicalModels.reduce((a, e) => [].concat(a, e.languages.map((f) => f.id)), []); + + // If a last commit date is not given, then just use the current time + model_info.lastModifiedDate = sources.lastCommitDate ?? (new Date).toISOString(); + + model_info.packageFilename = this.callbacks.path.basename(sources.kmpFileName); + model_info.packageFileSize = this.callbacks.fileSize(sources.kmpFileName); + if(model_info.packageFileSize === undefined) { + this.callbacks.reportMessage(ModelInfoCompilerMessages.Error_FileDoesNotExist({filename:sources.kmpFileName})); + return null; + } + + model_info.jsFilename = this.callbacks.path.basename(sources.modelFileName); + model_info.jsFileSize = this.callbacks.fileSize(sources.modelFileName); + if(model_info.jsFileSize === undefined) { + this.callbacks.reportMessage(ModelInfoCompilerMessages.Error_FileDoesNotExist({filename:sources.modelFileName})); + return null; + } + + model_info.packageIncludes = sources.kmpJsonData.files.filter((e) => !!e.name.match(/.[ot]tf$/i)).length ? ['fonts'] : []; + model_info.version = sources.kmpJsonData.info.version.description; + model_info.minKeymanVersion = minKeymanVersion; + model_info.helpLink = HelpRoot + model_info.id; + + if(sources.sourcePath) { + model_info.sourcePath = sources.sourcePath; + } + + const jsonOutput = JSON.stringify(model_info, null, 2); + return new TextEncoder().encode(jsonOutput); } - const jsonOutput = JSON.stringify(model_info, null, 2); - return new TextEncoder().encode(jsonOutput); -} + private isLicenseMIT(filename: string) { + const data = this.callbacks.loadFile(filename); + if(!data) { + this.callbacks.reportMessage(ModelInfoCompilerMessages.Error_LicenseFileDoesNotExist({filename})); + return false; + } + + let license = null; + try { + license = new TextDecoder().decode(data); + } catch(e) { + this.callbacks.reportMessage(ModelInfoCompilerMessages.Error_LicenseFileIsDamaged({filename})); + return false; + } + if(!license) { + this.callbacks.reportMessage(ModelInfoCompilerMessages.Error_LicenseFileIsDamaged({filename})); + return false; + } + const message = validateMITLicense(license); + if(message != null) { + this.callbacks.reportMessage(ModelInfoCompilerMessages.Error_LicenseIsNotValid({filename, message})); + return false; + } + return true; + } + + private loadJsFile(filename: string) { + const data = this.callbacks.loadFile(filename); + if(!data) { + this.callbacks.reportMessage(ModelInfoCompilerMessages.Error_FileDoesNotExist({filename})); + return null; + } + const text = new TextDecoder('utf-8', {fatal: true}).decode(data); + return text; + } +} \ No newline at end of file diff --git a/developer/src/kmc-model-info/src/model-info-file.ts b/developer/src/kmc-model-info/src/model-info-file.ts index a70cfba20a0..c038c67ca95 100644 --- a/developer/src/kmc-model-info/src/model-info-file.ts +++ b/developer/src/kmc-model-info/src/model-info-file.ts @@ -4,10 +4,9 @@ export interface ModelInfoFile { authorName?: string; authorEmail?: string; description?: string; - license: "mit"; + license?: "mit"; languages: Array; lastModifiedDate?: string; - links: ModelInfoFileLink[]; packageFilename?: string; packageFileSize?: number; jsFilename?: string; @@ -21,13 +20,7 @@ export interface ModelInfoFile { related?: {[id:string]:ModelInfoFileRelated}; } -export interface ModelInfoFileLink { - name: string; - url: string; -} - export interface ModelInfoFileRelated { deprecates?: string; deprecatedBy?: string; - note?: string; } \ No newline at end of file diff --git a/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/LICENSE.md b/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/LICENSE.md index fefb033d6c1..3a58ba2ae32 100644 --- a/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/LICENSE.md +++ b/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -© 2021 - 2022 SIL International +Copyright © 2021 - 2022 SIL International Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/build/sil.cmo.bw.model_info b/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/build/sil.cmo.bw.model_info index a9f7bbe1548..1f8d5c03548 100644 --- a/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/build/sil.cmo.bw.model_info +++ b/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/build/sil.cmo.bw.model_info @@ -3,7 +3,8 @@ "languages": [ "cmo-Khmr" ], - "description": "Bunong Wordlist is based on the Bible wordlist of 1,157 unique entries with their frequencies.", + "description": "

Bunong Wordlist is based on the Bible wordlist of 1,157 unique entries with their frequencies.

", + "helpLink": "https://help.keyman.com/model/sil.cmo.bw", "id": "sil.cmo.bw", "name": "Bunong Wordlist", "authorName": "", diff --git a/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/sil.cmo.bw.kpj b/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/sil.cmo.bw.kpj index 12aca7bbf28..11d0f84b757 100644 --- a/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/sil.cmo.bw.kpj +++ b/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/sil.cmo.bw.kpj @@ -48,13 +48,6 @@ .md
- - id_8146e45ce008c5c83d5a824f2121c46c - sil.cmo.bw.model_info - sil.cmo.bw.model_info - - .model_info - id_46ebc8fccf1bc10c92484653167449df sil.cmo.bw.model.js diff --git a/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/sil.cmo.bw.model_info b/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/sil.cmo.bw.model_info deleted file mode 100644 index c2d2b6070c5..00000000000 --- a/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/sil.cmo.bw.model_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "cmo-Khmr" - ], - "description": "Bunong Wordlist is based on the Bible wordlist of 1,157 unique entries with their frequencies." -} diff --git a/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/source/sil.cmo.bw.model.kps b/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/source/sil.cmo.bw.model.kps index 4fd452897cb..57b610c83ab 100644 --- a/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/source/sil.cmo.bw.model.kps +++ b/developer/src/kmc-model-info/test/fixtures/sil.cmo.bw/source/sil.cmo.bw.model.kps @@ -7,6 +7,7 @@ readme.htm + ..\LICENSE.md @@ -19,6 +20,7 @@ © 2021 - 2022 SIL International 1.1 + Bunong Wordlist is based on the Bible wordlist of 1,157 unique entries with their frequencies. @@ -27,6 +29,12 @@ 0 .js + + ..\LICENSE.md + + 0 + .md + welcome.htm diff --git a/developer/src/kmc-model-info/test/test-model-info-compiler.ts b/developer/src/kmc-model-info/test/test-model-info-compiler.ts index 418b62955dc..d74a2f5c60a 100644 --- a/developer/src/kmc-model-info/test/test-model-info-compiler.ts +++ b/developer/src/kmc-model-info/test/test-model-info-compiler.ts @@ -3,7 +3,7 @@ import { assert } from 'chai'; import 'mocha'; import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; import { makePathToFixture } from './helpers/index.js'; -import { writeMergedModelMetadataFile } from '../src/model-info-compiler.js'; +import { ModelInfoCompiler } from '../src/model-info-compiler.js'; import { KmpCompiler } from '@keymanapp/kmc-package'; const callbacks = new TestCompilerCallbacks(); @@ -14,22 +14,26 @@ beforeEach(function() { describe('model-info-compiler', function () { it('compile a .model_info file correctly', function() { - const path = makePathToFixture('sil.cmo.bw', 'sil.cmo.bw.model_info'); - const kpsFileName = makePathToFixture('sil.cmo.bw', 'source', 'sil.cmo.bw.model.kps'); + const kpsFilename = makePathToFixture('sil.cmo.bw', 'source', 'sil.cmo.bw.model.kps'); const kmpFileName = makePathToFixture('sil.cmo.bw', 'build', 'sil.cmo.bw.model.kmp'); const buildModelInfoFilename = makePathToFixture('sil.cmo.bw', 'build', 'sil.cmo.bw.model_info'); const kmpCompiler = new KmpCompiler(callbacks); - const kmpJsonData = kmpCompiler.transformKpsToKmpObject(kpsFileName); + const kmpJsonData = kmpCompiler.transformKpsToKmpObject(kpsFilename); const modelFileName = makePathToFixture('sil.cmo.bw', 'build', 'sil.cmo.bw.model.js'); - const data = writeMergedModelMetadataFile(path, callbacks, { + const data = (new ModelInfoCompiler(callbacks)).writeModelMetadataFile({ kmpFileName, kmpJsonData, model_id: 'sil.cmo.bw', modelFileName, - sourcePath: 'release/sil/sil.cmo.bw' + sourcePath: 'release/sil/sil.cmo.bw', + kpsFilename, + forPublishing: true, }); + if(data == null) { + callbacks.printMessages(); + } assert.isNotNull(data); const actual = JSON.parse(new TextDecoder().decode(data)); @@ -41,8 +45,4 @@ describe('model-info-compiler', function () { assert.deepEqual(actual, expected); }); - - it('compile a .model_info file correctly when no source .model_info exists', function() { - this.skip(); // TODO: support model_info when no source file exists (determine license from LICENSE.md) - }); }); diff --git a/developer/src/kmc-model-info/test/tsconfig.json b/developer/src/kmc-model-info/test/tsconfig.json index bf96b7b1509..6d48ad864d2 100644 --- a/developer/src/kmc-model-info/test/tsconfig.json +++ b/developer/src/kmc-model-info/test/tsconfig.json @@ -6,7 +6,6 @@ "rootDirs": ["./", "../src/"], "outDir": "../build/test", "esModuleInterop": true, - "moduleResolution": "node16", "allowSyntheticDefaultImports": true, "baseUrl": ".", "paths": { diff --git a/developer/src/kmc-model-info/tsconfig.json b/developer/src/kmc-model-info/tsconfig.json index c7edb11615d..6cb999c0a8d 100644 --- a/developer/src/kmc-model-info/tsconfig.json +++ b/developer/src/kmc-model-info/tsconfig.json @@ -6,6 +6,7 @@ "rootDir": "src/", "paths": { "@keymanapp/common-types": ["../../../common/web/types/src/main"], + "@keymanapp/developer-utils": ["../common/web/utils/index"], } }, "include": [ @@ -14,5 +15,6 @@ "references": [ { "path": "../../../common/web/types" }, { "path": "../../../common/models/types" }, + { "path": "../common/web/utils" }, ] } diff --git a/developer/src/kmc-model/package.json b/developer/src/kmc-model/package.json index d15bc21b976..faed0049d4a 100644 --- a/developer/src/kmc-model/package.json +++ b/developer/src/kmc-model/package.json @@ -34,15 +34,13 @@ "@keymanapp/common-types": "*", "@keymanapp/keyman-version": "*", "@keymanapp/models-types": "*", - "typescript": "^4.9.5", - "xml2js": "^0.4.19" + "typescript": "^4.9.5" }, "devDependencies": { "@keymanapp/developer-test-helpers": "*", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", - "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", "chalk": "^2.4.2", diff --git a/developer/src/kmc-model/src/build-trie.ts b/developer/src/kmc-model/src/build-trie.ts index 5f5ae99f85d..7a9b8873f4f 100644 --- a/developer/src/kmc-model/src/build-trie.ts +++ b/developer/src/kmc-model/src/build-trie.ts @@ -112,8 +112,10 @@ function _parseWordList(wordlist: WordList, source: WordListSource): void { wordform = wordform.normalize('NFC'); if (original !== wordform) { - // Mixed normalization forms are yucky! Warn about it. - callbacks.reportMessage(ModelCompilerMessages.Warn_MixedNormalizationForms({wordform: wordform})); + // Mixed normalization forms are yucky! Hint about it, because it may + // result in unexpected counts where multiple normalization forms for one + // word + callbacks.reportMessage(ModelCompilerMessages.Hint_MixedNormalizationForms({wordform: wordform})); } wordform = wordform.trim() @@ -129,9 +131,9 @@ function _parseWordList(wordlist: WordList, source: WordListSource): void { } if (wordsSeenInThisFile.has(wordform)) { - // The same word seen across multiple files is fine, - // but a word seen multiple times in one file is a problem! - callbacks.reportMessage(ModelCompilerMessages.Warn_DuplicateWordInSameFile({wordform: wordform})); + // The same word seen across multiple files is fine, but a word seen + // multiple times in one file may be a problem + callbacks.reportMessage(ModelCompilerMessages.Hint_DuplicateWordInSameFile({wordform: wordform})); } wordsSeenInThisFile.add(wordform); diff --git a/developer/src/kmc-model/src/model-compiler-errors.ts b/developer/src/kmc-model/src/model-compiler-errors.ts index beb3398caac..5d112eb96b6 100644 --- a/developer/src/kmc-model/src/model-compiler-errors.ts +++ b/developer/src/kmc-model/src/model-compiler-errors.ts @@ -2,8 +2,8 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerM const Namespace = CompilerErrorNamespace.ModelCompiler; // const SevInfo = CompilerErrorSeverity.Info | Namespace; -// const SevHint = CompilerErrorSeverity.Hint | Namespace; -const SevWarn = CompilerErrorSeverity.Warn | Namespace; +const SevHint = CompilerErrorSeverity.Hint | Namespace; +// const SevWarn = CompilerErrorSeverity.Warn | Namespace; const SevError = CompilerErrorSeverity.Error | Namespace; const SevFatal = CompilerErrorSeverity.Fatal | Namespace; @@ -24,13 +24,13 @@ export class ModelCompilerMessages { static Fatal_UnexpectedException = (o:{e: any}) => m(this.FATAL_UnexpectedException, null, o.e ?? 'unknown error'); static FATAL_UnexpectedException = SevFatal | 0x0001; - static Warn_MixedNormalizationForms = (o:{wordform: string}) => m(this.WARN_MixedNormalizationForms, + static Hint_MixedNormalizationForms = (o:{wordform: string}) => m(this.HINT_MixedNormalizationForms, `“${o.wordform}” is not in Unicode NFC. Automatically converting to NFC.`); - static WARN_MixedNormalizationForms = SevWarn | 0x0002; + static HINT_MixedNormalizationForms = SevHint | 0x0002; - static Warn_DuplicateWordInSameFile = (o:{wordform: string}) => m(this.WARN_DuplicateWordInSameFile, + static Hint_DuplicateWordInSameFile = (o:{wordform: string}) => m(this.HINT_DuplicateWordInSameFile, `duplicate word “${o.wordform}” found in same file; summing counts`); - static WARN_DuplicateWordInSameFile = SevWarn | 0x0003; + static HINT_DuplicateWordInSameFile = SevHint | 0x0003; static Error_UnimplementedModelFormat = (o:{format: string}) => m(this.ERROR_UnimplementedModelFormat, `Unimplemented model format: ${o.format}`); diff --git a/developer/src/kmc-model/test/test-join-word-breaker.ts b/developer/src/kmc-model/test/test-join-word-breaker.ts index d06bc7a6efd..b08411cb561 100644 --- a/developer/src/kmc-model/test/test-join-word-breaker.ts +++ b/developer/src/kmc-model/test/test-join-word-breaker.ts @@ -1,5 +1,5 @@ import { assert } from "chai"; -import defaultWordBreaker from './wordbreakers/default-wordbreaker-esm.js'; +import defaultWordBreaker from '@keymanapp/models-wordbreakers'; import {decorateWithJoin} from '../src/join-word-breaker-decorator.js'; describe('The join word breaker decorator', function () { diff --git a/developer/src/kmc-model/test/test-override-script-defaults.ts b/developer/src/kmc-model/test/test-override-script-defaults.ts index 909f52c2981..a0500320fd9 100644 --- a/developer/src/kmc-model/test/test-override-script-defaults.ts +++ b/developer/src/kmc-model/test/test-override-script-defaults.ts @@ -1,5 +1,5 @@ import { assert } from "chai"; -import defaultWordBreaker from './wordbreakers/default-wordbreaker-esm.js'; +import defaultWordBreaker from '@keymanapp/models-wordbreakers'; import {decorateWithScriptOverrides} from '../src/script-overrides-decorator.js'; const THIN_SPACE = "\u2009"; diff --git a/developer/src/kmc-model/test/test-parse-wordlist.ts b/developer/src/kmc-model/test/test-parse-wordlist.ts index ab610f5e802..93269e243d1 100644 --- a/developer/src/kmc-model/test/test-parse-wordlist.ts +++ b/developer/src/kmc-model/test/test-parse-wordlist.ts @@ -108,9 +108,9 @@ describe('parsing a word list', function () { assert.lengthOf(testCallbacks.messages, 4); // hello has been seen multiple times: - assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.WARN_DuplicateWordInSameFile)); + assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.HINT_DuplicateWordInSameFile)); // helló and hello + U+0301 have both been seen: - assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.WARN_MixedNormalizationForms)); + assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.HINT_MixedNormalizationForms)); // Let's parse another file: @@ -119,9 +119,9 @@ describe('parsing a word list', function () { parseWordListFromContents(repeatedWords, "hello\u0301\t5\n"); assert.lengthOf(testCallbacks.messages, 1); // hello + U+0301 (NFD) has been seen, but... - assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.WARN_MixedNormalizationForms)); + assert.isTrue(testCallbacks.hasMessage(ModelCompilerMessages.HINT_MixedNormalizationForms)); // BUT! We have not seen a duplicate **within the same file** - assert.isFalse(testCallbacks.hasMessage(ModelCompilerMessages.WARN_DuplicateWordInSameFile)); + assert.isFalse(testCallbacks.hasMessage(ModelCompilerMessages.HINT_DuplicateWordInSameFile)); assert.deepEqual(repeatedWords, { hello: expected['hello'], diff --git a/developer/src/kmc-model/test/tsconfig.json b/developer/src/kmc-model/test/tsconfig.json index 3902bed57f0..0917ea186ae 100644 --- a/developer/src/kmc-model/test/tsconfig.json +++ b/developer/src/kmc-model/test/tsconfig.json @@ -6,7 +6,6 @@ "rootDirs": ["./", "../src/"], "outDir": "../build/test", "esModuleInterop": true, - "moduleResolution": "node16", "allowSyntheticDefaultImports": true, "baseUrl": ".", "paths": { diff --git a/developer/src/kmc-model/test/wordbreakers/README.md b/developer/src/kmc-model/test/wordbreakers/README.md deleted file mode 100644 index d204b51c6a3..00000000000 --- a/developer/src/kmc-model/test/wordbreakers/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Wordbreakers ES Module format - -TODO: once we move common/models/wordbreakers to ESM, eliminate this. diff --git a/developer/src/kmc-model/test/wordbreakers/data.ts b/developer/src/kmc-model/test/wordbreakers/data.ts deleted file mode 100644 index 5622bf5c037..00000000000 --- a/developer/src/kmc-model/test/wordbreakers/data.ts +++ /dev/null @@ -1,1776 +0,0 @@ -// TEMP: esm version of /common/models/wordbreakers/default/data.ts -// Hand updated version of automatically generated file. -/** - * Valid values for a word break property. - */ -export enum WordBreakProperty { - Other, - LF, - Newline, - CR, - WSegSpace, - Double_Quote, - Single_Quote, - MidNum, - MidNumLet, - Numeric, - MidLetter, - ALetter, - ExtendNumLet, - Format, - Extend, - Hebrew_Letter, - ZWJ, - Katakana, - Regional_Indicator, - sot, - eot -}; - -/** - * Constants for indexing values in WORD_BREAK_PROPERTY. - */ -export enum I { - Start = 0, - Value = 1 -} - -export const WORD_BREAK_PROPERTY: [number, WordBreakProperty][] = [ - [/*start*/ 0x0, WordBreakProperty.Other], - [/*start*/ 0xA, WordBreakProperty.LF], - [/*start*/ 0xB, WordBreakProperty.Newline], - [/*start*/ 0xD, WordBreakProperty.CR], - [/*start*/ 0xE, WordBreakProperty.Other], - [/*start*/ 0x20, WordBreakProperty.WSegSpace], - [/*start*/ 0x21, WordBreakProperty.Other], - [/*start*/ 0x22, WordBreakProperty.Double_Quote], - [/*start*/ 0x23, WordBreakProperty.Other], - [/*start*/ 0x27, WordBreakProperty.Single_Quote], - [/*start*/ 0x28, WordBreakProperty.Other], - [/*start*/ 0x2C, WordBreakProperty.MidNum], - [/*start*/ 0x2D, WordBreakProperty.Other], - [/*start*/ 0x2E, WordBreakProperty.MidNumLet], - [/*start*/ 0x2F, WordBreakProperty.Other], - [/*start*/ 0x30, WordBreakProperty.Numeric], - [/*start*/ 0x3A, WordBreakProperty.MidLetter], - [/*start*/ 0x3B, WordBreakProperty.MidNum], - [/*start*/ 0x3C, WordBreakProperty.Other], - [/*start*/ 0x41, WordBreakProperty.ALetter], - [/*start*/ 0x5B, WordBreakProperty.Other], - [/*start*/ 0x5F, WordBreakProperty.ExtendNumLet], - [/*start*/ 0x60, WordBreakProperty.Other], - [/*start*/ 0x61, WordBreakProperty.ALetter], - [/*start*/ 0x7B, WordBreakProperty.Other], - [/*start*/ 0x85, WordBreakProperty.Newline], - [/*start*/ 0x86, WordBreakProperty.Other], - [/*start*/ 0xAA, WordBreakProperty.ALetter], - [/*start*/ 0xAB, WordBreakProperty.Other], - [/*start*/ 0xAD, WordBreakProperty.Format], - [/*start*/ 0xAE, WordBreakProperty.Other], - [/*start*/ 0xB5, WordBreakProperty.ALetter], - [/*start*/ 0xB6, WordBreakProperty.Other], - [/*start*/ 0xB7, WordBreakProperty.MidLetter], - [/*start*/ 0xB8, WordBreakProperty.Other], - [/*start*/ 0xBA, WordBreakProperty.ALetter], - [/*start*/ 0xBB, WordBreakProperty.Other], - [/*start*/ 0xC0, WordBreakProperty.ALetter], - [/*start*/ 0xD7, WordBreakProperty.Other], - [/*start*/ 0xD8, WordBreakProperty.ALetter], - [/*start*/ 0xF7, WordBreakProperty.Other], - [/*start*/ 0xF8, WordBreakProperty.ALetter], - [/*start*/ 0x2D8, WordBreakProperty.Other], - [/*start*/ 0x2DE, WordBreakProperty.ALetter], - [/*start*/ 0x300, WordBreakProperty.Extend], - [/*start*/ 0x370, WordBreakProperty.ALetter], - [/*start*/ 0x375, WordBreakProperty.Other], - [/*start*/ 0x376, WordBreakProperty.ALetter], - [/*start*/ 0x378, WordBreakProperty.Other], - [/*start*/ 0x37A, WordBreakProperty.ALetter], - [/*start*/ 0x37E, WordBreakProperty.MidNum], - [/*start*/ 0x37F, WordBreakProperty.ALetter], - [/*start*/ 0x380, WordBreakProperty.Other], - [/*start*/ 0x386, WordBreakProperty.ALetter], - [/*start*/ 0x387, WordBreakProperty.MidLetter], - [/*start*/ 0x388, WordBreakProperty.ALetter], - [/*start*/ 0x38B, WordBreakProperty.Other], - [/*start*/ 0x38C, WordBreakProperty.ALetter], - [/*start*/ 0x38D, WordBreakProperty.Other], - [/*start*/ 0x38E, WordBreakProperty.ALetter], - [/*start*/ 0x3A2, WordBreakProperty.Other], - [/*start*/ 0x3A3, WordBreakProperty.ALetter], - [/*start*/ 0x3F6, WordBreakProperty.Other], - [/*start*/ 0x3F7, WordBreakProperty.ALetter], - [/*start*/ 0x482, WordBreakProperty.Other], - [/*start*/ 0x483, WordBreakProperty.Extend], - [/*start*/ 0x48A, WordBreakProperty.ALetter], - [/*start*/ 0x530, WordBreakProperty.Other], - [/*start*/ 0x531, WordBreakProperty.ALetter], - [/*start*/ 0x557, WordBreakProperty.Other], - [/*start*/ 0x559, WordBreakProperty.ALetter], - [/*start*/ 0x55D, WordBreakProperty.Other], - [/*start*/ 0x55E, WordBreakProperty.ALetter], - [/*start*/ 0x55F, WordBreakProperty.MidLetter], - [/*start*/ 0x560, WordBreakProperty.ALetter], - [/*start*/ 0x589, WordBreakProperty.MidNum], - [/*start*/ 0x58A, WordBreakProperty.ALetter], - [/*start*/ 0x58B, WordBreakProperty.Other], - [/*start*/ 0x591, WordBreakProperty.Extend], - [/*start*/ 0x5BE, WordBreakProperty.Other], - [/*start*/ 0x5BF, WordBreakProperty.Extend], - [/*start*/ 0x5C0, WordBreakProperty.Other], - [/*start*/ 0x5C1, WordBreakProperty.Extend], - [/*start*/ 0x5C3, WordBreakProperty.Other], - [/*start*/ 0x5C4, WordBreakProperty.Extend], - [/*start*/ 0x5C6, WordBreakProperty.Other], - [/*start*/ 0x5C7, WordBreakProperty.Extend], - [/*start*/ 0x5C8, WordBreakProperty.Other], - [/*start*/ 0x5D0, WordBreakProperty.Hebrew_Letter], - [/*start*/ 0x5EB, WordBreakProperty.Other], - [/*start*/ 0x5EF, WordBreakProperty.Hebrew_Letter], - [/*start*/ 0x5F3, WordBreakProperty.ALetter], - [/*start*/ 0x5F4, WordBreakProperty.MidLetter], - [/*start*/ 0x5F5, WordBreakProperty.Other], - [/*start*/ 0x600, WordBreakProperty.Format], - [/*start*/ 0x606, WordBreakProperty.Other], - [/*start*/ 0x60C, WordBreakProperty.MidNum], - [/*start*/ 0x60E, WordBreakProperty.Other], - [/*start*/ 0x610, WordBreakProperty.Extend], - [/*start*/ 0x61B, WordBreakProperty.Other], - [/*start*/ 0x61C, WordBreakProperty.Format], - [/*start*/ 0x61D, WordBreakProperty.Other], - [/*start*/ 0x620, WordBreakProperty.ALetter], - [/*start*/ 0x64B, WordBreakProperty.Extend], - [/*start*/ 0x660, WordBreakProperty.Numeric], - [/*start*/ 0x66A, WordBreakProperty.Other], - [/*start*/ 0x66B, WordBreakProperty.Numeric], - [/*start*/ 0x66C, WordBreakProperty.MidNum], - [/*start*/ 0x66D, WordBreakProperty.Other], - [/*start*/ 0x66E, WordBreakProperty.ALetter], - [/*start*/ 0x670, WordBreakProperty.Extend], - [/*start*/ 0x671, WordBreakProperty.ALetter], - [/*start*/ 0x6D4, WordBreakProperty.Other], - [/*start*/ 0x6D5, WordBreakProperty.ALetter], - [/*start*/ 0x6D6, WordBreakProperty.Extend], - [/*start*/ 0x6DD, WordBreakProperty.Format], - [/*start*/ 0x6DE, WordBreakProperty.Other], - [/*start*/ 0x6DF, WordBreakProperty.Extend], - [/*start*/ 0x6E5, WordBreakProperty.ALetter], - [/*start*/ 0x6E7, WordBreakProperty.Extend], - [/*start*/ 0x6E9, WordBreakProperty.Other], - [/*start*/ 0x6EA, WordBreakProperty.Extend], - [/*start*/ 0x6EE, WordBreakProperty.ALetter], - [/*start*/ 0x6F0, WordBreakProperty.Numeric], - [/*start*/ 0x6FA, WordBreakProperty.ALetter], - [/*start*/ 0x6FD, WordBreakProperty.Other], - [/*start*/ 0x6FF, WordBreakProperty.ALetter], - [/*start*/ 0x700, WordBreakProperty.Other], - [/*start*/ 0x70F, WordBreakProperty.Format], - [/*start*/ 0x710, WordBreakProperty.ALetter], - [/*start*/ 0x711, WordBreakProperty.Extend], - [/*start*/ 0x712, WordBreakProperty.ALetter], - [/*start*/ 0x730, WordBreakProperty.Extend], - [/*start*/ 0x74B, WordBreakProperty.Other], - [/*start*/ 0x74D, WordBreakProperty.ALetter], - [/*start*/ 0x7A6, WordBreakProperty.Extend], - [/*start*/ 0x7B1, WordBreakProperty.ALetter], - [/*start*/ 0x7B2, WordBreakProperty.Other], - [/*start*/ 0x7C0, WordBreakProperty.Numeric], - [/*start*/ 0x7CA, WordBreakProperty.ALetter], - [/*start*/ 0x7EB, WordBreakProperty.Extend], - [/*start*/ 0x7F4, WordBreakProperty.ALetter], - [/*start*/ 0x7F6, WordBreakProperty.Other], - [/*start*/ 0x7F8, WordBreakProperty.MidNum], - [/*start*/ 0x7F9, WordBreakProperty.Other], - [/*start*/ 0x7FA, WordBreakProperty.ALetter], - [/*start*/ 0x7FB, WordBreakProperty.Other], - [/*start*/ 0x7FD, WordBreakProperty.Extend], - [/*start*/ 0x7FE, WordBreakProperty.Other], - [/*start*/ 0x800, WordBreakProperty.ALetter], - [/*start*/ 0x816, WordBreakProperty.Extend], - [/*start*/ 0x81A, WordBreakProperty.ALetter], - [/*start*/ 0x81B, WordBreakProperty.Extend], - [/*start*/ 0x824, WordBreakProperty.ALetter], - [/*start*/ 0x825, WordBreakProperty.Extend], - [/*start*/ 0x828, WordBreakProperty.ALetter], - [/*start*/ 0x829, WordBreakProperty.Extend], - [/*start*/ 0x82E, WordBreakProperty.Other], - [/*start*/ 0x840, WordBreakProperty.ALetter], - [/*start*/ 0x859, WordBreakProperty.Extend], - [/*start*/ 0x85C, WordBreakProperty.Other], - [/*start*/ 0x860, WordBreakProperty.ALetter], - [/*start*/ 0x86B, WordBreakProperty.Other], - [/*start*/ 0x8A0, WordBreakProperty.ALetter], - [/*start*/ 0x8B5, WordBreakProperty.Other], - [/*start*/ 0x8B6, WordBreakProperty.ALetter], - [/*start*/ 0x8C8, WordBreakProperty.Other], - [/*start*/ 0x8D3, WordBreakProperty.Extend], - [/*start*/ 0x8E2, WordBreakProperty.Format], - [/*start*/ 0x8E3, WordBreakProperty.Extend], - [/*start*/ 0x904, WordBreakProperty.ALetter], - [/*start*/ 0x93A, WordBreakProperty.Extend], - [/*start*/ 0x93D, WordBreakProperty.ALetter], - [/*start*/ 0x93E, WordBreakProperty.Extend], - [/*start*/ 0x950, WordBreakProperty.ALetter], - [/*start*/ 0x951, WordBreakProperty.Extend], - [/*start*/ 0x958, WordBreakProperty.ALetter], - [/*start*/ 0x962, WordBreakProperty.Extend], - [/*start*/ 0x964, WordBreakProperty.Other], - [/*start*/ 0x966, WordBreakProperty.Numeric], - [/*start*/ 0x970, WordBreakProperty.Other], - [/*start*/ 0x971, WordBreakProperty.ALetter], - [/*start*/ 0x981, WordBreakProperty.Extend], - [/*start*/ 0x984, WordBreakProperty.Other], - [/*start*/ 0x985, WordBreakProperty.ALetter], - [/*start*/ 0x98D, WordBreakProperty.Other], - [/*start*/ 0x98F, WordBreakProperty.ALetter], - [/*start*/ 0x991, WordBreakProperty.Other], - [/*start*/ 0x993, WordBreakProperty.ALetter], - [/*start*/ 0x9A9, WordBreakProperty.Other], - [/*start*/ 0x9AA, WordBreakProperty.ALetter], - [/*start*/ 0x9B1, WordBreakProperty.Other], - [/*start*/ 0x9B2, WordBreakProperty.ALetter], - [/*start*/ 0x9B3, WordBreakProperty.Other], - [/*start*/ 0x9B6, WordBreakProperty.ALetter], - [/*start*/ 0x9BA, WordBreakProperty.Other], - [/*start*/ 0x9BC, WordBreakProperty.Extend], - [/*start*/ 0x9BD, WordBreakProperty.ALetter], - [/*start*/ 0x9BE, WordBreakProperty.Extend], - [/*start*/ 0x9C5, WordBreakProperty.Other], - [/*start*/ 0x9C7, WordBreakProperty.Extend], - [/*start*/ 0x9C9, WordBreakProperty.Other], - [/*start*/ 0x9CB, WordBreakProperty.Extend], - [/*start*/ 0x9CE, WordBreakProperty.ALetter], - [/*start*/ 0x9CF, WordBreakProperty.Other], - [/*start*/ 0x9D7, WordBreakProperty.Extend], - [/*start*/ 0x9D8, WordBreakProperty.Other], - [/*start*/ 0x9DC, WordBreakProperty.ALetter], - [/*start*/ 0x9DE, WordBreakProperty.Other], - [/*start*/ 0x9DF, WordBreakProperty.ALetter], - [/*start*/ 0x9E2, WordBreakProperty.Extend], - [/*start*/ 0x9E4, WordBreakProperty.Other], - [/*start*/ 0x9E6, WordBreakProperty.Numeric], - [/*start*/ 0x9F0, WordBreakProperty.ALetter], - [/*start*/ 0x9F2, WordBreakProperty.Other], - [/*start*/ 0x9FC, WordBreakProperty.ALetter], - [/*start*/ 0x9FD, WordBreakProperty.Other], - [/*start*/ 0x9FE, WordBreakProperty.Extend], - [/*start*/ 0x9FF, WordBreakProperty.Other], - [/*start*/ 0xA01, WordBreakProperty.Extend], - [/*start*/ 0xA04, WordBreakProperty.Other], - [/*start*/ 0xA05, WordBreakProperty.ALetter], - [/*start*/ 0xA0B, WordBreakProperty.Other], - [/*start*/ 0xA0F, WordBreakProperty.ALetter], - [/*start*/ 0xA11, WordBreakProperty.Other], - [/*start*/ 0xA13, WordBreakProperty.ALetter], - [/*start*/ 0xA29, WordBreakProperty.Other], - [/*start*/ 0xA2A, WordBreakProperty.ALetter], - [/*start*/ 0xA31, WordBreakProperty.Other], - [/*start*/ 0xA32, WordBreakProperty.ALetter], - [/*start*/ 0xA34, WordBreakProperty.Other], - [/*start*/ 0xA35, WordBreakProperty.ALetter], - [/*start*/ 0xA37, WordBreakProperty.Other], - [/*start*/ 0xA38, WordBreakProperty.ALetter], - [/*start*/ 0xA3A, WordBreakProperty.Other], - [/*start*/ 0xA3C, WordBreakProperty.Extend], - [/*start*/ 0xA3D, WordBreakProperty.Other], - [/*start*/ 0xA3E, WordBreakProperty.Extend], - [/*start*/ 0xA43, WordBreakProperty.Other], - [/*start*/ 0xA47, WordBreakProperty.Extend], - [/*start*/ 0xA49, WordBreakProperty.Other], - [/*start*/ 0xA4B, WordBreakProperty.Extend], - [/*start*/ 0xA4E, WordBreakProperty.Other], - [/*start*/ 0xA51, WordBreakProperty.Extend], - [/*start*/ 0xA52, WordBreakProperty.Other], - [/*start*/ 0xA59, WordBreakProperty.ALetter], - [/*start*/ 0xA5D, WordBreakProperty.Other], - [/*start*/ 0xA5E, WordBreakProperty.ALetter], - [/*start*/ 0xA5F, WordBreakProperty.Other], - [/*start*/ 0xA66, WordBreakProperty.Numeric], - [/*start*/ 0xA70, WordBreakProperty.Extend], - [/*start*/ 0xA72, WordBreakProperty.ALetter], - [/*start*/ 0xA75, WordBreakProperty.Extend], - [/*start*/ 0xA76, WordBreakProperty.Other], - [/*start*/ 0xA81, WordBreakProperty.Extend], - [/*start*/ 0xA84, WordBreakProperty.Other], - [/*start*/ 0xA85, WordBreakProperty.ALetter], - [/*start*/ 0xA8E, WordBreakProperty.Other], - [/*start*/ 0xA8F, WordBreakProperty.ALetter], - [/*start*/ 0xA92, WordBreakProperty.Other], - [/*start*/ 0xA93, WordBreakProperty.ALetter], - [/*start*/ 0xAA9, WordBreakProperty.Other], - [/*start*/ 0xAAA, WordBreakProperty.ALetter], - [/*start*/ 0xAB1, WordBreakProperty.Other], - [/*start*/ 0xAB2, WordBreakProperty.ALetter], - [/*start*/ 0xAB4, WordBreakProperty.Other], - [/*start*/ 0xAB5, WordBreakProperty.ALetter], - [/*start*/ 0xABA, WordBreakProperty.Other], - [/*start*/ 0xABC, WordBreakProperty.Extend], - [/*start*/ 0xABD, WordBreakProperty.ALetter], - [/*start*/ 0xABE, WordBreakProperty.Extend], - [/*start*/ 0xAC6, WordBreakProperty.Other], - [/*start*/ 0xAC7, WordBreakProperty.Extend], - [/*start*/ 0xACA, WordBreakProperty.Other], - [/*start*/ 0xACB, WordBreakProperty.Extend], - [/*start*/ 0xACE, WordBreakProperty.Other], - [/*start*/ 0xAD0, WordBreakProperty.ALetter], - [/*start*/ 0xAD1, WordBreakProperty.Other], - [/*start*/ 0xAE0, WordBreakProperty.ALetter], - [/*start*/ 0xAE2, WordBreakProperty.Extend], - [/*start*/ 0xAE4, WordBreakProperty.Other], - [/*start*/ 0xAE6, WordBreakProperty.Numeric], - [/*start*/ 0xAF0, WordBreakProperty.Other], - [/*start*/ 0xAF9, WordBreakProperty.ALetter], - [/*start*/ 0xAFA, WordBreakProperty.Extend], - [/*start*/ 0xB00, WordBreakProperty.Other], - [/*start*/ 0xB01, WordBreakProperty.Extend], - [/*start*/ 0xB04, WordBreakProperty.Other], - [/*start*/ 0xB05, WordBreakProperty.ALetter], - [/*start*/ 0xB0D, WordBreakProperty.Other], - [/*start*/ 0xB0F, WordBreakProperty.ALetter], - [/*start*/ 0xB11, WordBreakProperty.Other], - [/*start*/ 0xB13, WordBreakProperty.ALetter], - [/*start*/ 0xB29, WordBreakProperty.Other], - [/*start*/ 0xB2A, WordBreakProperty.ALetter], - [/*start*/ 0xB31, WordBreakProperty.Other], - [/*start*/ 0xB32, WordBreakProperty.ALetter], - [/*start*/ 0xB34, WordBreakProperty.Other], - [/*start*/ 0xB35, WordBreakProperty.ALetter], - [/*start*/ 0xB3A, WordBreakProperty.Other], - [/*start*/ 0xB3C, WordBreakProperty.Extend], - [/*start*/ 0xB3D, WordBreakProperty.ALetter], - [/*start*/ 0xB3E, WordBreakProperty.Extend], - [/*start*/ 0xB45, WordBreakProperty.Other], - [/*start*/ 0xB47, WordBreakProperty.Extend], - [/*start*/ 0xB49, WordBreakProperty.Other], - [/*start*/ 0xB4B, WordBreakProperty.Extend], - [/*start*/ 0xB4E, WordBreakProperty.Other], - [/*start*/ 0xB55, WordBreakProperty.Extend], - [/*start*/ 0xB58, WordBreakProperty.Other], - [/*start*/ 0xB5C, WordBreakProperty.ALetter], - [/*start*/ 0xB5E, WordBreakProperty.Other], - [/*start*/ 0xB5F, WordBreakProperty.ALetter], - [/*start*/ 0xB62, WordBreakProperty.Extend], - [/*start*/ 0xB64, WordBreakProperty.Other], - [/*start*/ 0xB66, WordBreakProperty.Numeric], - [/*start*/ 0xB70, WordBreakProperty.Other], - [/*start*/ 0xB71, WordBreakProperty.ALetter], - [/*start*/ 0xB72, WordBreakProperty.Other], - [/*start*/ 0xB82, WordBreakProperty.Extend], - [/*start*/ 0xB83, WordBreakProperty.ALetter], - [/*start*/ 0xB84, WordBreakProperty.Other], - [/*start*/ 0xB85, WordBreakProperty.ALetter], - [/*start*/ 0xB8B, WordBreakProperty.Other], - [/*start*/ 0xB8E, WordBreakProperty.ALetter], - [/*start*/ 0xB91, WordBreakProperty.Other], - [/*start*/ 0xB92, WordBreakProperty.ALetter], - [/*start*/ 0xB96, WordBreakProperty.Other], - [/*start*/ 0xB99, WordBreakProperty.ALetter], - [/*start*/ 0xB9B, WordBreakProperty.Other], - [/*start*/ 0xB9C, WordBreakProperty.ALetter], - [/*start*/ 0xB9D, WordBreakProperty.Other], - [/*start*/ 0xB9E, WordBreakProperty.ALetter], - [/*start*/ 0xBA0, WordBreakProperty.Other], - [/*start*/ 0xBA3, WordBreakProperty.ALetter], - [/*start*/ 0xBA5, WordBreakProperty.Other], - [/*start*/ 0xBA8, WordBreakProperty.ALetter], - [/*start*/ 0xBAB, WordBreakProperty.Other], - [/*start*/ 0xBAE, WordBreakProperty.ALetter], - [/*start*/ 0xBBA, WordBreakProperty.Other], - [/*start*/ 0xBBE, WordBreakProperty.Extend], - [/*start*/ 0xBC3, WordBreakProperty.Other], - [/*start*/ 0xBC6, WordBreakProperty.Extend], - [/*start*/ 0xBC9, WordBreakProperty.Other], - [/*start*/ 0xBCA, WordBreakProperty.Extend], - [/*start*/ 0xBCE, WordBreakProperty.Other], - [/*start*/ 0xBD0, WordBreakProperty.ALetter], - [/*start*/ 0xBD1, WordBreakProperty.Other], - [/*start*/ 0xBD7, WordBreakProperty.Extend], - [/*start*/ 0xBD8, WordBreakProperty.Other], - [/*start*/ 0xBE6, WordBreakProperty.Numeric], - [/*start*/ 0xBF0, WordBreakProperty.Other], - [/*start*/ 0xC00, WordBreakProperty.Extend], - [/*start*/ 0xC05, WordBreakProperty.ALetter], - [/*start*/ 0xC0D, WordBreakProperty.Other], - [/*start*/ 0xC0E, WordBreakProperty.ALetter], - [/*start*/ 0xC11, WordBreakProperty.Other], - [/*start*/ 0xC12, WordBreakProperty.ALetter], - [/*start*/ 0xC29, WordBreakProperty.Other], - [/*start*/ 0xC2A, WordBreakProperty.ALetter], - [/*start*/ 0xC3A, WordBreakProperty.Other], - [/*start*/ 0xC3D, WordBreakProperty.ALetter], - [/*start*/ 0xC3E, WordBreakProperty.Extend], - [/*start*/ 0xC45, WordBreakProperty.Other], - [/*start*/ 0xC46, WordBreakProperty.Extend], - [/*start*/ 0xC49, WordBreakProperty.Other], - [/*start*/ 0xC4A, WordBreakProperty.Extend], - [/*start*/ 0xC4E, WordBreakProperty.Other], - [/*start*/ 0xC55, WordBreakProperty.Extend], - [/*start*/ 0xC57, WordBreakProperty.Other], - [/*start*/ 0xC58, WordBreakProperty.ALetter], - [/*start*/ 0xC5B, WordBreakProperty.Other], - [/*start*/ 0xC60, WordBreakProperty.ALetter], - [/*start*/ 0xC62, WordBreakProperty.Extend], - [/*start*/ 0xC64, WordBreakProperty.Other], - [/*start*/ 0xC66, WordBreakProperty.Numeric], - [/*start*/ 0xC70, WordBreakProperty.Other], - [/*start*/ 0xC80, WordBreakProperty.ALetter], - [/*start*/ 0xC81, WordBreakProperty.Extend], - [/*start*/ 0xC84, WordBreakProperty.Other], - [/*start*/ 0xC85, WordBreakProperty.ALetter], - [/*start*/ 0xC8D, WordBreakProperty.Other], - [/*start*/ 0xC8E, WordBreakProperty.ALetter], - [/*start*/ 0xC91, WordBreakProperty.Other], - [/*start*/ 0xC92, WordBreakProperty.ALetter], - [/*start*/ 0xCA9, WordBreakProperty.Other], - [/*start*/ 0xCAA, WordBreakProperty.ALetter], - [/*start*/ 0xCB4, WordBreakProperty.Other], - [/*start*/ 0xCB5, WordBreakProperty.ALetter], - [/*start*/ 0xCBA, WordBreakProperty.Other], - [/*start*/ 0xCBC, WordBreakProperty.Extend], - [/*start*/ 0xCBD, WordBreakProperty.ALetter], - [/*start*/ 0xCBE, WordBreakProperty.Extend], - [/*start*/ 0xCC5, WordBreakProperty.Other], - [/*start*/ 0xCC6, WordBreakProperty.Extend], - [/*start*/ 0xCC9, WordBreakProperty.Other], - [/*start*/ 0xCCA, WordBreakProperty.Extend], - [/*start*/ 0xCCE, WordBreakProperty.Other], - [/*start*/ 0xCD5, WordBreakProperty.Extend], - [/*start*/ 0xCD7, WordBreakProperty.Other], - [/*start*/ 0xCDE, WordBreakProperty.ALetter], - [/*start*/ 0xCDF, WordBreakProperty.Other], - [/*start*/ 0xCE0, WordBreakProperty.ALetter], - [/*start*/ 0xCE2, WordBreakProperty.Extend], - [/*start*/ 0xCE4, WordBreakProperty.Other], - [/*start*/ 0xCE6, WordBreakProperty.Numeric], - [/*start*/ 0xCF0, WordBreakProperty.Other], - [/*start*/ 0xCF1, WordBreakProperty.ALetter], - [/*start*/ 0xCF3, WordBreakProperty.Other], - [/*start*/ 0xD00, WordBreakProperty.Extend], - [/*start*/ 0xD04, WordBreakProperty.ALetter], - [/*start*/ 0xD0D, WordBreakProperty.Other], - [/*start*/ 0xD0E, WordBreakProperty.ALetter], - [/*start*/ 0xD11, WordBreakProperty.Other], - [/*start*/ 0xD12, WordBreakProperty.ALetter], - [/*start*/ 0xD3B, WordBreakProperty.Extend], - [/*start*/ 0xD3D, WordBreakProperty.ALetter], - [/*start*/ 0xD3E, WordBreakProperty.Extend], - [/*start*/ 0xD45, WordBreakProperty.Other], - [/*start*/ 0xD46, WordBreakProperty.Extend], - [/*start*/ 0xD49, WordBreakProperty.Other], - [/*start*/ 0xD4A, WordBreakProperty.Extend], - [/*start*/ 0xD4E, WordBreakProperty.ALetter], - [/*start*/ 0xD4F, WordBreakProperty.Other], - [/*start*/ 0xD54, WordBreakProperty.ALetter], - [/*start*/ 0xD57, WordBreakProperty.Extend], - [/*start*/ 0xD58, WordBreakProperty.Other], - [/*start*/ 0xD5F, WordBreakProperty.ALetter], - [/*start*/ 0xD62, WordBreakProperty.Extend], - [/*start*/ 0xD64, WordBreakProperty.Other], - [/*start*/ 0xD66, WordBreakProperty.Numeric], - [/*start*/ 0xD70, WordBreakProperty.Other], - [/*start*/ 0xD7A, WordBreakProperty.ALetter], - [/*start*/ 0xD80, WordBreakProperty.Other], - [/*start*/ 0xD81, WordBreakProperty.Extend], - [/*start*/ 0xD84, WordBreakProperty.Other], - [/*start*/ 0xD85, WordBreakProperty.ALetter], - [/*start*/ 0xD97, WordBreakProperty.Other], - [/*start*/ 0xD9A, WordBreakProperty.ALetter], - [/*start*/ 0xDB2, WordBreakProperty.Other], - [/*start*/ 0xDB3, WordBreakProperty.ALetter], - [/*start*/ 0xDBC, WordBreakProperty.Other], - [/*start*/ 0xDBD, WordBreakProperty.ALetter], - [/*start*/ 0xDBE, WordBreakProperty.Other], - [/*start*/ 0xDC0, WordBreakProperty.ALetter], - [/*start*/ 0xDC7, WordBreakProperty.Other], - [/*start*/ 0xDCA, WordBreakProperty.Extend], - [/*start*/ 0xDCB, WordBreakProperty.Other], - [/*start*/ 0xDCF, WordBreakProperty.Extend], - [/*start*/ 0xDD5, WordBreakProperty.Other], - [/*start*/ 0xDD6, WordBreakProperty.Extend], - [/*start*/ 0xDD7, WordBreakProperty.Other], - [/*start*/ 0xDD8, WordBreakProperty.Extend], - [/*start*/ 0xDE0, WordBreakProperty.Other], - [/*start*/ 0xDE6, WordBreakProperty.Numeric], - [/*start*/ 0xDF0, WordBreakProperty.Other], - [/*start*/ 0xDF2, WordBreakProperty.Extend], - [/*start*/ 0xDF4, WordBreakProperty.Other], - [/*start*/ 0xE31, WordBreakProperty.Extend], - [/*start*/ 0xE32, WordBreakProperty.Other], - [/*start*/ 0xE34, WordBreakProperty.Extend], - [/*start*/ 0xE3B, WordBreakProperty.Other], - [/*start*/ 0xE47, WordBreakProperty.Extend], - [/*start*/ 0xE4F, WordBreakProperty.Other], - [/*start*/ 0xE50, WordBreakProperty.Numeric], - [/*start*/ 0xE5A, WordBreakProperty.Other], - [/*start*/ 0xEB1, WordBreakProperty.Extend], - [/*start*/ 0xEB2, WordBreakProperty.Other], - [/*start*/ 0xEB4, WordBreakProperty.Extend], - [/*start*/ 0xEBD, WordBreakProperty.Other], - [/*start*/ 0xEC8, WordBreakProperty.Extend], - [/*start*/ 0xECE, WordBreakProperty.Other], - [/*start*/ 0xED0, WordBreakProperty.Numeric], - [/*start*/ 0xEDA, WordBreakProperty.Other], - [/*start*/ 0xF00, WordBreakProperty.ALetter], - [/*start*/ 0xF01, WordBreakProperty.Other], - [/*start*/ 0xF18, WordBreakProperty.Extend], - [/*start*/ 0xF1A, WordBreakProperty.Other], - [/*start*/ 0xF20, WordBreakProperty.Numeric], - [/*start*/ 0xF2A, WordBreakProperty.Other], - [/*start*/ 0xF35, WordBreakProperty.Extend], - [/*start*/ 0xF36, WordBreakProperty.Other], - [/*start*/ 0xF37, WordBreakProperty.Extend], - [/*start*/ 0xF38, WordBreakProperty.Other], - [/*start*/ 0xF39, WordBreakProperty.Extend], - [/*start*/ 0xF3A, WordBreakProperty.Other], - [/*start*/ 0xF3E, WordBreakProperty.Extend], - [/*start*/ 0xF40, WordBreakProperty.ALetter], - [/*start*/ 0xF48, WordBreakProperty.Other], - [/*start*/ 0xF49, WordBreakProperty.ALetter], - [/*start*/ 0xF6D, WordBreakProperty.Other], - [/*start*/ 0xF71, WordBreakProperty.Extend], - [/*start*/ 0xF85, WordBreakProperty.Other], - [/*start*/ 0xF86, WordBreakProperty.Extend], - [/*start*/ 0xF88, WordBreakProperty.ALetter], - [/*start*/ 0xF8D, WordBreakProperty.Extend], - [/*start*/ 0xF98, WordBreakProperty.Other], - [/*start*/ 0xF99, WordBreakProperty.Extend], - [/*start*/ 0xFBD, WordBreakProperty.Other], - [/*start*/ 0xFC6, WordBreakProperty.Extend], - [/*start*/ 0xFC7, WordBreakProperty.Other], - [/*start*/ 0x102B, WordBreakProperty.Extend], - [/*start*/ 0x103F, WordBreakProperty.Other], - [/*start*/ 0x1040, WordBreakProperty.Numeric], - [/*start*/ 0x104A, WordBreakProperty.Other], - [/*start*/ 0x1056, WordBreakProperty.Extend], - [/*start*/ 0x105A, WordBreakProperty.Other], - [/*start*/ 0x105E, WordBreakProperty.Extend], - [/*start*/ 0x1061, WordBreakProperty.Other], - [/*start*/ 0x1062, WordBreakProperty.Extend], - [/*start*/ 0x1065, WordBreakProperty.Other], - [/*start*/ 0x1067, WordBreakProperty.Extend], - [/*start*/ 0x106E, WordBreakProperty.Other], - [/*start*/ 0x1071, WordBreakProperty.Extend], - [/*start*/ 0x1075, WordBreakProperty.Other], - [/*start*/ 0x1082, WordBreakProperty.Extend], - [/*start*/ 0x108E, WordBreakProperty.Other], - [/*start*/ 0x108F, WordBreakProperty.Extend], - [/*start*/ 0x1090, WordBreakProperty.Numeric], - [/*start*/ 0x109A, WordBreakProperty.Extend], - [/*start*/ 0x109E, WordBreakProperty.Other], - [/*start*/ 0x10A0, WordBreakProperty.ALetter], - [/*start*/ 0x10C6, WordBreakProperty.Other], - [/*start*/ 0x10C7, WordBreakProperty.ALetter], - [/*start*/ 0x10C8, WordBreakProperty.Other], - [/*start*/ 0x10CD, WordBreakProperty.ALetter], - [/*start*/ 0x10CE, WordBreakProperty.Other], - [/*start*/ 0x10D0, WordBreakProperty.ALetter], - [/*start*/ 0x10FB, WordBreakProperty.Other], - [/*start*/ 0x10FC, WordBreakProperty.ALetter], - [/*start*/ 0x1249, WordBreakProperty.Other], - [/*start*/ 0x124A, WordBreakProperty.ALetter], - [/*start*/ 0x124E, WordBreakProperty.Other], - [/*start*/ 0x1250, WordBreakProperty.ALetter], - [/*start*/ 0x1257, WordBreakProperty.Other], - [/*start*/ 0x1258, WordBreakProperty.ALetter], - [/*start*/ 0x1259, WordBreakProperty.Other], - [/*start*/ 0x125A, WordBreakProperty.ALetter], - [/*start*/ 0x125E, WordBreakProperty.Other], - [/*start*/ 0x1260, WordBreakProperty.ALetter], - [/*start*/ 0x1289, WordBreakProperty.Other], - [/*start*/ 0x128A, WordBreakProperty.ALetter], - [/*start*/ 0x128E, WordBreakProperty.Other], - [/*start*/ 0x1290, WordBreakProperty.ALetter], - [/*start*/ 0x12B1, WordBreakProperty.Other], - [/*start*/ 0x12B2, WordBreakProperty.ALetter], - [/*start*/ 0x12B6, WordBreakProperty.Other], - [/*start*/ 0x12B8, WordBreakProperty.ALetter], - [/*start*/ 0x12BF, WordBreakProperty.Other], - [/*start*/ 0x12C0, WordBreakProperty.ALetter], - [/*start*/ 0x12C1, WordBreakProperty.Other], - [/*start*/ 0x12C2, WordBreakProperty.ALetter], - [/*start*/ 0x12C6, WordBreakProperty.Other], - [/*start*/ 0x12C8, WordBreakProperty.ALetter], - [/*start*/ 0x12D7, WordBreakProperty.Other], - [/*start*/ 0x12D8, WordBreakProperty.ALetter], - [/*start*/ 0x1311, WordBreakProperty.Other], - [/*start*/ 0x1312, WordBreakProperty.ALetter], - [/*start*/ 0x1316, WordBreakProperty.Other], - [/*start*/ 0x1318, WordBreakProperty.ALetter], - [/*start*/ 0x135B, WordBreakProperty.Other], - [/*start*/ 0x135D, WordBreakProperty.Extend], - [/*start*/ 0x1360, WordBreakProperty.Other], - [/*start*/ 0x1380, WordBreakProperty.ALetter], - [/*start*/ 0x1390, WordBreakProperty.Other], - [/*start*/ 0x13A0, WordBreakProperty.ALetter], - [/*start*/ 0x13F6, WordBreakProperty.Other], - [/*start*/ 0x13F8, WordBreakProperty.ALetter], - [/*start*/ 0x13FE, WordBreakProperty.Other], - [/*start*/ 0x1401, WordBreakProperty.ALetter], - [/*start*/ 0x166D, WordBreakProperty.Other], - [/*start*/ 0x166F, WordBreakProperty.ALetter], - [/*start*/ 0x1680, WordBreakProperty.WSegSpace], - [/*start*/ 0x1681, WordBreakProperty.ALetter], - [/*start*/ 0x169B, WordBreakProperty.Other], - [/*start*/ 0x16A0, WordBreakProperty.ALetter], - [/*start*/ 0x16EB, WordBreakProperty.Other], - [/*start*/ 0x16EE, WordBreakProperty.ALetter], - [/*start*/ 0x16F9, WordBreakProperty.Other], - [/*start*/ 0x1700, WordBreakProperty.ALetter], - [/*start*/ 0x170D, WordBreakProperty.Other], - [/*start*/ 0x170E, WordBreakProperty.ALetter], - [/*start*/ 0x1712, WordBreakProperty.Extend], - [/*start*/ 0x1715, WordBreakProperty.Other], - [/*start*/ 0x1720, WordBreakProperty.ALetter], - [/*start*/ 0x1732, WordBreakProperty.Extend], - [/*start*/ 0x1735, WordBreakProperty.Other], - [/*start*/ 0x1740, WordBreakProperty.ALetter], - [/*start*/ 0x1752, WordBreakProperty.Extend], - [/*start*/ 0x1754, WordBreakProperty.Other], - [/*start*/ 0x1760, WordBreakProperty.ALetter], - [/*start*/ 0x176D, WordBreakProperty.Other], - [/*start*/ 0x176E, WordBreakProperty.ALetter], - [/*start*/ 0x1771, WordBreakProperty.Other], - [/*start*/ 0x1772, WordBreakProperty.Extend], - [/*start*/ 0x1774, WordBreakProperty.Other], - [/*start*/ 0x17B4, WordBreakProperty.Extend], - [/*start*/ 0x17D4, WordBreakProperty.Other], - [/*start*/ 0x17DD, WordBreakProperty.Extend], - [/*start*/ 0x17DE, WordBreakProperty.Other], - [/*start*/ 0x17E0, WordBreakProperty.Numeric], - [/*start*/ 0x17EA, WordBreakProperty.Other], - [/*start*/ 0x180B, WordBreakProperty.Extend], - [/*start*/ 0x180E, WordBreakProperty.Format], - [/*start*/ 0x180F, WordBreakProperty.Other], - [/*start*/ 0x1810, WordBreakProperty.Numeric], - [/*start*/ 0x181A, WordBreakProperty.Other], - [/*start*/ 0x1820, WordBreakProperty.ALetter], - [/*start*/ 0x1879, WordBreakProperty.Other], - [/*start*/ 0x1880, WordBreakProperty.ALetter], - [/*start*/ 0x1885, WordBreakProperty.Extend], - [/*start*/ 0x1887, WordBreakProperty.ALetter], - [/*start*/ 0x18A9, WordBreakProperty.Extend], - [/*start*/ 0x18AA, WordBreakProperty.ALetter], - [/*start*/ 0x18AB, WordBreakProperty.Other], - [/*start*/ 0x18B0, WordBreakProperty.ALetter], - [/*start*/ 0x18F6, WordBreakProperty.Other], - [/*start*/ 0x1900, WordBreakProperty.ALetter], - [/*start*/ 0x191F, WordBreakProperty.Other], - [/*start*/ 0x1920, WordBreakProperty.Extend], - [/*start*/ 0x192C, WordBreakProperty.Other], - [/*start*/ 0x1930, WordBreakProperty.Extend], - [/*start*/ 0x193C, WordBreakProperty.Other], - [/*start*/ 0x1946, WordBreakProperty.Numeric], - [/*start*/ 0x1950, WordBreakProperty.Other], - [/*start*/ 0x19D0, WordBreakProperty.Numeric], - [/*start*/ 0x19DA, WordBreakProperty.Other], - [/*start*/ 0x1A00, WordBreakProperty.ALetter], - [/*start*/ 0x1A17, WordBreakProperty.Extend], - [/*start*/ 0x1A1C, WordBreakProperty.Other], - [/*start*/ 0x1A55, WordBreakProperty.Extend], - [/*start*/ 0x1A5F, WordBreakProperty.Other], - [/*start*/ 0x1A60, WordBreakProperty.Extend], - [/*start*/ 0x1A7D, WordBreakProperty.Other], - [/*start*/ 0x1A7F, WordBreakProperty.Extend], - [/*start*/ 0x1A80, WordBreakProperty.Numeric], - [/*start*/ 0x1A8A, WordBreakProperty.Other], - [/*start*/ 0x1A90, WordBreakProperty.Numeric], - [/*start*/ 0x1A9A, WordBreakProperty.Other], - [/*start*/ 0x1AB0, WordBreakProperty.Extend], - [/*start*/ 0x1AC1, WordBreakProperty.Other], - [/*start*/ 0x1B00, WordBreakProperty.Extend], - [/*start*/ 0x1B05, WordBreakProperty.ALetter], - [/*start*/ 0x1B34, WordBreakProperty.Extend], - [/*start*/ 0x1B45, WordBreakProperty.ALetter], - [/*start*/ 0x1B4C, WordBreakProperty.Other], - [/*start*/ 0x1B50, WordBreakProperty.Numeric], - [/*start*/ 0x1B5A, WordBreakProperty.Other], - [/*start*/ 0x1B6B, WordBreakProperty.Extend], - [/*start*/ 0x1B74, WordBreakProperty.Other], - [/*start*/ 0x1B80, WordBreakProperty.Extend], - [/*start*/ 0x1B83, WordBreakProperty.ALetter], - [/*start*/ 0x1BA1, WordBreakProperty.Extend], - [/*start*/ 0x1BAE, WordBreakProperty.ALetter], - [/*start*/ 0x1BB0, WordBreakProperty.Numeric], - [/*start*/ 0x1BBA, WordBreakProperty.ALetter], - [/*start*/ 0x1BE6, WordBreakProperty.Extend], - [/*start*/ 0x1BF4, WordBreakProperty.Other], - [/*start*/ 0x1C00, WordBreakProperty.ALetter], - [/*start*/ 0x1C24, WordBreakProperty.Extend], - [/*start*/ 0x1C38, WordBreakProperty.Other], - [/*start*/ 0x1C40, WordBreakProperty.Numeric], - [/*start*/ 0x1C4A, WordBreakProperty.Other], - [/*start*/ 0x1C4D, WordBreakProperty.ALetter], - [/*start*/ 0x1C50, WordBreakProperty.Numeric], - [/*start*/ 0x1C5A, WordBreakProperty.ALetter], - [/*start*/ 0x1C7E, WordBreakProperty.Other], - [/*start*/ 0x1C80, WordBreakProperty.ALetter], - [/*start*/ 0x1C89, WordBreakProperty.Other], - [/*start*/ 0x1C90, WordBreakProperty.ALetter], - [/*start*/ 0x1CBB, WordBreakProperty.Other], - [/*start*/ 0x1CBD, WordBreakProperty.ALetter], - [/*start*/ 0x1CC0, WordBreakProperty.Other], - [/*start*/ 0x1CD0, WordBreakProperty.Extend], - [/*start*/ 0x1CD3, WordBreakProperty.Other], - [/*start*/ 0x1CD4, WordBreakProperty.Extend], - [/*start*/ 0x1CE9, WordBreakProperty.ALetter], - [/*start*/ 0x1CED, WordBreakProperty.Extend], - [/*start*/ 0x1CEE, WordBreakProperty.ALetter], - [/*start*/ 0x1CF4, WordBreakProperty.Extend], - [/*start*/ 0x1CF5, WordBreakProperty.ALetter], - [/*start*/ 0x1CF7, WordBreakProperty.Extend], - [/*start*/ 0x1CFA, WordBreakProperty.ALetter], - [/*start*/ 0x1CFB, WordBreakProperty.Other], - [/*start*/ 0x1D00, WordBreakProperty.ALetter], - [/*start*/ 0x1DC0, WordBreakProperty.Extend], - [/*start*/ 0x1DFA, WordBreakProperty.Other], - [/*start*/ 0x1DFB, WordBreakProperty.Extend], - [/*start*/ 0x1E00, WordBreakProperty.ALetter], - [/*start*/ 0x1F16, WordBreakProperty.Other], - [/*start*/ 0x1F18, WordBreakProperty.ALetter], - [/*start*/ 0x1F1E, WordBreakProperty.Other], - [/*start*/ 0x1F20, WordBreakProperty.ALetter], - [/*start*/ 0x1F46, WordBreakProperty.Other], - [/*start*/ 0x1F48, WordBreakProperty.ALetter], - [/*start*/ 0x1F4E, WordBreakProperty.Other], - [/*start*/ 0x1F50, WordBreakProperty.ALetter], - [/*start*/ 0x1F58, WordBreakProperty.Other], - [/*start*/ 0x1F59, WordBreakProperty.ALetter], - [/*start*/ 0x1F5A, WordBreakProperty.Other], - [/*start*/ 0x1F5B, WordBreakProperty.ALetter], - [/*start*/ 0x1F5C, WordBreakProperty.Other], - [/*start*/ 0x1F5D, WordBreakProperty.ALetter], - [/*start*/ 0x1F5E, WordBreakProperty.Other], - [/*start*/ 0x1F5F, WordBreakProperty.ALetter], - [/*start*/ 0x1F7E, WordBreakProperty.Other], - [/*start*/ 0x1F80, WordBreakProperty.ALetter], - [/*start*/ 0x1FB5, WordBreakProperty.Other], - [/*start*/ 0x1FB6, WordBreakProperty.ALetter], - [/*start*/ 0x1FBD, WordBreakProperty.Other], - [/*start*/ 0x1FBE, WordBreakProperty.ALetter], - [/*start*/ 0x1FBF, WordBreakProperty.Other], - [/*start*/ 0x1FC2, WordBreakProperty.ALetter], - [/*start*/ 0x1FC5, WordBreakProperty.Other], - [/*start*/ 0x1FC6, WordBreakProperty.ALetter], - [/*start*/ 0x1FCD, WordBreakProperty.Other], - [/*start*/ 0x1FD0, WordBreakProperty.ALetter], - [/*start*/ 0x1FD4, WordBreakProperty.Other], - [/*start*/ 0x1FD6, WordBreakProperty.ALetter], - [/*start*/ 0x1FDC, WordBreakProperty.Other], - [/*start*/ 0x1FE0, WordBreakProperty.ALetter], - [/*start*/ 0x1FED, WordBreakProperty.Other], - [/*start*/ 0x1FF2, WordBreakProperty.ALetter], - [/*start*/ 0x1FF5, WordBreakProperty.Other], - [/*start*/ 0x1FF6, WordBreakProperty.ALetter], - [/*start*/ 0x1FFD, WordBreakProperty.Other], - [/*start*/ 0x2000, WordBreakProperty.WSegSpace], - [/*start*/ 0x2007, WordBreakProperty.Other], - [/*start*/ 0x2008, WordBreakProperty.WSegSpace], - [/*start*/ 0x200B, WordBreakProperty.Other], - [/*start*/ 0x200C, WordBreakProperty.Extend], - [/*start*/ 0x200D, WordBreakProperty.ZWJ], - [/*start*/ 0x200E, WordBreakProperty.Format], - [/*start*/ 0x2010, WordBreakProperty.Other], - [/*start*/ 0x2018, WordBreakProperty.MidNumLet], - [/*start*/ 0x201A, WordBreakProperty.Other], - [/*start*/ 0x2024, WordBreakProperty.MidNumLet], - [/*start*/ 0x2025, WordBreakProperty.Other], - [/*start*/ 0x2027, WordBreakProperty.MidLetter], - [/*start*/ 0x2028, WordBreakProperty.Newline], - [/*start*/ 0x202A, WordBreakProperty.Format], - [/*start*/ 0x202F, WordBreakProperty.ExtendNumLet], - [/*start*/ 0x2030, WordBreakProperty.Other], - [/*start*/ 0x203F, WordBreakProperty.ExtendNumLet], - [/*start*/ 0x2041, WordBreakProperty.Other], - [/*start*/ 0x2044, WordBreakProperty.MidNum], - [/*start*/ 0x2045, WordBreakProperty.Other], - [/*start*/ 0x2054, WordBreakProperty.ExtendNumLet], - [/*start*/ 0x2055, WordBreakProperty.Other], - [/*start*/ 0x205F, WordBreakProperty.WSegSpace], - [/*start*/ 0x2060, WordBreakProperty.Format], - [/*start*/ 0x2065, WordBreakProperty.Other], - [/*start*/ 0x2066, WordBreakProperty.Format], - [/*start*/ 0x2070, WordBreakProperty.Other], - [/*start*/ 0x2071, WordBreakProperty.ALetter], - [/*start*/ 0x2072, WordBreakProperty.Other], - [/*start*/ 0x207F, WordBreakProperty.ALetter], - [/*start*/ 0x2080, WordBreakProperty.Other], - [/*start*/ 0x2090, WordBreakProperty.ALetter], - [/*start*/ 0x209D, WordBreakProperty.Other], - [/*start*/ 0x20D0, WordBreakProperty.Extend], - [/*start*/ 0x20F1, WordBreakProperty.Other], - [/*start*/ 0x2102, WordBreakProperty.ALetter], - [/*start*/ 0x2103, WordBreakProperty.Other], - [/*start*/ 0x2107, WordBreakProperty.ALetter], - [/*start*/ 0x2108, WordBreakProperty.Other], - [/*start*/ 0x210A, WordBreakProperty.ALetter], - [/*start*/ 0x2114, WordBreakProperty.Other], - [/*start*/ 0x2115, WordBreakProperty.ALetter], - [/*start*/ 0x2116, WordBreakProperty.Other], - [/*start*/ 0x2119, WordBreakProperty.ALetter], - [/*start*/ 0x211E, WordBreakProperty.Other], - [/*start*/ 0x2124, WordBreakProperty.ALetter], - [/*start*/ 0x2125, WordBreakProperty.Other], - [/*start*/ 0x2126, WordBreakProperty.ALetter], - [/*start*/ 0x2127, WordBreakProperty.Other], - [/*start*/ 0x2128, WordBreakProperty.ALetter], - [/*start*/ 0x2129, WordBreakProperty.Other], - [/*start*/ 0x212A, WordBreakProperty.ALetter], - [/*start*/ 0x212E, WordBreakProperty.Other], - [/*start*/ 0x212F, WordBreakProperty.ALetter], - [/*start*/ 0x213A, WordBreakProperty.Other], - [/*start*/ 0x213C, WordBreakProperty.ALetter], - [/*start*/ 0x2140, WordBreakProperty.Other], - [/*start*/ 0x2145, WordBreakProperty.ALetter], - [/*start*/ 0x214A, WordBreakProperty.Other], - [/*start*/ 0x214E, WordBreakProperty.ALetter], - [/*start*/ 0x214F, WordBreakProperty.Other], - [/*start*/ 0x2160, WordBreakProperty.ALetter], - [/*start*/ 0x2189, WordBreakProperty.Other], - [/*start*/ 0x24B6, WordBreakProperty.ALetter], - [/*start*/ 0x24EA, WordBreakProperty.Other], - [/*start*/ 0x2C00, WordBreakProperty.ALetter], - [/*start*/ 0x2C2F, WordBreakProperty.Other], - [/*start*/ 0x2C30, WordBreakProperty.ALetter], - [/*start*/ 0x2C5F, WordBreakProperty.Other], - [/*start*/ 0x2C60, WordBreakProperty.ALetter], - [/*start*/ 0x2CE5, WordBreakProperty.Other], - [/*start*/ 0x2CEB, WordBreakProperty.ALetter], - [/*start*/ 0x2CEF, WordBreakProperty.Extend], - [/*start*/ 0x2CF2, WordBreakProperty.ALetter], - [/*start*/ 0x2CF4, WordBreakProperty.Other], - [/*start*/ 0x2D00, WordBreakProperty.ALetter], - [/*start*/ 0x2D26, WordBreakProperty.Other], - [/*start*/ 0x2D27, WordBreakProperty.ALetter], - [/*start*/ 0x2D28, WordBreakProperty.Other], - [/*start*/ 0x2D2D, WordBreakProperty.ALetter], - [/*start*/ 0x2D2E, WordBreakProperty.Other], - [/*start*/ 0x2D30, WordBreakProperty.ALetter], - [/*start*/ 0x2D68, WordBreakProperty.Other], - [/*start*/ 0x2D6F, WordBreakProperty.ALetter], - [/*start*/ 0x2D70, WordBreakProperty.Other], - [/*start*/ 0x2D7F, WordBreakProperty.Extend], - [/*start*/ 0x2D80, WordBreakProperty.ALetter], - [/*start*/ 0x2D97, WordBreakProperty.Other], - [/*start*/ 0x2DA0, WordBreakProperty.ALetter], - [/*start*/ 0x2DA7, WordBreakProperty.Other], - [/*start*/ 0x2DA8, WordBreakProperty.ALetter], - [/*start*/ 0x2DAF, WordBreakProperty.Other], - [/*start*/ 0x2DB0, WordBreakProperty.ALetter], - [/*start*/ 0x2DB7, WordBreakProperty.Other], - [/*start*/ 0x2DB8, WordBreakProperty.ALetter], - [/*start*/ 0x2DBF, WordBreakProperty.Other], - [/*start*/ 0x2DC0, WordBreakProperty.ALetter], - [/*start*/ 0x2DC7, WordBreakProperty.Other], - [/*start*/ 0x2DC8, WordBreakProperty.ALetter], - [/*start*/ 0x2DCF, WordBreakProperty.Other], - [/*start*/ 0x2DD0, WordBreakProperty.ALetter], - [/*start*/ 0x2DD7, WordBreakProperty.Other], - [/*start*/ 0x2DD8, WordBreakProperty.ALetter], - [/*start*/ 0x2DDF, WordBreakProperty.Other], - [/*start*/ 0x2DE0, WordBreakProperty.Extend], - [/*start*/ 0x2E00, WordBreakProperty.Other], - [/*start*/ 0x2E2F, WordBreakProperty.ALetter], - [/*start*/ 0x2E30, WordBreakProperty.Other], - [/*start*/ 0x3000, WordBreakProperty.WSegSpace], - [/*start*/ 0x3001, WordBreakProperty.Other], - [/*start*/ 0x3005, WordBreakProperty.ALetter], - [/*start*/ 0x3006, WordBreakProperty.Other], - [/*start*/ 0x302A, WordBreakProperty.Extend], - [/*start*/ 0x3030, WordBreakProperty.Other], - [/*start*/ 0x3031, WordBreakProperty.Katakana], - [/*start*/ 0x3036, WordBreakProperty.Other], - [/*start*/ 0x303B, WordBreakProperty.ALetter], - [/*start*/ 0x303D, WordBreakProperty.Other], - [/*start*/ 0x3099, WordBreakProperty.Extend], - [/*start*/ 0x309B, WordBreakProperty.Katakana], - [/*start*/ 0x309D, WordBreakProperty.Other], - [/*start*/ 0x30A0, WordBreakProperty.Katakana], - [/*start*/ 0x30FB, WordBreakProperty.Other], - [/*start*/ 0x30FC, WordBreakProperty.Katakana], - [/*start*/ 0x3100, WordBreakProperty.Other], - [/*start*/ 0x3105, WordBreakProperty.ALetter], - [/*start*/ 0x3130, WordBreakProperty.Other], - [/*start*/ 0x3131, WordBreakProperty.ALetter], - [/*start*/ 0x318F, WordBreakProperty.Other], - [/*start*/ 0x31A0, WordBreakProperty.ALetter], - [/*start*/ 0x31C0, WordBreakProperty.Other], - [/*start*/ 0x31F0, WordBreakProperty.Katakana], - [/*start*/ 0x3200, WordBreakProperty.Other], - [/*start*/ 0x32D0, WordBreakProperty.Katakana], - [/*start*/ 0x32FF, WordBreakProperty.Other], - [/*start*/ 0x3300, WordBreakProperty.Katakana], - [/*start*/ 0x3358, WordBreakProperty.Other], - [/*start*/ 0xA000, WordBreakProperty.ALetter], - [/*start*/ 0xA48D, WordBreakProperty.Other], - [/*start*/ 0xA4D0, WordBreakProperty.ALetter], - [/*start*/ 0xA4FE, WordBreakProperty.Other], - [/*start*/ 0xA500, WordBreakProperty.ALetter], - [/*start*/ 0xA60D, WordBreakProperty.Other], - [/*start*/ 0xA610, WordBreakProperty.ALetter], - [/*start*/ 0xA620, WordBreakProperty.Numeric], - [/*start*/ 0xA62A, WordBreakProperty.ALetter], - [/*start*/ 0xA62C, WordBreakProperty.Other], - [/*start*/ 0xA640, WordBreakProperty.ALetter], - [/*start*/ 0xA66F, WordBreakProperty.Extend], - [/*start*/ 0xA673, WordBreakProperty.Other], - [/*start*/ 0xA674, WordBreakProperty.Extend], - [/*start*/ 0xA67E, WordBreakProperty.Other], - [/*start*/ 0xA67F, WordBreakProperty.ALetter], - [/*start*/ 0xA69E, WordBreakProperty.Extend], - [/*start*/ 0xA6A0, WordBreakProperty.ALetter], - [/*start*/ 0xA6F0, WordBreakProperty.Extend], - [/*start*/ 0xA6F2, WordBreakProperty.Other], - [/*start*/ 0xA708, WordBreakProperty.ALetter], - [/*start*/ 0xA7C0, WordBreakProperty.Other], - [/*start*/ 0xA7C2, WordBreakProperty.ALetter], - [/*start*/ 0xA7CB, WordBreakProperty.Other], - [/*start*/ 0xA7F5, WordBreakProperty.ALetter], - [/*start*/ 0xA802, WordBreakProperty.Extend], - [/*start*/ 0xA803, WordBreakProperty.ALetter], - [/*start*/ 0xA806, WordBreakProperty.Extend], - [/*start*/ 0xA807, WordBreakProperty.ALetter], - [/*start*/ 0xA80B, WordBreakProperty.Extend], - [/*start*/ 0xA80C, WordBreakProperty.ALetter], - [/*start*/ 0xA823, WordBreakProperty.Extend], - [/*start*/ 0xA828, WordBreakProperty.Other], - [/*start*/ 0xA82C, WordBreakProperty.Extend], - [/*start*/ 0xA82D, WordBreakProperty.Other], - [/*start*/ 0xA840, WordBreakProperty.ALetter], - [/*start*/ 0xA874, WordBreakProperty.Other], - [/*start*/ 0xA880, WordBreakProperty.Extend], - [/*start*/ 0xA882, WordBreakProperty.ALetter], - [/*start*/ 0xA8B4, WordBreakProperty.Extend], - [/*start*/ 0xA8C6, WordBreakProperty.Other], - [/*start*/ 0xA8D0, WordBreakProperty.Numeric], - [/*start*/ 0xA8DA, WordBreakProperty.Other], - [/*start*/ 0xA8E0, WordBreakProperty.Extend], - [/*start*/ 0xA8F2, WordBreakProperty.ALetter], - [/*start*/ 0xA8F8, WordBreakProperty.Other], - [/*start*/ 0xA8FB, WordBreakProperty.ALetter], - [/*start*/ 0xA8FC, WordBreakProperty.Other], - [/*start*/ 0xA8FD, WordBreakProperty.ALetter], - [/*start*/ 0xA8FF, WordBreakProperty.Extend], - [/*start*/ 0xA900, WordBreakProperty.Numeric], - [/*start*/ 0xA90A, WordBreakProperty.ALetter], - [/*start*/ 0xA926, WordBreakProperty.Extend], - [/*start*/ 0xA92E, WordBreakProperty.Other], - [/*start*/ 0xA930, WordBreakProperty.ALetter], - [/*start*/ 0xA947, WordBreakProperty.Extend], - [/*start*/ 0xA954, WordBreakProperty.Other], - [/*start*/ 0xA960, WordBreakProperty.ALetter], - [/*start*/ 0xA97D, WordBreakProperty.Other], - [/*start*/ 0xA980, WordBreakProperty.Extend], - [/*start*/ 0xA984, WordBreakProperty.ALetter], - [/*start*/ 0xA9B3, WordBreakProperty.Extend], - [/*start*/ 0xA9C1, WordBreakProperty.Other], - [/*start*/ 0xA9CF, WordBreakProperty.ALetter], - [/*start*/ 0xA9D0, WordBreakProperty.Numeric], - [/*start*/ 0xA9DA, WordBreakProperty.Other], - [/*start*/ 0xA9E5, WordBreakProperty.Extend], - [/*start*/ 0xA9E6, WordBreakProperty.Other], - [/*start*/ 0xA9F0, WordBreakProperty.Numeric], - [/*start*/ 0xA9FA, WordBreakProperty.Other], - [/*start*/ 0xAA00, WordBreakProperty.ALetter], - [/*start*/ 0xAA29, WordBreakProperty.Extend], - [/*start*/ 0xAA37, WordBreakProperty.Other], - [/*start*/ 0xAA40, WordBreakProperty.ALetter], - [/*start*/ 0xAA43, WordBreakProperty.Extend], - [/*start*/ 0xAA44, WordBreakProperty.ALetter], - [/*start*/ 0xAA4C, WordBreakProperty.Extend], - [/*start*/ 0xAA4E, WordBreakProperty.Other], - [/*start*/ 0xAA50, WordBreakProperty.Numeric], - [/*start*/ 0xAA5A, WordBreakProperty.Other], - [/*start*/ 0xAA7B, WordBreakProperty.Extend], - [/*start*/ 0xAA7E, WordBreakProperty.Other], - [/*start*/ 0xAAB0, WordBreakProperty.Extend], - [/*start*/ 0xAAB1, WordBreakProperty.Other], - [/*start*/ 0xAAB2, WordBreakProperty.Extend], - [/*start*/ 0xAAB5, WordBreakProperty.Other], - [/*start*/ 0xAAB7, WordBreakProperty.Extend], - [/*start*/ 0xAAB9, WordBreakProperty.Other], - [/*start*/ 0xAABE, WordBreakProperty.Extend], - [/*start*/ 0xAAC0, WordBreakProperty.Other], - [/*start*/ 0xAAC1, WordBreakProperty.Extend], - [/*start*/ 0xAAC2, WordBreakProperty.Other], - [/*start*/ 0xAAE0, WordBreakProperty.ALetter], - [/*start*/ 0xAAEB, WordBreakProperty.Extend], - [/*start*/ 0xAAF0, WordBreakProperty.Other], - [/*start*/ 0xAAF2, WordBreakProperty.ALetter], - [/*start*/ 0xAAF5, WordBreakProperty.Extend], - [/*start*/ 0xAAF7, WordBreakProperty.Other], - [/*start*/ 0xAB01, WordBreakProperty.ALetter], - [/*start*/ 0xAB07, WordBreakProperty.Other], - [/*start*/ 0xAB09, WordBreakProperty.ALetter], - [/*start*/ 0xAB0F, WordBreakProperty.Other], - [/*start*/ 0xAB11, WordBreakProperty.ALetter], - [/*start*/ 0xAB17, WordBreakProperty.Other], - [/*start*/ 0xAB20, WordBreakProperty.ALetter], - [/*start*/ 0xAB27, WordBreakProperty.Other], - [/*start*/ 0xAB28, WordBreakProperty.ALetter], - [/*start*/ 0xAB2F, WordBreakProperty.Other], - [/*start*/ 0xAB30, WordBreakProperty.ALetter], - [/*start*/ 0xAB6A, WordBreakProperty.Other], - [/*start*/ 0xAB70, WordBreakProperty.ALetter], - [/*start*/ 0xABE3, WordBreakProperty.Extend], - [/*start*/ 0xABEB, WordBreakProperty.Other], - [/*start*/ 0xABEC, WordBreakProperty.Extend], - [/*start*/ 0xABEE, WordBreakProperty.Other], - [/*start*/ 0xABF0, WordBreakProperty.Numeric], - [/*start*/ 0xABFA, WordBreakProperty.Other], - [/*start*/ 0xAC00, WordBreakProperty.ALetter], - [/*start*/ 0xD7A4, WordBreakProperty.Other], - [/*start*/ 0xD7B0, WordBreakProperty.ALetter], - [/*start*/ 0xD7C7, WordBreakProperty.Other], - [/*start*/ 0xD7CB, WordBreakProperty.ALetter], - [/*start*/ 0xD7FC, WordBreakProperty.Other], - [/*start*/ 0xFB00, WordBreakProperty.ALetter], - [/*start*/ 0xFB07, WordBreakProperty.Other], - [/*start*/ 0xFB13, WordBreakProperty.ALetter], - [/*start*/ 0xFB18, WordBreakProperty.Other], - [/*start*/ 0xFB1D, WordBreakProperty.Hebrew_Letter], - [/*start*/ 0xFB1E, WordBreakProperty.Extend], - [/*start*/ 0xFB1F, WordBreakProperty.Hebrew_Letter], - [/*start*/ 0xFB29, WordBreakProperty.Other], - [/*start*/ 0xFB2A, WordBreakProperty.Hebrew_Letter], - [/*start*/ 0xFB37, WordBreakProperty.Other], - [/*start*/ 0xFB38, WordBreakProperty.Hebrew_Letter], - [/*start*/ 0xFB3D, WordBreakProperty.Other], - [/*start*/ 0xFB3E, WordBreakProperty.Hebrew_Letter], - [/*start*/ 0xFB3F, WordBreakProperty.Other], - [/*start*/ 0xFB40, WordBreakProperty.Hebrew_Letter], - [/*start*/ 0xFB42, WordBreakProperty.Other], - [/*start*/ 0xFB43, WordBreakProperty.Hebrew_Letter], - [/*start*/ 0xFB45, WordBreakProperty.Other], - [/*start*/ 0xFB46, WordBreakProperty.Hebrew_Letter], - [/*start*/ 0xFB50, WordBreakProperty.ALetter], - [/*start*/ 0xFBB2, WordBreakProperty.Other], - [/*start*/ 0xFBD3, WordBreakProperty.ALetter], - [/*start*/ 0xFD3E, WordBreakProperty.Other], - [/*start*/ 0xFD50, WordBreakProperty.ALetter], - [/*start*/ 0xFD90, WordBreakProperty.Other], - [/*start*/ 0xFD92, WordBreakProperty.ALetter], - [/*start*/ 0xFDC8, WordBreakProperty.Other], - [/*start*/ 0xFDF0, WordBreakProperty.ALetter], - [/*start*/ 0xFDFC, WordBreakProperty.Other], - [/*start*/ 0xFE00, WordBreakProperty.Extend], - [/*start*/ 0xFE10, WordBreakProperty.MidNum], - [/*start*/ 0xFE11, WordBreakProperty.Other], - [/*start*/ 0xFE13, WordBreakProperty.MidLetter], - [/*start*/ 0xFE14, WordBreakProperty.MidNum], - [/*start*/ 0xFE15, WordBreakProperty.Other], - [/*start*/ 0xFE20, WordBreakProperty.Extend], - [/*start*/ 0xFE30, WordBreakProperty.Other], - [/*start*/ 0xFE33, WordBreakProperty.ExtendNumLet], - [/*start*/ 0xFE35, WordBreakProperty.Other], - [/*start*/ 0xFE4D, WordBreakProperty.ExtendNumLet], - [/*start*/ 0xFE50, WordBreakProperty.MidNum], - [/*start*/ 0xFE51, WordBreakProperty.Other], - [/*start*/ 0xFE52, WordBreakProperty.MidNumLet], - [/*start*/ 0xFE53, WordBreakProperty.Other], - [/*start*/ 0xFE54, WordBreakProperty.MidNum], - [/*start*/ 0xFE55, WordBreakProperty.MidLetter], - [/*start*/ 0xFE56, WordBreakProperty.Other], - [/*start*/ 0xFE70, WordBreakProperty.ALetter], - [/*start*/ 0xFE75, WordBreakProperty.Other], - [/*start*/ 0xFE76, WordBreakProperty.ALetter], - [/*start*/ 0xFEFD, WordBreakProperty.Other], - [/*start*/ 0xFEFF, WordBreakProperty.Format], - [/*start*/ 0xFF00, WordBreakProperty.Other], - [/*start*/ 0xFF07, WordBreakProperty.MidNumLet], - [/*start*/ 0xFF08, WordBreakProperty.Other], - [/*start*/ 0xFF0C, WordBreakProperty.MidNum], - [/*start*/ 0xFF0D, WordBreakProperty.Other], - [/*start*/ 0xFF0E, WordBreakProperty.MidNumLet], - [/*start*/ 0xFF0F, WordBreakProperty.Other], - [/*start*/ 0xFF10, WordBreakProperty.Numeric], - [/*start*/ 0xFF1A, WordBreakProperty.MidLetter], - [/*start*/ 0xFF1B, WordBreakProperty.MidNum], - [/*start*/ 0xFF1C, WordBreakProperty.Other], - [/*start*/ 0xFF21, WordBreakProperty.ALetter], - [/*start*/ 0xFF3B, WordBreakProperty.Other], - [/*start*/ 0xFF3F, WordBreakProperty.ExtendNumLet], - [/*start*/ 0xFF40, WordBreakProperty.Other], - [/*start*/ 0xFF41, WordBreakProperty.ALetter], - [/*start*/ 0xFF5B, WordBreakProperty.Other], - [/*start*/ 0xFF66, WordBreakProperty.Katakana], - [/*start*/ 0xFF9E, WordBreakProperty.Extend], - [/*start*/ 0xFFA0, WordBreakProperty.ALetter], - [/*start*/ 0xFFBF, WordBreakProperty.Other], - [/*start*/ 0xFFC2, WordBreakProperty.ALetter], - [/*start*/ 0xFFC8, WordBreakProperty.Other], - [/*start*/ 0xFFCA, WordBreakProperty.ALetter], - [/*start*/ 0xFFD0, WordBreakProperty.Other], - [/*start*/ 0xFFD2, WordBreakProperty.ALetter], - [/*start*/ 0xFFD8, WordBreakProperty.Other], - [/*start*/ 0xFFDA, WordBreakProperty.ALetter], - [/*start*/ 0xFFDD, WordBreakProperty.Other], - [/*start*/ 0xFFF9, WordBreakProperty.Format], - [/*start*/ 0xFFFC, WordBreakProperty.Other], - [/*start*/ 0x10000, WordBreakProperty.ALetter], - [/*start*/ 0x1000C, WordBreakProperty.Other], - [/*start*/ 0x1000D, WordBreakProperty.ALetter], - [/*start*/ 0x10027, WordBreakProperty.Other], - [/*start*/ 0x10028, WordBreakProperty.ALetter], - [/*start*/ 0x1003B, WordBreakProperty.Other], - [/*start*/ 0x1003C, WordBreakProperty.ALetter], - [/*start*/ 0x1003E, WordBreakProperty.Other], - [/*start*/ 0x1003F, WordBreakProperty.ALetter], - [/*start*/ 0x1004E, WordBreakProperty.Other], - [/*start*/ 0x10050, WordBreakProperty.ALetter], - [/*start*/ 0x1005E, WordBreakProperty.Other], - [/*start*/ 0x10080, WordBreakProperty.ALetter], - [/*start*/ 0x100FB, WordBreakProperty.Other], - [/*start*/ 0x10140, WordBreakProperty.ALetter], - [/*start*/ 0x10175, WordBreakProperty.Other], - [/*start*/ 0x101FD, WordBreakProperty.Extend], - [/*start*/ 0x101FE, WordBreakProperty.Other], - [/*start*/ 0x10280, WordBreakProperty.ALetter], - [/*start*/ 0x1029D, WordBreakProperty.Other], - [/*start*/ 0x102A0, WordBreakProperty.ALetter], - [/*start*/ 0x102D1, WordBreakProperty.Other], - [/*start*/ 0x102E0, WordBreakProperty.Extend], - [/*start*/ 0x102E1, WordBreakProperty.Other], - [/*start*/ 0x10300, WordBreakProperty.ALetter], - [/*start*/ 0x10320, WordBreakProperty.Other], - [/*start*/ 0x1032D, WordBreakProperty.ALetter], - [/*start*/ 0x1034B, WordBreakProperty.Other], - [/*start*/ 0x10350, WordBreakProperty.ALetter], - [/*start*/ 0x10376, WordBreakProperty.Extend], - [/*start*/ 0x1037B, WordBreakProperty.Other], - [/*start*/ 0x10380, WordBreakProperty.ALetter], - [/*start*/ 0x1039E, WordBreakProperty.Other], - [/*start*/ 0x103A0, WordBreakProperty.ALetter], - [/*start*/ 0x103C4, WordBreakProperty.Other], - [/*start*/ 0x103C8, WordBreakProperty.ALetter], - [/*start*/ 0x103D0, WordBreakProperty.Other], - [/*start*/ 0x103D1, WordBreakProperty.ALetter], - [/*start*/ 0x103D6, WordBreakProperty.Other], - [/*start*/ 0x10400, WordBreakProperty.ALetter], - [/*start*/ 0x1049E, WordBreakProperty.Other], - [/*start*/ 0x104A0, WordBreakProperty.Numeric], - [/*start*/ 0x104AA, WordBreakProperty.Other], - [/*start*/ 0x104B0, WordBreakProperty.ALetter], - [/*start*/ 0x104D4, WordBreakProperty.Other], - [/*start*/ 0x104D8, WordBreakProperty.ALetter], - [/*start*/ 0x104FC, WordBreakProperty.Other], - [/*start*/ 0x10500, WordBreakProperty.ALetter], - [/*start*/ 0x10528, WordBreakProperty.Other], - [/*start*/ 0x10530, WordBreakProperty.ALetter], - [/*start*/ 0x10564, WordBreakProperty.Other], - [/*start*/ 0x10600, WordBreakProperty.ALetter], - [/*start*/ 0x10737, WordBreakProperty.Other], - [/*start*/ 0x10740, WordBreakProperty.ALetter], - [/*start*/ 0x10756, WordBreakProperty.Other], - [/*start*/ 0x10760, WordBreakProperty.ALetter], - [/*start*/ 0x10768, WordBreakProperty.Other], - [/*start*/ 0x10800, WordBreakProperty.ALetter], - [/*start*/ 0x10806, WordBreakProperty.Other], - [/*start*/ 0x10808, WordBreakProperty.ALetter], - [/*start*/ 0x10809, WordBreakProperty.Other], - [/*start*/ 0x1080A, WordBreakProperty.ALetter], - [/*start*/ 0x10836, WordBreakProperty.Other], - [/*start*/ 0x10837, WordBreakProperty.ALetter], - [/*start*/ 0x10839, WordBreakProperty.Other], - [/*start*/ 0x1083C, WordBreakProperty.ALetter], - [/*start*/ 0x1083D, WordBreakProperty.Other], - [/*start*/ 0x1083F, WordBreakProperty.ALetter], - [/*start*/ 0x10856, WordBreakProperty.Other], - [/*start*/ 0x10860, WordBreakProperty.ALetter], - [/*start*/ 0x10877, WordBreakProperty.Other], - [/*start*/ 0x10880, WordBreakProperty.ALetter], - [/*start*/ 0x1089F, WordBreakProperty.Other], - [/*start*/ 0x108E0, WordBreakProperty.ALetter], - [/*start*/ 0x108F3, WordBreakProperty.Other], - [/*start*/ 0x108F4, WordBreakProperty.ALetter], - [/*start*/ 0x108F6, WordBreakProperty.Other], - [/*start*/ 0x10900, WordBreakProperty.ALetter], - [/*start*/ 0x10916, WordBreakProperty.Other], - [/*start*/ 0x10920, WordBreakProperty.ALetter], - [/*start*/ 0x1093A, WordBreakProperty.Other], - [/*start*/ 0x10980, WordBreakProperty.ALetter], - [/*start*/ 0x109B8, WordBreakProperty.Other], - [/*start*/ 0x109BE, WordBreakProperty.ALetter], - [/*start*/ 0x109C0, WordBreakProperty.Other], - [/*start*/ 0x10A00, WordBreakProperty.ALetter], - [/*start*/ 0x10A01, WordBreakProperty.Extend], - [/*start*/ 0x10A04, WordBreakProperty.Other], - [/*start*/ 0x10A05, WordBreakProperty.Extend], - [/*start*/ 0x10A07, WordBreakProperty.Other], - [/*start*/ 0x10A0C, WordBreakProperty.Extend], - [/*start*/ 0x10A10, WordBreakProperty.ALetter], - [/*start*/ 0x10A14, WordBreakProperty.Other], - [/*start*/ 0x10A15, WordBreakProperty.ALetter], - [/*start*/ 0x10A18, WordBreakProperty.Other], - [/*start*/ 0x10A19, WordBreakProperty.ALetter], - [/*start*/ 0x10A36, WordBreakProperty.Other], - [/*start*/ 0x10A38, WordBreakProperty.Extend], - [/*start*/ 0x10A3B, WordBreakProperty.Other], - [/*start*/ 0x10A3F, WordBreakProperty.Extend], - [/*start*/ 0x10A40, WordBreakProperty.Other], - [/*start*/ 0x10A60, WordBreakProperty.ALetter], - [/*start*/ 0x10A7D, WordBreakProperty.Other], - [/*start*/ 0x10A80, WordBreakProperty.ALetter], - [/*start*/ 0x10A9D, WordBreakProperty.Other], - [/*start*/ 0x10AC0, WordBreakProperty.ALetter], - [/*start*/ 0x10AC8, WordBreakProperty.Other], - [/*start*/ 0x10AC9, WordBreakProperty.ALetter], - [/*start*/ 0x10AE5, WordBreakProperty.Extend], - [/*start*/ 0x10AE7, WordBreakProperty.Other], - [/*start*/ 0x10B00, WordBreakProperty.ALetter], - [/*start*/ 0x10B36, WordBreakProperty.Other], - [/*start*/ 0x10B40, WordBreakProperty.ALetter], - [/*start*/ 0x10B56, WordBreakProperty.Other], - [/*start*/ 0x10B60, WordBreakProperty.ALetter], - [/*start*/ 0x10B73, WordBreakProperty.Other], - [/*start*/ 0x10B80, WordBreakProperty.ALetter], - [/*start*/ 0x10B92, WordBreakProperty.Other], - [/*start*/ 0x10C00, WordBreakProperty.ALetter], - [/*start*/ 0x10C49, WordBreakProperty.Other], - [/*start*/ 0x10C80, WordBreakProperty.ALetter], - [/*start*/ 0x10CB3, WordBreakProperty.Other], - [/*start*/ 0x10CC0, WordBreakProperty.ALetter], - [/*start*/ 0x10CF3, WordBreakProperty.Other], - [/*start*/ 0x10D00, WordBreakProperty.ALetter], - [/*start*/ 0x10D24, WordBreakProperty.Extend], - [/*start*/ 0x10D28, WordBreakProperty.Other], - [/*start*/ 0x10D30, WordBreakProperty.Numeric], - [/*start*/ 0x10D3A, WordBreakProperty.Other], - [/*start*/ 0x10E80, WordBreakProperty.ALetter], - [/*start*/ 0x10EAA, WordBreakProperty.Other], - [/*start*/ 0x10EAB, WordBreakProperty.Extend], - [/*start*/ 0x10EAD, WordBreakProperty.Other], - [/*start*/ 0x10EB0, WordBreakProperty.ALetter], - [/*start*/ 0x10EB2, WordBreakProperty.Other], - [/*start*/ 0x10F00, WordBreakProperty.ALetter], - [/*start*/ 0x10F1D, WordBreakProperty.Other], - [/*start*/ 0x10F27, WordBreakProperty.ALetter], - [/*start*/ 0x10F28, WordBreakProperty.Other], - [/*start*/ 0x10F30, WordBreakProperty.ALetter], - [/*start*/ 0x10F46, WordBreakProperty.Extend], - [/*start*/ 0x10F51, WordBreakProperty.Other], - [/*start*/ 0x10FB0, WordBreakProperty.ALetter], - [/*start*/ 0x10FC5, WordBreakProperty.Other], - [/*start*/ 0x10FE0, WordBreakProperty.ALetter], - [/*start*/ 0x10FF7, WordBreakProperty.Other], - [/*start*/ 0x11000, WordBreakProperty.Extend], - [/*start*/ 0x11003, WordBreakProperty.ALetter], - [/*start*/ 0x11038, WordBreakProperty.Extend], - [/*start*/ 0x11047, WordBreakProperty.Other], - [/*start*/ 0x11066, WordBreakProperty.Numeric], - [/*start*/ 0x11070, WordBreakProperty.Other], - [/*start*/ 0x1107F, WordBreakProperty.Extend], - [/*start*/ 0x11083, WordBreakProperty.ALetter], - [/*start*/ 0x110B0, WordBreakProperty.Extend], - [/*start*/ 0x110BB, WordBreakProperty.Other], - [/*start*/ 0x110BD, WordBreakProperty.Format], - [/*start*/ 0x110BE, WordBreakProperty.Other], - [/*start*/ 0x110CD, WordBreakProperty.Format], - [/*start*/ 0x110CE, WordBreakProperty.Other], - [/*start*/ 0x110D0, WordBreakProperty.ALetter], - [/*start*/ 0x110E9, WordBreakProperty.Other], - [/*start*/ 0x110F0, WordBreakProperty.Numeric], - [/*start*/ 0x110FA, WordBreakProperty.Other], - [/*start*/ 0x11100, WordBreakProperty.Extend], - [/*start*/ 0x11103, WordBreakProperty.ALetter], - [/*start*/ 0x11127, WordBreakProperty.Extend], - [/*start*/ 0x11135, WordBreakProperty.Other], - [/*start*/ 0x11136, WordBreakProperty.Numeric], - [/*start*/ 0x11140, WordBreakProperty.Other], - [/*start*/ 0x11144, WordBreakProperty.ALetter], - [/*start*/ 0x11145, WordBreakProperty.Extend], - [/*start*/ 0x11147, WordBreakProperty.ALetter], - [/*start*/ 0x11148, WordBreakProperty.Other], - [/*start*/ 0x11150, WordBreakProperty.ALetter], - [/*start*/ 0x11173, WordBreakProperty.Extend], - [/*start*/ 0x11174, WordBreakProperty.Other], - [/*start*/ 0x11176, WordBreakProperty.ALetter], - [/*start*/ 0x11177, WordBreakProperty.Other], - [/*start*/ 0x11180, WordBreakProperty.Extend], - [/*start*/ 0x11183, WordBreakProperty.ALetter], - [/*start*/ 0x111B3, WordBreakProperty.Extend], - [/*start*/ 0x111C1, WordBreakProperty.ALetter], - [/*start*/ 0x111C5, WordBreakProperty.Other], - [/*start*/ 0x111C9, WordBreakProperty.Extend], - [/*start*/ 0x111CD, WordBreakProperty.Other], - [/*start*/ 0x111CE, WordBreakProperty.Extend], - [/*start*/ 0x111D0, WordBreakProperty.Numeric], - [/*start*/ 0x111DA, WordBreakProperty.ALetter], - [/*start*/ 0x111DB, WordBreakProperty.Other], - [/*start*/ 0x111DC, WordBreakProperty.ALetter], - [/*start*/ 0x111DD, WordBreakProperty.Other], - [/*start*/ 0x11200, WordBreakProperty.ALetter], - [/*start*/ 0x11212, WordBreakProperty.Other], - [/*start*/ 0x11213, WordBreakProperty.ALetter], - [/*start*/ 0x1122C, WordBreakProperty.Extend], - [/*start*/ 0x11238, WordBreakProperty.Other], - [/*start*/ 0x1123E, WordBreakProperty.Extend], - [/*start*/ 0x1123F, WordBreakProperty.Other], - [/*start*/ 0x11280, WordBreakProperty.ALetter], - [/*start*/ 0x11287, WordBreakProperty.Other], - [/*start*/ 0x11288, WordBreakProperty.ALetter], - [/*start*/ 0x11289, WordBreakProperty.Other], - [/*start*/ 0x1128A, WordBreakProperty.ALetter], - [/*start*/ 0x1128E, WordBreakProperty.Other], - [/*start*/ 0x1128F, WordBreakProperty.ALetter], - [/*start*/ 0x1129E, WordBreakProperty.Other], - [/*start*/ 0x1129F, WordBreakProperty.ALetter], - [/*start*/ 0x112A9, WordBreakProperty.Other], - [/*start*/ 0x112B0, WordBreakProperty.ALetter], - [/*start*/ 0x112DF, WordBreakProperty.Extend], - [/*start*/ 0x112EB, WordBreakProperty.Other], - [/*start*/ 0x112F0, WordBreakProperty.Numeric], - [/*start*/ 0x112FA, WordBreakProperty.Other], - [/*start*/ 0x11300, WordBreakProperty.Extend], - [/*start*/ 0x11304, WordBreakProperty.Other], - [/*start*/ 0x11305, WordBreakProperty.ALetter], - [/*start*/ 0x1130D, WordBreakProperty.Other], - [/*start*/ 0x1130F, WordBreakProperty.ALetter], - [/*start*/ 0x11311, WordBreakProperty.Other], - [/*start*/ 0x11313, WordBreakProperty.ALetter], - [/*start*/ 0x11329, WordBreakProperty.Other], - [/*start*/ 0x1132A, WordBreakProperty.ALetter], - [/*start*/ 0x11331, WordBreakProperty.Other], - [/*start*/ 0x11332, WordBreakProperty.ALetter], - [/*start*/ 0x11334, WordBreakProperty.Other], - [/*start*/ 0x11335, WordBreakProperty.ALetter], - [/*start*/ 0x1133A, WordBreakProperty.Other], - [/*start*/ 0x1133B, WordBreakProperty.Extend], - [/*start*/ 0x1133D, WordBreakProperty.ALetter], - [/*start*/ 0x1133E, WordBreakProperty.Extend], - [/*start*/ 0x11345, WordBreakProperty.Other], - [/*start*/ 0x11347, WordBreakProperty.Extend], - [/*start*/ 0x11349, WordBreakProperty.Other], - [/*start*/ 0x1134B, WordBreakProperty.Extend], - [/*start*/ 0x1134E, WordBreakProperty.Other], - [/*start*/ 0x11350, WordBreakProperty.ALetter], - [/*start*/ 0x11351, WordBreakProperty.Other], - [/*start*/ 0x11357, WordBreakProperty.Extend], - [/*start*/ 0x11358, WordBreakProperty.Other], - [/*start*/ 0x1135D, WordBreakProperty.ALetter], - [/*start*/ 0x11362, WordBreakProperty.Extend], - [/*start*/ 0x11364, WordBreakProperty.Other], - [/*start*/ 0x11366, WordBreakProperty.Extend], - [/*start*/ 0x1136D, WordBreakProperty.Other], - [/*start*/ 0x11370, WordBreakProperty.Extend], - [/*start*/ 0x11375, WordBreakProperty.Other], - [/*start*/ 0x11400, WordBreakProperty.ALetter], - [/*start*/ 0x11435, WordBreakProperty.Extend], - [/*start*/ 0x11447, WordBreakProperty.ALetter], - [/*start*/ 0x1144B, WordBreakProperty.Other], - [/*start*/ 0x11450, WordBreakProperty.Numeric], - [/*start*/ 0x1145A, WordBreakProperty.Other], - [/*start*/ 0x1145E, WordBreakProperty.Extend], - [/*start*/ 0x1145F, WordBreakProperty.ALetter], - [/*start*/ 0x11462, WordBreakProperty.Other], - [/*start*/ 0x11480, WordBreakProperty.ALetter], - [/*start*/ 0x114B0, WordBreakProperty.Extend], - [/*start*/ 0x114C4, WordBreakProperty.ALetter], - [/*start*/ 0x114C6, WordBreakProperty.Other], - [/*start*/ 0x114C7, WordBreakProperty.ALetter], - [/*start*/ 0x114C8, WordBreakProperty.Other], - [/*start*/ 0x114D0, WordBreakProperty.Numeric], - [/*start*/ 0x114DA, WordBreakProperty.Other], - [/*start*/ 0x11580, WordBreakProperty.ALetter], - [/*start*/ 0x115AF, WordBreakProperty.Extend], - [/*start*/ 0x115B6, WordBreakProperty.Other], - [/*start*/ 0x115B8, WordBreakProperty.Extend], - [/*start*/ 0x115C1, WordBreakProperty.Other], - [/*start*/ 0x115D8, WordBreakProperty.ALetter], - [/*start*/ 0x115DC, WordBreakProperty.Extend], - [/*start*/ 0x115DE, WordBreakProperty.Other], - [/*start*/ 0x11600, WordBreakProperty.ALetter], - [/*start*/ 0x11630, WordBreakProperty.Extend], - [/*start*/ 0x11641, WordBreakProperty.Other], - [/*start*/ 0x11644, WordBreakProperty.ALetter], - [/*start*/ 0x11645, WordBreakProperty.Other], - [/*start*/ 0x11650, WordBreakProperty.Numeric], - [/*start*/ 0x1165A, WordBreakProperty.Other], - [/*start*/ 0x11680, WordBreakProperty.ALetter], - [/*start*/ 0x116AB, WordBreakProperty.Extend], - [/*start*/ 0x116B8, WordBreakProperty.ALetter], - [/*start*/ 0x116B9, WordBreakProperty.Other], - [/*start*/ 0x116C0, WordBreakProperty.Numeric], - [/*start*/ 0x116CA, WordBreakProperty.Other], - [/*start*/ 0x1171D, WordBreakProperty.Extend], - [/*start*/ 0x1172C, WordBreakProperty.Other], - [/*start*/ 0x11730, WordBreakProperty.Numeric], - [/*start*/ 0x1173A, WordBreakProperty.Other], - [/*start*/ 0x11800, WordBreakProperty.ALetter], - [/*start*/ 0x1182C, WordBreakProperty.Extend], - [/*start*/ 0x1183B, WordBreakProperty.Other], - [/*start*/ 0x118A0, WordBreakProperty.ALetter], - [/*start*/ 0x118E0, WordBreakProperty.Numeric], - [/*start*/ 0x118EA, WordBreakProperty.Other], - [/*start*/ 0x118FF, WordBreakProperty.ALetter], - [/*start*/ 0x11907, WordBreakProperty.Other], - [/*start*/ 0x11909, WordBreakProperty.ALetter], - [/*start*/ 0x1190A, WordBreakProperty.Other], - [/*start*/ 0x1190C, WordBreakProperty.ALetter], - [/*start*/ 0x11914, WordBreakProperty.Other], - [/*start*/ 0x11915, WordBreakProperty.ALetter], - [/*start*/ 0x11917, WordBreakProperty.Other], - [/*start*/ 0x11918, WordBreakProperty.ALetter], - [/*start*/ 0x11930, WordBreakProperty.Extend], - [/*start*/ 0x11936, WordBreakProperty.Other], - [/*start*/ 0x11937, WordBreakProperty.Extend], - [/*start*/ 0x11939, WordBreakProperty.Other], - [/*start*/ 0x1193B, WordBreakProperty.Extend], - [/*start*/ 0x1193F, WordBreakProperty.ALetter], - [/*start*/ 0x11940, WordBreakProperty.Extend], - [/*start*/ 0x11941, WordBreakProperty.ALetter], - [/*start*/ 0x11942, WordBreakProperty.Extend], - [/*start*/ 0x11944, WordBreakProperty.Other], - [/*start*/ 0x11950, WordBreakProperty.Numeric], - [/*start*/ 0x1195A, WordBreakProperty.Other], - [/*start*/ 0x119A0, WordBreakProperty.ALetter], - [/*start*/ 0x119A8, WordBreakProperty.Other], - [/*start*/ 0x119AA, WordBreakProperty.ALetter], - [/*start*/ 0x119D1, WordBreakProperty.Extend], - [/*start*/ 0x119D8, WordBreakProperty.Other], - [/*start*/ 0x119DA, WordBreakProperty.Extend], - [/*start*/ 0x119E1, WordBreakProperty.ALetter], - [/*start*/ 0x119E2, WordBreakProperty.Other], - [/*start*/ 0x119E3, WordBreakProperty.ALetter], - [/*start*/ 0x119E4, WordBreakProperty.Extend], - [/*start*/ 0x119E5, WordBreakProperty.Other], - [/*start*/ 0x11A00, WordBreakProperty.ALetter], - [/*start*/ 0x11A01, WordBreakProperty.Extend], - [/*start*/ 0x11A0B, WordBreakProperty.ALetter], - [/*start*/ 0x11A33, WordBreakProperty.Extend], - [/*start*/ 0x11A3A, WordBreakProperty.ALetter], - [/*start*/ 0x11A3B, WordBreakProperty.Extend], - [/*start*/ 0x11A3F, WordBreakProperty.Other], - [/*start*/ 0x11A47, WordBreakProperty.Extend], - [/*start*/ 0x11A48, WordBreakProperty.Other], - [/*start*/ 0x11A50, WordBreakProperty.ALetter], - [/*start*/ 0x11A51, WordBreakProperty.Extend], - [/*start*/ 0x11A5C, WordBreakProperty.ALetter], - [/*start*/ 0x11A8A, WordBreakProperty.Extend], - [/*start*/ 0x11A9A, WordBreakProperty.Other], - [/*start*/ 0x11A9D, WordBreakProperty.ALetter], - [/*start*/ 0x11A9E, WordBreakProperty.Other], - [/*start*/ 0x11AC0, WordBreakProperty.ALetter], - [/*start*/ 0x11AF9, WordBreakProperty.Other], - [/*start*/ 0x11C00, WordBreakProperty.ALetter], - [/*start*/ 0x11C09, WordBreakProperty.Other], - [/*start*/ 0x11C0A, WordBreakProperty.ALetter], - [/*start*/ 0x11C2F, WordBreakProperty.Extend], - [/*start*/ 0x11C37, WordBreakProperty.Other], - [/*start*/ 0x11C38, WordBreakProperty.Extend], - [/*start*/ 0x11C40, WordBreakProperty.ALetter], - [/*start*/ 0x11C41, WordBreakProperty.Other], - [/*start*/ 0x11C50, WordBreakProperty.Numeric], - [/*start*/ 0x11C5A, WordBreakProperty.Other], - [/*start*/ 0x11C72, WordBreakProperty.ALetter], - [/*start*/ 0x11C90, WordBreakProperty.Other], - [/*start*/ 0x11C92, WordBreakProperty.Extend], - [/*start*/ 0x11CA8, WordBreakProperty.Other], - [/*start*/ 0x11CA9, WordBreakProperty.Extend], - [/*start*/ 0x11CB7, WordBreakProperty.Other], - [/*start*/ 0x11D00, WordBreakProperty.ALetter], - [/*start*/ 0x11D07, WordBreakProperty.Other], - [/*start*/ 0x11D08, WordBreakProperty.ALetter], - [/*start*/ 0x11D0A, WordBreakProperty.Other], - [/*start*/ 0x11D0B, WordBreakProperty.ALetter], - [/*start*/ 0x11D31, WordBreakProperty.Extend], - [/*start*/ 0x11D37, WordBreakProperty.Other], - [/*start*/ 0x11D3A, WordBreakProperty.Extend], - [/*start*/ 0x11D3B, WordBreakProperty.Other], - [/*start*/ 0x11D3C, WordBreakProperty.Extend], - [/*start*/ 0x11D3E, WordBreakProperty.Other], - [/*start*/ 0x11D3F, WordBreakProperty.Extend], - [/*start*/ 0x11D46, WordBreakProperty.ALetter], - [/*start*/ 0x11D47, WordBreakProperty.Extend], - [/*start*/ 0x11D48, WordBreakProperty.Other], - [/*start*/ 0x11D50, WordBreakProperty.Numeric], - [/*start*/ 0x11D5A, WordBreakProperty.Other], - [/*start*/ 0x11D60, WordBreakProperty.ALetter], - [/*start*/ 0x11D66, WordBreakProperty.Other], - [/*start*/ 0x11D67, WordBreakProperty.ALetter], - [/*start*/ 0x11D69, WordBreakProperty.Other], - [/*start*/ 0x11D6A, WordBreakProperty.ALetter], - [/*start*/ 0x11D8A, WordBreakProperty.Extend], - [/*start*/ 0x11D8F, WordBreakProperty.Other], - [/*start*/ 0x11D90, WordBreakProperty.Extend], - [/*start*/ 0x11D92, WordBreakProperty.Other], - [/*start*/ 0x11D93, WordBreakProperty.Extend], - [/*start*/ 0x11D98, WordBreakProperty.ALetter], - [/*start*/ 0x11D99, WordBreakProperty.Other], - [/*start*/ 0x11DA0, WordBreakProperty.Numeric], - [/*start*/ 0x11DAA, WordBreakProperty.Other], - [/*start*/ 0x11EE0, WordBreakProperty.ALetter], - [/*start*/ 0x11EF3, WordBreakProperty.Extend], - [/*start*/ 0x11EF7, WordBreakProperty.Other], - [/*start*/ 0x11FB0, WordBreakProperty.ALetter], - [/*start*/ 0x11FB1, WordBreakProperty.Other], - [/*start*/ 0x12000, WordBreakProperty.ALetter], - [/*start*/ 0x1239A, WordBreakProperty.Other], - [/*start*/ 0x12400, WordBreakProperty.ALetter], - [/*start*/ 0x1246F, WordBreakProperty.Other], - [/*start*/ 0x12480, WordBreakProperty.ALetter], - [/*start*/ 0x12544, WordBreakProperty.Other], - [/*start*/ 0x13000, WordBreakProperty.ALetter], - [/*start*/ 0x1342F, WordBreakProperty.Other], - [/*start*/ 0x13430, WordBreakProperty.Format], - [/*start*/ 0x13439, WordBreakProperty.Other], - [/*start*/ 0x14400, WordBreakProperty.ALetter], - [/*start*/ 0x14647, WordBreakProperty.Other], - [/*start*/ 0x16800, WordBreakProperty.ALetter], - [/*start*/ 0x16A39, WordBreakProperty.Other], - [/*start*/ 0x16A40, WordBreakProperty.ALetter], - [/*start*/ 0x16A5F, WordBreakProperty.Other], - [/*start*/ 0x16A60, WordBreakProperty.Numeric], - [/*start*/ 0x16A6A, WordBreakProperty.Other], - [/*start*/ 0x16AD0, WordBreakProperty.ALetter], - [/*start*/ 0x16AEE, WordBreakProperty.Other], - [/*start*/ 0x16AF0, WordBreakProperty.Extend], - [/*start*/ 0x16AF5, WordBreakProperty.Other], - [/*start*/ 0x16B00, WordBreakProperty.ALetter], - [/*start*/ 0x16B30, WordBreakProperty.Extend], - [/*start*/ 0x16B37, WordBreakProperty.Other], - [/*start*/ 0x16B40, WordBreakProperty.ALetter], - [/*start*/ 0x16B44, WordBreakProperty.Other], - [/*start*/ 0x16B50, WordBreakProperty.Numeric], - [/*start*/ 0x16B5A, WordBreakProperty.Other], - [/*start*/ 0x16B63, WordBreakProperty.ALetter], - [/*start*/ 0x16B78, WordBreakProperty.Other], - [/*start*/ 0x16B7D, WordBreakProperty.ALetter], - [/*start*/ 0x16B90, WordBreakProperty.Other], - [/*start*/ 0x16E40, WordBreakProperty.ALetter], - [/*start*/ 0x16E80, WordBreakProperty.Other], - [/*start*/ 0x16F00, WordBreakProperty.ALetter], - [/*start*/ 0x16F4B, WordBreakProperty.Other], - [/*start*/ 0x16F4F, WordBreakProperty.Extend], - [/*start*/ 0x16F50, WordBreakProperty.ALetter], - [/*start*/ 0x16F51, WordBreakProperty.Extend], - [/*start*/ 0x16F88, WordBreakProperty.Other], - [/*start*/ 0x16F8F, WordBreakProperty.Extend], - [/*start*/ 0x16F93, WordBreakProperty.ALetter], - [/*start*/ 0x16FA0, WordBreakProperty.Other], - [/*start*/ 0x16FE0, WordBreakProperty.ALetter], - [/*start*/ 0x16FE2, WordBreakProperty.Other], - [/*start*/ 0x16FE3, WordBreakProperty.ALetter], - [/*start*/ 0x16FE4, WordBreakProperty.Extend], - [/*start*/ 0x16FE5, WordBreakProperty.Other], - [/*start*/ 0x16FF0, WordBreakProperty.Extend], - [/*start*/ 0x16FF2, WordBreakProperty.Other], - [/*start*/ 0x1B000, WordBreakProperty.Katakana], - [/*start*/ 0x1B001, WordBreakProperty.Other], - [/*start*/ 0x1B164, WordBreakProperty.Katakana], - [/*start*/ 0x1B168, WordBreakProperty.Other], - [/*start*/ 0x1BC00, WordBreakProperty.ALetter], - [/*start*/ 0x1BC6B, WordBreakProperty.Other], - [/*start*/ 0x1BC70, WordBreakProperty.ALetter], - [/*start*/ 0x1BC7D, WordBreakProperty.Other], - [/*start*/ 0x1BC80, WordBreakProperty.ALetter], - [/*start*/ 0x1BC89, WordBreakProperty.Other], - [/*start*/ 0x1BC90, WordBreakProperty.ALetter], - [/*start*/ 0x1BC9A, WordBreakProperty.Other], - [/*start*/ 0x1BC9D, WordBreakProperty.Extend], - [/*start*/ 0x1BC9F, WordBreakProperty.Other], - [/*start*/ 0x1BCA0, WordBreakProperty.Format], - [/*start*/ 0x1BCA4, WordBreakProperty.Other], - [/*start*/ 0x1D165, WordBreakProperty.Extend], - [/*start*/ 0x1D16A, WordBreakProperty.Other], - [/*start*/ 0x1D16D, WordBreakProperty.Extend], - [/*start*/ 0x1D173, WordBreakProperty.Format], - [/*start*/ 0x1D17B, WordBreakProperty.Extend], - [/*start*/ 0x1D183, WordBreakProperty.Other], - [/*start*/ 0x1D185, WordBreakProperty.Extend], - [/*start*/ 0x1D18C, WordBreakProperty.Other], - [/*start*/ 0x1D1AA, WordBreakProperty.Extend], - [/*start*/ 0x1D1AE, WordBreakProperty.Other], - [/*start*/ 0x1D242, WordBreakProperty.Extend], - [/*start*/ 0x1D245, WordBreakProperty.Other], - [/*start*/ 0x1D400, WordBreakProperty.ALetter], - [/*start*/ 0x1D455, WordBreakProperty.Other], - [/*start*/ 0x1D456, WordBreakProperty.ALetter], - [/*start*/ 0x1D49D, WordBreakProperty.Other], - [/*start*/ 0x1D49E, WordBreakProperty.ALetter], - [/*start*/ 0x1D4A0, WordBreakProperty.Other], - [/*start*/ 0x1D4A2, WordBreakProperty.ALetter], - [/*start*/ 0x1D4A3, WordBreakProperty.Other], - [/*start*/ 0x1D4A5, WordBreakProperty.ALetter], - [/*start*/ 0x1D4A7, WordBreakProperty.Other], - [/*start*/ 0x1D4A9, WordBreakProperty.ALetter], - [/*start*/ 0x1D4AD, WordBreakProperty.Other], - [/*start*/ 0x1D4AE, WordBreakProperty.ALetter], - [/*start*/ 0x1D4BA, WordBreakProperty.Other], - [/*start*/ 0x1D4BB, WordBreakProperty.ALetter], - [/*start*/ 0x1D4BC, WordBreakProperty.Other], - [/*start*/ 0x1D4BD, WordBreakProperty.ALetter], - [/*start*/ 0x1D4C4, WordBreakProperty.Other], - [/*start*/ 0x1D4C5, WordBreakProperty.ALetter], - [/*start*/ 0x1D506, WordBreakProperty.Other], - [/*start*/ 0x1D507, WordBreakProperty.ALetter], - [/*start*/ 0x1D50B, WordBreakProperty.Other], - [/*start*/ 0x1D50D, WordBreakProperty.ALetter], - [/*start*/ 0x1D515, WordBreakProperty.Other], - [/*start*/ 0x1D516, WordBreakProperty.ALetter], - [/*start*/ 0x1D51D, WordBreakProperty.Other], - [/*start*/ 0x1D51E, WordBreakProperty.ALetter], - [/*start*/ 0x1D53A, WordBreakProperty.Other], - [/*start*/ 0x1D53B, WordBreakProperty.ALetter], - [/*start*/ 0x1D53F, WordBreakProperty.Other], - [/*start*/ 0x1D540, WordBreakProperty.ALetter], - [/*start*/ 0x1D545, WordBreakProperty.Other], - [/*start*/ 0x1D546, WordBreakProperty.ALetter], - [/*start*/ 0x1D547, WordBreakProperty.Other], - [/*start*/ 0x1D54A, WordBreakProperty.ALetter], - [/*start*/ 0x1D551, WordBreakProperty.Other], - [/*start*/ 0x1D552, WordBreakProperty.ALetter], - [/*start*/ 0x1D6A6, WordBreakProperty.Other], - [/*start*/ 0x1D6A8, WordBreakProperty.ALetter], - [/*start*/ 0x1D6C1, WordBreakProperty.Other], - [/*start*/ 0x1D6C2, WordBreakProperty.ALetter], - [/*start*/ 0x1D6DB, WordBreakProperty.Other], - [/*start*/ 0x1D6DC, WordBreakProperty.ALetter], - [/*start*/ 0x1D6FB, WordBreakProperty.Other], - [/*start*/ 0x1D6FC, WordBreakProperty.ALetter], - [/*start*/ 0x1D715, WordBreakProperty.Other], - [/*start*/ 0x1D716, WordBreakProperty.ALetter], - [/*start*/ 0x1D735, WordBreakProperty.Other], - [/*start*/ 0x1D736, WordBreakProperty.ALetter], - [/*start*/ 0x1D74F, WordBreakProperty.Other], - [/*start*/ 0x1D750, WordBreakProperty.ALetter], - [/*start*/ 0x1D76F, WordBreakProperty.Other], - [/*start*/ 0x1D770, WordBreakProperty.ALetter], - [/*start*/ 0x1D789, WordBreakProperty.Other], - [/*start*/ 0x1D78A, WordBreakProperty.ALetter], - [/*start*/ 0x1D7A9, WordBreakProperty.Other], - [/*start*/ 0x1D7AA, WordBreakProperty.ALetter], - [/*start*/ 0x1D7C3, WordBreakProperty.Other], - [/*start*/ 0x1D7C4, WordBreakProperty.ALetter], - [/*start*/ 0x1D7CC, WordBreakProperty.Other], - [/*start*/ 0x1D7CE, WordBreakProperty.Numeric], - [/*start*/ 0x1D800, WordBreakProperty.Other], - [/*start*/ 0x1DA00, WordBreakProperty.Extend], - [/*start*/ 0x1DA37, WordBreakProperty.Other], - [/*start*/ 0x1DA3B, WordBreakProperty.Extend], - [/*start*/ 0x1DA6D, WordBreakProperty.Other], - [/*start*/ 0x1DA75, WordBreakProperty.Extend], - [/*start*/ 0x1DA76, WordBreakProperty.Other], - [/*start*/ 0x1DA84, WordBreakProperty.Extend], - [/*start*/ 0x1DA85, WordBreakProperty.Other], - [/*start*/ 0x1DA9B, WordBreakProperty.Extend], - [/*start*/ 0x1DAA0, WordBreakProperty.Other], - [/*start*/ 0x1DAA1, WordBreakProperty.Extend], - [/*start*/ 0x1DAB0, WordBreakProperty.Other], - [/*start*/ 0x1E000, WordBreakProperty.Extend], - [/*start*/ 0x1E007, WordBreakProperty.Other], - [/*start*/ 0x1E008, WordBreakProperty.Extend], - [/*start*/ 0x1E019, WordBreakProperty.Other], - [/*start*/ 0x1E01B, WordBreakProperty.Extend], - [/*start*/ 0x1E022, WordBreakProperty.Other], - [/*start*/ 0x1E023, WordBreakProperty.Extend], - [/*start*/ 0x1E025, WordBreakProperty.Other], - [/*start*/ 0x1E026, WordBreakProperty.Extend], - [/*start*/ 0x1E02B, WordBreakProperty.Other], - [/*start*/ 0x1E100, WordBreakProperty.ALetter], - [/*start*/ 0x1E12D, WordBreakProperty.Other], - [/*start*/ 0x1E130, WordBreakProperty.Extend], - [/*start*/ 0x1E137, WordBreakProperty.ALetter], - [/*start*/ 0x1E13E, WordBreakProperty.Other], - [/*start*/ 0x1E140, WordBreakProperty.Numeric], - [/*start*/ 0x1E14A, WordBreakProperty.Other], - [/*start*/ 0x1E14E, WordBreakProperty.ALetter], - [/*start*/ 0x1E14F, WordBreakProperty.Other], - [/*start*/ 0x1E2C0, WordBreakProperty.ALetter], - [/*start*/ 0x1E2EC, WordBreakProperty.Extend], - [/*start*/ 0x1E2F0, WordBreakProperty.Numeric], - [/*start*/ 0x1E2FA, WordBreakProperty.Other], - [/*start*/ 0x1E800, WordBreakProperty.ALetter], - [/*start*/ 0x1E8C5, WordBreakProperty.Other], - [/*start*/ 0x1E8D0, WordBreakProperty.Extend], - [/*start*/ 0x1E8D7, WordBreakProperty.Other], - [/*start*/ 0x1E900, WordBreakProperty.ALetter], - [/*start*/ 0x1E944, WordBreakProperty.Extend], - [/*start*/ 0x1E94B, WordBreakProperty.ALetter], - [/*start*/ 0x1E94C, WordBreakProperty.Other], - [/*start*/ 0x1E950, WordBreakProperty.Numeric], - [/*start*/ 0x1E95A, WordBreakProperty.Other], - [/*start*/ 0x1EE00, WordBreakProperty.ALetter], - [/*start*/ 0x1EE04, WordBreakProperty.Other], - [/*start*/ 0x1EE05, WordBreakProperty.ALetter], - [/*start*/ 0x1EE20, WordBreakProperty.Other], - [/*start*/ 0x1EE21, WordBreakProperty.ALetter], - [/*start*/ 0x1EE23, WordBreakProperty.Other], - [/*start*/ 0x1EE24, WordBreakProperty.ALetter], - [/*start*/ 0x1EE25, WordBreakProperty.Other], - [/*start*/ 0x1EE27, WordBreakProperty.ALetter], - [/*start*/ 0x1EE28, WordBreakProperty.Other], - [/*start*/ 0x1EE29, WordBreakProperty.ALetter], - [/*start*/ 0x1EE33, WordBreakProperty.Other], - [/*start*/ 0x1EE34, WordBreakProperty.ALetter], - [/*start*/ 0x1EE38, WordBreakProperty.Other], - [/*start*/ 0x1EE39, WordBreakProperty.ALetter], - [/*start*/ 0x1EE3A, WordBreakProperty.Other], - [/*start*/ 0x1EE3B, WordBreakProperty.ALetter], - [/*start*/ 0x1EE3C, WordBreakProperty.Other], - [/*start*/ 0x1EE42, WordBreakProperty.ALetter], - [/*start*/ 0x1EE43, WordBreakProperty.Other], - [/*start*/ 0x1EE47, WordBreakProperty.ALetter], - [/*start*/ 0x1EE48, WordBreakProperty.Other], - [/*start*/ 0x1EE49, WordBreakProperty.ALetter], - [/*start*/ 0x1EE4A, WordBreakProperty.Other], - [/*start*/ 0x1EE4B, WordBreakProperty.ALetter], - [/*start*/ 0x1EE4C, WordBreakProperty.Other], - [/*start*/ 0x1EE4D, WordBreakProperty.ALetter], - [/*start*/ 0x1EE50, WordBreakProperty.Other], - [/*start*/ 0x1EE51, WordBreakProperty.ALetter], - [/*start*/ 0x1EE53, WordBreakProperty.Other], - [/*start*/ 0x1EE54, WordBreakProperty.ALetter], - [/*start*/ 0x1EE55, WordBreakProperty.Other], - [/*start*/ 0x1EE57, WordBreakProperty.ALetter], - [/*start*/ 0x1EE58, WordBreakProperty.Other], - [/*start*/ 0x1EE59, WordBreakProperty.ALetter], - [/*start*/ 0x1EE5A, WordBreakProperty.Other], - [/*start*/ 0x1EE5B, WordBreakProperty.ALetter], - [/*start*/ 0x1EE5C, WordBreakProperty.Other], - [/*start*/ 0x1EE5D, WordBreakProperty.ALetter], - [/*start*/ 0x1EE5E, WordBreakProperty.Other], - [/*start*/ 0x1EE5F, WordBreakProperty.ALetter], - [/*start*/ 0x1EE60, WordBreakProperty.Other], - [/*start*/ 0x1EE61, WordBreakProperty.ALetter], - [/*start*/ 0x1EE63, WordBreakProperty.Other], - [/*start*/ 0x1EE64, WordBreakProperty.ALetter], - [/*start*/ 0x1EE65, WordBreakProperty.Other], - [/*start*/ 0x1EE67, WordBreakProperty.ALetter], - [/*start*/ 0x1EE6B, WordBreakProperty.Other], - [/*start*/ 0x1EE6C, WordBreakProperty.ALetter], - [/*start*/ 0x1EE73, WordBreakProperty.Other], - [/*start*/ 0x1EE74, WordBreakProperty.ALetter], - [/*start*/ 0x1EE78, WordBreakProperty.Other], - [/*start*/ 0x1EE79, WordBreakProperty.ALetter], - [/*start*/ 0x1EE7D, WordBreakProperty.Other], - [/*start*/ 0x1EE7E, WordBreakProperty.ALetter], - [/*start*/ 0x1EE7F, WordBreakProperty.Other], - [/*start*/ 0x1EE80, WordBreakProperty.ALetter], - [/*start*/ 0x1EE8A, WordBreakProperty.Other], - [/*start*/ 0x1EE8B, WordBreakProperty.ALetter], - [/*start*/ 0x1EE9C, WordBreakProperty.Other], - [/*start*/ 0x1EEA1, WordBreakProperty.ALetter], - [/*start*/ 0x1EEA4, WordBreakProperty.Other], - [/*start*/ 0x1EEA5, WordBreakProperty.ALetter], - [/*start*/ 0x1EEAA, WordBreakProperty.Other], - [/*start*/ 0x1EEAB, WordBreakProperty.ALetter], - [/*start*/ 0x1EEBC, WordBreakProperty.Other], - [/*start*/ 0x1F130, WordBreakProperty.ALetter], - [/*start*/ 0x1F14A, WordBreakProperty.Other], - [/*start*/ 0x1F150, WordBreakProperty.ALetter], - [/*start*/ 0x1F16A, WordBreakProperty.Other], - [/*start*/ 0x1F170, WordBreakProperty.ALetter], - [/*start*/ 0x1F18A, WordBreakProperty.Other], - [/*start*/ 0x1F1E6, WordBreakProperty.Regional_Indicator], - [/*start*/ 0x1F200, WordBreakProperty.Other], - [/*start*/ 0x1F3FB, WordBreakProperty.Extend], - [/*start*/ 0x1F400, WordBreakProperty.Other], - [/*start*/ 0x1FBF0, WordBreakProperty.Numeric], - [/*start*/ 0x1FBFA, WordBreakProperty.Other], - [/*start*/ 0xE0001, WordBreakProperty.Format], - [/*start*/ 0xE0002, WordBreakProperty.Other], - [/*start*/ 0xE0020, WordBreakProperty.Extend], - [/*start*/ 0xE0080, WordBreakProperty.Other], - [/*start*/ 0xE0100, WordBreakProperty.Extend], - [/*start*/ 0xE01F0, WordBreakProperty.Other], -]; diff --git a/developer/src/kmc-model/test/wordbreakers/default-wordbreaker-esm.ts b/developer/src/kmc-model/test/wordbreakers/default-wordbreaker-esm.ts deleted file mode 100644 index 55c0e560da6..00000000000 --- a/developer/src/kmc-model/test/wordbreakers/default-wordbreaker-esm.ts +++ /dev/null @@ -1,383 +0,0 @@ -// TEMP: esm version of /common/models/wordbreakers/default/index.ts - - import { I, WORD_BREAK_PROPERTY, WordBreakProperty } from './data.js'; - - - /** - * Word breaker based on Unicode Standard Annex #29, Section 4.1: - * Default Word Boundary Specification. - * - * @see http://unicode.org/reports/tr29/#Word_Boundaries - * @see https://github.com/eddieantonio/unicode-default-word-boundary/tree/v12.0.0 - */ - export default function default_(text: string): Span[] { - let boundaries = findBoundaries(text); - if (boundaries.length == 0) { - return []; - } - - // All non-empty strings have at least TWO boundaries: at the start and at the end of - // the string. - let spans = []; - for (let i = 0; i < boundaries.length - 1; i++) { - let start = boundaries[i]; - let end = boundaries[i + 1]; - let span = new LazySpan(text, start, end); - - if (isNonSpace(span.text)) { - spans.push(span); - // Preserve a sequence-final space if it exists. Needed to signal "end of word". - } else if (i == boundaries.length - 2) { // if "we just checked the final boundary"... - // We don't want to return the whitespace itself; the correct token is simply ''. - span = new LazySpan(text, end, end); - spans.push(span); - } - } - return spans; - } - - // Utilities // - // type WordBreakProperty = data.WordBreakProperty; - // const WordBreakProperty = data.WordBreakProperty; - // type I = data.I; - // const I = data.I; - // const WORD_BREAK_PROPERTY = data.WORD_BREAK_PROPERTY; - - /** - * A span that does not cut out the substring until it absolutely has to! - */ - class LazySpan implements Span { - private _source: string; - readonly start: number; - readonly end: number; - constructor(source: string, start: number, end: number) { - this._source = source; - this.start = start; - this.end = end; - } - - get text(): string { - return this._source.substring(this.start, this.end); - } - - get length(): number { - return this.end - this.start; - } - } - - /** - * Returns true when the chunk does not solely consist of whitespace. - * - * @param chunk a chunk of text. Starts and ends at word boundaries. - */ - function isNonSpace(chunk: string): boolean { - return !Array.from(chunk).map(property).every(wb => ( - wb === WordBreakProperty.CR || - wb === WordBreakProperty.LF || - wb === WordBreakProperty.Newline || - wb === WordBreakProperty.WSegSpace - )); - } - - /** - * Yields a series of string indices where a word break should - * occur. That is, there should be a break BEFORE each string - * index yielded by this generator. - * - * @param text Text to find word boundaries in. - */ - function findBoundaries(text: string): number[] { - // WB1 and WB2: no boundaries if given an empty string. - if (text.length === 0) { - // There are no boundaries in an empty string! - return []; - } - - // This algorithm works by maintaining a sliding window of four SCALAR VALUES. - // - // - Scalar values? JavaScript strings are NOT actually a string of - // Unicode code points; some characters are made up of TWO - // JavaScript indices. e.g., - // "💩".length === 2; - // "💩"[0] === '\uD83D'; - // "💩"[1] === '\uDCA9'; - // - // These characters that are represented by TWO indices are - // called "surrogate pairs". Since we don't want to be in the - // "middle" of a character, make sure we're always advancing - // by scalar values, and NOT indices. That means, we sometimes - // need to advance by TWO indices, not just one. - // - Four values? Some rules look at what's to the left of - // left, and some look at what's to the right of right. So - // keep track of this! - - let boundaries = []; - - let rightPos: number; - let lookaheadPos = 0; // lookahead, one scalar value to the right of right. - // Before the start of the string is also the start of the string. - let lookbehind: WordBreakProperty; - let left = WordBreakProperty.sot; - let right = WordBreakProperty.sot; - let lookahead = wordbreakPropertyAt(0); - // Count RIs to make sure we're not splitting emoji flags: - let nConsecutiveRegionalIndicators = 0; - - do { - // Shift all positions, one scalar value to the right. - rightPos = lookaheadPos; - lookaheadPos = positionAfter(lookaheadPos); - // Shift all properties, one scalar value to the right. - [lookbehind, left, right, lookahead] = - [left, right, lookahead, wordbreakPropertyAt(lookaheadPos)]; - - // Break at the start and end of text, unless the text is empty. - // WB1: Break at start of text... - if (left === WordBreakProperty.sot) { - boundaries.push(rightPos); - continue; - } - // WB2: Break at the end of text... - if (right === WordBreakProperty.eot) { - boundaries.push(rightPos); - break; // Reached the end of the string. We're done! - } - // WB3: Do not break within CRLF: - if (left === WordBreakProperty.CR && right === WordBreakProperty.LF) - continue; - // WB3b: Otherwise, break after... - if (left === WordBreakProperty.Newline || - left === WordBreakProperty.CR || - left === WordBreakProperty.LF) { - boundaries.push(rightPos); - continue; - } - // WB3a: ...and before newlines - if (right === WordBreakProperty.Newline || - right === WordBreakProperty.CR || - right === WordBreakProperty.LF) { - boundaries.push(rightPos); - continue; - } - - // TODO: WB3c is not implemented, due to its complex, error-prone - // implementation, requiring a ginormous regexp, and the fact that - // the only thing it does is prevent big emoji sequences from being - // split up, like 🧚🏼‍♂️ - // https://www.unicode.org/Public/emoji/12.0/emoji-zwj-sequences.txt - - // WB3d: Keep horizontal whitespace together - if (left === WordBreakProperty.WSegSpace && right == WordBreakProperty.WSegSpace) - continue; - - // WB4: Ignore format and extend characters - // This is to keep grapheme clusters together! - // See: Section 6.2: https://unicode.org/reports/tr29/#Grapheme_Cluster_and_Format_Rules - // N.B.: The rule about "except after sot, CR, LF, and - // Newline" already been by WB1, WB2, WB3a, and WB3b above. - while (right === WordBreakProperty.Format || - right === WordBreakProperty.Extend || - right === WordBreakProperty.ZWJ) { - // Continue advancing in the string, as if these - // characters do not exist. DO NOT update left and - // lookbehind however! - [rightPos, lookaheadPos] = [lookaheadPos, positionAfter(lookaheadPos)]; - [right, lookahead] = [lookahead, wordbreakPropertyAt(lookaheadPos)]; - } - // In ignoring the characters in the previous loop, we could - // have fallen off the end of the string, so end the loop - // prematurely if that happens! - if (right === WordBreakProperty.eot) { - boundaries.push(rightPos); - break; - } - // WB4 (continued): Lookahead must ALSO ignore these format, - // extend, ZWJ characters! - while (lookahead === WordBreakProperty.Format || - lookahead === WordBreakProperty.Extend || - lookahead === WordBreakProperty.ZWJ) { - // Continue advancing in the string, as if these - // characters do not exist. DO NOT update left and right, - // however! - lookaheadPos = positionAfter(lookaheadPos); - lookahead = wordbreakPropertyAt(lookaheadPos); - } - - // WB5: Do not break between most letters. - if (isAHLetter(left) && isAHLetter(right)) - continue; - // Do not break across certain punctuation - // WB6: (Don't break before apostrophes in contractions) - if (isAHLetter(left) && isAHLetter(lookahead) && - (right === WordBreakProperty.MidLetter || isMidNumLetQ(right))) - continue; - // WB7: (Don't break after apostrophes in contractions) - if (isAHLetter(lookbehind) && isAHLetter(right) && - (left === WordBreakProperty.MidLetter || isMidNumLetQ(left))) - continue; - // WB7a - if (left === WordBreakProperty.Hebrew_Letter && right === WordBreakProperty.Single_Quote) - continue; - // WB7b - if (left === WordBreakProperty.Hebrew_Letter && right === WordBreakProperty.Double_Quote && - lookahead === WordBreakProperty.Hebrew_Letter) - continue; - // WB7c - if (lookbehind === WordBreakProperty.Hebrew_Letter && left === WordBreakProperty.Double_Quote && - right === WordBreakProperty.Hebrew_Letter) - continue; - // Do not break within sequences of digits, or digits adjacent to letters. - // e.g., "3a" or "A3" - // WB8 - if (left === WordBreakProperty.Numeric && right === WordBreakProperty.Numeric) - continue; - // WB9 - if (isAHLetter(left) && right === WordBreakProperty.Numeric) - continue; - // WB10 - if (left === WordBreakProperty.Numeric && isAHLetter(right)) - continue; - // Do not break within sequences, such as 3.2, 3,456.789 - // WB11 - if (lookbehind === WordBreakProperty.Numeric && right === WordBreakProperty.Numeric && - (left === WordBreakProperty.MidNum || isMidNumLetQ(left))) - continue; - // WB12 - if (left === WordBreakProperty.Numeric && lookahead === WordBreakProperty.Numeric && - (right === WordBreakProperty.MidNum || isMidNumLetQ(right))) - continue; - // WB13: Do not break between Katakana - if (left === WordBreakProperty.Katakana && right === WordBreakProperty.Katakana) - continue; - // Do not break from extenders (e.g., U+202F NARROW NO-BREAK SPACE) - // WB13a - if ((isAHLetter(left) || - left === WordBreakProperty.Numeric || - left === WordBreakProperty.Katakana || - left === WordBreakProperty.ExtendNumLet) && - right === WordBreakProperty.ExtendNumLet) - continue; - // WB13b - if ((isAHLetter(right) || - right === WordBreakProperty.Numeric || - right === WordBreakProperty.Katakana) && left === WordBreakProperty.ExtendNumLet) - continue; - - // WB15 & WB16: - // Do not break within emoji flag sequences. That is, do not break between - // regional indicator (RI) symbols if there is an odd number of RI - // characters before the break point. - if (right === WordBreakProperty.Regional_Indicator) { - // Emoji flags are actually composed of TWO scalar values, each being a - // "regional indicator". These indicators correspond to Latin letters. Put - // two of them together, and they spell out an ISO 3166-1-alpha-2 country - // code. Since these always come in pairs, NEVER split the pairs! So, if - // we happen to be inside the middle of an odd numbered of - // Regional_Indicators, DON'T SPLIT IT! - nConsecutiveRegionalIndicators += 1; - if ((nConsecutiveRegionalIndicators % 2) == 1) { - continue; - } - } else { - nConsecutiveRegionalIndicators = 0; - } - // WB999: Otherwise, break EVERYWHERE (including around ideographs) - boundaries.push(rightPos); - } while (rightPos < text.length); - - return boundaries; - - ///// Internal utility functions ///// - - /** - * Returns the position of the start of the next scalar value. This jumps - * over surrogate pairs. - * - * If asked for the character AFTER the end of the string, this always - * returns the length of the string. - */ - function positionAfter(pos: number): number { - if (pos >= text.length) { - return text.length; - } else if (isStartOfSurrogatePair(text[pos])) { - return pos + 2; - } - return pos + 1; - } - - /** - * Return the value of the Word_Break property at the given string index. - * @param pos position in the text. - */ - function wordbreakPropertyAt(pos: number) { - if (pos < 0) { - return WordBreakProperty.sot; // Always "start of string" before the string starts! - } else if (pos >= text.length) { - return WordBreakProperty.eot; // Always "end of string" after the string ends! - } else if (isStartOfSurrogatePair(text[pos])) { - // Surrogate pairs the next TWO items from the string! - return property(text[pos] + text[pos + 1]); - } - return property(text[pos]); - } - - // Word_Break rule macros - // See: https://unicode.org/reports/tr29/#WB_Rule_Macros - function isAHLetter(prop: WordBreakProperty): boolean { - return prop === WordBreakProperty.ALetter || - prop === WordBreakProperty.Hebrew_Letter; - } - - function isMidNumLetQ(prop: WordBreakProperty): boolean { - return prop === WordBreakProperty.MidNumLet || - prop === WordBreakProperty.Single_Quote; - } - } - - function isStartOfSurrogatePair(character: string) { - let codeUnit = character.charCodeAt(0); - return codeUnit >= 0xD800 && codeUnit <= 0xDBFF; - } - - /** - * Return the Word_Break property value for a character. - * Note that - * @param character a scalar value - */ - function property(character: string): WordBreakProperty { - // This MUST be a scalar value. - // TODO: remove dependence on character.codepointAt()? - let codepoint = character.codePointAt(0) as number; - return searchForProperty(codepoint, 0, WORD_BREAK_PROPERTY.length - 1); - } - - /** - * Binary search for the word break property of a given CODE POINT. - * - * The auto-generated data.ts master array defines a **character range** - * lookup table. If a character's codepoint is equal to or greater than - * the I.Start value for an entry and exclusively less than the next entry, - * it falls in the first entry's range bucket and is classified accordingly - * by this method. - */ - function searchForProperty(codePoint: number, left: number, right: number): WordBreakProperty { - // All items that are not found in the array are assigned the 'Other' property. - if (right < left) { - return WordBreakProperty.Other; - } - - let midpoint = left + ~~((right - left) / 2); - let candidate = WORD_BREAK_PROPERTY[midpoint]; - - let nextRange = WORD_BREAK_PROPERTY[midpoint + 1]; - let startOfNextRange = nextRange ? nextRange[I.Start] : Infinity; - - if (codePoint < candidate[I.Start]) { - return searchForProperty(codePoint, left, midpoint - 1); - } else if (codePoint >= startOfNextRange) { - return searchForProperty(codePoint, midpoint + 1, right); - } - - // We found it! - return candidate[I.Value]; - } diff --git a/developer/src/kmc-package/package.json b/developer/src/kmc-package/package.json index 5ae24b58b14..a5f9f3e7936 100644 --- a/developer/src/kmc-package/package.json +++ b/developer/src/kmc-package/package.json @@ -31,7 +31,9 @@ }, "dependencies": { "@keymanapp/common-types": "*", - "jszip": "^3.7.0" + "jszip": "^3.7.0", + "marked": "^7.0.0", + "xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829" }, "devDependencies": { "@keymanapp/developer-test-helpers": "*", diff --git a/developer/src/kmc-package/src/compiler/kmp-compiler.ts b/developer/src/kmc-package/src/compiler/kmp-compiler.ts index dbce6b09578..b26643e1781 100644 --- a/developer/src/kmc-package/src/compiler/kmp-compiler.ts +++ b/developer/src/kmc-package/src/compiler/kmp-compiler.ts @@ -11,16 +11,30 @@ import { transcodeToCP1252 } from './cp1252.js'; import { MIN_LM_FILEVERSION_KMP_JSON, PackageVersionValidator } from './package-version-validator.js'; import { PackageKeyboardTargetValidator } from './package-keyboard-target-validator.js'; import { PackageMetadataUpdater } from './package-metadata-updater.js'; +import { markdownToHTML } from './markdown.js'; const KMP_JSON_FILENAME = 'kmp.json'; const KMP_INF_FILENAME = 'kmp.inf'; +// welcome.htm: this is a legacy filename, as of 17.0 the welcome +// (documentation) filename can be any file, but we will fallback to detecting +// this filename for existing keyboard packages. +const WELCOME_HTM_FILENAME = 'welcome.htm'; + export class KmpCompiler { constructor(private callbacks: CompilerCallbacks) { } public transformKpsToKmpObject(kpsFilename: string): KmpJsonFile.KmpJsonFile { + const kps = this.loadKpsFile(kpsFilename); + if(!kps) { + return null; + } + return this.transformKpsFileToKmpObject(kpsFilename, kps); + } + + public loadKpsFile(kpsFilename: string): KpsFile.KpsFile { // Load the KPS data from XML as JS structured data. const buffer = this.callbacks.loadFile(kpsFilename); if(!buffer) { @@ -32,7 +46,6 @@ export class KmpCompiler { const kpsPackage = (() => { let a: KpsFile.KpsPackage; let parser = new xml2js.Parser({ - tagNameProcessors: [xml2js.processors.firstCharLowerCase], explicitArray: false }); // TODO: add unit test for xml errors parsing .kps file @@ -40,7 +53,11 @@ export class KmpCompiler { return a; })(); - let kps: KpsFile.KpsFile = kpsPackage.package; + const kps: KpsFile.KpsFile = kpsPackage.Package; + return kps; + } + + public transformKpsFileToKmpObject(kpsFilename: string, kps: KpsFile.KpsFile): KmpJsonFile.KmpJsonFile { // // To convert to kmp.json, we need to: @@ -67,43 +84,46 @@ export class KmpCompiler { // Fill in additional fields // - let keys: [keyof KpsFile.KpsFileOptions, keyof KmpJsonFile.KmpJsonFileOptions][] = [ - ['executeProgram','executeProgram'], - ['graphicFile', 'graphicFile'], - ['msiFileName','msiFilename'], - ['msiOptions', 'msiOptions'], - ['readMeFile', 'readmeFile'] - ]; - if(kps.options) { - for (let [src,dst] of keys) { - if (kps.options[src]) { - if(dst == 'graphicFile' || dst == 'readmeFile') { - kmp.options[dst] = /[/\\]?([^/\\]*)$/.exec(kps.options[src])[1]; - } else { - kmp.options[dst] = kps.options[src]; - } - } - } + if(kps.Options) { + kmp.options.executeProgram = kps.Options.ExecuteProgram || undefined; + kmp.options.graphicFile = kps.Options.GraphicFile || undefined; + kmp.options.msiFilename = kps.Options.MsiFileName || undefined; + kmp.options.msiOptions = kps.Options.MsiOptions || undefined; + kmp.options.readmeFile = kps.Options.ReadMeFile || undefined; + kmp.options.licenseFile = kps.Options.LicenseFile || undefined; + kmp.options.welcomeFile = kps.Options.WelcomeFile || undefined; } // // Add basic metadata // - if(kps.info) { - kmp.info = this.kpsInfoToKmpInfo(kps.info); + if(kps.Info) { + kmp.info = this.kpsInfoToKmpInfo(kps.Info); + } + + // + // Add related package metadata + // + + if(kps.RelatedPackages) { + // Note: 'relationship' field is required for kmp.json but optional for .kps, only + // two values are supported -- deprecates or related. + kmp.relatedPackages = (this.arrayWrap(kps.RelatedPackages.RelatedPackage) as KpsFile.KpsFileRelatedPackage[]).map(p => + ({id: p.$.ID, relationship: p.$.Relationship == 'deprecates' ? 'deprecates' : 'related'}) + ); } // // Add file metadata // - if(kps.files && kps.files.file) { - kmp.files = this.arrayWrap(kps.files.file).map((file: KpsFile.KpsFileContentFile) => { + if(kps.Files && kps.Files.File) { + kmp.files = this.arrayWrap(kps.Files.File).map((file: KpsFile.KpsFileContentFile) => { return { - name: file.name.trim(), - description: file.description.trim(), - copyLocation: parseInt(file.copyLocation, 10) || undefined + name: file.Name.trim(), + description: file.Description.trim(), + copyLocation: parseInt(file.CopyLocation, 10) || undefined // note: we don't emit fileType as that is not permitted in kmp.json }; }); @@ -112,7 +132,7 @@ export class KmpCompiler { // Keyboard packages also include a legacy kmp.inf file (this will be removed, // one day) - if(kps.keyboards && kps.keyboards.keyboard) { + if(kps.Keyboards && kps.Keyboards.Keyboard) { kmp.files.push({ name: KMP_INF_FILENAME, description: "Package information" @@ -129,17 +149,32 @@ export class KmpCompiler { // Add keyboard metadata // - if(kps.keyboards && kps.keyboards.keyboard) { - 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.trim(), - id:keyboard.iD.trim(), - version:keyboard.version.trim(), - rtl:keyboard.rTL == 'True' ? true : undefined, - languages: keyboard.languages ? - this.kpsLanguagesToKmpLanguages(this.arrayWrap(keyboard.languages.language) as KpsFile.KpsFileLanguage[]) : - [] + if(kps.Keyboards && kps.Keyboards.Keyboard) { + 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.trim(), + id:keyboard.ID.trim(), + version:keyboard.Version.trim(), + rtl:keyboard.RTL == 'True' ? true : undefined, + languages: keyboard.Languages ? + this.kpsLanguagesToKmpLanguages(this.arrayWrap(keyboard.Languages.Language) as KpsFile.KpsFileLanguage[]) : + [], + examples: keyboard.Examples ? + (this.arrayWrap(keyboard.Examples.Example) as KpsFile.KpsFileLanguageExample[]).map( + e => ({id: e.$.ID, keys: e.$.Keys, text: e.$.Text, note: e.$.Note}) + ) as KmpJsonFile.KmpJsonFileExample[] : + undefined, + webDisplayFonts: keyboard.WebDisplayFonts ? + (this.arrayWrap(keyboard.WebDisplayFonts.Font) as KpsFile.KpsFileFont[]).map( + e => (this.callbacks.path.basename(e.$.Filename)) + ) : + undefined, + webOskFonts: keyboard.WebOSKFonts ? + (this.arrayWrap(keyboard.WebOSKFonts.Font) as KpsFile.KpsFileFont[]).map( + e => (this.callbacks.path.basename(e.$.Filename)) + ) : + undefined, })); } @@ -147,12 +182,12 @@ export class KmpCompiler { // Add lexical-model metadata // - if(kps.lexicalModels && kps.lexicalModels.lexicalModel) { - kmp.lexicalModels = this.arrayWrap(kps.lexicalModels.lexicalModel).map((model: KpsFile.KpsFileLexicalModel) => ({ - name:model.name.trim(), - id:model.iD.trim(), - languages: model.languages ? - this.kpsLanguagesToKmpLanguages(this.arrayWrap(model.languages.language) as KpsFile.KpsFileLanguage[]) : [] + if(kps.LexicalModels && kps.LexicalModels.LexicalModel) { + kmp.lexicalModels = this.arrayWrap(kps.LexicalModels.LexicalModel).map((model: KpsFile.KpsFileLexicalModel) => ({ + name:model.Name.trim(), + id:model.ID.trim(), + languages: model.Languages ? + this.kpsLanguagesToKmpLanguages(this.arrayWrap(model.Languages.Language) as KpsFile.KpsFileLanguage[]) : [] })); } @@ -177,7 +212,7 @@ export class KmpCompiler { return null; } - if(kps.keyboards && kps.keyboards.keyboard) { + if(kps.Keyboards && kps.Keyboards.Keyboard) { kmp.system.fileVersion = versionValidator.getMinKeymanVersion(metadata); } else { kmp.system.fileVersion = MIN_LM_FILEVERSION_KMP_JSON; @@ -201,21 +236,24 @@ export class KmpCompiler { // Add Windows Start Menu metadata // - if(kps.startMenu && (kps.startMenu.folder || kps.startMenu.items)) { + if(kps.StartMenu && (kps.StartMenu.Folder || kps.StartMenu.Items)) { kmp.startMenu = {}; - if(kps.startMenu.addUninstallEntry === '') kmp.startMenu.addUninstallEntry = true; - if(kps.startMenu.folder) kmp.startMenu.folder = kps.startMenu.folder; - if(kps.startMenu.items && kps.startMenu.items.item) { - kmp.startMenu.items = this.arrayWrap(kps.startMenu.items.item); + if(kps.StartMenu.AddUninstallEntry === '') kmp.startMenu.addUninstallEntry = true; + if(kps.StartMenu.Folder) kmp.startMenu.folder = kps.StartMenu.Folder; + if(kps.StartMenu.Items && kps.StartMenu.Items.Item) { + kmp.startMenu.items = this.arrayWrap(kps.StartMenu.Items.Item).map((item: KpsFile.KpsFileStartMenuItem) => ({ + filename: item.FileName, + name: item.Name, + arguments: item.Arguments, + icon: item.Icon, + location: item.Location + })); // Remove default values for(let item of kmp.startMenu.items) { if(item.icon == '') delete item.icon; if(item.location == 'psmelStartMenu') delete item.location; if(item.arguments == '') delete item.arguments; - // Horrific case change between .kps and kmp.json: - item.filename = (item).fileName; - delete (item).fileName; } } else { kmp.startMenu.items = []; @@ -229,25 +267,33 @@ export class KmpCompiler { // Helper functions - private kpsInfoToKmpInfo(info: KpsFile.KpsFileInfo): KmpJsonFile.KmpJsonFileInfo { - let ni: KmpJsonFile.KmpJsonFileInfo = {}; + private kpsInfoToKmpInfo(kpsInfo: KpsFile.KpsFileInfo): KmpJsonFile.KmpJsonFileInfo { + let kmpInfo: KmpJsonFile.KmpJsonFileInfo = {}; - const keys: [(keyof KpsFile.KpsFileInfo), (keyof KmpJsonFile.KmpJsonFileInfo)][] = [ - ['author','author'], - ['copyright','copyright'], - ['name','name'], - ['version','version'], - ['webSite','website'] + const keys: [(keyof KpsFile.KpsFileInfo), (keyof KmpJsonFile.KmpJsonFileInfo), boolean][] = [ + ['Author','author',false], + ['Copyright','copyright',false], + ['Name','name',false], + ['Version','version',false], + ['WebSite','website',false], + ['Description','description',true], ]; - for (let [src,dst] of keys) { - if (info[src]) { - ni[dst] = {description: info[src]._ ?? (typeof info[src] == 'string' ? info[src].toString() : '')}; - if(info[src].$ && info[src].$.URL) ni[dst].url = info[src].$.URL; + for (let [src,dst,isMarkdown] of keys) { + if (kpsInfo[src]) { + kmpInfo[dst] = { + description: (kpsInfo[src]._ ?? (typeof kpsInfo[src] == 'string' ? kpsInfo[src].toString() : '')).trim() + }; + if(isMarkdown) { + kmpInfo[dst].description = markdownToHTML(kmpInfo[dst].description, false).trim(); + } + if(kpsInfo[src].$?.URL) { + kmpInfo[dst].url = kpsInfo[src].$.URL.trim(); + } } } - return ni; + return kmpInfo; }; private arrayWrap(a: unknown) { @@ -264,8 +310,6 @@ export class KmpCompiler { return language.map((element) => { return { name: element._, id: element.$.ID } }); }; - - private stripUndefined(o: any) { for(const key in o) { if(o[key] === undefined) { @@ -340,6 +384,35 @@ export class KmpCompiler { return null; } + // TODO #9477: transform .md to .htm + + // Remove path data from file references in options + + if(data.options.graphicFile) { + data.options.graphicFile = this.callbacks.path.basename(data.options.graphicFile); + } + if(data.options.readmeFile) { + data.options.readmeFile = this.callbacks.path.basename(data.options.readmeFile); + } + if(data.options.licenseFile) { + data.options.licenseFile = this.callbacks.path.basename(data.options.licenseFile); + } + if(data.options.welcomeFile) { + data.options.welcomeFile = this.callbacks.path.basename(data.options.welcomeFile); + } else if(data.files.find(file => file.name == WELCOME_HTM_FILENAME)) { + // We will, for improved backward-compatibility with existing packages, add a + // reference to the file welcome.htm is it is present in the package. This allows + // newer tools to avoid knowing about welcome.htm, if we assume that they work with + // packages compiled with kmc-package (17.0+) and not kmcomp (5.x-16.x). + data.options.welcomeFile = WELCOME_HTM_FILENAME; + } + + if(data.options.msiFilename) { + data.options.msiFilename = this.callbacks.path.basename(data.options.msiFilename); + } + + // Write kmp.json and kmp.inf + zip.file(KMP_JSON_FILENAME, JSON.stringify(data, null, 2)); if(hasKmpInf) { zip.file(KMP_INF_FILENAME, this.buildKmpInf(data)); diff --git a/developer/src/kmc-package/src/compiler/kmx-keyboard-metadata.ts b/developer/src/kmc-package/src/compiler/kmx-keyboard-metadata.ts index 1fbefd7e940..75ada3a8acc 100644 --- a/developer/src/kmc-package/src/compiler/kmx-keyboard-metadata.ts +++ b/developer/src/kmc-package/src/compiler/kmx-keyboard-metadata.ts @@ -8,7 +8,8 @@ export function getCompiledKmxKeyboardMetadata(kmx: KMX.KEYBOARD): WebKeyboardMe keyboardName: getStoreFromKmx(kmx, KMX.KMXFile.TSS_NAME), keyboardVersion: getStoreFromKmx(kmx, KMX.KMXFile.TSS_KEYBOARDVERSION), minKeymanVersion: ((kmx.fileVersion & 0xFF00) >> 8).toString() + '.' + (kmx.fileVersion & 0xFF).toString(), - targets: getStoreFromKmx(kmx, KMX.KMXFile.TSS_TARGETS) ?? 'windows' + targets: getStoreFromKmx(kmx, KMX.KMXFile.TSS_TARGETS) ?? 'windows', + hasTouchLayout: !!getStoreFromKmx(kmx, KMX.KMXFile.TSS_LAYOUTFILE) }; } diff --git a/developer/src/kmc-package/src/compiler/markdown.ts b/developer/src/kmc-package/src/compiler/markdown.ts new file mode 100644 index 00000000000..75ed984fa6c --- /dev/null +++ b/developer/src/kmc-package/src/compiler/markdown.ts @@ -0,0 +1,50 @@ +/** + * Markdown transform for our `description` field. Tweaked to disable all inline + * HTML, because we want descriptions to be short and sweet, and don't need any + * of the more complex formatting that inline HTML affords. + */ + +// +// Note: using marked 7.0.0. +// https://github.com/markedjs/marked/issues/2926 +// +// Version 7.0.1 introduced a TypeScript 5.0+ feature `export type *` which causes: +// +// ../../../node_modules/marked/lib/marked.d.ts:722:5 - error TS1383: Only named exports may use 'export type'. +// 722 export type * from "MarkedOptions"; +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// https://github.com/markedjs/marked/compare/v7.0.0...v7.0.1#diff-32d87a2bc59f429470ccf7afc8ae8818914d4d45c3f7c5b1767c6a0a240b55c9R449-R451 +// +// When we move to TS 5.0, we can upgrade marked. +// +import { Marked } from 'marked'; + +/* + Markdown rendering: we don't want to use the global object, because this + pollutes the settings for all modules. So we construct our own instance, + so that we can strip all inline HTML; we don't need any +*/ + +const renderer = { + html(_html:string, _block:boolean) { + // we don't allow inline HTML + return ''; + } +} +const markedStripHtml = new Marked({renderer}); +const marked = new Marked(); + +/** + * + * @param markdown + * @param allowHTML + * @returns + */ +export function markdownToHTML(markdown: string, allowHTML: boolean): string { + // : .parse can return a Promise if async=true. We don't pass ths + // option, and this sync usage isn't separated out in the types for + // Marked.prototype.parse, so avoids tsc complaints here. + const html = (allowHTML ? marked : markedStripHtml).parse(markdown.trim()); + return html; +} diff --git a/developer/src/kmc-package/src/compiler/messages.ts b/developer/src/kmc-package/src/compiler/messages.ts index c230c697723..2eeaa6e0618 100644 --- a/developer/src/kmc-package/src/compiler/messages.ts +++ b/developer/src/kmc-package/src/compiler/messages.ts @@ -2,7 +2,7 @@ import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageSpec as m const Namespace = CompilerErrorNamespace.PackageCompiler; 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; @@ -50,9 +50,9 @@ export class CompilerMessages { `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, + static Hint_PackageShouldNotRepeatLanguages = (o:{resourceType: string, id: string, minimalTag: string, firstTag: string, secondTag: string}) => m(this.HINT_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 HINT_PackageShouldNotRepeatLanguages = SevHint | 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, `+ @@ -88,9 +88,9 @@ export class CompilerMessages { `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, + static Hint_LanguageTagIsNotMinimal = (o: {resourceType: string, id:string, actual:string, expected:string}) => m(this.HINT_LanguageTagIsNotMinimal, `Language tag '${o.actual}' in ${o.resourceType} ${o.id} is not minimal, and should be '${o.expected}'.`); - static WARN_LanguageTagIsNotMinimal = SevWarn | 0x0015; + static HINT_LanguageTagIsNotMinimal = SevHint | 0x0015; static Error_ModelMustHaveAtLeastOneLanguage = (o:{id:string}) => m(this.ERROR_ModelMustHaveAtLeastOneLanguage, `The lexical model ${o.id} must have at least one language specified.`); @@ -116,5 +116,12 @@ export class CompilerMessages { `The keyboard ${o.id} should have at least one language specified.`); static WARN_KeyboardShouldHaveAtLeastOneLanguage = SevWarn | 0x001B; + static Hint_JsKeyboardFileHasNoTouchTargets = (o:{id:string}) => m(this.HINT_JsKeyboardFileHasNoTouchTargets, + `The keyboard ${o.id} has been included for touch platforms, but does not include a touch layout.`); + static HINT_JsKeyboardFileHasNoTouchTargets = SevHint | 0x001C; + + static Hint_PackageContainsSourceFile = (o:{filename:string}) => m(this.HINT_PackageContainsSourceFile, + `The source file ${o.filename} should not be included in the package; instead include the compiled result.`); + static HINT_PackageContainsSourceFile = SevHint | 0x001D; } diff --git a/developer/src/kmc-package/src/compiler/package-keyboard-target-validator.ts b/developer/src/kmc-package/src/compiler/package-keyboard-target-validator.ts index a79ec8f00a7..96cb23686e9 100644 --- a/developer/src/kmc-package/src/compiler/package-keyboard-target-validator.ts +++ b/developer/src/kmc-package/src/compiler/package-keyboard-target-validator.ts @@ -8,9 +8,14 @@ export class PackageKeyboardTargetValidator { public verifyAllTargets(kmp: KmpJsonFile.KmpJsonFile, metadata: KeyboardMetadataCollection): void { for(let keyboard of Object.keys(metadata)) { + let hasJS = true; + if(metadata[keyboard].data.targets) { - // get the targets from the .kmx - this.verifyTargets(metadata[keyboard].keyboard, metadata[keyboard].data.targets, kmp); + // get the targets from the .kmx (only the .kmx has the targets data) + hasJS = this.verifyTargets(metadata[keyboard].keyboard, metadata[keyboard].data.targets, kmp); + } + if(hasJS && !metadata[keyboard].data.hasTouchLayout) { + this.callbacks.reportMessage(CompilerMessages.Hint_JsKeyboardFileHasNoTouchTargets({id: metadata[keyboard].keyboard.id})); } } } @@ -23,7 +28,7 @@ export class PackageKeyboardTargetValidator { keyboard: KmpJsonFile.KmpJsonFileKeyboard, targetsText: string, kmp: KmpJsonFile.KmpJsonFile - ): void { + ): boolean { // 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 @@ -34,7 +39,12 @@ export class PackageKeyboardTargetValidator { 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})); + return false; } + // A js file is included and targeted + return true; } + // js is not targeted + return false; } } \ No newline at end of file diff --git a/developer/src/kmc-package/src/compiler/package-validation.ts b/developer/src/kmc-package/src/compiler/package-validation.ts index 94e180a465e..3fe787d054a 100644 --- a/developer/src/kmc-package/src/compiler/package-validation.ts +++ b/developer/src/kmc-package/src/compiler/package-validation.ts @@ -1,4 +1,4 @@ -import { KmpJsonFile, CompilerCallbacks, CompilerOptions } from '@keymanapp/common-types'; +import { KmpJsonFile, CompilerCallbacks, CompilerOptions, KeymanFileTypes } from '@keymanapp/common-types'; import { CompilerMessages } from './messages.js'; import { keymanEngineForWindowsFiles, keymanForWindowsInstallerFiles, keymanForWindowsRedistFiles } from './redist-files.js'; @@ -64,11 +64,11 @@ export class PackageValidation { const minimalTag = locale.minimize().toString(); if(minimalTag.toLowerCase() !== lang.id.toLowerCase()) { - this.callbacks.reportMessage(CompilerMessages.Warn_LanguageTagIsNotMinimal({resourceType, id, actual: lang.id, expected: minimalTag})); + this.callbacks.reportMessage(CompilerMessages.Hint_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]})); + this.callbacks.reportMessage(CompilerMessages.Hint_PackageShouldNotRepeatLanguages({resourceType, id, minimalTag, firstTag: lang.id, secondTag: minimalTags[minimalTag]})); } else { minimalTags[minimalTag] = lang.id; @@ -167,15 +167,35 @@ export class PackageValidation { } private checkIfContentFileIsDangerous(file: KmpJsonFile.KmpJsonFileContentFile): boolean { - let filename = this.callbacks.path.basename(file.name).toLowerCase(); + const filename = this.callbacks.path.basename(file.name).toLowerCase(); + + // # Test for inclusion of redistributable files + if(keymanForWindowsInstallerFiles.includes(filename) || keymanForWindowsRedistFiles.includes(filename) || keymanEngineForWindowsFiles.includes(filename)) { this.callbacks.reportMessage(CompilerMessages.Warn_RedistFileShouldNotBeInPackage({filename})); } + + // # Test for inclusion of .doc or .docx files + if(filename.match(/\.doc(x?)$/)) { this.callbacks.reportMessage(CompilerMessages.Warn_DocFileDangerous({filename})); } + + // # Test for inclusion of keyboard source files + // + // We treat this as a hint, because it's not a dangerous problem, just + // something that suggests that perhaps they are trying to distribute the + // wrong files. + // + // Note: we allow .xml in the package because there are other files + // which may be valid, not just LDML keyboards + const fileType = KeymanFileTypes.sourceTypeFromFilename(file.name); + if(fileType !== null && fileType !== KeymanFileTypes.Source.LdmlKeyboard) { + this.callbacks.reportMessage(CompilerMessages.Hint_PackageContainsSourceFile({filename: file.name})); + } + return true; } diff --git a/developer/src/kmc-package/src/compiler/package-version-validator.ts b/developer/src/kmc-package/src/compiler/package-version-validator.ts index 8f71418be2f..b53ee564e8e 100644 --- a/developer/src/kmc-package/src/compiler/package-version-validator.ts +++ b/developer/src/kmc-package/src/compiler/package-version-validator.ts @@ -35,7 +35,7 @@ export class PackageVersionValidator { * @returns */ public validateAndUpdateVersions(kps: KpsFile.KpsFile, kmp: KmpJsonFile.KmpJsonFile, keyboardMetadata: KeyboardMetadataCollection) { - const followKeyboardVersion = kps.options?.followKeyboardVersion !== undefined; + const followKeyboardVersion = kps.Options?.FollowKeyboardVersion !== undefined; if(followKeyboardVersion) { if(!this.checkFollowKeyboardVersion(kmp)) { diff --git a/developer/src/kmc-package/src/compiler/web-keyboard-metadata.ts b/developer/src/kmc-package/src/compiler/web-keyboard-metadata.ts index 5800163ff95..eed412df1e2 100644 --- a/developer/src/kmc-package/src/compiler/web-keyboard-metadata.ts +++ b/developer/src/kmc-package/src/compiler/web-keyboard-metadata.ts @@ -6,6 +6,7 @@ export interface WebKeyboardMetadata { isRtl: boolean; isMnemonic: boolean; targets?: string; + hasTouchLayout: boolean; }; /** @@ -24,12 +25,14 @@ export function getCompiledWebKeyboardMetadata(js: string): WebKeyboardMetadata const minverRegex = /this.KMINVER\s*=\s*([''"])(.*?)\1/; const rtlRegex = /this.KRTL\s*=\s*(.*?)\s*;/; const mnemonicRegex = /this.KM\s*=\s*(.*?)\s*;/; + const touchLayoutRegex = /this.KVKL\s*=\s*{/; const name = nameRegex.exec(js); const kbver = kbverRegex.exec(js); const minver = minverRegex.exec(js); const rtl = rtlRegex.exec(js); const mnemonic = mnemonicRegex.exec(js); + const touchLayout = touchLayoutRegex.exec(js); const SKeymanVersion70 = '7.0'; @@ -38,6 +41,7 @@ export function getCompiledWebKeyboardMetadata(js: string): WebKeyboardMetadata keyboardVersion: kbver ? kbver[2] : null, minKeymanVersion: minver ? minver[2] : SKeymanVersion70, isRtl: !!(rtl && rtl[1].match(/^(1|true)$/)), - isMnemonic: !!(mnemonic && mnemonic[1].match(/^(1|true)$/)) + isMnemonic: !!(mnemonic && mnemonic[1].match(/^(1|true)$/)), + hasTouchLayout: !!touchLayout }; } diff --git a/developer/src/kmc-package/src/compiler/windows-package-installer-compiler.ts b/developer/src/kmc-package/src/compiler/windows-package-installer-compiler.ts new file mode 100644 index 00000000000..8f920535bbf --- /dev/null +++ b/developer/src/kmc-package/src/compiler/windows-package-installer-compiler.ts @@ -0,0 +1,145 @@ +/** + * Create a .exe installer that bundles one or more .kmp files, together with + * setup.exe, keymandesktop.msi, and generates and includes a setup.inf also. + * + * This module is effectively deprecated, but is present to keep parity with the + * legacy kmcomp compiler. Thus, it is included as part of the package compiler, + * and will be removed in a future version. + * + * This tool assumes that the installer .msi is the same version as the + * compiler, unlike the legacy compiler which read this metadata from the .msi. + */ + +import JSZip from 'jszip'; +import { CompilerCallbacks, KeymanFileTypes, KmpJsonFile, KpsFile } from "@keymanapp/common-types"; +import KEYMAN_VERSION from "@keymanapp/keyman-version"; +import { KmpCompiler } from "./kmp-compiler.js"; +import { CompilerMessages } from "./messages.js"; + +const SETUP_INF_FILENAME = 'setup.inf'; +const PRODUCT_NAME = 'Keyman'; + +export interface WindowsPackageInstallerSources { + msiFilename: string; + setupExeFilename: string; + licenseFilename: string; // MIT license + titleImageFilename?: string; + + appName?: string; + startDisabled: boolean; + startWithConfiguration: boolean; +}; + +export class WindowsPackageInstallerCompiler { + private kmpCompiler: KmpCompiler; + + constructor(private callbacks: CompilerCallbacks) { + this.kmpCompiler = new KmpCompiler(this.callbacks); + } + + public async compile(kpsFilename: string, sources: WindowsPackageInstallerSources): Promise { + const kps = this.kmpCompiler.loadKpsFile(kpsFilename); + + // Check existence of required files + for(const filename of [sources.licenseFilename, sources.msiFilename, sources.setupExeFilename]) { + if(!this.callbacks.fs.existsSync(filename)) { + this.callbacks.reportMessage(CompilerMessages.Error_FileDoesNotExist({filename})); + return null; + } + } + + // Check existence of optional files + for(const filename of [sources.titleImageFilename]) { + if(filename && !this.callbacks.fs.existsSync(filename)) { + this.callbacks.reportMessage(CompilerMessages.Error_FileDoesNotExist({filename})); + return null; + } + } + + // Note: we never use the MSIFileName field from the .kps any more + // Nor do we use the MSIOptions field. + + // Build the zip + const zipBuffer = await this.buildZip(kps, kpsFilename, sources); + if(!zipBuffer) { + // Error messages already reported by buildZip + return null; + } + + // Build the sfx + const sfxBuffer = this.buildSfx(zipBuffer, sources); + return sfxBuffer; + } + + private async buildZip(kps: KpsFile.KpsFile, kpsFilename: string, sources: WindowsPackageInstallerSources): Promise { + const kmpJson: KmpJsonFile.KmpJsonFile = this.kmpCompiler.transformKpsFileToKmpObject(kpsFilename, kps); + if(!kmpJson.info?.name?.description) { + this.callbacks.reportMessage(CompilerMessages.Error_PackageNameCannotBeBlank()); + return null; + } + + const kmpFilename = this.callbacks.path.basename(kpsFilename, KeymanFileTypes.Source.Package) + KeymanFileTypes.Binary.Package; + const setupInfBuffer = this.buildSetupInf(sources, kmpJson, kmpFilename, kps); + const kmpBuffer = await this.kmpCompiler.buildKmpFile(kpsFilename, kmpJson); + + // Note that this does not technically generate a "valid" sfx according to + // the zip spec, because the offsets in the .zip are relative to the start + // of the zip, rather than to the start of the sfx. However, as Keyman's + // installer chops out the zip from the sfx and loads it in a new stream, it + // works as expected. + const zip = JSZip(); + zip.file(SETUP_INF_FILENAME, setupInfBuffer); + zip.file(kmpFilename, kmpBuffer); + zip.file(this.callbacks.path.basename(sources.msiFilename), this.callbacks.loadFile(sources.msiFilename)); + zip.file(this.callbacks.path.basename(sources.licenseFilename), this.callbacks.loadFile(sources.licenseFilename)); + if(sources.titleImageFilename) { + zip.file(this.callbacks.path.basename(sources.titleImageFilename), this.callbacks.loadFile(sources.titleImageFilename)); + } + + return zip.generateAsync({type: 'uint8array', compression:'DEFLATE'}); + } + + private buildSetupInf(sources: WindowsPackageInstallerSources, kmpJson: KmpJsonFile.KmpJsonFile, kmpFilename: string, kps: KpsFile.KpsFile) { + let setupInf = `[Setup] +Version=${KEYMAN_VERSION.VERSION} +MSIFileName=${this.callbacks.path.basename(sources.msiFilename)} +MSIOptions= +AppName=${sources.appName ?? PRODUCT_NAME} +License=${this.callbacks.path.basename(sources.licenseFilename)} +`; + if (sources.titleImageFilename) { + setupInf += `TitleImage=${this.callbacks.path.basename(sources.titleImageFilename)}\n`; + } + if (kmpJson.options.graphicFile) { + setupInf += `BitmapFileName=${this.callbacks.path.basename(kmpJson.options.graphicFile)}\n`; + } + if (sources.startDisabled) { + setupInf += `StartDisabled=True\n`; + } + if (sources.startWithConfiguration) { + setupInf += `StartWithConfiguration=True\n`; + } + + setupInf += `\n[Packages]\n`; + setupInf += kmpFilename + '\n'; + // TODO: multiple packages? + const strings = !kps.Strings?.String ? [] : (Array.isArray(kps.Strings.String) ? kps.Strings.String : [kps.Strings.String]); + if (strings.length) { + setupInf += `\n[Strings]\n`; + for (const str of strings) { + setupInf += `${str.$?.Name}=${str.$?.Value}\n`; + } + } + + const setupInfBuffer = new TextEncoder().encode(setupInf); + return setupInfBuffer; + } + + private buildSfx(zipBuffer: Uint8Array, sources: WindowsPackageInstallerSources): Uint8Array { + const setupRedistBuffer = this.callbacks.loadFile(sources.setupExeFilename); + const buffer = new Uint8Array(setupRedistBuffer.length + zipBuffer.length); + buffer.set(setupRedistBuffer, 0); + buffer.set(zipBuffer, setupRedistBuffer.length); + return buffer; + } +} diff --git a/developer/src/kmc-package/src/main.ts b/developer/src/kmc-package/src/main.ts index 5903a59de30..481db13ce31 100644 --- a/developer/src/kmc-package/src/main.ts +++ b/developer/src/kmc-package/src/main.ts @@ -1,2 +1,3 @@ export { KmpCompiler } from "./compiler/kmp-compiler.js"; -export { PackageValidation } from "./compiler/package-validation.js"; \ No newline at end of file +export { PackageValidation } from "./compiler/package-validation.js"; +export { WindowsPackageInstallerSources, WindowsPackageInstallerCompiler } from "./compiler/windows-package-installer-compiler.js"; \ No newline at end of file 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 index 335201a6e5b..51b3e3f363d 100644 --- 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 @@ -10,12 +10,11 @@ - - khmer_angkor.kmn - Keyboard Khmer Angkor + + example.txt + Example text file 0 - .kmn + .txt diff --git a/developer/src/kmc-package/test/fixtures/invalid/example.txt b/developer/src/kmc-package/test/fixtures/invalid/example.txt new file mode 100644 index 00000000000..0f8c58ac580 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/example.txt @@ -0,0 +1 @@ +This is a sample text file \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/invalid/hint_js_keyboard_file_has_no_touch_targets.js b/developer/src/kmc-package/test/fixtures/invalid/hint_js_keyboard_file_has_no_touch_targets.js new file mode 100644 index 00000000000..5a6fff14f71 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/hint_js_keyboard_file_has_no_touch_targets.js @@ -0,0 +1 @@ +KeymanWeb.KR(new Keyboard_hint_js_keyboard_file_has_no_touch_targets());function Keyboard_hint_js_keyboard_file_has_no_touch_targets(){this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;this.KI="Keyboard_hint_js_keyboard_file_has_no_touch_targets";this.KN="hint_js_keyboard_file_has_no_touch_targets";this.KMINVER="9.0";this.KV=null;this.KDU=0;this.KH='';this.KM=0;this.KBVER="1.0";this.KMBM=0x0;this.KVER="17.0.184.0";this.KVS=[];this.gs=function(t,e) {return this.g0(t,e);};this.gs=function(t,e) {return this.g0(t,e);};this.g0=function(t,e) {var k=KeymanWeb,r=0,m=0;return r;};} \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/invalid/hint_js_keyboard_file_has_no_touch_targets.kmn b/developer/src/kmc-package/test/fixtures/invalid/hint_js_keyboard_file_has_no_touch_targets.kmn new file mode 100644 index 00000000000..f5919a61c73 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/hint_js_keyboard_file_has_no_touch_targets.kmn @@ -0,0 +1,7 @@ +store(&name) 'hint_js_keyboard_file_has_no_touch_targets' +store(&keyboardversion) '1.0' +store(&targets) 'web' + +begin unicode > use(main) + +group(main) using keys \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/invalid/hint_js_keyboard_file_has_no_touch_targets.kps b/developer/src/kmc-package/test/fixtures/invalid/hint_js_keyboard_file_has_no_touch_targets.kps new file mode 100644 index 00000000000..0a24a29deb4 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/hint_js_keyboard_file_has_no_touch_targets.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 + + + + hint_js_keyboard_file_has_no_touch_targets.js + Keyboard + 0 + .js + + + + + hint_js_keyboard_file_has_no_touch_targets + hint_js_keyboard_file_has_no_touch_targets + 1.0 + + Central Khmer (Khmer, Cambodia) + + + + + 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/hint_language_tag_is_not_minimal.kps similarity index 95% rename from developer/src/kmc-package/test/fixtures/invalid/warn_language_tag_is_not_minimal.kps rename to developer/src/kmc-package/test/fixtures/invalid/hint_language_tag_is_not_minimal.kps index cc757d6431c..d29ae419231 100644 --- a/developer/src/kmc-package/test/fixtures/invalid/warn_language_tag_is_not_minimal.kps +++ b/developer/src/kmc-package/test/fixtures/invalid/hint_language_tag_is_not_minimal.kps @@ -24,7 +24,7 @@ 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/hint_package_should_not_repeat_languages.kps similarity index 94% rename from developer/src/kmc-package/test/fixtures/invalid/warn_package_should_not_repeat_languages.kps rename to developer/src/kmc-package/test/fixtures/invalid/hint_package_should_not_repeat_languages.kps index ef8fcfb89df..e67d60f7c29 100644 --- a/developer/src/kmc-package/test/fixtures/invalid/warn_package_should_not_repeat_languages.kps +++ b/developer/src/kmc-package/test/fixtures/invalid/hint_package_should_not_repeat_languages.kps @@ -24,7 +24,7 @@ basic 1.0 - + Central Khmer (Khmer, Cambodia) Khmer diff --git a/developer/src/kmc-package/test/fixtures/invalid/hint_source_file_should_not_be_in_package.kps b/developer/src/kmc-package/test/fixtures/invalid/hint_source_file_should_not_be_in_package.kps new file mode 100644 index 00000000000..8de72f2a829 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/invalid/hint_source_file_should_not_be_in_package.kps @@ -0,0 +1,38 @@ + + + + 15.0.266.0 + 7.0 + + + SENĆOŦEN (Saanich Dialect) Keyboard + © 2019 National Research Council Canada + Eddie Antonio Santos + 1.0 + + + + basic.kmx + Keyboard Basic + 0 + .kmx + + + + basic.kmn + Keyboard Source Basic + 0 + .kmn + + + + + Basic + basic + 1.0 + + Khmer + + + + 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.hint_package_should_not_repeat_languages.model.kps similarity index 95% rename from developer/src/kmc-package/test/fixtures/invalid/keyman.en.warn_package_should_not_repeat_languages.model.kps rename to developer/src/kmc-package/test/fixtures/invalid/keyman.en.hint_package_should_not_repeat_languages.model.kps index 0fdb2703214..1e8b849b2ad 100644 --- 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.hint_package_should_not_repeat_languages.model.kps @@ -24,7 +24,7 @@ example.qaa.sencoten North Straits Salish - + Salish Again SENĆOŦEN diff --git a/developer/src/kmc-package/test/fixtures/invalid/my_file.PDF b/developer/src/kmc-package/test/fixtures/invalid/my_file.PDF new file mode 100644 index 00000000000..8f8a5ee09e0 Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/invalid/my_file.PDF differ diff --git a/developer/src/kmc-package/test/fixtures/khmer_angkor/ref/kmp.json b/developer/src/kmc-package/test/fixtures/khmer_angkor/ref/kmp.json index 5cb4d4029e4..1fd94b374ef 100644 --- a/developer/src/kmc-package/test/fixtures/khmer_angkor/ref/kmp.json +++ b/developer/src/kmc-package/test/fixtures/khmer_angkor/ref/kmp.json @@ -5,7 +5,8 @@ }, "options": { "readmeFile": "readme.htm", - "graphicFile": "splash.gif" + "graphicFile": "splash.gif", + "welcomeFile": "welcome.htm" }, "info": { "name": { diff --git a/developer/src/kmc-package/test/fixtures/khmer_angkor/source/khmer_angkor.kps b/developer/src/kmc-package/test/fixtures/khmer_angkor/source/khmer_angkor.kps index 2272334352b..cb4628a0f56 100644 --- a/developer/src/kmc-package/test/fixtures/khmer_angkor/source/khmer_angkor.kps +++ b/developer/src/kmc-package/test/fixtures/khmer_angkor/source/khmer_angkor.kps @@ -8,6 +8,7 @@ readme.htm splash.gif + welcome.htm diff --git a/developer/src/kmc-package/test/fixtures/kmp_2.0/DejaVuSans.ttf b/developer/src/kmc-package/test/fixtures/kmp_2.0/DejaVuSans.ttf new file mode 100644 index 00000000000..27cff476ef3 Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/kmp_2.0/DejaVuSans.ttf differ diff --git a/developer/src/kmc-package/test/fixtures/kmp_2.0/khmer_angkor.js b/developer/src/kmc-package/test/fixtures/kmp_2.0/khmer_angkor.js new file mode 100644 index 00000000000..4ee51e4d3e6 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/kmp_2.0/khmer_angkor.js @@ -0,0 +1 @@ +if(typeof keyman === 'undefined') {console.log('Keyboard requires KeymanWeb 10.0 or later');if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");} else {KeymanWeb.KR(new Keyboard_khmer_angkor());}function Keyboard_khmer_angkor(){this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;this.KI="Keyboard_khmer_angkor";this.KN="Khmer Angkor";this.KMINVER="10.0";this.KV={F:' 1em "Khmer Busra Kbd"',K102:0};this.KV.KLS={"rightalt": ["‍","‌","@","","$","€","៙","៚","*","{","}","≈","","","","","ៜ","","ឯ","ឫ","ឨ","[","]","ឦ","ឱ","ឰ","ឩ","ឳ","\\","","","","+","-","×","÷",":","‘","’","ឝ","៘","៖","ៈ","","","","","","","<",">","#","&","ឞ",";","",",",".","/","","","","",""," "],"rightalt-shift": ["","៱","៲","៳","៴","៵","៶","៷","៸","៹","៰","","","","","","᧠","᧡","᧢","᧣","᧤","᧥","᧦","᧧","᧨","᧩","᧪","᧫","","","","","᧬","᧭","᧮","᧯","᧰","᧱","᧲","᧳","᧴","᧵","᧶","","","","","","","᧷","᧸","᧹","᧺","᧻","᧼","᧽","᧾","᧿","","","","","","",""],"default": ["«","១","២","៣","៤","៥","៦","៧","៨","៩","០","ឥ","ឲ","","","","ឆ","","","រ","ត","យ","","","","ផ","","ឪ","ឮ","","","","","ស","ដ","ថ","ង","ហ","","ក","ល","","","","","","","","","ឋ","ខ","ច","វ","ប","ន","ម","","។","","","","","","","​"],"shift": ["»","!","ៗ","\"","៛","%","","","","(",")","","=","","","","ឈ","","","ឬ","ទ","","","","","ភ","","ឧ","ឭ","","","","","","ឌ","ធ","អ","ះ","ញ","គ","ឡ","","","","","","","","","ឍ","ឃ","ជ","","ព","ណ","","","៕","?","","","","","",""]};this.KV.BK=(function(x){var e=Array.apply(null,Array(65)).map(String.prototype.valueOf,""),r=[],v,i,m=['default','shift','ctrl','shift-ctrl','alt','shift-alt','ctrl-alt','shift-ctrl-alt'];for(i=m.length-1;i>=0;i--)if((v=x[m[i]])||r.length)r=(v?v:e).slice().concat(r);return r})(this.KV.KLS);this.KDU=0;this.KH='';this.KM=0;this.KBVER="1.3";this.KMBM=0x0018;this.KVKD="T_17D2_1780 T_17D2_1781 T_17D2_1782 T_17D2_1783 T_17D2_1784 T_17D2_1785 T_17D2_1786 T_17D2_1787 T_17D2_1788 T_17D2_1789 T_17D2_178A T_17D2_178B T_17D2_178C T_17D2_178D T_17D2_178E T_17D2_178F T_17D2_1790 T_17D2_1791 T_17D2_1792 T_17D2_1793 T_17D2_1794 T_17D2_1795 T_17D2_1796 T_17D2_1797 T_17D2_1798 T_17D2_1799 T_17D2_179A T_17D2_179B T_17D2_179C T_17D2_179D T_17D2_179E T_17D2_179F T_17D2_17A0 T_17D2_17A1 T_17D2_17A2 U_0030 U_0031 U_0032 U_0033 U_0034 U_0035 U_0036 U_0037 U_0038 U_0039";this.KVKL={"phone":{"font":"Khmer Busra Kbd","fontsize":"0.8em","displayUnderlying":false,"layer":[{"id":"default","row":[{"id":"1","key":[{"id":"K_Q","text":"\u1786","sk":[{"layer":"shift","id":"K_Q","text":"\u1788"},{"id":"T_17D2_1786","text":"\uEF86"},{"id":"T_17D2_1788","text":"\uEF88"}]},{"id":"K_W","text":"\uEFB9","sk":[{"layer":"shift","id":"K_W","text":"\uEFBA"}]},{"id":"K_E","text":"\uEFC1","sk":[{"layer":"shift","id":"K_E","text":"\uEFC2"},{"layer":"shift","id":"K_S","text":"\uEFC3"},{"layer":"shift","id":"K_V","text":"\uEF12"},{"id":"U_17AF","text":"\u17AF"},{"id":"U_17B0","text":"\u17B0"}]},{"id":"K_R","text":"\u179A","sk":[{"id":"T_17D2_179A","text":"\uEF9A"},{"id":"U_17AB","text":"\u17AB"},{"id":"U_17AC","text":"\u17AC"}]},{"id":"K_T","text":"\u178F","sk":[{"layer":"shift","id":"K_T","text":"\u1791"},{"id":"T_17D2_178F","text":"\uEF8F"},{"layer":"default","id":"T_17D2_1791","text":"\uEF91"}]},{"id":"K_Y","text":"\u1799","sk":[{"id":"T_17D2_1799","text":"\uEF99"}]},{"id":"K_U","text":"\uEFBB","sk":[{"layer":"shift","id":"K_U","text":"\uEFBC"},{"layer":"shift","id":"K_Y","text":"\uEFBD"},{"id":"U_17A7","text":"\u17A7"},{"layer":"shift","id":"U_17AA","text":"\u17AA"},{"layer":"shift","id":"U_17A9","text":"\u17A9"},{"id":"U_17A8","text":"\u17A8"}]},{"id":"K_I","text":"\uEFB7","sk":[{"layer":"shift","id":"K_I","text":"\uEFB8"},{"id":"U_17A5","text":"\u17A5"},{"layer":"shift","id":"U_17A6","text":"\u17A6"}]},{"id":"K_O","text":"\uEFC4","sk":[{"layer":"shift","id":"K_O","text":"\uEFC5"},{"id":"K_LBRKT","text":"\uEFC0"},{"layer":"shift","id":"K_LBRKT","text":"\uEFBF"},{"layer":"shift","id":"K_COLON","text":"\uEF14"},{"id":"U_17B1","text":"\u17B1"},{"id":"U_17B2","text":"\u17B2"},{"layer":"shift","id":"U_17B3","text":"\u17B3"}]},{"id":"K_P","text":"\u1795","sk":[{"layer":"shift","id":"K_P","text":"\u1797"},{"id":"T_17D2_1795","text":"\uEF95"},{"layer":"default","id":"T_17D2_1797","text":"\uEF97"}]}]},{"id":"2","key":[{"width":"100","id":"K_A","text":"\uEFB6","sk":[{"layer":"shift","id":"K_A","text":"\uEF11"}]},{"id":"K_S","text":"\u179F","sk":[{"id":"T_17D2_179F","text":"\uEF9F"},{"id":"U_179D","text":"\u179D"},{"id":"U_179E","text":"\u179E"}]},{"id":"K_D","text":"\u178A","sk":[{"layer":"shift","id":"K_D","text":"\u178C"},{"id":"T_17D2_178A","text":"\uEF8A"},{"layer":"default","id":"T_17D2_178C","text":"\uEF8C"}]},{"id":"K_F","text":"\u1790","sk":[{"layer":"shift","id":"K_F","text":"\u1792"},{"id":"T_17D2_1790","text":"\uEF90"},{"layer":"default","id":"T_17D2_1792","text":"\uEF92"}]},{"id":"K_G","text":"\u1784","sk":[{"layer":"shift","id":"K_G","text":"\u17A2"},{"id":"T_17D2_1784","text":"\uEF84"},{"layer":"default","id":"T_17D2_17A2","text":"\uEFA2"}]},{"id":"K_H","text":"\u17A0","sk":[{"id":"T_17D2_17A0","text":"\uEFA0"},{"layer":"shift","id":"K_H","text":"\u17C7"},{"id":"U_17C8","text":"\u17C8"}]},{"layer":"shift","id":"K_J","text":"\u1789","sk":[{"id":"T_17D2_1789","text":"\uEF89"}]},{"id":"K_K","text":"\u1780","sk":[{"layer":"shift","id":"K_K","text":"\u1782"},{"id":"T_17D2_1780","text":"\uEF80"},{"id":"T_17D2_1782","text":"\uEF82"}]},{"id":"K_L","text":"\u179B","sk":[{"layer":"shift","id":"K_L","text":"\u17A1"},{"id":"T_17D2_179B","text":"\uEF9B"},{"id":"U_17AD","text":"\u17AD"},{"id":"U_17AE","text":"\u17AE"}]},{"id":"K_COLON","text":"\uEFBE"}]},{"id":"3","key":[{"id":"K_Z","text":"\u178B","sk":[{"layer":"shift","id":"K_Z","text":"\u178D"},{"id":"T_17D2_178B","text":"\uEF8B"},{"layer":"default","id":"T_17D2_178D","text":"\uEF8D"}]},{"id":"K_X","text":"\u1781","sk":[{"layer":"shift","id":"K_X","text":"\u1783"},{"id":"T_17D2_1781","text":"\uEF81"},{"layer":"default","id":"T_17D2_1783","text":"\uEF83"}]},{"id":"K_C","text":"\u1785","sk":[{"layer":"shift","id":"K_C","text":"\u1787"},{"id":"T_17D2_1785","text":"\uEF85"},{"layer":"default","id":"T_17D2_1787","text":"\uEF87"}]},{"id":"K_V","text":"\u179C","sk":[{"id":"T_17D2_179C","text":"\uEF9C"}]},{"id":"K_B","text":"\u1794","sk":[{"layer":"shift","id":"K_B","text":"\u1796"},{"id":"T_17D2_1794","text":"\uEF94"},{"layer":"default","id":"T_17D2_1796","text":"\uEF96"}]},{"id":"K_N","text":"\u1793","sk":[{"layer":"shift","id":"K_N","text":"\u178E"},{"id":"T_17D2_1793","text":"\uEF93"},{"layer":"default","id":"T_17D2_178E","text":"\uEF8E"}]},{"id":"K_M","text":"\u1798","sk":[{"id":"T_17D2_1798","text":"\uEF98"},{"layer":"shift","id":"K_M","text":"\uEFC6"}]},{"id":"K_COMMA","text":"\uEF10","sk":[{"layer":"shift","id":"K_COMMA","text":"\uEF13"},{"layer":"shift","id":"K_6","text":"\uEFCD"},{"layer":"shift","id":"K_7","text":"\uEFD0"},{"layer":"shift","id":"K_8","text":"\uEFCF"},{"layer":"shift","id":"K_HYPHEN","text":"\uEFCC"},{"layer":"shift","id":"U_17D1","text":"\uEFD1"},{"layer":"shift","id":"U_17DD","text":"\uEFDD"},{"layer":"shift","id":"U_17CE","text":"\uEFCE"}]},{"width":"100","id":"K_QUOTE","text":"\uEFCB","sk":[{"layer":"shift","id":"K_QUOTE","text":"\uEFC9"},{"id":"K_SLASH","text":"\uEFCA"}]},{"width":"100","id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"4","key":[{"nextlayer":"numeric","width":"140","id":"K_NUMLOCK","sp":"1","text":"\u17E1\u17E2\u17E3"},{"width":"120","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"555","id":"K_SPACE","text":"\u200B","sk":[{"layer":"default","id":"U_0020","text":" "}]},{"width":"120","id":"K_PERIOD","text":"\u17D4","sk":[{"layer":"shift","id":"K_PERIOD","text":"\u17D5"},{"id":"U_0021","text":"!"},{"id":"U_003F","text":"?"}]},{"width":"140","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]},{"id":"numeric","row":[{"id":"1","key":[{"id":"K_1","text":"\u17E1","sk":[{"id":"U_0031","text":"1"}]},{"id":"K_2","text":"\u17E2","sk":[{"id":"U_0032","text":"2"}]},{"id":"K_3","text":"\u17E3","sk":[{"id":"U_0033","text":"3"}]},{"id":"K_4","text":"\u17E4","sk":[{"id":"U_0034","text":"4"}]},{"id":"K_5","text":"\u17E5","sk":[{"id":"U_0035","text":"5"}]},{"id":"K_6","text":"\u17E6","sk":[{"id":"U_0036","text":"6"}]},{"id":"K_7","text":"\u17E7","sk":[{"id":"U_0037","text":"7"}]},{"id":"K_8","text":"\u17E8","sk":[{"id":"U_0038","text":"8"}]},{"id":"K_9","text":"\u17E9","sk":[{"id":"U_0039","text":"9"}]},{"id":"K_0","text":"\u17E0","sk":[{"id":"U_0030","text":"0"},{"id":"U_17D3","text":"\uEFD3"}]}]},{"id":"2","key":[{"id":"U_0040","text":"@","sk":[{"id":"U_00A9","text":"\u00A9"},{"id":"U_00AE","text":"\u00AE"}]},{"id":"U_0023","text":"#","sk":[{"id":"U_2116","text":"\u2116"},{"id":"U_007E","text":"~"}]},{"id":"U_17DB","text":"\u17DB","sk":[{"id":"U_0024","text":"$"},{"id":"U_0E3F","text":"\u0E3F"},{"id":"U_00A2","text":"\u00A2"},{"id":"U_00A3","text":"\u00A3"},{"id":"U_00A5","text":"\u00A5"}]},{"id":"U_0026","text":"&"},{"id":"U_0025","text":"%","sk":[{"id":"U_2030","text":"\u2030"},{"id":"U_2031","text":"\u2031"}]},{"id":"U_002B","text":"+","sk":[{"id":"U_002D","text":"-"},{"id":"U_00D7","text":"\u00D7"},{"id":"U_00F7","text":"\u00F7"},{"id":"U_00B1","text":"\u00B1"}]},{"id":"U_003D","text":"=","sk":[{"id":"U_005F","text":"_"},{"id":"U_2260","text":"\u2260"}]},{"id":"U_002A","text":"*","sk":[{"id":"U_005E","text":"^"}]},{"id":"U_003F","text":"?","sk":[{"id":"U_00BF","text":"\u00BF"}]},{"id":"U_0021","text":"!","sk":[{"id":"U_00A1","text":"\u00A1"}]}]},{"id":"3","key":[{"id":"U_2018","text":"\u2018","sk":[{"id":"U_2019","text":"\u2019"}]},{"id":"U_201C","text":"\u201C","sk":[{"id":"U_201D","text":"\u201D"}]},{"id":"U_00AB","text":"\u00AB","sk":[{"id":"U_00BB","text":"\u00BB"}]},{"id":"U_002F","text":"\/","sk":[{"id":"U_005C","text":"\\"},{"id":"U_007C","text":"|"},{"id":"U_00A6","text":"\u00A6"}]},{"id":"U_0028","text":"(","sk":[{"id":"U_0029","text":")"},{"id":"U_005B","text":"["},{"id":"U_005D","text":"]"},{"id":"U_007B","text":"{"},{"id":"U_007D","text":"}"}]},{"id":"U_17D9","text":"\u17D9","sk":[{"id":"U_17DA","text":"\u17DA"},{"id":"U_17DC","text":"\u17DC"},{"id":"U_00A7","text":"\u00A7"},{"id":"U_00D8","text":"\u00D8"}]},{"id":"U_17D7","text":"\u17D7"},{"id":"U_003C","text":"<","sk":[{"id":"U_2264","text":"\u2264"},{"id":"U_003E","text":">"},{"id":"U_2265","text":"\u2265"}]},{"id":"U_17D6","text":"\u17D6","sk":[{"id":"U_003A","text":":"},{"id":"U_003B","text":";"},{"id":"U_2026","text":"\u2026"}]},{"id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"4","key":[{"nextlayer":"default","width":"140","id":"K_LCONTROL","sp":"2","text":"\u17E1\u17E2\u17E3"},{"width":"120","id":"K_LOPT","sp":"1","text":"*Menu*"},{"layer":"shift","width":"555","id":"K_SPACE","text":"\u200B"},{"width":"120","id":"K_PERIOD","text":"\u17D4","sk":[{"layer":"shift","id":"K_PERIOD","text":"\u17D5"},{"id":"U_0021","text":"!"},{"id":"U_003F","text":"?"}]},{"width":"140","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]}]},"tablet":{"font":"Khmer Busra Kbd","fontsize":"0.8em","displayUnderlying":false,"layer":[{"id":"default","row":[{"id":"1","key":[{"id":"K_1","text":"\u17E1"},{"id":"K_2","text":"\u17E2"},{"id":"K_3","text":"\u17E3"},{"id":"K_4","text":"\u17E4"},{"id":"K_5","text":"\u17E5"},{"id":"K_6","text":"\u17E6"},{"id":"K_7","text":"\u17E7"},{"id":"K_8","text":"\u17E8"},{"id":"K_9","text":"\u17E9"},{"id":"K_0","text":"\u17E0"},{"id":"K_HYPHEN","text":"\u17A5"},{"id":"K_EQUAL","text":"\u17B2"},{"width":"100","id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"2","key":[{"id":"K_Q","pad":"75","text":"\u1786"},{"id":"K_W","text":"\uEFB9"},{"id":"K_E","text":"\uEFC1"},{"id":"K_R","text":"\u179A"},{"id":"K_T","text":"\u178F"},{"id":"K_Y","text":"\u1799"},{"id":"K_U","text":"\uEFBB"},{"id":"K_I","text":"\uEFB7"},{"id":"K_O","text":"\uEFC4"},{"id":"K_P","text":"\u1795"},{"id":"K_LBRKT","text":"\uEFC0"},{"id":"K_RBRKT","text":"\u17AA"},{"width":"10","id":"T_new_138","sp":"10"}]},{"id":"3","key":[{"id":"K_BKQUOTE","text":"\u00AB"},{"id":"K_A","text":"\uEFB6"},{"id":"K_S","text":"\u179F"},{"id":"K_D","text":"\u178A"},{"id":"K_F","text":"\u1790"},{"id":"K_G","text":"\u1784"},{"id":"K_H","text":"\u17A0"},{"id":"K_J","text":"\uEFD2"},{"id":"K_K","text":"\u1780"},{"id":"K_L","text":"\u179B"},{"id":"K_COLON","text":"\uEFBE"},{"id":"K_QUOTE","text":"\uEFCB"},{"id":"K_BKSLASH","text":"\u17AE"}]},{"id":"4","key":[{"nextlayer":"shift","width":"160","id":"K_SHIFT","sp":"1","text":"*Shift*"},{"id":"K_oE2"},{"id":"K_Z","text":"\u178B"},{"id":"K_X","text":"\u1781"},{"id":"K_C","text":"\u1785"},{"id":"K_V","text":"\u179C"},{"id":"K_B","text":"\u1794"},{"id":"K_N","text":"\u1793"},{"id":"K_M","text":"\u1798"},{"id":"K_COMMA","text":"\uEF10"},{"id":"K_PERIOD","text":"\u17D4"},{"id":"K_SLASH","text":"\uEFCA"},{"width":"10","id":"T_new_164","sp":"10"}]},{"id":"5","key":[{"nextlayer":"rightalt","width":"160","id":"K_LCONTROL","sp":"1","text":"*AltGr*"},{"width":"160","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"930","id":"K_SPACE","text":"\u200B"},{"width":"160","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]},{"id":"rightalt","row":[{"id":"1","key":[{"id":"K_1","text":"\u200C"},{"id":"K_2","text":"@"},{"id":"K_3","text":"\uEFD1"},{"id":"K_4","text":"$"},{"id":"K_5","text":"\u20AC"},{"id":"K_6","text":"\u17D9"},{"id":"K_7","text":"\u17DA"},{"id":"K_8","text":"*"},{"id":"K_9","text":"{"},{"id":"K_0","text":"}"},{"id":"K_HYPHEN","text":"\u2248"},{"id":"K_EQUAL","text":"\uEFCE"},{"width":"100","id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"2","key":[{"id":"K_Q","pad":"75","text":"\u17DC"},{"id":"K_W","text":"\uEFDD"},{"id":"K_E","text":"\u17AF"},{"id":"K_R","text":"\u17AB"},{"id":"K_T","text":"\u17A8"},{"id":"K_Y","text":"["},{"id":"K_U","text":"]"},{"id":"K_I","text":"\u17A6"},{"id":"K_O","text":"\u17B1"},{"id":"K_P","text":"\u17B0"},{"id":"K_LBRKT","text":"\u17A9"},{"id":"K_RBRKT","text":"\u17B3"},{"width":"10","id":"T_new_307","sp":"10"}]},{"id":"3","key":[{"id":"K_BKQUOTE","text":"\u200D"},{"id":"K_A","text":"+"},{"id":"K_S","text":"-"},{"id":"K_D","text":"\u00D7"},{"id":"K_F","text":"\u00F7"},{"id":"K_G","text":":"},{"id":"K_H","text":"\u2018"},{"id":"K_J","text":"\u2019"},{"id":"K_K","text":"\u179D"},{"id":"K_L","text":"\u17D8"},{"id":"K_COLON","text":"\u17D6"},{"id":"K_QUOTE","text":"\u17C8"},{"id":"K_BKSLASH","text":"\\"}]},{"id":"4","key":[{"nextlayer":"shift","width":"160","id":"K_SHIFT","sp":"1","text":"*Shift*"},{"id":"K_oE2"},{"id":"K_Z","text":"<"},{"id":"K_X","text":">"},{"id":"K_C","text":"#"},{"id":"K_V","text":"&"},{"id":"K_B","text":"\u179E"},{"id":"K_N","text":";"},{"id":"K_M","text":"\uEFD3"},{"id":"K_COMMA","text":","},{"id":"K_PERIOD","text":"."},{"id":"K_SLASH","text":"\/"},{"width":"10","id":"T_new_333","sp":"10"}]},{"id":"5","key":[{"nextlayer":"default","width":"160","id":"K_LCONTROL","sp":"2","text":"*AltGr*"},{"width":"160","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"930","id":"K_SPACE","text":"\u00A0"},{"width":"160","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]},{"id":"shift","row":[{"id":"1","key":[{"id":"K_1","text":"!"},{"id":"K_2","text":"\u17D7"},{"id":"K_3","text":"\""},{"id":"K_4","text":"\u17DB"},{"id":"K_5","text":"%"},{"id":"K_6","text":"\uEFCD"},{"id":"K_7","text":"\uEFD0"},{"id":"K_8","text":"\uEFCF"},{"id":"K_9","text":"("},{"id":"K_0","text":")"},{"id":"K_HYPHEN","text":"\uEFCC"},{"id":"K_EQUAL","text":"="},{"width":"100","id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"2","key":[{"id":"K_Q","pad":"75","text":"\u1788"},{"id":"K_W","text":"\uEFBA"},{"id":"K_E","text":"\uEFC2"},{"id":"K_R","text":"\u17AC"},{"id":"K_T","text":"\u1791"},{"id":"K_Y","text":"\uEFBD"},{"id":"K_U","text":"\uEFBC"},{"id":"K_I","text":"\uEFB8"},{"id":"K_O","text":"\uEFC5"},{"id":"K_P","text":"\u1797"},{"id":"K_LBRKT","text":"\uEFBF"},{"id":"K_RBRKT","text":"\u17A7"},{"width":"10","id":"T_new_364","sp":"10"}]},{"id":"3","key":[{"id":"K_BKQUOTE","text":"\u00BB"},{"id":"K_A","text":"\uEF11"},{"id":"K_S","text":"\uEFC3"},{"id":"K_D","text":"\u178C"},{"id":"K_F","text":"\u1792"},{"id":"K_G","text":"\u17A2"},{"id":"K_H","text":"\u17C7"},{"id":"K_J","text":"\u1789"},{"id":"K_K","text":"\u1782"},{"id":"K_L","text":"\u17A1"},{"id":"K_COLON","text":"\uEF14"},{"id":"K_QUOTE","text":"\uEFC9"},{"id":"K_BKSLASH","text":"\u17AD"}]},{"id":"4","key":[{"nextlayer":"default","width":"160","id":"K_SHIFT","sp":"2","text":"*Shift*"},{"id":"K_oE2"},{"id":"K_Z","text":"\u178D"},{"id":"K_X","text":"\u1783"},{"id":"K_C","text":"\u1787"},{"id":"K_V","text":"\uEF12"},{"id":"K_B","text":"\u1796"},{"id":"K_N","text":"\u178E"},{"id":"K_M","text":"\uEFC6"},{"id":"K_COMMA","text":"\uEF13"},{"id":"K_PERIOD","text":"\u17D5"},{"id":"K_SLASH","text":"?"},{"width":"10","id":"T_new_390","sp":"10"}]},{"id":"5","key":[{"nextlayer":"rightalt","width":"160","id":"K_LCONTROL","sp":"1","text":"*AltGr*"},{"width":"160","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"930","id":"K_SPACE"},{"width":"160","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]}]}};this.s12=['','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''];this.s13="កខគឃងចឆជឈញដឋឌឍណតថទធនបផពភមយរលវសហឡអឝឞ";this.s14=['','','','','','','','','','','','','','','',''];this.s15="ាិីឹឺុូួើឿៀេែៃោៅ";this.s16=['','',''];this.s17="ំះៈ";this.s18=['','','','','','','','','','','','','','','','','','',''];this.s19="ាិីឹឺុូួើឿៀេែៃោៅំះៈ";this.s20="ាិីឹឺុូួើឿៀេែៃោៅំះៈ";this.s21="េោុិីឹែ";this.s22="ាុ";this.s23="េោុិីឹែាុ";this.s24=['','','','','','','','','','','','','','',''];this.s25="ឥឦឧឨឩឪឫឬឭឮឯឰឱឲឳ";this.s26=['','','','','','','','','','',''];this.s27="់័៌៏៍ៈ៎៑៝ៜ្";this.s28=['',''];this.s29="៉៊";this.s30=['','','','','','','',''];this.s31="។៕៖ៗ៘៙៚៓";this.s32=['','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''];this.s33="«»()!\"%=?{}\\@*,×./[]‍‌+-÷:≈‘’;<>#&";this.s34=['','',''];this.s35="​  ";this.s36=['','',''];this.s37="៛$€";this.s38=['','','','','','','','','',''];this.s39="០១២៣៤៥៦៧៨៩";this.s40=['','','','','','','','','',''];this.s41="៰៱៲៳៴៵៶៷៸៹";this.s42=['','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''];this.s43="᧬᧻᧹᧮᧢᧯᧰᧱᧧᧲᧳᧴᧽᧼᧨᧩᧠᧣᧭᧤᧦᧺᧡᧸᧥᧷᧵᧾᧿᧪᧫᧶";this.s44=['','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''];this.s45="កខគឃងចឆជឈញដឋឌឍណតថទធនបផពភមយរលវឝឞសហឡអ";this.s46=['','','','','','','','','',''];this.s47="0123456789";this.s48="ិីឹឺើ័";this.s49="សហអ";this.s50="ប";this.s51="ងញមយរវនល";this.s52="ងញមយរវនលប";this.s53="យមងបវ";this.s54="យលងរ";this.s55="បហអ";this.s56="ហសអ";this.s57="បយលមនញងរវអ";this.s58="ឆឈបផតទ";this.s59="វឣ";this.s63="touch";this.KVER="15.0.270.0";this.KVS=[];this.gs=function(t,e) {return this.g0(t,e);};this.gs=function(t,e) {return this.g0(t,e);};this.g0=function(t,e) {var k=KeymanWeb,r=0,m=0;if(k.KKM(e,16384,8)) {if(k.KFCM(2,t,['្',{t:'a',a:this.s13}])&&k.KIFS(31,this.s63,t)){r=m=1;k.KDC(2,t);}else if(k.KFCM(2,t,[{t:'a',a:this.s22},'ំ'])){r=m=1;k.KDC(2,t);}else if(k.KFCM(2,t,[{t:'a',a:this.s21},'ះ'])){r=m=1;k.KDC(2,t);}}else if(k.KKM(e,16392,75)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឝ");}}else if(k.KKM(e,16392,66)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឞ");}}else if(k.KKM(e,16392,222)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ៈ");}else if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ៈ");}}else if(k.KKM(e,16392,221)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឳ");}}else if(k.KKM(e,16392,80)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឰ");}}else if(k.KKM(e,16392,73)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឦ");}}else if(k.KKM(e,16392,84)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឨ");}}else if(k.KKM(e,16392,79)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឱ");}}else if(k.KKM(e,16392,219)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឩ");}}else if(k.KKM(e,16392,82)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឫ");}}else if(k.KKM(e,16392,69)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឯ");}}else if(k.KKM(e,16392,87)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៝");}}else if(k.KKM(e,16392,51)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៑");}}else if(k.KKM(e,16392,187)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៎");}}else if(k.KKM(e,16392,81)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ៜ");}}else if(k.KKM(e,16392,186)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៖");}}else if(k.KKM(e,16392,77)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៓");}}else if(k.KKM(e,16392,76)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៘");}}else if(k.KKM(e,16392,54)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៙");}}else if(k.KKM(e,16392,55)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៚");}}else if(k.KKM(e,16392,192)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"‍");}}else if(k.KKM(e,16392,49)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"‌");}}else if(k.KKM(e,16392,85)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"]");}}else if(k.KKM(e,16392,89)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"[");}}else if(k.KKM(e,16392,191)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"/");}}else if(k.KKM(e,16392,65)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"+");}}else if(k.KKM(e,16392,83)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"-");}}else if(k.KKM(e,16392,70)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"÷");}}else if(k.KKM(e,16392,190)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,".");}}else if(k.KKM(e,16392,220)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"\\");}}else if(k.KKM(e,16392,48)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"}");}}else if(k.KKM(e,16392,71)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,":");}}else if(k.KKM(e,16392,68)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"×");}}else if(k.KKM(e,16392,188)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,",");}}else if(k.KKM(e,16392,189)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"≈");}}else if(k.KKM(e,16392,56)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"*");}}else if(k.KKM(e,16392,72)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"‘");}}else if(k.KKM(e,16392,50)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"@");}}else if(k.KKM(e,16392,74)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"’");}}else if(k.KKM(e,16392,78)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,";");}}else if(k.KKM(e,16392,90)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"<");}}else if(k.KKM(e,16392,88)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,">");}}else if(k.KKM(e,16392,57)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"{");}}else if(k.KKM(e,16392,86)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"&");}}else if(k.KKM(e,16392,67)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"#");}}else if(k.KKM(e,16392,53)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"€");}}else if(k.KKM(e,16392,52)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"$");}}else if(k.KKM(e,16408,52)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៴");}}else if(k.KKM(e,16408,56)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៸");}}else if(k.KKM(e,16408,48)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៰");}}else if(k.KKM(e,16408,49)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៱");}}else if(k.KKM(e,16408,50)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៲");}}else if(k.KKM(e,16408,51)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៳");}}else if(k.KKM(e,16408,57)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៹");}}else if(k.KKM(e,16408,53)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៵");}}else if(k.KKM(e,16408,54)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៶");}}else if(k.KKM(e,16408,55)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៷");}}else if(k.KKM(e,16408,221)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧫");}}else if(k.KKM(e,16408,219)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧪");}}else if(k.KKM(e,16408,65)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧬");}}else if(k.KKM(e,16408,66)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧻");}}else if(k.KKM(e,16408,67)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧹");}}else if(k.KKM(e,16408,68)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧮");}}else if(k.KKM(e,16408,69)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧢");}}else if(k.KKM(e,16408,70)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧯");}}else if(k.KKM(e,16408,71)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧰");}}else if(k.KKM(e,16408,72)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧱");}}else if(k.KKM(e,16408,222)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧶");}}else if(k.KKM(e,16408,74)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧲");}}else if(k.KKM(e,16408,75)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧳");}}else if(k.KKM(e,16408,76)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧴");}}else if(k.KKM(e,16408,77)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧽");}}else if(k.KKM(e,16408,78)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧼");}}else if(k.KKM(e,16408,79)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧨");}}else if(k.KKM(e,16408,80)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧩");}}else if(k.KKM(e,16408,81)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧠");}}else if(k.KKM(e,16408,82)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧣");}}else if(k.KKM(e,16408,83)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧭");}}else if(k.KKM(e,16408,84)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧤");}}else if(k.KKM(e,16408,85)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧦");}}else if(k.KKM(e,16408,86)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧺");}}else if(k.KKM(e,16408,87)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧡");}}else if(k.KKM(e,16408,88)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧸");}}else if(k.KKM(e,16408,89)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧥");}}else if(k.KKM(e,16408,190)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧿");}}else if(k.KKM(e,16408,90)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧷");}}else if(k.KKM(e,16408,186)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧵");}}else if(k.KKM(e,16408,188)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧾");}}else if(k.KKM(e,16408,73)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"᧧");}}else if(k.KKM(e,16392,32)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t," ");}}else if(k.KKM(e,16384,261)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ច");}}else if(k.KKM(e,16384,260)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ង");}}else if(k.KKM(e,16384,264)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ឈ");}}else if(k.KKM(e,16384,265)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ញ");}}else if(k.KKM(e,16384,262)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ឆ");}}else if(k.KKM(e,16384,259)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ឃ");}}else if(k.KKM(e,16384,266)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ដ");}}else if(k.KKM(e,16384,267)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ឋ");}}else if(k.KKM(e,16384,268)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ឌ");}}if(m) {}else if(k.KKM(e,16384,290)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្អ");}}else if(k.KKM(e,16384,289)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ឡ");}}else if(k.KKM(e,16384,288)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ហ");}}else if(k.KKM(e,16384,287)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ស");}}else if(k.KKM(e,16384,286)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ឞ");}}else if(k.KKM(e,16384,285)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ឝ");}}else if(k.KKM(e,16384,284)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្វ");}}else if(k.KKM(e,16384,283)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ល");}}else if(k.KKM(e,16384,282)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្រ");}}else if(k.KKM(e,16384,281)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្យ");}}else if(k.KKM(e,16384,280)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ម");}}else if(k.KKM(e,16384,279)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ភ");}}else if(k.KKM(e,16384,278)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ព");}}else if(k.KKM(e,16384,277)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ផ");}}else if(k.KKM(e,16384,276)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ប");}}else if(k.KKM(e,16384,275)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ន");}}else if(k.KKM(e,16384,274)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ធ");}}else if(k.KKM(e,16384,273)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ទ");}}else if(k.KKM(e,16384,272)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ថ");}}else if(k.KKM(e,16384,271)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ត");}}else if(k.KKM(e,16384,256)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ក");}}else if(k.KKM(e,16384,270)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ណ");}}else if(k.KKM(e,16384,257)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ខ");}}else if(k.KKM(e,16384,258)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្គ");}}else if(k.KKM(e,16384,269)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ឍ");}}else if(k.KKM(e,16384,263)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្ជ");}}else if(k.KKM(e,16384,106)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"*");}}else if(k.KKM(e,16400,106)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"*");}}else if(k.KKM(e,16384,107)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"+");}}else if(k.KKM(e,16400,107)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"+");}}else if(k.KKM(e,16384,109)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"-");}}else if(k.KKM(e,16400,109)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"-");}}else if(k.KKM(e,16384,110)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,".");}}else if(k.KKM(e,16400,110)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,".");}}else if(k.KKM(e,16384,111)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"/");}}else if(k.KKM(e,16400,111)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"/");}}else if(k.KKM(e,16384,32)) {if(k.KFCM(1,t,['​'])){r=m=1;k.KDC(1,t);k.KO(-1,t," ");}else if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"​");}}else if(k.KKM(e,16400,32)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t," ");}}else if(k.KKM(e,16400,49)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"!");}}else if(k.KKM(e,16400,222)) {if(k.KFCM(3,t,[{t:'a',a:this.s58},'្','អ'])){r=m=1;k.KDC(3,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្អ៉");k.KB(t);}else if(k.KFCM(3,t,['ល','្',{t:'a',a:this.s55}])){r=m=1;k.KDC(3,t);k.KO(-1,t,"ល្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៉");k.KB(t);}else if(k.KFCM(3,t,['ម','្',{t:'a',a:this.s56}])){r=m=1;k.KDC(3,t);k.KO(-1,t,"ម្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៉");k.KB(t);}else if(k.KFCM(3,t,['ស','្',{t:'a',a:this.s57}])){r=m=1;k.KDC(3,t);k.KO(-1,t,"ស្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៉");k.KB(t);}else if(k.KFCM(3,t,[{t:'a',a:this.s59},'្','ហ'])){r=m=1;k.KDC(3,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្ហ៉");k.KB(t);}else if(k.KFCM(3,t,['អ','្','ង'])){r=m=1;k.KDC(3,t);k.KO(-1,t,"អ្ង៉");k.KB(t);}else if(k.KFCM(3,t,['អ','្','វ'])){r=m=1;k.KDC(3,t);k.KO(-1,t,"អ្វ៉");k.KB(t);}else if(k.KFCM(1,t,[{t:'a',a:this.s29}])){r=m=1;k.KDC(1,t);k.KIO(-1,this.s29,1,t);k.KB(t);}else if(k.KFCM(1,t,[{t:'a',a:this.s49}])){r=m=1;k.KDC(1,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៉");k.KB(t);}else if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៉");}}else if(k.KKM(e,16400,51)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"\"");}}else if(k.KKM(e,16400,52)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៛");}}else if(k.KKM(e,16400,53)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"%");}}else if(k.KKM(e,16400,55)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"័");}}else if(k.KKM(e,16384,222)) {if(k.KFCM(2,t,['្',{t:'a',a:this.s13}])){r=m=1;k.KDC(2,t);k.KO(-1,t,"្");k.KIO(-1,this.s13,2,t);k.KB(t);}else if(k.KFCM(1,t,[{t:'a',a:this.s15}])){r=m=1;k.KDC(1,t);k.KIO(-1,this.s15,1,t);k.KB(t);}else if(k.KFCM(1,t,[{t:'a',a:this.s17}])){r=m=1;k.KDC(1,t);k.KIO(-1,this.s17,1,t);k.KB(t);}else if(k.KFCM(1,t,[{t:'a',a:this.s29}])){r=m=1;k.KDC(1,t);k.KIO(-1,this.s29,1,t);k.KB(t);}else if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"់");}}else if(k.KKM(e,16400,57)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"(");}}else if(k.KKM(e,16400,48)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,")");}}else if(k.KKM(e,16400,56)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៏");}}else if(k.KKM(e,16400,187)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"=");}}else if(k.KKM(e,16384,188)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ុំ");}}else if(k.KKM(e,16384,189)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឥ");}}else if(k.KKM(e,16384,190)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"។");}}else if(k.KKM(e,16384,191)) {if(k.KFCM(3,t,['ល','្',{t:'a',a:this.s53}])){r=m=1;k.KDC(3,t);k.KO(-1,t,"ល្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៊");k.KB(t);}else if(k.KFCM(3,t,['ម','្',{t:'a',a:this.s54}])){r=m=1;k.KDC(3,t);k.KO(-1,t,"ម្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៊");k.KB(t);}else if(k.KFCM(1,t,[{t:'a',a:this.s29}])){r=m=1;k.KDC(1,t);k.KIO(-1,this.s29,1,t);k.KB(t);}else if(k.KFCM(1,t,[{t:'a',a:this.s52}])){r=m=1;k.KDC(1,t);k.KIO(-1,this.s52,1,t);k.KO(-1,t,"៊");k.KB(t);}else if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៊");}}else if(k.KKM(e,16384,48)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"០");}}else if(k.KKM(e,16384,49)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"១");}}else if(k.KKM(e,16384,50)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"២");}}else if(k.KKM(e,16384,51)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៣");}}else if(k.KKM(e,16384,52)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៤");}}else if(k.KKM(e,16384,53)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៥");}}else if(k.KKM(e,16384,54)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៦");}}else if(k.KKM(e,16384,55)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៧");}}else if(k.KKM(e,16384,56)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៨");}}else if(k.KKM(e,16384,57)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៩");}}else if(k.KKM(e,16400,186)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ោះ");}}else if(k.KKM(e,16384,186)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ើ");}}else if(k.KKM(e,16400,188)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ុះ");}}else if(k.KKM(e,16384,187)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឲ");}}else if(k.KKM(e,16400,190)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៕");}}else if(k.KKM(e,16400,191)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"?");}}else if(k.KKM(e,16400,50)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ៗ");}}else if(k.KKM(e,16400,65)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ាំ");}}else if(k.KKM(e,16400,66)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ព");}}else if(k.KKM(e,16400,67)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ជ");}}else if(k.KKM(e,16400,68)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឌ");}}else if(k.KKM(e,16400,69)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ែ");}}else if(k.KKM(e,16400,70)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ធ");}}else if(k.KKM(e,16400,71)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"អ");}}else if(k.KKM(e,16400,72)) {if(k.KFCM(1,t,['ះ'])){r=m=1;k.KDC(1,t);k.KO(-1,t,"ៈ");}else if(k.KFCM(1,t,['ៈ'])){r=m=1;k.KDC(1,t);k.KO(-1,t,"ះ");}else if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ះ");}}else if(k.KKM(e,16400,73)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ី");}}else if(k.KKM(e,16400,74)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ញ");}}else if(k.KKM(e,16400,75)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"គ");}}else if(k.KKM(e,16400,76)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឡ");}}else if(k.KKM(e,16400,77)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ំ");}}else if(k.KKM(e,16400,78)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ណ");}}else if(k.KKM(e,16400,79)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ៅ");}}else if(k.KKM(e,16400,80)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ភ");}}else if(k.KKM(e,16400,81)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឈ");}}else if(k.KKM(e,16400,82)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឬ");}}else if(k.KKM(e,16400,83)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ៃ");}}else if(k.KKM(e,16400,84)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ទ");}}else if(k.KKM(e,16400,85)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ូ");}}else if(k.KKM(e,16400,86)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"េះ");}}else if(k.KKM(e,16400,87)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឺ");}}else if(k.KKM(e,16400,88)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឃ");}}else if(k.KKM(e,16400,89)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ួ");}}else if(k.KKM(e,16400,90)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឍ");}}else if(k.KKM(e,16384,219)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ៀ");}}else if(k.KKM(e,16384,220)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឮ");}}else if(k.KKM(e,16384,221)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឪ");}}else if(k.KKM(e,16400,54)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៍");}}if(m) {}else if(k.KKM(e,16400,189)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"៌");}}else if(k.KKM(e,16384,192)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"«");}}else if(k.KKM(e,16384,65)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ា");}}else if(k.KKM(e,16384,66)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ប");}}else if(k.KKM(e,16384,67)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ច");}}else if(k.KKM(e,16384,68)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ដ");}}else if(k.KKM(e,16384,69)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"េ");}}else if(k.KKM(e,16384,70)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ថ");}}else if(k.KKM(e,16384,71)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ង");}}else if(k.KKM(e,16384,72)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ហ");}}else if(k.KKM(e,16384,73)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ិ");}}else if(k.KKM(e,16384,74)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"្");}}else if(k.KKM(e,16384,75)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ក");}}else if(k.KKM(e,16384,76)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ល");}}else if(k.KKM(e,16384,77)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ម");}}else if(k.KKM(e,16384,78)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ន");}}else if(k.KKM(e,16384,79)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ោ");}}else if(k.KKM(e,16384,80)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ផ");}}else if(k.KKM(e,16384,81)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឆ");}}else if(k.KKM(e,16384,82)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"រ");}}else if(k.KKM(e,16384,83)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ស");}}else if(k.KKM(e,16384,84)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ត");}}else if(k.KKM(e,16384,85)) {if(k.KFCM(3,t,[{t:'a',a:this.s49},'ា','ំ'])){r=m=1;k.KDC(3,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(3,t,[{t:'a',a:this.s52},'ា','ំ'])){r=m=1;k.KDC(3,t);k.KIO(-1,this.s52,1,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ុ");}}else if(k.KKM(e,16384,86)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"វ");}}else if(k.KKM(e,16384,87)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឹ");}}else if(k.KKM(e,16384,88)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ខ");}}else if(k.KKM(e,16384,89)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"យ");}}else if(k.KKM(e,16384,90)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឋ");}}else if(k.KKM(e,16400,219)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឿ");}}else if(k.KKM(e,16400,220)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឭ");}}else if(k.KKM(e,16400,221)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"ឧ");}}else if(k.KKM(e,16400,192)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"»");}}if(m==1) {k.KDC(-1,t);r=this.g1(t,e);m=2;}return r;};this.g1=function(t,e) {var k=KeymanWeb,r=1,m=0;if(k.KFCM(7,t,[{t:'a',a:this.s58},'្','អ','ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្");k.KO(-1,t,"អ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(7,t,['ល','្',{t:'a',a:this.s55},'ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(7,t,['ម','្',{t:'a',a:this.s56},'ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(7,t,['ស','្','ប','ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KO(-1,t,"ប៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(7,t,['ស','្',{t:'a',a:this.s57},'ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(7,t,[{t:'a',a:this.s59},'្','ហ','ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្");k.KO(-1,t,"ហ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(7,t,['អ','្','ង','ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"ង៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(7,t,['អ','្','វ','ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"វ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(7,t,['ហ','្','ប','ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KO(-1,t,"ហ");k.KO(-1,t,"្");k.KO(-1,t,"ប៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(7,t,['ហ','្',{t:'a',a:this.s52},'ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KO(-1,t,"ហ");k.KO(-1,t,"្");k.KIO(-1,this.s52,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(7,t,['ល','្',{t:'a',a:this.s53},'ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(7,t,['ម','្',{t:'a',a:this.s54},'ុ','ំ','ា','ំ'])){m=1;k.KDC(7,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['្','ដ',{t:'a',a:this.s22},'ំ','្','រ'])){m=1;k.KDC(6,t);k.KO(-1,t,"្ត");k.KO(-1,t,"្");k.KO(-1,t,"រ");k.KIO(-1,this.s22,3,t);k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['្','ដ',{t:'a',a:this.s21},'ះ','្','រ'])){m=1;k.KDC(6,t);k.KO(-1,t,"្ត");k.KO(-1,t,"្");k.KO(-1,t,"រ");k.KIO(-1,this.s21,3,t);k.KO(-1,t,"ះ");}else if(k.KFCM(6,t,['្','រ',{t:'a',a:this.s22},'ំ','្','ដ'])){m=1;k.KDC(6,t);k.KO(-1,t,"្ត");k.KO(-1,t,"្");k.KO(-1,t,"រ");k.KIO(-1,this.s22,3,t);k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['្','រ',{t:'a',a:this.s21},'ះ','្','ដ'])){m=1;k.KDC(6,t);k.KO(-1,t,"្ត");k.KO(-1,t,"្");k.KO(-1,t,"រ");k.KIO(-1,this.s21,3,t);k.KO(-1,t,"ះ");}else if(k.KFCM(6,t,['្','រ',{t:'a',a:this.s22},'ំ','្',{t:'a',a:this.s45}])){m=1;k.KDC(6,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,6,t);k.KO(-1,t,"្");k.KO(-1,t,"រ");k.KIO(-1,this.s22,3,t);k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['្','រ',{t:'a',a:this.s21},'ះ','្',{t:'a',a:this.s45}])){m=1;k.KDC(6,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,6,t);k.KO(-1,t,"្");k.KO(-1,t,"រ");k.KIO(-1,this.s21,3,t);k.KO(-1,t,"ះ");}else if(k.KFCM(6,t,[{t:'a',a:this.s58},'្','អ','ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្");k.KO(-1,t,"អ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s55},'ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s56},'ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ស','្','ប','ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KO(-1,t,"ប៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ស','្',{t:'a',a:this.s57},'ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,[{t:'a',a:this.s59},'្','ហ','ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្");k.KO(-1,t,"ហ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['អ','្','ង','ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"ង៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['អ','្','វ','ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"វ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ហ','្','ប','ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ហ");k.KO(-1,t,"្");k.KO(-1,t,"ប៉");k.KO(-1,t,"ប");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ហ','្',{t:'a',a:this.s52},'ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ហ");k.KO(-1,t,"្");k.KIO(-1,this.s52,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s52,3,t);k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s53},'ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s54},'ុ','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s53},'៊',{t:'a',a:this.s15},{t:'a',a:this.s17}])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៉");k.KIO(-1,this.s15,5,t);k.KIO(-1,this.s17,6,t);}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s54},'៊',{t:'a',a:this.s15},{t:'a',a:this.s17}])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៉");k.KIO(-1,this.s15,5,t);k.KIO(-1,this.s17,6,t);}else if(k.KFCM(6,t,[{t:'a',a:this.s58},'្','អ','៉','ា','ំ'])){m=1;k.KDC(6,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្");k.KO(-1,t,"អ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s55},'៉','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s56},'៉','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ស','្','ប','៉','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KO(-1,t,"ប៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ស','្',{t:'a',a:this.s57},'៉','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,[{t:'a',a:this.s59},'្','ហ','៉','ា','ំ'])){m=1;k.KDC(6,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្");k.KO(-1,t,"ហ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['អ','្','ង','៉','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"ង៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['អ','្','វ','៉','ា','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"វ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,[{t:'a',a:this.s58},'្','អ','ា','ុ','ំ'])){m=1;k.KDC(6,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្");k.KO(-1,t,"អ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,[{t:'a',a:this.s58},'្','អ','ុ','ំ','ា'])){m=1;k.KDC(6,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្");k.KO(-1,t,"អ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s55},'ា','ុ','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s55},'ុ','ំ','ា'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s56},'ា','ុ','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s56},'ុ','ំ','ា'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ស','្',{t:'a',a:this.s57},'ា','ុ','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ស','្',{t:'a',a:this.s57},'ុ','ំ','ា'])){m=1;k.KDC(6,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,[{t:'a',a:this.s59},'្','ហ','ា','ុ','ំ'])){m=1;k.KDC(6,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្");k.KO(-1,t,"ហ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,[{t:'a',a:this.s59},'្','ហ','ុ','ំ','ា'])){m=1;k.KDC(6,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្");k.KO(-1,t,"ហ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['អ','្','ង','ា','ុ','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"ង៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['អ','្','ង','ុ','ំ','ា'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"ង៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['អ','្','វ','ា','ុ','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"វ៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['អ','្','វ','ុ','ំ','ា'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"វ៊");k.KO(-1,t,"ុ");k.KO(-1,t,"ា");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s53},'ា','ុ','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s53},'ុ','ំ','ា'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s54},'ា','ុ','ំ'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s54},'ុ','ំ','ា'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(6,t,[{t:'a',a:this.s58},'្','អ','េ','ុ','ី'])){m=1;k.KDC(6,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្");k.KO(-1,t,"អ៊ើ");}else if(k.KFCM(6,t,[{t:'a',a:this.s58},'្','អ','ុ','េ','ី'])){m=1;k.KDC(6,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្");k.KO(-1,t,"អ៊ើ");}else if(k.KFCM(6,t,[{t:'a',a:this.s58},'្','អ','៉','េ','ី'])){m=1;k.KDC(6,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្");k.KO(-1,t,"អ៊ើ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s55},'េ','ុ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s55},'ុ','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s55},'៉','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s56},'េ','ុ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s56},'ុ','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s56},'៉','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(6,t,['ស','្',{t:'a',a:this.s57},'េ','ុ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(6,t,['ស','្',{t:'a',a:this.s57},'ុ','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(6,t,['ស','្',{t:'a',a:this.s57},'៉','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(6,t,[{t:'a',a:this.s59},'្','ហ','េ','ុ','ី'])){m=1;k.KDC(6,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្");k.KO(-1,t,"ហ៊ើ");}else if(k.KFCM(6,t,[{t:'a',a:this.s59},'្','ហ','ុ','េ','ី'])){m=1;k.KDC(6,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្");k.KO(-1,t,"ហ៊ើ");}else if(k.KFCM(6,t,[{t:'a',a:this.s59},'្','ហ','៉','េ','ី'])){m=1;k.KDC(6,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្");k.KO(-1,t,"ហ៊ើ");}else if(k.KFCM(6,t,['អ','្','ង','េ','ុ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"ង៊ើ");}else if(k.KFCM(6,t,['អ','្','ង','ុ','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"ង៊ើ");}else if(k.KFCM(6,t,['អ','្','ង','៉','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"ង៊ើ");}else if(k.KFCM(6,t,['អ','្','វ','េ','ុ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"វ៊ើ");}else if(k.KFCM(6,t,['អ','្','វ','ុ','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"វ៊ើ");}else if(k.KFCM(6,t,['អ','្','វ','៉','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"វ៊ើ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s53},'េ','ុ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៉ើ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s53},'ុ','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៉ើ");}else if(k.KFCM(6,t,['ល','្',{t:'a',a:this.s53},'៊','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៉ើ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s54},'េ','ុ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៉ើ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s54},'ុ','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៉ើ");}else if(k.KFCM(6,t,['ម','្',{t:'a',a:this.s54},'៊','េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៉ើ");}else if(k.KFCM(6,t,['្','យ','្',{t:'a',a:this.s13},'េ','ឺ'])){m=1;k.KDC(6,t);k.KO(-1,t,"្");k.KIO(-1,this.s13,4,t);k.KO(-1,t,"ឿ");}else if(k.KFCM(6,t,['្','យ','្',{t:'a',a:this.s13},'េ','ឹ'])){m=1;k.KDC(6,t);k.KO(-1,t,"្");k.KIO(-1,this.s13,4,t);k.KO(-1,t,"ឿ");}else if(k.KFCM(6,t,['្','យ','្',{t:'a',a:this.s13},'េ','ី'])){m=1;k.KDC(6,t);k.KO(-1,t,"្");k.KIO(-1,this.s13,4,t);k.KO(-1,t,"ឿ");}else if(k.KFCM(5,t,[{t:'a',a:this.s29},{t:'a',a:this.s22},'ំ','្',{t:'a',a:this.s45}])){m=1;k.KDC(5,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,5,t);k.KIO(-1,this.s29,1,t);k.KIO(-1,this.s22,2,t);k.KO(-1,t,"ំ");}else if(k.KFCM(5,t,[{t:'a',a:this.s29},{t:'a',a:this.s21},'ះ','្',{t:'a',a:this.s45}])){m=1;k.KDC(5,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,5,t);k.KIO(-1,this.s29,1,t);k.KIO(-1,this.s21,2,t);k.KO(-1,t,"ះ");}else if(k.KFCM(5,t,['្','ដ',{t:'a',a:this.s20},'្','រ'])){m=1;k.KDC(5,t);k.KO(-1,t,"្ត");k.KO(-1,t,"្");k.KO(-1,t,"រ");k.KIO(-1,this.s20,3,t);}else if(k.KFCM(5,t,['្','រ',{t:'a',a:this.s20},'្','ដ'])){m=1;k.KDC(5,t);k.KO(-1,t,"្ត");k.KO(-1,t,"្");k.KO(-1,t,"រ");k.KIO(-1,this.s20,3,t);}else if(k.KFCM(5,t,['្','រ',{t:'a',a:this.s29},'្',{t:'a',a:this.s45}])){m=1;k.KDC(5,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,5,t);k.KO(-1,t,"្");k.KO(-1,t,"រ");k.KIO(-1,this.s29,3,t);}else if(k.KFCM(5,t,['្','រ',{t:'a',a:this.s20},'្',{t:'a',a:this.s45}])){m=1;k.KDC(5,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,5,t);k.KO(-1,t,"្");k.KO(-1,t,"រ");k.KIO(-1,this.s20,3,t);}else if(k.KFCM(5,t,[{t:'a',a:this.s58},'្','អ','ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្");k.KO(-1,t,"អ៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,[{t:'a',a:this.s58},'្','អ',{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្");k.KO(-1,t,"អ៊");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(5,t,['ល','្',{t:'a',a:this.s55},'ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ល','្',{t:'a',a:this.s55},{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(5,t,['ម','្',{t:'a',a:this.s56},'ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ម','្',{t:'a',a:this.s56},{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,4,t);}if(m) {}else if(k.KFCM(5,t,['ស','្','ប','ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KO(-1,t,"ប៉");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ស','្','ប',{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KO(-1,t,"ប៉");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(5,t,['ស','្',{t:'a',a:this.s57},'ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ស','្',{t:'a',a:this.s57},{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(5,t,[{t:'a',a:this.s59},'្','ហ','ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្");k.KO(-1,t,"ហ៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,[{t:'a',a:this.s59},'្','ហ',{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្");k.KO(-1,t,"ហ៊");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(5,t,['អ','្','ង','ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"ង៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['អ','្','ង',{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"ង៊");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(5,t,['អ','្','វ','ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"វ៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['អ','្','វ',{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"វ៊");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(5,t,['ហ','្','ប','ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ហ");k.KO(-1,t,"្");k.KO(-1,t,"ប៉");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ហ','្','ប',{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KO(-1,t,"ហ");k.KO(-1,t,"្");k.KO(-1,t,"ប៉");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(5,t,['ហ','្',{t:'a',a:this.s52},'ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ហ");k.KO(-1,t,"្");k.KIO(-1,this.s52,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ហ','្',{t:'a',a:this.s52},{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KO(-1,t,"ហ");k.KO(-1,t,"្");k.KIO(-1,this.s52,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(5,t,['ល','្',{t:'a',a:this.s53},'ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៉");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ល','្',{t:'a',a:this.s53},{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៉");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(5,t,['ម','្',{t:'a',a:this.s54},'ុ',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៉");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ម','្',{t:'a',a:this.s54},{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(5,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៉");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(5,t,[{t:'a',a:this.s49},'ុ','ំ','ា','ំ'])){m=1;k.KDC(5,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(5,t,[{t:'a',a:this.s52},'ុ','ំ','ា','ំ'])){m=1;k.KDC(5,t);k.KIO(-1,this.s52,1,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(5,t,['ល','្',{t:'a',a:this.s53},'៊',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s53,3,t);k.KO(-1,t,"៉");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ម','្',{t:'a',a:this.s54},'៊',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s54,3,t);k.KO(-1,t,"៉");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['្',{t:'a',a:this.s51},'៊',{t:'a',a:this.s15},{t:'a',a:this.s17}])){m=1;k.KDC(5,t);k.KO(-1,t,"្");k.KIO(-1,this.s51,2,t);k.KO(-1,t,"៊");k.KIO(-1,this.s15,4,t);k.KIO(-1,this.s17,5,t);}else if(k.KFCM(5,t,[{t:'a',a:this.s58},'្','អ','៉',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KIO(-1,this.s58,1,t);k.KO(-1,t,"្");k.KO(-1,t,"អ៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ល','្',{t:'a',a:this.s55},'៉',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ល");k.KO(-1,t,"្");k.KIO(-1,this.s55,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ម','្',{t:'a',a:this.s56},'៉',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ម");k.KO(-1,t,"្");k.KIO(-1,this.s56,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ស','្','ប','៉',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KO(-1,t,"ប៉");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ស','្',{t:'a',a:this.s57},'៉',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"ស");k.KO(-1,t,"្");k.KIO(-1,this.s57,3,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,[{t:'a',a:this.s59},'្','ហ','៉',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KIO(-1,this.s59,1,t);k.KO(-1,t,"្");k.KO(-1,t,"ហ៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['អ','្','ង','៉',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"ង៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['អ','្','វ','៉',{t:'a',a:this.s48}])){m=1;k.KDC(5,t);k.KO(-1,t,"អ");k.KO(-1,t,"្");k.KO(-1,t,"វ៊");k.KIO(-1,this.s48,5,t);}else if(k.KFCM(5,t,['ព','័','ន','្','ឋ'])){m=1;k.KDC(5,t);k.KO(-1,t,"ព");k.KO(-1,t,"័");k.KO(-1,t,"ន");k.KO(-1,t,"្ធ");}else if(k.KFCM(4,t,[{t:'a',a:this.s15},{t:'a',a:this.s17},{t:'a',a:this.s15},{t:'a',a:this.s17}])){m=1;k.KDC(4,t);k.KIO(-1,this.s15,3,t);k.KIO(-1,this.s17,4,t);}else if(k.KFCM(4,t,[{t:'a',a:this.s22},'ំ','្',{t:'a',a:this.s45}])){m=1;k.KDC(4,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,4,t);k.KIO(-1,this.s22,1,t);k.KO(-1,t,"ំ");}else if(k.KFCM(4,t,[{t:'a',a:this.s21},'ះ','្',{t:'a',a:this.s45}])){m=1;k.KDC(4,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,4,t);k.KIO(-1,this.s21,1,t);k.KO(-1,t,"ះ");}else if(k.KFCM(4,t,[{t:'a',a:this.s29},{t:'a',a:this.s20},'្',{t:'a',a:this.s45}])){m=1;k.KDC(4,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,4,t);k.KIO(-1,this.s29,1,t);k.KIO(-1,this.s20,2,t);}else if(k.KFCM(4,t,['្','ដ','្','រ'])){m=1;k.KDC(4,t);k.KO(-1,t,"្ត");k.KO(-1,t,"្");k.KO(-1,t,"រ");}else if(k.KFCM(4,t,['្','រ','្','ដ'])){m=1;k.KDC(4,t);k.KO(-1,t,"្ត");k.KO(-1,t,"្");k.KO(-1,t,"រ");}else if(k.KFCM(4,t,['្','រ','្',{t:'a',a:this.s45}])){m=1;k.KDC(4,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,4,t);k.KO(-1,t,"្");k.KO(-1,t,"រ");}else if(k.KFCM(4,t,[{t:'a',a:this.s29},{t:'a',a:this.s15},{t:'a',a:this.s17},{t:'a',a:this.s29}])){m=1;k.KDC(4,t);k.KIO(-1,this.s15,2,t);k.KIO(-1,this.s17,3,t);k.KIO(-1,this.s29,4,t);}else if(k.KFCM(4,t,[{t:'a',a:this.s49},'ុ','ា','ំ'])){m=1;k.KDC(4,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(4,t,[{t:'a',a:this.s52},'ុ','ា','ំ'])){m=1;k.KDC(4,t);k.KIO(-1,this.s52,1,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(4,t,[{t:'a',a:this.s51},'៊',{t:'a',a:this.s15},{t:'a',a:this.s17}])){m=1;k.KDC(4,t);k.KIO(-1,this.s51,1,t);k.KO(-1,t,"៉");k.KIO(-1,this.s15,3,t);k.KIO(-1,this.s17,4,t);}else if(k.KFCM(4,t,['្',{t:'a',a:this.s51},'៊',{t:'a',a:this.s48}])){m=1;k.KDC(4,t);k.KO(-1,t,"្");k.KIO(-1,this.s51,2,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,4,t);}else if(k.KFCM(4,t,['ប','្','យ',{t:'a',a:this.s29}])){m=1;k.KDC(4,t);k.KO(-1,t,"ប្យ");k.KIO(-1,this.s29,4,t);}else if(k.KFCM(4,t,['ស','្','ប',{t:'a',a:this.s29}])){m=1;k.KDC(4,t);k.KO(-1,t,"ស្ប");k.KIO(-1,this.s29,4,t);}else if(k.KFCM(4,t,['ឆ','្','ប',{t:'a',a:this.s29}])){m=1;k.KDC(4,t);k.KO(-1,t,"ឆ្ប");k.KIO(-1,this.s29,4,t);}else if(k.KFCM(4,t,['ប','្','យ',{t:'a',a:this.s29}])){m=1;k.KDC(4,t);k.KO(-1,t,"ប្យ");k.KIO(-1,this.s29,4,t);}else if(k.KFCM(4,t,['ស','្','ប',{t:'a',a:this.s29}])){m=1;k.KDC(4,t);k.KO(-1,t,"ស្ប");k.KIO(-1,this.s29,4,t);}else if(k.KFCM(4,t,['ឆ','្','ប',{t:'a',a:this.s29}])){m=1;k.KDC(4,t);k.KO(-1,t,"ឆ្ប");k.KIO(-1,this.s29,4,t);}else if(k.KFCM(4,t,[{t:'a',a:this.s49},'៉',{t:'a',a:this.s15},{t:'a',a:this.s17}])){m=1;k.KDC(4,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊");k.KIO(-1,this.s15,3,t);k.KIO(-1,this.s17,4,t);}else if(k.KFCM(4,t,[{t:'a',a:this.s49},'ា','ុ','ំ'])){m=1;k.KDC(4,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(4,t,[{t:'a',a:this.s49},'ុ','ំ','ា'])){m=1;k.KDC(4,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(4,t,[{t:'a',a:this.s52},'ា','ុ','ំ'])){m=1;k.KDC(4,t);k.KIO(-1,this.s52,1,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(4,t,[{t:'a',a:this.s52},'ុ','ំ','ា'])){m=1;k.KDC(4,t);k.KIO(-1,this.s52,1,t);k.KO(-1,t,"៉");k.KO(-1,t,"ា");k.KO(-1,t,"ំ");}else if(k.KFCM(4,t,[{t:'a',a:this.s49},'េ','ុ','ី'])){m=1;k.KDC(4,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(4,t,[{t:'a',a:this.s49},'ុ','េ','ី'])){m=1;k.KDC(4,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(4,t,[{t:'a',a:this.s49},'៉','េ','ី'])){m=1;k.KDC(4,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊ើ");}else if(k.KFCM(4,t,[{t:'a',a:this.s51},'េ','ុ','ី'])){m=1;k.KDC(4,t);k.KIO(-1,this.s51,1,t);k.KO(-1,t,"៉ើ");}else if(k.KFCM(4,t,[{t:'a',a:this.s51},'ុ','េ','ី'])){m=1;k.KDC(4,t);k.KIO(-1,this.s51,1,t);k.KO(-1,t,"៉ើ");}else if(k.KFCM(4,t,[{t:'a',a:this.s51},'៊','េ','ី'])){m=1;k.KDC(4,t);k.KIO(-1,this.s51,1,t);k.KO(-1,t,"៉ើ");}else if(k.KFCM(4,t,['ព','ន','្','ឋ'])){m=1;k.KDC(4,t);k.KO(-1,t,"ព");k.KO(-1,t,"ន");k.KO(-1,t,"្ធ");}else if(k.KFCM(4,t,['្','យ','េ','ឺ'])){m=1;k.KDC(4,t);k.KO(-1,t,"ឿ");}else if(k.KFCM(4,t,['្','យ','េ','ឹ'])){m=1;k.KDC(4,t);k.KO(-1,t,"ឿ");}else if(k.KFCM(4,t,['្','យ','េ','ី'])){m=1;k.KDC(4,t);k.KO(-1,t,"ឿ");}else if(k.KFCM(3,t,[{t:'a',a:this.s15},{t:'a',a:this.s17},{t:'a',a:this.s15}])){m=1;k.KDC(3,t);k.KIO(-1,this.s15,3,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s15},{t:'a',a:this.s17},{t:'a',a:this.s17}])){m=1;k.KDC(3,t);k.KIO(-1,this.s17,3,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s15},{t:'a',a:this.s15},{t:'a',a:this.s17}])){m=1;k.KDC(3,t);k.KIO(-1,this.s15,2,t);k.KIO(-1,this.s17,3,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s17},{t:'a',a:this.s15},{t:'a',a:this.s17}])){m=1;k.KDC(3,t);k.KIO(-1,this.s15,2,t);k.KIO(-1,this.s17,3,t);}else if(k.KFCM(3,t,['្',{t:'a',a:this.s15},{t:'a',a:this.s17}])){m=1;k.KDC(3,t);k.KIO(-1,this.s15,2,t);k.KIO(-1,this.s17,3,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s20},'្',{t:'a',a:this.s45}])){m=1;k.KDC(3,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,3,t);k.KIO(-1,this.s20,1,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s29},'្',{t:'a',a:this.s45}])){m=1;k.KDC(3,t);k.KO(-1,t,"្");k.KIO(-1,this.s45,3,t);k.KIO(-1,this.s29,1,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s15},{t:'a',a:this.s17},{t:'a',a:this.s29}])){m=1;k.KDC(3,t);k.KIO(-1,this.s29,3,t);k.KIO(-1,this.s15,1,t);k.KIO(-1,this.s17,2,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s29},{t:'a',a:this.s20},{t:'a',a:this.s29}])){m=1;k.KDC(3,t);k.KIO(-1,this.s29,3,t);k.KIO(-1,this.s20,2,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s49},'ុ',{t:'a',a:this.s48}])){m=1;k.KDC(3,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,3,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s49},{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(3,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,2,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s52},'ុ',{t:'a',a:this.s48}])){m=1;k.KDC(3,t);k.KIO(-1,this.s52,1,t);k.KO(-1,t,"៉");k.KIO(-1,this.s48,3,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s52},{t:'a',a:this.s48},'ុ'])){m=1;k.KDC(3,t);k.KIO(-1,this.s52,1,t);k.KO(-1,t,"៉");k.KIO(-1,this.s48,2,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s51},'៊',{t:'a',a:this.s48}])){m=1;k.KDC(3,t);k.KIO(-1,this.s51,1,t);k.KO(-1,t,"៉");k.KIO(-1,this.s48,3,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s49},'៉',{t:'a',a:this.s48}])){m=1;k.KDC(3,t);k.KIO(-1,this.s49,1,t);k.KO(-1,t,"៊");k.KIO(-1,this.s48,3,t);}else if(k.KFCM(3,t,[{t:'a',a:this.s13},{t:'a',a:this.s15},'៌'])){m=1;k.KDC(3,t);k.KIO(-1,this.s13,1,t);k.KO(-1,t,"៌");k.KIO(-1,this.s15,2,t);}else if(k.KFCM(3,t,['ណ','្','ត'])){m=1;k.KDC(3,t);k.KO(-1,t,"ណ");k.KO(-1,t,"្ដ");}else if(k.KFCM(3,t,['ន','្','ដ'])){m=1;k.KDC(3,t);k.KO(-1,t,"ន");k.KO(-1,t,"្ត");}else if(k.KFCM(3,t,['ទ','្','ប'])){m=1;k.KDC(3,t);k.KO(-1,t,"ឡ");}else if(k.KFCM(3,t,['ប','្','ញ'])){m=1;k.KDC(3,t);k.KO(-1,t,"ឫ");}else if(k.KFCM(3,t,['ព','្','ញ'])){m=1;k.KDC(3,t);k.KO(-1,t,"ឭ");}else if(k.KFCM(3,t,['ព','្','ឋ'])){m=1;k.KDC(3,t);k.KO(-1,t,"ឰ");}else if(k.KFCM(3,t,['ដ','្','ធ'])){m=1;k.KDC(3,t);k.KO(-1,t,"ដ្ឋ");}else if(k.KFCM(3,t,['ទ','្','ឋ'])){m=1;k.KDC(3,t);k.KO(-1,t,"ទ្ធ");}else if(k.KFCM(3,t,['ឪ','្','យ'])){m=1;k.KDC(3,t);k.KO(-1,t,"ឱ");k.KO(-1,t,"្");k.KO(-1,t,"យ");}else if(k.KFCM(3,t,['ឳ','្','យ'])){m=1;k.KDC(3,t);k.KO(-1,t,"ឱ");k.KO(-1,t,"្");k.KO(-1,t,"យ");}else if(k.KFCM(3,t,['ញ','្','វ'])){m=1;k.KDC(3,t);k.KO(-1,t,"ព");k.KO(-1,t,"្");k.KO(-1,t,"វា");}else if(k.KFCM(2,t,['េ','ា'])){m=1;k.KDC(2,t);k.KO(-1,t,"ោ");}else if(k.KFCM(2,t,['ា','េ'])){m=1;k.KDC(2,t);k.KO(-1,t,"ោ");}else if(k.KFCM(2,t,['េ','ី'])){m=1;k.KDC(2,t);k.KO(-1,t,"ើ");}else if(k.KFCM(2,t,['ី','េ'])){m=1;k.KDC(2,t);k.KO(-1,t,"ើ");}else if(k.KFCM(2,t,['ំ','ុ'])){m=1;k.KDC(2,t);k.KO(-1,t,"ុំ");}else if(k.KFCM(2,t,['ំ','ា'])){m=1;k.KDC(2,t);k.KO(-1,t,"ាំ");}else if(k.KFCM(2,t,[{t:'a',a:this.s15},{t:'a',a:this.s15}])){m=1;k.KDC(2,t);k.KIO(-1,this.s15,2,t);}else if(k.KFCM(2,t,[{t:'a',a:this.s17},{t:'a',a:this.s17}])){m=1;k.KDC(2,t);k.KIO(-1,this.s17,2,t);}if(m) {}else if(k.KFCM(2,t,['្','្'])){m=1;k.KDC(2,t);k.KO(-1,t,"្");}else if(k.KFCM(2,t,['្',{t:'a',a:this.s20}])){m=1;k.KDC(2,t);k.KIO(-1,this.s20,2,t);}else if(k.KFCM(2,t,[{t:'a',a:this.s20},{t:'a',a:this.s29}])){m=1;k.KDC(2,t);k.KIO(-1,this.s29,2,t);k.KIO(-1,this.s20,1,t);}else if(k.KFCM(2,t,['ឫ','ុ'])){m=1;k.KDC(2,t);k.KO(-1,t,"ឬ");}else if(k.KFCM(2,t,['ឭ','ា'])){m=1;k.KDC(2,t);k.KO(-1,t,"ញ");}else if(k.KFCM(2,t,['ឮ','ា'])){m=1;k.KDC(2,t);k.KO(-1,t,"ញ");}else if(k.KFCM(2,t,['ឭ','ុ'])){m=1;k.KDC(2,t);k.KO(-1,t,"ឮ");}else if(k.KFCM(2,t,['ឧ','ិ'])){m=1;k.KDC(2,t);k.KO(-1,t,"ឱ");}else if(k.KFCM(2,t,['ឧ','៌'])){m=1;k.KDC(2,t);k.KO(-1,t,"ឱ");}else if(k.KFCM(2,t,['ឧ','៍'])){m=1;k.KDC(2,t);k.KO(-1,t,"ឱ");}return r;};} \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/kmp_2.0/khmer_angkor.kmx b/developer/src/kmc-package/test/fixtures/kmp_2.0/khmer_angkor.kmx new file mode 100644 index 00000000000..c3d0289f486 Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/kmp_2.0/khmer_angkor.kmx differ diff --git a/developer/src/kmc-package/test/fixtures/kmp_2.0/khmer_angkor.kps b/developer/src/kmc-package/test/fixtures/kmp_2.0/khmer_angkor.kps new file mode 100644 index 00000000000..15867cd2fee --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/kmp_2.0/khmer_angkor.kps @@ -0,0 +1,159 @@ + + + + 15.0.266.0 + 7.0 + + + + readme.htm + splash.gif + + + + + + + + + + Khmer Angkor + © 2015-2022 SIL International + Makara Sok + + https://keyman.com/keyboards/khmer_angkor + # Khmer Angkor + +Khmer Unicode keyboard layout based on the NiDA keyboard layout. Automatically corrects many common keying errors, including: + +* Using wrong vowel combination +* Typing clusters out of order +* Typing wrong mark for consonant shifters +* And more! + + + + khmer_angkor.js + File khmer_angkor.js + 0 + .js + + + khmer_angkor.kvk + File khmer_angkor.kvk + 0 + .kvk + + + khmer_angkor.kmx + Keyboard Khmer Angkor + 0 + .kmx + + + 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 + + + DejaVuSans.ttf + Font DejaVuSans + 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/kmp_2.0/kmp.json b/developer/src/kmc-package/test/fixtures/kmp_2.0/kmp.json new file mode 100644 index 00000000000..8cc2f03cd69 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/kmp_2.0/kmp.json @@ -0,0 +1,141 @@ +{ + "system": { + "keymanDeveloperVersion": "16.0.60.0", + "fileVersion": "7.0" + }, + "options": { + "readmeFile": "readme.htm", + "graphicFile": "splash.gif" + }, + "info": { + "name": { + "description": "Khmer Angkor" + }, + "copyright": { + "description": "\u00A9 2015-2022 SIL International" + }, + "author": { + "description": "Makara Sok", + "url": "mailto:makara_sok@sil.org" + }, + "version": { + "description": "1.3" + }, + "website": { + "description": "https://keyman.com/keyboards/khmer_angkor", + "url": "https://keyman.com/keyboards/khmer_angkor" + }, + "description": { + "description": "

Khmer Angkor

\n

Khmer Unicode keyboard layout based on the NiDA keyboard layout. Automatically corrects many common keying errors, including:

\n
    \n
  • Using wrong vowel combination
  • \n
  • Typing clusters out of order
  • \n
  • Typing wrong mark for consonant shifters
  • \n
  • And more!
  • \n
" + } + }, + "files": [ + { + "name": "khmer_angkor.js", + "description": "File khmer_angkor.js" + }, + { + "name": "khmer_angkor.kvk", + "description": "File khmer_angkor.kvk" + }, + { + "name": "khmer_angkor.kmx", + "description": "Keyboard Khmer Angkor" + }, + { + "name": "keyboard_layout.png", + "description": "File keyboard_layout.png" + }, + { + "name": "welcome.htm", + "description": "File welcome.htm" + }, + { + "name": "FONTLOG.txt", + "description": "File FONTLOG.txt" + }, + { + "name": "Mondulkiri-R.ttf", + "description": "Font Khmer Mondulkiri" + }, + { + "name": "OFL.txt", + "description": "File OFL.txt" + }, + { + "name": "OFL-FAQ.txt", + "description": "File OFL-FAQ.txt" + }, + { + "name": "KAK_Documentation_EN.pdf", + "description": "File KAK_Documentation_EN.pdf" + }, + { + "name": "KAK_Documentation_KH.pdf", + "description": "File KAK_Documentation_KH.pdf" + }, + { + "name": "readme.htm", + "description": "File readme.htm" + }, + { + "name": "image002.png", + "description": "File image002.png" + }, + { + "name": "khmer_busra_kbd.ttf", + "description": "Font KhmerBusraKbd" + }, + { + "description": "Font DejaVuSans", + "name": "DejaVuSans.ttf" + }, + { + "name": "splash.gif", + "description": "File splash.gif" + }, + { + "name": "kmp.inf", + "description": "Package information" + }, + { + "name": "kmp.json", + "description": "Package information (JSON)" + } + ], + "keyboards": [ + { + "name": "Khmer Angkor", + "id": "khmer_angkor", + "version": "1.3", + "oskFont": "khmer_busra_kbd.ttf", + "displayFont": "Mondulkiri-R.ttf", + "webOskFonts": ["khmer_busra_kbd.ttf"], + "webDisplayFonts": ["DejaVuSans.ttf"], + "languages": [ + { + "name": "Central Khmer (Khmer, Cambodia)", + "id": "km" + } + ], + "examples": [ + { + "id": "km", + "keys": "x j m E r", + "text": "ខ្មែរ", + "note": "Name of language" + }, + { + "id": "km", + "keys": "B j r H r a C a N a c k j r k m j B u C a", + "text": "ព្រះរាជាណាចក្រកម្ពុជា", + "note": "'Kingdom of Cambodia'" + } + ] + } + ], + "relatedPackages": [ + { "id": "modern_khmer", "relationship": "deprecates" }, + { "id": "khmer_nida", "relationship": "related" } + ] +} diff --git a/developer/src/test/auto/keyboard-package-versions/test-1.0/test-1.0.kpj b/developer/src/kmc-package/test/fixtures/versioning/test-1.0/test-1.0.kpj similarity index 100% rename from developer/src/test/auto/keyboard-package-versions/test-1.0/test-1.0.kpj rename to developer/src/kmc-package/test/fixtures/versioning/test-1.0/test-1.0.kpj diff --git a/developer/src/kmc-package/test/fixtures/versioning/test-1.0/test.js b/developer/src/kmc-package/test/fixtures/versioning/test-1.0/test.js new file mode 100644 index 00000000000..9ee538d9ae4 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/versioning/test-1.0/test.js @@ -0,0 +1 @@ +KeymanWeb.KR(new Keyboard_test());function Keyboard_test(){this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;this.KI="Keyboard_test";this.KN="Keyboard Version Test";this.KMINVER="9.0";this.KV=null;this.KDU=0;this.KH='';this.KM=0;this.KBVER="1.0";this.KMBM=0x0;this.KVER="17.0.159.0";this.KVS=[];this.gs=function(t,e) {return this.g0(t,e);};this.gs=function(t,e) {return this.g0(t,e);};this.g0=function(t,e) {var k=KeymanWeb,r=0,m=0;return r;};} \ No newline at end of file diff --git a/developer/src/test/auto/keyboard-package-versions/test-1.0/test.kmn b/developer/src/kmc-package/test/fixtures/versioning/test-1.0/test.kmn similarity index 100% rename from developer/src/test/auto/keyboard-package-versions/test-1.0/test.kmn rename to developer/src/kmc-package/test/fixtures/versioning/test-1.0/test.kmn diff --git a/developer/src/kmc-package/test/fixtures/versioning/test-1.0/test.kmx b/developer/src/kmc-package/test/fixtures/versioning/test-1.0/test.kmx new file mode 100644 index 00000000000..9e4bd15d391 Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/versioning/test-1.0/test.kmx differ diff --git a/developer/src/test/auto/keyboard-package-versions/test-2.0/test-2.0.kpj b/developer/src/kmc-package/test/fixtures/versioning/test-2.0/test-2.0.kpj similarity index 100% rename from developer/src/test/auto/keyboard-package-versions/test-2.0/test-2.0.kpj rename to developer/src/kmc-package/test/fixtures/versioning/test-2.0/test-2.0.kpj diff --git a/developer/src/kmc-package/test/fixtures/versioning/test-2.0/test.js b/developer/src/kmc-package/test/fixtures/versioning/test-2.0/test.js new file mode 100644 index 00000000000..788a3d58ffa --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/versioning/test-2.0/test.js @@ -0,0 +1 @@ +KeymanWeb.KR(new Keyboard_test());function Keyboard_test(){this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;this.KI="Keyboard_test";this.KN="Keyboard Version Test";this.KMINVER="9.0";this.KV=null;this.KDU=0;this.KH='';this.KM=0;this.KBVER="2.0";this.KMBM=0x0;this.KVER="17.0.159.0";this.KVS=[];this.gs=function(t,e) {return this.g0(t,e);};this.gs=function(t,e) {return this.g0(t,e);};this.g0=function(t,e) {var k=KeymanWeb,r=0,m=0;return r;};} \ No newline at end of file diff --git a/developer/src/test/auto/keyboard-package-versions/test-2.0/test.kmn b/developer/src/kmc-package/test/fixtures/versioning/test-2.0/test.kmn similarity index 100% rename from developer/src/test/auto/keyboard-package-versions/test-2.0/test.kmn rename to developer/src/kmc-package/test/fixtures/versioning/test-2.0/test.kmn diff --git a/developer/src/kmc-package/test/fixtures/versioning/test-2.0/test.kmx b/developer/src/kmc-package/test/fixtures/versioning/test-2.0/test.kmx new file mode 100644 index 00000000000..0efeffe9743 Binary files /dev/null and b/developer/src/kmc-package/test/fixtures/versioning/test-2.0/test.kmx differ diff --git a/developer/src/test/auto/keyboard-package-versions/test-keyboard-1-vs-package-2.kps b/developer/src/kmc-package/test/fixtures/versioning/test-keyboard-1-vs-package-2.kps similarity index 100% rename from developer/src/test/auto/keyboard-package-versions/test-keyboard-1-vs-package-2.kps rename to developer/src/kmc-package/test/fixtures/versioning/test-keyboard-1-vs-package-2.kps diff --git a/developer/src/test/auto/keyboard-package-versions/test-package-1-vs-keyboard-2.kps b/developer/src/kmc-package/test/fixtures/versioning/test-package-1-vs-keyboard-2.kps similarity index 100% rename from developer/src/test/auto/keyboard-package-versions/test-package-1-vs-keyboard-2.kps rename to developer/src/kmc-package/test/fixtures/versioning/test-package-1-vs-keyboard-2.kps diff --git a/developer/src/test/auto/keyboard-package-versions/test1-2.kps b/developer/src/kmc-package/test/fixtures/versioning/test1-2.kps similarity index 100% rename from developer/src/test/auto/keyboard-package-versions/test1-2.kps rename to developer/src/kmc-package/test/fixtures/versioning/test1-2.kps diff --git a/developer/src/test/auto/keyboard-package-versions/test1.kps b/developer/src/kmc-package/test/fixtures/versioning/test1.kps similarity index 100% rename from developer/src/test/auto/keyboard-package-versions/test1.kps rename to developer/src/kmc-package/test/fixtures/versioning/test1.kps diff --git a/developer/src/test/auto/keyboard-package-versions/test2-1.kps b/developer/src/kmc-package/test/fixtures/versioning/test2-1.kps similarity index 100% rename from developer/src/test/auto/keyboard-package-versions/test2-1.kps rename to developer/src/kmc-package/test/fixtures/versioning/test2-1.kps diff --git a/developer/src/test/auto/keyboard-package-versions/test2.kps b/developer/src/kmc-package/test/fixtures/versioning/test2.kps similarity index 100% rename from developer/src/test/auto/keyboard-package-versions/test2.kps rename to developer/src/kmc-package/test/fixtures/versioning/test2.kps diff --git a/developer/src/kmc-package/test/fixtures/windows-installer/keymandesktop.txt b/developer/src/kmc-package/test/fixtures/windows-installer/keymandesktop.txt new file mode 100644 index 00000000000..96d012d7d1a --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/windows-installer/keymandesktop.txt @@ -0,0 +1,3 @@ +This placeholder file represents keymandesktop.msi, the Windows Installer package for Keyman for Windows. + +(Not including the real file to reduce file size in repo, as it is not necessary in order to complete the unit test) \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/windows-installer/license.txt b/developer/src/kmc-package/test/fixtures/windows-installer/license.txt new file mode 100644 index 00000000000..cb2aa8919fb --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/windows-installer/license.txt @@ -0,0 +1,3 @@ +This placeholder file represents license.txt. + +(Not including the real file to reduce file size in repo, as it is not necessary in order to complete the unit test) \ No newline at end of file diff --git a/developer/src/kmc-package/test/fixtures/windows-installer/setup.txt b/developer/src/kmc-package/test/fixtures/windows-installer/setup.txt new file mode 100644 index 00000000000..1a72b86ef09 --- /dev/null +++ b/developer/src/kmc-package/test/fixtures/windows-installer/setup.txt @@ -0,0 +1,3 @@ +This placeholder file represents setup-redist.exe, the sfx loader. + +(Not including the real file to reduce file size in repo, as it is not necessary in order to complete the unit test) \ No newline at end of file diff --git a/developer/src/kmc-package/test/test-markdown.ts b/developer/src/kmc-package/test/test-markdown.ts new file mode 100644 index 00000000000..348d6426a7a --- /dev/null +++ b/developer/src/kmc-package/test/test-markdown.ts @@ -0,0 +1,20 @@ +import { assert } from 'chai'; +import 'mocha'; +import { markdownToHTML } from '../src/compiler/markdown.js'; + +describe('markdownToHTML', function () { + it('should convert markdown into HTML', function() { + const html = markdownToHTML('# heading\n\n**bold** and _beautiful_', true); + assert.equal(html, `

heading

\n

bold and beautiful

\n`); + }); + + it('should strip inline html if asked to do so', function() { + const html = markdownToHTML(`# heading\n\n\n\n**bold** and _beautiful_`, false); + assert.equal(html, `

heading

\n

bold and beautiful

\n`); + }); + + it('should keep inline html if asked to do so', function() { + const html = markdownToHTML(`# heading\n\n\n\n**bold** and _beautiful_`, true); + assert.equal(html, `

heading

\n\n\n

bold and beautiful

\n`); + }); +}); diff --git a/developer/src/kmc-package/test/test-messages.ts b/developer/src/kmc-package/test/test-messages.ts index e1d463dc4f2..f9649e9c090 100644 --- a/developer/src/kmc-package/test/test-messages.ts +++ b/developer/src/kmc-package/test/test-messages.ts @@ -95,16 +95,16 @@ describe('CompilerMessages', function () { testForMessage(this, ['invalid', 'error_package_cannot_contain_both_models_and_keyboards.kps'], CompilerMessages.ERROR_PackageCannotContainBothModelsAndKeyboards); }); - // WARN_PackageShouldNotRepeatLanguages (models) + // HINT_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); + it('should generate HINT_PackageShouldNotRepeatLanguages if model has same language repeated', async function() { + testForMessage(this, ['invalid', 'keyman.en.hint_package_should_not_repeat_languages.model.kps'], CompilerMessages.HINT_PackageShouldNotRepeatLanguages); }); - // WARN_PackageShouldNotRepeatLanguages (keyboards) + // HINT_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); + it('should generate HINT_PackageShouldNotRepeatLanguages if keyboard has same language repeated', async function() { + testForMessage(this, ['invalid', 'hint_package_should_not_repeat_languages.kps'], CompilerMessages.HINT_PackageShouldNotRepeatLanguages); }); // WARN_PackageNameDoesNotFollowLexicalModelConventions @@ -128,6 +128,13 @@ describe('CompilerMessages', function () { CompilerMessages.WARN_FileInPackageDoesNotFollowFilenameConventions, {checkFilenameConventions: true}); }); + // Test the inverse -- no warning generated if checkFilenameConventions is false + + it('should not generate WARN_FileInPackageDoesNotFollowFilenameConventions if content filename has wrong conventions but checkFilenameConventions is false', async function() { + testForMessage(this, ['invalid', 'warn_file_in_package_does_not_follow_filename_conventions.kps'], null, {checkFilenameConventions: false}); + testForMessage(this, ['invalid', 'warn_file_in_package_does_not_follow_filename_conventions_2.kps'], null, {checkFilenameConventions: false}); + }); + // ERROR_PackageNameCannotBeBlank it('should generate ERROR_PackageNameCannotBeBlank if package info has empty name', async function() { @@ -153,10 +160,10 @@ describe('CompilerMessages', function () { testForMessage(this, ['invalid', 'error_language_tag_is_not_valid.kps'], CompilerMessages.ERROR_LanguageTagIsNotValid); }); - // WARN_LanguageTagIsNotMinimal + // HINT_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); + it('should generate HINT_LanguageTagIsNotMinimal if keyboard has a non-minimal language tag', async function() { + testForMessage(this, ['invalid', 'hint_language_tag_is_not_minimal.kps'], CompilerMessages.HINT_LanguageTagIsNotMinimal); }); // ERROR_ModelMustHaveAtLeastOneLanguage @@ -182,7 +189,7 @@ describe('CompilerMessages', function () { // ERROR_PackageMustContainAPackageOrAKeyboard - it('should generate ERROR_PackageMustContainAModelOrAKeyboard if package contains a .doc file', async function() { + it('should generate ERROR_PackageMustContainAModelOrAKeyboard if package contains no keyboard or model', async function() { testForMessage(this, ['invalid', 'error_package_must_contain_a_model_or_a_keyboard.kps'], CompilerMessages.ERROR_PackageMustContainAModelOrAKeyboard); }); @@ -201,4 +208,22 @@ describe('CompilerMessages', function () { CompilerMessages.WARN_KeyboardShouldHaveAtLeastOneLanguage); }); + // HINT_JsKeyboardFileHasNoTouchTargets + + it('should generate HINT_JsKeyboardFileHasNoTouchTargets if keyboard has no touch targets', async function() { + testForMessage(this, ['invalid', 'hint_js_keyboard_file_has_no_touch_targets.kps'], + CompilerMessages.HINT_JsKeyboardFileHasNoTouchTargets); + }); + + it('should not generate HINT_JsKeyboardFileHasNoTouchTargets if keyboard has a touch target', async function() { + testForMessage(this, ['khmer_angkor', 'source', 'khmer_angkor.kps'], null); + }); + + // HINT_PackageContainsSourceFile + + it('should generate HINT_PackageContainsSourceFile if package contains a source file', async function() { + testForMessage(this, ['invalid', 'hint_source_file_should_not_be_in_package.kps'], + CompilerMessages.HINT_PackageContainsSourceFile); + }); + }); diff --git a/developer/src/kmc-package/test/test-package-compiler.ts b/developer/src/kmc-package/test/test-package-compiler.ts index d33d6803e1d..433d730c181 100644 --- a/developer/src/kmc-package/test/test-package-compiler.ts +++ b/developer/src/kmc-package/test/test-package-compiler.ts @@ -155,6 +155,34 @@ describe('KmpCompiler', function () { assert.deepEqual(kmpJsonActual, kmpJsonFixture); }); + it(`should support .kps 17.0 metadata correctly`, function () { + callbacks.clear(); + + const kpsPath = makePathToFixture('kmp_2.0', 'khmer_angkor.kps'); + const kmpJsonRefPath = makePathToFixture('kmp_2.0', 'kmp.json'); + + debugger; + + let kmpJsonActual = kmpCompiler.transformKpsToKmpObject(kpsPath); + if(kmpJsonActual == null) { + callbacks.printMessages(); + assert.isNotNull(kmpJsonActual); + } + let kmpJsonFixture = JSON.parse(fs.readFileSync(kmpJsonRefPath, 'utf-8')); + assert.isNotNull(kmpJsonFixture); + + // Blank out system.keymanDeveloperVersion which will vary + kmpJsonActual.system.keymanDeveloperVersion = '-'; + kmpJsonFixture.system.keymanDeveloperVersion = '-'; + + // Strip file paths to basename for the actual (this is currently part of the + // zip phase and not unit testable without refactoring) + for(const file of kmpJsonActual.files) { + file.name = path.basename(file.name); + } + + assert.deepEqual(kmpJsonActual, kmpJsonFixture); + }); /* * Testing Warnings and Errors */ diff --git a/developer/src/kmc-package/test/test-versioning.ts b/developer/src/kmc-package/test/test-versioning.ts new file mode 100644 index 00000000000..e9771a369ab --- /dev/null +++ b/developer/src/kmc-package/test/test-versioning.ts @@ -0,0 +1,42 @@ +import 'mocha'; +import { assert } from 'chai'; +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; +import { makePathToFixture } from './helpers/index.js'; +import { KmpCompiler } from '../src/compiler/kmp-compiler.js'; +import { KmpJsonFile } from '@keymanapp/common-types'; + +// This unit test was translated from a Delphi test +// Keyman.Test.System.CompilePackageVersioningTest, but note the difference in +// test results documented below. + +describe('package versioning', function () { + + const cases: [string,string][] = [ + ['test-single-version-1-package', 'test1.kps'], + ['test-single-version-2-package', 'test2.kps'], + + // The test result for the following two packages has changed in Keyman 17, + // in commit 92c8c6497e987a0583c62c91515884e6e36ece23, to 'shouldPass': + // + // `WARN_KeyboardVersionsDoNotMatchPackageVersion` has been removed, because + // it did not really make sense; if 'FollowKeyboardVersion' is set, it could + // not be raised, and otherwise, the author may wish to have separate + // keyboard + package versions anyway. + + ['test-version-2-1-mismatch', 'test2-1.kps'], + ['test-version-1-2-mismatch', 'test1-2.kps'], + + ['test-keyboard-1-package-2', 'test-keyboard-1-vs-package-2.kps'], + ['test-package-1-keyboard-2', 'test-package-1-vs-keyboard-2.kps'], + ]; + + for(const [ caseTitle, filename ] of cases) { + it(caseTitle, async function () { + const callbacks = new TestCompilerCallbacks(); + const kmpCompiler = new KmpCompiler(callbacks); + const kpsPath = makePathToFixture('versioning', filename); + const kmpJson: KmpJsonFile.KmpJsonFile = kmpCompiler.transformKpsToKmpObject(kpsPath); + assert.isTrue(kmpJson !== null); + }); + } +}); diff --git a/developer/src/kmc-package/test/test-windows-package-installer-compiler.ts b/developer/src/kmc-package/test/test-windows-package-installer-compiler.ts new file mode 100644 index 00000000000..8ed2fee0e7b --- /dev/null +++ b/developer/src/kmc-package/test/test-windows-package-installer-compiler.ts @@ -0,0 +1,79 @@ +import 'mocha'; +import * as fs from 'fs'; +import * as path from 'path'; +import { assert } from 'chai'; +import JSZip from 'jszip'; +import KEYMAN_VERSION from "@keymanapp/keyman-version"; +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; +import { makePathToFixture } from './helpers/index.js'; +import { WindowsPackageInstallerCompiler, WindowsPackageInstallerSources } from '../src/compiler/windows-package-installer-compiler.js'; + +describe('WindowsPackageInstallerCompiler', function () { + it(`should build an SFX archive`, async function () { + this.timeout(10000); // this test can take a little while to run + + const callbacks = new TestCompilerCallbacks(); + let compiler = new WindowsPackageInstallerCompiler(callbacks); + + const kpsPath = makePathToFixture('khmer_angkor', 'source', 'khmer_angkor.kps'); + const sources: WindowsPackageInstallerSources = { + licenseFilename: makePathToFixture('windows-installer', 'license.txt'), + msiFilename: makePathToFixture('windows-installer', 'keymandesktop.txt'), + setupExeFilename: makePathToFixture('windows-installer', 'setup.txt'), + startDisabled: false, + startWithConfiguration: true, + appName: 'Testing', + }; + + const sfxBuffer = await compiler.compile(kpsPath, sources); + + // This returns a buffer with a SFX loader and a zip suffix. For the sake of repository size + // we actually provide a stub SFX loader and a stub MSI file, which is enough to verify that + // the compiler is generating what it thinks is a valid file. + + const zip = JSZip(); + + // Check that file.kmp contains just 3 files - setup.inf, keymandesktop.msi, and khmer_angkor.kmp, + // and that they match exactly what we expect + const setupExeSize = fs.statSync(sources.setupExeFilename).size; + const setupExe = sfxBuffer.slice(0, setupExeSize); + const zipBuffer = sfxBuffer.slice(setupExeSize); + + // Verify setup.exe sfx loader + const setupExeFixture = fs.readFileSync(sources.setupExeFilename); + assert.deepEqual(setupExe, setupExeFixture); + + // Load the zip from the buffer + const zipFile = await zip.loadAsync(zipBuffer, {checkCRC32: true}); + + // Verify setup.inf; note that BitmapFileName splash.gif comes from the .kmp + const setupInfFixture = `[Setup] +Version=${KEYMAN_VERSION.VERSION} +MSIFileName=${path.basename(sources.msiFilename)} +MSIOptions= +AppName=${sources.appName} +License=${path.basename(sources.licenseFilename)} +BitmapFileName=splash.gif +StartWithConfiguration=True + +[Packages] +khmer_angkor.kmp +`; + + const setupInf = new TextDecoder().decode(await zipFile.file('setup.inf').async('uint8array')); + assert.equal(setupInf.trim(), setupInfFixture.trim()); + + const verifyFile = async (filename: string) => { + const fixture = fs.readFileSync(filename); + const file = await zipFile.file(path.basename(filename)).async('uint8array'); + assert.deepEqual(file, fixture, `File in zip '${filename}' did not match fixture`); + }; + + await verifyFile(sources.msiFilename); + await verifyFile(sources.licenseFilename); + + // We only test for existence of the file in the zip for now + const kmp = await zipFile.file('khmer_angkor.kmp').async('uint8array'); + assert.isNotEmpty(kmp); + }); +}); diff --git a/developer/src/kmc/README.md b/developer/src/kmc/README.md index f0c816bbb54..31022383d0a 100644 --- a/developer/src/kmc/README.md +++ b/developer/src/kmc/README.md @@ -9,13 +9,11 @@ This package provides the following Keyman **command line tools**: file. - `kmlmp` — uses a `.model.kmp` file to generate a redistributable **lexical model package**. - - `kmlmi` — merges Keyman lexical model `.model_info` files. `kmlmc` is intended to be used standalone, or as part of a build system. `kmlmp` -is used only by command line tools. `kmlmi` is used exclusively in the -[lexical-models repository][lexical models]. +is used only by command line tools. -Note: `kmc` will in the future replace `kmlmc`, `kmlmp`, and `kmlmi`. +Note: `kmc` will in the future replace `kmlmc` and `kmlmp`. In order to build [lexical models][], these tools must be built and compiled. @@ -40,18 +38,29 @@ To see more command line options by using the `--help` option: kmc --help -kmlmc Usage ------------ +--- -To compile a lexical model from its `.model.ts` source, use `kmlmc`: +To compile a lexical model from its `.model.ts` source, use `kmc`: - kmlmc my-lexical-model.model.ts --outFile my-lexical-model.js + kmc build my-lexical-model.model.ts --outFile my-lexical-model.model.js To see more command line options by using the `--help` option: - kmlmc --help - kmlmp --help - kmlmi --help + kmc --help + +--- + +kmc can now build package installers for Windows. Example usage (Bash on +Windows, using 'node .' instead of 'kmc' to run the local build): + +``` +node . build windows-package-installer \ + $KEYMAN_ROOT/developer/src/kmc-package/test/fixtures/khmer_angkor/source/khmer_angkor.kps \ + --msi /c/Program\ Files\ \(x86\)/Common\ Files/Keyman/Cached\ Installer\ Files/keymandesktop.msi \ + --exe $KEYMAN_ROOT/windows/bin/desktop/setup-redist.exe \ + --license $KEYMAN_ROOT/LICENSE.md \ + --out-file ./khmer.exe +``` How to build from source ------------------------ diff --git a/developer/src/kmc/build-bundler.js b/developer/src/kmc/build-bundler.js index e24ca39da0f..9b5f9cdd9bd 100644 --- a/developer/src/kmc/build-bundler.js +++ b/developer/src/kmc/build-bundler.js @@ -8,7 +8,6 @@ await esbuild.build({ entryPoints: [ 'build/src/kmc.js', 'build/src/kmlmc.js', - 'build/src/kmlmi.js', 'build/src/kmlmp.js', ], bundle: true, diff --git a/developer/src/kmc/build.sh b/developer/src/kmc/build.sh index e7c809419ef..d000c99c8d5 100755 --- a/developer/src/kmc/build.sh +++ b/developer/src/kmc/build.sh @@ -20,6 +20,7 @@ builder_describe "Build Keyman Keyboard Compiler kmc" \ "@/common/include" \ "@/common/web/keyman-version" \ "@/common/web/types" \ + "@/developer/src/common/web/utils" \ "@/developer/src/kmc-analyze" \ "@/developer/src/kmc-keyboard-info" \ "@/developer/src/kmc-kmn" \ @@ -74,9 +75,10 @@ fi if builder_start_action test; then eslint . tsc --build test/ - mocha - # TODO: enable c8 (disabled because no coverage at present) - # && c8 --reporter=lcov --reporter=text mocha + readonly C8_THRESHOLD=35 + c8 --reporter=lcov --reporter=text --lines $C8_THRESHOLD --statements $C8_THRESHOLD --branches $C8_THRESHOLD --functions $C8_THRESHOLD mocha + builder_echo warning "Coverage thresholds are currently $C8_THRESHOLD%, which is lower than ideal." + builder_echo warning "Please increase threshold in build.sh as test coverage improves." builder_finish_action success test fi diff --git a/developer/src/kmc/package.json b/developer/src/kmc/package.json index b3ae21c078e..7f9e0a9053a 100644 --- a/developer/src/kmc/package.json +++ b/developer/src/kmc/package.json @@ -11,10 +11,9 @@ ], "scripts": { "build": "tsc -b", - "bundle": "npm run bundle-kmc && npm run bundle-kmlmc && npm run bundle-kmlmi && npm run bundle-kmlmp", + "bundle": "npm run bundle-kmc && npm run bundle-kmlmc && npm run bundle-kmlmp", "bundle-kmc": "esbuild build/src/kmc.js --bundle --platform=node --target=es2022 > build/cjs-src/kmc.cjs", "bundle-kmlmc": "esbuild build/src/kmlmc.js --bundle --platform=node --target=es2022 > build/cjs-src/kmlmc.cjs", - "bundle-kmlmi": "esbuild build/src/kmlmi.js --bundle --platform=node --target=es2022 > build/cjs-src/kmlmi.cjs", "bundle-kmlmp": "esbuild build/src/kmlmp.js --bundle --platform=node --target=es2022 > build/cjs-src/kmlmp.cjs", "test": "eslint . && cd test && tsc -b && cd .. && mocha", "prepublishOnly": "npm run build" @@ -33,8 +32,7 @@ "bin": { "kmc": "build/src/kmc.js", "kmlmc": "build/src/kmlmc.js", - "kmlmp": "build/src/kmlmp.js", - "kmlmi": "build/src/kmlmi.js" + "kmlmp": "build/src/kmlmp.js" }, "dependencies": { "@keymanapp/common-types": "*", @@ -60,7 +58,6 @@ "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", - "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", "esbuild": "^0.15.8", diff --git a/developer/src/kmc/src/commands/analyze.ts b/developer/src/kmc/src/commands/analyze.ts index eceb29f7bf2..e878cc49221 100644 --- a/developer/src/kmc/src/commands/analyze.ts +++ b/developer/src/kmc/src/commands/analyze.ts @@ -22,14 +22,12 @@ interface AnalysisActivityOptions /* not inheriting from CompilerBaseOptions */ export function declareAnalyze(program: Command) { let command = program.command('analyze [infile...]'); - BaseOptions.addVersion(command); declareOskCharUse(command); declareOskRewrite(command); } function declareOskCharUse(command: Command) { let subCommand = command.command('osk-char-use'); - BaseOptions.addVersion(subCommand); BaseOptions.addLogLevel(subCommand); subCommand .description('Analyze On Screen Keyboards for character usage') @@ -37,7 +35,8 @@ function declareOskCharUse(command: Command) { .option('--include-counts', 'Include number of times each character is referenced', false) .option('--strip-dotted-circle', 'Strip U+25CC (dotted circle base) from results', false) .addOption(new Option('-m, --mapping-file ', 'Result file to write to (.json, .md, or .txt)').makeOptionMandatory()) - .action(async (filenames: string[], options: any) => { + .action(async (filenames: string[], _options: any, commander: any) => { + const options = commander.optsWithGlobals(); if(!filenames.length) { // If there are no filenames provided, then we are building the current // folder ('.') as a project-style build diff --git a/developer/src/kmc/src/commands/build.ts b/developer/src/kmc/src/commands/build.ts index 1047dd8208c..0d091680b7c 100644 --- a/developer/src/kmc/src/commands/build.ts +++ b/developer/src/kmc/src/commands/build.ts @@ -9,9 +9,11 @@ import { CompilerFileCallbacks, CompilerOptions, KeymanFileTypes } from '@keyman import { BaseOptions } from '../util/baseOptions.js'; import { expandFileLists } from '../util/fileLists.js'; import { isProject } from '../util/projectLoader.js'; +import { buildTestData } from './buildTestData/index.js'; +import { buildWindowsPackageInstaller } from './buildWindowsPackageInstaller/index.js'; +import { ExtendedCompilerOptions } from 'src/util/extendedCompilerOptions.js'; - -function commandOptionsToCompilerOptions(options: any): CompilerOptions { +function commandOptionsToCompilerOptions(options: any): ExtendedCompilerOptions { // We don't want to rename command line options to match the precise // properties that we have in CompilerOptions, but nor do we want to rename // CompilerOptions properties... @@ -19,45 +21,52 @@ function commandOptionsToCompilerOptions(options: any): CompilerOptions { // CompilerBaseOptions outFile: options.outFile, logLevel: options.logLevel, + logFormat: options.logFormat, color: options.color, // CompilerOptions shouldAddCompilerVersion: options.compilerVersion, saveDebug: options.debug, compilerWarningsAsErrors: options.compilerWarningsAsErrors, warnDeprecatedCode: options.warnDeprecatedCode, + // ExtendedOptions + forPublishing: options.forPublishing, } } export function declareBuild(program: Command) { - BaseOptions.addAll(program - .command('build [infile...]') - .description(`Compile one or more source files or projects.`) + const buildCommand = program + .command('build') + .option('--color', 'Force colorization for log messages') + .option('--no-color', 'No colorization for log messages; if both omitted, detects from console') + + // These options are only used with build file but are included here so that + // they are visible in `kmc build --help` + .option('-d, --debug', 'Include debug information in output') + .option('-w, --compiler-warnings-as-errors', 'Causes warnings to fail the build; overrides project-level warnings-as-errors option') + .option('-W, --no-compiler-warnings-as-errors', 'Warnings do not fail the build; overrides project-level warnings-as-errors option') + .option('--no-compiler-version', 'Exclude compiler version metadata from output') + .option('--no-warn-deprecated-code', 'Turn off warnings for deprecated code styles'); + + BaseOptions.addAll(buildCommand); + + buildCommand.command('file [infile...]', {isDefault: true}) + .description(`Compile one or more source files or projects ('file' subcommand is default).`) + .option('--for-publishing', 'Verify that project meets @keymanapp repository requirements') .addHelpText('after', ` Supported file types: - * folder: Keyman project in folder - * .kpj: Keyman project - * .kmn: Keyman keyboard - * .xml: LDML keyboard - * .model.ts: Keyman lexical model - * .kps: Keyman keyboard or lexical model package - -The following two metadata file types are also supported: - * .model_info: lexical model metadata file - * .keyboard_info: keyboard metadata file +* folder: Keyman project in folder +* .kpj: Keyman project +* .kmn: Keyman keyboard +* .xml: LDML keyboard +* .model.ts: Keyman lexical model +* .kps: Keyman keyboard or lexical model package File lists can be referenced with @filelist.txt. If no input file is supplied, kmc will build the current folder.`) - ) - .option('-d, --debug', 'Include debug information in output') - .option('-w, --compiler-warnings-as-errors', 'Causes warnings to fail the build; overrides project-level warnings-as-errors option') - .option('-W, --no-compiler-warnings-as-errors', 'Warnings do not fail the build; overrides project-level warnings-as-errors option') - .option('--no-compiler-version', 'Exclude compiler version metadata from output') - .option('--no-warn-deprecated-code', 'Turn off warnings for deprecated code styles') - .option('--color', 'Force colorization for log messages') - .option('--no-color', 'No colorization for log messages; if both omitted, detects from console') - .action(async (filenames: string[], options: any) => { - options = commandOptionsToCompilerOptions(options); + + .action(async (filenames: string[], _options: any, commander: any) => { + const options = commandOptionsToCompilerOptions(commander.optsWithGlobals()); const callbacks = new NodeCompilerCallbacks(options); if(!filenames.length) { @@ -77,6 +86,23 @@ If no input file is supplied, kmc will build the current folder.`) } } }); + + buildCommand + .command('ldml-test-data ') + .description('Convert LDML keyboard test .xml to .json') + .action(buildTestData); + + buildCommand + .command('windows-package-installer ') + .description('Build an executable installer for Windows for a Keyman package') + .option('--msi ', 'Location of keymandesktop.msi') + .option('--exe ', 'Location of setup.exe') + .option('--license ', 'Location of license.txt') + .option('--title-image [titleImageFilename]', 'Location of title image') + .option('--app-name [applicationName]', 'Installer property: name of the application to be installed', 'Keyman') + .option('--start-disabled', 'Installer property: do not enable keyboards after installation completes') + .option('--start-with-configuration', 'Installer property: start Keyman Configuration after installation completes') + .action(buildWindowsPackageInstaller); } async function build(filename: string, parentCallbacks: NodeCompilerCallbacks, options: CompilerOptions): Promise { @@ -92,6 +118,10 @@ async function build(filename: string, parentCallbacks: NodeCompilerCallbacks, o return false; } + // Normalize case for the filename and expand the path; this avoids false + // positive case mismatches on input filenames and glommed paths + filename = fs.realpathSync.native(filename); + let builder = null; // If infile is a directory, then we treat that as a project and build it @@ -116,22 +146,25 @@ async function build(filename: string, parentCallbacks: NodeCompilerCallbacks, o if(fs.statSync(filename).isDirectory()) { buildFilename = path.join(buildFilename, path.basename(buildFilename) + KeymanFileTypes.Source.Project); } - buildFilename = path.relative(process.cwd(), buildFilename).replace(/\\/g, '/'); + const relativeFilename = path.relative(process.cwd(), buildFilename).replace(/\\/g, '/'); const callbacks = new CompilerFileCallbacks(buildFilename, options, parentCallbacks); - callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename:buildFilename})); + callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename:buildFilename, relativeFilename})); let result = await builder.build(filename, callbacks, options); result = result && !callbacks.hasFailureMessage(); if(result) { callbacks.reportMessage(builder instanceof BuildProject - ? InfrastructureMessages.Info_ProjectBuiltSuccessfully({filename:buildFilename}) - : InfrastructureMessages.Info_FileBuiltSuccessfully({filename:buildFilename}) + ? InfrastructureMessages.Info_ProjectBuiltSuccessfully({filename:buildFilename, relativeFilename}) + : InfrastructureMessages.Info_FileBuiltSuccessfully({filename:buildFilename, relativeFilename}) ); } else { + if(!callbacks.hasFailureMessage(false)) { // false == check only for error+fatal messages + callbacks.reportMessage(InfrastructureMessages.Info_WarningsHaveFailedBuild()); + } callbacks.reportMessage(builder instanceof BuildProject - ? InfrastructureMessages.Info_ProjectNotBuiltSuccessfully({filename:buildFilename}) - : InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename:buildFilename}) + ? InfrastructureMessages.Info_ProjectNotBuiltSuccessfully({filename:buildFilename, relativeFilename}) + : InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename:buildFilename, relativeFilename}) ); } diff --git a/developer/src/kmc/src/commands/buildClasses/BuildKeyboardInfo.ts b/developer/src/kmc/src/commands/buildClasses/BuildKeyboardInfo.ts index 42bc4e4a950..8d8d66938f1 100644 --- a/developer/src/kmc/src/commands/buildClasses/BuildKeyboardInfo.ts +++ b/developer/src/kmc/src/commands/buildClasses/BuildKeyboardInfo.ts @@ -1,69 +1,49 @@ import * as fs from 'fs'; import { BuildActivity } from './BuildActivity.js'; -import { CompilerCallbacks, CompilerOptions, KeymanDeveloperProject, KeymanFileTypes } from '@keymanapp/common-types'; +import { CompilerCallbacks, KeymanDeveloperProject, KeymanFileTypes } from '@keymanapp/common-types'; import { KeyboardInfoCompiler } from '@keymanapp/kmc-keyboard-info'; import { loadProject } from '../../util/projectLoader.js'; import { InfrastructureMessages } from '../../messages/infrastructureMessages.js'; -import { KmpCompiler } from '@keymanapp/kmc-package'; import { calculateSourcePath } from '../../util/calculateSourcePath.js'; - -const HelpRoot = 'https://help.keyman.com/keyboard/'; +import { getLastGitCommitDate } from '../../util/getLastGitCommitDate.js'; +import { ExtendedCompilerOptions } from 'src/util/extendedCompilerOptions.js'; export class BuildKeyboardInfo extends BuildActivity { public get name(): string { return 'Keyboard metadata'; } - public get sourceExtension(): KeymanFileTypes.Source { return KeymanFileTypes.Source.KeyboardInfo; } + public get sourceExtension(): KeymanFileTypes.Source { return KeymanFileTypes.Source.Project; } public get compiledExtension(): KeymanFileTypes.Binary { return KeymanFileTypes.Binary.KeyboardInfo; } public get description(): string { return 'Build a keyboard metadata file'; } - public async build(infile: string, callbacks: CompilerCallbacks, options: CompilerOptions): Promise { - if(KeymanFileTypes.filenameIs(infile, KeymanFileTypes.Source.KeyboardInfo)) { - // We are given a .keyboard_info but need to use the project file in the - // same folder, so that we can find the related files. This also supports - // version 2.0 projects (where the .kpj file is optional). - infile = KeymanFileTypes.replaceExtension(infile, KeymanFileTypes.Source.KeyboardInfo, KeymanFileTypes.Source.Project); + public async build(infile: string, callbacks: CompilerCallbacks, options: ExtendedCompilerOptions): Promise { + if(!KeymanFileTypes.filenameIs(infile, KeymanFileTypes.Source.Project)) { + // Even if the project file does not exist, we use its name as our reference + // in order to avoid ambiguity + throw new Error(`BuildKeyboardInfo called with unexpected file type ${infile}`); } + const project = loadProject(infile, callbacks); if(!project) { + // Error messages written by loadProject return false; } - const metadata = findProjectFile(callbacks, project, KeymanFileTypes.Source.KeyboardInfo); - if(!metadata) { - // Project loader should always have added a metadata file - return false; - } - - if(!fs.existsSync(project.resolveInputFilePath(metadata))) { - // For now, if the metadata file does not exist, we won't attempt to build - // it. One day in the future, when source metadata files become optional, - // we'll need to skip this - return true; - } - - const keyboard = findProjectFile(callbacks, project, KeymanFileTypes.Source.KeymanKeyboard); const kps = findProjectFile(callbacks, project, KeymanFileTypes.Source.Package); if(!keyboard || !kps) { + // Error messages written by findProjectFile return false; } - let kmpCompiler = new KmpCompiler(callbacks); - let kmpJsonData = kmpCompiler.transformKpsToKmpObject(project.resolveInputFilePath(kps)); - if(!kmpJsonData) { - // Errors will have been emitted by KmpCompiler - return false; - } + const jsFilename = project.resolveOutputFilePath(keyboard, KeymanFileTypes.Source.KeymanKeyboard, KeymanFileTypes.Binary.WebKeyboard); + const lastCommitDate = getLastGitCommitDate(project.projectPath); - const keyboardFileNameJs = project.resolveOutputFilePath(keyboard, KeymanFileTypes.Source.KeymanKeyboard, KeymanFileTypes.Binary.WebKeyboard); - const keyboard_id = callbacks.path.basename(metadata.filename, KeymanFileTypes.Source.KeyboardInfo); const compiler = new KeyboardInfoCompiler(callbacks); - const data = compiler.writeMergedKeyboardInfoFile(project.resolveInputFilePath(metadata), { - keyboard_id, - kmpFileName: project.resolveOutputFilePath(kps, KeymanFileTypes.Source.Package, KeymanFileTypes.Binary.Package), - kmpJsonData, - kpsFileName: project.resolveInputFilePath(kps), - helpLink: HelpRoot + keyboard_id, - keyboardFileNameJs: fs.existsSync(keyboardFileNameJs) ? keyboardFileNameJs : undefined, - sourcePath: calculateSourcePath(infile) + const data = compiler.writeKeyboardInfoFile({ + kmpFilename: project.resolveOutputFilePath(kps, KeymanFileTypes.Source.Package, KeymanFileTypes.Binary.Package), + kpsFilename: project.resolveInputFilePath(kps), + jsFilename: fs.existsSync(jsFilename) ? jsFilename : undefined, + sourcePath: calculateSourcePath(infile), + lastCommitDate, + forPublishing: !!options.forPublishing, }); if(data == null) { @@ -71,8 +51,10 @@ export class BuildKeyboardInfo extends BuildActivity { return false; } + const outputFilename = project.getOutputFilePath(KeymanFileTypes.Binary.KeyboardInfo); + fs.writeFileSync( - project.resolveOutputFilePath(metadata, KeymanFileTypes.Source.KeyboardInfo, KeymanFileTypes.Binary.KeyboardInfo), + outputFilename, data ); diff --git a/developer/src/kmc/src/commands/buildClasses/BuildKmnKeyboard.ts b/developer/src/kmc/src/commands/buildClasses/BuildKmnKeyboard.ts index 0cf9d7d152c..61dd2f97a69 100644 --- a/developer/src/kmc/src/commands/buildClasses/BuildKmnKeyboard.ts +++ b/developer/src/kmc/src/commands/buildClasses/BuildKmnKeyboard.ts @@ -3,6 +3,8 @@ import { platform } from 'os'; import { KmnCompiler } from '@keymanapp/kmc-kmn'; import { CompilerOptions, CompilerCallbacks, KeymanFileTypes } from '@keymanapp/common-types'; import { BuildActivity } from './BuildActivity.js'; +import * as fs from 'fs'; +import { InfrastructureMessages } from '../../messages/infrastructureMessages.js'; export class BuildKmnKeyboard extends BuildActivity { public get name(): string { return 'Keyman keyboard'; } @@ -16,14 +18,18 @@ export class BuildKmnKeyboard extends BuildActivity { } // We need to resolve paths to absolute paths before calling kmc-kmn - let outfile = this.getOutputFilename(infile, options); - + if(options.outFile) { + options.outFile = getPosixAbsolutePath(options.outFile); + const folderName = path.dirname(options.outFile); + try { + fs.mkdirSync(folderName, {recursive: true}); + } catch(e) { + callbacks.reportMessage(InfrastructureMessages.Error_CannotCreateFolder({folderName, e})); + return false; + } + } infile = getPosixAbsolutePath(infile); - outfile = getPosixAbsolutePath(outfile); - - // TODO: Currently this only builds .kmn->.kmx, and targeting .js is as-yet unsupported - // TODO: outfile should be set in options only? - return compiler.run(infile, outfile, options); + return compiler.run(infile, options); } } diff --git a/developer/src/kmc/src/commands/buildClasses/BuildModelInfo.ts b/developer/src/kmc/src/commands/buildClasses/BuildModelInfo.ts index 9dd4d476e7c..bddfcc38db7 100644 --- a/developer/src/kmc/src/commands/buildClasses/BuildModelInfo.ts +++ b/developer/src/kmc/src/commands/buildClasses/BuildModelInfo.ts @@ -1,15 +1,17 @@ import * as fs from 'fs'; import { BuildActivity } from './BuildActivity.js'; -import { CompilerCallbacks, CompilerOptions, KeymanFileTypes } from '@keymanapp/common-types'; -import { writeMergedModelMetadataFile } from '@keymanapp/kmc-model-info'; +import { CompilerCallbacks, KeymanFileTypes } from '@keymanapp/common-types'; +import { ModelInfoCompiler } from '@keymanapp/kmc-model-info'; import { KmpCompiler } from '@keymanapp/kmc-package'; import { loadProject } from '../../util/projectLoader.js'; import { InfrastructureMessages } from '../../messages/infrastructureMessages.js'; import { calculateSourcePath } from '../../util/calculateSourcePath.js'; +import { getLastGitCommitDate } from '../../util/getLastGitCommitDate.js'; +import { ExtendedCompilerOptions } from 'src/util/extendedCompilerOptions.js'; export class BuildModelInfo extends BuildActivity { public get name(): string { return 'Lexical model metadata'; } - public get sourceExtension(): KeymanFileTypes.Source { return KeymanFileTypes.Source.ModelInfo; } + public get sourceExtension(): KeymanFileTypes.Source { return KeymanFileTypes.Source.Project; } public get compiledExtension(): KeymanFileTypes.Binary { return KeymanFileTypes.Binary.ModelInfo; } public get description(): string { return 'Build a lexical model metadata file'; } @@ -23,24 +25,19 @@ export class BuildModelInfo extends BuildActivity { * @param options * @returns */ - public async build(infile: string, callbacks: CompilerCallbacks, options: CompilerOptions): Promise { - if(KeymanFileTypes.filenameIs(infile, KeymanFileTypes.Source.ModelInfo)) { - // We are given a .model_info but need to use the project file in the - // same folder, so that we can find the related files. - infile = KeymanFileTypes.replaceExtension(infile, KeymanFileTypes.Source.ModelInfo, KeymanFileTypes.Source.Project); + public async build(infile: string, callbacks: CompilerCallbacks, options: ExtendedCompilerOptions): Promise { + if(!KeymanFileTypes.filenameIs(infile, KeymanFileTypes.Source.Project)) { + // Even if the project file does not exist, we use its name as our reference + // in order to avoid ambiguity + throw new Error(`BuildModelInfo called with unexpected file type ${infile}`); } + const project = loadProject(infile, callbacks); if(!project) { // Error messages will be reported by loadProject return false; } - const metadata = project.files.find(file => file.fileType == KeymanFileTypes.Source.ModelInfo); - if(!metadata) { - callbacks.reportMessage(InfrastructureMessages.Error_FileTypeNotFound({ext: KeymanFileTypes.Source.ModelInfo})); - return false; - } - const model = project.files.find(file => file.fileType == KeymanFileTypes.Source.Model); if(!model) { callbacks.reportMessage(InfrastructureMessages.Error_FileTypeNotFound({ext: KeymanFileTypes.Source.Model})); @@ -60,25 +57,26 @@ export class BuildModelInfo extends BuildActivity { return false; } - const data = writeMergedModelMetadataFile( - project.resolveInputFilePath(metadata), - callbacks, - { - model_id: callbacks.path.basename(metadata.filename, KeymanFileTypes.Source.ModelInfo), - kmpJsonData, - sourcePath: calculateSourcePath(infile), - modelFileName: project.resolveOutputFilePath(model, KeymanFileTypes.Source.Model, KeymanFileTypes.Binary.Model), - kmpFileName: project.resolveOutputFilePath(kps, KeymanFileTypes.Source.Package, KeymanFileTypes.Binary.Package), - } - ); + const lastCommitDate = getLastGitCommitDate(project.projectPath); + const compiler = new ModelInfoCompiler(callbacks); + const data = compiler.writeModelMetadataFile({ + model_id: callbacks.path.basename(project.projectPath, KeymanFileTypes.Source.Project), + kmpJsonData, + sourcePath: calculateSourcePath(infile), + modelFileName: project.resolveOutputFilePath(model, KeymanFileTypes.Source.Model, KeymanFileTypes.Binary.Model), + kmpFileName: project.resolveOutputFilePath(kps, KeymanFileTypes.Source.Package, KeymanFileTypes.Binary.Package), + kpsFilename: project.resolveInputFilePath(kps), + lastCommitDate, + forPublishing: !!options.forPublishing, + }); if(data == null) { - // Error messages have already been emitted by writeMergedModelMetadataFile + // Error messages have already been emitted by writeModelMetadataFile return false; } fs.writeFileSync( - project.resolveOutputFilePath(metadata, KeymanFileTypes.Source.ModelInfo, KeymanFileTypes.Binary.ModelInfo), + project.getOutputFilePath(KeymanFileTypes.Binary.ModelInfo), data ); diff --git a/developer/src/kmc/src/commands/buildClasses/BuildProject.ts b/developer/src/kmc/src/commands/buildClasses/BuildProject.ts index 460a5af88f7..de0e55dfc16 100644 --- a/developer/src/kmc/src/commands/buildClasses/BuildProject.ts +++ b/developer/src/kmc/src/commands/buildClasses/BuildProject.ts @@ -1,17 +1,18 @@ import * as path from 'path'; import * as fs from 'fs'; -import { CompilerCallbacks, CompilerFileCallbacks, CompilerOptions, KeymanDeveloperProject, KeymanDeveloperProjectFile, KeymanFileTypes } from '@keymanapp/common-types'; +import { CompilerCallbacks, CompilerFileCallbacks, KeymanDeveloperProject, KeymanDeveloperProjectFile, KeymanFileTypes } from '@keymanapp/common-types'; import { BuildActivity } from './BuildActivity.js'; -import { buildActivities } from './buildActivities.js'; +import { buildActivities, buildKeyboardInfoActivity, buildModelInfoActivity } from './buildActivities.js'; import { InfrastructureMessages } from '../../messages/infrastructureMessages.js'; import { loadProject } from '../../util/projectLoader.js'; +import { ExtendedCompilerOptions } from 'src/util/extendedCompilerOptions.js'; export class BuildProject extends BuildActivity { public get name(): string { return 'Project'; } public get sourceExtension(): KeymanFileTypes.Source { return KeymanFileTypes.Source.Project; } public get compiledExtension(): KeymanFileTypes.Binary { return null; } public get description(): string { return 'Build a keyboard or lexical model project'; } - public async build(infile: string, callbacks: CompilerCallbacks, options: CompilerOptions): Promise { + public async build(infile: string, callbacks: CompilerCallbacks, options: ExtendedCompilerOptions): Promise { let builder = new ProjectBuilder(infile, callbacks, options); return builder.run(); } @@ -20,10 +21,10 @@ export class BuildProject extends BuildActivity { class ProjectBuilder { callbacks: CompilerCallbacks; infile: string; - options: CompilerOptions; + options: ExtendedCompilerOptions; project: KeymanDeveloperProject; - constructor(infile: string, callbacks: CompilerCallbacks, options: CompilerOptions) { + constructor(infile: string, callbacks: CompilerCallbacks, options: ExtendedCompilerOptions) { this.infile = path.resolve(infile); this.callbacks = new CompilerFileCallbacks(infile, options, callbacks); this.options = options; @@ -52,13 +53,24 @@ class ProjectBuilder { } } - // TODO: generate .keyboard_info from .kps + etc (and support merge of - // $PROJECTPATH/.keyboard_info for version 1.0 projects) + // Build project metadata + if(this.options.forPublishing || !this.project.options.skipMetadataFiles) { + if(!await (this.buildProjectTargets( + this.project.isKeyboardProject() + ? buildKeyboardInfoActivity + : buildModelInfoActivity))) { + return false; + } + } return true; } async buildProjectTargets(activity: BuildActivity): Promise { + if(activity.sourceExtension == KeymanFileTypes.Source.Project) { + return await this.buildTarget(this.project.projectFile, activity); + } + let result = true; for(let file of this.project.files) { if(file.fileType.toLowerCase() == activity.sourceExtension) { @@ -76,7 +88,7 @@ class ProjectBuilder { const buildFilename = path.relative(process.cwd(), infile).replace(/\\/g, '/'); const callbacks = new CompilerFileCallbacks(buildFilename, options, this.callbacks); - callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename: buildFilename})); + callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename: infile, relativeFilename:buildFilename})); fs.mkdirSync(path.dirname(options.outFile), {recursive:true}); @@ -87,9 +99,9 @@ class ProjectBuilder { result = result && !callbacks.hasFailureMessage(this.options.compilerWarningsAsErrors ?? this.project.options.compilerWarningsAsErrors); if(result) { - callbacks.reportMessage(InfrastructureMessages.Info_FileBuiltSuccessfully({filename: buildFilename})); + callbacks.reportMessage(InfrastructureMessages.Info_FileBuiltSuccessfully({filename: infile, relativeFilename:buildFilename})); } else { - callbacks.reportMessage(InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename: buildFilename})); + callbacks.reportMessage(InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename: infile, relativeFilename: buildFilename})); } return result; diff --git a/developer/src/kmc/src/commands/buildClasses/buildActivities.ts b/developer/src/kmc/src/commands/buildClasses/buildActivities.ts index aae271fbacb..d8d1a9b5d55 100644 --- a/developer/src/kmc/src/commands/buildClasses/buildActivities.ts +++ b/developer/src/kmc/src/commands/buildClasses/buildActivities.ts @@ -13,10 +13,13 @@ export const buildActivities: BuildActivity[] = [ new BuildLdmlKeyboard(), new BuildModel(), new BuildPackage(), - new BuildKeyboardInfo(), - new BuildModelInfo(), ]; +// These are built from the .kpj reference after all others +export const buildKeyboardInfoActivity = new BuildKeyboardInfo(); +export const buildModelInfoActivity = new BuildModelInfo(); + + // Note: BuildProject is not listed here to avoid circular references, // because it depends on the other activities here. This means that // BuildProject must be separately checked. diff --git a/developer/src/kmc/src/commands/buildTestData.ts b/developer/src/kmc/src/commands/buildTestData.ts deleted file mode 100644 index b78d1177383..00000000000 --- a/developer/src/kmc/src/commands/buildTestData.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Command } from 'commander'; -import { buildTestData } from './buildTestData/index.js'; -import { BaseOptions } from '../util/baseOptions.js'; - -export function declareBuildTestData(program: Command) { - BaseOptions.addAll(program - .command('build-test-data ') - .description('Convert keyboard test .xml to .json') - ) - .action(buildTestData); -} - - diff --git a/developer/src/kmc/src/commands/buildTestData/index.ts b/developer/src/kmc/src/commands/buildTestData/index.ts index b50c63c1d6e..97650d272ae 100644 --- a/developer/src/kmc/src/commands/buildTestData/index.ts +++ b/developer/src/kmc/src/commands/buildTestData/index.ts @@ -5,7 +5,9 @@ import { CompilerBaseOptions, CompilerCallbacks, defaultCompilerOptions, LDMLKey import { NodeCompilerCallbacks } from '../../util/NodeCompilerCallbacks.js'; import { fileURLToPath } from 'url'; -export function buildTestData(infile: string, options: CompilerBaseOptions) { +export function buildTestData(infile: string, _options: any, commander: any) { + const options: CompilerBaseOptions = commander.optsWithGlobals(); + let compilerOptions: kmcLdml.LdmlCompilerOptions = { ...defaultCompilerOptions, ...options, diff --git a/developer/src/kmc/src/commands/buildWindowsPackageInstaller/index.ts b/developer/src/kmc/src/commands/buildWindowsPackageInstaller/index.ts new file mode 100644 index 00000000000..90bd30ce7f5 --- /dev/null +++ b/developer/src/kmc/src/commands/buildWindowsPackageInstaller/index.ts @@ -0,0 +1,47 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { CompilerBaseOptions, CompilerCallbacks, defaultCompilerOptions } from '@keymanapp/common-types'; +import { NodeCompilerCallbacks } from '../../util/NodeCompilerCallbacks.js'; +import { WindowsPackageInstallerCompiler, WindowsPackageInstallerSources } from '@keymanapp/kmc-package'; + +interface WindowsPackageInstallerOptions extends CompilerBaseOptions { + msi: string; + exe: string; + license: string; + titleImage?: string; + appName?: string; + startDisabled: boolean; + startWithConfiguration: boolean; +}; + +export async function buildWindowsPackageInstaller(infile: string, _options: any, commander: any) { + const options: WindowsPackageInstallerOptions = commander.optsWithGlobals(); + const sources: WindowsPackageInstallerSources = { + licenseFilename: options.license, + msiFilename: options.msi, + setupExeFilename: options.exe, + startDisabled: options.startDisabled, + startWithConfiguration: options.startWithConfiguration, + appName: options.appName, + titleImageFilename: options.titleImage + } + + // Normalize case for the filename and expand the path; this avoids false + // positive case mismatches on input filenames and glommed paths + infile = fs.realpathSync.native(infile); + + const callbacks: CompilerCallbacks = new NodeCompilerCallbacks({...defaultCompilerOptions, ...options}); + const compiler = new WindowsPackageInstallerCompiler(callbacks); + + const buffer = await compiler.compile(infile, sources); + if(!buffer) { + // errors will have been reported already + process.exit(1); + } + + const fileBaseName = options.outFile ?? infile; + const outFileBase = path.basename(fileBaseName, path.extname(fileBaseName)); + const outFileDir = path.dirname(fileBaseName); + const outFileExe = path.join(outFileDir, outFileBase + '.exe'); + fs.writeFileSync(outFileExe, buffer); +} diff --git a/developer/src/kmc/src/kmc.ts b/developer/src/kmc/src/kmc.ts index d22fc223485..049dc7c6aad 100644 --- a/developer/src/kmc/src/kmc.ts +++ b/developer/src/kmc/src/kmc.ts @@ -3,12 +3,11 @@ * kmc - Keyman Next Generation Compiler */ -import { Command } from 'commander'; +import { Command, Option } from 'commander'; import { declareBuild } from './commands/build.js'; -import { declareBuildTestData } from './commands/buildTestData.js'; import { declareAnalyze } from './commands/analyze.js'; -import { BaseOptions } from './util/baseOptions.js'; import { KeymanSentry } from './util/KeymanSentry.js'; +import KEYMAN_VERSION from "@keymanapp/keyman-version"; await KeymanSentry.runTestIfCLRequested(); try { @@ -25,16 +24,25 @@ async function run() { /* Arguments */ const program = new Command(); - program.description('Keyman Developer Command Line Interface'); - BaseOptions.addVersion(program); - BaseOptions.addSentry(program); + program + .description('Keyman Developer Command Line Interface') + .configureHelp({ + showGlobalOptions: true + }) + .version(KEYMAN_VERSION.VERSION_WITH_TAG) + + // This corresponds to an option tested in KeymanSentry.ts, which is + // searched for in process.argv, in order to avoid depending on Commander to + // start Sentry, and to ensure that we capture errors as early as possible + // in launch + .addOption(new Option('--no-error-reporting', 'Disable error reporting to keyman.com (overriding user settings)')) + .addOption(new Option('--error-reporting', 'Enable error reporting to keyman.com (overriding user settings)')); if(await KeymanSentry.isEnabled()) { KeymanSentry.init(); } declareBuild(program); - declareBuildTestData(program); // TODO: consider renaming this (build vs build-test-data is confusing) declareAnalyze(program); /* Future commands: diff --git a/developer/src/kmc/src/kmlmi.ts b/developer/src/kmc/src/kmlmi.ts deleted file mode 100644 index 219032d0446..00000000000 --- a/developer/src/kmc/src/kmlmi.ts +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env node -/** - * kmlmi - Keyman Lexical Model model_info Compiler - */ - -import * as fs from 'fs'; -import * as path from 'path'; -import { Command } from 'commander'; -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 './util/NodeCompilerCallbacks.js'; - -let inputFilename: string; -const program = new Command(); - -/* Arguments */ -program - .description('Merges Keyman lexical model model_info files. Intended for use within the keymanapp/lexical-models repository.') - .version(KEYMAN_VERSION.VERSION_WITH_TAG) - .arguments('') - .action(infile => inputFilename = infile) - .option('-o, --outFile ', 'where to save the resultant file') - .option('-m, --model ', 'model id, defaults to basename of input file sans .model_info extension') - .option('-s, --source ', 'path to source of model, relative to lexical-models repo root') - .option('--kpsFilename ', 'path to .model.kps file, defaults to source/.model.kps') - .option('--kmpFilename ', 'path to .model.kmp file, defaults to build/.model.kmp') - .option('--jsFilename ', 'path to .model.js file, defaults to build/.model.js'); - -program.parse(process.argv); - -// Deal with input arguments: - -if (!inputFilename) { - exitDueToUsageError('Must provide a lexical model .model_info source file.'); -} - -let model_id: string = program.opts().model ? program.opts().model : path.basename(inputFilename).replace(/\.model_info$/, ""); -let outputFilename: string = program.opts().outFile ? program.opts().outFile : path.join(path.dirname(inputFilename), 'build', path.basename(inputFilename)); -let kpsFilename = program.opts().kpsFilename ? program.opts().kpsFilename : path.join(path.dirname(inputFilename), 'source', path.basename(inputFilename).replace(/\.model_info$/, '.model.kps')); -let kmpFilename = program.opts().kmpFilename ? program.opts().kmpFilename : path.join(path.dirname(inputFilename), 'build', path.basename(inputFilename).replace(/\.model_info$/, '.model.kmp')); -let jsFilename = program.opts().jsFilename ? program.opts().jsFilename : path.join(path.dirname(inputFilename), 'build', path.basename(inputFilename).replace(/\.model_info$/, '.model.js')); - -// -// Load .kps source data -// - -const callbacks = new NodeCompilerCallbacks({logLevel: 'info'}); -let kmpCompiler = new KmpCompiler(callbacks); -let kmpJsonData = kmpCompiler.transformKpsToKmpObject(kpsFilename); -if(!kmpJsonData) { - process.exit(SysExits.EX_DATAERR); -} - -// -// Validate the package file -// - -const validation = new PackageValidation(callbacks, {}); -if(!validation.validate(kpsFilename, kmpJsonData)) { - process.exit(SysExits.EX_DATAERR); -} - -// -// Write out the merged .model_info file -// - -let modelInfoOptions: ModelInfoOptions = { - model_id: model_id, - kmpJsonData: kmpJsonData, - sourcePath: program.opts().source, - modelFileName: jsFilename, - kmpFileName: kmpFilename -}; - -const data = writeMergedModelMetadataFile( - inputFilename, - callbacks, - modelInfoOptions); -if(!data) { - process.exit(SysExits.EX_DATAERR); -} -fs.writeFileSync(outputFilename, data); - -function exitDueToUsageError(message: string): never { - console.error(`${program.name()}: ${message}`); - console.error(); - program.outputHelp(); - return process.exit(SysExits.EX_USAGE); -} diff --git a/developer/src/kmc/src/messages/infrastructureMessages.ts b/developer/src/kmc/src/messages/infrastructureMessages.ts index ee828dae751..e96ed4dd3a4 100644 --- a/developer/src/kmc/src/messages/infrastructureMessages.ts +++ b/developer/src/kmc/src/messages/infrastructureMessages.ts @@ -12,8 +12,8 @@ export class InfrastructureMessages { static FATAL_UnexpectedException = SevFatal | 0x0001; // For this message, we override the filename with the passed-in file. A bit of a hack but does the job - static Info_BuildingFile = (o:{filename:string}) => ({filename:o.filename, ...m(this.INFO_BuildingFile, - `Building ${o.filename}`)}); + static Info_BuildingFile = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(this.INFO_BuildingFile, + `Building ${o.relativeFilename}`)}); static INFO_BuildingFile = SevInfo | 0x0002; static Error_FileDoesNotExist = (o:{filename:string}) => m(this.ERROR_FileDoesNotExist, @@ -29,13 +29,13 @@ export class InfrastructureMessages { static ERROR_OutFileNotValidForProjects = SevError | 0x0005; // For this message, we override the filename with the passed-in file. A bit of a hack but does the job - static Info_FileBuiltSuccessfully = (o:{filename:string}) => ({filename:o.filename, ...m(this.INFO_FileBuiltSuccessfully, - `${o.filename} built successfully.`)}); + static Info_FileBuiltSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(this.INFO_FileBuiltSuccessfully, + `${o.relativeFilename} built successfully.`)}); static INFO_FileBuiltSuccessfully = SevInfo | 0x0006; // For this message, we override the filename with the passed-in file. A bit of a hack but does the job - static Info_FileNotBuiltSuccessfully = (o:{filename:string}) => ({filename:o.filename, ...m(this.INFO_FileNotBuiltSuccessfully, - `${o.filename} failed to build.`)}); + static Info_FileNotBuiltSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(this.INFO_FileNotBuiltSuccessfully, + `${o.relativeFilename} failed to build.`)}); static INFO_FileNotBuiltSuccessfully = SevInfo | 0x0007; static Error_InvalidProjectFile = (o:{message:string}) => m(this.ERROR_InvalidProjectFile, @@ -51,13 +51,13 @@ export class InfrastructureMessages { static ERROR_UnknownFileFormat = SevError | 0x000A; // For this message, we override the filename with the passed-in file. A bit of a hack but does the job - static Info_ProjectBuiltSuccessfully = (o:{filename:string}) => ({filename:o.filename, ...m(this.INFO_ProjectBuiltSuccessfully, - `Project ${o.filename} built successfully.`)}); + static Info_ProjectBuiltSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(this.INFO_ProjectBuiltSuccessfully, + `Project ${o.relativeFilename} built successfully.`)}); static INFO_ProjectBuiltSuccessfully = SevInfo | 0x000B; // For this message, we override the filename with the passed-in file. A bit of a hack but does the job - static Info_ProjectNotBuiltSuccessfully = (o:{filename:string}) => ({filename:o.filename, ...m(this.INFO_ProjectNotBuiltSuccessfully, - `Project ${o.filename} failed to build.`)}); + static Info_ProjectNotBuiltSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(this.INFO_ProjectNotBuiltSuccessfully, + `Project ${o.relativeFilename} failed to build.`)}); static INFO_ProjectNotBuiltSuccessfully = SevInfo | 0x000C; static Info_TooManyMessages = (o:{count:number}) => m(this.INFO_TooManyMessages, @@ -71,5 +71,13 @@ export class InfrastructureMessages { static Error_NotAProjectFile = (o:{filename:string}) => m(this.ERROR_NotAProjectFile, `File ${o.filename} must have a .kpj extension to be treated as a project.`); static ERROR_NotAProjectFile = SevError | 0x000F; + + static Info_WarningsHaveFailedBuild = () => m(this.INFO_WarningsHaveFailedBuild, + `The build failed because option "treat warnings as errors" is enabled and there are one or more warnings.`); + static INFO_WarningsHaveFailedBuild = SevInfo | 0x0010; + + static Error_CannotCreateFolder = (o:{folderName:string, e: any}) => m(this.ERROR_CannotCreateFolder, null, + `Unable to create folder ${o.folderName}: ${o.e ?? 'unknown error'}`); + static ERROR_CannotCreateFolder = SevError | 0x0011; } diff --git a/developer/src/kmc/src/util/NodeCompilerCallbacks.ts b/developer/src/kmc/src/util/NodeCompilerCallbacks.ts index d5e15e0bccc..dc048317deb 100644 --- a/developer/src/kmc/src/util/NodeCompilerCallbacks.ts +++ b/developer/src/kmc/src/util/NodeCompilerCallbacks.ts @@ -1,5 +1,6 @@ import * as fs from 'fs'; import * as path from 'path'; +import { platform } from 'os'; import { CompilerCallbacks, CompilerEvent, CompilerPathCallbacks, CompilerFileSystemCallbacks, compilerLogLevelToSeverity, CompilerErrorSeverity, @@ -71,8 +72,17 @@ export class NodeCompilerCallbacks implements CompilerCallbacks { // 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); + let filename = fs.realpathSync(originalFilename); + let nativeFilename = fs.realpathSync.native(filename); + if(platform() == 'win32' && originalFilename.match(/^.:/)) { + // When an absolute path is passed in, it includes a drive letter. + // Drive letter case can differ but we don't care about that on win32. + // Typically absolute paths only appear for input parameters, as absolute + // paths are flagged as warnings when they appear in source files anyway. + // Upper casing the drive letter just avoids the issue. + filename = filename[0].toUpperCase() + filename.substring(1); + nativeFilename = nativeFilename[0].toUpperCase() + nativeFilename.substring(1); + } if(filename != nativeFilename) { this.reportMessage(InfrastructureMessages.Hint_FilenameHasDifferingCase({ reference: path.basename(originalFilename), @@ -154,6 +164,28 @@ export class NodeCompilerCallbacks implements CompilerCallbacks { event.filename = this.messageFilename; } + this.printMessage(event); + } + + private printMessage(event: CompilerEvent) { + if(this.options.logFormat == 'tsv') { + this.printTsvMessage(event); + } else { + this.printFormattedMessage(event); + } + } + + private printTsvMessage(event: CompilerEvent) { + process.stdout.write([ + CompilerError.formatFilename(event.filename, {fullPath:true, forwardSlashes:false}), + CompilerError.formatLine(event.line), + CompilerError.formatSeverity(event.code), + CompilerError.formatCode(event.code), + CompilerError.formatMessage(event.message) + ].join('\t') + '\n'); + } + + private printFormattedMessage(event: CompilerEvent) { const severityColor = severityColors[CompilerError.severity(event.code)] ?? color.reset; const messageColor = this.messageSpecialColor(event) ?? color.reset; process.stdout.write( @@ -172,7 +204,6 @@ export class NodeCompilerCallbacks implements CompilerCallbacks { // Special case: we'll add a blank line after project builds process.stdout.write('\n'); } - } /** diff --git a/developer/src/kmc/src/util/baseOptions.ts b/developer/src/kmc/src/util/baseOptions.ts index a53539a124b..94163769516 100644 --- a/developer/src/kmc/src/util/baseOptions.ts +++ b/developer/src/kmc/src/util/baseOptions.ts @@ -1,36 +1,25 @@ -import { ALL_COMPILER_LOG_LEVELS } from "@keymanapp/common-types"; +import { ALL_COMPILER_LOG_FORMATS, ALL_COMPILER_LOG_LEVELS } from "@keymanapp/common-types"; import { Command, Option } from "commander"; -import KEYMAN_VERSION from "@keymanapp/keyman-version"; // These options map to CompilerBaseOptions export class BaseOptions { - public static addVersion(program: Command) { - return program.version(KEYMAN_VERSION.VERSION_WITH_TAG); - } - - public static addSentry(program: Command) { - // This corresponds to an option tested in KeymanSentry.ts, which is - // searched for in process.argv, in order to avoid depending on Commander to - // start Sentry, and to ensure that we capture errors as early as possible - // in launch - return program - .addOption(new Option('--no-error-reporting', 'Disable error reporting to keyman.com (overriding user settings)')) - .addOption(new Option('--error-reporting', 'Enable error reporting to keyman.com (overriding user settings)')); - } - public static addLogLevel(program: Command) { return program.addOption(new Option('-l, --log-level ', 'Log level').choices(ALL_COMPILER_LOG_LEVELS).default('info')); } + public static addLogFormat(program: Command) { + return program.addOption(new Option('--log-format ', 'Log format').choices(ALL_COMPILER_LOG_FORMATS).default('formatted')); + } + public static addOutFile(program: Command) { return program.option('-o, --out-file ', 'Override the default path and filename for the output file') } public static addAll(program: Command) { return [ - this.addVersion, this.addLogLevel, + this.addLogFormat, this.addOutFile, ].reduce((p,f) => f(p), program); } diff --git a/developer/src/kmc/src/util/extendedCompilerOptions.ts b/developer/src/kmc/src/util/extendedCompilerOptions.ts new file mode 100644 index 00000000000..8d2903e53cb --- /dev/null +++ b/developer/src/kmc/src/util/extendedCompilerOptions.ts @@ -0,0 +1,10 @@ +import { CompilerOptions } from '@keymanapp/common-types'; + +export interface ExtendedCompilerOptions extends CompilerOptions { + /** + * Verify that the project meets the requirements of the keymanapp/keyboards + * or keymanapp/lexical-models repository, e.g. verify that project license is + * MIT + */ + forPublishing?: boolean; +}; diff --git a/developer/src/kmc/src/util/getDeveloperBinPath.ts b/developer/src/kmc/src/util/getDeveloperBinPath.ts index e59e6ac92f3..4bbe4cedc06 100644 --- a/developer/src/kmc/src/util/getDeveloperBinPath.ts +++ b/developer/src/kmc/src/util/getDeveloperBinPath.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; /** * Locates the Keyman Developer bin folder, checking first if this is running * from the source repository (using the presence of the KEYMAN_ROOT environment - * variable), and if not, checking for the presence of kmcomp.exe in each + * variable), and if not, checking for the presence of kmconvert.exe in each * parent folder recursively until we reach the root of the filesystem. * @returns string | null */ @@ -21,18 +21,18 @@ export function getDeveloperBinPath(): string { } } - // Otherwise, we will look in parent folders until we find kmcomp.exe - // TODO: once we eliminate kmcomp.exe, we'll need to do this some other way? + // Otherwise, we will look in parent folders until we find kmconvert.exe + // TODO: if we eliminate kmconvert.exe, we'll need to do this some other way? let p = fileURLToPath(import.meta.url); const root = path.parse(p).root.toLowerCase(); // lower-case for drive letter on Windows p = path.dirname(p); do { - if (fs.existsSync(path.join(p, 'kmcomp.exe'))) { + if (fs.existsSync(path.join(p, 'kmconvert.exe'))) { return p; } p = path.dirname(p); } while (p.toLowerCase() != root); - // kmcomp.exe was not found on the path + // kmconvert.exe was not found on the path return null; } diff --git a/developer/src/kmc/src/util/getLastGitCommitDate.ts b/developer/src/kmc/src/util/getLastGitCommitDate.ts new file mode 100644 index 00000000000..645fcc6820b --- /dev/null +++ b/developer/src/kmc/src/util/getLastGitCommitDate.ts @@ -0,0 +1,48 @@ +import { execFileSync } from 'child_process'; + +// RFC3339 pattern, UTC +export const expectedGitDateFormat = /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ$/; + +/** + * Returns the date and time of the last commit from git for the passed in path + * @param path Path for which to retrieve the last commit message + * @returns string, in RFC3339, 'YYYY-MM-DDThh:nn:ssZ' + */ +export function getLastGitCommitDate(path: string): string { + // TZ=UTC0 git log -1 --no-merges --date=format:%Y-%m-%dT%H:%M:%SZ --format=%ad + let result = null; + + try { + result = execFileSync('git', [ + 'log', // git log + '-1', // one commit only + '--no-merges', // we're only interested in 'real' commits + '--date=format:%Y-%m-%dT%H:%M:%SZ', // format the date in our expected RFC3339 format + '--format=%ad' // emit only the commit date + ], { + env: { ...process.env, TZ: 'TZ0' }, // use UTC timezone, not local + encoding: 'utf-8', // force a string result rather than Buffer + windowsHide: true, // on windows, we may need this to suppress a console window popup + cwd: path, // path to run git from + stdio: ['pipe', 'pipe', 'pipe'] // all output via pipe, so we don't get git errors on console + }); + } catch (e) { + // If git is not available, or the file is not in-repo, then it is probably + // fine to just silently return null, as the only machines where this is + // critical are the CI machines where we build and deploy .keyboard_info + // files, and where git will always be available. It would be possible to + // have this raise an error in CI environments, but the chance of error + // seems low. + return null; + } + + result = result.trim(); + + // We'll only return the result if it walks like a date, swims like a date, + // and quacks like a date. + if (!result.match(expectedGitDateFormat)) { + return null; + } + + return result; +} diff --git a/developer/src/kmc/test/test-build.ts b/developer/src/kmc/test/test-build.ts index 65203efe88d..26ef09e746b 100644 --- a/developer/src/kmc/test/test-build.ts +++ b/developer/src/kmc/test/test-build.ts @@ -34,11 +34,13 @@ describe('compilerWarningsAsErrors', function () { const builder = new BuildProject(); const path = makePathToFixture('compiler-warnings-as-errors', `compiler_warnings_as_errors_${truth.kpj === true ? 'true' : (truth.kpj === false ? 'false' : 'undefined')}.kpj`); - const result = await builder.build(path, callbacks, {compilerWarningsAsErrors: truth.cli}); + const result = await builder.build(path, callbacks, { + compilerWarningsAsErrors: truth.cli, + }); + if(truth.result != result) { + callbacks.printMessages(); + } if(truth.result) { - if(callbacks.messages.length != 0) { - callbacks.printMessages(); - } assert.isTrue(result); } else { assert.isFalse(result); diff --git a/developer/src/kmc/test/test-getLastGitCommitDate.ts b/developer/src/kmc/test/test-getLastGitCommitDate.ts new file mode 100644 index 00000000000..3497ef309dc --- /dev/null +++ b/developer/src/kmc/test/test-getLastGitCommitDate.ts @@ -0,0 +1,17 @@ +import { assert } from 'chai'; +import 'mocha'; +import { makePathToFixture } from './helpers/index.js'; +import { expectedGitDateFormat, getLastGitCommitDate } from '../src/util/getLastGitCommitDate.js'; + +describe('getLastGitCommitDate', function () { + it('should return a valid date for a folder in this repo', async function() { + const path = makePathToFixture('.'); + const date = getLastGitCommitDate(path); + assert.match(date, expectedGitDateFormat); + }); + + it('should return null for a folder outside the repo', async function() { + const date = getLastGitCommitDate('/'); + assert.isNull(date); + }); +}); diff --git a/developer/src/kmc/test/test-infrastructureMessages.ts b/developer/src/kmc/test/test-infrastructureMessages.ts index a9b55d075df..fde352f3dd3 100644 --- a/developer/src/kmc/test/test-infrastructureMessages.ts +++ b/developer/src/kmc/test/test-infrastructureMessages.ts @@ -6,6 +6,7 @@ import { makePathToFixture } from './helpers/index.js'; import { NodeCompilerCallbacks } from '../src/util/NodeCompilerCallbacks.js'; import { CompilerErrorNamespace } from '@keymanapp/common-types'; import { unitTestEndpoints } from '../src/commands/build.js'; +import { KmnCompilerMessages } from '@keymanapp/kmc-kmn'; describe('InfrastructureMessages', function () { it('should have a valid InfrastructureMessages object', function() { @@ -65,4 +66,22 @@ describe('InfrastructureMessages', function () { assert.isTrue(ncb.hasMessage(InfrastructureMessages.HINT_FilenameHasDifferingCase), `HINT_FilenameHasDifferingCase not generated, instead got: `+JSON.stringify(ncb.messages,null,2)); }); + + // INFO_WarningsHaveFailedBuild + + it('should generate INFO_WarningsHaveFailedBuild if only warnings failed the build', async function() { + // NOTE: we can probably re-use this format for most other message tests in the future + const ncb = new NodeCompilerCallbacks({logLevel: 'silent'}); + const filename = makePathToFixture('compiler-warnings-as-errors', 'keyboard.kmn'); + const expectedMessages = [ + InfrastructureMessages.INFO_BuildingFile, + KmnCompilerMessages.WARN_HeaderStatementIsDeprecated, + InfrastructureMessages.INFO_WarningsHaveFailedBuild, + InfrastructureMessages.INFO_FileNotBuiltSuccessfully + ]; + await unitTestEndpoints.build(filename, ncb, {compilerWarningsAsErrors: true}); + assert.deepEqual(ncb.messages.map(m => m.code), expectedMessages, + `actual callbacks.messages:\n${JSON.stringify(ncb.messages,null,2)}\n\n`+ + `did not match expected:\n${JSON.stringify(expectedMessages,null,2)}\n\n`); + }); }); diff --git a/developer/src/kmc/test/test-project-build.ts b/developer/src/kmc/test/test-project-build.ts index 088de1e40f2..88563168805 100644 --- a/developer/src/kmc/test/test-project-build.ts +++ b/developer/src/kmc/test/test-project-build.ts @@ -16,20 +16,18 @@ describe('BuildProject', function () { compilerWarningsAsErrors: true, saveDebug: false, warnDeprecatedCode: true, - logLevel: 'info' + logLevel: 'info', }); - if(callbacks.messages.length != 6) { - callbacks.printMessages(); - } - assert.equal(callbacks.messages.length, 6); const messages = [ InfrastructureMessages.INFO_BuildingFile, // kmn InfrastructureMessages.INFO_FileBuiltSuccessfully, InfrastructureMessages.INFO_BuildingFile, // kps InfrastructureMessages.INFO_FileBuiltSuccessfully, - InfrastructureMessages.INFO_BuildingFile, // keyboard_info - InfrastructureMessages.INFO_FileBuiltSuccessfully, ]; + if(callbacks.messages.length != messages.length) { + callbacks.printMessages(); + } + assert.equal(callbacks.messages.length, messages.length); for(let i = 0; i < messages.length; i++) { assert.equal(callbacks.messages[i].code, messages[i]); } diff --git a/developer/src/kmc/tsconfig.kmc-base.json b/developer/src/kmc/tsconfig.kmc-base.json index f8cff50b3fa..6956b6e05db 100644 --- a/developer/src/kmc/tsconfig.kmc-base.json +++ b/developer/src/kmc/tsconfig.kmc-base.json @@ -1,3 +1,3 @@ { - "extends": "../../../tsconfig.esm-base.json", + "extends": "../../../tsconfig.base.json", } diff --git a/developer/src/kmcmpdll/CasedKeys.cpp b/developer/src/kmcmpdll/CasedKeys.cpp deleted file mode 100644 index 3118f1be150..00000000000 --- a/developer/src/kmcmpdll/CasedKeys.cpp +++ /dev/null @@ -1,149 +0,0 @@ - -#include "pch.h" - -#include -#include -#include "../../../common/windows/cpp/include/vkeys.h" -#include - -#include "CharToKeyConversion.h" - -extern BOOL FMnemonicLayout; // TODO: these globals should be consolidated one day - -DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp); - -DWORD VerifyCasedKeys(PFILE_STORE sp) { - assert(sp != NULL); - - if (FMnemonicLayout) { - // The &CasedKeys system store is not supported for - // mnemonic layouts in 14.0 - return CERR_CasedKeysNotSupportedWithMnemonicLayout; - } - - // We will rewrite this store with virtual keys - - PWSTR p = sp->dpString; - PWSTR buf = new WCHAR[wcslen(p) * 5 + 1]; // extended keys are 5 units long, so this is the max length - PWSTR q = buf; - - while (*p) { - UINT key = 0, shift = 0; - if (*p != UC_SENTINEL) { - if (!MapUSCharToVK(*p, &key, &shift)) { - return CERR_CasedKeysMustContainOnlyVirtualKeys; - } - if (shift & K_SHIFTFLAG) { - return CERR_CasedKeysMustNotIncludeShiftStates; - } - } - else { - if (*(p + 1) != CODE_EXTENDED) { - return CERR_CasedKeysMustContainOnlyVirtualKeys; - } - shift = *(p + 2); - key = *(p + 3); - if (shift != ISVIRTUALKEY) { - return CERR_CasedKeysMustNotIncludeShiftStates; - } - } - *q++ = UC_SENTINEL; - *q++ = CODE_EXTENDED; - *q++ = shift; - *q++ = key; - *q++ = UC_SENTINEL_EXTENDEDEND; - *q = 0; - - p = incxstr(p); - } - - delete[] sp->dpString; - sp->dpString = buf; - - return CERR_None; -} - -DWORD ExpandCapsRulesForGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) { - assert(fk != NULL); - assert(gp != NULL); - - if (FMnemonicLayout) { - // The &CasedKeys system store is not supported for - // mnemonic layouts in 14.0 - return CERR_None; - } - - PFILE_STORE sp = FindSystemStore(fk, TSS_CASEDKEYS); - if (!sp) { - // If there is no &CasedKeys system store, then we do not - // process the key - return CERR_None; - } - - DWORD msg; - // ExpandCapsRule may add extra rules at the end of gp->dpKeyArray, - // reallocating it, so we (a) cache the original length, and (b) - // dereference the array every call - int cxKeyArray = gp->cxKeyArray; - for (int i = 0; i < cxKeyArray; i++) { - if ((msg = ExpandCapsRule(gp, &gp->dpKeyArray[i], sp)) != CERR_None) { - return msg; - } - } - return CERR_None; -} - -DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp) { - UINT key = kpp->Key; - UINT shift = kpp->ShiftFlags; - - if (shift == 0) { - // Convert US key cap to a virtual key - if (!MapUSCharToVK(kpp->Key, &key, &shift)) { - return CERR_None; - } - } - - if (shift & (CAPITALFLAG | NOTCAPITALFLAG)) { - // Don't attempt expansion if either Caps Lock flag is specified in the key rule - return CERR_None; - } - - PWSTR p = sp->dpString; - for (; *p; p = incxstr(p)) { - // We've already verified that the store contains only virtual keys in VerifyCasedKeys - if (*(p + 3) == key) { - break; - } - } - - if (!*p) { - // This key is not modified by Caps Lock - return CERR_None; - } - - // This key is modified by Caps Lock, so we need to duplicate this rule - PFILE_KEY k = new FILE_KEY[gp->cxKeyArray + 1]; - if (!k) return CERR_CannotAllocateMemory; - memcpy(k, gp->dpKeyArray, gp->cxKeyArray * sizeof(FILE_KEY)); - - kpp = &k[(INT_PTR)(kpp - gp->dpKeyArray)]; - - delete gp->dpKeyArray; - gp->dpKeyArray = k; - gp->cxKeyArray++; - - k = &k[gp->cxKeyArray - 1]; - k->dpContext = new WCHAR[wcslen(kpp->dpContext) + 1]; - k->dpOutput = new WCHAR[wcslen(kpp->dpOutput) + 1]; - wcscpy_s(k->dpContext, wcslen(kpp->dpContext) + 1, kpp->dpContext); // copy the context. - wcscpy_s(k->dpOutput, wcslen(kpp->dpOutput) + 1, kpp->dpOutput); // copy the output. - k->Key = key; - k->Line = kpp->Line; - // Add the CAPITAL FLAG, invert shift flag for the rule - k->ShiftFlags = shift ^ K_SHIFTFLAG | CAPITALFLAG; - kpp->Key = key; - kpp->ShiftFlags = shift | NOTCAPITALFLAG; - - return CERR_None; -} diff --git a/developer/src/kmcmpdll/CasedKeys.h b/developer/src/kmcmpdll/CasedKeys.h deleted file mode 100644 index 66ba8a312dc..00000000000 --- a/developer/src/kmcmpdll/CasedKeys.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -DWORD VerifyCasedKeys(PFILE_STORE sp); -DWORD ExpandCapsRulesForGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp); diff --git a/developer/src/kmcmpdll/CharToKeyConversion.cpp b/developer/src/kmcmpdll/CharToKeyConversion.cpp deleted file mode 100644 index 24723c980f6..00000000000 --- a/developer/src/kmcmpdll/CharToKeyConversion.cpp +++ /dev/null @@ -1,187 +0,0 @@ - -#include "pch.h" -#include - -/* Following code lifted from syskbd.cpp and tweaked for compiler use. Todo: consolidate */ - -WCHAR VKToChar(WORD keyCode, UINT shiftFlags) -{ - char shiftedDigit[] = ")!@#$%^&*("; - int n, Shift; - - if (!(shiftFlags & ISVIRTUALKEY)) return keyCode; - - if (shiftFlags & (LCTRLFLAG | RCTRLFLAG | LALTFLAG | RALTFLAG)) return 0; - - if (keyCode >= '0' && keyCode <= '9') - { - n = keyCode - '0'; - return ((shiftFlags & K_SHIFTFLAG) ? shiftedDigit[n] : keyCode); - } - - if (keyCode >= 'A' && keyCode <= 'Z') - { - Shift = (shiftFlags & K_SHIFTFLAG); - if (shiftFlags & (CAPITALFLAG)) Shift = !Shift; - return (Shift ? keyCode : keyCode + 32); - } - - if (keyCode >= VK_NUMPAD0 && keyCode <= VK_NUMPAD9) - { - if (!(shiftFlags & NUMLOCKFLAG)) return 0; - return keyCode - (VK_NUMPAD0 - '0'); - } - - Shift = (shiftFlags & K_SHIFTFLAG); - - switch (keyCode) - { - case VK_ACCENT: - return Shift ? '~' : '`'; - case VK_HYPHEN: - return Shift ? '_' : '-'; - case VK_EQUAL: - return Shift ? '+' : '='; - case VK_BKSLASH: - return Shift ? '|' : 92; - case VK_LBRKT: - return Shift ? '{' : '['; - case VK_RBRKT: - return Shift ? '}' : ']'; - case VK_COLON: - return Shift ? ':' : ';'; - case VK_QUOTE: - return Shift ? '"' : 39; - case VK_COMMA: - return Shift ? '<' : ','; - case VK_PERIOD: - return Shift ? '>' : '.'; - case VK_SLASH: - return Shift ? '?' : '/'; - case VK_SPACE: - return ' '; - } - return 0; - //keyCode; -} - - -/* This array is lifted from preservedkeymap.cpp */ -// TODO: share this -const struct -{ - UINT key; - BOOL shift; -} USCharMap[] = { - { VK_SPACE, FALSE }, // 20 ' ' - { '1', TRUE }, // 21 '!' - { VK_QUOTE, TRUE }, // 22 '"' - { '3', TRUE }, // 23 '#' - { '4', TRUE }, // 24 '$' - { '5', TRUE }, // 25 '%' - { '7', TRUE }, // 26 '&' - { VK_QUOTE, FALSE }, // 27 ''' - { '9', TRUE }, // 28 '(' - { '0', TRUE }, // 29 ')' - { '8', TRUE }, // 2A '*' - { VK_EQUAL, TRUE }, // 2B '+' - { VK_COMMA, FALSE }, // 2C ',' - { VK_HYPHEN, FALSE }, // 2D '-' - { VK_PERIOD, FALSE }, // 2E '.' - { VK_SLASH, FALSE }, // 2F '/' - - { '0', FALSE }, // 30 '0' - { '1', FALSE }, // 31 '1' - { '2', FALSE }, // 32 '2' - { '3', FALSE }, // 33 '3' - { '4', FALSE }, // 34 '4' - { '5', FALSE }, // 35 '5' - { '6', FALSE }, // 36 '6' - { '7', FALSE }, // 37 '7' - { '8', FALSE }, // 38 '8' - { '9', FALSE }, // 39 '9' - { VK_COLON, TRUE }, // 3A ':' - { VK_COLON, FALSE }, // 3B ';' - { VK_COMMA, TRUE }, // 3C '<' - { VK_EQUAL, FALSE }, // 3D '=' - { VK_PERIOD, TRUE }, // 3E '>' - { VK_SLASH, TRUE }, // 3F '?' - - { '2', TRUE }, // 40 '@' - { 'A', TRUE }, // 41 'A' - { 'B', TRUE }, // 42 'B' - { 'C', TRUE }, // 43 'C' - { 'D', TRUE }, // 44 'D' - { 'E', TRUE }, // 45 'E' - { 'F', TRUE }, // 46 'F' - { 'G', TRUE }, // 47 'G' - { 'H', TRUE }, // 48 'H' - { 'I', TRUE }, // 49 'I' - { 'J', TRUE }, // 4A 'J' - { 'K', TRUE }, // 4B 'K' - { 'L', TRUE }, // 4C 'L' - { 'M', TRUE }, // 4D 'M' - { 'N', TRUE }, // 4E 'N' - { 'O', TRUE }, // 4F 'O' - - { 'P', TRUE }, // 50 'P' - { 'Q', TRUE }, // 51 'Q' - { 'R', TRUE }, // 52 'R' - { 'S', TRUE }, // 53 'S' - { 'T', TRUE }, // 54 'T' - { 'U', TRUE }, // 55 'U' - { 'V', TRUE }, // 56 'V' - { 'W', TRUE }, // 57 'W' - { 'X', TRUE }, // 58 'X' - { 'Y', TRUE }, // 59 'Y' - { 'Z', TRUE }, // 5A 'Z' - { VK_LBRKT, FALSE }, // 5B '[' - { VK_BKSLASH, FALSE }, // 5C '\' - { VK_RBRKT, FALSE }, // 5D ']' - { '6', TRUE }, // 5E '^' - { VK_HYPHEN, TRUE }, // 5F '_' - - { VK_ACCENT, FALSE }, // 60 '`' - { 'A', FALSE }, // 61 'a' - { 'B', FALSE }, // 62 'b' - { 'C', FALSE }, // 63 'c' - { 'D', FALSE }, // 64 'd' - { 'E', FALSE }, // 65 'e' - { 'F', FALSE }, // 66 'f' - { 'G', FALSE }, // 67 'g' - { 'H', FALSE }, // 68 'h' - { 'I', FALSE }, // 69 'i' - { 'J', FALSE }, // 6A 'j' - { 'K', FALSE }, // 6B 'k' - { 'L', FALSE }, // 6C 'l' - { 'M', FALSE }, // 6D 'm' - { 'N', FALSE }, // 6E 'n' - { 'O', FALSE }, // 6F 'o' - - { 'P', FALSE }, // 70 'p' - { 'Q', FALSE }, // 71 'q' - { 'R', FALSE }, // 72 'r' - { 'S', FALSE }, // 73 's' - { 'T', FALSE }, // 74 't' - { 'U', FALSE }, // 75 'u' - { 'V', FALSE }, // 76 'v' - { 'W', FALSE }, // 77 'w' - { 'X', FALSE }, // 78 'x' - { 'Y', FALSE }, // 79 'y' - { 'Z', FALSE }, // 7A 'z' - { VK_LBRKT, TRUE }, // 7B '{' - { VK_BKSLASH, TRUE }, // 7C '|' - { VK_RBRKT, TRUE }, // 7D '}' - { VK_ACCENT, TRUE } // 7E '~' -}; - -BOOL MapUSCharToVK(UINT ch, UINT *puKey, UINT *puShiftFlags) { - assert(puKey != NULL); - assert(puShiftFlags != NULL); - if (ch >= 0x20 && ch < 0x7F) { - *puKey = USCharMap[ch - 0x20].key; - *puShiftFlags = ISVIRTUALKEY | (USCharMap[ch - 0x20].shift ? K_SHIFTFLAG : 0); - return TRUE; - } - return FALSE; -} diff --git a/developer/src/kmcmpdll/CharToKeyConversion.h b/developer/src/kmcmpdll/CharToKeyConversion.h deleted file mode 100644 index 6524a73f995..00000000000 --- a/developer/src/kmcmpdll/CharToKeyConversion.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -#define VK_COLON 0xBA -#define VK_EQUAL 0xBB -#define VK_COMMA 0xBC -#define VK_HYPHEN 0xBD -#define VK_PERIOD 0xBE -#define VK_SLASH 0xBF -#define VK_ACCENT 0xC0 -#define VK_LBRKT 0xDB -#define VK_BKSLASH 0xDC -#define VK_RBRKT 0xDD -#define VK_QUOTE 0xDE -#define VK_xDF 0xDF - -BOOL MapUSCharToVK(UINT ch, UINT *puKey, UINT *puShiftFlags); -WCHAR VKToChar(WORD keyCode, UINT shiftFlags); - diff --git a/developer/src/kmcmpdll/CheckFilenameConsistency.cpp b/developer/src/kmcmpdll/CheckFilenameConsistency.cpp deleted file mode 100644 index 1d553839f49..00000000000 --- a/developer/src/kmcmpdll/CheckFilenameConsistency.cpp +++ /dev/null @@ -1,121 +0,0 @@ - -#include "pch.h" - -#include -#include -#include -#include -#include -#include "CheckFilenameConsistency.h" - -extern char CompileDir[MAX_PATH]; - -BOOL FileExists(char const * filename) { - _finddata_t fi; - intptr_t n; - - if ((n = _findfirst(filename, &fi)) != -1) { - _findclose(n); - return TRUE; - } - return FALSE; -} - -BOOL IsRelativePath(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 == '\\') return FALSE; - if (*p && *(p + 1) == ':') return FALSE; - - return TRUE; -} - -BOOL IsRelativePath(wchar_t 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 == L'\\') return FALSE; - if (*p && *(p + 1) == L':') return FALSE; - - return TRUE; -} - -DWORD CheckFilenameConsistency(char const * Filename, BOOL ReportMissingFile) { - PWCHAR WFilename = strtowstr((char *)Filename); - DWORD const result = CheckFilenameConsistency(WFilename, ReportMissingFile); - delete WFilename; - return result; -} - -DWORD CheckFilenameConsistency(wchar_t const * Filename, BOOL ReportMissingFile) { - WCHAR Name[_MAX_PATH], FName[_MAX_FNAME], Ext[_MAX_EXT]; - _wfinddata_t fi; - intptr_t n; - - if (IsRelativePath(Filename)) { - PWCHAR WCompileDir = strtowstr(CompileDir); - wcscpy_s(Name, _countof(Name), WCompileDir); // I3481 - wcscat_s(Name, _countof(Name), Filename); // I3481 - } - else { - wcscpy_s(Name, _countof(Name), Filename); // I3481 - } - - if ((n = _wfindfirst(Name, &fi)) == -1) { - if (ReportMissingFile) { - wsprintf(ErrExtra, "referenced file '%ls'", Filename); - AddWarning(CWARN_MissingFile); - } - return CERR_None; - } - - _wsplitpath_s(Filename, nullptr, 0, nullptr, 0, FName, _MAX_FNAME, Ext, _MAX_EXT); - _wmakepath_s(Name, _MAX_PATH, nullptr, nullptr, FName, Ext); - if (wcscmp(Name, fi.name) != 0) { - wsprintf(ErrExtra, "reference '%ls' does not match actual filename '%ls'", Name, fi.name); - AddWarning(CHINT_FilenameHasDifferingCase); - } - return CERR_None; -} - -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. This is ripe for rewrite! - // But let's check what we have anyway - PFILE_STORE sp; - DWORD i, msg; - for (i = 0, sp = fk->dpStoreArray; i < fk->cxStoreArray; i++, sp++) { - if (!sp->fIsCall) continue; - - const std::wstring callsite(sp->dpString); - const auto colon = callsite.find(':'); - if (colon == std::wstring::npos) continue; - - auto func = callsite.substr(colon + 1); - func.append(L".call_js"); - if ((msg = CheckFilenameConsistency(func.c_str(), FALSE)) != CERR_None) { - return msg; - } - } - return CERR_None; -} diff --git a/developer/src/kmcmpdll/CheckFilenameConsistency.h b/developer/src/kmcmpdll/CheckFilenameConsistency.h deleted file mode 100644 index fc9a645f1e8..00000000000 --- a/developer/src/kmcmpdll/CheckFilenameConsistency.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include -#include - -DWORD CheckFilenameConsistencyForCalls(PFILE_KEYBOARD fk); -DWORD CheckFilenameConsistency(char const * Filename, BOOL ReportMissingFile); -DWORD CheckFilenameConsistency(wchar_t const * Filename, BOOL ReportMissingFile); -BOOL FileExists(char const * filename); -BOOL IsRelativePath(char const * p); -BOOL IsRelativePath(wchar_t const * p); diff --git a/developer/src/kmcmpdll/CheckForDuplicates.cpp b/developer/src/kmcmpdll/CheckForDuplicates.cpp deleted file mode 100644 index d22bfa80e6f..00000000000 --- a/developer/src/kmcmpdll/CheckForDuplicates.cpp +++ /dev/null @@ -1,44 +0,0 @@ - -#include "pch.h" - -#include -#include -#include - -#include "CheckForDuplicates.h" - - -DWORD CheckForDuplicateGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) noexcept { - DWORD i; - PFILE_GROUP gp0 = fk->dpGroupArray; - for (i = 0; i < fk->cxGroupArray; i++, gp0++) { - if (gp0 == gp) { - continue; - } - if (_wcsicmp(gp0->szName, gp->szName) == 0) { - wsprintf(ErrExtra, "Group '%ls' declared on line %d", gp0->szName, gp0->Line); - return CERR_DuplicateGroup; - } - } - return CERR_None; -} - -DWORD CheckForDuplicateStore(PFILE_KEYBOARD fk, PFILE_STORE sp) noexcept { - if (!sp->szName[0]) { - // Stores with zero length names are reserved system stores. - // They cannot be defined in user code. This is not an issue. - return CERR_None; - } - DWORD i; - PFILE_STORE sp0 = fk->dpStoreArray; - for (i = 0; i < fk->cxStoreArray; i++, sp0++) { - if (sp0 == sp) { - continue; - } - if (_wcsicmp(sp0->szName, sp->szName) == 0) { - wsprintf(ErrExtra, "Store '%ls' declared on line %d", sp0->szName, sp0->line); - return CERR_DuplicateStore; - } - } - return CERR_None; -} diff --git a/developer/src/kmcmpdll/CheckForDuplicates.h b/developer/src/kmcmpdll/CheckForDuplicates.h deleted file mode 100644 index 2dabf76edf6..00000000000 --- a/developer/src/kmcmpdll/CheckForDuplicates.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -DWORD CheckForDuplicateGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) noexcept; -DWORD CheckForDuplicateStore(PFILE_KEYBOARD fk, PFILE_STORE sp) noexcept; diff --git a/developer/src/kmcmpdll/CheckNCapsConsistency.cpp b/developer/src/kmcmpdll/CheckNCapsConsistency.cpp deleted file mode 100644 index 4c9e6853f99..00000000000 --- a/developer/src/kmcmpdll/CheckNCapsConsistency.cpp +++ /dev/null @@ -1,98 +0,0 @@ - -#include "pch.h" - -#include -#include -#include -#include "CharToKeyConversion.h" - -/** - * If any rule uses CAPS or NCAPS for a given key, then every rule that - * uses that key must also use CAPS or NCAPS, as otherwise the results are - * inconsistent. For example, if Caps Lock is on, then [K_F] may still be - * matched: - * - * + [K_F] > 'foo' - * + [CAPS K_F] > 'bar' - * - * Because of the way that KeymanWeb compiles rules, this may even apply when - * we have a key rule that would otherwise be ignored due to context. In the - * following example, [Caps Lock] + [y] would result in no output (or default - * output) rather than 'bar', because the preceding rule would capture the - * Y key, ignoring Caps Lock, meaning that the subsequent rule would never - * even get tested. (Note that this was introduced in the KeymanWeb compiler - * fix for extremely long if/else ladders in Keyman 12 in #1561, and this is - * technically slightly inconsistent with Keyman Core, although in my analysis - * only in this already ambiguous situation. - * - * 'x' + [K_Y] > 'foo' - * [CAPS K_Y] > 'bar' - * - * Given all this, we'll warn any time we find a key that has inconsistent use - * of CAPS/NCAPS in its rules. - * - * @param fk Keyboard to check - */ -BOOL CheckNCapsConsistency(PFILE_KEYBOARD fk) { - struct CapsUsage { - int ncaps_line, caps_line, neither_line; - }; - - // 256 virtual key codes + sizeof the virtual key dictionary is max key code possible - const int nkeys = 256 + fk->cxVKDictionary; - const int oldCurrentLine = currentLine; - auto caps_ncaps_usage = new CapsUsage [nkeys]; - - memset(caps_ncaps_usage, 0, nkeys * sizeof(CapsUsage)); - - PFILE_GROUP gp; - DWORD gn; - for (gn = 0, gp = fk->dpGroupArray; gn < fk->cxGroupArray; gn++, gp++) { - if (!gp->fUsingKeys) { - continue; - } - - PFILE_KEY kp; - DWORD kn; - for (kn = 0, kp = gp->dpKeyArray; kn < gp->cxKeyArray; kn++, kp++) { - UINT key; - UINT shift; - if (kp->ShiftFlags & ISVIRTUALKEY) { - if (kp->Key >= nkeys) { - assert(false); - continue; - } - key = kp->Key; - shift = kp->ShiftFlags; - } - else if (!MapUSCharToVK(kp->Key, &key, &shift)) { - // Not a valid key - continue; - } - - if (shift & NOTCAPITALFLAG) { - if (!caps_ncaps_usage[key].ncaps_line) caps_ncaps_usage[key].ncaps_line = (kp->Line == 0 ? 1 : kp->Line); - } - else if (shift & CAPITALFLAG) { - if (!caps_ncaps_usage[key].caps_line) caps_ncaps_usage[key].caps_line = (kp->Line == 0 ? 1 : kp->Line); - } - else { - if (!caps_ncaps_usage[key].neither_line) caps_ncaps_usage[key].neither_line = (kp->Line == 0 ? 1 : kp->Line); - } - } - } - - for (int i = 0; i < nkeys; i++) { - if (caps_ncaps_usage[i].neither_line && (caps_ncaps_usage[i].caps_line || caps_ncaps_usage[i].ncaps_line)) { - // We set the current line to one needing work: the developer should add the NCAPS flag - currentLine = caps_ncaps_usage[i].neither_line; - AddWarning(CWARN_KeyShouldIncludeNCaps); - } - } - - delete[] caps_ncaps_usage; - - currentLine = oldCurrentLine; - - return TRUE; -} diff --git a/developer/src/kmcmpdll/CheckNCapsConsistency.h b/developer/src/kmcmpdll/CheckNCapsConsistency.h deleted file mode 100644 index f6a11b5fe26..00000000000 --- a/developer/src/kmcmpdll/CheckNCapsConsistency.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include - -BOOL CheckNCapsConsistency(PFILE_KEYBOARD fk); diff --git a/developer/src/kmcmpdll/Compiler.cpp b/developer/src/kmcmpdll/Compiler.cpp deleted file mode 100644 index 100e43fc5bd..00000000000 --- a/developer/src/kmcmpdll/Compiler.cpp +++ /dev/null @@ -1,3795 +0,0 @@ -/* - Name: Compiler - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 20 Jun 2006 - - Modified Date: 25 Oct 2016 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 20 Jun 2006 - mcdurdin - Initial version - 23 Aug 2006 - mcdurdin - Add VISUALKEYBOARD, KMW_RTL, KMW_HELPFILE, KMW_HELPTEXT, KMW_EMBEDJS system stores - 14 Sep 2006 - mcdurdin - Support icons in version 7 - 28 Sep 2006 - mcdurdin - Added product validation - 28 Sep 2006 - mcdurdin - Added test for version 7.0 icon support - 06 Oct 2006 - mcdurdin - Fix buffer overflow in UTF8 conversion - 04 Dec 2006 - mcdurdin - Fix readfile buffer bug - 04 Jan 2007 - mcdurdin - Add notany support - 22 Jan 2007 - mcdurdin - Add K_NPENTER reference - 25 Jan 2007 - mcdurdin - Resize buffers to 4095 - 30 May 2007 - mcdurdin - I786 - Compiler crash if zero length string in keystroke any - 23 Aug 2007 - mcdurdin - I1011 - Fix buffer clobbering for UTF8 conversion of large files - 27 Mar 2008 - mcdurdin - I1358 - Support for multiple languages for office config - 14 Jun 2008 - mcdurdin - Support documenting language id as a single WORD instead of by PRIMARY/SUB - 14 Jun 2008 - mcdurdin - Support Windows languages list - 28 Jul 2008 - mcdurdin - I1569 - line prefixes - 22 Mar 2010 - mcdurdin - Compiler fixup - x64 support - 25 May 2010 - mcdurdin - I1632 - Keyboard Options - 24 Jun 2010 - mcdurdin - I2432 - Use local buffers so GetXString can be re-entrant (used by if()) - 26 Jul 2010 - mcdurdin - I2467 - 8.0 renumber - 18 Mar 2011 - mcdurdin - I2646 - Compiler warning on invalid Ethnologue codes - 18 Mar 2011 - mcdurdin - I2525 - Unterminated string can crash compiler - 19 Jul 2011 - mcdurdin - I2993 - Named code constants cause a warning 0x208D to appear - 26 Jun 2012 - mcdurdin - I3377 - KM9 - Update code references from 8.0 to 9.0 - 17 Aug 2012 - mcdurdin - I3431 - V9.0 - Spaces in values in if comparisons break the compiler parser - 27 Aug 2012 - mcdurdin - I3439 - V9.0 - Refactor xstring support in C++ code - 27 Aug 2012 - mcdurdin - I3437 - V9.0 - Add support for set(&layer) and layer() - 27 Aug 2012 - mcdurdin - I3438 - V9.0 - Add support for custom virtual keys - 27 Aug 2012 - mcdurdin - I3430 - V9.0 - Add support for if(&platform) and if(&baselayout) to compilers - 27 Aug 2012 - mcdurdin - I3440 - V9.0 - Tidy up set statement delimiter recognition - 24 Oct 2012 - mcdurdin - I3483 - V9.0 - Add support for compiling in the layout file - 24 Oct 2012 - mcdurdin - I3481 - V9.0 - Eliminate unsafe calls in C++ - 24 Jan 2012 - mcdurdin - I3137 - If key part of VK rule is missing, compiler generates invalid file - 06 Feb 2012 - mcdurdin - I3228 - kmcmpdll sometimes tries to write temp files to Program Files - 03 Nov 2012 - mcdurdin - I3510 - V9.0 - Merge of I3228 - kmcmpdll sometimes tries to write temp files to Program Files - 03 Nov 2012 - mcdurdin - I3511 - V9.0 - Merge of I3137 - If key part of VK rule is missing, compiler generates invalid file - 13 Dec 2012 - mcdurdin - I3654 - V9.0 - Compiler appears to create unregistered keyboards even when registered - 13 Dec 2012 - mcdurdin - I3641 - V9.0 - compiler dll buffer overrun bugs - 13 Dec 2012 - mcdurdin - I3681 - V9.0 - KeymanWeb compiler should output formatted js when debug=1 - 13 Dec 2012 - mcdurdin - I3686 - V9.0 - AddStore attaches property flags to wrong store structure - 19 Mar 2014 - mcdurdin - I4140 - V9.0 - Add keyboard version information to keyboards - 04 Nov 2014 - mcdurdin - I4504 - V9.0 - Consolidate the compile action into single command - 02 Jul 2015 - mcdurdin - I4784 - Compiler does not recognise baselayout, layer or platform at the start of a line - 02 Jul 2015 - mcdurdin - I4785 - baselayout(), layer() and platform() produce incorrect compiled code - 24 Aug 2015 - mcdurdin - I4865 - Add treat hints and warnings as errors into project - 24 Aug 2015 - mcdurdin - I4866 - Add warn on deprecated features to project and compile - 24 Aug 2015 - mcdurdin - I4867 - Add test for code after use warning to compile - 06 Nov 2015 - mcdurdin - I4914 - kmcmpdll does not pick an index() statement that has an offset one past the key - 23 Feb 2016 - mcdurdin - I4982 - Defined character constants cannot be referenced correctly in other stores - 25 Oct 2016 - mcdurdin - I5135 - Remove product and licensing references from Developer projects -*/ -#include "pch.h" - -#include -#include -#include "../../../common/windows/cpp/include/vkeys.h" -#include -#include -#include - -#include "virtualcharkeys.h" - -#include "../../../common/windows/cpp/include/crc32.h" -#include "../../../common/windows/cpp/include/ConvertUTF.h" -#include "debugstore.h" -#include "namedcodeconstants.h" -#include "../../../common/windows/cpp/include/unicode.h" - -#include "edition.h" - -#include "CharToKeyConversion.h" -#include "CasedKeys.h" -#include "CheckNCapsConsistency.h" -#include "CheckFilenameConsistency.h" -#include "UnreachableRules.h" -#include "CheckForDuplicates.h" - -int xatoi(PWSTR *p); -int atoiW(PWSTR p); -void safe_wcsncpy(PWSTR out, PWSTR in, int cbMax); -int UTF32ToUTF16(int n, int *n1, int *n2); -int GetDeadKey(PFILE_KEYBOARD fk, PWSTR p); - -BOOL IsValidCallStore(PFILE_STORE fs); -BOOL IsSameToken(PWSTR *p, PWSTR token); -DWORD GetRHS(PFILE_KEYBOARD fk, PWSTR p, PWSTR buf, int bufsize, int offset, int IsUnicode); -PWSTR GetDelimitedString(PWSTR *p, PWSTR Delimiters, WORD Flags); -DWORD GetXString(PFILE_KEYBOARD fk, PWSTR str, PWSTR token, PWSTR output, int max, int offset, PWSTR *newp, int isVKey, - int isUnicode); - -int GetGroupNum(PFILE_KEYBOARD fk, PWSTR p); -int LineTokenType(PWSTR *str); - -DWORD ParseLine(PFILE_KEYBOARD fk, PWSTR str); - -DWORD ProcessGroupFinish(PFILE_KEYBOARD fk); -DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PWSTR p); -DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PWSTR p); -DWORD AddDebugStore(PFILE_KEYBOARD fk, PWSTR str); -DWORD ProcessKeyLine(PFILE_KEYBOARD fk, PWSTR str, BOOL IsUnicode); -DWORD ProcessEthnologueStore(PWSTR p); // I2646 -DWORD ProcessHotKey(PWSTR p, DWORD *hk); -DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PWSTR szName, PDWORD FileSize, PBYTE *Buf); - -DWORD ExpandKp(PFILE_KEYBOARD fk, PFILE_KEY kpp, DWORD storeIndex); - -DWORD ReadLine(HANDLE hInfile, PWSTR str, BOOL PreProcess); - -DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, HANDLE hOutfile); -BOOL CompileKeyboardHandle(HANDLE hInfile, PFILE_KEYBOARD fk); - -int GetVKCode(PFILE_KEYBOARD fk, PWSTR p); // I3438 // TODO: Consolidate GetDeadKey and GetVKCode? -DWORD BuildVKDictionary(PFILE_KEYBOARD fk); // I3438 -DWORD AddStore(PFILE_KEYBOARD fk, DWORD SystemID, PWSTR str, DWORD *dwStoreID = NULL); -DWORD ProcessSystemStore(PFILE_KEYBOARD fk, DWORD SystemID, PFILE_STORE sp); -void RecordDeadkeyNames(PFILE_KEYBOARD fk); -DWORD AddCompilerVersionStore(PFILE_KEYBOARD fk); -BOOL CheckStoreUsage(PFILE_KEYBOARD fk, int storeIndex, BOOL fIsStore, BOOL fIsOption, BOOL fIsCall); - -DWORD process_if(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); -DWORD process_reset(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); -DWORD process_set(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); -DWORD process_save(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); -DWORD process_platform(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); // I3430 -DWORD process_baselayout(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); // I3430 -DWORD process_set_synonym(DWORD dwSystemID, PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); // I3437 -DWORD process_expansion(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx, int max); - -BOOL IsValidKeyboardVersion(WCHAR *dpString); // I4140 - -HANDLE UTF16TempFromUTF8(HANDLE hInfile, BOOL hasPreamble); - -const PWCHAR LineTokens[] = { - L"SVNBHBGMNSCCLLCMLB", L"store", L"VERSION ", L"NAME ", - L"BITMAP ", L"HOTKEY ", L"begin", L"group", L"match", L"nomatch", - L"SHIFT FREES CAPS", L"CAPS ON ONLY", L"CAPS ALWAYS OFF", - L"LANGUAGE ", L"LAYOUT ", L"COPYRIGHT ", L"MESSAGE ", L"LANGUAGENAME ", - L"BITMAPS " }; - -#define SSN__PREFIX L"&" - -const PWCHAR StoreTokens[] = { - L"", - SSN__PREFIX L"BITMAP", - SSN__PREFIX L"COPYRIGHT", - SSN__PREFIX L"HOTKEY", - SSN__PREFIX L"LANGUAGE", - SSN__PREFIX L"LAYOUT", - SSN__PREFIX L"MESSAGE", - SSN__PREFIX L"NAME", - SSN__PREFIX L"VERSION", - SSN__PREFIX L"CAPSONONLY", - SSN__PREFIX L"CAPSALWAYSOFF", - SSN__PREFIX L"SHIFTFREESCAPS", - SSN__PREFIX L"LANGUAGENAME", - L"", - L"", - SSN__PREFIX L"ETHNOLOGUECODE", - L"", - SSN__PREFIX L"MNEMONICLAYOUT", - SSN__PREFIX L"INCLUDECODES", - SSN__PREFIX L"OLDCHARPOSMATCHING", - L"", - L"", - L"", - L"", - SSN__PREFIX L"VISUALKEYBOARD", - SSN__PREFIX L"KMW_RTL", - SSN__PREFIX L"KMW_HELPFILE", - SSN__PREFIX L"KMW_HELPTEXT", - SSN__PREFIX L"KMW_EMBEDJS", - SSN__PREFIX L"WINDOWSLANGUAGES", - L"", - SSN__PREFIX L"PLATFORM", // read only // I3430 - SSN__PREFIX L"BASELAYOUT", // read only // I3430 - SSN__PREFIX L"LAYER", // read-write via set? // I3430 - L"", // I3438 - SSN__PREFIX L"LAYOUTFILE", // I3483 - SSN__PREFIX L"KEYBOARDVERSION", // I4140 - SSN__PREFIX L"KMW_EMBEDCSS", - SSN__PREFIX L"TARGETS", // I4504 - SSN__PREFIX L"CASEDKEYS", // #2241 - SSN__PREFIX L"", // TSS_BEGIN_NEWCONTEXT - SSN__PREFIX L"", // TSS_BEGIN_POSTKEYSTROKE - SSN__PREFIX L"NEWLAYER", - SSN__PREFIX L"OLDLAYER", - NULL -}; - -static_assert(_countof(StoreTokens) == TSS__MAX + 2, "StoreTokens should have exactly TSS__MAX+2 elements"); - -HINSTANCE g_hInstance; -CompilerMessageProc msgproc = NULL; -int currentLine = 0, nErrors = 0; -char CompileDir[MAX_PATH]; -int ErrChr; -char ErrExtra[256]; -BOOL FSaveDebug, FCompilerWarningsAsErrors, FWarnDeprecatedCode; // I4865 // I4866 -BOOL FShouldAddCompilerVersion = TRUE; -BOOL FOldCharPosMatching = FALSE, FMnemonicLayout = FALSE; -NamedCodeConstants *CodeConstants = NULL; - -int BeginLine[4]; - -/* Compile target */ - -int CompileTarget; - -#define CKF_KEYMAN 0 -#define CKF_KEYMANWEB 1 - -BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID lpvReserved) -{ - if (fdwReason == DLL_PROCESS_ATTACH) g_hInstance = hinst; - return TRUE; -} - - -PWSTR strtowstr(PSTR in) -{ - PWSTR result; - size_t len; - - mbstowcs_s(&len, NULL, 0, in, strlen(in)); // I3481 - result = new WCHAR[len + 1]; - mbstowcs_s(&len, result, len + 1, in, strlen(in)); // I3481 // I3641 - result[len] = 0; - return result; -} - -PSTR wstrtostr(PWSTR in) -{ - PSTR result; - size_t len; - - wcstombs_s(&len, NULL, 0, in, wcslen(in)); // I3481 - result = new CHAR[len + 1]; - wcstombs_s(&len, result, len + 1, in, wcslen(in)); // I3481 // I3641 - result[len] = 0; - return result; -} - -BOOL AddCompileString(LPSTR buf) -{ - SetLastError(0); - (*msgproc)(currentLine + 1, CWARN_Info, buf); - return FALSE; -} - -BOOL AddCompileMessage(DWORD msg) -{ - char szText[SZMAX_ERRORTEXT + 1 + 280]; - - SetLastError(0); - if (msg & CERR_FATAL) - { - LoadString(g_hInstance, msg, szText, SZMAX_ERRORTEXT); - (*msgproc)(currentLine + 1, msg, szText); - nErrors++; - return TRUE; - } - - if (msg & CERR_ERROR) nErrors++; - LoadString(g_hInstance, msg, szText, SZMAX_ERRORTEXT); - if (ErrChr > 0) - wsprintf(strchr(szText, 0), " character offset: %d", ErrChr); - if (*ErrExtra) - wsprintf(strchr(szText, 0), " %s", ErrExtra); - - ErrChr = 0; *ErrExtra = 0; - - if (!(*msgproc)(currentLine, msg, szText)) return TRUE; - - return FALSE; -} - -extern "C" BOOL __declspec(dllexport) SetCompilerOptions(PCOMPILER_OPTIONS options) { - if(!options || options->dwSize < sizeof(COMPILER_OPTIONS)) { - return FALSE; - } - - FShouldAddCompilerVersion = options->ShouldAddCompilerVersion; - return TRUE; -} - -extern "C" BOOL __declspec(dllexport) CompileKeyboardFile(PSTR pszInfile, PSTR pszOutfile, BOOL ASaveDebug, BOOL ACompilerWarningsAsErrors, BOOL AWarnDeprecatedCode, CompilerMessageProc pMsgProc) // I4865 // I4866 -{ - HANDLE hInfile = INVALID_HANDLE_VALUE, hOutfile = INVALID_HANDLE_VALUE; - BOOL err; - DWORD len; - char str[260]; - - FSaveDebug = ASaveDebug; - FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865 - FWarnDeprecatedCode = AWarnDeprecatedCode; // I4866 - - CompileTarget = CKF_KEYMAN; - - if (!pMsgProc || !pszInfile || !pszOutfile) SetError(CERR_BadCallParams); - - PSTR p; - if (p = strrchr(pszInfile, '\\')) - { - strncpy_s(CompileDir, _countof(CompileDir), pszInfile, (INT_PTR)(p - pszInfile + 1)); // I3481 - CompileDir[(INT_PTR)(p - pszInfile + 1)] = 0; - } - else - CompileDir[0] = 0; - - msgproc = pMsgProc; - currentLine = 0; - nErrors = 0; - - AddCompileString("NOTE: Using legacy compiler"); - - hInfile = CreateFileA(pszInfile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if (hInfile == INVALID_HANDLE_VALUE) 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 (!ReadFile(hInfile, str, 3, &len, NULL)) - { - CloseHandle(hInfile); - return CERR_CannotReadInfile; - } - SetFilePointer(hInfile, 0, NULL, FILE_BEGIN); - if (str[0] == UTF8Sig[0] && str[1] == UTF8Sig[1] && str[2] == UTF8Sig[2]) - hInfile = UTF16TempFromUTF8(hInfile, TRUE); - else if (str[0] == UTF16Sig[0] && str[1] == UTF16Sig[1]) - SetFilePointer(hInfile, 2, NULL, FILE_BEGIN); - else - hInfile = UTF16TempFromUTF8(hInfile, FALSE); // Will fall back to ansi for invalid UTF-8 - if (hInfile == INVALID_HANDLE_VALUE) // I3228 // I3510 - { - return CERR_CannotCreateTempfile; - } - - hOutfile = CreateFileA(pszOutfile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); - if (hOutfile == INVALID_HANDLE_VALUE) SetError(CERR_CannotCreateOutfile); - - DWORD msg; - FILE_KEYBOARD fk; - CodeConstants = new NamedCodeConstants; - - err = CompileKeyboardHandle(hInfile, &fk); - if (err) - { - if ((msg = WriteCompiledKeyboard(&fk, hOutfile)) != CERR_None) - AddCompileMessage(msg); - } - else - AddCompileMessage(CERR_InvalidValue); - - CloseHandle(hInfile); - CloseHandle(hOutfile); - - delete CodeConstants; - - if (nErrors > 0) - { - DeleteFile(pszOutfile); - return FALSE; - } - - return err; -} - - -extern "C" BOOL __declspec(dllexport) CompileKeyboardFileToBuffer(PSTR pszInfile, PFILE_KEYBOARD pfkBuffer, BOOL ACompilerWarningsAsErrors, BOOL AWarnDeprecatedCode, CompilerMessageProc pMsgProc, int Target) // I4865 // I4866 -{ - HANDLE hInfile = INVALID_HANDLE_VALUE; - BOOL err; - DWORD len; - char str[260]; - - FSaveDebug = TRUE; // I3681 - FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865 - FWarnDeprecatedCode = AWarnDeprecatedCode; // I4866 - - CompileTarget = Target; - - if (!pMsgProc || !pszInfile || !pfkBuffer) SetError(CERR_BadCallParams); - - PSTR p; - if (p = strrchr(pszInfile, '\\')) - { - strncpy_s(CompileDir, _countof(CompileDir), pszInfile, (INT_PTR)(p - pszInfile + 1)); // I3481 - CompileDir[(INT_PTR)(p - pszInfile + 1)] = 0; - } - else - CompileDir[0] = 0; - - msgproc = pMsgProc; - currentLine = 0; - nErrors = 0; - - AddCompileString("NOTE: Using legacy compiler"); - - hInfile = CreateFileA(pszInfile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if (hInfile == INVALID_HANDLE_VALUE) 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 (!ReadFile(hInfile, str, 3, &len, NULL)) - { - CloseHandle(hInfile); - return CERR_CannotReadInfile; - } - SetFilePointer(hInfile, 0, NULL, FILE_BEGIN); - if (str[0] == UTF8Sig[0] && str[1] == UTF8Sig[1] && str[2] == UTF8Sig[2]) - hInfile = UTF16TempFromUTF8(hInfile, TRUE); - else if (str[0] == UTF16Sig[0] && str[1] == UTF16Sig[1]) - SetFilePointer(hInfile, 2, NULL, FILE_BEGIN); - else - hInfile = UTF16TempFromUTF8(hInfile, FALSE); - - CodeConstants = new NamedCodeConstants; - err = CompileKeyboardHandle(hInfile, pfkBuffer); - delete CodeConstants; - CloseHandle(hInfile); - - if (nErrors > 0) - return FALSE; - - return err; -} - -void GetVersionInfo(DWORD *VersionMajor, DWORD *VersionMinor) -{ - HRSRC hres = FindResource(0, MAKEINTRESOURCE(1), RT_VERSION); - if (hres) - { - HGLOBAL hmem = LoadResource(0, hres); - PSTR buf = (PSTR)LockResource(hmem); - *VersionMajor = *((PDWORD)&buf[0x30]); - *VersionMinor = *((PDWORD)&buf[0x34]); - } -} - -BOOL CompileKeyboardHandle(HANDLE hInfile, PFILE_KEYBOARD fk) -{ - PWSTR str, p; - - DWORD msg; - - FMnemonicLayout = FALSE; - - if (!fk) { - SetError(CERR_SomewhereIGotItWrong); - } - - str = new 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; - - BeginLine[BEGIN_ANSI] = -1; - BeginLine[BEGIN_UNICODE] = -1; - BeginLine[BEGIN_NEWCONTEXT] = -1; - BeginLine[BEGIN_POSTKEYSTROKE] = -1; - - /* Add a store for the Keyman 6.0 copyright information string */ - - if(FShouldAddCompilerVersion) { - DWORD vmajor, vminor; - GetVersionInfo(&vmajor, &vminor); - swprintf(str, LINESIZE, L"Created with Keyman Developer version %d.%d.%d.%d", HIWORD(vmajor), - LOWORD(vmajor), HIWORD(vminor), LOWORD(vminor)); // I3481 - - AddStore(fk, TSS_KEYMANCOPYRIGHT, str); - } - - /* Add a system store for the Keyman edition number */ - - swprintf(str, LINESIZE, L"%d", 0); // I3481 - AddStore(fk, TSS_CUSTOMKEYMANEDITION, str); - PWSTR tbuf = strtowstr((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(hInfile, 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; - } - } - - if (msg != CERR_EndOfFile) SetError(msg); - - SetFilePointer(hInfile, 2, NULL, FILE_BEGIN); - currentLine = 0; - - /* Reindex the list of codeconstants after stores added */ - - CodeConstants->reindex(); - - /* ReadLine will automatically skip over $Keyman lines, and parse wrapped lines */ - while ((msg = ReadLine(hInfile, str, FALSE)) == CERR_None) - { - msg = ParseLine(fk, str); - if (msg != CERR_None) SetError(msg); - } - - if (msg != CERR_EndOfFile) SetError(msg); - - ProcessGroupFinish(fk); - - if (FSaveDebug) RecordDeadkeyNames(fk); - - /* Add the compiler version as a system store */ - if ((msg = AddCompilerVersionStore(fk)) != CERR_None) SetError(msg); - - if ((msg = BuildVKDictionary(fk)) != CERR_None) SetError(msg); // I3438 - - if ((msg = CheckFilenameConsistencyForCalls(fk)) != CERR_None) SetError(msg); - - delete str; - - if (!CheckKeyboardFinalVersion(fk)) { - return FALSE; - } - - /* Warn on inconsistent use of NCAPS */ - if (!FMnemonicLayout) { - CheckNCapsConsistency(fk); - } - - /* Flag presence of deprecated features */ - CheckForDeprecatedFeatures(fk); - - return TRUE; -} - -DWORD ProcessBeginLine(PFILE_KEYBOARD fk, PWSTR p) -{ - WCHAR tstr[128]; - PWSTR q, pp; - int BeginMode; - DWORD msg; - - pp = p; - - q = wcschr(p, '>'); - if (!q) return CERR_NoTokensFound; - - while (iswspace(*p)) p++; - if (_wcsnicmp(p, L"unicode", 7) == 0) BeginMode = BEGIN_UNICODE; - else if (_wcsnicmp(p, L"ansi", 4) == 0) BeginMode = BEGIN_ANSI; - else if (_wcsnicmp(p, L"newContext", 10) == 0) BeginMode = BEGIN_NEWCONTEXT; - else if (_wcsnicmp(p, L"postKeystroke", 13) == 0) BeginMode = BEGIN_POSTKEYSTROKE; - else if (*p != '>') return CERR_InvalidToken; - else BeginMode = BEGIN_ANSI; - - if(BeginLine[BeginMode] != -1) { - return CERR_RepeatedBegin; - } - BeginLine[BeginMode] = currentLine; - - if ((msg = GetRHS(fk, p, tstr, 80, (int)(INT_PTR)(p - pp), FALSE)) != CERR_None) return msg; - - if (tstr[0] != UC_SENTINEL || tstr[1] != CODE_USE) { - return CERR_InvalidBegin; - } - if (tstr[3] != 0) { - return CERR_InvalidToken; - } - - if (BeginMode == BEGIN_ANSI || BeginMode == BEGIN_UNICODE) { - fk->StartGroup[BeginMode] = tstr[2] - 1; - // mcd-03-01-2000: removed the secondary group idea; this was undocumented and - // is not supported under Keyman 5.0: ugly!! - // if(tstr[3] == UC_SENTINEL && tstr[4] == CODE_USE) fk->StartGroup[1] = tstr[5] - 1; - - if (FSaveDebug) { - /* Record a system store for the line number of the begin statement */ - AddDebugStore(fk, BeginMode == BEGIN_UNICODE ? DEBUGSTORE_BEGIN L"Unicode" : DEBUGSTORE_BEGIN L"ANSI"); - } - } else { - PFILE_GROUP gp = &fk->dpGroupArray[tstr[2] - 1]; - if (!gp->fReadOnly) { - return BeginMode == BEGIN_NEWCONTEXT ? - CERR_NewContextGroupMustBeReadonly : - CERR_PostKeystrokeGroupMustBeReadonly; - } - return AddStore(fk, BeginMode == BEGIN_NEWCONTEXT ? TSS_BEGIN_NEWCONTEXT : TSS_BEGIN_POSTKEYSTROKE, tstr, NULL); - } - - return CERR_None; -} - -DWORD ValidateMatchNomatchOutput(PWSTR p) { - while (p && *p) { - if (*p == UC_SENTINEL) { - switch (*(p + 1)) { - case CODE_CONTEXT: - case CODE_CONTEXTEX: - case CODE_INDEX: - return CERR_ContextAndIndexInvalidInMatchNomatch; - } - } - p = incxstr(p); - } - return CERR_None; -} - -DWORD ParseLine(PFILE_KEYBOARD fk, PWSTR str) -{ - PWSTR p, q, pp; - PFILE_GROUP gp; - DWORD msg; - int IsUnicode = TRUE; // For NOW! - - p = str; - pp = str; - - switch (LineTokenType(&p)) - { - case T_BLANK: - case T_COMMENT: - break; // Ignore the line - case T_VERSION: - case T_STORE: - break; // The line has already been processed - - case T_BEGIN: - // after a begin can be "Unicode", "ANSI", "NewContext", "PostKeystroke", or nothing (=ANSI) - if ((msg = ProcessBeginLine(fk, p)) != CERR_None) return msg; - break; - - case T_GROUP: - if (fk->currentGroup == 0xFFFFFFFF) fk->currentGroup = 0; - else - { - if ((msg = ProcessGroupFinish(fk)) != CERR_None) return msg; // finish off previous group first? - fk->currentGroup++; - } - // if( (err = ProcessGroupLine( fk, p )) != CERR_None ) return err; - break; - - case T_NAME: - WarnDeprecatedHeader(); // I4866 - q = GetDelimitedString(&p, L"\"\"", 0); - if (!q) return CERR_InvalidName; - - if ((msg = AddStore(fk, TSS_NAME, q)) != CERR_None) return msg; - break; - - case T_COPYRIGHT: - WarnDeprecatedHeader(); // I4866 - q = GetDelimitedString(&p, L"\"\"", 0); - if (!q) return CERR_InvalidCopyright; - - if ((msg = AddStore(fk, TSS_COPYRIGHT, q)) != CERR_None) return msg; - break; - - case T_MESSAGE: - WarnDeprecatedHeader(); // I4866 - q = GetDelimitedString(&p, L"\"\"", 0); - if (!q) return CERR_InvalidMessage; - - if ((msg = AddStore(fk, TSS_MESSAGE, q)) != CERR_None) return msg; - break; - - case T_LANGUAGENAME: - WarnDeprecatedHeader(); // I4866 - q = GetDelimitedString(&p, L"\"\"", 0); - if (!q) return CERR_InvalidLanguageName; - - if ((msg = AddStore(fk, TSS_LANGUAGENAME, q)) != CERR_None) return msg; - break; - - case T_LANGUAGE: - { - WarnDeprecatedHeader(); // I4866 - wchar_t *tokcontext = NULL; - q = wcstok_s(p, L"\n", &tokcontext); // I3481 - if ((msg = AddStore(fk, TSS_LANGUAGE, q)) != CERR_None) return msg; - break; - } - case T_LAYOUT: - { - WarnDeprecatedHeader(); // I4866 - wchar_t *tokcontext = NULL; - q = wcstok_s(p, L"\n", &tokcontext); // I3481 - if ((msg = AddStore(fk, TSS_LAYOUT, q)) != CERR_None) return msg; - break; - } - case T_CAPSOFF: - WarnDeprecatedHeader(); // I4866 - if ((msg = AddStore(fk, TSS_CAPSALWAYSOFF, L"1")) != CERR_None) return msg; - break; - - case T_CAPSON: - WarnDeprecatedHeader(); // I4866 - if ((msg = AddStore(fk, TSS_CAPSONONLY, L"1")) != CERR_None) return msg; - break; - - case T_SHIFT: - WarnDeprecatedHeader(); // I4866 - if ((msg = AddStore(fk, TSS_SHIFTFREESCAPS, L"1")) != CERR_None) return msg; - break; - - case T_HOTKEY: - { - WarnDeprecatedHeader(); // I4866 - wchar_t *tokcontext = NULL; - if ((q = wcstok_s(p, L"\n", &tokcontext)) == NULL) return CERR_CodeInvalidInThisSection; // I3481 - if ((msg = AddStore(fk, TSS_HOTKEY, q)) != CERR_None) return msg; - break; - } - case T_BITMAP: - { - WarnDeprecatedHeader(); // I4866 - wchar_t *tokcontext = NULL; - if ((q = wcstok_s(p, L"\n", &tokcontext)) == NULL) return CERR_InvalidBitmapLine; // I3481 - - while (iswspace(*q)) q++; - if (*q == '"') { - p = q; - q = GetDelimitedString(&p, L"\"\"", 0); - if (!q) return CERR_InvalidBitmapLine; - } - - if ((msg = AddStore(fk, TSS_BITMAP, q)) != CERR_None) return msg; - break; - } - case T_BITMAPS: - { - WarnDeprecatedHeader(); // I4866 - wchar_t *tokcontext = NULL; - AddWarning(CWARN_BitmapNotUsed); - - if ((q = wcstok_s(p, L"\n", &tokcontext)) == NULL) return CERR_InvalidBitmapLine; // I3481 - if (wcschr(q, ',')) *wcschr(q, ',') = 0; - if ((msg = AddStore(fk, TSS_BITMAP, q)) != CERR_None) return msg; - - break; - } - case T_KEYTOKEY: // A rule - if (fk->currentGroup == 0xFFFFFFFF) return CERR_CodeInvalidInThisSection; - if ((msg = ProcessKeyLine(fk, p, IsUnicode)) != CERR_None) return msg; - break; - - case T_MATCH: - if (fk->currentGroup == 0xFFFFFFFF) return CERR_CodeInvalidInThisSection; - { - PWCHAR buf = new WCHAR[GLOBAL_BUFSIZE]; - if ((msg = GetRHS(fk, p, buf, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(p - pp), IsUnicode)) != CERR_None) - { - delete buf; - return msg; - } - - if ((msg = ValidateMatchNomatchOutput(buf)) != CERR_None) { - delete buf; - return msg; - } - - gp = &fk->dpGroupArray[fk->currentGroup]; - - gp->dpMatch = new WCHAR[wcslen(buf) + 1]; - wcscpy_s(gp->dpMatch, wcslen(buf) + 1, buf); // I3481 - - delete buf; - - if (FSaveDebug) - { - WCHAR tstr[128]; - //char buf[256]; - //swprintf(tstr, "%d", fk->currentGroup); - /* Record a system store for the line number of the begin statement */ - //wcscpy(tstr, DEBUGSTORE_MATCH); - - //wcscat(tstr, pw); - - swprintf(tstr, _countof(tstr), L"%ls%d %ls", DEBUGSTORE_MATCH, (int) fk->currentGroup, gp->szName); // I3481 - AddDebugStore(fk, tstr); - } - } - break; - - case T_NOMATCH: - if (fk->currentGroup == 0xFFFFFFFF) return CERR_CodeInvalidInThisSection; - { - PWCHAR buf = new WCHAR[GLOBAL_BUFSIZE]; - if ((msg = GetRHS(fk, p, buf, GLOBAL_BUFSIZE, (int)(INT_PTR)(p - pp), IsUnicode)) != CERR_None) - { - delete[] buf; - return msg; - } - - if ((msg = ValidateMatchNomatchOutput(buf)) != CERR_None) { - delete[] buf; - return msg; - } - - gp = &fk->dpGroupArray[fk->currentGroup]; - - gp->dpNoMatch = new WCHAR[wcslen(buf) + 1]; - wcscpy_s(gp->dpNoMatch, wcslen(buf) + 1, buf); // I3481 - - delete[] buf; - - if (FSaveDebug) - { - WCHAR tstr[128]; - /* Record a system store for the line number of the begin statement */ - swprintf(tstr, _countof(tstr), L"%ls%d %ls", DEBUGSTORE_NOMATCH, fk->currentGroup, gp->szName); // I3481 - AddDebugStore(fk, tstr); - } - } - break; - - default: - return CERR_InvalidToken; - } - - return CERR_None; -} - -//********************************************************************************************************************** - -DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PWSTR p) -{ - PFILE_GROUP gp; - PWSTR q; - - gp = new FILE_GROUP[fk->cxGroupArray + 1]; - if (!gp) return CERR_CannotAllocateMemory; - - if (fk->dpGroupArray) - { - memcpy(gp, fk->dpGroupArray, sizeof(FILE_GROUP) * fk->cxGroupArray); - delete fk->dpGroupArray; - } - - fk->dpGroupArray = gp; - gp = &fk->dpGroupArray[fk->cxGroupArray]; - fk->cxGroupArray++; - - gp->dpKeyArray = NULL; - gp->dpMatch = NULL; - gp->dpNoMatch = NULL; - gp->cxKeyArray = 0; - - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q) return CERR_InvalidGroupLine; - - gp->fUsingKeys = FALSE; - gp->fReadOnly = IsSameToken(&p, L"readonly"); - if (!gp->fReadOnly) { - if (IsSameToken(&p, L"using") && IsSameToken(&p, L"keys")) - gp->fUsingKeys = TRUE; - } - - safe_wcsncpy(gp->szName, q, SZMAX_GROUPNAME); - - gp->Line = currentLine; - - if (FSaveDebug) - { - WCHAR tstr[128]; - /* Record a system store for the line number of the begin statement */ - swprintf(tstr, _countof(tstr), L"%ls%d %ls", DEBUGSTORE_GROUP, fk->cxGroupArray - 1, gp->szName); // I3481 - AddDebugStore(fk, tstr); - } - - return CheckForDuplicateGroup(fk, gp); -} - -int cmpkeys(const void *key, const void *elem) -{ - PFILE_KEY akey, aelem; - int l1, l2; - WCHAR char_key, char_elem; - akey = (PFILE_KEY)key; - aelem = (PFILE_KEY)elem; - char_key = VKToChar(akey->Key, akey->ShiftFlags); - char_elem = VKToChar(aelem->Key, aelem->ShiftFlags); - if (char_key == char_elem) //akey->Key == aelem->Key) - { - l1 = xstrlen(akey->dpContext); l2 = xstrlen(aelem->dpContext); - if (l1 == l2) - { - if (akey->Line < aelem->Line) return -1; - if (akey->Line > aelem->Line) return 1; - if(akey->Key == aelem->Key) { - if(akey->ShiftFlags == aelem->ShiftFlags) { - return akey->LineStoreIndex - aelem->LineStoreIndex; - } - return akey->ShiftFlags - aelem->ShiftFlags; - } - return akey->Key - aelem->Key; - } - if (l1 < l2) return 1; - if (l1 > l2) return -1; - if(akey->Key == aelem->Key) { - if(akey->ShiftFlags == aelem->ShiftFlags) { - return akey->LineStoreIndex - aelem->LineStoreIndex; - } - return akey->ShiftFlags - aelem->ShiftFlags; - } - return akey->Key - aelem->Key; - } - return(char_key - char_elem); // akey->Key - aelem->Key); -} - -DWORD ProcessGroupFinish(PFILE_KEYBOARD fk) -{ - PFILE_GROUP gp; - DWORD msg; - - if (fk->currentGroup == 0xFFFFFFFF) return CERR_None; - // Just got to first group - so nothing to finish yet - - gp = &fk->dpGroupArray[fk->currentGroup]; - - // Finish off the previous group stuff! - if ((msg = ExpandCapsRulesForGroup(fk, gp)) != CERR_None) return msg; - qsort(gp->dpKeyArray, gp->cxKeyArray, sizeof(FILE_KEY), cmpkeys); - - return VerifyUnreachableRules(gp); -} - -/*************************************** -* Store management -*/ - -DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PWSTR p) -{ - PWSTR q, pp; - PFILE_STORE sp; - //WCHAR temp[GLOBAL_BUFSIZE]; - DWORD msg; - int i = 0; - - pp = p; - - if ((q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL)) == NULL) return CERR_InvalidStoreLine; - - if (*q == *SSN__PREFIX) - { - for (i = 0; StoreTokens[i]; i++) - if (!_wcsicmp(q, StoreTokens[i])) // I3481 - break; - if (!StoreTokens[i]) return CERR_InvalidSystemStore; - } - - sp = new FILE_STORE[fk->cxStoreArray + 1]; - if (!sp) return CERR_CannotAllocateMemory; - - if (fk->dpStoreArray) - { - memcpy(sp, fk->dpStoreArray, sizeof(FILE_STORE) * fk->cxStoreArray); - delete fk->dpStoreArray; - } - - fk->dpStoreArray = sp; - sp = &fk->dpStoreArray[fk->cxStoreArray]; - - sp->line = currentLine; - sp->fIsOption = FALSE; - sp->fIsReserved = FALSE; - sp->fIsStore = FALSE; - sp->fIsDebug = FALSE; - sp->fIsCall = FALSE; - - safe_wcsncpy(sp->szName, q, SZMAX_STORENAME); - { - PWCHAR temp = new WCHAR[GLOBAL_BUFSIZE]; - - if ((msg = GetXString(fk, p, L"c\n", temp, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(p - pp), &p, FALSE, TRUE)) != CERR_None) - { - delete[] temp; - return msg; - } - - sp->dwSystemID = i; - sp->dpString = new WCHAR[wcslen(temp) + 1]; - wcscpy_s(sp->dpString, wcslen(temp) + 1, temp); // I3481 - - delete[] temp; - } - - if (xstrlen(sp->dpString) == 1 && *sp->dpString != UC_SENTINEL && - sp->dwSystemID == 0 && (fk->version >= VERSION_60 || fk->version == 0)) - { - // In this case, we want to change behaviour for older versioned keyboards so that - // we don't mix up named character codes which weren't supported in 5.x - VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes); - // Add a single char store as a defined character constant - if (Uni_IsSurrogate1(*sp->dpString)) - CodeConstants->AddCode(Uni_SurrogateToUTF32(sp->dpString[0], sp->dpString[1]), sp->szName, fk->cxStoreArray); - else - CodeConstants->AddCode(sp->dpString[0], sp->szName, fk->cxStoreArray); - CodeConstants->reindex(); // has to be done after every character add due to possible use in another store. // I4982 - } - - fk->cxStoreArray++; // increment now, because GetXString refers to stores - - if (i > 0) - if ((msg = ProcessSystemStore(fk, i, sp)) != CERR_None) return msg; - - return CheckForDuplicateStore(fk, sp); -} - -DWORD AddStore(PFILE_KEYBOARD fk, DWORD SystemID, PWSTR str, DWORD *dwStoreID) -{ - PFILE_STORE sp; - - sp = new FILE_STORE[fk->cxStoreArray + 1]; - if (!sp) return CERR_CannotAllocateMemory; - - if (fk->dpStoreArray) - { - memcpy(sp, fk->dpStoreArray, sizeof(FILE_STORE) * fk->cxStoreArray); - delete fk->dpStoreArray; - } - - fk->dpStoreArray = sp; - sp = &fk->dpStoreArray[fk->cxStoreArray]; - - sp->line = currentLine; - sp->fIsOption = FALSE; // I3686 - sp->fIsReserved = (SystemID != TSS_NONE); - sp->fIsStore = FALSE; - sp->fIsDebug = FALSE; - sp->fIsCall = FALSE; - - safe_wcsncpy(sp->szName, StoreTokens[SystemID], SZMAX_STORENAME); - - sp->dpString = new WCHAR[wcslen(str) + 1]; - wcscpy_s(sp->dpString, wcslen(str) + 1, str); // I3481 - - sp->dwSystemID = SystemID; - - if (dwStoreID) *dwStoreID = fk->cxStoreArray; - - fk->cxStoreArray++; - - return ProcessSystemStore(fk, SystemID, sp); -} - -DWORD AddDebugStore(PFILE_KEYBOARD fk, PWSTR str) -{ - PFILE_STORE sp; - WCHAR tstr[16]; - - swprintf(tstr, _countof(tstr), L"%d", currentLine); // I3481 - - sp = new FILE_STORE[fk->cxStoreArray + 1]; - if (!sp) return CERR_CannotAllocateMemory; - - if (fk->dpStoreArray) - { - memcpy(sp, fk->dpStoreArray, sizeof(FILE_STORE) * fk->cxStoreArray); - delete[] fk->dpStoreArray; - } - - fk->dpStoreArray = sp; - sp = &fk->dpStoreArray[fk->cxStoreArray]; - - safe_wcsncpy(sp->szName, str, SZMAX_STORENAME); - - sp->dpString = new WCHAR[wcslen(tstr) + 1]; - wcscpy_s(sp->dpString, wcslen(tstr) + 1, tstr); // I3481 - sp->line = 0; - sp->fIsOption = FALSE; - sp->fIsReserved = TRUE; - sp->fIsStore = FALSE; - sp->fIsDebug = TRUE; - sp->fIsCall = FALSE; - sp->dwSystemID = TSS_DEBUG_LINE; - fk->cxStoreArray++; - - return CERR_None; -} - -PWCHAR pssBuf = NULL; - -DWORD ProcessSystemStore(PFILE_KEYBOARD fk, DWORD SystemID, PFILE_STORE sp) -{ - //WCHAR buf[GLOBAL_BUFSIZE]; - int i, j; - DWORD msg; - PWSTR p, q; - char *pp; - - if (!pssBuf) pssBuf = new WCHAR[GLOBAL_BUFSIZE]; - PWCHAR buf = pssBuf; - - switch (SystemID) - { - case TSS_BITMAP: - if ((msg = ImportBitmapFile(fk, sp->dpString, &fk->dwBitmapSize, &fk->lpBitmap)) != CERR_None) - return msg; - break; - - case TSS_CALLDEFINITION: - break; - - case TSS_CALLDEFINITION_LOADFAILED: - break; - - case TSS_CAPSALWAYSOFF: - if (*sp->dpString == L'1') fk->dwFlags |= KF_CAPSALWAYSOFF; - break; - - case TSS_CAPSONONLY: - if (*sp->dpString == L'1') fk->dwFlags |= KF_CAPSONONLY; - break; - - case TSS_COMPILEDVERSION: - break; - - case TSS_COPYRIGHT: - break; - - case TSS_DEBUG_LINE: - break; - - case TSS_ETHNOLOGUECODE: - VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_EthnologueCode); - if ((msg = ProcessEthnologueStore(sp->dpString)) != CERR_None) return msg; // I2646 - break; - - case TSS_HOTKEY: - if ((msg = ProcessHotKey(sp->dpString, &fk->dwHotKey)) != CERR_None) return msg; - - swprintf(buf, GLOBAL_BUFSIZE, L"%d", (int)fk->dwHotKey); // I3481 - delete[] sp->dpString; - sp->dpString = new WCHAR[wcslen(buf) + 1]; - wcscpy_s(sp->dpString, wcslen(buf) + 1, buf); // I3481 - break; - - case TSS_INCLUDECODES: - VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes); - pp = wstrtostr(sp->dpString); - if (!CodeConstants->LoadFile(pp)) - { - delete[] pp; - return CERR_CannotLoadIncludeFile; - } - delete[] pp; - CodeConstants->reindex(); // I4982 - break; - - case TSS_LANGUAGE: - { - wchar_t *context = NULL; - q = wcstok_s(sp->dpString, L", ", &context); // I3481 - if (!q) return CERR_InvalidLanguageLine; - - i = xatoi(&q); - q = wcstok_s(NULL, L" c\n", &context); // I3481 - if (!q) - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_InvalidLanguageLine); - j = SUBLANGID(i); - i = PRIMARYLANGID(i); - } - else - j = xatoi(&q); - - if (i < 1 || j < 1 || i > 0x3FF || j > 0x3F) return CERR_InvalidLanguageLine; - if (i >= 0x200 || j >= 0x20) AddWarning(CWARN_CustomLanguagesNotSupported); - - fk->KeyboardID = (DWORD)MAKELANGID(i, j); - - swprintf(buf, GLOBAL_BUFSIZE, L"%x %x", i, j); // I3481 - delete[] sp->dpString; - sp->dpString = new WCHAR[wcslen(buf) + 1]; - wcscpy_s(sp->dpString, wcslen(buf) + 1, buf); // I3481 - - break; - } - case TSS_LANGUAGENAME: - break; - - case TSS_LAYOUT: - if (fk->KeyboardID == 0) return CERR_LayoutButNoLanguage; - - q = sp->dpString; - - fk->KeyboardID |= (xatoi(&q) << 16L); - break; - - case TSS_MESSAGE: - break; - - case TSS_MNEMONIC: - VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_MnemonicLayout); - FMnemonicLayout = atoiW(sp->dpString) == 1; - if (FMnemonicLayout && FindSystemStore(fk, TSS_CASEDKEYS) != NULL) { - // The &CasedKeys system store is not supported for - // mnemonic layouts - return CERR_CasedKeysNotSupportedWithMnemonicLayout; - } - break; - - case TSS_NAME: - break; - - case TSS_OLDCHARPOSMATCHING: - VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_OldCharPosMatching); - FOldCharPosMatching = atoiW(sp->dpString); - break; - - case TSS_SHIFTFREESCAPS: - if (*sp->dpString == L'1') fk->dwFlags |= KF_SHIFTFREESCAPS; - break; - - case TSS_VERSION: - if ((fk->dwFlags & KF_AUTOMATICVERSION) == 0) return CERR_VersionAlreadyIncluded; - p = sp->dpString; - if (wcstof(p, NULL) < 5.0) { - AddWarning(CWARN_OldVersion); - } - - if (wcsncmp(p, L"3.0", 3) == 0) fk->version = VERSION_50; //0x0a0b000n= a.bn - else if (wcsncmp(p, L"3.1", 3) == 0) fk->version = VERSION_50; //all versions < 5.0 - else if (wcsncmp(p, L"3.2", 3) == 0) fk->version = VERSION_50; //we compile as if - else if (wcsncmp(p, L"4.0", 3) == 0) fk->version = VERSION_50; //they are 5.0.100.0 - else if (wcsncmp(p, L"5.01", 4) == 0) fk->version = VERSION_501; - else if (wcsncmp(p, L"5.0", 3) == 0) fk->version = VERSION_50; - else if (wcsncmp(p, L"6.0", 3) == 0) fk->version = VERSION_60; - else if (wcsncmp(p, L"7.0", 3) == 0) fk->version = VERSION_70; - else if (wcsncmp(p, L"8.0", 3) == 0) fk->version = VERSION_80; - else if (wcsncmp(p, L"9.0", 3) == 0) fk->version = VERSION_90; - else if (wcsncmp(p, L"10.0", 4) == 0) fk->version = VERSION_100; - else if (wcsncmp(p, L"14.0", 4) == 0) fk->version = VERSION_140; // Adds support for #917 -- context() with notany() for KeymanWeb - else if (wcsncmp(p, L"15.0", 4) == 0) fk->version = VERSION_150; // Adds support for U_xxxx_yyyy #2858 - else if (wcsncmp(p, L"16.0", 4) == 0) fk->version = VERSION_160; // KMXPlus - else return CERR_InvalidVersion; - - if (fk->version < VERSION_60) FOldCharPosMatching = TRUE; - - fk->dwFlags &= ~KF_AUTOMATICVERSION; - - break; - - case TSS_VISUALKEYBOARD: - VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); - { - // Strip path from the store, leaving bare filename only - p = sp->dpString; - wchar_t *pp = wcsrchr(p, L'\\'); - if (!pp) { - pp = p; - } else { - pp++; - } - q = new WCHAR[wcslen(pp) + 1]; - wcscpy_s(q, wcslen(pp) + 1, pp); - - // Change compiled reference file extension to .kvk - pp = wcschr(q, 0) - 5; - if (pp > q && _wcsicmp(pp, L".kvks") == 0) { - pp[4] = 0; - } - - delete[] sp->dpString; - sp->dpString = q; - - if ((msg = CheckFilenameConsistency(sp->dpString, FALSE)) != CERR_None) { - return msg; - } - } - break; - case TSS_KMW_RTL: - case TSS_KMW_HELPTEXT: - VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); - break; - - 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 - VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyTargets); - break; - - case TSS_WINDOWSLANGUAGES: - { - wchar_t *context = NULL; - VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); - size_t szQ = wcslen(sp->dpString) * 6 + 1; // I3481 - q = new WCHAR[szQ]; // guaranteed to be enough space for recoding - *q = 0; WCHAR *r = q; - p = wcstok_s(sp->dpString, L" ", &context); // I3481 - while (p) - { - int n = xatoi(&p); - - j = SUBLANGID(n); - i = PRIMARYLANGID(n); - - if (i < 1 || j < 1 || i > 0x3FF || j > 0x3F) { - delete[] q; - return CERR_InvalidLanguageLine; - } - - swprintf(r, szQ - (size_t)(r - q), L"x%04.4x ", n); // I3481 - - p = wcstok_s(NULL, L" ", &context); // I3481 - r = wcschr(q, 0); // I3481 - } - delete[] sp->dpString; - if (*q) *(wcschr(q, 0) - 1) = 0; // delete final space - safe because we control the formatting - ugly? scared? - sp->dpString = q; - break; - } - case TSS_COMPARISON: - VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly); - break; - - case TSS_VKDICTIONARY: // I3438 - VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyVirtualKeyDictionary); - break; - - 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; - - case TSS_KEYBOARDVERSION: // I4140 - VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyKeyboardVersion); - if (!IsValidKeyboardVersion(sp->dpString)) { - return CERR_KeyboardVersionFormatInvalid; - } - - break; - - case TSS_CASEDKEYS: - if ((msg = VerifyCasedKeys(sp)) != CERR_None) { - return msg; - } - break; - - case TSS_BEGIN_NEWCONTEXT: - case TSS_BEGIN_POSTKEYSTROKE: - break; - - case TSS_NEWLAYER: - case TSS_OLDLAYER: - break; - - default: - return CERR_InvalidSystemStore; - } - return CERR_None; -} - -BOOL IsValidKeyboardVersion(WCHAR *dpString) { // I4140 - /* version format \d+(\.\d+)* e.g. 9.0.3, 1.0, 1.2.3.4, 6.2.1.4.6.4, blank is not allowed */ - - do { - if (!iswdigit(*dpString)) { - return FALSE; - } - while (iswdigit(*dpString)) { - dpString++; - } - if (*dpString == '.') { - dpString++; - if (!iswdigit(*dpString)) { - return FALSE; - } - } - } while (*dpString != 0); - - return TRUE; -} - -BOOL GetFileVersion(char *filename, WORD *d1, WORD *d2, WORD *d3, WORD *d4) -{ - char fnbuf[260]; - DWORD h; - DWORD sz; - PSTR p; - VS_FIXEDFILEINFO *vffi; - UINT len; - - GetModuleFileName(0, fnbuf, 260); - sz = GetFileVersionInfoSize(fnbuf, &h); - if (sz == 0) return FALSE; - p = new char[sz]; - if (!p) return FALSE; - GetFileVersionInfo(fnbuf, h, sz, p); - VerQueryValue(p, "\\", (void **)&vffi, &len); - - *d1 = HIWORD(vffi->dwFileVersionMS); - *d2 = LOWORD(vffi->dwFileVersionMS); - *d3 = HIWORD(vffi->dwFileVersionLS); - *d4 = LOWORD(vffi->dwFileVersionLS); - - delete[] p; - return TRUE; -} - -DWORD AddCompilerVersionStore(PFILE_KEYBOARD fk) -{ - if(!FShouldAddCompilerVersion) { - return CERR_None; - } - - WCHAR verstr[32]; - WORD d1, d2, d3, d4; - DWORD msg; - - GetFileVersion(NULL, &d1, &d2, &d3, &d4); - swprintf(verstr, _countof(verstr), L"%d.%d.%d.%d", d1, d2, d3, d4); // I3481 - - if ((msg = AddStore(fk, TSS_COMPILEDVERSION, verstr)) != CERR_None) return msg; - - return CERR_None; -} - -/**************************** -* Rule lines -*/ - -DWORD CheckStatementOffsets(PFILE_KEYBOARD fk, PFILE_GROUP gp, PWSTR context, PWSTR output, PWSTR key) { - PWSTR p, q; - int i; - for (p = output; *p; p = incxstr(p)) { - if (*p == UC_SENTINEL) { - if (*(p + 1) == CODE_INDEX) { - int indexStore = *(p + 2) - 1; - int contextOffset = *(p + 3); - for (q = context, i = 1; *q && i < contextOffset; q = incxstr(q), i++); - - if (*q == 0) { - if (!gp->fUsingKeys) - // no key in the rule, so offset is past end of context - return CERR_IndexDoesNotPointToAny; - if (i < contextOffset) // I4914 - // offset is beyond the key - return CERR_IndexDoesNotPointToAny; - q = key; - } - - // find the any - if (*q != UC_SENTINEL || *(q + 1) != CODE_ANY) - return CERR_IndexDoesNotPointToAny; - - int anyStore = *(q + 2) - 1; - - if (xstrlen(fk->dpStoreArray[indexStore].dpString) < xstrlen(fk->dpStoreArray[anyStore].dpString)) { - AddWarning(CWARN_IndexStoreShort); //TODO: if this fails, then we return FALSE instead of an error - } - } else if (*(p + 1) == CODE_CONTEXTEX) { - int contextOffset = *(p + 2); - if (contextOffset > xstrlen(context)) - return CERR_ContextExHasInvalidOffset; - - // Due to a limitation in earlier versions of KeymanWeb, the minimum version - // for context() referring to notany() is 14.0. See #917 for details. - if (CompileTarget == CKF_KEYMANWEB) { - for (q = context, i = 1; *q && i < contextOffset; q = incxstr(q), i++); - if (*q == UC_SENTINEL && *(q + 1) == CODE_NOTANY) { - VERIFY_KEYBOARD_VERSION(fk, VERSION_140, CERR_140FeatureOnlyContextAndNotAnyWeb); - } - } - } - } - } - return CERR_None; -} - -/** - * Checks that the order of statements in the context matches the specification - * Rule structure: [context] ['+' key] '>' output - * Context structure: [nul] [if()|baselayout()|platform()]+ [char|any|context()|deadkey()|dk()|index()|notany()|outs()] - * Test that nul is first, then if(), baselayout(), platform() statements are before any other content - */ -BOOL CheckContextStatementPositions(PWSTR context) { - BOOL hadContextChar = FALSE; - for (PWSTR p = context; *p; p = incxstr(p)) { - if (*p == UC_SENTINEL) { - switch (*(p + 1)) { - case CODE_NUL: - if (p > context) { - AddWarning(CWARN_NulNotFirstStatementInContext); - } - break; - case CODE_IFOPT: - case CODE_IFSYSTEMSTORE: - if (hadContextChar) { - AddWarning(CWARN_IfShouldBeAtStartOfContext); - } - break; - default: - hadContextChar = TRUE; - } - } - else { - hadContextChar = TRUE; - } - } - - return TRUE; -} - -/** - * Checks if a use() statement is followed by other content in the output of a rule - */ -DWORD -CheckUseStatementsInOutput(const PFILE_GROUP gp, const PWSTR output) { // I4867 - BOOL hasUse = FALSE; - PWSTR p; - for (p = output; *p; p = incxstr(p)) { - if (*p == UC_SENTINEL && *(p + 1) == CODE_USE) { - hasUse = TRUE; - } else if (hasUse) { - AddWarning(CWARN_UseNotLastStatementInRule); - break; - } - } - return CERR_None; -} - -/** - * Adds implicit `context` to start of output of rules for readonly groups - */ -DWORD -InjectContextToReadonlyOutput(PWSTR pklOut) { - if (pklOut[0] != UC_SENTINEL || pklOut[1] != CODE_CONTEXT) { - if (wcslen(pklOut) > GLOBAL_BUFSIZE - 3) { - return CERR_CannotAllocateMemory; - } - memmove(pklOut + 2, pklOut, (wcslen(pklOut) + 1) * 2); - pklOut[0] = UC_SENTINEL; - pklOut[1] = CODE_CONTEXT; - } - return CERR_None; -} - -/** - * Verifies that a keyboard does not attempt to emit characters or - * other changes to text store when processing a readonly group - */ -DWORD -CheckOutputIsReadonly(const PFILE_KEYBOARD fk, const PWSTR output) { // I4867 - PWSTR p; - for (p = output; *p; p = incxstr(p)) { - if (*p != UC_SENTINEL) { - return CERR_OutputInReadonlyGroup; - } - switch (*(p + 1)) { - case CODE_CALL: - // We cannot be sure that the callee is going to be readonly - // but we have to operate on a trust basis for call() in any - // case, so we'll allow it. - continue; - case CODE_USE: - // We only allow use() of other readonly groups - { - PFILE_GROUP targetGroup = &fk->dpGroupArray[*(p + 2) - 1]; - if (!targetGroup->fReadOnly) { - return CERR_CannotUseReadWriteGroupFromReadonlyGroup; - } - } - continue; - case CODE_SETOPT: - case CODE_RESETOPT: - case CODE_SAVEOPT: - // it is okay to set, reset or save keyboard options - // although it's hard to see good use cases for this - continue; - case CODE_SETSYSTEMSTORE: - // it is okay to set system stores; Engine or Core will - // ignore set(&) that are not permissible in the given context - continue; - case CODE_CONTEXT: - // We allow `context` but only as the very first statement in output - if (p == output) { - continue; - } - return CERR_OutputInReadonlyGroup; - default: - // Note: conceptually, CODE_NUL could be transformed to CODE_CONTEXT - // if the context was also empty, but it is probably safest to avoid this, - // given CODE_CONTEXT does what we need anyway - return CERR_StatementNotPermittedInReadonlyGroup; - } - } - return CERR_None; -} - - DWORD ProcessKeyLine(PFILE_KEYBOARD fk, PWSTR str, BOOL IsUnicode) -{ - PWSTR p, pp; - DWORD msg; - PFILE_GROUP gp; - PFILE_KEY kp; - PWCHAR pklIn, pklKey, pklOut; - - pklIn = new WCHAR[GLOBAL_BUFSIZE]; // I2432 - Allocate buffers each line -- slightly slower but safer than keeping a single buffer - pklKey = new WCHAR[GLOBAL_BUFSIZE]; - pklOut = new WCHAR[GLOBAL_BUFSIZE]; - if (!pklIn || !pklKey || !pklOut) - return CERR_CannotAllocateMemory; // forget about the little leak if pklKey or pklOut fail... - - __try - { - - gp = &fk->dpGroupArray[fk->currentGroup]; - - pp = str; - - if (gp->fUsingKeys) { - if ((msg = GetXString(fk, str, L"+", pklIn, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg; - - str = p + 1; - if ((msg = GetXString(fk, str, L">", pklKey, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg; - if (pklKey[0] == 0) return CERR_ZeroLengthString; - if (xstrlen(pklKey) > 1) AddWarning(CWARN_KeyBadLength); - } else { - if ((msg = GetXString(fk, str, L">", pklIn, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg; - if (pklIn[0] == 0) return CERR_ZeroLengthString; - } - - str = p + 1; - if ((msg = GetXString(fk, str, L"c\n", pklOut, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg; - - if (pklOut[0] == 0) return CERR_ZeroLengthString; - - CheckContextStatementPositions(pklIn); - - // Test index and context offsets in context - if ((msg = CheckStatementOffsets(fk, gp, pklIn, pklOut, pklKey)) != CERR_None) return msg; - - // Test that use() statements are not followed by other content - if ((msg = CheckUseStatementsInOutput(gp, pklOut)) != CERR_None) { - return msg; // I4867 - } - - if (gp->fReadOnly) { - // Ensure no output is made from the rule, and that - // use() statements meet required readonly semantics - if ((msg = CheckOutputIsReadonly(fk, pklOut)) != CERR_None) { - return msg; - } - - // Inject `context` to start of output if group is readonly - // to keep the output internally consistent - if ((msg = InjectContextToReadonlyOutput(pklOut)) != CERR_None) { - return msg; - } - } - - kp = new FILE_KEY[gp->cxKeyArray + 1]; - if (!kp) return CERR_CannotAllocateMemory; - if (gp->dpKeyArray) - { - memcpy(kp, gp->dpKeyArray, gp->cxKeyArray * sizeof(FILE_KEY)); - delete gp->dpKeyArray; - } - - gp->dpKeyArray = kp; - kp = &gp->dpKeyArray[gp->cxKeyArray]; - gp->cxKeyArray++; - - kp->dpOutput = new WCHAR[wcslen(pklOut) + 1]; - wcscpy_s(kp->dpOutput, wcslen(pklOut) + 1, pklOut); // I3481 - - kp->dpContext = new WCHAR[wcslen(pklIn) + 1]; - wcscpy_s(kp->dpContext, wcslen(pklIn) + 1, pklIn); // I3481 - - kp->Line = currentLine; - kp->LineStoreIndex = 0; - - // Finished if we are not using keys - - if (!gp->fUsingKeys) - { - kp->Key = 0; - kp->ShiftFlags = 0; - return CERR_None; - } - - // Expand each rule out into multiple rules - much faster processing at the key hit time - - if (*pklKey == 0) return CERR_ZeroLengthString; - - if (*pklKey == UC_SENTINEL) - switch (*(pklKey + 1)) - { - case CODE_ANY: - kp->ShiftFlags = 0; - if ((msg = ExpandKp(fk, kp, *(pklKey + 2) - 1)) != CERR_None) return msg; - break; - - case CODE_EXTENDED: - kp->Key = *(pklKey + 3); - kp->ShiftFlags = *(pklKey + 2); - break; - - default: - return CERR_InvalidCodeInKeyPartOfRule; - } - else - { - kp->ShiftFlags = 0; - kp->Key = *pklKey; - } - - } - __finally - { - delete pklIn; // I2432 - Allocate buffers each line -- slightly slower but safer than keeping a single buffer - delete pklKey; - delete pklOut; - } - - return CERR_None; -} - - - -DWORD ExpandKp_ReplaceIndex(PFILE_KEYBOARD fk, PFILE_KEY k, DWORD keyIndex, int nAnyIndex) -{ - /* Replace each index(xx,keyIndex) in k->dpOutput with appropriate char as based on nAnyIndex */ - PFILE_STORE s; - int i; - PWSTR pIndex, pStore; - - for (pIndex = k->dpOutput; *pIndex; pIndex = incxstr(pIndex)) - { - if (*pIndex == UC_SENTINEL && *(pIndex + 1) == CODE_INDEX && *(pIndex + 3) == keyIndex) - { - s = &fk->dpStoreArray[*(pIndex + 2) - 1]; - for (i = 0, pStore = s->dpString; i < nAnyIndex; i++, pStore = incxstr(pStore)); - PWSTR qStore = incxstr(pStore); - - int w = (int)(INT_PTR)(qStore - pStore); - if (w > 4) - { - *pIndex = UC_SENTINEL; - *(pIndex + 1) = CODE_BEEP; - memmove(pIndex + 2, pIndex + 4, wcslen(pIndex + 3) * 2); - } - else - { - memcpy(pIndex, pStore, w * 2); - if (w < 4) memmove(pIndex + w, pIndex + 4, wcslen(pIndex + 3) * 2); - } - } - } - - return CERR_None; -} - - -DWORD ExpandKp(PFILE_KEYBOARD fk, PFILE_KEY kpp, DWORD storeIndex) -{ - PFILE_KEY k; - PWSTR pn; - DWORD nchrs, n; - int keyIndex; - - PFILE_STORE sp = &fk->dpStoreArray[storeIndex]; - PFILE_GROUP gp = &fk->dpGroupArray[fk->currentGroup]; - - PWSTR dpContext = kpp->dpContext; - PWSTR dpOutput = kpp->dpOutput; - - nchrs = xstrlen(sp->dpString); - pn = sp->dpString; - keyIndex = xstrlen(dpContext) + 1; - - /* - Now we change them to plain characters in the output in multiple rules, - and set the keystroke to the appropriate character in the store. - */ - - k = new FILE_KEY[gp->cxKeyArray + nchrs - 1]; - if (!k) return CERR_CannotAllocateMemory; - memcpy(k, gp->dpKeyArray, gp->cxKeyArray * sizeof(FILE_KEY)); - - kpp = &k[(INT_PTR)(kpp - gp->dpKeyArray)]; - - delete gp->dpKeyArray; - gp->dpKeyArray = k; - gp->cxKeyArray += nchrs - 1; - - for (k = kpp, n = 0, pn = sp->dpString; *pn; pn = incxstr(pn), k++, n++) - { - k->dpContext = new WCHAR[wcslen(dpContext) + 1]; - k->dpOutput = new WCHAR[wcslen(dpOutput) + 1]; - - wcscpy_s(k->dpContext, wcslen(dpContext) + 1, dpContext); // copy the context. // I3481 - wcscpy_s(k->dpOutput, wcslen(dpOutput) + 1, dpOutput); // copy the output. - - if (*pn == UC_SENTINEL) - { - switch (*(pn + 1)) - { - case CODE_EXTENDED: - k->Key = *(pn + 3); // set the key to store offset. - k->ShiftFlags = *(pn + 2); - break; - default: - return CERR_CodeInvalidInKeyStore; - } - } - else - { - k->Key = *pn; // set the key to store offset. - k->ShiftFlags = 0; - } - k->Line = kpp->Line; - k->LineStoreIndex = (WORD)n; - ExpandKp_ReplaceIndex(fk, k, keyIndex, n); - } - - delete dpContext; - delete dpOutput; - - return CERR_None; -} - - -PWSTR GetDelimitedString(PWSTR *p, PWSTR Delimiters, WORD Flags) -{ - PWSTR q, r; - WCHAR dOpen, dClose; - - dOpen = *Delimiters; dClose = *(Delimiters + 1); - - q = *p; - while (iswspace(*q)) q++; //***QUERY - - if (*q != dOpen) return NULL; - - q++; - - r = wcschr(q, dClose); // Find closing delimiter - if (!r) return NULL; - - if (Flags & GDS_CUTLEAD) - while (iswspace(*q)) q++; // cut off leading spaces - - if (Flags & GDS_CUTFOLL) - if (!iswspace(*(r - 1))) *r = 0; - else - { - r--; // Cut off following spaces - while (iswspace(*r) && r > q) r--; - r++; - *r = 0; r = wcschr((r + 1), dClose); - } - else *r = 0; - - r++; while (iswspace(*r)) r++; // Ignore spaces after the close - if (*r == 0) r--; // Safety for terminating strings. - - *p = r; // Update pointer position - - return q; // Return delimited string -} - - -enum LinePrefixType { lptNone, lptKeymanAndKeymanWeb, lptKeymanWebOnly, lptKeymanOnly, lptOther }; - -LinePrefixType GetLinePrefixType(PWSTR *p) -{ - PWSTR s = *p; - - while (iswspace(*s)) s++; - - PWSTR q = s; - - if (*s != '$') return lptNone; - - /* I1569 - fix named constants at the start of the line */ - s++; - while (__iswcsym(*s)) s++; - if (*s != ':') return lptNone; - - if (_wcsnicmp(q, L"$keyman:", 8) == 0) - { - *p += 8; - return lptKeymanAndKeymanWeb; - } - if (_wcsnicmp(q, L"$keymanweb:", 11) == 0) - { - *p += 11; - return lptKeymanWebOnly; - } - if (_wcsnicmp(q, L"$keymanonly:", 12) == 0) - { - *p += 12; - return lptKeymanOnly; - } - - return lptOther; -} - -int LineTokenType(PWSTR *str) -{ - int i; - size_t l; - PWSTR p = *str; - - LinePrefixType lpt = GetLinePrefixType(&p); - if (lpt == lptOther) return T_BLANK; - - /* Test KeymanWeb, Keyman and KeymanOnly prefixes */ - if (CompileTarget == CKF_KEYMAN && lpt == lptKeymanWebOnly) return T_BLANK; - if (CompileTarget == CKF_KEYMANWEB && lpt == lptKeymanOnly) return T_BLANK; - - while (iswspace(*p)) p++; - - if (wcschr(LineTokens[0], towupper(*p))) - for (i = 0; i <= T_W_END - T_W_START; i++) - { - l = wcslen(LineTokens[i + 1]); - if (_wcsnicmp(p, LineTokens[i + 1], l) == 0) - { - p += l; while (iswspace(*p)) p++; *str = p; - return i + T_W_START; - } - } - - switch (towupper(*p)) - { - case 'C': - if (iswspace(*(p + 1))) return T_COMMENT; - break; - case 0: - return T_BLANK; - default: - if (wcschr(L"\"aAbBlLpPnN[OoxXdD0123456789\'+UuiI$", *p)) // I4784 - { - *str = p; - return T_KEYTOKEY; - } - } - return T_UNKNOWN; -} - -const PWSTR DeadKeyChars = -L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; - -BOOL strvalidchrs(PWSTR q, PWSTR chrs) -{ - for (; *q; q++) - if (!wcschr(chrs, *q)) return FALSE; - return TRUE; -} - -DWORD GetXString(PFILE_KEYBOARD fk, PWSTR str, PWSTR token, PWSTR output, int max, int offset, PWSTR *newp, int isVKey, int isUnicode) -{ - DWORD err; - PWSTR p = str, q, r; - int type, mx = 0, n, n1, n2, tokenFound = FALSE, z, sFlag = 0, j; - DWORD i; - BOOL finished = FALSE; - WCHAR c; - - PWCHAR tstr = NULL; - int tstrMax = 0; - - tstr = new WCHAR[max]; // I2432 - Allocate buffers each line -- slightly slower but safer than keeping a single buffer - GetXString is re-entrant with if() - tstrMax = max; - - __try - { - *tstr = 0; - - *output = 0; - - p = str; - do - { - tokenFound = FALSE; - while (iswspace(*p) && !wcschr(token, *p)) p++; - if (!*p) break; - - ErrChr = (int)(INT_PTR)(p - str) + offset + 1; - - /* - char *tokenTypes[] = { - "clearcontext", "deadkey", "context", "return", "switch", - "index", "outs", "beep", "nul", "use", "any", "fix", "dk", "k_", "x", "d", "c", - "[", "]" }; - */ - - switch (towupper(*p)) - { - case 'X': - case 'D': type = 0; break; // xFF, d130: chars, deadkey(n) - case '\"': type = 1; break; // "xxxx": chars - case '\'': type = 2; break; // 'xxxx': chars - case 'A': type = 3; break; // any(s) - case 'B': type = 4; break; // beep, baselayout (synonym for if(&baselayout)) // I3430 - case 'I': type = 5; break; // index(s,n), if - case 'O': type = 6; break; // outs(s) - case 'C': type = 7; break; // context, comments, clearcontext, call(s) - case 'N': type = 8; break; // nul, notany - case 'U': type = 9; break; // use(g) - case 'R': type = 10; break; // return, reset - case '[': type = 11; break; // start of vkey section - //case ']': type = 12; break; // end of vkey section - //case 'K': type = 13; break; // virtual key name or "key" - case 'S': type = 14; break; // switch, set, save - case 'F': type = 15; break; // fix (synonym for clearcontext) - case '$': type = 16; break; // named code constants - case 'P': type = 17; break; // platform (synonym for if(&platform)) // I3430 - case 'L': type = 18; break; // layer (synonym for set(&layer)) // I3437 - case '.': type = 19; break; // .. allows us to specify ranges -- either vkeys or characters - default: - if (iswdigit(*p)) type = 0; // octal number - else type = 99; // error! - } - if (wcschr(token, *p)) tokenFound = TRUE; - - switch (type) - { - case 99: - if (tokenFound) break; - wsprintf(ErrExtra, "token: %c", (int)*p); - return CERR_InvalidToken; - case 0: - if (_wcsnicmp(p, L"deadkey", z = 7) == 0 || - _wcsnicmp(p, L"dk", z = 2) == 0) - { - p += z; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidDeadkey; - - DWORD n = fk->cxDeadKeyArray; - - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_DEADKEY; - if (!strvalidchrs(q, DeadKeyChars)) return CERR_InvalidDeadkey; - tstr[mx++] = GetDeadKey(fk, q); //atoiW(q); 7-5-01: named deadkeys - tstr[mx] = 0; - } - else - { - n = xatoi(&p); - if (*p != '\0' && !iswspace(*p)) return CERR_InvalidValue; - if ((err = UTF32ToUTF16(n, &n1, &n2)) != CERR_None) return err; - tstr[mx++] = n1; - if (n2 >= 0) tstr[mx++] = n2; - tstr[mx] = 0; - } - continue; - case 1: - q = wcschr(p + 1, '\"'); - if (!q) return CERR_UnterminatedString; - if ((INT_PTR)(q - p) - 1 + mx > max) return CERR_UnterminatedString; - if (sFlag) return CERR_StringInVirtualKeySection; - wcsncat_s(tstr, max, p + 1, (INT_PTR)(q - p) - 1); // I3481 - mx += (int)(INT_PTR)(q - p) - 1; - tstr[mx] = 0; - p = q + 1; - continue; - case 2: - q = wcschr(p + 1, '\''); - if (!q) return CERR_UnterminatedString; - if ((INT_PTR)(q - p) - 1 + mx > max) return CERR_UnterminatedString; - if (sFlag) return CERR_StringInVirtualKeySection; - wcsncat_s(tstr, max, p + 1, (INT_PTR)(q - p) - 1); // I3481 - mx += (int)(INT_PTR)(q - p) - 1; - tstr[mx] = 0; - p = q + 1; - continue; - case 3: - if (_wcsnicmp(p, L"any", 3) != 0) return CERR_InvalidToken; - if (sFlag) return CERR_AnyInVirtualKeySection; - p += 3; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidAny; - - for (i = 0; i < fk->cxStoreArray; i++) - { - if (_wcsicmp(q, fk->dpStoreArray[i].szName) == 0) break; - } - if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; - - if (!*fk->dpStoreArray[i].dpString) return CERR_ZeroLengthString; - CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); - - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_ANY; - tstr[mx++] = (WCHAR)i + 1; // store to index + 1, avoids End-of-string - tstr[mx] = 0; - continue; - case 4: - if (_wcsnicmp(p, L"beep", 4) == 0) - { - if (sFlag) return CERR_BeepInVirtualKeySection; - p += 4; - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_BEEP; - tstr[mx] = 0; - } - else if (_wcsnicmp(p, L"baselayout", 10) == 0) // I3430 - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnly_IfSystemStores); - if (sFlag) return CERR_InvalidInVirtualKeySection; - p += 10; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidToken; - err = process_baselayout(fk, q, tstr, &mx); - if (err != CERR_None) return err; - } - else - return CERR_InvalidToken; - - continue; - case 5: - if (_wcsnicmp(p, L"if", 2) == 0) - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly); - if (sFlag) return CERR_InvalidInVirtualKeySection; - p += 2; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidIf; - - err = process_if(fk, q, tstr, &mx); - if (err != CERR_None) return err; - } - else - { - if (_wcsnicmp(p, L"index", 5) != 0) return CERR_InvalidToken; - if (sFlag) return CERR_IndexInVirtualKeySection; - p += 5; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - - if (!q || !*q) return CERR_InvalidIndex; - - { - wchar_t *context = NULL; - r = wcstok_s(q, L" ,", &context); // I3481 - if (!r) return CERR_InvalidIndex; - - for (i = 0; i < fk->cxStoreArray; i++) - { - if (_wcsicmp(r, fk->dpStoreArray[i].szName) == 0) break; - } - if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; - - CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); - - r = wcstok_s(NULL, L" ,", &context); // I3481 - if (!r) return CERR_InvalidIndex; - } - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_INDEX; - tstr[mx++] = (WCHAR)i + 1; // avoid EOS for stores - tstr[mx++] = atoiW(r); // character offset of original any. - - tstr[mx] = 0; - } - continue; - case 6: - if (_wcsnicmp(p, L"outs", 4) != 0) return CERR_InvalidToken; - if (sFlag) return CERR_OutsInVirtualKeySection; - p += 4; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidOuts; - - for (i = 0; i < fk->cxStoreArray; i++) - { - if (_wcsicmp(q, fk->dpStoreArray[i].szName) == 0) break; - } - if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; - - CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); - - for (q = fk->dpStoreArray[i].dpString; *q; q++) - { - tstr[mx++] = *q; - if (mx >= max - 1) return CERR_BufferOverflow; - } - tstr[mx] = 0; - continue; - case 7: - if (iswspace(*(p + 1))) break; // is a comment -- pre-stripped - so why this test? - if (_wcsnicmp(p, L"context", 7) == 0) - { - if (sFlag) return CERR_ContextInVirtualKeySection; - p += 7; - - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (q && *q) - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_Contextn); - int n1; - n1 = atoiW(q); - if (n1 < 1 || n1 >= 0xF000) return CERR_InvalidToken; - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_CONTEXTEX; - tstr[mx++] = n1; - tstr[mx] = 0; - } - else - { - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_CONTEXT; - tstr[mx] = 0; - } - } - else if (_wcsnicmp(p, L"clearcontext", 12) == 0) - { - p += 12; - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_CLEARCONTEXT; - tstr[mx] = 0; - } - else if (_wcsnicmp(p, L"call", 4) == 0) - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_501, CERR_501FeatureOnly_Call); - if (sFlag) return CERR_CallInVirtualKeySection; - p += 4; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidCall; - - for (i = 0; i < fk->cxStoreArray; i++) - { - if (_wcsicmp(q, fk->dpStoreArray[i].szName) == 0) break; - } - - if (!IsValidCallStore(&fk->dpStoreArray[i])) return CERR_InvalidCall; - CheckStoreUsage(fk, i, FALSE, FALSE, TRUE); - - if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_CALL; - tstr[mx++] = (WCHAR)i + 1; - tstr[mx] = 0; - - fk->dpStoreArray[i].dwSystemID = TSS_CALLDEFINITION; - } - else - return CERR_InvalidToken; - continue; - case 8: - if (_wcsnicmp(p, L"notany", 6) == 0) - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly) - if (sFlag) return CERR_AnyInVirtualKeySection; - p += 6; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidAny; - - for (i = 0; i < fk->cxStoreArray; i++) - { - if (_wcsicmp(q, fk->dpStoreArray[i].szName) == 0) break; - } - if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; - CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_NOTANY; - tstr[mx++] = (WCHAR)i + 1; // store to index + 1, avoids End-of-string - tstr[mx] = 0; - continue; - } - if (_wcsnicmp(p, L"nul", 3) != 0) return CERR_InvalidToken; - - p += 3; - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_NUL; - tstr[mx] = 0; - continue; - case 9: - if (_wcsnicmp(p, L"use", 3) != 0) - { - if (*(p + 1) == '+') - { - n = xatoi(&p); - if (*p != '\0' && !iswspace(*p)) return CERR_InvalidValue; - if ((err = UTF32ToUTF16(n, &n1, &n2)) != CERR_None) return err; - tstr[mx++] = n1; - if (n2 >= 0) tstr[mx++] = n2; - tstr[mx] = 0; - if (!isUnicode) AddWarning(CWARN_UnicodeInANSIGroup); - continue; - } - return CERR_InvalidToken; - } - p += 3; - - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidUse; - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_USE; - tstr[mx] = GetGroupNum(fk, q); - if (tstr[mx] == 0) return CERR_GroupDoesNotExist; - tstr[++mx] = 0; - continue; - case 10: - if (_wcsnicmp(p, L"reset", 5) == 0) - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly); - if (sFlag) return CERR_InvalidInVirtualKeySection; - p += 5; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidReset; - - err = process_reset(fk, q, tstr, &mx); - if (err != CERR_None) return err; - } - else - { - if (_wcsnicmp(p, L"return", 6) != 0) return CERR_InvalidToken; - - p += 6; - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_RETURN; - tstr[mx] = 0; - wcsncpy_s(output, max, tstr, max); // I3481 - output[max - 1] = 0; - return 0; - } - continue; - case 11: - p++; sFlag = ISVIRTUALKEY /* 0 */; finished = FALSE; - - //printf("--EXTENDEDSTRING--\n"); - - do - { - while (iswspace(*p)) p++; - - switch (towupper(*p)) - { - case 'N': - if (_wcsnicmp(p, L"NCAPS", 5) == 0) - sFlag |= NOTCAPITALFLAG, p += 5; - else finished = TRUE; - break; - case 'L': - if (_wcsnicmp(p, L"LALT", 4) == 0) - sFlag |= LALTFLAG, p += 4; - else if (_wcsnicmp(p, L"LCTRL", 5) == 0) - sFlag |= LCTRLFLAG, p += 5; - else finished = TRUE; - break; - case 'R': - if (_wcsnicmp(p, L"RALT", 4) == 0) - sFlag |= RALTFLAG, p += 4; - else if (_wcsnicmp(p, L"RCTRL", 5) == 0) - sFlag |= RCTRLFLAG, p += 5; - else finished = TRUE; - break; - case 'A': - if (_wcsnicmp(p, L"ALT", 3) == 0) - sFlag |= K_ALTFLAG, p += 3; - else finished = TRUE; - break; - case 'C': - if (_wcsnicmp(p, L"CTRL", 4) == 0) - sFlag |= K_CTRLFLAG, p += 4; - else if (_wcsnicmp(p, L"CAPS", 4) == 0) - sFlag |= CAPITALFLAG, p += 4; - else finished = TRUE; - break; - case 'S': - if (_wcsnicmp(p, L"SHIFT", 5) == 0) - sFlag |= K_SHIFTFLAG, p += 5; - else finished = TRUE; - break; - default: - finished = TRUE; - break; - } - } while (!finished); - - if ((sFlag & (LCTRLFLAG | LALTFLAG)) && (sFlag & (RCTRLFLAG | RALTFLAG))) { - AddWarning(CWARN_MixingLeftAndRightModifiers); - } - - // If we use chiral modifiers, or we use state keys, and we target web in the keyboard, and we don't manually specify a keyboard version, bump the minimum - // version to 10.0. This makes an assumption that if we are using these features in a keyboard and it has no version specified, that we want to use the features - // in the web target platform, even if there are platform() rules excluding this possibility. In that (rare) situation, the keyboard developer should simply specify - // the &version to be 9.0 or whatever to avoid this behaviour. - if (sFlag & (LCTRLFLAG | LALTFLAG | RCTRLFLAG | RALTFLAG | CAPITALFLAG | NOTCAPITALFLAG | NUMLOCKFLAG | NOTNUMLOCKFLAG | SCROLLFLAG | NOTSCROLLFLAG) && - CompileTarget == CKF_KEYMANWEB && - fk->dwFlags & KF_AUTOMATICVERSION) { - VERIFY_KEYBOARD_VERSION(fk, VERSION_100, 0); - } - //printf("sFlag: %x\n", sFlag); - - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_EXTENDED; - tstr[mx++] = sFlag; - - while (iswspace(*p)) p++; - - q = p; - - if (*q == ']') - { - return CERR_InvalidToken; // I3137 - key portion of VK is missing e.g. "[CTRL ALT]", this generates invalid kmx file that can crash Keyman or compiler later on // I3511 - } - - while (*q != ']') - { - if (*q == '\'' || *q == '"') - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_VirtualCharKey); - if (!FMnemonicLayout) AddWarning(CWARN_VirtualCharKeyWithPositionalLayout); - WCHAR chQuote = *q; - q++; if (*q == chQuote || *q == '\n' || *q == 0) return CERR_InvalidToken; - tstr[mx - 1] |= VIRTUALCHARKEY; - tstr[mx++] = *q; - q++; if (*q != chQuote) return CERR_InvalidToken; - q++; - while (iswspace(*q)) q++; - if (*q != ']') return CERR_InvalidToken; - break; /* out of while loop */ - } - - for (j = 0; !iswspace(*q) && *q != ']' && *q != 0; q++, j++); - - if (*q == 0) return CERR_InvalidToken; - - WCHAR vkname[SZMAX_VKDICTIONARYNAME]; // I3438 - - if (j >= SZMAX_VKDICTIONARYNAME) return CERR_InvalidToken; - - wcsncpy_s(vkname, _countof(vkname), p, j); // I3481 - vkname[j] = 0; - - if (_wcsicmp(vkname, L"K_NPENTER") == 0) - i = 5; // I649 - K_NPENTER hack - else - { - for (i = 0; i <= VK__MAX; i++) - { - if (_wcsicmp(vkname, VKeyNames[i]) == 0 || _wcsicmp(vkname, VKeyISO9995Names[i]) == 0) - break; - } - } - - if (i == VK__MAX + 1) - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_InvalidToken); - - i = GetVKCode(fk, vkname); // I3438 - if (i == 0) - return CERR_InvalidToken; - } - - p = q; - - tstr[mx++] = (int)i; - - if (FMnemonicLayout && (i <= VK__MAX) && VKeyMayBeVCKey[i]) AddWarning(CWARN_VirtualKeyWithMnemonicLayout); // I3438 - - while (iswspace(*q)) q++; - } - tstr[mx++] = UC_SENTINEL_EXTENDEDEND; - tstr[mx] = 0; - //printf("--EXTENDEDEND--\n"); - - p = q + 1; - - sFlag = 0; - - continue; - case 14: - if (_wcsnicmp(p, L"set", 3) == 0) - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly); - p += 3; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidSet; - - err = process_set(fk, q, tstr, &mx); - if (err != CERR_None) return err; - } - else if (_wcsnicmp(p, L"save", 4) == 0) - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly); - p += 4; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidSave; - - err = process_save(fk, q, tstr, &mx); - if (err != CERR_None) return err; - } - else - { - if (_wcsnicmp(p, L"switch", 6) != 0) return CERR_InvalidToken; - p += 6; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidSwitch; - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_SWITCH; - tstr[mx++] = atoiW(q); - tstr[mx] = 0; - } - continue; - case 15: - if (_wcsnicmp(p, L"fix", 3) == 0) - { - p += 3; - tstr[mx++] = UC_SENTINEL; - tstr[mx++] = CODE_CLEARCONTEXT; - tstr[mx] = 0; - } - else - return CERR_InvalidToken; - continue; - case 16: - VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes); - q = p + 1; - while (*q && !iswspace(*q)) q++; - c = *q; *q = 0; - n = CodeConstants->GetCode(p + 1, &i); - *q = c; - if (n == 0) return CERR_InvalidNamedCode; - if (i < 0xFFFFFFFFL) CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); // I2993 - if (n > 0xFFFF) - { - tstr[mx++] = Uni_UTF32ToSurrogate1(n); - tstr[mx++] = Uni_UTF32ToSurrogate2(n); - } - else - tstr[mx++] = n; - tstr[mx] = 0; - p = q; - continue; - case 17: - if (_wcsnicmp(p, L"platform", 8) != 0) return CERR_InvalidToken; // I3430 - VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnly_IfSystemStores); - if (sFlag) return CERR_InvalidInVirtualKeySection; - p += 8; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidToken; - err = process_platform(fk, q, tstr, &mx); - if (err != CERR_None) return err; - continue; - case 18: // I3437 - if (_wcsnicmp(p, L"layer", 5) != 0) return CERR_InvalidToken; - VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnly_SetSystemStores); - if (sFlag) return CERR_InvalidInVirtualKeySection; - p += 5; - q = GetDelimitedString(&p, L"()", GDS_CUTLEAD | GDS_CUTFOLL); - if (!q || !*q) return CERR_InvalidToken; - err = process_set_synonym(TSS_LAYER, fk, q, tstr, &mx); - if (err != CERR_None) return err; - continue; - case 19: // #2241 - if (*(p + 1) != '.') return CERR_InvalidToken; - if (sFlag) return CERR_InvalidInVirtualKeySection; - p += 2; - err = process_expansion(fk, p, tstr, &mx, max); - if (err != CERR_None) return err; - continue; - default: - return CERR_InvalidToken; - } - if (tokenFound) - { - *newp = p; - wcsncpy_s(output, max, tstr, max); // I3481 - output[max - 1] = 0; - ErrChr = 0; - return CERR_None; - } - if (mx >= max) return CERR_BufferOverflow; - } while (*p); - - if (!*token) - { - *newp = p; - wcsncpy_s(output, max, tstr, max); // I3481 - output[max - 1] = 0; - ErrChr = 0; - return CERR_None; - } - - } - __finally - { - delete[] tstr; // I2432 - Allocate buffers each line -- slightly slower but safer than keeping a single buffer - GetXString is re-entrant with if() - } - - return CERR_NoTokensFound; -} - -DWORD process_if_synonym(DWORD dwSystemID, PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx); // I3430 - -DWORD process_baselayout(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx) // I3430 -{ - /* baselayout() */ - return process_if_synonym(TSS_BASELAYOUT, fk, q, tstr, mx); -} - -DWORD process_platform(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx) // I3430 -{ - /* platform() */ - return process_if_synonym(TSS_PLATFORM, fk, q, tstr, mx); -} - -DWORD process_if_synonym(DWORD dwSystemID, PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx) // I3430 -{ - PWCHAR temp = new WCHAR[GLOBAL_BUFSIZE]; - - DWORD msg; - - PWSTR r; - - if ((msg = GetXString(fk, q, L"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None) - { - delete temp; - return msg; - } - - DWORD dwStoreID; - - if ((msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID)) != CERR_None) - { - delete temp; - return msg; - } - - tstr[(*mx)++] = UC_SENTINEL; - tstr[(*mx)++] = (WCHAR)CODE_IFSYSTEMSTORE; - tstr[(*mx)++] = (WCHAR)(dwSystemID + 1); // I4785 - tstr[(*mx)++] = 2; - tstr[(*mx)++] = (WCHAR)(dwStoreID + 1); - tstr[(*mx)] = 0; - - delete[] temp; - - return CERR_None; -} - -DWORD process_if(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx) // I3431 -{ - /* if( <'='|'!='> ) */ - DWORD i, code, not = FALSE; - LPWSTR r = q, s = q; - while (*s && *s != L' ' && *s != L'!' && *s != L'=') s++; - r = s; - while (*s == L' ') s++; - if (*s == L'!') - { - s++; - not = TRUE; - } - - if (*s != '=') return CERR_InvalidIf; - s++; - while (*s == ' ') s++; - *r = 0; - r = q; - - if (r[0] == '&') - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnly_IfSystemStores); - for (i = 0; StoreTokens[i]; i++) - { - if (_wcsicmp(r, StoreTokens[i]) == 0) break; - } - if (!StoreTokens[i]) return CERR_IfSystemStore_NotFound; - code = CODE_IFSYSTEMSTORE; - } - else - { - code = CODE_IFOPT; - - for (i = 0; i < fk->cxStoreArray; i++) - { - if (_wcsicmp(r, fk->dpStoreArray[i].szName) == 0) break; - } - if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; - CheckStoreUsage(fk, i, FALSE, TRUE, FALSE); - } - - PWCHAR temp = new WCHAR[GLOBAL_BUFSIZE]; - - DWORD msg; - - if ((msg = GetXString(fk, s, L"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None) - { - delete[] temp; - return msg; - } - - DWORD dwStoreID; - - if ((msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID)) != CERR_None) - { - delete[] temp; - return msg; - } - - tstr[(*mx)++] = UC_SENTINEL; - tstr[(*mx)++] = (WCHAR)code; - tstr[(*mx)++] = (WCHAR)(i + 1); - tstr[(*mx)++] = not ? 1 : 2; - tstr[(*mx)++] = (WCHAR)(dwStoreID + 1); - tstr[(*mx)] = 0; - - return CERR_None; -} - -DWORD process_reset(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx) -{ - /* reset() */ - DWORD i; - for (i = 0; i < fk->cxStoreArray; i++) - { - if (_wcsicmp(q, fk->dpStoreArray[i].szName) == 0) break; - } - if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; - CheckStoreUsage(fk, i, FALSE, TRUE, FALSE); - - tstr[(*mx)++] = UC_SENTINEL; - tstr[(*mx)++] = CODE_RESETOPT; - tstr[(*mx)++] = (WCHAR)(i + 1); - tstr[(*mx)] = 0; - - return CERR_None; -} - -DWORD process_expansion(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx, int max) { - BOOL isVKey = FALSE; - - WORD BaseKey=0, BaseShiftFlags=0; - DWORD BaseChar=0; - - if (*mx == 0) { - return CERR_ExpansionMustFollowCharacterOrVKey; - } - LPWSTR p = &tstr[*mx]; - p = decxstr(p, tstr); - if (*p == UC_SENTINEL) { - if (*(p + 1) != CODE_EXTENDED) { - return CERR_ExpansionMustFollowCharacterOrVKey; - } - isVKey = TRUE; - BaseKey = *(p + 3); - BaseShiftFlags = *(p + 2); - } - else { - BaseChar = Uni_UTF16ToUTF32(p); - } - - // Look ahead at next element - WCHAR temp[GLOBAL_BUFSIZE]; - PWCHAR r = NULL; - - DWORD msg; - - if ((msg = GetXString(fk, q, L"", temp, _countof(temp) - 1, 0, &r, FALSE, TRUE)) != CERR_None) - { - return msg; - } - - WORD HighKey, HighShiftFlags; - DWORD HighChar; - - switch(temp[0]) { - case 0: - return isVKey ? CERR_VKeyExpansionMustBeFollowedByVKey : CERR_CharacterExpansionMustBeFollowedByCharacter; - case UC_SENTINEL: - // Verify that range is valid virtual key range - if(!isVKey) { - return CERR_CharacterExpansionMustBeFollowedByCharacter; - } - if (temp[1] != CODE_EXTENDED) { - return CERR_VKeyExpansionMustBeFollowedByVKey; - } - HighKey = temp[3], HighShiftFlags = temp[2]; - if (HighShiftFlags != BaseShiftFlags) { - return CERR_VKeyExpansionMustUseConsistentShift; - } - if (HighKey <= BaseKey) { - return CERR_ExpansionMustBePositive; - } - // Verify space in buffer - if (*mx + (HighKey - BaseKey) * 5 + 1 >= max) return CERR_BufferOverflow; - // Inject an expansion. - for (BaseKey++; BaseKey < HighKey; BaseKey++) { - // < HighKey because caller will add HighKey to output - tstr[(*mx)++] = UC_SENTINEL; - tstr[(*mx)++] = CODE_EXTENDED; - tstr[(*mx)++] = BaseShiftFlags; - tstr[(*mx)++] = BaseKey; - tstr[(*mx)++] = UC_SENTINEL_EXTENDEDEND; - } - tstr[*mx] = 0; - break; - default: - // Verify that range is a valid character range - if (isVKey) { - return CERR_VKeyExpansionMustBeFollowedByVKey; - } - - HighChar = Uni_UTF16ToUTF32(temp); - if (HighChar <= BaseChar) { - return CERR_ExpansionMustBePositive; - } - // Inject an expansion. - for (BaseChar++; BaseChar < HighChar; BaseChar++) { - // < HighChar because caller will add HighChar to output - if (Uni_IsSMP(BaseChar)) { - // We'll test on each char to avoid complex calculations crossing SMP boundary - if (*mx + 3 >= max) return CERR_BufferOverflow; - tstr[(*mx)++] = (WCHAR) Uni_UTF32ToSurrogate1(BaseChar); - tstr[(*mx)++] = (WCHAR) Uni_UTF32ToSurrogate2(BaseChar); - } - else { - if (*mx + 2 >= max) return CERR_BufferOverflow; - tstr[(*mx)++] = (WCHAR) BaseChar; - } - } - tstr[*mx] = 0; - } - - return CERR_None; -} - -DWORD process_set_synonym(DWORD dwSystemID, PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx) // I3437 -{ - /* set( <'='> ), layer */ - DWORD code = CODE_SETSYSTEMSTORE; - PWCHAR temp = new WCHAR[GLOBAL_BUFSIZE], r; - DWORD msg; - - if ((msg = GetXString(fk, q, L"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None) - { - delete[] temp; - return msg; - } - - DWORD dwStoreID; - - msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID); - delete[] temp; - if (msg != CERR_None) return msg; - - tstr[(*mx)++] = UC_SENTINEL; - tstr[(*mx)++] = (WCHAR)CODE_SETSYSTEMSTORE; - tstr[(*mx)++] = (WCHAR)(dwSystemID + 1); - tstr[(*mx)++] = (WCHAR)(dwStoreID + 1); - tstr[(*mx)] = 0; - return CERR_None; -} - -DWORD process_set(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx) -{ - /* set( <'='> */ - LPWSTR r = q, s = q; // I3440 - while (*s && *s != L' ' && *s != L'=') s++; - r = s; - while (*s == L' ') s++; - if (*s != '=') return CERR_InvalidSet; - s++; - while (*s == ' ') s++; - *r = 0; - r = q; - - DWORD i, code; - - if (r[0] == '&') - { - VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnly_SetSystemStores); // I3437 - for (i = 0; StoreTokens[i]; i++) - { - if (_wcsicmp(r, StoreTokens[i]) == 0) break; - } - if (!StoreTokens[i]) return CERR_SetSystemStore_NotFound; - code = CODE_SETSYSTEMSTORE; - } - else - { - wchar_t *context = NULL; - LPWSTR r = wcstok_s(q, L" =", &context); // I3481 - - for (i = 0; i < fk->cxStoreArray; i++) - { - if (_wcsicmp(r, fk->dpStoreArray[i].szName) == 0) break; - } - if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; - CheckStoreUsage(fk, i, FALSE, TRUE, FALSE); - code = CODE_SETOPT; - } - - PWCHAR temp = new WCHAR[GLOBAL_BUFSIZE]; - - DWORD msg; - - //r = wcstok(NULL, L" ="); - - if ((msg = GetXString(fk, s, L"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None) - { - delete[] temp; - return msg; - } - - DWORD dwStoreID; - - msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID); - delete[] temp; - if (msg != CERR_None) return msg; - - tstr[(*mx)++] = UC_SENTINEL; - tstr[(*mx)++] = (WCHAR)code; - tstr[(*mx)++] = (WCHAR)(i + 1); - tstr[(*mx)++] = (WCHAR)(dwStoreID + 1); - tstr[(*mx)] = 0; - return CERR_None; -} - -DWORD process_save(PFILE_KEYBOARD fk, LPWSTR q, LPWSTR tstr, int *mx) -{ - /* save() */ - DWORD i; - for (i = 0; i < fk->cxStoreArray; i++) - { - if (_wcsicmp(q, fk->dpStoreArray[i].szName) == 0) break; - } - if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist; - CheckStoreUsage(fk, i, FALSE, TRUE, FALSE); - - tstr[(*mx)++] = UC_SENTINEL; - tstr[(*mx)++] = CODE_SAVEOPT; - tstr[(*mx)++] = (WCHAR)(i + 1); - tstr[(*mx)] = 0; - return CERR_None; -} - -int xatoi(PWSTR *p) -{ - PWSTR endptr; - int n; - - switch (towupper(**p)) - { - case 'U': - (*p)++; - if (**p != '+') return 0; - (*p)++; - n = (int)wcstol(*p, &endptr, 16); - *p = endptr; - break; - case 'X': - (*p)++; - n = (int)wcstol(*p, &endptr, 16); - *p = endptr; - break; - case 'D': - (*p)++; - n = (int)wcstol(*p, &endptr, 10); - *p = endptr; - break; - default: - n = (int)wcstol(*p, &endptr, 8); - *p = endptr; - break; - } - return n; -} - -int GetGroupNum(PFILE_KEYBOARD fk, PWSTR p) -{ - PFILE_GROUP gp; - DWORD i; - - for (i = 0, gp = fk->dpGroupArray; i < fk->cxGroupArray; gp++, i++) - { - if (_wcsicmp(gp->szName, p) == 0) return i + 1; - } - return 0; -} - - -DWORD ProcessEthnologueStore(PWSTR p) // I2646 -{ - DWORD res = CERR_None; - PWSTR q = NULL; - while (*p) - { - while (wcschr(L" ,;", *p)) - { - if (*p != ' ') res = CWARN_PunctuationInEthnologueCode; - p++; - } - if (q == p) return CERR_InvalidEthnologueCode; - if (*p) - { - for (int i = 0; i < 3; i++) - { - if (!iswalpha(*p)) return CERR_InvalidEthnologueCode; - p++; - } - } - q = p; - } - return res; -} - -#define K_HOTKEYSHIFTFLAGS (K_SHIFTFLAG | K_CTRLFLAG | K_ALTFLAG | ISVIRTUALKEY) - -DWORD ProcessHotKey(PWSTR p, DWORD *hk) -{ - PWSTR q, r; - DWORD sFlag; - int j, i; - - *hk = 0; - - if (*p == UC_SENTINEL && *(p + 1) == CODE_EXTENDED) { - WORD Key = *(p + 3); - WORD ShiftFlags = *(p + 2); - - // Convert virtual key to hotkey (different bitflags) - - if (ShiftFlags & ~K_HOTKEYSHIFTFLAGS) { - AddWarning(CWARN_HotkeyHasInvalidModifier); - } - - if (ShiftFlags & K_SHIFTFLAG) *hk |= HK_SHIFT; - if (ShiftFlags & K_CTRLFLAG) *hk |= HK_CTRL; - if (ShiftFlags & K_ALTFLAG) *hk |= HK_ALT; - - *hk |= Key; - - return CERR_None; - } - - q = wcschr(p, '['); - if (q) - { - q++; - sFlag = 0; - - do - { - while (iswspace(*q)) q++; - - if (_wcsnicmp(q, L"ALT", 3) == 0) sFlag |= HK_ALT, q += 3; - else if (_wcsnicmp(q, L"CTRL", 4) == 0) sFlag |= HK_CTRL, q += 4; - else if (_wcsnicmp(q, L"SHIFT", 5) == 0) sFlag |= HK_SHIFT, q += 5; - else if (towupper(*q) != 'K') return CERR_InvalidToken; - } while (towupper(*q) != 'K'); - - r = wcschr(q, ']'); - if (r) - { - r--; - while (iswspace(*r) && r > q) r--; - r++; - } - else return CERR_NoTokensFound; - - j = (int)(INT_PTR)(r - q); - - for (i = 0; i <= VK__MAX; i++) // I3438 - if (j == (int)wcslen(VKeyNames[i]) && _wcsnicmp(q, VKeyNames[i], j) == 0) break; - - if (i == VK__MAX + 1) return CERR_InvalidToken; // I3438 - - *hk = i | sFlag; - - return CERR_None; - } - - q = GetDelimitedString(&p, L"\"\"", GDS_CUTLEAD | GDS_CUTFOLL); - if (q) - { - if (wcschr(q, '^')) *hk |= HK_CTRL; - if (wcschr(q, '+')) *hk |= HK_SHIFT; - if (wcschr(q, '%')) *hk |= HK_ALT; - q = wcschr(q, 0) - 1; - *hk |= *q; - return CERR_None; - } - - return CERR_CodeInvalidInThisSection; -} - - -void SetChecksum(LPBYTE buf, LPDWORD CheckSum, DWORD sz) -{ - BuildCRCTable(); - *CheckSum = CalculateBufferCRC(buf, sz); -} - - -BOOL CheckStoreUsage(PFILE_KEYBOARD fk, int storeIndex, BOOL fIsStore, BOOL fIsOption, BOOL fIsCall) -{ - PFILE_STORE sp = &fk->dpStoreArray[storeIndex]; - if (fIsStore && !sp->fIsStore) - { - if (sp->fIsDebug || sp->fIsOption || sp->fIsReserved || sp->fIsCall) - AddWarning(CWARN_StoreAlreadyUsedAsOptionOrCall); - sp->fIsStore = TRUE; - } - else if (fIsOption && !sp->fIsOption) - { - if (sp->fIsDebug || sp->fIsStore || sp->fIsReserved || sp->fIsCall) - AddWarning(CWARN_StoreAlreadyUsedAsStoreOrCall); - sp->fIsOption = TRUE; - } - else if (fIsCall && !sp->fIsCall) - { - if (sp->fIsDebug || sp->fIsStore || sp->fIsReserved || sp->fIsOption) - AddWarning(CWARN_StoreAlreadyUsedAsStoreOrOption); - sp->fIsCall = TRUE; - } - - return TRUE; -} - -DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, HANDLE hOutfile) -{ - PFILE_GROUP fgp; - PFILE_STORE fsp; - PFILE_KEY fkp; - - PCOMP_KEYBOARD ck; - PCOMP_GROUP gp; - PCOMP_STORE sp; - PCOMP_KEY kp; - PBYTE buf; - size_t offset; - size_t size; - DWORD i, j; - - // Calculate how much memory to allocate - - size = sizeof(COMP_KEYBOARD) + - fk->cxGroupArray * sizeof(COMP_GROUP) + - fk->cxStoreArray * sizeof(COMP_STORE) + - /*wcslen(fk->szName)*2 + 2 + - wcslen(fk->szCopyright)*2 + 2 + - wcslen(fk->szLanguageName)*2 + 2 + - wcslen(fk->szMessage)*2 + 2 +*/ - fk->dwBitmapSize; - - for (i = 0, fgp = fk->dpGroupArray; i < fk->cxGroupArray; i++, fgp++) - { - if (FSaveDebug) size += wcslen(fgp->szName) * 2 + 2; - size += fgp->cxKeyArray * sizeof(COMP_KEY); - for (j = 0, fkp = fgp->dpKeyArray; j < fgp->cxKeyArray; j++, fkp++) - { - size += wcslen(fkp->dpOutput) * 2 + 2; - size += wcslen(fkp->dpContext) * 2 + 2; - } - - if (fgp->dpMatch) size += wcslen(fgp->dpMatch) * 2 + 2; - if (fgp->dpNoMatch) size += wcslen(fgp->dpNoMatch) * 2 + 2; - } - - for (i = 0; i < fk->cxStoreArray; i++) - { - size += wcslen(fk->dpStoreArray[i].dpString) * 2 + 2; - if (FSaveDebug || fk->dpStoreArray[i].fIsOption) size += wcslen(fk->dpStoreArray[i].szName) * 2 + 2; - } - - buf = new BYTE[size]; - if (!buf) return CERR_CannotAllocateMemory; - memset(buf, 0, size); - - ck = (PCOMP_KEYBOARD)buf; - - ck->dwIdentifier = FILEID_COMPILED; - ck->dwFileVersion = fk->version; - ck->dwCheckSum = 0; // do checksum afterwards. - ck->KeyboardID = fk->KeyboardID; - ck->IsRegistered = TRUE; // I5135 - ck->cxStoreArray = fk->cxStoreArray; - ck->cxGroupArray = fk->cxGroupArray; - ck->StartGroup[0] = fk->StartGroup[0]; - ck->StartGroup[1] = fk->StartGroup[1]; - ck->dwHotKey = fk->dwHotKey; - - ck->dwFlags = fk->dwFlags; - - offset = sizeof(COMP_KEYBOARD); - - /*ck->dpLanguageName = offset; - wcscpy((PWSTR)(buf + offset), fk->szLanguageName); - offset += wcslen(fk->szLanguageName)*2 + 2; - - ck->dpName = offset; - wcscpy((PWSTR)(buf + offset), fk->szName); - offset += wcslen(fk->szName)*2 + 2; - - ck->dpCopyright = offset; - wcscpy((PWSTR)(buf + offset), fk->szCopyright); - offset += wcslen(fk->szCopyright)*2 + 2; - - ck->dpMessage = offset; - wcscpy((PWSTR)(buf + offset), fk->szMessage); - offset += wcslen(fk->szMessage)*2 + 2;*/ - - ck->dpStoreArray = (DWORD)offset; - sp = (PCOMP_STORE)(buf + offset); - fsp = fk->dpStoreArray; - offset += sizeof(COMP_STORE) * ck->cxStoreArray; - for (i = 0; i < ck->cxStoreArray; i++, sp++, fsp++) - { - sp->dwSystemID = fsp->dwSystemID; - sp->dpString = (DWORD)offset; - wcscpy_s((PWSTR)(buf + offset), (size - offset) / sizeof(WCHAR), fsp->dpString); // I3481 // I3641 - offset += wcslen(fsp->dpString) * 2 + 2; - - if (FSaveDebug || fsp->fIsOption) - { - sp->dpName = (DWORD)offset; - wcscpy_s((PWSTR)(buf + offset), (size - offset) / sizeof(WCHAR), fsp->szName); // I3481 // I3641 - offset += wcslen(fsp->szName) * 2 + 2; - } - else sp->dpName = 0; - } - - ck->dpGroupArray = (DWORD)offset; - gp = (PCOMP_GROUP)(buf + offset); - fgp = fk->dpGroupArray; - - offset += sizeof(COMP_GROUP) * ck->cxGroupArray; - - for (i = 0; i < ck->cxGroupArray; i++, gp++, fgp++) - { - gp->cxKeyArray = fgp->cxKeyArray; - gp->fUsingKeys = fgp->fUsingKeys; - - gp->dpMatch = gp->dpNoMatch = 0; - - if (fgp->dpMatch) - { - gp->dpMatch = (DWORD)offset; - wcscpy_s((PWSTR)(buf + offset), (size - offset) / sizeof(WCHAR), fgp->dpMatch); // I3481 // I3641 - offset += wcslen(fgp->dpMatch) * 2 + 2; - } - if (fgp->dpNoMatch) - { - gp->dpNoMatch = (DWORD)offset; - wcscpy_s((PWSTR)(buf + offset), (size - offset) / sizeof(WCHAR), fgp->dpNoMatch); // I3481 // I3641 - offset += wcslen(fgp->dpNoMatch) * 2 + 2; - } - - if (FSaveDebug) - { - gp->dpName = (DWORD)offset; - wcscpy_s((PWSTR)(buf + offset), (size - offset) / sizeof(WCHAR), fgp->szName); // I3481 // I3641 - offset += wcslen(fgp->szName) * 2 + 2; - } - else gp->dpName = 0; - - gp->dpKeyArray = (DWORD)offset; - kp = (PCOMP_KEY)(buf + offset); - fkp = fgp->dpKeyArray; - offset += gp->cxKeyArray * sizeof(COMP_KEY); - for (j = 0; j < gp->cxKeyArray; j++, kp++, fkp++) - { - kp->_reserved = 0; - kp->Key = fkp->Key; - if (FSaveDebug) kp->Line = fkp->Line; else kp->Line = 0; - kp->ShiftFlags = fkp->ShiftFlags; - kp->dpOutput = (DWORD)offset; - wcscpy_s((PWSTR)(buf + offset), (size - offset) / sizeof(WCHAR), fkp->dpOutput); // I3481 // I3641 - offset += wcslen(fkp->dpOutput) * 2 + 2; - kp->dpContext = (DWORD)offset; - wcscpy_s((PWSTR)(buf + offset), (size - offset) / sizeof(WCHAR), fkp->dpContext); // I3481 // I3641 - offset += wcslen(fkp->dpContext) * 2 + 2; - } - } - - ck->dwBitmapSize = fk->dwBitmapSize; - ck->dpBitmapOffset = (DWORD)offset; - memcpy(buf + offset, fk->lpBitmap, fk->dwBitmapSize); - offset += fk->dwBitmapSize; - - if (offset != size) { - delete[] buf; - return CERR_SomewhereIGotItWrong; - } - - if (ck->dwFileVersion < VERSION_160) { - SetChecksum(buf, &ck->dwCheckSum, (DWORD)size); - } - else { - ck->dwCheckSum = 0; // checksum is deprecated for 16.0+ - } - - DWORD dwBytesWritten = 0; - WriteFile(hOutfile, buf, (DWORD)size, &dwBytesWritten, NULL); - - if (dwBytesWritten != size) { - delete[] buf; - return CERR_UnableToWriteFully; - } - - delete[] buf; - - return CERR_None; -} - -DWORD ReadLine(HANDLE hInfile, PWSTR wstr, BOOL PreProcess) -{ - DWORD len; - PWSTR p; - BOOL LineCarry = FALSE, InComment = FALSE; - DWORD n; - WCHAR currentQuotes = 0; - WCHAR str[LINESIZE + 3]; - - if (!ReadFile(hInfile, str, LINESIZE * 2, &len, NULL)) return CERR_CannotReadInfile; - len /= 2; - str[len] = 0; - - if (SetFilePointer(hInfile, 0, NULL, FILE_CURRENT) == GetFileSize(hInfile, NULL)) - // Always a "\r\n" to the EOF, avoids funny bugs - wcscat_s(str, _countof(str), L"\r\n"); // I3481 - - if (len == 0) return CERR_EndOfFile; - - for (p = str, n = 0; n < len; n++, p++) - { - if (currentQuotes != 0) - { - if (*p == L'\n') - { - *p = 0; // I2525 - wcscpy_s(wstr, LINESIZE, str); // I3481 - return (PreProcess ? CERR_None : CERR_UnterminatedString); - } - if (*p == currentQuotes) currentQuotes = 0; - continue; - } - if (InComment) { - if (*p == L'\n') break; - *p = L' '; - continue; - } - if (*p == L'\\') { - LineCarry = TRUE; - *p = L' '; - continue; - } - if (LineCarry) - { - switch (*p) - { - case L' ': - case L'\t': - case L'\r': - *p = L' '; - continue; - case L'\n': - currentLine++; - LineCarry = FALSE; - *p = L' '; - continue; - } - *p = 0; // I2525 - wcscpy_s(wstr, LINESIZE, str); // I3481 - return (PreProcess ? CERR_None : CERR_InvalidLineContinuation); - } - - if (*p == L'\n') break; - switch (*p) - { - case L'c': - case L'C': - if ((p == str || iswspace(*(p - 1))) && iswspace(*(p + 1))) { - InComment = TRUE; - *p = L' '; - } - continue; - case L'\r': - case L'\t': - *p = L' '; - continue; - case L'\'': - case L'\"': - currentQuotes = *p; - continue; - } - } - - if (n == len) - { - str[LINESIZE - 1] = 0; // I2525 - wcscpy_s(wstr, LINESIZE, str); // I3481 - if (len == LINESIZE) - return (PreProcess ? CERR_None : CERR_LineTooLong); - } - - if (*p == L'\n') currentLine++; - - SetFilePointer(hInfile, -(int)(len * 2 - (INT_PTR)(p - str) * 2 - 2), NULL, FILE_CURRENT); - - p--; - while (p >= str && iswspace(*p)) p--; - p++; - *p++ = L'\n'; - *p = 0; - // trim spaces now, why not? - wcscpy_s(wstr, LINESIZE, str); // I3481 - - return CERR_None; -} - -DWORD GetRHS(PFILE_KEYBOARD fk, PWSTR p, PWSTR buf, int bufsize, int offset, int IsUnicode) -{ - PWSTR q; - - p = wcschr(p, '>'); - - if (!p) return CERR_NoTokensFound; - - p++; - - return GetXString(fk, p, L"c\n", buf, bufsize, offset, &q, TRUE, IsUnicode); -} - -void safe_wcsncpy(PWSTR out, PWSTR in, int cbMax) -{ - wcsncpy_s(out, cbMax, in, cbMax - 1); // I3481 - out[cbMax - 1] = 0; -} - -BOOL IsSameToken(PWSTR *p, PWSTR token) -{ - PWSTR q; - q = *p; - while (iswspace(*q)) q++; - if (_wcsnicmp(q, token, wcslen(token)) == 0) - { - q += wcslen(token); - while (iswspace(*q)) q++; - *p = q; - return TRUE; - } - return FALSE; -} - - -DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PWSTR szName, PDWORD FileSize, PBYTE *Buf) -{ - HANDLE hFile; - char szNewName[260], *p; - - p = wstrtostr(szName); - - if (IsRelativePath(p)) - { - strcpy_s(szNewName, _countof(szNewName), CompileDir); // I3481 - strcat_s(szNewName, _countof(szNewName), p); // I3481 - } - else - strcpy_s(szNewName, _countof(szNewName), p); // I3481 - - hFile = CreateFileA(szNewName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if (hFile == INVALID_HANDLE_VALUE) - { - strcat_s(szNewName, _countof(szNewName), ".bmp"); // I3481 - hFile = CreateFileA(szNewName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if (hFile == INVALID_HANDLE_VALUE) return CERR_CannotReadBitmapFile; - } - - DWORD msg; - - if ((msg = CheckFilenameConsistency(szNewName, FALSE)) != CERR_None) { - return msg; - } - - delete[] p; - - *FileSize = GetFileSize(hFile, NULL); - - if (*FileSize < 2) return CERR_CannotReadBitmapFile; - - *Buf = new BYTE[*FileSize]; - - if (!ReadFile(hFile, *Buf, *FileSize, FileSize, NULL)) { - delete[] * Buf; - *Buf = NULL; - return CERR_CannotReadBitmapFile; - } - - CloseHandle(hFile); - - /* Test for version 7.0 icon support */ - if (*((PCHAR)*Buf) != 'B' && *(((PCHAR)*Buf) + 1) != 'M') { - VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly); - } - - return CERR_None; -} - -int atoiW(PWSTR p) -{ - PSTR q = wstrtostr(p); - int i = atoi(q); - delete[] q; - return i; -} - -int CheckUTF16(int n) -{ - const int res[] = { - 0xFDD0, 0xFDD1, 0xFDD2, 0xFDD3, 0xFDD4, 0xFDD5, 0xFDD6, 0xFDD7, - 0xFDD8, 0xFDD9, 0xFDDA, 0xFDDB, 0xFDDC, 0xFDDD, 0xFDDE, 0xFDDF, - 0xFDE0, 0xFDE1, 0xFDE2, 0xFDE3, 0xFDE4, 0xFDE5, 0xFDE6, 0xFDE7, - 0xFDE8, 0xFDE9, 0xFDEA, 0xFDEB, 0xFDEC, 0xFDED, 0xFDEE, 0xFDEF, - 0xFFFF, 0xFFFE, 0 }; - - if (n == 0) return CERR_ReservedCharacter; - for (int i = 0; res[i] > 0; i++) - if (n == res[i]) - { - AddWarning(CWARN_ReservedCharacter); - break; - } - return CERR_None; -} - -int UTF32ToUTF16(int n, int *n1, int *n2) -{ - *n2 = -1; - if (n <= 0xFFFF) - { - *n1 = n; - if (n >= 0xD800 && n <= 0xDFFF) AddWarning(CWARN_UnicodeSurrogateUsed); - return CheckUTF16(*n1); - } - - if ((n & 0xFFFF) == 0xFFFF || (n & 0xFFFF) == 0xFFFE) AddWarning(CWARN_ReservedCharacter); - if (n < 0 || n > 0x10FFFF) return CERR_InvalidCharacter; - n = n - 0x10000; - *n1 = (n / 0x400) + 0xD800; - *n2 = (n % 0x400) + 0xDC00; - if ((n = CheckUTF16(*n1)) != CERR_None) return n; - return CheckUTF16(*n2); -} - -DWORD BuildVKDictionary(PFILE_KEYBOARD fk) // I3438 -{ - DWORD i; - size_t len = 0; - if (fk->cxVKDictionary == 0) return CERR_None; - for (i = 0; i < fk->cxVKDictionary; i++) - { - len += wcslen(fk->dpVKDictionary[i].szName) + 1; - } - PWSTR storeval = new WCHAR[len], p = storeval; - for (i = 0; i < fk->cxVKDictionary; i++) - { - wcscpy_s(p, len - (size_t)(p - storeval), fk->dpVKDictionary[i].szName); // I3481 - p = wcschr(p, 0); - *p = ' '; - p++; - } - - p--; - *p = 0; - - DWORD dwStoreID; - DWORD msg = AddStore(fk, TSS_VKDICTIONARY, storeval, &dwStoreID); - delete[] storeval; - return msg; -} - -int GetVKCode(PFILE_KEYBOARD fk, PWSTR p) // I3438 // TODO: Consolidate GetDeadKey and GetVKCode? -{ - DWORD i; - - for (i = 0; i < fk->cxVKDictionary; i++) - if (_wcsicmp(fk->dpVKDictionary[i].szName, p) == 0) - return i + VK__MAX + 1; // 256 - - if (fk->cxVKDictionary % 10 == 0) - { - PFILE_VKDICTIONARY pvk = new FILE_VKDICTIONARY[fk->cxVKDictionary + 10]; - memcpy(pvk, fk->dpVKDictionary, fk->cxVKDictionary * sizeof(FILE_VKDICTIONARY)); - delete fk->dpVKDictionary; - fk->dpVKDictionary = pvk; - } - wcsncpy_s(fk->dpVKDictionary[fk->cxVKDictionary].szName, _countof(fk->dpVKDictionary[fk->cxVKDictionary].szName), p, SZMAX_VKDICTIONARYNAME - 1); // I3481 - fk->dpVKDictionary[fk->cxVKDictionary].szName[SZMAX_VKDICTIONARYNAME - 1] = 0; - - fk->cxVKDictionary++; - return fk->cxVKDictionary + VK__MAX; // 256-1 -} - -int GetDeadKey(PFILE_KEYBOARD fk, PWSTR p) -{ - DWORD i; - - for (i = 0; i < fk->cxDeadKeyArray; i++) - if (_wcsicmp(fk->dpDeadKeyArray[i].szName, p) == 0) - return i + 1; - - if (fk->cxDeadKeyArray % 10 == 0) - { - PFILE_DEADKEY dk = new FILE_DEADKEY[fk->cxDeadKeyArray + 10]; - memcpy(dk, fk->dpDeadKeyArray, fk->cxDeadKeyArray * sizeof(FILE_DEADKEY)); - delete[] fk->dpDeadKeyArray; - fk->dpDeadKeyArray = dk; - } - wcsncpy_s(fk->dpDeadKeyArray[fk->cxDeadKeyArray].szName, _countof(fk->dpDeadKeyArray[fk->cxDeadKeyArray].szName), p, SZMAX_DEADKEYNAME); // I3481 - fk->dpDeadKeyArray[fk->cxDeadKeyArray].szName[SZMAX_DEADKEYNAME - 1] = 0; - - fk->cxDeadKeyArray++; - return fk->cxDeadKeyArray; -} - -void RecordDeadkeyNames(PFILE_KEYBOARD fk) -{ - WCHAR buf[SZMAX_DEADKEYNAME + 16]; - DWORD i; - for (i = 0; i < fk->cxDeadKeyArray; i++) - { - swprintf(buf, _countof(buf), L"%ls%d %ls", DEBUGSTORE_DEADKEY, (int)i, fk->dpDeadKeyArray[i].szName); // I3481 - AddDebugStore(fk, buf); - } -} - -BOOL IsValidCallStore(PFILE_STORE fs) -{ - int i; - PWSTR p; - for (i = 0, p = fs->dpString; *p; p++) - if (*p == ':') i++; - else if (!((*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z') || - (*p >= '0' && *p <= '9') || - *p == '.' || - *p == '_')) - return FALSE; - - return i == 1; -} - -HANDLE CreateTempFile() -{ - char szTempPathBuffer[MAX_PATH], szTempFileName[MAX_PATH]; // I3228 // I3510 - if (!GetTempPath(MAX_PATH, szTempPathBuffer)) return INVALID_HANDLE_VALUE; - if (!GetTempFileName(szTempPathBuffer, "kmx", 0, szTempFileName)) return INVALID_HANDLE_VALUE; // I3228 // I3510 - return CreateFile(szTempFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, - FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); -} - -/////////////////// -HANDLE UTF16TempFromUTF8(HANDLE hInfile, BOOL hasPreamble) -{ - HANDLE hOutfile = CreateTempFile(); - if (hOutfile == INVALID_HANDLE_VALUE) // I3228 // I3510 - { - CloseHandle(hInfile); - return INVALID_HANDLE_VALUE; - } - - PBYTE buf, p; - PWSTR outbuf, poutbuf; - DWORD len, len2; - WCHAR prolog = 0xFEFF; - WriteFile(hOutfile, &prolog, 2, &len2, NULL); - - len = GetFileSize(hInfile, NULL); - if (hasPreamble) { - SetFilePointer(hInfile, 3, NULL, FILE_BEGIN); // Cut off UTF-8 marker - len -= 3; - } - - buf = new BYTE[len + 1]; // null terminated - outbuf = new WCHAR[len + 1]; - if (ReadFile(hInfile, buf, len, &len2, NULL)) { - 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. - ConversionResult cr = ConvertUTF8toUTF16(&p, &buf[len2], (UTF16 **)&poutbuf, (const UTF16 *)&outbuf[len], lenientConversion); - WriteFile(hOutfile, outbuf, (DWORD)(INT_PTR)(poutbuf - outbuf) * 2, &len2, NULL); - } - 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 - AddCompileMessage(CHINT_NonUnicodeFile); - poutbuf = strtowstr((PSTR)buf); - WriteFile(hOutfile, poutbuf, (DWORD)wcslen(poutbuf) * 2, &len2, NULL); - delete[] poutbuf; - } - else { - WriteFile(hOutfile, outbuf, (DWORD)(INT_PTR)(poutbuf - outbuf) * 2, &len2, NULL); - } - } - } - - CloseHandle(hInfile); - delete[] buf; - delete[] outbuf; - SetFilePointer(hOutfile, 2, NULL, FILE_BEGIN); - return hOutfile; -} - -extern "C" void __declspec(dllexport) Keyman_Diagnostic(int mode) { - if (mode == 0) { - RaiseException(0x0EA0BEEF, EXCEPTION_NONCONTINUABLE, 0, NULL); - } -} - -PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, DWORD dwSystemID) { - assert(fk != NULL); - assert(dwSystemID != 0); - - PFILE_STORE sp = fk->dpStoreArray; - for (DWORD i = 0; i < fk->cxStoreArray; i++, sp++) { - if (sp->dwSystemID == dwSystemID) { - return sp; - } - } - return NULL; -} diff --git a/developer/src/kmcmpdll/Compiler.rc b/developer/src/kmcmpdll/Compiler.rc deleted file mode 100644 index 4c58996cb19..00000000000 --- a/developer/src/kmcmpdll/Compiler.rc +++ /dev/null @@ -1,252 +0,0 @@ -#include -#include "kmn_compiler_errors.h" - -#include "version.rc" - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE -BEGIN - CERR_InvalidLayoutLine "Invalid 'layout' command" - CERR_NoVersionLine "No version line found for file" - CERR_InvalidGroupLine "Invalid 'group' command" - CERR_InvalidStoreLine "Invalid 'store' command" - CERR_InvalidCodeInKeyPartOfRule - "Invalid command or code found in key part of rule" - CERR_InvalidDeadkey "Invalid 'deadkey' or 'dk' command" - CERR_InvalidValue "Invalid value in extended string" - CERR_ZeroLengthString "A string of zero characters was found" - CERR_TooManyIndexToKeyRefs - "Too many index commands refering to key string" - CERR_UnterminatedString "Unterminated string in line" - CERR_StringInVirtualKeySection - "extended string illegal in virtual key section" - CERR_AnyInVirtualKeySection - "'any' command is illegal in virtual key section" - CERR_InvalidAny "Invalid 'any' command" - CERR_StoreDoesNotExist "Store referenced does not exist" - CERR_BeepInVirtualKeySection - "'beep' command is illegal in virtual key section" - CERR_IndexInVirtualKeySection - "'index' command is illegal in virtual key section" -END - -STRINGTABLE -BEGIN - CERR_BadCallParams "CompileKeyboardFile was called with bad parameters" - CERR_InfileNotExist "Cannot find the input file" - CERR_CannotCreateOutfile "Cannot open output file for writing" - CERR_UnableToWriteFully "Unable to write the file completely" - CERR_CannotReadInfile "Cannot read the input file - possible disk error" - CERR_SomewhereIGotItWrong "Internal error: contact Tavultesoft" -END - -STRINGTABLE -BEGIN - CERR_BufferOverflow "The compiler memory buffer overflowed" - CERR_Break "Compiler interrupted by user" -END - -STRINGTABLE -BEGIN - CERR_CannotAllocateMemory "Out of memory" -END - -STRINGTABLE -BEGIN - CERR_InvalidBitmapLine "Invalid 'bitmaps' command" - CERR_CannotReadBitmapFile "Cannot open the bitmap file for reading" - CERR_IndexDoesNotPointToAny - "An index() in the output does not have a corresponding any() statement" - CERR_ReservedCharacter "A reserved character was found" - CERR_InvalidCharacter "A character was found that is outside the valid Unicode range (U+0000 - U+10FFFF)" - CERR_InvalidCall "The 'call' command is invalid" - CERR_CallInVirtualKeySection - "'call' command is illegal in virtual key section" - CERR_CodeInvalidInKeyStore - "The command is invalid inside a store that is used in a key part of the rule" - CERR_CannotLoadIncludeFile - "Cannot load the included file: it is either invalid or does not exist" - CERR_60FeatureOnly_EthnologueCode - "EthnologueCode system store requires VERSION 6.0" - CERR_60FeatureOnly_MnemonicLayout - "MnemonicLayout functionality requires VERSION 6.0" - CERR_60FeatureOnly_OldCharPosMatching - "OldCharPosMatching system store requires VERSION 6.0" - CERR_60FeatureOnly_NamedCodes - "Named character constants requires VERSION 6.0" - CERR_60FeatureOnly_Contextn "Context(n) requires VERSION 6.0" - CERR_501FeatureOnly_Call "Call() requires VERSION 5.01" - CERR_InvalidNamedCode "Invalid named code constant" -END - -STRINGTABLE -BEGIN - CERR_InvalidSystemStore "Invalid system store name found" - CERR_60FeatureOnly_VirtualCharKey - "Virtual character keys require VERSION 6.0" - CERR_VersionAlreadyIncluded - "Only one VERSION or store(&version) line allowed in a source file." - CERR_70FeatureOnly "This feature requires store(&version) '7.0'" - CERR_80FeatureOnly "This feature requires store(&version) '8.0'" - CERR_InvalidInVirtualKeySection - "This statement is not valid in a virtual key section" - CERR_InvalidIf "The if() statement is not valid" - CERR_InvalidReset "The reset() statement is not valid" - CERR_InvalidSet "The set() statement is not valid" - CERR_InvalidSave "The save() statement is not valid" - CERR_InvalidEthnologueCode "Invalid ðnologuecode format" - CERR_90FeatureOnly_IfSystemStores - "if(&store) requires store(&version) '9.0'" - CERR_IfSystemStore_NotFound "System store in if() not found" - CERR_90FeatureOnly_SetSystemStores "set(&store) requires store(&version) '9.0'" - CERR_SetSystemStore_NotFound "System store in set() not found" - CERR_90FeatureOnlyVirtualKeyDictionary "Custom virtual key names require store(&version) '9.0'" -END - -STRINGTABLE -BEGIN - CERR_InvalidIndex "Invalid 'index' command" - CERR_OutsInVirtualKeySection - "'outs' command is illegal in virtual key section" - CERR_InvalidOuts "Invalid 'outs' command" - CERR_ContextInVirtualKeySection - "'context' command is illegal in virtual key section" - CERR_InvalidUse "Invalid 'use' command" - CERR_GroupDoesNotExist "Group does not exist" - CERR_VirtualKeyNotAllowedHere "Virtual key is not allowed here" - CERR_InvalidSwitch "Invalid 'switch' command" - CERR_NoTokensFound "No tokens found in line" - CERR_InvalidLineContinuation "Invalid line continuation" - CERR_LineTooLong "Line too long" - CERR_InvalidCopyright "Invalid 'copyright' command" - CERR_CodeInvalidInThisSection - "This line is invalid in this section of the file" - CERR_InvalidMessage "Invalid 'message' command" - CERR_InvalidLanguageName "Invalid 'languagename' command" -END - -STRINGTABLE -BEGIN - CWARN_TooManyWarnings "Too many warnings or errors" - CWARN_OldVersion "The keyboard file is an old version" - CWARN_BitmapNotUsed "The 'bitmaps' statement is obsolete and only the first bitmap referred to will be used, you should use 'bitmap'." - CWARN_CustomLanguagesNotSupported - "Languages over 0x1FF, 0x1F are not supported correctly by Windows. You should use no LANGUAGE line instead." - CWARN_KeyBadLength "There are too many characters in the keystroke part of the rule." - CWARN_IndexStoreShort "The store referenced in index() is shorter than the store referenced in any()" - CWARN_UnicodeInANSIGroup "A Unicode character was found in an ANSI group" - CWARN_ANSIInUnicodeGroup "An ANSI character was found in a Unicode group" - CWARN_UnicodeSurrogateUsed - "A Unicode surrogate character was found. You should use Unicode scalar values to represent values > U+FFFF" - CWARN_ReservedCharacter "A Unicode character was found that should not be used" - CWARN_Info "Information" - CWARN_VirtualKeyWithMnemonicLayout - "Virtual key used instead of virtual character key with a mnemonic layout" - CWARN_VirtualCharKeyWithPositionalLayout - "Virtual character key used with a positional layout instead of mnemonic layout" - CWARN_StoreAlreadyUsedAsOptionOrCall - "Store already used as an option or in a call statement and should not be used as a normal store" - CWARN_StoreAlreadyUsedAsStoreOrCall - "Store already used as a normal store or in a call statement and should not be used as an option" - CWARN_StoreAlreadyUsedAsStoreOrOption - "Store already used as a normal store or as an option and should not be used in a call statement" -END - -STRINGTABLE -BEGIN - CERR_None "(no error)" - CERR_EndOfFile "(no error - reserved code)" -END - -STRINGTABLE -BEGIN - CERR_InvalidToken "Invalid token found" - CERR_InvalidBegin "Invalid 'begin' command" - CERR_InvalidName "Invalid 'name' command" - CERR_InvalidVersion "Invalid 'version' command" - CERR_InvalidLanguageLine "Invalid 'language' command" - CERR_LayoutButNoLanguage "Layout command found but no language command" -END - -STRINGTABLE -BEGIN - CWARN_PunctuationInEthnologueCode - "Punctuation should not be used to separate Ethnologue codes; instead use spaces" -END - -STRINGTABLE -BEGIN - CERR_CannotCreateTempfile "Cannot create temp file" -END - -STRINGTABLE -BEGIN - CERR_90FeatureOnlyLayoutFile "Touch layout file reference requires store(&version) '9.0'" - CERR_90FeatureOnlyKeyboardVersion "KeyboardVersion system store requires store(&version) '9.0'" - CERR_KeyboardVersionFormatInvalid "KeyboardVersion format is invalid, expecting dot-separated integers" - CERR_ContextExHasInvalidOffset "context() statement has offset out of range" - CERR_90FeatureOnlyEmbedCSS "Embedding CSS requires store(&version) '9.0'" - CERR_90FeatureOnlyTargets "&TARGETS system store requires store(&version) '9.0'" - CERR_ContextAndIndexInvalidInMatchNomatch "context and index statements cannot be used in a match or nomatch statement" - CERR_140FeatureOnlyContextAndNotAnyWeb "For web and touch platforms, context() statement referring to notany() requires store(&version) '14.0'" - CWARN_PlatformNotInTargets "The specified platform is not a target platform" -END - -STRINGTABLE -BEGIN - CWARN_HeaderStatementIsDeprecated "Header statements are deprecated; use instead the equivalent system store" - CWARN_UseNotLastStatementInRule "A rule with use() statements in the output should not have other content following the use() statements" - CWARN_KVKFileIsInSourceFormat ".kvk file should be binary but is an XML file" -END - -STRINGTABLE -BEGIN - CWARN_DontMixChiralAndNonChiralModifiers "Don't mix the use of left/right modifiers with non-left/right modifiers in the same platform" - CWARN_MixingLeftAndRightModifiers "Left and right modifiers should not both be used in the same rule" -END - -STRINGTABLE -BEGIN - CWARN_LanguageHeadersDeprecatedInKeyman10 "This language header has been deprecated in Keyman 10. Instead, add language metadata in the package file" - CWARN_HotkeyHasInvalidModifier "Hotkey has modifiers that are not supported. Use only SHIFT, CTRL and ALT" - CHINT_NonUnicodeFile "Keyman Developer has detected that the file has ANSI encoding. Consider converting this file to UTF-8" - 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" - CHINT_UnreachableRule "This rule will never be matched as another rule takes precedence" - CHINT_FilenameHasDifferingCase "Casing differences may fail on some platforms:" - CWARN_MissingFile "The referenced file could not be found" -END - -STRINGTABLE -BEGIN - CERR_ExpansionMustFollowCharacterOrVKey "An expansion must follow a character or a virtual key" - CERR_VKeyExpansionMustBeFollowedByVKey "A virtual key expansion must be terminated by a virtual key" - CERR_CharacterExpansionMustBeFollowedByCharacter "A character expansion must be terminated by a character key" - CERR_VKeyExpansionMustUseConsistentShift "A virtual key expansion must use the same shift state for both terminators" - CERR_ExpansionMustBePositive "An expansion must have positive difference (i.e. A-Z, not Z-A)" -END - -STRINGTABLE -BEGIN - CERR_CasedKeysMustContainOnlyVirtualKeys "The &CasedKeys system store must contain only virtual keys or characters found on a US English keyboard" - CERR_CasedKeysMustNotIncludeShiftStates "The &CasedKeys system store must not include shift states" - CERR_CasedKeysNotSupportedWithMnemonicLayout "The &CasedKeys system store is not supported with mnemonic layouts" - CERR_CannotUseReadWriteGroupFromReadonlyGroup "Group used from a readonly group must also be readonly" - CERR_StatementNotPermittedInReadonlyGroup "Statement is not permitted in output of readonly group" - CERR_OutputInReadonlyGroup "Output is not permitted in a readonly group" - CERR_NewContextGroupMustBeReadonly "Group used in begin newContext must be readonly" - CERR_PostKeystrokeGroupMustBeReadonly "Group used in begin postKeystroke must be readonly" - CERR_DuplicateGroup "A group with this name has already been defined." - CERR_DuplicateStore "A store with this name has already been defined." - CERR_RepeatedBegin "This begin statement has already been defined." -END diff --git a/developer/src/kmcmpdll/DeprecationChecks.cpp b/developer/src/kmcmpdll/DeprecationChecks.cpp deleted file mode 100644 index d64f2279e06..00000000000 --- a/developer/src/kmcmpdll/DeprecationChecks.cpp +++ /dev/null @@ -1,52 +0,0 @@ - -#include "pch.h" - -#include -#include -#include - -BOOL WarnDeprecatedHeader() { // I4866 - if (FWarnDeprecatedCode) { - AddWarning(CWARN_HeaderStatementIsDeprecated); - } - return TRUE; -} - -/* Flag presence of deprecated features */ -BOOL CheckForDeprecatedFeatures(PFILE_KEYBOARD fk) { - /* - For Keyman 10, we deprecated: - // < Keyman 7 - #define TSS_LANGUAGE 4 - #define TSS_LAYOUT 5 - #define TSS_LANGUAGENAME 12 - #define TSS_ETHNOLOGUECODE 15 - - // Keyman 7 - #define TSS_WINDOWSLANGUAGES 29 - */ - int oldCurrentLine = currentLine; - DWORD i; - PFILE_STORE sp; - - if (!FWarnDeprecatedCode) { - return TRUE; - } - - if (fk->version >= VERSION_100) { - for (i = 0, sp = fk->dpStoreArray; i < fk->cxStoreArray; i++, sp++) { - if (sp->dwSystemID == TSS_LANGUAGE || - sp->dwSystemID == TSS_LAYOUT || - sp->dwSystemID == TSS_LANGUAGENAME || - sp->dwSystemID == TSS_ETHNOLOGUECODE || - sp->dwSystemID == TSS_WINDOWSLANGUAGES) { - currentLine = sp->line; - AddWarning(CWARN_LanguageHeadersDeprecatedInKeyman10); - } - } - } - - currentLine = oldCurrentLine; - - return TRUE; -} diff --git a/developer/src/kmcmpdll/DeprecationChecks.h b/developer/src/kmcmpdll/DeprecationChecks.h deleted file mode 100644 index 1397289b421..00000000000 --- a/developer/src/kmcmpdll/DeprecationChecks.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include -#include - -BOOL WarnDeprecatedHeader(); -BOOL CheckForDeprecatedFeatures(PFILE_KEYBOARD fk); \ No newline at end of file diff --git a/developer/src/kmcmpdll/Edition.cpp b/developer/src/kmcmpdll/Edition.cpp deleted file mode 100644 index 3253804c6cf..00000000000 --- a/developer/src/kmcmpdll/Edition.cpp +++ /dev/null @@ -1,8 +0,0 @@ - -#include "pch.h" -#include "edition.h" - -int GetEdition() -{ - return ED_STANDARD; -} diff --git a/developer/src/kmcmpdll/Edition.h b/developer/src/kmcmpdll/Edition.h deleted file mode 100644 index 26eb44c6160..00000000000 --- a/developer/src/kmcmpdll/Edition.h +++ /dev/null @@ -1,12 +0,0 @@ - -#ifndef _EDITION_H -#define _EDITION_H - -int GetEdition(); - -#define ED_DEMO 0 -#define ED_STANDARD 1 -#define ED_PRO 2 -#define ED_OEM 3 - -#endif diff --git a/developer/src/kmcmpdll/Makefile b/developer/src/kmcmpdll/Makefile deleted file mode 100644 index 1769adac773..00000000000 --- a/developer/src/kmcmpdll/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -# -# KMCmpDll Makefile -# - -!include ..\Defines.mak - - -build: version.res dirs - $(MSBUILD) kmcmpdll.vcxproj $(MSBUILD_BUILD) "/p:Platform=Win32" - $(COPY) $(WIN32_TARGET_PATH)\kmcmpdll.dll $(DEVELOPER_PROGRAM) - $(COPY) $(WIN32_TARGET_PATH)\kmcmpdll.lib $(DEVELOPER_OUTLIB) - $(COPY) $(WIN32_TARGET_PATH)\kmcmpdll.pdb $(DEVELOPER_DEBUGPATH) -# for debugging purposes, it's useful to have a copy -# of kmcmpdll.dll in the TIKE project folder; it is -# .gitignored - -mkdir ..\kmcomp\$(WIN32_TARGET_PATH) - -mkdir ..\tike\$(WIN32_TARGET_PATH) - $(COPY) $(WIN32_TARGET_PATH)\kmcmpdll.dll ..\tike\$(WIN32_TARGET_PATH)\kmcmpdll.dll - $(COPY) $(WIN32_TARGET_PATH)\kmcmpdll.dll ..\kmcomp\$(WIN32_TARGET_PATH)\kmcmpdll.dll - - $(MSBUILD) kmcmpdll.vcxproj $(MSBUILD_BUILD) "/p:Platform=x64" - $(COPY) $(X64_TARGET_PATH)\kmcmpdll.x64.dll $(DEVELOPER_PROGRAM)\kmcmpdll.x64.dll - $(COPY) $(X64_TARGET_PATH)\kmcmpdll.x64.lib $(DEVELOPER_OUTLIB)\kmcmpdll.x64.lib - $(COPY) $(X64_TARGET_PATH)\kmcmpdll.x64.pdb $(DEVELOPER_DEBUGPATH)\kmcmpdll.x64.pdb - -mkdir ..\kmcomp\$(X64_TARGET_PATH) - $(COPY) $(X64_TARGET_PATH)\kmcmpdll.x64.dll ..\kmcomp\$(X64_TARGET_PATH)\kmcmpdll.x64.dll - - -clean: def-clean - $(MSBUILD) kmcmpdll.vcxproj $(MSBUILD_CLEAN) - -signcode: - $(SIGNCODE) /d "Keyman Developer Compiler" $(DEVELOPER_PROGRAM)\kmcmpdll.dll - $(SIGNCODE) /d "Keyman Developer Compiler" $(DEVELOPER_PROGRAM)\kmcmpdll.x64.dll - -wrap-symbols: - $(SYMSTORE) $(DEVELOPER_PROGRAM)\kmcmpdll.dll /t keyman-developer - $(SYMSTORE) $(DEVELOPER_PROGRAM)\kmcmpdll.x64.dll /t keyman-developer - $(SYMSTORE) $(DEVELOPER_DEBUGPATH)\kmcmpdll.pdb /t keyman-developer - $(SYMSTORE) $(DEVELOPER_DEBUGPATH)\kmcmpdll.x64.pdb /t keyman-developer - -test-manifest: - @rem This target needed as dependency for TIKE and KMCMPDLL - -install: - $(COPY) $(DEVELOPER_PROGRAM)\kmcmpdll.dll "$(INSTALLPATH_KEYMANDEVELOPER)\kmcmpdll.dll" - $(COPY) $(DEVELOPER_PROGRAM)\kmcmpdll.x64.dll "$(INSTALLPATH_KEYMANDEVELOPER)\kmcmpdll.x64.dll" - -!include ..\Target.mak diff --git a/developer/src/kmcmpdll/NamedCodeConstants.cpp b/developer/src/kmcmpdll/NamedCodeConstants.cpp deleted file mode 100644 index 14f523b35ec..00000000000 --- a/developer/src/kmcmpdll/NamedCodeConstants.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/* - Name: NamedCodeConstants - Copyright: Copyright (C) 2003-2017 SIL International. - Documentation: - Description: - Create Date: 19 Jul 2011 - - Modified Date: 13 Dec 2012 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 19 Jul 2011 - mcdurdin - I2993 - Named code constants cause a warning 0x208D to appear - 24 Oct 2012 - mcdurdin - I3481 - V9.0 - Eliminate unsafe calls in C++ - 06 Feb 2012 - mcdurdin - I3056 - If file was not found in first search folder, named code constants failed to compile - 06 Feb 2012 - mcdurdin - I3056 - UTF-8 support for named code constants file - 03 Nov 2012 - mcdurdin - I3512 - V9.0 - Merge of I3056 - If file was not found in first search folder, named code constants failed to compile - 13 Dec 2012 - mcdurdin - I3641 - V9.0 - compiler dll buffer overrun bugs -*/ - -#include "pch.h" -#include -#include "NamedCodeConstants.h" -#include "CheckFilenameConsistency.h" -#include "kmcmpdll.h" - -extern char CompileDir[]; - - -int IsHangulSyllable(const wchar_t *codename, int *code); - - -NamedCodeConstants::NamedCodeConstants() -{ - nEntries = 0; - entries = NULL; - nEntries_file = 0; - entries_file = NULL; - reindex(); -} - -NamedCodeConstants::~NamedCodeConstants() -{ - if(entries) delete entries; - if(entries_file) delete entries_file; -} - -void NamedCodeConstants::AddCode(int n, const wchar_t *p, DWORD storeIndex) -{ - if((nEntries_file % ALLOC_SIZE) == 0) - { - NCCENTRY *bn = new NCCENTRY[nEntries_file + ALLOC_SIZE]; - if(nEntries_file > 0) - { - memcpy(bn, entries_file, sizeof(NCCENTRY) * nEntries_file); - delete entries_file; - } - entries_file = bn; - } - - entries_file[nEntries_file].code = n; - wcsncpy_s(entries_file[nEntries_file].name, _countof(entries_file[nEntries_file].name), p, MAX_ENAME); // I3481 - entries_file[nEntries_file].name[MAX_ENAME] = 0; - - for (wchar_t *r = entries_file[nEntries_file].name; *r; r++) - if (iswblank(*r) && *r != '-') *r = '_'; - - entries_file[nEntries_file].storeIndex = storeIndex; - nEntries_file++; -} - -void NamedCodeConstants::AddCode_IncludedCodes(int n, const wchar_t *p) -{ - if((nEntries % ALLOC_SIZE) == 0) - { - NCCENTRY *bn = new NCCENTRY[nEntries + ALLOC_SIZE]; - if(nEntries > 0) - { - memcpy(bn, entries, sizeof(NCCENTRY) * nEntries); - delete entries; - } - entries = bn; - } - - entries[nEntries].code = n; - wcsncpy_s(entries[nEntries].name, _countof(entries[nEntries].name), p, MAX_ENAME); // I3481 - entries[nEntries].name[MAX_ENAME] = 0; - for (wchar_t *r = entries[nEntries].name; *r; r++) - if (iswblank(*r)) *r = '_'; - - entries[nEntries].storeIndex = 0xFFFFFFFFL; - nEntries++; -} - -int __cdecl sort_entries(const void *elem1, const void *elem2) -{ - return _wcsicmp( - ((NCCENTRY *)elem1)->name, - ((NCCENTRY *)elem2)->name); -} - -BOOL NamedCodeConstants::IntLoadFile(const char *filename) -{ - FILE *fp = NULL; - - if (CheckFilenameConsistency(filename, FALSE) != CERR_None) { - return FALSE; - } - - if(fopen_s(&fp, filename, "rt") != 0) return FALSE; // I3481 - - char str[256], *p, *q, *context = NULL; - BOOL neol, first = TRUE; - - while(fgets(str, 256, fp)) - { - neol = *(strchr(str, 0) - 1) == '\n'; - p = strtok_s(str, ";", &context); // I3481 - q = strtok_s(NULL, ";\n", &context); - if(p && q) - { - if(first && *p == (char)0xEF && *(p+1) == (char)0xBB && *(p+2) == (char)0xBF) p += 3; // I3056 UTF-8 // I3512 - first = FALSE; - _strupr_s(q, strlen(q)+1); // I3481 // I3641 - int n = strtol(p, NULL, 16); - if (*q != '<') { - PWSTR q0 = strtowstr(q); - AddCode_IncludedCodes(n, q0); - delete[] q0; - } - } - if(!neol) - { - while(fgets(str, 256, fp)) if(*(strchr(str, 0)-1) == '\n') break; - } - } - - fclose(fp); - - return TRUE; -} - -BOOL NamedCodeConstants::LoadFile(const char *filename) -{ - char buf[260]; - // Look in current directory first - strncpy_s(buf, _countof(buf), filename, 259); buf[259] = 0; // I3481 - if(FileExists(buf)) - return IntLoadFile(buf); - // Then look in keyboard file directory (CompileDir) - strncpy_s(buf, _countof(buf), CompileDir, 259); buf[259] = 0; // I3481 - strncat_s(buf, _countof(buf), filename, 259-strlen(CompileDir)); buf[259] = 0; - if(FileExists(buf)) - return IntLoadFile(buf); - // Finally look in kmcmpdll.dll directory - GetModuleFileName(0, buf, 260); - char *p = strrchr(buf, '\\'); if(p) p++; else p = buf; - *p = 0; - strncat_s(buf, _countof(buf), filename, 259-strlen(buf)); buf[259] = 0; // I3481 // I3641 - if(FileExists(buf)) - return IntLoadFile(buf); - - reindex(); - - return FALSE; -} - -void NamedCodeConstants::reindex() -{ - if (entries != NULL) { - qsort(entries, nEntries, sizeof(NCCENTRY), sort_entries); - } - - wchar_t c = L'.', d; - int i; - - for(i = 0; i < 128; i++) chrindexes[i] = -1; - - if (entries != NULL) { - for (i = 0; i < nEntries; i++) - { - d = towupper(entries[i].name[0]); - if (d != c && d >= 32 && d <= 127) - chrindexes[c = d] = i; - } - } -} - -int NamedCodeConstants::GetCode(const wchar_t *codename, DWORD *storeIndex) -{ - *storeIndex = 0xFFFFFFFFL; // I2993 - int code = GetCode_IncludedCodes(codename); - if(code) return code; - for(int i = 0; i < nEntries_file; i++) - if(!_wcsicmp(entries_file[i].name, codename)) - { - *storeIndex = entries_file[i].storeIndex; - return entries_file[i].code; - } - return 0; -} - -int NamedCodeConstants::GetCode_IncludedCodes(const wchar_t *codename) -{ - wchar_t c = towupper(*codename); - int code; - - if(IsHangulSyllable(codename, &code)) return code; - - if(c < 32 || c > 127 || chrindexes[c] < 0) return 0; - for(int n = chrindexes[c]; n < nEntries && towupper(entries[n].name[0]) == c; n++) - { - int cmp = _wcsicmp(codename, entries[n].name); - if(cmp == 0) return entries[n].code; - if(cmp < 0) break; - } - return 0; -} - -/* - - Hangul Syllables - -*/ - -const int - HangulSBase = 0xAC00, - HangulLBase = 0x1100, - HangulVBase = 0x1161, - HangulTBase = 0x11A7, - HangulLCount = 19, - HangulVCount = 21, - HangulTCount = 28, - HangulNCount = HangulVCount * HangulTCount, // 588 - HangulSCount = HangulLCount * HangulNCount; // 11172 - -const wchar_t * - Hangul_JAMO_L_TABLE[] = { - L"G", L"GG", L"N", L"D", L"DD", L"R", L"M", L"B", L"BB", - L"S", L"SS", L"", L"J", L"JJ", L"C", L"K", L"T", L"P", L"H" }; - -const wchar_t * - Hangul_JAMO_V_TABLE[] = { - L"A", L"AE", L"YA", L"YAE", L"EO", L"E", L"YEO", L"YE", L"O", - L"WA", L"WAE", L"OE", L"YO", L"U", L"WEO", L"WE", L"WI", - L"YU", L"EU", L"YI", L"I" }; - -const wchar_t * - Hangul_JAMO_T_TABLE[] = { - L"", L"G", L"GG", L"GS", L"N", L"NJ", L"NH", L"D", L"L", L"LG", L"LM", - L"LB", L"LS", L"LT", L"LP", L"LH", L"M", L"B", L"BS", - L"S", L"SS", L"NG", L"J", L"C", L"K", L"T", L"P", L"H" }; - -int IsHangulSyllable(const wchar_t *codename, int *code) -{ - if(_wcsnicmp(codename, L"HANGUL_SYLLABLE_", 16)) return 0; - codename += 16; - if(!*codename) return 0; - - int i, LIndex, VIndex, TIndex; - - /* Find initial */ - - int ch = towupper(*codename); - if(strchr("GNDRMBSJCKTPH", ch)) - { - /* Has an initial syllable */ - int fdouble = towupper(*(codename+1)) == ch; - - LIndex = -1; - for(i = 0; i < HangulLCount; i++) - if(Hangul_JAMO_L_TABLE[i][0] == ch && - (!fdouble || (Hangul_JAMO_L_TABLE[i][1] == ch && fdouble))) - { - LIndex = i; - break; - } - if(LIndex == -1) return 0; - codename++; - if(fdouble) codename++; - } - else LIndex = 11; /* no initial */ - - /* Find vowel */ - - wchar_t V[4] = L""; - V[0] = *codename; - if(V[0] && strchr("AEIOUWY", towupper(*(codename+1)))) V[1] = *(codename+1); - if(V[1] && strchr("AEIOUWY", towupper(*(codename+2)))) V[2] = *(codename+2); - - VIndex = -1; - for(i = 0; i < HangulVCount; i++) - if(!_wcsicmp(Hangul_JAMO_V_TABLE[i], V)) { VIndex = i; break; } - - if(VIndex == -1) return 0; - - codename += wcslen(V); - - /* Find final */ - - TIndex = -1; - - for(i = 0; i < HangulTCount; i++) - if(!_wcsicmp(Hangul_JAMO_T_TABLE[i], codename)) { TIndex = i; break; } - - if(TIndex == -1) return 0; - - /* Composition */ - - *code = (HangulSBase + (LIndex * HangulVCount + VIndex) * HangulTCount) + TIndex; - - return 1; -} diff --git a/developer/src/kmcmpdll/NamedCodeConstants.h b/developer/src/kmcmpdll/NamedCodeConstants.h deleted file mode 100644 index 3efaf4ed1df..00000000000 --- a/developer/src/kmcmpdll/NamedCodeConstants.h +++ /dev/null @@ -1,36 +0,0 @@ - -#ifndef _NAMEDCODECONSTANTS_H -#define _NAMEDCODECONSTANTS_H - -#define MAX_ENAME 128 -#define ALLOC_SIZE 256 - -struct NCCENTRY -{ - wchar_t name[MAX_ENAME+1]; - int code; - DWORD storeIndex; -}; - -class NamedCodeConstants -{ -private: - NCCENTRY *entries; // entries from &includecodes - NCCENTRY *entries_file; // entries from store(myconst) x - int nEntries, nEntries_file; - int chrindexes[128]; // A-Z, 0-9, -, _; simple index - - int GetCode_IncludedCodes(const wchar_t *codename); - void AddCode_IncludedCodes(int n, const wchar_t *p); - BOOL IntLoadFile(const char *filename); -public: - NamedCodeConstants(); - ~NamedCodeConstants(); - - void reindex(); - void AddCode(int n, const wchar_t *p, DWORD storeIndex); - BOOL LoadFile(const char *filename); - int GetCode(const wchar_t *codename, DWORD *storeIndex); -}; - -#endif //_NAMEDCODECONSTANTS_H diff --git a/developer/src/kmcmpdll/UnreachableRules.cpp b/developer/src/kmcmpdll/UnreachableRules.cpp deleted file mode 100644 index 268360904d6..00000000000 --- a/developer/src/kmcmpdll/UnreachableRules.cpp +++ /dev/null @@ -1,52 +0,0 @@ - -#include "pch.h" - -#include -#include -#include "../../../common/windows/cpp/include/vkeys.h" -#include - -#include -#include -#include -#include - -#include "UnreachableRules.h" - -std::wstring MakeHashKeyFromFileKey(PFILE_KEY kp) { - std::wstringstream key; - key << kp->Key << "," << kp->ShiftFlags << ","; - if (kp->dpContext) - key << kp->dpContext; - return key.str(); -} - -DWORD VerifyUnreachableRules(PFILE_GROUP gp) { - PFILE_KEY kp = gp->dpKeyArray; - DWORD i; - - int oldCurrentLine = currentLine; - - std::unordered_map map; - std::unordered_set reportedLines; - - for (i = 0; i < gp->cxKeyArray; i++, kp++) { - std::wstring key = MakeHashKeyFromFileKey(kp); - if (map.count(key) > 0) { - FILE_KEY const & k1 = map.at(key); - if (kp->Line != k1.Line && reportedLines.count(kp->Line) == 0) { - reportedLines.insert(kp->Line); - currentLine = kp->Line; - wsprintf(ErrExtra, "Overridden by rule on line %d", k1.Line); - AddWarning(CHINT_UnreachableRule); - } - } - else { - map.insert({ key, *kp }); - } - } - - currentLine = oldCurrentLine; - - return CERR_None; -} diff --git a/developer/src/kmcmpdll/UnreachableRules.h b/developer/src/kmcmpdll/UnreachableRules.h deleted file mode 100644 index 94815af1c0c..00000000000 --- a/developer/src/kmcmpdll/UnreachableRules.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -DWORD VerifyUnreachableRules(PFILE_GROUP gp); diff --git a/developer/src/kmcmpdll/compfile.h b/developer/src/kmcmpdll/compfile.h deleted file mode 100644 index 9e65c36600e..00000000000 --- a/developer/src/kmcmpdll/compfile.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - Name: Compfile - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 25 Jan 2007 - - Modified Date: 25 May 2010 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 25 Jan 2007 - mcdurdin - Add GLOBAL_BUFSIZE, enlarge LINESIZE - 25 May 2010 - mcdurdin - I1632 - Keyboard Options -*/ - -#ifndef _COMPFILE_H -#define _COMPFILE_H - -#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 - -#define SZMAX_STORENAME 80 -#define SZMAX_GROUPNAME 80 -#define SZMAX_DEADKEYNAME 80 -#define SZMAX_ERRORTEXT 512 -#define SZMAX_VKDICTIONARYNAME 80 - -#define MAX_WARNINGS 100 - -#define T_COMMENT 1 // A comment line -#define T_KEYTOKEY 2 // A rule line -#define T_BLANK 3 // A blank line - -#define T_W_START 4 // Start of easily matched line types -#define T_STORE 4 // A store line -#define T_VERSION 5 // A 'VERSION 3.2/3.1/3.0' line -#define T_NAME 6 // A 'NAME "xxxx"' line -#define T_BITMAP 7 // A 'BITMAP bmpfile' line -#define T_HOTKEY 8 // A 'HOTKEY "^%+C"' line -#define T_BEGIN 9 // A 'begin > use(xxxx)' line -#define T_GROUP 10 // A 'group(xxxx)' line -#define T_MATCH 11 // A 'match > "XXXX"' line -#define T_NOMATCH 12 // A 'nomatch > "XXXX"' line -#define T_SHIFT 13 // A 'SHIFT FREES CAPS' line -#define T_CAPSON 14 // A 'CAPS ON ONLY' line -#define T_CAPSOFF 15 // A 'CAPS ALWAYS OFF' line -#define T_LANGUAGE 16 // A 'LANGUAGE xxxh, xxh' line -#define T_LAYOUT 17 // A 'LAYOUT xxxxh' line -#define T_COPYRIGHT 18 // A 'COPYRIGHT "so and so"' line -#define T_MESSAGE 19 // A 'MESSAGE "so and so"' line -#define T_LANGUAGENAME 20 // A 'LANGUAGENAME "xxxx"' line -#define T_BITMAPS 21 // An unused 'BITMAPS' line (version 3.x) -#define T_W_END 21 // End of easily matched rule types - -#define T_UNKNOWN 99 // Unrecognized line type (illegal line) - -#define GDS_CUTLEAD 0x01 // GetDelimitedString: cut leading spaces -#define GDS_CUTFOLL 0x02 // GetDelimitedString: cut following spaces - -#define GXS_LINE 0x00 // GetExtendedString error: in a "line" -#define GXS_RULE 0x01 // GetExtendedString error: in a "rule" -#define GXS_STORE 0x02 // GetExtendedString error: in a "store" - -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 - //FileStoreType fstType; - BOOL fIsStore; - BOOL fIsReserved; - BOOL fIsOption; - 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 -}; -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 - BOOL fReadOnly; // group(xx) [readonly] <-- specified or not - DWORD Line; -}; - -typedef FILE_GROUP *PFILE_GROUP; - -struct FILE_DEADKEY { - WCHAR szName[SZMAX_DEADKEYNAME]; -}; - -typedef FILE_DEADKEY *PFILE_DEADKEY; - -struct FILE_VKDICTIONARY { - WCHAR szName[SZMAX_VKDICTIONARYNAME]; -}; -typedef FILE_VKDICTIONARY *PFILE_VKDICTIONARY; - -struct FILE_KEYBOARD { - DWORD KeyboardID; // deprecated, unused - - 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 - - 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) - - 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 cxVKDictionary; - PFILE_VKDICTIONARY dpVKDictionary; // temp - virtual key dictionary - - void* extra; // used by kmcmplib and its consumers; unused in kmcmpdll -}; - -typedef FILE_KEYBOARD *PFILE_KEYBOARD; - -/* - These size values are used in unit tests to ensure - that the structure sizes correspond precisely across - compilers (pas and c++). -*/ - -const DWORD sz_FILE_STORE = sizeof(FILE_STORE); -const DWORD sz_FILE_KEY = sizeof(FILE_KEY); -const DWORD sz_FILE_GROUP = sizeof(FILE_GROUP); -const DWORD sz_FILE_DEADKEY = sizeof(FILE_DEADKEY); -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; -}; - -typedef COMPMSG *PCOMPMSG; - -struct COMPILEMESSAGES { - int nMessages; - int nErrors; - - PCOMPMSG cm; - - DWORD fatalCode; - char szFatalText[SZMAX_ERRORTEXT]; - - DWORD currentLine; -}; - -typedef COMPILEMESSAGES *PCOMPILEMESSAGES; - -/* -struct TVersion -{ - //int MinVersion; // 0x0500 usually - //int CompilerVersion[4]; - //int MinCompilerVersion[4]; - int KeyboardVersion; // 0x0500 usually -}; - -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)) -*/ - -#endif // _COMPFILE_H diff --git a/developer/src/kmcmpdll/debugstore.h b/developer/src/kmcmpdll/debugstore.h deleted file mode 100644 index 5d5e8c2e3b3..00000000000 --- a/developer/src/kmcmpdll/debugstore.h +++ /dev/null @@ -1,20 +0,0 @@ - -#ifndef DEBUGSTORE_H -#define DEBUGSTORE_H - -#define DEBUGSTORE_BEGIN L"B" -#define DEBUGSTORE_BEGIN_C L'B' - -#define DEBUGSTORE_MATCH L"M" -#define DEBUGSTORE_MATCH_C L'M' - -#define DEBUGSTORE_NOMATCH L"N" -#define DEBUGSTORE_NOMATCH_C L'N' - -#define DEBUGSTORE_GROUP L"G" -#define DEBUGSTORE_GROUP_C L'G' - -#define DEBUGSTORE_DEADKEY L"D" -#define DEBUGSTORE_DEADKEY_C L'D' - -#endif /* DEBUGSTORE_H */ diff --git a/developer/src/kmcmpdll/json-validation.cpp b/developer/src/kmcmpdll/json-validation.cpp deleted file mode 100644 index c3c1af04843..00000000000 --- a/developer/src/kmcmpdll/json-validation.cpp +++ /dev/null @@ -1,83 +0,0 @@ - -#include -#include - -#include - -typedef bool (*kmcmp_ValidateJsonMessageProc)(int64_t offset, const char* szText, void* context); - -using nlohmann::json; -using nlohmann::json_uri; -using nlohmann::json_schema_draft4::json_validator; - -typedef BOOL (CALLBACK *ValidateJsonMessageProc)(INT64 offset, const char* szText); - -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()); - - try { - lf >> schema; - } - catch (std::exception &e) { - throw e; - } -} - -bool kmcmpMessageProc(int64_t offset, const char* szText, void* context) { - return ((ValidateJsonMessageProc)context)(offset, szText); -} - -extern "C" BOOL __declspec(dllexport) ValidateJsonFile(PWSTR pwszSchemaFile, PWSTR pwszJsonFile, ValidateJsonMessageProc MessageProc) { - - std::fstream f(pwszSchemaFile); - if (!f.good()) { - MessageProc(-1, "Schema file could not be loaded."); - 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()); - 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()); - return FALSE; - } - - // 3) do the actual validation of the document - json document; - - std::fstream fd(pwszJsonFile); - if (!fd.good()) { - MessageProc(-3, "Json file could not be loaded."); - return FALSE; - } - - try { - fd >> document; - validator.validate(document); - } - catch (std::exception &e) { - MessageProc(fd.tellp(), e.what()); - return FALSE; - } - - return TRUE; -} \ No newline at end of file diff --git a/developer/src/kmcmpdll/kcframe/kcframe.cpp b/developer/src/kmcmpdll/kcframe/kcframe.cpp deleted file mode 100644 index 22402e52300..00000000000 --- a/developer/src/kmcmpdll/kcframe/kcframe.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - Name: kcframe - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 24 Aug 2015 - - Modified Date: 24 Aug 2015 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 24 Aug 2015 - mcdurdin - I4865 - Add treat hints and warnings as errors into project - - 24 Aug 2015 - mcdurdin - I4866 - Add warn on deprecated features to project and compile - -*/ - -#include -#include - -#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); - return 1; -} - -int main(int argc, char *argv[]) -{ - if (argc == 2 && strcmp(argv[1], "--sizeof") == 0) { - printf("FILE_KEYBOARD_SIZE = %zu\n", sizeof(FILE_KEYBOARD)); - printf("FILE_GROUP_SIZE = %zu\n", sizeof(FILE_GROUP)); - printf("FILE_STORE_SIZE = %zu\n", sizeof(FILE_STORE)); - printf("FILE_KEY_SIZE = %zu\n", sizeof(FILE_KEY)); - printf("FILE_DEADKEY_SIZE = %zu\n", sizeof(FILE_DEADKEY)); - return 0; - } - if(argc < 3) - { - puts("Usage: kcframe infile.kmn outfile.kmx"); - return 1; - } - - return CompileKeyboardFile(argv[1], argv[2], TRUE, FALSE, TRUE, msgproc) ? 0 : 1; -} diff --git a/developer/src/kmcmpdll/kcframe/kcframe.vcxproj b/developer/src/kmcmpdll/kcframe/kcframe.vcxproj deleted file mode 100644 index 24cb578d733..00000000000 --- a/developer/src/kmcmpdll/kcframe/kcframe.vcxproj +++ /dev/null @@ -1,267 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {E92CC897-8228-4728-8110-028088D7A99C} - kcframe - 10.0.17763.0 - - - - Application - false - MultiByte - v141 - - - Application - false - MultiByte - v141 - - - Application - false - MultiByte - v141 - - - Application - false - MultiByte - v141 - - - - - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - false - false - true - true - AllRules.ruleset - AllRules.ruleset - - - - - AllRules.ruleset - AllRules.ruleset - - - - - - - $(ProjectDir)..\bin\$(Platform)\$(Configuration)\ - obj\$(Platform)\$(Configuration)\ - - - $(ProjectDir)..\bin\$(Platform)\$(Configuration)\ - obj\$(Platform)\$(Configuration)\ - - - obj\$(Platform)\$(Configuration)\ - $(ProjectName).x64 - $(ProjectDir)..\bin\$(Platform)\$(Configuration)\ - - - obj\$(Platform)\$(Configuration)\ - $(ProjectName).x64 - $(ProjectDir)..\bin\$(Platform)\$(Configuration)\ - - - - .\Release/kcframe.tlb - - - - - MaxSpeed - OnlyExplicitInline - .;..;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - MultiThreaded - true - .\Release/kcframe.pch - .\Release/ - Level3 - true - true - - - NDEBUG;%(PreprocessorDefinitions) - 0x0c09 - - - true - Console - MachineX86 - true - - - true - .\Release/kcframe.bsc - - - - - .\Release/kcframe.tlb - - - - - MaxSpeed - OnlyExplicitInline - .;..;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - MultiThreaded - true - .\Release/kcframe.pch - .\Release/ - Level3 - true - true - - - NDEBUG;%(PreprocessorDefinitions) - 0x0c09 - - - true - Console - true - - - true - .\Release/kcframe.bsc - - - - - .\Debug/kcframe.tlb - - - - - Disabled - .;..;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - .\Debug/kcframe.pch - .\Debug/ - Level3 - true - EditAndContinue - true - - - _DEBUG;%(PreprocessorDefinitions) - 0x0c09 - - - true - true - Console - MachineX86 - true - - - true - .\Debug/kcframe.bsc - - - - - .\Debug/kcframe.tlb - - - - - Disabled - .;..;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - .\Debug/kcframe.pch - .\Debug/ - Level3 - true - ProgramDatabase - true - - - _DEBUG;%(PreprocessorDefinitions) - 0x0c09 - - - true - true - Console - true - - - true - .\Debug/kcframe.bsc - - - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - - - {7e26fe08-721e-424b-9da0-4a0dca88a86e} - - - - - - \ No newline at end of file diff --git a/developer/src/kmcmpdll/kcframe/kcframe.vcxproj.filters b/developer/src/kmcmpdll/kcframe/kcframe.vcxproj.filters deleted file mode 100644 index 0cd42a560b1..00000000000 --- a/developer/src/kmcmpdll/kcframe/kcframe.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - {52df63e2-6932-4c95-8a20-bc312924cb32} - cpp;c;cxx;rc;def;r;odl;idl;hpj;bat - - - {251970ed-fef3-4dfa-a930-a6a4baf00219} - h;hpp;hxx;hm;inl - - - {91a9b75f-0ab8-4840-92cc-cc9e963b5c67} - ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe - - - - - Source Files - - - \ No newline at end of file diff --git a/developer/src/kmcmpdll/kmcmpdll.h b/developer/src/kmcmpdll/kmcmpdll.h deleted file mode 100644 index d0523391253..00000000000 --- a/developer/src/kmcmpdll/kmcmpdll.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include "compfile.h" - -BOOL AddCompileString(LPSTR buf); -BOOL AddCompileMessage(DWORD msg); - -// TODO: These macros can return FALSE in functions that expect a DWORD CERR_x -// return value type. This is just plain wrong! -#define SetError(err) { if(AddCompileMessage(err) || (err & CERR_FATAL)) return FALSE; } -#define AddWarning(warn) { if(AddCompileMessage(warn)) return FALSE; } - -extern BOOL FWarnDeprecatedCode; -extern int currentLine; -extern char ErrExtra[]; - - -PWSTR strtowstr(PSTR in); -PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, DWORD dwSystemID); diff --git a/developer/src/kmcmpdll/kmcmpdll.sln b/developer/src/kmcmpdll/kmcmpdll.sln deleted file mode 100644 index 2da430e2494..00000000000 --- a/developer/src/kmcmpdll/kmcmpdll.sln +++ /dev/null @@ -1,41 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.705 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kcframe", "kcframe.vcxproj", "{E92CC897-8228-4728-8110-028088D7A99C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmcmpdll", "kmcmpdll.vcxproj", "{7E26FE08-721E-424B-9DA0-4A0DCA88A86E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E92CC897-8228-4728-8110-028088D7A99C}.Debug|Win32.ActiveCfg = Debug|Win32 - {E92CC897-8228-4728-8110-028088D7A99C}.Debug|Win32.Build.0 = Debug|Win32 - {E92CC897-8228-4728-8110-028088D7A99C}.Debug|x64.ActiveCfg = Debug|x64 - {E92CC897-8228-4728-8110-028088D7A99C}.Debug|x64.Build.0 = Debug|x64 - {E92CC897-8228-4728-8110-028088D7A99C}.Release|Win32.ActiveCfg = Release|Win32 - {E92CC897-8228-4728-8110-028088D7A99C}.Release|Win32.Build.0 = Release|Win32 - {E92CC897-8228-4728-8110-028088D7A99C}.Release|x64.ActiveCfg = Release|x64 - {E92CC897-8228-4728-8110-028088D7A99C}.Release|x64.Build.0 = Release|x64 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Debug|Win32.ActiveCfg = Debug|Win32 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Debug|Win32.Build.0 = Debug|Win32 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Debug|x64.ActiveCfg = Debug|x64 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Debug|x64.Build.0 = Debug|x64 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Release|Win32.ActiveCfg = Release|Win32 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Release|Win32.Build.0 = Release|Win32 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Release|x64.ActiveCfg = Release|x64 - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {167A867F-8CD6-4890-90E5-96DF59FFBB8C} - EndGlobalSection -EndGlobal diff --git a/developer/src/kmcmpdll/kmcmpdll.vcxproj b/developer/src/kmcmpdll/kmcmpdll.vcxproj deleted file mode 100644 index 2bcc038bd00..00000000000 --- a/developer/src/kmcmpdll/kmcmpdll.vcxproj +++ /dev/null @@ -1,450 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {7E26FE08-721E-424B-9DA0-4A0DCA88A86E} - kmcmpdll - 10.0 - - - - DynamicLibrary - false - v142 - - - DynamicLibrary - false - v142 - - - DynamicLibrary - false - v142 - - - DynamicLibrary - false - v142 - - - - - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(ProjectDir)bin\$(Platform)\$(Configuration)\ - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - false - false - $(ProjectDir)obj\$(Platform)\$(Configuration)\ - true - true - AllRules.ruleset - AllRules.ruleset - - - - - AllRules.ruleset - AllRules.ruleset - - - - - - - $(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)\ - $(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)\ - $(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 - $(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)\ - $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - ./kmcmpdll.tlb - - - - - MaxSpeed - OnlyExplicitInline - .;$(KEYMAN_ROOT)\windows\src\global\inc;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);JSON_SCHEMA_VALIDATOR_EXPORTS - true - Sync - MultiThreaded - true - All - $(IntDir) - Level3 - true - true - Use - pch.h - - - NDEBUG;%(PreprocessorDefinitions) - 0x0c09 - $(KEYMAN_ROOT)\windows\src\global\inc - - - version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) - true - true - - - Windows - 0x3C100000 - MachineX86 - true - true - - - true - ./kmcmpdll.bsc - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - ./kmcmpdll.tlb - - - - - MaxSpeed - OnlyExplicitInline - .;$(KEYMAN_ROOT)\windows\src\global\inc;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);JSON_SCHEMA_VALIDATOR_EXPORTS - true - Sync - MultiThreaded - true - All - $(IntDir) - Level3 - true - true - Use - pch.h - - - NDEBUG;%(PreprocessorDefinitions) - 0x0c09 - $(KEYMAN_ROOT)\windows\src\global\inc - - - version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) - true - true - Windows - true - true - - - true - ./kmcmpdll.bsc - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - ./kmcmpdll.tlb - - - - - Disabled - .;$(KEYMAN_ROOT)\windows\src\global\inc;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);JSON_SCHEMA_VALIDATOR_EXPORTS - Sync - MultiThreadedDebug - true - Level3 - true - EditAndContinue - true - Use - pch.h - $(IntDir) - - - _DEBUG;%(PreprocessorDefinitions) - 0x0c09 - $(KEYMAN_ROOT)\windows\src\global\inc - - - version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) - true - true - true - Windows - 0x3C100000 - MachineX86 - true - - - true - ./kmcmpdll.bsc - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - ./kmcmpdll.tlb - - - - - Disabled - .;$(KEYMAN_ROOT)\windows\src\global\inc;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);JSON_SCHEMA_VALIDATOR_EXPORTS - Sync - MultiThreadedDebug - $(IntDir) - true - Level3 - true - ProgramDatabase - true - Use - pch.h - - - _DEBUG;%(PreprocessorDefinitions) - 0x0c09 - $(KEYMAN_ROOT)\windows\src\global\inc - - - version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies) - true - true - true - Windows - true - - - true - ./kmcmpdll.bsc - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NotUsing - NotUsing - NotUsing - NotUsing - - - NotUsing - NotUsing - NotUsing - NotUsing - - - NotUsing - NotUsing - NotUsing - NotUsing - - - - - - - - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - NotUsing - NotUsing - NotUsing - NotUsing - - - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - NotUsing - NotUsing - NotUsing - NotUsing - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - Create - Create - Create - Create - - - - - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - - - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - %(PreprocessorDefinitions) - - - - - - - - - \ No newline at end of file diff --git a/developer/src/kmcmpdll/kmcmpdll.vcxproj.filters b/developer/src/kmcmpdll/kmcmpdll.vcxproj.filters deleted file mode 100644 index 4131bedac41..00000000000 --- a/developer/src/kmcmpdll/kmcmpdll.vcxproj.filters +++ /dev/null @@ -1,119 +0,0 @@ - - - - - {dd851de3-1698-40e6-9560-0ef4523ddb79} - h - - - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - Header files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/developer/src/kmcmpdll/pch.cpp b/developer/src/kmcmpdll/pch.cpp deleted file mode 100644 index 17305716aac..00000000000 --- a/developer/src/kmcmpdll/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" \ No newline at end of file diff --git a/developer/src/kmcmpdll/pch.h b/developer/src/kmcmpdll/pch.h deleted file mode 100644 index dcba976ea74..00000000000 --- a/developer/src/kmcmpdll/pch.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 -#endif - -#ifndef STRICT -#define STRICT -#endif - -#include -#include - -#include "../../../common/windows/cpp/include/legacy_kmx_file.h" -#include "../../../common/windows/cpp/include/xstring.h" - -#include "../../../common/windows/cpp/include/registry.h" -#include "../../../common/windows/cpp/include/unicode.h" -#include "../../../common/windows/cpp/include/crc32.h" - -#include -#include - -#include diff --git a/developer/src/kmcmpdll/version.rc b/developer/src/kmcmpdll/version.rc deleted file mode 100644 index b963c10ddb1..00000000000 --- a/developer/src/kmcmpdll/version.rc +++ /dev/null @@ -1,32 +0,0 @@ -#include "../../../common/windows/cpp/include/keymanversion.h" - -1 VERSIONINFO - FILEVERSION KV_FILEVERSION - PRODUCTVERSION KV_PRODUCTVERSION - FILEFLAGSMASK 0x3fL - FILEFLAGS 0x0L - FILEOS 0x4L - FILETYPE 0x2L - FILESUBTYPE 0x0L - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0C0904E4" - BEGIN - VALUE "CompanyName", KV_COMPANY_NAME - VALUE "FileDescription", "Keyman compiler library\0" - VALUE "FileVersion", KV_VERSION_STRING - VALUE "InternalName", "KMCOMP\0" - VALUE "LegalCopyright", KV_LEGAL_COPYRIGHT - VALUE "LegalTrademarks", KV_LEGAL_TRADEMARKS - VALUE "OriginalFilename", "KMCOMP.DLL\0" - VALUE "ProductName", "Keyman Developer\0" - VALUE "ProductVersion", KV_VERSION_STRING - VALUE "Comments", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1252 - END - END diff --git a/developer/src/kmcmpdll/versioning.cpp b/developer/src/kmcmpdll/versioning.cpp deleted file mode 100644 index 1601e955632..00000000000 --- a/developer/src/kmcmpdll/versioning.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "pch.h" -#include -#include -#include - -BOOL CheckKeyboardFinalVersion(PFILE_KEYBOARD fk) { - char buf[128]; - - if (fk->dwFlags & KF_AUTOMATICVERSION) { - if (fk->version <= 0) { - fk->version = VERSION_60; // minimum version that we can be safe with - } - - wsprintf(buf, "The compiler has assigned a minimum engine version of %d.%d based on features used in this keyboard", (int)((fk->version & 0xFF00) >> 8), (int)(fk->version & 0xFF)); - AddCompileString(buf); - } - - return TRUE; -} - -BOOL VerifyKeyboardVersion(PFILE_KEYBOARD fk, DWORD ver) { - if (fk->dwFlags & KF_AUTOMATICVERSION) { - fk->version = max(fk->version, ver); - return TRUE; - } - - return fk->version >= ver; -} diff --git a/developer/src/kmcmpdll/versioning.h b/developer/src/kmcmpdll/versioning.h deleted file mode 100644 index 6e4c0c986f2..00000000000 --- a/developer/src/kmcmpdll/versioning.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include - -#define VERIFY_KEYBOARD_VERSION(fk, ver, err) { \ - if(!VerifyKeyboardVersion((fk), (ver))) \ - return (err); \ -} - -BOOL CheckKeyboardFinalVersion(PFILE_KEYBOARD fk); -BOOL VerifyKeyboardVersion(PFILE_KEYBOARD fk, DWORD ver); \ No newline at end of file diff --git a/developer/src/kmcmpdll/virtualcharkeys.cpp b/developer/src/kmcmpdll/virtualcharkeys.cpp deleted file mode 100644 index 3b50ab957e8..00000000000 --- a/developer/src/kmcmpdll/virtualcharkeys.cpp +++ /dev/null @@ -1,274 +0,0 @@ -#include "pch.h" -#include "virtualcharkeys.h" - -BOOL VKeyMayBeVCKey[256] = { - FALSE, // L"K_?00", // &H0 - FALSE, // L"K_LBUTTON", // &H1 - FALSE, // L"K_RBUTTON", // &H2 - FALSE, // L"K_CANCEL", // &H3 - FALSE, // L"K_MBUTTON", // &H4 - FALSE, // L"K_?05", // &H5 - FALSE, // L"K_?06", // &H6 - FALSE, // L"K_?07", // &H7 - FALSE, // L"K_BKSP", // &H8 - FALSE, // L"K_TAB", // &H9 - FALSE, // L"K_?0A", // &HA - FALSE, // L"K_?0B", // &HB - FALSE, // L"K_KP5", // &HC - FALSE, // L"K_ENTER", // &HD - FALSE, // L"K_?0E", // &HE - FALSE, // L"K_?0F", // &HF - FALSE, // L"K_SHIFT", // &H10 - FALSE, // L"K_CONTROL", // &H11 - FALSE, // L"K_ALT", // &H12 - FALSE, // L"K_PAUSE", // &H13 - FALSE, // L"K_CAPS", // &H14 - FALSE, // L"K_KANJI?15", // &H15 - FALSE, // L"K_KANJI?16", // &H16 - FALSE, // L"K_KANJI?17", // &H17 - FALSE, // L"K_KANJI?18", // &H18 - FALSE, // L"K_KANJI?19", // &H19 - FALSE, // L"K_?1A", // &H1A - FALSE, // L"K_ESC", // &H1B - FALSE, // L"K_KANJI?1C", // &H1C - FALSE, // L"K_KANJI?1D", // &H1D - FALSE, // L"K_KANJI?1E", // &H1E - FALSE, // L"K_KANJI?1F", // &H1F - TRUE, // L"K_SPACE", // &H20 - FALSE, // L"K_PGUP", // &H21 - FALSE, // L"K_PGDN", // &H22 - FALSE, // L"K_END", // &H23 - FALSE, // L"K_HOME", // &H24 - FALSE, // L"K_LEFT", // &H25 - FALSE, // L"K_UP", // &H26 - FALSE, // L"K_RIGHT", // &H27 - FALSE, // L"K_DOWN", // &H28 - FALSE, // L"K_SEL", // &H29 - FALSE, // L"K_PRINT", // &H2A - FALSE, // L"K_EXEC", // &H2B - FALSE, // L"K_PRTSCN", // &H2C - FALSE, // L"K_INS", // &H2D - FALSE, // L"K_DEL", // &H2E - FALSE, // L"K_HELP", // &H2F - TRUE, // L"K_0", // &H30 - TRUE, // L"K_1", // &H31 - TRUE, // L"K_2", // &H32 - TRUE, // L"K_3", // &H33 - TRUE, // L"K_4", // &H34 - TRUE, // L"K_5", // &H35 - TRUE, // L"K_6", // &H36 - TRUE, // L"K_7", // &H37 - TRUE, // L"K_8", // &H38 - TRUE, // L"K_9", // &H39 - FALSE, // L"K_?3A", // &H3A - FALSE, // L"K_?3B", // &H3B - FALSE, // L"K_?3C", // &H3C - FALSE, // L"K_?3D", // &H3D - FALSE, // L"K_?3E", // &H3E - FALSE, // L"K_?3F", // &H3F - FALSE, // L"K_?40", // &H40 - - TRUE, // L"K_A", // &H41 - TRUE, // L"K_B", // &H42 - TRUE, // L"K_C", // &H43 - TRUE, // L"K_D", // &H44 - TRUE, // L"K_E", // &H45 - TRUE, // L"K_F", // &H46 - TRUE, // L"K_G", // &H47 - TRUE, // L"K_H", // &H48 - TRUE, // L"K_I", // &H49 - TRUE, // L"K_J", // &H4A - TRUE, // L"K_K", // &H4B - TRUE, // L"K_L", // &H4C - TRUE, // L"K_M", // &H4D - TRUE, // L"K_N", // &H4E - TRUE, // L"K_O", // &H4F - TRUE, // L"K_P", // &H50 - TRUE, // L"K_Q", // &H51 - TRUE, // L"K_R", // &H52 - TRUE, // L"K_S", // &H53 - TRUE, // L"K_T", // &H54 - TRUE, // L"K_U", // &H55 - TRUE, // L"K_V", // &H56 - TRUE, // L"K_W", // &H57 - TRUE, // L"K_X", // &H58 - TRUE, // L"K_Y", // &H59 - TRUE, // L"K_Z", // &H5A - FALSE, // L"K_?5B", // &H5B - FALSE, // L"K_?5C", // &H5C - FALSE, // L"K_?5D", // &H5D - FALSE, // L"K_?5E", // &H5E - FALSE, // L"K_?5F", // &H5F - FALSE, // L"K_NP0", // &H60 - FALSE, // L"K_NP1", // &H61 - FALSE, // L"K_NP2", // &H62 - FALSE, // L"K_NP3", // &H63 - FALSE, // L"K_NP4", // &H64 - FALSE, // L"K_NP5", // &H65 - FALSE, // L"K_NP6", // &H66 - FALSE, // L"K_NP7", // &H67 - FALSE, // L"K_NP8", // &H68 - FALSE, // L"K_NP9", // &H69 - FALSE, // L"K_NPSTAR", // &H6A - FALSE, // L"K_NPPLUS", // &H6B - FALSE, // L"K_SEPARATOR", // &H6C - FALSE, // L"K_NPMINUS", // &H6D - FALSE, // L"K_NPDOT", // &H6E - FALSE, // L"K_NPSLASH", // &H6F - FALSE, // L"K_F1", // &H70 - FALSE, // L"K_F2", // &H71 - FALSE, // L"K_F3", // &H72 - FALSE, // L"K_F4", // &H73 - FALSE, // L"K_F5", // &H74 - FALSE, // L"K_F6", // &H75 - FALSE, // L"K_F7", // &H76 - FALSE, // L"K_F8", // &H77 - FALSE, // L"K_F9", // &H78 - FALSE, // L"K_F10", // &H79 - FALSE, // L"K_F11", // &H7A - FALSE, // L"K_F12", // &H7B - FALSE, // L"K_F13", // &H7C - FALSE, // L"K_F14", // &H7D - FALSE, // L"K_F15", // &H7E - FALSE, // L"K_F16", // &H7F - FALSE, // L"K_F17", // &H80 - FALSE, // L"K_F18", // &H81 - FALSE, // L"K_F19", // &H82 - FALSE, // L"K_F20", // &H83 - FALSE, // L"K_F21", // &H84 - FALSE, // L"K_F22", // &H85 - FALSE, // L"K_F23", // &H86 - FALSE, // L"K_F24", // &H87 - - FALSE, // L"K_?88", // &H88 - FALSE, // L"K_?89", // &H89 - FALSE, // L"K_?8A", // &H8A - FALSE, // L"K_?8B", // &H8B - FALSE, // L"K_?8C", // &H8C - FALSE, // L"K_?8D", // &H8D - FALSE, // L"K_?8E", // &H8E - FALSE, // L"K_?8F", // &H8F - - FALSE, // L"K_NUMLOCK", // &H90 - FALSE, // L"K_SCROLL", // &H91 - - FALSE, // L"K_?92", // &H92 - FALSE, // L"K_?93", // &H93 - FALSE, // L"K_?94", // &H94 - FALSE, // L"K_?95", // &H95 - FALSE, // L"K_?96", // &H96 - FALSE, // L"K_?97", // &H97 - FALSE, // L"K_?98", // &H98 - FALSE, // L"K_?99", // &H99 - FALSE, // L"K_?9A", // &H9A - FALSE, // L"K_?9B", // &H9B - FALSE, // L"K_?9C", // &H9C - FALSE, // L"K_?9D", // &H9D - FALSE, // L"K_?9E", // &H9E - FALSE, // L"K_?9F", // &H9F - FALSE, // L"K_?A0", // &HA0 - FALSE, // L"K_?A1", // &HA1 - FALSE, // L"K_?A2", // &HA2 - FALSE, // L"K_?A3", // &HA3 - FALSE, // L"K_?A4", // &HA4 - FALSE, // L"K_?A5", // &HA5 - FALSE, // L"K_?A6", // &HA6 - FALSE, // L"K_?A7", // &HA7 - FALSE, // L"K_?A8", // &HA8 - FALSE, // L"K_?A9", // &HA9 - FALSE, // L"K_?AA", // &HAA - FALSE, // L"K_?AB", // &HAB - FALSE, // L"K_?AC", // &HAC - FALSE, // L"K_?AD", // &HAD - FALSE, // L"K_?AE", // &HAE - FALSE, // L"K_?AF", // &HAF - FALSE, // L"K_?B0", // &HB0 - FALSE, // L"K_?B1", // &HB1 - FALSE, // L"K_?B2", // &HB2 - FALSE, // L"K_?B3", // &HB3 - FALSE, // L"K_?B4", // &HB4 - FALSE, // L"K_?B5", // &HB5 - FALSE, // L"K_?B6", // &HB6 - FALSE, // L"K_?B7", // &HB7 - FALSE, // L"K_?B8", // &HB8 - FALSE, // L"K_?B9", // &HB9 - - TRUE, // L"K_COLON", // &HBA - TRUE, // L"K_EQUAL", // &HBB - TRUE, // L"K_COMMA", // &HBC - TRUE, // L"K_HYPHEN", // &HBD - TRUE, // L"K_PERIOD", // &HBE - TRUE, // L"K_SLASH", // &HBF - TRUE, // L"K_BKQUOTE", // &HC0 - - TRUE, // L"K_?C1", // &HC1 -- 103rd key on Brazilian - /? - FALSE, // L"K_?C2", // &HC2 -- 104th key on Brazilian - numpad . - FALSE, // L"K_?C3", // &HC3 - FALSE, // L"K_?C4", // &HC4 - FALSE, // L"K_?C5", // &HC5 - FALSE, // L"K_?C6", // &HC6 - FALSE, // L"K_?C7", // &HC7 - FALSE, // L"K_?C8", // &HC8 - FALSE, // L"K_?C9", // &HC9 - FALSE, // L"K_?CA", // &HCA - FALSE, // L"K_?CB", // &HCB - FALSE, // L"K_?CC", // &HCC - FALSE, // L"K_?CD", // &HCD - FALSE, // L"K_?CE", // &HCE - FALSE, // L"K_?CF", // &HCF - FALSE, // L"K_?D0", // &HD0 - FALSE, // L"K_?D1", // &HD1 - FALSE, // L"K_?D2", // &HD2 - FALSE, // L"K_?D3", // &HD3 - FALSE, // L"K_?D4", // &HD4 - FALSE, // L"K_?D5", // &HD5 - FALSE, // L"K_?D6", // &HD6 - FALSE, // L"K_?D7", // &HD7 - FALSE, // L"K_?D8", // &HD8 - FALSE, // L"K_?D9", // &HD9 - FALSE, // L"K_?DA", // &HDA - - TRUE, // L"K_LBRKT", // &HDB - TRUE, // L"K_BKSLASH", // &HDC - TRUE, // L"K_RBRKT", // &HDD - TRUE, // L"K_QUOTE", // &HDE - TRUE, // L"K_oDF", // &HDF - TRUE, // L"K_oE0", // &HE0 - TRUE, // L"K_oE1", // &HE1 - TRUE, // L"K_oE2", // &HE2 - TRUE, // L"K_oE3", // &HE3 - TRUE, // L"K_oE4", // &HE4 - - FALSE, // L"K_?E5", // &HE5 - - TRUE, // L"K_oE6", // &HE6 - - FALSE, // L"K_?E7", // &HE7 - FALSE, // L"K_?E8", // &HE8 - - TRUE, // L"K_oE9", // &HE9 - TRUE, // L"K_oEA", // &HEA - TRUE, // L"K_oEB", // &HEB - TRUE, // L"K_oEC", // &HEC - TRUE, // L"K_oED", // &HED - TRUE, // L"K_oEE", // &HEE - TRUE, // L"K_oEF", // &HEF - TRUE, // L"K_oF0", // &HF0 - TRUE, // L"K_oF1", // &HF1 - TRUE, // L"K_oF2", // &HF2 - TRUE, // L"K_oF3", // &HF3 - TRUE, // L"K_oF4", // &HF4 - TRUE, // L"K_oF5", // &HF5 - - FALSE, // L"K_?F6", // &HF6 - FALSE, // L"K_?F7", // &HF7 - FALSE, // L"K_?F8", // &HF8 - FALSE, // L"K_?F9", // &HF9 - FALSE, // L"K_?FA", // &HFA - FALSE, // L"K_?FB", // &HFB - FALSE, // L"K_?FC", // &HFC - FALSE, // L"K_?FD", // &HFD - FALSE, // L"K_?FE", // &HFE - FALSE, // L"K_?FF" // &HFF - }; - diff --git a/developer/src/kmcmpdll/virtualcharkeys.h b/developer/src/kmcmpdll/virtualcharkeys.h deleted file mode 100644 index fbb724781ff..00000000000 --- a/developer/src/kmcmpdll/virtualcharkeys.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _VIRTUALCHARKEYS_H -#define _VIRTUALCHARKEYS_H - -#include - -extern BOOL VKeyMayBeVCKey[256]; - -#endif - diff --git a/developer/src/kmcmpdll/xstring.cpp b/developer/src/kmcmpdll/xstring.cpp deleted file mode 100644 index 471878d6275..00000000000 --- a/developer/src/kmcmpdll/xstring.cpp +++ /dev/null @@ -1,48 +0,0 @@ - -class XString -{ -friend class XStringPointer; -private: - int RefCount; /* If RefCount > 0 when ~XString called, log an error */ - WCHAR *buf; /* Buffer for XString */ - int nbuf; /* Length of buf in bytes */ -public: - XString(); /* New, empty XString */ - XString(PWSTR FInitWStr); /* New, copy from FInitWStr */ - XString(XString *FInitXStr); /* New, copy from FInitXStr */ - ~XString(); - int Length(); /* Length in characters */ - int WordLength() { return nbuf; } /* Length in WORDs */ - - void Append(XString *FAppendXStr); - void Append(WCHAR *FAppendWStr); - void Append(WCHAR FAppendChar); - void Delete(int Position); /* Delete the character at Position */ - void Delete(int Position, int Length); /* Delete the character at Position to Pos+Len-1 */ -}; - -class XStringPointer -{ -private: - XString str; - WCHAR *p; - -public: - XStringPointer(XString FParent); - ~XStringPointer(); - - operator++(); - operator--(); - WCHAR operator[](int n); - WCHAR *cp() { return p; } - BOOL IsSurrogate(); - BOOL IsFunction(); - BOOL IsFunction(int FunctionID); - int Position(); /* Returns the current character position */ - int WordPosition(); /* Returns the current word position */ - void Insert(XString *FInsertXStr); /* Insert a character before pointer position */ - void Insert(WCHAR *FInsertWStr); - void Insert(WCHAR FInsertChar); - void Delete(); /* Delete the current character */ - void Delete(int n); /* Delete the current character + n-1 chrs */ -}; diff --git a/developer/src/kmcmplib/Makefile b/developer/src/kmcmplib/Makefile index 4acc7d3cfbf..26aed7fdc2c 100644 --- a/developer/src/kmcmplib/Makefile +++ b/developer/src/kmcmplib/Makefile @@ -28,7 +28,7 @@ wrap-symbols: @rem Not required test-manifest: - @rem This target needed as dependency for TIKE and KMCMPDLL + @rem This target needed as dependency for TIKE test: $(CALLER) ./build.sh test $(BUILD_DEBUG) diff --git a/developer/src/kmcmplib/src/Compiler.cpp b/developer/src/kmcmplib/src/Compiler.cpp index 02990bc3248..3634d5d5d8b 100644 --- a/developer/src/kmcmplib/src/Compiler.cpp +++ b/developer/src/kmcmplib/src/Compiler.cpp @@ -1029,6 +1029,8 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE else if (u16ncmp(p, u"10.0", 4) == 0) fk->version = VERSION_100; else if (u16ncmp(p, u"14.0", 4) == 0) fk->version = VERSION_140; // Adds support for #917 -- context() with notany() for KeymanWeb else if (u16ncmp(p, u"15.0", 4) == 0) fk->version = VERSION_150; // Adds support for U_xxxx_yyyy #2858 + else if (u16ncmp(p, u"16.0", 4) == 0) fk->version = VERSION_160; // KMXPlus + else if (u16ncmp(p, u"17.0", 4) == 0) fk->version = VERSION_170; // Flicks and gestures else return CERR_InvalidVersion; @@ -1058,7 +1060,7 @@ KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE u16ncpy(q, pp2, u16len(pp2) + 1); // Change compiled reference file extension to .kvk - pp2 = ( km_kbp_cp *) u16chr(q, 0) - 5; + pp2 = ( km_core_cp *) u16chr(q, 0) - 5; if (pp2 > q && u16icmp(pp2, u".kvks") == 0) { pp2[4] = 0; } diff --git a/developer/src/kmcmplib/src/version.rc b/developer/src/kmcmplib/src/version.rc index 685b3ff8e4b..2ceffd05acd 100644 --- a/developer/src/kmcmplib/src/version.rc +++ b/developer/src/kmcmplib/src/version.rc @@ -16,10 +16,10 @@ VALUE "CompanyName", KV_COMPANY_NAME VALUE "FileDescription", "Keyman compiler library\0" VALUE "FileVersion", KV_VERSION_STRING - VALUE "InternalName", "KMCOMP\0" + VALUE "InternalName", "KMCMPLIB\0" VALUE "LegalCopyright", KV_LEGAL_COPYRIGHT VALUE "LegalTrademarks", KV_LEGAL_TRADEMARKS - VALUE "OriginalFilename", "KMCOMP.DLL\0" + VALUE "OriginalFilename", "KMCMPLIB.DLL\0" VALUE "ProductName", "Keyman Developer\0" VALUE "ProductVersion", KV_VERSION_STRING VALUE "Comments", "\0" diff --git a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/compile_legacy.bat b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/compile_legacy.bat index 5e8b891ea6c..e21f85a4bca 100644 --- a/developer/src/kmcmplib/tests/fixtures/valid-keyboards/compile_legacy.bat +++ b/developer/src/kmcmplib/tests/fixtures/valid-keyboards/compile_legacy.bat @@ -1,4 +1,7 @@ @echo off echo Compiles the keyboards using the legacy kmcomp.exe echo to use as baseline comparisons for kmcmplib +rem TODO: we'll need a binary download for kmcomp.exe +rem We can use the last version available on downloads.keyman.com +rem see keyboards repo for example of best way to download+extract for %%d in (*.kmn) do ..\..\..\..\..\bin\kmcomp -no-compiler-version -d %%d \ No newline at end of file diff --git a/developer/src/kmcmplib/version.rc b/developer/src/kmcmplib/version.rc index b963c10ddb1..04811532c4f 100644 --- a/developer/src/kmcmplib/version.rc +++ b/developer/src/kmcmplib/version.rc @@ -16,10 +16,10 @@ VALUE "CompanyName", KV_COMPANY_NAME VALUE "FileDescription", "Keyman compiler library\0" VALUE "FileVersion", KV_VERSION_STRING - VALUE "InternalName", "KMCOMP\0" + VALUE "InternalName", "KMCMPLIB\0" VALUE "LegalCopyright", KV_LEGAL_COPYRIGHT VALUE "LegalTrademarks", KV_LEGAL_TRADEMARKS - VALUE "OriginalFilename", "KMCOMP.DLL\0" + VALUE "OriginalFilename", "KMCMPLIB.DLL\0" VALUE "ProductName", "Keyman Developer\0" VALUE "ProductVersion", KV_VERSION_STRING VALUE "Comments", "\0" diff --git a/developer/src/kmcomp/Keyman.Developer.System.Project.ProjectLogConsole.pas b/developer/src/kmcomp/Keyman.Developer.System.Project.ProjectLogConsole.pas deleted file mode 100644 index 9961aa25ba7..00000000000 --- a/developer/src/kmcomp/Keyman.Developer.System.Project.ProjectLogConsole.pas +++ /dev/null @@ -1,193 +0,0 @@ -unit Keyman.Developer.System.Project.ProjectLogConsole; - -interface - -uses - Keyman.Developer.System.Project.ProjectLog; - -type - TProjectLogConsole = class - strict private - class var FInstance: TProjectLogConsole; - private - hConsole: THandle; - hOutFile: THandle; - FFullySilent: Boolean; - FSilent: Boolean; - FFilename: string; - FHasWarning: Boolean; - FMessageCount: Integer; - FColor: Boolean; - procedure DetectColorMode; - public - type TColorMode = (cmDefault, cmForceColor, cmForceNoColor); - public - constructor Create(ASilent, AFullySilent: Boolean; AhOutFile: THandle; AColorMode: TColorMode); - procedure Log(AState: TProjectLogState; Filename: string; Msg: string; MsgCode, Line: Integer); overload; - procedure Log(AState: TProjectLogState; Msg: string; MsgCode, Line: Integer); overload; - class property Instance: TProjectLogConsole read FInstance; - property Filename: string read FFilename write FFilename; - property HasWarning: Boolean read FHasWarning; - end; - -function CompilerMessage(line: Integer; msgcode: LongWord; text: PAnsiChar): Integer; stdcall; // I3310 -function CompilerMessageW( line: Integer; msgcode: LongWord; const text: string): Integer; -implementation - -uses - System.SysUtils, - Winapi.Windows, - - compile; - -const - MAX_MESSAGES = 100; - -{ TProjectLogConsole } - -procedure TProjectLogConsole.Log(AState: TProjectLogState; Filename, - Msg: string; MsgCode, Line: Integer); -var - dw: DWord; - str: string; - astr: RawByteString; -const - nlstr: array[0..2] of ansichar = (#$D, #$A, #$0); // I3310 - -const - ESC=#$1b; - ESC_BRIGHT_YELLOW=ESC+'[38;2;255;255;0m'; - ESC_RED=ESC+'[38;2;255;0;0m'; - ESC_GREEN=ESC+'[38;2;0;255;0m'; - ESC_DEFAULT=ESC+'[0m'; -begin - // TODO: Colour - if (AState = plsInfo) and FSilent then Exit; - - if AState = plsWarning then - FHasWarning := True; - - if (AState in [plsWarning, plsError, plsSuccess, plsFailure]) and FFullySilent then Exit; - - str := ''; - - if AState in [plsWarning, plsError] then - begin - Inc(FMessageCount); - if FMessageCount > MAX_MESSAGES then - Exit; - - if FMessageCount = MAX_MESSAGES then - str := Format('More than %d warnings or errors received; suppressing further messages', [MAX_MESSAGES]); - end; - - if str = '' then - - str := TProjectLog.FormatMessage(AState, Filename, msg, msgcode, line); - - if hOutfile <> 0 then - begin - astr := UTF8Encode(str); - WriteFile(hOutfile, astr, Length(astr), dw, nil); - WriteFile(hOutfile, nlstr, 2, dw, nil); - end - else - begin - if FColor then - begin - case AState of - plsInfo: write(ESC_DEFAULT); - plsWarning: write(ESC_BRIGHT_YELLOW); - plsSuccess: write(ESC_GREEN); - plsFailure, - plsFatal, - plsError: write(ESC_RED); - else write(ESC_DEFAULT); - end; - end; - writeln(str); - if FColor then - write(ESC_DEFAULT); - end; -end; - -procedure TProjectLogConsole.Log(AState: TProjectLogState; Msg: string; MsgCode, Line: Integer); -begin - Log(AState, FFilename, Msg, MsgCode, Line); -end; - -{ TLogger } - -constructor TProjectLogConsole.Create(ASilent, AFullySilent: Boolean; AhOutFile: THandle; AColorMode: TColorMode); -begin - Assert(FInstance = nil); - FInstance := Self; - inherited Create; - FSilent := ASilent; - FFullySilent := AFullySilent; - hOutFile := AhOutFile; - - case AColorMode of - cmDefault: DetectColorMode; - cmForceColor: FColor := True; - cmForceNoColor: FColor := False; - end; -end; - -procedure TProjectLogConsole.DetectColorMode; -var - mode: DWORD; -const - ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4; -begin - mode := 0; - hConsole := GetStdHandle(STD_OUTPUT_HANDLE); - if hConsole = INVALID_HANDLE_VALUE then - begin - writeln(Format('GetStdHandle failed with %d %s', [GetLastError, SysErrorMessage(GetLastError)])); - Exit; - end; - - if GetEnvironmentVariable('MSYSTEM') = 'MINGW64' then - begin - // MinGW64 test - // Use colour mode only with a non-redirected console. This test fails with pipes. - // For pipe use, explicitly use -no-color parameter - FColor := GetFileType(hConsole) = 3; - end - else - begin - // Win32 console color mode test - if not GetConsoleMode(hConsole, mode) then - Exit; - - mode := mode or ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if not SetConsoleMode(hConsole, mode) then - Exit; - - FColor := True; - end; -end; - -function CompilerMessageW( line: Integer; msgcode: LongWord; const text: string): Integer; -var - state: TProjectLogState; -begin - if (msgcode = CWARN_Info) then state := plsInfo - else if (msgcode and CERR_ERROR) <> 0 then state := plsError - else if (msgcode and CERR_WARNING) <> 0 then begin state := plsWarning; end // I4706 - else if (msgcode and CERR_FATAL) <> 0 then state := plsFatal - else if (msgcode and CERR_HINT) <> 0 then state := plsHint - else state := plsFatal; - - TProjectLogConsole.Instance.Log(state, text, msgcode, line); - - Result := 1; -end; - -function CompilerMessage(line: Integer; msgcode: LongWord; text: PAnsiChar): Integer; stdcall; // I3310 -begin - Result := CompilerMessageW(line, msgcode, string(AnsiString(text))); -end; - -end. diff --git a/developer/src/kmcomp/Keyman.Developer.System.ValidateRepoChanges.pas b/developer/src/kmcomp/Keyman.Developer.System.ValidateRepoChanges.pas deleted file mode 100644 index 8a5e8443828..00000000000 --- a/developer/src/kmcomp/Keyman.Developer.System.ValidateRepoChanges.pas +++ /dev/null @@ -1,463 +0,0 @@ -unit Keyman.Developer.System.ValidateRepoChanges; - -interface - -uses - System.Classes, - System.JSON, - - kpsfile; - -type - TValidateRepoChanges = class - private - class var err_old, err_new: TStringList; - class var root: string; - class function Search(path, operation: string): Boolean; static; - class function CheckKeyboardInfo(name: string): Boolean; static; - class function CheckPackage(name: string): Boolean; static; - class function LoadKeyboardInfoFile(filename: string): TJSONObject; static; - class function GetLanguageCodesFromJson(root: TJSONObject; - langs: TStringList): Boolean; static; - class function GetLanguageCodesFromKps(kps: TKPSFile; - langs: TStringList): Boolean; static; - class function CompareKeyboardInfoScripts(name: string): Boolean; static; - public - class function Execute(path, operation: string): Boolean; - end; - -implementation - -uses - System.Character, - System.Generics.Collections, - System.SysUtils, - - BCP47Tag, - Keyman.System.KeyboardInfoFile, - Keyman.System.CanonicalLanguageCodeUtils, - Keyman.System.Standards.LangTagsRegistry, - TempFileManager, - Unicode, - utilexecute; - -{ TValidateRepoChanges } - -class function TValidateRepoChanges.Execute(path, operation: string): Boolean; -begin - if path = '' then path := 'c:\projects\keyman\keyboards'; - - path := IncludeTrailingPathDelimiter(path); - - root := path; - -// Result := CheckKeyboardInfo(path + 'release\k\kayan\kayan.keyboard_info'); - - err_old := TStringList.Create; - err_new := TStringList.Create; - try - Result := Search(path, operation); - err_old.SaveToFile('repo-check.old.txt'); - err_new.SaveToFile('repo-check.new.txt'); - finally - err_old.Free; - err_new.Free; - end; -end; - -class function TValidateRepoChanges.Search(path, operation: string): Boolean; -var - f: TSearchRec; -begin - if FindFirst(path + '*.keyboard_info', 0, f) = 0 then - begin - repeat - if operation = 'compare-bcp47-revisions' then CheckKeyboardInfo(path + f.Name) - else if operation = 'compare-bcp47-scripts' then CompareKeyboardInfoScripts(path + f.Name); - until FindNext(f) <> 0; - FindClose(f); - end; - if FindFirst(path + '*.kps', 0, f) = 0 then - begin - repeat - if operation = 'compare-bcp47-revisions' then CheckPackage(path + f.Name); - until FindNext(f) <> 0; - FindClose(f); - end; - if FindFirst(path + '*', faDirectory, f) = 0 then - begin - repeat - if ((f.Attr and faDirectory) = faDirectory) and (f.Name <> '.') and (f.Name <> '..') and not SameText(f.Name, 'build') then - Search(path + f.Name + '\', operation); - until FindNext(f) <> 0; - FindClose(f); - end; - - Result := True; -end; - -class function TValidateRepoChanges.LoadKeyboardInfoFile(filename: string): TJSONObject; -begin - with TStringStream.Create('', TEncoding.UTF8) do - try - LoadFromFile(filename); - Result := TJSONObject.ParseJsonValue(DataString) as TJSONObject; - finally - Free; - end; -end; - -class function TValidateRepoChanges.CheckKeyboardInfo(name: string): Boolean; -var - i: Integer; - json_new, json_old: TJSONObject; -// t: TTempFile; - relpath: string; - content: string; - ec: Integer; - lang_new: TStringList; - lang_old: TStringList; - langc_old: TArray; - langc_new: TArray; - found_error: Boolean; - j: Integer; - langc_old_prev: string; -begin - found_error := False; - relpath := name.Substring(root.Length).Replace('\', '/'); - - json_new := LoadKeyboardInfoFile(name); - if not Assigned(json_new) then - raise Exception.Create('Unable to load file '+name); - // Get previous revision from git - -// t := TTempFileManager.Get('.keyboard_info'); - - if not TUtilExecute.Console('git show "HEAD:'+relpath+'"', root, content, ec) then - RaiseLastOSError; - - if ec <> 0 then - raise Exception.Create('Unable to execute git show for '+name); - - if Copy(content, 1, 3) = string(UTF8Signature) then - Delete(content, 1, 3); - - json_old := TJSONObject.ParseJsonValue(content) as TJSONObject; - if not Assigned(json_old) then - raise Exception.Create('Unable to load old file '+name); - - lang_new := TStringList.Create; - lang_old := TStringList.Create; - try - if not GetLanguageCodesFromJson(json_new, lang_new) then - raise Exception.Create('Unable to get Language codes'); - if not GetLanguageCodesFromJson(json_old, lang_old) then - raise Exception.Create('Unable to get Language codes'); - - j := 0; - for i := 0 to lang_new.Count - 1 do - begin - repeat - if j >= lang_old.Count then - begin - if not found_error then - begin - writeln('Checking '+relpath); - found_error := True; - end; - writeln(' Mismatch in number of language codes'); - break; - end; - - langc_new := lang_new[i].Split([',']); - langc_old := lang_old[j].Split([',']); - Inc(j); - until langc_old[0] <> langc_old_prev; - langc_old_prev := langc_old[0]; - if j >= lang_old.Count then Break; - - if langc_new[0] <> langc_old[0] then - begin - if not found_error then - begin - writeln('Checking '+relpath); - found_error := True; - end; - writeln(Format(' New code %s [%s] does not match old code %s [%s]', [langc_new[1], langc_new[0], langc_old[1], langc_old[0]])); - end; - end; - - if found_error then - begin - err_old.Add('Checking '+relpath); - for i := 0 to lang_old.Count - 1 do - begin - langc_old := lang_old[i].Split([',']); - err_old.Add(Format(' %s [%s]', [langc_old[1], langc_old[0]])); - end; - err_old.Add(''); - - err_new.Add('Checking '+relpath); - for i := 0 to lang_new.Count - 1 do - begin - langc_new := lang_new[i].Split([',']); - err_new.Add(Format(' %s [%s]', [langc_new[1], langc_new[0]])); - end; - err_new.Add(''); - end; - finally - lang_new.Free; - lang_old.Free; - end; - - Result := True; -end; - -class function TValidateRepoChanges.GetLanguageCodesFromJson(root: TJSONObject; langs: TStringList): Boolean; -var - i: Integer; - a: TJSONArray; - - procedure AddLang(lang: string); - begin - langs.Add(TCanonicalLanguageCodeUtils.FindBestTag(lang, False, False)+','+lang); - end; - -begin - if root.Values[TKeyboardInfoFile.SLanguages] = nil then - Exit(False); - - if root.Values[TKeyboardInfoFile.SLanguages] is TJSONArray then - begin - a := root.Values[TKeyboardInfoFile.SLanguages] as TJSONArray; - for i := 0 to a.Count - 1 do - AddLang(a.Items[i].AsType); - end - else - begin - root := root.Values[TKeyboardInfoFile.SLanguages] as TJSONObject; - for i := 0 to root.Count - 1 do - AddLang(root.Pairs[i].JsonString.Value); - end; - - langs.Sort; - - Result := True; -end; - -class function TValidateRepoChanges.GetLanguageCodesFromKps(kps: TKPSFile; langs: TStringList): Boolean; -var - i: Integer; - j: Integer; - - procedure AddLang(lang: string); - begin - langs.Add(TCanonicalLanguageCodeUtils.FindBestTag(lang, False, False)+','+lang); - end; - -begin - for i := 0 to kps.Keyboards.Count - 1 do - for j := 0 to kps.Keyboards[i].Languages.Count - 1 do - AddLang(kps.Keyboards[i].Languages[j].ID); - - langs.Sort; - - Result := True; -end; - -class function TValidateRepoChanges.CheckPackage(name: string): Boolean; -var - i: Integer; - kps_new, kps_old: TKPSFile; -// t: TTempFile; - relpath: string; - content: string; - ec: Integer; - lang_new: TStringList; - lang_old: TStringList; - langc_old: TArray; - langc_new: TArray; - found_error: Boolean; - j: Integer; - langc_old_prev: string; -begin - found_error := False; - relpath := name.Substring(root.Length).Replace('\', '/'); - - kps_new := TKPSFile.Create; - kps_new.FileName := name; - kps_new.LoadXML; - - if not TUtilExecute.Console('git show "HEAD:'+relpath+'"', root, content, ec) then - RaiseLastOSError; - - if ec <> 0 then - raise Exception.Create('Unable to execute git show for '+name); - - if Copy(content, 1, 3) = string(UTF8Signature) then - Delete(content, 1, 3); - - kps_old := TKPSFile.Create; - kps_old.LoadXMLFromText(content); - - lang_new := TStringList.Create; - lang_old := TStringList.Create; - try - if not GetLanguageCodesFromKps(kps_new, lang_new) then - raise Exception.Create('Unable to get Language codes'); - if not GetLanguageCodesFromKps(kps_old, lang_old) then - raise Exception.Create('Unable to get Language codes'); - - j := 0; - for i := 0 to lang_new.Count - 1 do - begin - repeat - if j >= lang_old.Count then - begin - if not found_error then - begin - writeln('Checking '+relpath); - found_error := True; - end; - writeln(' Mismatch in number of language codes'); - break; - end; - - langc_new := lang_new[i].Split([',']); - langc_old := lang_old[j].Split([',']); - Inc(j); - until langc_old[0] <> langc_old_prev; - langc_old_prev := langc_old[0]; - if j >= lang_old.Count then Break; - - if langc_new[0] <> langc_old[0] then - begin - if not found_error then - begin - writeln('Checking '+relpath); - found_error := True; - end; - writeln(Format(' New code %s [%s] does not match old code %s [%s]', [langc_new[1], langc_new[0], langc_old[1], langc_old[0]])); - end; - end; - - if found_error then - begin - err_old.Add('Checking '+relpath); - for i := 0 to lang_old.Count - 1 do - begin - langc_old := lang_old[i].Split([',']); - err_old.Add(Format(' %s [%s]', [langc_old[1], langc_old[0]])); - end; - err_old.Add(''); - - err_new.Add('Checking '+relpath); - for i := 0 to lang_new.Count - 1 do - begin - langc_new := lang_new[i].Split([',']); - err_new.Add(Format(' %s [%s]', [langc_new[1], langc_new[0]])); - end; - err_new.Add(''); - end; - finally - lang_new.Free; - lang_old.Free; - end; - kps_old.Free; - kps_new.Free; - - Result := True; -end; - -class function TValidateRepoChanges.CompareKeyboardInfoScripts(name: string): Boolean; -var - i: Integer; - json: TJSONObject; - relpath: string; - script, base_script: string; - lang: TStringList; - base_lang_item, lang_item: TArray; - found_error: Boolean; - - procedure WriteHeader; - begin - if not found_error then - begin - writeln; - writeln('Checking '+relpath); - end; - found_error := True; - end; - - function GetScript(lang: string): string; - var - v: string; - LangTag: TLangTag; - BCP47: TBCP47Tag; - begin - if TLangTagsMap.AllTags.TryGetValue(lang, v) then - lang := v; - - if not TLangTagsMap.LangTags.TryGetValue(lang, LangTag) then - begin - BCP47 := TBCP47Tag.Create(lang); - try - Exit(BCP47.Script); - finally - BCP47.Free; - end; - end; - - Result := LangTag.script; - end; - -begin - found_error := False; - relpath := name.Substring(root.Length).Replace('\', '/'); - - json := LoadKeyboardInfoFile(name); - if not Assigned(json) then - raise Exception.Create('Unable to load file '+name); - - lang := TStringList.Create; - try - if not GetLanguageCodesFromJson(json, lang) then - raise Exception.Create('Unable to get Language codes'); - - if lang.Count = 0 then - begin - WriteHeader; - writeln(' Warning: no languages found'); - Exit(True); - end; - - base_lang_item := lang[0].Split([',']); - - // Lookup the tag first, canonicalize to the base tag for known tags - base_script := GetScript(base_lang_item[0]); - if base_script = '' then - begin - WriteHeader; - writeln(' Warning: could not identify tag '+base_lang_item[0]); - Exit(True); - end; - - for i := 1 to lang.Count - 1 do - begin - lang_item := lang[i].Split([',']); - script := GetScript(lang_item[0]); - if script <> base_script then - begin - WriteHeader; - writeln(Format(' Tag %s [%s] has script <%s>, which differs from base tag %s [%s], script <%s>', - [lang_item[1], lang_item[0], script, base_lang_item[1], base_lang_item[0], base_script])); - end; - end; - finally - lang.Free; - end; - - Result := True; -end; - -end. diff --git a/developer/src/kmcomp/Makefile b/developer/src/kmcomp/Makefile deleted file mode 100644 index f559f32afa6..00000000000 --- a/developer/src/kmcomp/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -# -# Kmcomp Makefile -# - -!include ..\Defines.mak - -TARGET_BASE=kmcomp -TARGET_EXT=exe -TARGET_GROUP=developer - -build: version.res manifest.res dirs icons - $(DELPHI_MSBUILD) kmcomp.dproj "/p:Platform=Win32" - - $(SENTRYTOOL_DELPHIPREP) $(WIN32_TARGET_PATH)\kmcomp.exe -dpr kmcomp.dpr - $(TDS2DBG) $(WIN32_TARGET_PATH)\kmcomp.exe - $(COPY) $(WIN32_TARGET_PATH)\kmcomp.exe $(DEVELOPER_PROGRAM) - if exist $(WIN32_TARGET_PATH)\kmcomp.dbg $(COPY) $(WIN32_TARGET_PATH)\kmcomp.dbg $(DEVELOPER_DEBUGPATH) - - $(DELPHI_MSBUILD) kmcomp.dproj "/p:Platform=Win64" - - if exist $(WIN64_TARGET_PATH)\kmcomp.x64.exe del $(WIN64_TARGET_PATH)\kmcomp.x64.exe -# Delphi does not allow us to build to a different target filename so we rename after build - ren $(WIN64_TARGET_PATH)\kmcomp.exe kmcomp.x64.exe - - if exist $(WIN64_TARGET_PATH)\kmcomp.x64.map del $(WIN64_TARGET_PATH)\kmcomp.x64.map - ren $(WIN64_TARGET_PATH)\kmcomp.map kmcomp.x64.map - -# $(SENTRYTOOL_DELPHIPREP) $(WIN64_TARGET_PATH)\kmcomp.x64.exe -dpr kmcomp.dpr - $(COPY) $(WIN64_TARGET_PATH)\kmcomp.x64.exe $(DEVELOPER_PROGRAM)\kmcomp.x64.exe - if exist $(WIN64_TARGET_PATH)\kmcomp.dbg $(COPY) $(WIN64_TARGET_PATH)\kmcomp.dbg $(DEVELOPER_DEBUGPATH)\kmcomp.x64.dbg - -icons: - rc icons.rc - -clean: def-clean - if exist icons.res del icons.res - -signcode: - $(SIGNCODE) /d "Keyman Developer Command-Line Compiler" $(DEVELOPER_PROGRAM)\kmcomp.exe - $(SIGNCODE) /d "Keyman Developer Command-Line Compiler" $(DEVELOPER_PROGRAM)\kmcomp.x64.exe - -wrap-symbols: - $(SYMSTORE) $(DEVELOPER_PROGRAM)\kmcomp.exe /t keyman-developer - $(SYMSTORE) $(DEVELOPER_PROGRAM)\kmcomp.x64.exe /t keyman-developer - $(SYMSTORE) $(DEVELOPER_DEBUGPATH)\kmcomp.dbg /t keyman-developer -#TODO: $(SYMSTORE) $(DEVELOPER_DEBUGPATH)\kmcomp.x64.dbg /t keyman-developer - -test-manifest: -# test that (a) linked manifest exists and correct - $(MT) -nologo -inputresource:$(DEVELOPER_PROGRAM)\kmcomp.exe -validate_manifest - $(MT) -nologo -inputresource:$(DEVELOPER_PROGRAM)\kmcomp.x64.exe -validate_manifest - -install: - $(COPY) $(DEVELOPER_PROGRAM)\kmcomp.exe "$(INSTALLPATH_KEYMANDEVELOPER)\kmcomp.exe" - $(COPY) $(DEVELOPER_PROGRAM)\kmcomp.exe "$(INSTALLPATH_KEYMANDEVELOPER)\kmcomp.x64.exe" - -!include ..\Target.mak diff --git a/developer/src/kmcomp/icons.rc b/developer/src/kmcomp/icons.rc deleted file mode 100644 index aa14bfef90d..00000000000 --- a/developer/src/kmcomp/icons.rc +++ /dev/null @@ -1 +0,0 @@ -1 ICON DISCARDABLE "..\\images\\KeymanDeveloper90.ico" diff --git a/developer/src/kmcomp/kccompilekvk.pas b/developer/src/kmcomp/kccompilekvk.pas deleted file mode 100644 index facb82122ae..00000000000 --- a/developer/src/kmcomp/kccompilekvk.pas +++ /dev/null @@ -1,97 +0,0 @@ -unit kccompilekvk; - -interface - -function CompileVisualKeyboardFromKMX(FInFile, FOutFile: string): Boolean; - -implementation - -uses - System.Classes, - System.SysUtils, - - Keyman.Developer.System.Project.ProjectLog, - Keyman.Developer.System.Project.ProjectLogConsole, - KeyboardParser, - kmxfile, - kmxfileconsts, - main, - utilsystem, - VisualKeyboard; - -const - CWARN_KVKFileIsInSourceFormat = $000020A2; // from kmn_compiler_errors.h - -(** - Compiles the visual keyboard from xml to binary, and/or copies to destination folder - - Parameters: FInFile Source .kmn file name with path - FOutFile Destination .kmx file name with path - - Returns: True on success - - The input FInFile references - If the keyboard output is compiled to the same folder as the input, and a .kvk file - is referenced in the .kmn file (instead of a .kvks file), then this function will - take no action. This means that if the user has incorrectly saved a .kvk in XML, - the output .kvk file will be in the wrong format for older versions of Keyman (there - was a small window of time in May/June 2017 where .kvk files could be either XML or - binary). -*) -function CompileVisualKeyboardFromKMX(FInFile, FOutFile: string): Boolean; -var - FKVKInputFileName, FKVKOutputFileName: string; -begin - try - with TKeyboardParser.Create do // I4720 - try - LoadFromFile(FInFile); - FKVKInputFileName := GetSystemStoreValue(ssVisualKeyboard); - if FKVKInputFileName = '' then - begin - // Keyboard does not include a .kvk file - Exit(True); - end; - - FKVKInputFileName := ExpandFileNameClean(FInFile, FKVKInputFileName); - FKVKOutputFileName := ExpandFileNameClean(FOutFile, - ChangeFileExt(ExtractFileName(FKVKInputFileName), '.kvk')); - - with TVisualKeyboard.Create do - try - LoadFromFile(FKVKInputFileName); - if not SameFilename(FKVKInputFileName, FKVKOutputFileName) then - begin - // Source and destination files are different - SaveToFile(FKVKOutputFileName, kvksfBinary); - TProjectLogConsole.Instance.Log(plsSuccess, FKVKInputFileName, 'Visual keyboard '+FKVKInputFileName+' successfully compiled to '+FKVKOutputFileName, 0, 0); - end - else - begin - if LoadedFileFormat = kvksfXML then - TProjectLogConsole.Instance.Log(plsWarning, FKVKInputFileName, '.kvk file should be binary but is an XML file', CWARN_KVKFileIsInSourceFormat, 0) - else - TProjectLogConsole.Instance.Log(plsInfo, FKVKInputFileName, 'Visual keyboard '+FKVKInputFileName+' is valid', 0, 0); - end; - finally - Free; - end; - finally - Free; - end; - Result := True; - except - on E:EFCreateError do - begin - TProjectLogConsole.Instance.Log(plsFatal, FKVKInputFileName, E.Message, 0, 0); - Exit(False); - end; - on E:EVisualKeyboardLoader do - begin - TProjectLogConsole.Instance.Log(plsFatal, FKVKInputFileName, E.Message, 0, 0); - Exit(False); - end; - end; -end; - -end. diff --git a/developer/src/kmcomp/kccompilepackage.pas b/developer/src/kmcomp/kccompilepackage.pas deleted file mode 100644 index 8bdbcd26c79..00000000000 --- a/developer/src/kmcomp/kccompilepackage.pas +++ /dev/null @@ -1,111 +0,0 @@ -(* - Name: kccompilepackage - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 1 Aug 2006 - - Modified Date: 11 May 2015 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 01 Aug 2006 - mcdurdin - LoadIni instead of Load - 30 Apr 2007 - mcdurdin - Load from XML instead of from INI for compiling packages - 30 May 2007 - mcdurdin - I817 - Build bootstrap installer - 04 Jun 2007 - mcdurdin - Remove KeymanPath reference, Add AInstallerMSI, AUpdate - 04 May 2015 - mcdurdin - I4694 - V9.0 - Split UI actions from non-UI actions in projects - 11 May 2015 - mcdurdin - I4706 - V9.0 - Update compile logging for silent and warning-as-error cleanness -*) -unit kccompilepackage; - -interface - -uses - Windows, - SysUtils, - CompilePackage, - CompilePackageInstaller, - PackageInfo, - kpsfile; - -function DoKCCompilePackage(FileName: string; AWarnAsError, ACheckFilenameConventions, AInstaller: Boolean; - const AInstallerMSI: string; AUpdateInstaller: Boolean; const ASchemaPath: string): Boolean; // I4706 - -implementation - -uses - Keyman.Developer.System.Project.ProjectLog, - Keyman.Developer.System.Project.ProjectLogConsole, - Keyman.Developer.System.ValidateKpsFile; - -type - TKCCompilePackage = class - pack: TKPSFile; - procedure SelfMessage(Sender: TObject; msg: string; State: TProjectLogState); - end; - -procedure TKCCompilePackage.SelfMessage(Sender: TObject; msg: string; State: TProjectLogState); // I4706 -begin - TProjectLogConsole.Instance.Log(State, pack.Filename, Msg, 0, 0); -end; - -function DoKCCompilePackage(FileName: string; AWarnAsError, ACheckFilenameConventions, AInstaller: Boolean; - const AInstallerMSI: string; AUpdateInstaller: Boolean; const ASchemaPath: string): Boolean; // I4706 -var - tcp: TKCCompilePackage; - pack: TKPSFile; - buf: array[0..260] of char; - pbuf: PChar; -begin - Result := False; - pack := TKPSFile.Create; - try - GetFullPathName(PChar(FileName), 260, buf, pbuf); FileName := buf; - if not FileExists(FileName) then - begin - TProjectLogConsole.Instance.Log(plsFatal, FileName, 'Package file does not exist', 0, 0); - Exit; - end; - - if (ASchemaPath <> '') and (FileExists(ASchemaPath + 'kps.xsd')) then - begin - if not TValidateKpsFile.Execute(FileName, ASchemaPath + 'kps.xsd', - TProjectLogConsole.Instance.Log) then - begin - TProjectLogConsole.Instance.Log(plsFailure, FileName, 'Package '+FileName+' had validation errors.', 0, 0); - Exit; - end; - end; - - pack.FileName := FileName; - pack.LoadXML; - - tcp := TKCCompilePackage.Create; - try - tcp.pack := pack; - Result := DoCompilePackage(pack, tcp.SelfMessage, False, ACheckFilenameConventions, ChangeFileExt(pack.FileName, '.kmp')); // I4694 - if AWarnAsError and TProjectLogConsole.Instance.HasWarning then Result := False; - - if AInstaller and Result then - begin - Result := DoCompilePackageInstaller(pack, tcp.SelfMessage, False, AInstallerMSI, // I4694 - ChangeFileExt(pack.FileName, '.exe'), '', AUpdateInstaller, True, '', '', '', False, False); - if AWarnAsError and TProjectLogConsole.Instance.HasWarning then Result := False; - end; - - if Result - then TProjectLogConsole.Instance.Log(plsSuccess, FileName, 'Package '+FileName+' compiled successfully.', 0, 0) - else TProjectLogConsole.Instance.Log(plsFailure, FileName, 'Package '+FileName+' could not be compiled.', 0, 0); - finally - tcp.Free; - end; - finally - pack.Free; - end; -end; - -end. diff --git a/developer/src/kmcomp/kccompileproject.pas b/developer/src/kmcomp/kccompileproject.pas deleted file mode 100644 index 2c401888b7d..00000000000 --- a/developer/src/kmcomp/kccompileproject.pas +++ /dev/null @@ -1,138 +0,0 @@ -(* - Name: kccompileproject - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 5 May 2015 - - Modified Date: 11 May 2015 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 05 May 2015 - mcdurdin - I4699 - V9.0 - Compile .kpj files from kmcomp - 11 May 2015 - mcdurdin - I4706 - V9.0 - Update compile logging for silent and warning-as-error cleanness - 11 May 2015 - mcdurdin - I4709 - V9.0 - Use static hashing for id for project files to avoid unnecessary changes -*) -unit kccompileproject; // I4699 - -interface - -function DoKCCompileProject(AProjectFilename: string; ADebug, AClean, AWarnAsError, ACheckFilenameConventions: Boolean; ATarget: string): Boolean; // I4706 - -implementation - -uses - System.SysUtils, - - Keyman.Developer.System.Project.kmnProjectFileAction, - Keyman.Developer.System.Project.kpsProjectFileAction, - Keyman.Developer.System.Project.modelTsProjectFileAction, - Keyman.Developer.System.Project.ProjectLog, - Keyman.Developer.System.Project.ProjectLogConsole, - Keyman.Developer.System.Project.ProjectFile; - -type - TProjectConsole = class(TProject) - private - FSilent: Boolean; - FFullySilent: Boolean; - public - procedure Log(AState: TProjectLogState; Filename: string; Msg: string; MsgCode, Line: Integer); override; // I4706 - function Save: Boolean; override; // I4709 - - property Silent: Boolean read FSilent write FSilent; - property FullySilent: Boolean read FFullySilent write FFullySilent; - end; - -function DoKCCompileProject(AProjectFilename: string; ADebug, AClean, AWarnAsError, ACheckFilenameConventions: Boolean; ATarget: string): Boolean; // I4706 -var - i: Integer; - Found: Boolean; - kmn: TkmnProjectFileAction; - kps: TkpsProjectFileAction; - modelTs: TmodelTsProjectFileAction; - - function Matches(AFile: TProjectFile; AClass: TProjectFileClass): Boolean; - begin - Result := - (AFile is AClass) and - ((ATarget = '') or - SameText(ExtractFileName(ATarget), ExtractFileName(AFile.FileName))); - end; - -begin - Result := False; - Found := False; - with TProjectConsole.Create(ptUnknown, AProjectFilename, False) do - try - Options.CheckFilenameConventions := Options.CheckFilenameConventions or ACheckFilenameConventions; // never downgrade this option - for i := 0 to Files.Count - 1 do - if Matches(Files[i], TkmnProjectFileAction) then - begin - kmn := Files[i] as TkmnProjectFileAction; - kmn.Debug := ADebug; - kmn.WarnAsError := AWarnAsError; - if AClean then - begin - if not kmn.Clean then Exit; - end - else - if not kmn.CompileKeyboard then Exit; - Found := True; - end - else if Matches(Files[i], TmodelTsProjectFileAction) then - begin - modelTs := Files[i] as TmodelTsProjectFileAction; - modelTs.Debug := ADebug; - modelTs.WarnAsError := AWarnAsError; - if AClean then - begin - if not modelTs.Clean then Exit; - end - else - if not modelTs.CompileModel then Exit; - Found := True; - end; - - - for i := 0 to Files.Count - 1 do - if Matches(Files[i], TkpsProjectFileAction) then - begin - kps := Files[i] as TkpsProjectFileAction; - kps.WarnAsError := AWarnAsError; - if AClean then - begin - if not kps.Clean then Exit; - end - else - if not kps.CompilePackage(nil, False) then Exit; - Found := True; - end; - - finally - Free; - end; - - if not Found then - TProjectLogConsole.Instance.Log(plsFatal, AProjectFileName, 'Target not found (or project empty)', 0, 0); - Result := Found; -end; - -{ TProjectConsole } - -procedure TProjectConsole.Log(AState: TProjectLogState; Filename, Msg: string; MsgCode, Line: Integer); // I4706 -begin - TProjectLogConsole.Instance.Log(AState, Filename, Msg, MsgCode, Line); -end; - -function TProjectConsole.Save: Boolean; // I4709 -begin - // We don't modify the project file in the console - Result := True; -end; - -end. diff --git a/developer/src/kmcomp/kmcomp.dpr b/developer/src/kmcomp/kmcomp.dpr deleted file mode 100644 index 30204a63dba..00000000000 --- a/developer/src/kmcomp/kmcomp.dpr +++ /dev/null @@ -1,153 +0,0 @@ -program kmcomp; - -{$APPTYPE CONSOLE} - -uses - System.SysUtils, - main in 'main.pas', - compile in '..\common\delphi\compiler\compile.pas', - VersionInfo in '..\..\..\common\windows\delphi\general\VersionInfo.pas', - RegistryKeys in '..\..\..\common\windows\delphi\general\RegistryKeys.pas', - kccompilepackage in 'kccompilepackage.pas', - CompilePackage in '..\common\delphi\compiler\CompilePackage.pas', - kpsfile in '..\common\delphi\packages\kpsfile.pas', - PackageInfo in '..\..\..\common\windows\delphi\packages\PackageInfo.pas', - PackageFileFormats in '..\..\..\common\windows\delphi\packages\PackageFileFormats.pas', - kmpinffile in '..\..\..\common\windows\delphi\packages\kmpinffile.pas', - RedistFiles in '..\tike\main\RedistFiles.pas', - DebugPaths in '..\..\..\common\windows\delphi\general\DebugPaths.pas', - httpuploader in '..\..\..\common\windows\delphi\general\httpuploader.pas', - httpuploader_messageprocessor_forms in '..\..\..\common\windows\delphi\general\httpuploader_messageprocessor_forms.pas', - utilfiletypes in '..\..\..\common\windows\delphi\general\utilfiletypes.pas', - klog in '..\..\..\common\windows\delphi\general\klog.pas', - KeyNames in '..\..\..\common\windows\delphi\general\KeyNames.pas', - StockFileNames in '..\..\..\common\windows\delphi\general\StockFileNames.pas', - KeymanDeveloperOptions in '..\tike\main\KeymanDeveloperOptions.pas', - Upload_Settings in '..\..\..\common\windows\delphi\general\Upload_Settings.pas', - utilstr in '..\..\..\common\windows\delphi\general\utilstr.pas', - utilsystem in '..\..\..\common\windows\delphi\general\utilsystem.pas', - utildir in '..\..\..\common\windows\delphi\general\utildir.pas', - utilkeyboard in '..\..\..\common\windows\delphi\keyboards\utilkeyboard.pas', - unicode in '..\..\..\common\windows\delphi\general\Unicode.pas', - utilhttp in '..\..\..\common\windows\delphi\general\utilhttp.pas', - GetOsVersion in '..\..\..\common\windows\delphi\general\GetOsVersion.pas', - UfrmTike in '..\tike\main\UfrmTike.pas' {TikeForm: TTntForm}, - CompilePackageInstaller in '..\common\delphi\compiler\CompilePackageInstaller.pas', - UTikeDebugMode in '..\tike\main\UTikeDebugMode.pas', - CompileKeymanWeb in '..\tike\compile\CompileKeymanWeb.pas', - VisualKeyboard in '..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboard.pas', - KeymanWebKeyCodes in '..\tike\compile\KeymanWebKeyCodes.pas', - ExtShiftState in '..\..\..\common\windows\delphi\visualkeyboard\ExtShiftState.pas', - kmxfileconsts in '..\..\..\common\windows\delphi\keyboards\kmxfileconsts.pas', - wininet5 in '..\..\..\common\windows\delphi\general\wininet5.pas', - kmxfileutils in '..\..\..\common\windows\delphi\keyboards\kmxfileutils.pas', - GlobalProxySettings in '..\..\..\common\windows\delphi\general\GlobalProxySettings.pas', - VKeyChars in '..\..\..\common\windows\delphi\general\VKeyChars.pas', - ErrorControlledRegistry in '..\..\..\common\windows\delphi\vcl\ErrorControlledRegistry.pas', - utilexecute in '..\..\..\common\windows\delphi\general\utilexecute.pas', - KeymanVersion in '..\..\..\common\windows\delphi\general\KeymanVersion.pas', - kmxfile in '..\..\..\common\windows\delphi\keyboards\kmxfile.pas', - Glossary in '..\..\..\common\windows\delphi\general\Glossary.pas', - VKeys in '..\..\..\common\windows\delphi\general\VKeys.pas', - CompileErrorCodes in '..\common\delphi\compiler\CompileErrorCodes.pas', - TouchLayout in '..\tike\oskbuilder\TouchLayout.pas', - TouchLayoutDefinitions in '..\tike\oskbuilder\TouchLayoutDefinitions.pas', - KeyboardFonts in '..\common\delphi\general\KeyboardFonts.pas', - KeyboardParser in '..\tike\main\KeyboardParser.pas', - WindowsLanguages in '..\common\delphi\general\WindowsLanguages.pas', - TempFileManager in '..\..\..\common\windows\delphi\general\TempFileManager.pas', - JsonUtil in '..\..\..\common\windows\delphi\general\JsonUtil.pas', - TikeUnicodeData in '..\tike\main\TikeUnicodeData.pas', - UnicodeData in '..\..\..\common\windows\delphi\charmap\UnicodeData.pas', - ttinfo in '..\..\..\common\windows\delphi\general\ttinfo.pas', - ADODB_TLB in '..\..\..\common\windows\delphi\tlb\ADODB_TLB.pas', - ADOX_TLB in '..\..\..\common\windows\delphi\tlb\ADOX_TLB.pas', - UKeymanTargets in '..\common\delphi\general\UKeymanTargets.pas', - kccompileproject in 'kccompileproject.pas', - Keyman.Developer.System.Project.ProjectFile in '..\tike\project\Keyman.Developer.System.Project.ProjectFile.pas', - mrulist in '..\tike\main\mrulist.pas', - Keyman.Developer.System.Project.Project in '..\tike\project\Keyman.Developer.System.Project.Project.pas', - Keyman.Developer.System.Project.ProjectFiles in '..\tike\project\Keyman.Developer.System.Project.ProjectFiles.pas', - Keyman.Developer.System.Project.ProjectFileType in '..\tike\project\Keyman.Developer.System.Project.ProjectFileType.pas', - Keyman.Developer.System.Project.ProjectLoader in '..\tike\project\Keyman.Developer.System.Project.ProjectLoader.pas', - Keyman.Developer.System.Project.ProjectSaver in '..\tike\project\Keyman.Developer.System.Project.ProjectSaver.pas', - Keyman.Developer.System.Project.kmnProjectFile in '..\tike\project\Keyman.Developer.System.Project.kmnProjectFile.pas', - Keyman.Developer.System.Project.kmxProjectFile in '..\tike\project\Keyman.Developer.System.Project.kmxProjectFile.pas', - Keyman.Developer.System.Project.kpsProjectFile in '..\tike\project\Keyman.Developer.System.Project.kpsProjectFile.pas', - Keyman.Developer.System.Project.kvkProjectFile in '..\tike\project\Keyman.Developer.System.Project.kvkProjectFile.pas', - Keyman.Developer.System.Project.ProjectLog in '..\tike\project\Keyman.Developer.System.Project.ProjectLog.pas', - UserMessages in '..\..\..\common\windows\delphi\general\UserMessages.pas', - VisualKeyboardLoaderBinary in '..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardLoaderBinary.pas', - VisualKeyboardLoaderXML in '..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardLoaderXML.pas', - VisualKeyboardSaverBinary in '..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardSaverBinary.pas', - VisualKeyboardSaverXML in '..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardSaverXML.pas', - kccompilekvk in 'kccompilekvk.pas', - ValidateKeyboardInfo in '..\tike\compile\ValidateKeyboardInfo.pas', - MergeKeyboardInfo in '..\tike\compile\MergeKeyboardInfo.pas', - JsonExtractKeyboardInfo in '..\tike\compile\JsonExtractKeyboardInfo.pas', - Keyman.System.PackageInfoRefreshKeyboards in '..\common\delphi\packages\Keyman.System.PackageInfoRefreshKeyboards.pas', - Keyman.System.KeyboardJSInfo in '..\common\delphi\keyboards\Keyman.System.KeyboardJSInfo.pas', - Keyman.System.KeyboardUtils in '..\common\delphi\keyboards\Keyman.System.KeyboardUtils.pas', - Keyman.System.KMXFileLanguages in '..\common\delphi\keyboards\Keyman.System.KMXFileLanguages.pas', - Keyman.System.Standards.ISO6393ToBCP47Registry in '..\..\..\common\windows\delphi\standards\Keyman.System.Standards.ISO6393ToBCP47Registry.pas', - Keyman.System.Standards.LCIDToBCP47Registry in '..\..\..\common\windows\delphi\standards\Keyman.System.Standards.LCIDToBCP47Registry.pas', - Keyman.System.KeyboardInfoFile in '..\common\delphi\keyboards\Keyman.System.KeyboardInfoFile.pas', - BCP47Tag in '..\..\..\common\windows\delphi\general\BCP47Tag.pas', - Keyman.System.LanguageCodeUtils in '..\..\..\common\windows\delphi\general\Keyman.System.LanguageCodeUtils.pas', - Keyman.System.RegExGroupHelperRSP19902 in '..\..\..\common\windows\delphi\vcl\Keyman.System.RegExGroupHelperRSP19902.pas', - Keyman.System.Standards.BCP47SubtagRegistry in '..\..\..\common\windows\delphi\standards\Keyman.System.Standards.BCP47SubtagRegistry.pas', - Keyman.System.Standards.BCP47SuppressScriptRegistry in '..\..\..\common\windows\delphi\standards\Keyman.System.Standards.BCP47SuppressScriptRegistry.pas', - TextFileFormat in '..\common\delphi\general\TextFileFormat.pas', - Keyman.Developer.System.Project.kmnProjectFileAction in '..\tike\project\Keyman.Developer.System.Project.kmnProjectFileAction.pas', - Keyman.Developer.System.Project.kpsProjectFileAction in '..\tike\project\Keyman.Developer.System.Project.kpsProjectFileAction.pas', - Keyman.System.PackageInfoRefreshLexicalModels in '..\common\delphi\packages\Keyman.System.PackageInfoRefreshLexicalModels.pas', - Keyman.Developer.System.Project.modelTsProjectFile in '..\tike\project\Keyman.Developer.System.Project.modelTsProjectFile.pas', - Keyman.Developer.System.Project.modelTsProjectFileAction in '..\tike\project\Keyman.Developer.System.Project.modelTsProjectFileAction.pas', - Keyman.Developer.System.LexicalModelCompile in '..\common\delphi\lexicalmodels\Keyman.Developer.System.LexicalModelCompile.pas', - Keyman.System.LexicalModelUtils in '..\common\delphi\lexicalmodels\Keyman.System.LexicalModelUtils.pas', - Keyman.Developer.System.Project.ProjectLogConsole in 'Keyman.Developer.System.Project.ProjectLogConsole.pas', - Sentry.Client in '..\..\..\common\windows\delphi\ext\sentry\Sentry.Client.pas', - Sentry.Client.Console in '..\..\..\common\windows\delphi\ext\sentry\Sentry.Client.Console.pas', - sentry in '..\..\..\common\windows\delphi\ext\sentry\sentry.pas', - Keyman.System.KeymanSentryClient in '..\..\..\common\windows\delphi\general\Keyman.System.KeymanSentryClient.pas', - KeymanPaths in '..\..\..\common\windows\delphi\general\KeymanPaths.pas', - Keyman.System.CanonicalLanguageCodeUtils in '..\..\..\common\windows\delphi\general\Keyman.System.CanonicalLanguageCodeUtils.pas', - Keyman.System.Standards.LangTagsRegistry in '..\..\..\common\windows\delphi\standards\Keyman.System.Standards.LangTagsRegistry.pas', - Keyman.Developer.System.Project.UrlRenderer in '..\tike\project\Keyman.Developer.System.Project.UrlRenderer.pas', - Keyman.Developer.System.ValidateRepoChanges in 'Keyman.Developer.System.ValidateRepoChanges.pas', - Keyman.Developer.System.KeymanDeveloperPaths in '..\tike\main\Keyman.Developer.System.KeymanDeveloperPaths.pas', - Keyman.Developer.System.ValidateKpsFile in '..\common\delphi\compiler\Keyman.Developer.System.ValidateKpsFile.pas'; - -{$R icons.RES} -{$R version.res} -{$R manifest.res} - -const -{$IFDEF WIN64} - LOGGER_DEVELOPER_TOOLS_KMCOMP = TKeymanSentryClient.LOGGER_DEVELOPER_TOOLS + '.kmcomp.x64'; -{$ELSE} - LOGGER_DEVELOPER_TOOLS_KMCOMP = TKeymanSentryClient.LOGGER_DEVELOPER_TOOLS + '.kmcomp'; -{$ENDIF} -begin - TKeymanSentryClient.Start(TSentryClientConsole, kscpDeveloper, LOGGER_DEVELOPER_TOOLS_KMCOMP); - try - try - if (ParamStr(1) = '-sentry-client-test-exception') and (ParamStr(2) = 'dll') then - begin - compile.Compiler_Diagnostic_Console(0); - Exit; - end; - TKeymanSentryClient.Validate; - Run; - except - on E: Exception do - if not SentryHandleException(E) then - begin - writeln(E.Message); - ExitCode := 99; - end; - end; - finally - TKeymanSentryClient.Stop; - end; -end. diff --git a/developer/src/kmcomp/kmcomp.dproj b/developer/src/kmcomp/kmcomp.dproj deleted file mode 100644 index 36c16e35356..00000000000 --- a/developer/src/kmcomp/kmcomp.dproj +++ /dev/null @@ -1,1173 +0,0 @@ - - - {CB0DF7C0-19BB-4F2B-AFCC-2E51DAB3A1AA} - kmcomp.dpr - True - Release - 3 - Console - None - 18.8 - Win32 - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Cfg_1 - true - true - - - true - Cfg_1 - true - true - - - true - Base - true - - - true - Cfg_2 - true - true - - - true - Cfg_2 - true - true - - - $(BDS)\bin\delphi_PROJECTICON.ico - $(BDS)\bin\delphi_PROJECTICNS.icns - kmcomp - true - KPSMALL;VERSION_KEYMAN_DEVELOPER;$(DCC_Define) - 00400000 - false - 14 - 0 - System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;System.Win;Vcl.Imaging;$(DCC_Namespace) - true - 1 - 3 - 8 - false - false - 3081 - false - true - false - true - CompanyName=CASEG Practice Computing;FileDescription=Blue Chip;FileVersion=0.8.0.14;InternalName=BlueChip;LegalCopyright=Copyright © 1997-1999 CASEG Practice Computing;LegalTrademarks=;OriginalFilename=BlueChip.exe;ProductName=Blue Chip;ProductVersion=1.0.0.0;Comments= - VCL40;VCLX40;VCLDB40;VCLDBX40;VCLSMP40;QRPT40;TEEUI40;TEEDB40;TEE40;ibevnt40;nmfast40;bcutil;VCLJPG40;bccomp;bcdb;bcacct;bcappt;$(DCC_UsePackage) - false - true - true - .\bin\$(Platform)\$(Config) - .\obj\$(Platform)\$(Config) - true - - - kmcomp_Icon.ico - Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - 1033 - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - - - kmcomp_Icon.ico - Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) - Debug - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - 1033 - (None) - - - false - 0 - 0 - RELEASE;$(DCC_Define) - - - c:\temp\foo.kps -schema-path C:\Projects\keyman\app\common\schemas\kps\ - true - 2 - 1033 - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - kmcomp_Icon.ico - true - 2 - true - - - 1033 - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - kmcomp_Icon.ico - true - false - 1 - - - DEBUG;$(DCC_Define) - false - - - khmer_angkor.kpj - C:\Projects\keyman\keyboards\release\k\khmer_angkor - true - 1033 - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - kmcomp_Icon.ico - - - 1033 - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - kmcomp_Icon.ico - khmer_angkor.kpj - C:\Projects\keyman\keyboards\release\k\khmer_angkor - - - - MainSource - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TikeForm
- TTntForm -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - -
- - Delphi.Personality.12 - VCLApplication - - - - kmcomp.dpr - - - $00000C09 - - - False - False - 0 - 8 - 0 - 14 - False - False - False - False - False - 3081 - 1252 - - - CASEG Practice Computing - Blue Chip - 0.8.0.14 - BlueChip - Copyright © 1997-1999 CASEG Practice Computing - - BlueChip.exe - Blue Chip - 1.0.0.0 - - - - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - - - - True - True - - - - - true - - - - - kmcomp.exe - true - - - - - kmcomp.exe - true - - - - - kmcomp.exe - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - 1 - - - 0 - - - - - classes - 1 - - - classes - 1 - - - - - res\xml - 1 - - - res\xml - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\armeabi - 1 - - - library\lib\armeabi - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\mips - 1 - - - library\lib\mips - 1 - - - - - library\lib\armeabi-v7a - 1 - - - library\lib\arm64-v8a - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - res\drawable - 1 - - - res\drawable - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - res\values-v21 - 1 - - - res\values-v21 - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - res\drawable - 1 - - - res\drawable - 1 - - - - - res\drawable-xxhdpi - 1 - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-ldpi - 1 - - - res\drawable-ldpi - 1 - - - - - res\drawable-mdpi - 1 - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - res\drawable-xhdpi - 1 - - - - - res\drawable-mdpi - 1 - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - res\drawable-xhdpi - 1 - - - - - res\drawable-xxhdpi - 1 - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-xxxhdpi - 1 - - - res\drawable-xxxhdpi - 1 - - - - - res\drawable-small - 1 - - - res\drawable-small - 1 - - - - - res\drawable-normal - 1 - - - res\drawable-normal - 1 - - - - - res\drawable-large - 1 - - - res\drawable-large - 1 - - - - - res\drawable-xlarge - 1 - - - res\drawable-xlarge - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - 1 - - - 1 - - - 0 - - - - - 1 - .framework - - - 1 - .framework - - - 0 - - - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .dll;.bpl - - - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .bpl - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - - - - - - 1 - - - 1 - - - 1 - - - - - - - - Contents\Resources - 1 - - - Contents\Resources - 1 - - - - - library\lib\armeabi-v7a - 1 - - - library\lib\arm64-v8a - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 0 - - - - - library\lib\armeabi-v7a - 1 - - - - - 1 - - - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - - - - - - - - - - - 12 - - - - -
diff --git a/developer/src/kmcomp/kmcomp.res b/developer/src/kmcomp/kmcomp.res deleted file mode 100644 index bfe8dde79b6..00000000000 Binary files a/developer/src/kmcomp/kmcomp.res and /dev/null differ diff --git a/developer/src/kmcomp/kmcomp_Icon.ico b/developer/src/kmcomp/kmcomp_Icon.ico deleted file mode 100644 index 1bed139f3c1..00000000000 Binary files a/developer/src/kmcomp/kmcomp_Icon.ico and /dev/null differ diff --git a/developer/src/kmcomp/main.pas b/developer/src/kmcomp/main.pas deleted file mode 100644 index 4aa29cf6c1a..00000000000 --- a/developer/src/kmcomp/main.pas +++ /dev/null @@ -1,408 +0,0 @@ -(* - Name: main - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 20 Jun 2006 - - Modified Date: 24 Aug 2015 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 20 Jun 2006 - mcdurdin - Initial version - 02 Aug 2006 - mcdurdin - Timeout when Beta expires - 25 Jan 2007 - mcdurdin - Add -x option to encrypt kmx and kct files - 30 Apr 2007 - mcdurdin - Initialize COM before building - 30 May 2007 - mcdurdin - I817 - build bootstrap installer option - 04 Jun 2007 - mcdurdin - I817 - support updating to a new msi file - 27 Mar 2008 - mcdurdin - I1369 - Support compiling KeymanWeb keyboards in KMCOMP - 25 May 2010 - mcdurdin - I2392 - Activation Client integration - 18 May 2012 - mcdurdin - I3306 - V9.0 - Remove TntControls + Win9x support - 08 Jun 2012 - mcdurdin - I3310 - V9.0 - Unicode in Delphi fixes - 13 Dec 2012 - mcdurdin - I3681 - V9.0 - KeymanWeb compiler should output formatted js when debug=1 - 05 May 2015 - mcdurdin - I4699 - V9.0 - Compile .kpj files from kmcomp - 11 May 2015 - mcdurdin - I4706 - V9.0 - Update compile logging for silent and warning-as-error cleanness - 11 May 2015 - mcdurdin - I4707 - V9.0 - Add clean target to kmcomp - 22 Jun 2015 - mcdurdin - I4763 - Package compiler (buildpkg) needs to specify username and password on command line - 03 Aug 2015 - mcdurdin - I4825 - Make kmcomp command line details consistent - 24 Aug 2015 - mcdurdin - I4865 - Add treat hints and warnings as errors into project - 24 Aug 2015 - mcdurdin - I4866 - Add warn on deprecated features to project and compile - -*) -unit main; // I3306 - -interface - -procedure Run; - -implementation - -uses - System.AnsiStrings, - System.SysUtils, - Winapi.ActiveX, - Winapi.Windows, - - KeyboardParser, - kmxfileconsts, - Keyman.Developer.System.Project.ProjectLog, - Keyman.Developer.System.Project.ProjectLogConsole, - Keyman.Developer.System.ValidateRepoChanges, - VersionInfo, - compile, - KCCompilePackage, - KCCompileProject, - KCCompileKVK, - KeymanVersion, - CompileKeymanWeb, - JsonExtractKeyboardInfo, - ValidateKeyboardInfo, - MergeKeyboardInfo, - UKeymanTargets; - -function CompileKeyboard(FInFile, FOutFile: string; FDebug, FWarnAsError: Boolean): Boolean; forward; // I4706 -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; - -procedure Run; -var - hOutfile: THandle; - FNologo, FDebug, FSilent, FError: Boolean; - s, FParamTarget, FParamInfile, FParamOutfile, FParamDebugfile: string; - i: Integer; - FInstaller: Boolean; - FUpdateInstaller: Boolean; - FInstallerMSI: string; - FClean: Boolean; - FValidateRepoChanges, FFullySilent: Boolean; - FWarnAsError: Boolean; - FCheckFilenameConventions: Boolean; - FValidating: Boolean; - FMerging: Boolean; - FParamInfile2: string; - FParamJsonFields: string; - FJsonExtract: Boolean; - FParamDistribution: Boolean; - FMergingValidateIds: Boolean; - FShouldAddCompilerVersion: Boolean; - FJsonSchemaPath: string; - FParamSourcePath: string; - FParamHelpLink: string; - FColorMode: TProjectLogConsole.TColorMode; - cmd, spc: string; -begin - FSilent := False; - FFullySilent := False; - FDebug := False; - FError := False; - FInstaller := False; - FUpdateInstaller := False; - FClean := False; - FNologo := False; - FValidateRepoChanges := False; - FWarnAsError := False; - FCheckFilenameConventions := False; - FValidating := False; - FJsonExtract := False; - FMerging := False; - FMergingValidateIds := False; - FParamDistribution := False; - FInstallerMSI := ''; - FColorMode := cmDefault; - - FShouldAddCompilerVersion := True; - - FParamInfile := ''; - FParamOutfile := ''; - FParamDebugfile := ''; - FParamTarget := ''; // I4699 - - FJsonSchemaPath := ExtractFilePath(ParamStr(0)); - - i := 1; - while i <= ParamCount do - begin - s := LowerCase(ParamStr(i)); - if s = '-nologo' then // I4706 - FNologo := True - else if s = '-validate-repo-changes' then - FValidateRepoChanges := True - else if s = '-s' then FSilent := True // I4706 - else if s = '-ss' then // I4706 - begin - FSilent := True; - FFullySilent := True; - end - else if s = '-c' then FClean := True // I4707 - else if s = '-u' then FUpdateInstaller := True - else if s = '-d' then FDebug := True - else if s = '-w' then FWarnAsError := True // I4706 - else if s = '-cfc' then FCheckFilenameConventions := True - else if s = '-t' then // I4699 - begin - Inc(i); - if FParamTarget <> '' - then FError := True - else FParamTarget := ParamStr(i); - end - else if s = '-v' then FValidating := True - else if s = '-vs' then FValidating := True - else if s = '-vd' then begin FValidating := True; FParamDistribution := True; end - else if s = '-m' then - begin - FMerging := True; - if (FParamInfile <> '') and (FParamInfile2 = '') then - begin - Inc(i); - FParamInfile2 := ParamStr(i); - end; - end - else if s = '-source-path' then - begin - Inc(i); - FParamSourcePath := ParamStr(i); - end - else if s = '-add-help-link' then - begin - Inc(i); - FParamHelpLink := ParamStr(i); - end - else if s = '-schema-path' then - begin - Inc(i); - FJsonSchemaPath := IncludeTrailingPathDelimiter(ParamStr(i)); - end - else if s = '-m-validate-id' then - FMergingValidateIds := True - else if s = '-extract-keyboard-info' then - begin - FJsonExtract := True; - Inc(i); - FParamJsonFields := ParamStr(i); - end - else if s = '-color' then - FColorMode := cmForceColor - else if s = '-no-color' then - FColorMode := cmForceNoColor - else if s = '-no-compiler-version' then - FShouldAddCompilerVersion := False - else if (s = '-help') or (s = '-h') then - begin - // Force help - FParamInfile := ''; - Break; - end - else if FParamInfile = '' then FParamInfile := ParamStr(i) - else if FParamOutfile = '' then FParamOutfile := ParamStr(i) - else if FParamDebugfile = '' then FParamDebugfile := ParamStr(i) - else FError := True; - Inc(i); - end; - - if FUpdateInstaller and (FInstallerMSI = '') then - begin - writeln('Invalid arguments: -u cannot be specified if installer.msi is not.'); - ExitCode := 3; - Exit; - end; - - if (not FSilent and not FNologo) or FError then // I4706 - begin -{$IFDEF WIN64} - writeln(SKeymanDeveloperName + ' Compiler (64-bit)'); -{$ELSE} - writeln(SKeymanDeveloperName + ' Compiler (32-bit)'); -{$ENDIF} - writeln('Version ' + CKeymanVersionInfo.VersionWithTag + ', ' + GetVersionCopyright); - end; - - if FError or (FParamInfile = '') then - begin - cmd := ChangeFileExt(ExtractFileName(ParamStr(0)), ''); - spc := StringOfChar(' ', cmd.Length); - 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]'); - 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'); - writeln(' outfile.kmx can only be specified for a .kmn infile'); - writeln(' outfile.js write a KeymanWeb file'); - writeln(' error.log write to an error log; outfile must be specified'); // I4825 - writeln; - writeln(' -h, -help print this help information'); - writeln(' -s silent; don''t print information-level messages'); - writeln(' -ss fully silent; don''t print anything (except fatal errors)'); - writeln(' -nologo don''t print the compiler description'); - writeln(' -c clean target (only for .kpj)'); - writeln(' -d include debug information'); - writeln(' -w treat warnings as errors'); - writeln(' -cfc check filename conventions'); - writeln(' -t build only the target file from the project (only for .kpj)'); // I4699 - writeln(' -add-help-link path to help file on https://help.keyman.com/keyboards'); - writeln; - writeln(' -color If specified, forces color log messages on'); - writeln(' -no-color If specified, forces color log messages off. If neither specified,'); - 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; - writeln(' JSON .keyboard_info compile targets:'); - writeln(' -v[s] validate infile against source schema'); - writeln(' -vd validate infile against distribution schema'); - writeln(' -m merge information from infile (can be .kmp and .js) into .keyboard_info output file'); - writeln(' -m-validate-id validate the id against the .js, .kmx and .kmp filenames when merging'); - writeln(' -extract-keyboard-info print json data .keyboard_info for build script integration'); - writeln(' -source-path specify path to add to the sourcePath field in the .keyboard_info output file'); - writeln(' -schema-path specify path to the keyboard_info json schema definitions'); - writeln(' if not specified, then defaults to same folder as kmcomp.exe'); - - if FError - then ExitCode := 2 - else ExitCode := 0; - Exit; - end; - - CoInitializeEx(nil, COINIT_APARTMENTTHREADED); - try - if not FSilent then writeln(''); - - FixupPathSlashes(FParamDebugFile); - FixupPathSlashes(FParamTarget); - FixupPathSlashes(FParamInfile); - FixupPathSlashes(FParamOutfile); - FixupPathSlashes(FParamInfile2); - FixupPathSlashes(FInstallerMSI); - FixupPathSlashes(FJsonSchemaPath); - - if FParamDebugfile <> '' then - begin - hOutfile := CreateFile(PChar(FParamDebugfile), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0); - if hOutfile = INVALID_HANDLE_VALUE then hOutfile := 0; - end - else - hOutfile := 0; - - TProjectLogConsole.Create(FSilent, FFullySilent, hOutfile, FColorMode); - - KCSetCompilerOptions(FParamInfile, FShouldAddCompilerVersion); - - if FValidateRepoChanges then - FError := not TValidateRepoChanges.Execute(FParamInfile, FParamOutfile) - else if FMerging then - FError := not TMergeKeyboardInfo.Execute(FParamSourcePath, FParamInfile, FParamInfile2, FParamOutfile, FParamHelpLink, FMergingValidateIds, FSilent, TProjectLogConsole.Instance.Log) - else if FValidating then - FError := not TValidateKeyboardInfo.Execute(FParamInfile, FJsonSchemaPath, FParamDistribution, FSilent, TProjectLogConsole.Instance.Log) - else if FJsonExtract then - FError := not TJsonExtractKeyboardInfo.Execute(FParamInfile, FParamJsonFields, FSilent, TProjectLogConsole.Instance.Log) - else if LowerCase(ExtractFileExt(FParamInfile)) = '.kpj' then // I4699 - Ferror := not DoKCCompileProject(FParamInfile, FDebug, FClean, FWarnAsError, FCheckFilenameConventions, FParamTarget) // I4706 // I4707 - else if LowerCase(ExtractFileExt(FParamInfile)) = '.kps' then - FError := not DoKCCompilePackage(FParamInfile, FWarnAsError, FInstaller, FCheckFilenameConventions, FInstallerMSI, FUpdateInstaller, FJsonSchemaPath) // I4706 - else - FError := not CompileKeyboard(FParamInfile, FParamOutfile, FDebug, FWarnAsError); // I4706 - - if hOutfile <> 0 then CloseHandle(hOutfile); - finally - CoUninitialize(); - end; - - if FError then - ExitCode := 1; -end; - -function KCSetCompilerOptions(const FInFile: string; FShouldAddCompilerVersion: Boolean): Boolean; -var - opt: TCompilerOptions; -begin - TProjectLogConsole.Instance.Filename := FInFile; - - opt.dwSize := sizeof(TCompilerOptions); - opt.ShouldAddCompilerVersion := FShouldAddCompilerVersion; - - Result := SetCompilerOptions(@opt, @CompilerMessageW); - - if not Result then - begin - TProjectLogConsole.Instance.Log(plsError, FInFile, 'Could not set compiler options', 0, 0); - end; -end; - -function CompileKeyboard(FInFile, FOutFile: string; FDebug, FWarnAsError: Boolean): Boolean; // I4706 -var - FIsJS, FIsKMX: Boolean; - kp: TKeyboardParser; - FTargets: TKeymanTargets; -begin - if ExtractFileExt(FOutFile) = '.*' then - begin - // Load the input .kmn and determine if it targets .js and .kmx - kp := TKeyboardParser.Create; - try - kp.LoadFromFile(FInFile); - - // Compile targets - copied from kmnProjectFile - FTargets := StringToKeymanTargets(kp.GetSystemStoreValue(ssTargets)); - if ktAny in FTargets then FTargets := AllKeymanTargets; - if FTargets = [] then FTargets := [ktWindows]; - - FIsJS := FTargets * KMWKeymanTargets <> []; - FIsKMX := FTargets * KMXKeymanTargets <> []; - finally - kp.Free; - end; - end - else - begin - FIsJS := SameText(ExtractFileExt(FOutFile), '.js'); - FIsKMX := not FIsJS; - end; - - Result := True; - - if FIsJS then - begin - if FOutFile = '' then FOutFile := FInFile; - FOutFile := ChangeFileExt(FOutFile, '.js'); - - with TCompileKeymanWeb.Create do - try - Result := Result and Compile(nil, FInFile, FOutFile, FDebug, @CompilerMessageW); // I3681 // I4865 // I4866 - finally - Free; - end; - - if TProjectLogConsole.Instance.HasWarning and FWarnAsError then Result := False; // I4706 - if Result - then TProjectLogConsole.Instance.Log(plsSuccess, FInFile, 'Keyboard '+FInFile+' compiled, output saved as '+FOutFile+'.', 0, 0) - else TProjectLogConsole.Instance.Log(plsFailure, FInFile, 'Keyboard '+FInFile+' could not be compiled.', 0, 0); - end; - - if Result and FIsKMX then - begin - if FOutFile = '' then FOutFile := FInFile; - FOutFile := ChangeFileExt(FOutFile, '.kmx'); - Result := Result and (CompileKeyboardFile(PChar(FInFile), PChar(FOutFile), FDebug, FWarnAsError, True, @CompilerMessage) <> 0); // I4865 // I4866 - Result := Result and CompileVisualKeyboardFromKMX(FInFile, FOutFile); - - if TProjectLogConsole.Instance.HasWarning and FWarnAsError then Result := False; // I4706 - if Result - then TProjectLogConsole.Instance.Log(plsSuccess, FInFile, 'Keyboard '+FInFile+' compiled, output saved as '+FOutFile+'.', 0, 0) - else TProjectLogConsole.Instance.Log(plsFailure, FInFile, 'Keyboard '+FInFile+' could not be compiled.', 0, 0); - end; -end; - -procedure FixupPathSlashes(var path: string); -begin - path := path.Replace('/', '\', [rfReplaceAll]); -end; - - -end. - diff --git a/developer/src/kmcomp/manifest.in b/developer/src/kmcomp/manifest.in deleted file mode 100644 index 11ef5edb716..00000000000 --- a/developer/src/kmcomp/manifest.in +++ /dev/null @@ -1,22 +0,0 @@ - - - - Keyman Developer Command Line Compiler - - - - - - - - - - - - - - - - - - diff --git a/developer/src/kmcomp/manifest.rc b/developer/src/kmcomp/manifest.rc deleted file mode 100644 index 984fce31b2c..00000000000 --- a/developer/src/kmcomp/manifest.rc +++ /dev/null @@ -1 +0,0 @@ -1 24 manifest.xml diff --git a/developer/src/kmcomp/version.rc b/developer/src/kmcomp/version.rc deleted file mode 100644 index c4bec5f02e7..00000000000 --- a/developer/src/kmcomp/version.rc +++ /dev/null @@ -1,32 +0,0 @@ -#include "../../../common/windows/cpp/include/keymanversion.h" - -1 VERSIONINFO - FILEVERSION KV_FILEVERSION - PRODUCTVERSION KV_PRODUCTVERSION - FILEFLAGSMASK 0x3fL - FILEFLAGS 0x0L - FILEOS 0x4L - FILETYPE 0x1L - FILESUBTYPE 0x0L - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0C0904E4" - BEGIN - VALUE "CompanyName", KV_COMPANY_NAME - VALUE "FileDescription", "Keyman commandline compiler\0" - VALUE "FileVersion", KV_VERSION_STRING - VALUE "InternalName", "KMCOMP\0" - VALUE "LegalCopyright", KV_LEGAL_COPYRIGHT - VALUE "LegalTrademarks", KV_LEGAL_TRADEMARKS - VALUE "OriginalFilename", "KMCOMP.EXE\0" - VALUE "ProductName", "Keyman Developer\0" - VALUE "ProductVersion", KV_VERSION_STRING - VALUE "Comments", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1252 - END - END diff --git a/developer/src/kmconvert/Keyman.Developer.System.KMConvertParameters.pas b/developer/src/kmconvert/Keyman.Developer.System.KMConvertParameters.pas index 6835abf78ba..b58e5dd8fb5 100644 --- a/developer/src/kmconvert/Keyman.Developer.System.KMConvertParameters.pas +++ b/developer/src/kmconvert/Keyman.Developer.System.KMConvertParameters.pas @@ -279,7 +279,7 @@ function TKMConvertParameters.ValidateBCP47Tag(const component, tag: string): Bo if not TCanonicalLanguageCodeUtils.IsCanonical(tag, msg, False, False) then begin // Just a warning, because it's not illegal, just generates a warning - // kmcomp-side + // kmc-side OutputText('Warning: '+component+': '+msg); end; diff --git a/developer/src/kmconvert/Keyman.Developer.System.KeyboardProjectTemplate.pas b/developer/src/kmconvert/Keyman.Developer.System.KeyboardProjectTemplate.pas index 0d2fb9f05fd..09aa463909b 100644 --- a/developer/src/kmconvert/Keyman.Developer.System.KeyboardProjectTemplate.pas +++ b/developer/src/kmconvert/Keyman.Developer.System.KeyboardProjectTemplate.pas @@ -35,11 +35,9 @@ TKeyboardProjectTemplate = class(TProjectTemplate) procedure WriteKPJ; procedure WriteKVKS; procedure WriteTouchLayout; - procedure WriteKeyboardInfo; procedure WriteIcon; protected const - SFileTemplate_KeyboardInfo = '%s.keyboard_info'; // in root SDataPath_BasicKeyboard = 'basic-keyboard\'; function DataPath: string; override; @@ -104,8 +102,6 @@ procedure TKeyboardProjectTemplate.Generate; WriteKPJ; WriteRepositoryMetadata; - - WriteKeyboardInfo; end; function TKeyboardProjectTemplate.GetIconFilename: string; @@ -150,15 +146,6 @@ function TKeyboardProjectTemplate.HasTouchLayout: Boolean; Result := (TouchKeymanTargets+[ktAny]) * Targets <> []; end; -procedure TKeyboardProjectTemplate.WriteKeyboardInfo; -begin - // Write keyboardid.keyboard_info - Transform( - Format(SFileTemplate_KeyboardInfo, ['keyboard']), - Format(SFileTemplate_KeyboardInfo, [ID]) - ); -end; - procedure TKeyboardProjectTemplate.WriteKMN; var FKeyboardSource: string; @@ -227,7 +214,6 @@ procedure TKeyboardProjectTemplate.WriteKPJ; kpj.Files.Add(TOpenableProjectFile.Create(kpj, BasePath + ID + '\' + SFile_HistoryMD, nil)); kpj.Files.Add(TOpenableProjectFile.Create(kpj, BasePath + ID + '\' + SFile_LicenseMD, nil)); kpj.Files.Add(TOpenableProjectFile.Create(kpj, BasePath + ID + '\' + SFile_ReadmeMD, nil)); - kpj.Files.Add(TOpenableProjectFile.Create(kpj, BasePath + ID + '\' + Format(SFileTemplate_KeyboardInfo, [ID]), nil)); kpj.Save; finally @@ -285,6 +271,12 @@ procedure TKeyboardProjectTemplate.WriteKPS; kps.Files.Add(f); kps.Options.ReadmeFile := f; + // Add license + f := TPackageContentFile.Create(kps); + f.FileName := BasePath + ID + '\' + SFile_LicenseMD; + kps.Files.Add(f); + kps.Options.LicenseFile := f; + // Add metadata about the keyboard pk := TPackageKeyboard.Create(kps); pk.Name := Name; diff --git a/developer/src/kmconvert/Keyman.Developer.System.ModelProjectTemplate.pas b/developer/src/kmconvert/Keyman.Developer.System.ModelProjectTemplate.pas index ac7349fc469..be0d0c98254 100644 --- a/developer/src/kmconvert/Keyman.Developer.System.ModelProjectTemplate.pas +++ b/developer/src/kmconvert/Keyman.Developer.System.ModelProjectTemplate.pas @@ -179,6 +179,12 @@ procedure TModelProjectTemplate.WriteKPS; kps.Files.Add(f); kps.Options.ReadmeFile := f; + // Add license + f := TPackageContentFile.Create(kps); + f.FileName := BasePath + ID + '\' + SFile_LicenseMD; + kps.Files.Add(f); + kps.Options.LicenseFile := f; + // Add metadata about the lexical model plm := TPackageLexicalModel.Create(kps); plm.Name := Name; diff --git a/developer/src/kmconvert/data/basic-keyboard/keyboard.keyboard_info b/developer/src/kmconvert/data/basic-keyboard/keyboard.keyboard_info deleted file mode 100644 index 62777e5e129..00000000000 --- a/developer/src/kmconvert/data/basic-keyboard/keyboard.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - $LANGUAGES_KEYBOARD_INFO - ], - "description": "$NAME generated from template" -} diff --git a/developer/src/server/Makefile b/developer/src/server/Makefile index 0a580ea3d6a..d3786f01b8f 100644 --- a/developer/src/server/Makefile +++ b/developer/src/server/Makefile @@ -5,7 +5,7 @@ !include ..\Defines.mak build: .virtual - $(GIT_BASH_FOR_KEYMAN) build.sh --production --test + $(GIT_BASH_FOR_KEYMAN) build.sh configure build test installer clean: .virtual #TODO: move to build.sh `clean` target @@ -14,7 +14,10 @@ clean: .virtual -del tsconfig.tsbuildinfo signcode: - @rem nothing to do + $(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\server\build\src\win32\console\node-hide-console-window.node + $(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\server\build\src\win32\console\node-hide-console-window.x64.node + $(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\server\build\src\win32\trayicon\addon.node + $(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\server\build\src\win32\trayicon\addon.x64.node wrap-symbols: @rem nothing to do diff --git a/developer/src/server/build-addins.inc.sh b/developer/src/server/build-addins.inc.sh index cce1e9ae6f0..d48d44d1e74 100644 --- a/developer/src/server/build-addins.inc.sh +++ b/developer/src/server/build-addins.inc.sh @@ -11,7 +11,7 @@ isNodeX64() { # [[ $(file -b "$(which node)" | grep x86-64) ]] && echo 1 || echo 0 } -build_addins() { +do_build_addins() { local NODEX64=$(isNodeX64) local ARCH=x64 local TRAYICON_TARGET=addon.x64.node @@ -29,8 +29,13 @@ build_addins() { echo "TRAYICON_TARGET=$TRAYICON_TARGET" echo "HIDECONSOLE_TARGET=$HIDECONSOLE_TARGET" - HIDECONSOLE_TARGET="$KEYMAN_ROOT/developer/src/server/src/win32/console/$HIDECONSOLE_TARGET" - TRAYICON_TARGET="$KEYMAN_ROOT/developer/src/server/src/win32/trayicon/$TRAYICON_TARGET" + local HIDECONSOLE_SRC_TARGET="$KEYMAN_ROOT/developer/src/server/src/win32/console/$HIDECONSOLE_TARGET" + local HIDECONSOLE_BIN_TARGET="$KEYMAN_ROOT/developer/src/server/build/src/win32/console/$HIDECONSOLE_TARGET" + local TRAYICON_SRC_TARGET="$KEYMAN_ROOT/developer/src/server/src/win32/trayicon/$TRAYICON_TARGET" + local TRAYICON_BIN_TARGET="$KEYMAN_ROOT/developer/src/server/build/src/win32/trayicon/$TRAYICON_TARGET" + + mkdir -p "$(dirname "$HIDECONSOLE_BIN_TARGET")" + mkdir -p "$(dirname "$TRAYICON_BIN_TARGET")" # # Build node-windows-trayicon @@ -39,7 +44,8 @@ build_addins() { pushd "$KEYMAN_ROOT/node_modules/node-windows-trayicon" rm -rf build npx node-gyp clean configure build --arch=$ARCH --silent - cp build/Release/addon.node "$TRAYICON_TARGET" + cp build/Release/addon.node "$TRAYICON_SRC_TARGET" + cp build/Release/addon.node "$TRAYICON_BIN_TARGET" popd # @@ -49,7 +55,8 @@ build_addins() { pushd "$KEYMAN_ROOT/node_modules/hetrodo-node-hide-console-window-napi" rm -rf build npx node-gyp clean configure build --arch=$ARCH --silent - cp build/Release/node-hide-console-window.node "$HIDECONSOLE_TARGET" + cp build/Release/node-hide-console-window.node "$HIDECONSOLE_SRC_TARGET" + cp build/Release/node-hide-console-window.node "$HIDECONSOLE_BIN_TARGET" popd # @@ -57,10 +64,10 @@ build_addins() { # if (( NODEX64 )); then - [[ $(isFileX64 "$TRAYICON_TARGET") == 1 ]] || builder_die "$TRAYICON_TARGET should be 64-bit" - [[ $(isFileX64 "$HIDECONSOLE_TARGET") == 1 ]] || builder_die "$HIDECONSOLE_TARGET should be 64-bit" + [[ $(isFileX64 "$TRAYICON_BIN_TARGET") == 1 ]] || builder_die "$TRAYICON_TARGET should be 64-bit" + [[ $(isFileX64 "$HIDECONSOLE_BIN_TARGET") == 1 ]] || builder_die "$HIDECONSOLE_TARGET should be 64-bit" else - [[ $(isFileX64 "$TRAYICON_TARGET") == 0 ]] || builder_die "$TRAYICON_TARGET should not be 64-bit" - [[ $(isFileX64 "$HIDECONSOLE_TARGET") == 0 ]] || builder_die "$HIDECONSOLE_TARGET should not be 64-bit" + [[ $(isFileX64 "$TRAYICON_BIN_TARGET") == 0 ]] || builder_die "$TRAYICON_TARGET should not be 64-bit" + [[ $(isFileX64 "$HIDECONSOLE_BIN_TARGET") == 0 ]] || builder_die "$HIDECONSOLE_TARGET should not be 64-bit" fi } diff --git a/developer/src/server/build.sh b/developer/src/server/build.sh index c735e26fecb..51523c52a3e 100755 --- a/developer/src/server/build.sh +++ b/developer/src/server/build.sh @@ -1,10 +1,4 @@ #!/usr/bin/env bash -# -# Compiles Keyman Developer Server for deployment -# - -# Exit on command failure and when using unset variables: -set -eu ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary @@ -16,121 +10,68 @@ EX_USAGE=64 . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" . "$KEYMAN_ROOT/resources/build/jq.inc.sh" -pushd "$(dirname "$THIS_SCRIPT")" - -# Build the main script. -build () { - npm run build || builder_die "Could not build top-level JavaScript file." +cd "$THIS_SCRIPT_PATH" + +builder_describe "Build Keyman Developer Server" \ + @/common/web/keyman-version \ + @/web \ + clean \ + configure \ + build \ + test \ + "installer Prepare for Keyman Developer installer" \ + "publish Publish to NPM" \ + ":server Keyman Developer Server main program" \ + ":addins Windows addins for GUI integration" + +builder_describe_internal_dependency \ + publish:server build:server \ + publish:server build:addins + +builder_describe_outputs \ + configure:server /node_modules \ + configure:addins /node_modules \ + build:server /developer/src/server/build/src/index.js \ + build:addins /developer/src/server/build/src/win32/trayicon/addon.node + +builder_parse "$@" + +#------------------------------------------------------------------------------------------------------------------- + +function clean_server() { + rm -rf "$THIS_SCRIPT_PATH/build" + rm -rf "$THIS_SCRIPT_PATH/node_modules" + rm -rf "$THIS_SCRIPT_PATH/tsconfig.tsbuildinfo" + rm -rf "$THIS_SCRIPT_PATH/src/site/resource" + + # No longer in use, cleanup from previous versions: + rm -rf "$THIS_SCRIPT_PATH/dist" } -display_usage ( ) { - echo "Usage: $0 [--production|--watch] [--test|--tdd]" - echo " $0 --help" - echo - echo " --help displays this screen and exits" - echo " --production, -p builds production release in /developer/bin/server/" - echo " --skip-package-install, -S don't run npm install (not valid with --production)" - echo " --test, -t runs unit tests after building" - #echo " --watch, -w builds dev server in watch mode" - echo " --tdd runs unit tests WITHOUT building" - echo " --no-build-addins don't build/copy Win32 addins" - echo " --no-build-kmw don't build KeymanWeb" - echo " --no-copy-kmw don't copy KeymanWeb" -} - -################################ Main script ################################ - -run_tests=0 -production=0 -build=1 -install_dependencies=1 -build_keymanweb=1 -copy_keymanweb=1 -build_addins=1 - -# Process command-line arguments -while [[ $# -gt 0 ]] ; do - key="$1" - case $key in - --help|-h|-?) - display_usage - exit - ;; - --production|-p) - production=1 - ;; - --skip-package-install|-S) - install_dependencies=0 - ;; - --no-build-kmw) - build_keymanweb=0 - ;; - --no-build-addins) - build_addins=0 - ;; - --no-copy-kmw) - copy_keymanweb=0 - ;; - --test) - run_tests=1 - ;; - --tdd) - run_tests=1 - build=0 - install_dependencies=0 - ;; - *) - echo "$0: invalid option: $key" - display_usage - exit $EX_USAGE - esac - shift # past the processed argument -done - -# ---------------------------------------- -# Install dependencies -# ---------------------------------------- - -# Check if Node.JS/npm is installed. -type npm >/dev/null ||\ - builder_die "Build environment setup error detected! Please ensure Node.js is installed!" - -if (( install_dependencies )) ; then +function configure_server() { verify_npm_setup # See https://github.com/bubenshchykov/ngrok/issues/254, https://github.com/bubenshchykov/ngrok/pull/255 # TODO: this is horrible; is there a way we can avoid this? rm -f "$KEYMAN_ROOT"/node_modules/ngrok/bin/ngrok.exe -fi - -# ---------------------------------------- -# Rebuild and bundle addins -# ---------------------------------------- +} -if (( build_addins )); then - . ./build-addins.inc.sh +function build_addins() { + # Rebuild and bundle addins + source ./build-addins.inc.sh # If we have an x64 version of node installed if [[ $(isNodeX64) ]]; then - build_addins + do_build_addins fi # Build with the Keyman Developer x86 version of node - PATH="$KEYMAN_ROOT/developer/src/inst/node/dist:$PATH" build_addins -fi - -# ---------------------------------------- -# Build and bundle KeymanWeb -# ---------------------------------------- - -if (( build_keymanweb )); then - pushd "$KEYMAN_ROOT/web/" - ./build.sh build --debug - popd -fi + PATH="$KEYMAN_ROOT/developer/src/inst/node/dist:$PATH" do_build_addins +} -if (( copy_keymanweb )); then - WEB_SRC="$KEYMAN_ROOT/web/build/publish/debug" - DST="$(dirname "$THIS_SCRIPT")/src/site/resource" +function build_server() { + # Copy keymanweb build artifacts + local WEB_SRC="$KEYMAN_ROOT/web/build/publish/debug" + local DST="$THIS_SCRIPT_PATH/src/site/resource" rm -rf "$DST" mkdir -p "$DST/osk" @@ -140,33 +81,26 @@ if (( copy_keymanweb )); then cp -R "$WEB_SRC/ui/"* "$DST/ui/" cp "$KEYMAN_ROOT/web/LICENSE" "$DST/" cp "$KEYMAN_ROOT/web/README.md" "$DST/" -fi - -# ---------------------------------------- -# Build the project -# ---------------------------------------- -npm run build || builder_die "Compilation failed." -echo "Typescript compilation successful." + # Build server + tsc --build -# ---------------------------------------- -# Unit tests -# ---------------------------------------- + # Post build + mkdir -p "$THIS_SCRIPT_PATH/build/src/site/" + mkdir -p "$THIS_SCRIPT_PATH/build/src/win32/" + cp -r "$THIS_SCRIPT_PATH/src/site/"** "$THIS_SCRIPT_PATH/build/src/site/" + cp -r "$THIS_SCRIPT_PATH/src/win32/"** "$THIS_SCRIPT_PATH/build/src/win32/" -if (( run_tests )); then - npm test || builder_die "Tests failed" -fi - -# ---------------------------------------- -# Deploy to dist/ -# ---------------------------------------- + replaceVersionStrings "$THIS_SCRIPT_PATH/build/src/site/lib/sentry/init.js.in" "$THIS_SCRIPT_PATH/build/src/site/lib/sentry/init.js" + rm "$THIS_SCRIPT_PATH/build/src/site/lib/sentry/init.js.in" +} -if (( production )) ; then +function installer_server() { # We need to build in a tmp folder so that npm doesn't get confused by our # monorepo setup, and so we can copy the relevant node_modules in, because # we'll need them in order to build the deployable version. - PRODBUILDTEMP=`mktemp -d` + local PRODBUILDTEMP=`mktemp -d` echo "Preparing in $PRODBUILDTEMP" # Remove @keymanapp devDependencies because they won't install outside the # monorepo context @@ -180,14 +114,35 @@ if (( production )) ; then rm -f node_modules/ngrok/bin/ngrok.exe popd - # @keymanapp/keyman-version is required in dist now but we need to copy it in manually + # @keymanapp/keyman-version is required in build/ now but we need to copy it in manually mkdir -p "$PRODBUILDTEMP/node_modules/@keymanapp/" cp -R "$KEYMAN_ROOT/node_modules/@keymanapp/keyman-version/" "$PRODBUILDTEMP/node_modules/@keymanapp/" # We'll build in the $KEYMAN_ROOT/developer/bin/server/ folder rm -rf "$KEYMAN_ROOT/developer/bin/server/" - mkdir -p "$KEYMAN_ROOT/developer/bin/server/dist/" - cp -R dist/* "$KEYMAN_ROOT/developer/bin/server/dist/" + mkdir -p "$KEYMAN_ROOT/developer/bin/server/build/" + cp -R build/* "$KEYMAN_ROOT/developer/bin/server/build/" cp -R "$PRODBUILDTEMP"/* "$KEYMAN_ROOT/developer/bin/server/" rm -rf "$PRODBUILDTEMP" -fi +} + +function test_server() { + # eslint . + tsc --build test + # c8 --reporter=lcov --reporter=text + mocha +} + +builder_run_action clean:server clean_server +builder_run_action configure:server configure_server +builder_run_action build:server build_server +builder_run_action build:addins build_addins +builder_run_action test:server test_server +# builder_run_action test:addins # no op +builder_run_action installer:server installer_server + +# TODO: consider 'watch' +# function watch_server() { +# tsc-watch --onSuccess "node --inspect ." --onFailure "node --inspect ." +# } +# builder_run_action watch:server watch_server diff --git a/developer/src/server/package.json b/developer/src/server/package.json index dd779db1a4d..fbba3454395 100644 --- a/developer/src/server/package.json +++ b/developer/src/server/package.json @@ -1,13 +1,11 @@ { "name": "@keymanapp/developer-server", "description": "Keyman Developer backend server", - "main": "dist/index.js", + "main": "build/src/index.js", + "type": "module", "scripts": { - "build": "tsc -b", - "postbuild": "npx gosh ./postbuild.sh", - "prod": "node .", - "test": "mocha", - "watch": "npm run postbuild && tsc-watch --onSuccess \"node --inspect .\" --onFailure \"node --inspect .\"" + "build": "npx gosh ./build.sh build", + "test": "npx gosh ./build.sh test" }, "license": "MIT", "dependencies": { @@ -24,13 +22,13 @@ "node-windows-trayicon": "keymanapp/node-windows-trayicon#keyman-16.0" }, "devDependencies": { - "@keymanapp/resources-gosh": "*", "@keymanapp/keyman-version": "*", + "@keymanapp/resources-gosh": "*", "@types/chai": "^4.3.0", "@types/express": "^4.17.13", "@types/mocha": "^9.1.0", "@types/multer": "^1.4.7", - "@types/node": "^17.0.0", + "@types/node": "^20.4.1", "@types/ws": "^8.2.2", "chai": "^4.3.4", "copyfiles": "^2.4.1", @@ -41,6 +39,6 @@ }, "mocha": { "require": "ts-node/register", - "spec": "**/*.test.ts" + "spec": "build/**/*.test.js" } } diff --git a/developer/src/server/postbuild.sh b/developer/src/server/postbuild.sh deleted file mode 100755 index e4b90e7e50b..00000000000 --- a/developer/src/server/postbuild.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# -# postbuild script run by npm during/after a build -# - -# 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 - -. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" - -LOCAL_ROOT="$(dirname "$THIS_SCRIPT")" - -mkdir -p "$LOCAL_ROOT/dist/site/" -mkdir -p "$LOCAL_ROOT/dist/win32/" -cp -r "$LOCAL_ROOT/src/site/"** "$LOCAL_ROOT/dist/site/" -cp -r "$LOCAL_ROOT/src/win32/"** "$LOCAL_ROOT/dist/win32/" - -replaceVersionStrings "$LOCAL_ROOT/dist/site/lib/sentry/init.js.in" "$LOCAL_ROOT/dist/site/lib/sentry/init.js" -rm "$LOCAL_ROOT/dist/site/lib/sentry/init.js.in" diff --git a/developer/src/server/src/config.ts b/developer/src/server/src/config.ts index 5c67cd8c7e5..6197cde05aa 100644 --- a/developer/src/server/src/config.ts +++ b/developer/src/server/src/config.ts @@ -1,4 +1,4 @@ -import fs = require('fs'); +import * as fs from 'fs'; export class Configuration { public readonly appDataPath: string; diff --git a/developer/src/server/src/data.ts b/developer/src/server/src/data.ts index fcc750495d7..58763f07013 100644 --- a/developer/src/server/src/data.ts +++ b/developer/src/server/src/data.ts @@ -1,5 +1,5 @@ -import fs = require('fs'); -import { configuration } from './config'; +import * as fs from 'fs'; +import { configuration } from './config.js'; export interface DebugObject { id: string; diff --git a/developer/src/server/src/environment.ts b/developer/src/server/src/environment.ts index be359f81d32..f3e05660245 100644 --- a/developer/src/server/src/environment.ts +++ b/developer/src/server/src/environment.ts @@ -1,5 +1,5 @@ -import { extractVersionData } from './version-data'; +import { extractVersionData } from './version-data.js'; // TODO: environment should be just KEYMAN_VERSION -const KEYMAN_VERSION = require("@keymanapp/keyman-version").KEYMAN_VERSION; +import KEYMAN_VERSION from "@keymanapp/keyman-version"; export const environment = extractVersionData(KEYMAN_VERSION.VERSION_WITH_TAG); diff --git a/developer/src/server/src/handlers/api/debugobject/get.ts b/developer/src/server/src/handlers/api/debugobject/get.ts index 3da2896d556..a701ca3a8f3 100644 --- a/developer/src/server/src/handlers/api/debugobject/get.ts +++ b/developer/src/server/src/handlers/api/debugobject/get.ts @@ -1,6 +1,6 @@ -import chalk = require('chalk'); -import express = require('express'); -import { DebugObject, isValidId, simplifyId } from "../../../data"; +import * as express from 'express'; +import chalk from 'chalk'; +import { DebugObject, isValidId, simplifyId } from "../../../data.js"; export default function apiGet (data: { [id: string]: DebugObject }, req: express.Request, res: express.Response, next: express.NextFunction) { let id = req.query['id'] as string; diff --git a/developer/src/server/src/handlers/api/debugobject/register.ts b/developer/src/server/src/handlers/api/debugobject/register.ts index 9b52b577d88..a1f7a72cd4c 100644 --- a/developer/src/server/src/handlers/api/debugobject/register.ts +++ b/developer/src/server/src/handlers/api/debugobject/register.ts @@ -1,9 +1,9 @@ -import express = require('express'); -import { DebugObject, isValidId, simplifyId } from "../../../data"; -import fs = require('fs'); -import crypto = require('crypto'); -import { configuration } from '../../../config'; -import chalk = require('chalk'); +import * as express from 'express'; +import { DebugObject, isValidId, simplifyId } from "../../../data.js"; +import * as fs from 'fs'; +import * as crypto from 'crypto'; +import { configuration } from '../../../config.js'; +import chalk from 'chalk'; // We allow only 12 objects of each type in the cache const MAX_OBJECTS = 12; diff --git a/developer/src/server/src/handlers/api/debugobject/unregister.ts b/developer/src/server/src/handlers/api/debugobject/unregister.ts index 6473c2edbde..59a846d135d 100644 --- a/developer/src/server/src/handlers/api/debugobject/unregister.ts +++ b/developer/src/server/src/handlers/api/debugobject/unregister.ts @@ -1,7 +1,7 @@ -import express = require('express'); -import { DebugObject, isValidId, simplifyId } from "../../../data"; -import fs = require('fs'); -import chalk = require('chalk'); +import * as express from 'express'; +import { DebugObject, isValidId, simplifyId } from "../../../data.js"; +import * as fs from 'fs'; +import chalk from 'chalk'; export default function apiUnregister (root:{ [id: string]: O }, req: express.Request, res: express.Response, next: express.NextFunction) { let id = req.body['id']; diff --git a/developer/src/server/src/handlers/api/font/register.ts b/developer/src/server/src/handlers/api/font/register.ts index 14d60e8e12f..8da3646a63f 100644 --- a/developer/src/server/src/handlers/api/font/register.ts +++ b/developer/src/server/src/handlers/api/font/register.ts @@ -1,6 +1,6 @@ -import chalk = require('chalk'); -import express = require('express'); -import { data, DebugFont, simplifyId } from "../../../data"; +import chalk from 'chalk'; +import * as express from 'express'; +import { data, DebugFont, simplifyId } from "../../../data.js"; export default function apiKeyboardRegister (req: express.Request, res: express.Response, next: express.NextFunction) { const id = simplifyId(req.body['id']); diff --git a/developer/src/server/src/handlers/api/keyboard/register.ts b/developer/src/server/src/handlers/api/keyboard/register.ts index 02bdd3dde71..bb89d7b4c30 100644 --- a/developer/src/server/src/handlers/api/keyboard/register.ts +++ b/developer/src/server/src/handlers/api/keyboard/register.ts @@ -1,6 +1,6 @@ -import chalk = require('chalk'); -import express = require('express'); -import { data, DebugKeyboard } from "../../../data"; +import chalk from 'chalk'; +import * as express from 'express'; +import { data, DebugKeyboard } from "../../../data.js"; export default function apiKeyboardRegister (req: express.Request, res: express.Response, next: express.NextFunction) { const keyboard: DebugKeyboard = data.keyboards[req.body['id']]; diff --git a/developer/src/server/src/handlers/api/package/register.ts b/developer/src/server/src/handlers/api/package/register.ts index c21340c7fe7..cf6e2388468 100644 --- a/developer/src/server/src/handlers/api/package/register.ts +++ b/developer/src/server/src/handlers/api/package/register.ts @@ -1,6 +1,6 @@ -import chalk = require('chalk'); -import express = require('express'); -import { data, DebugPackage } from "../../../data"; +import chalk from 'chalk'; +import * as express from 'express'; +import { data, DebugPackage } from "../../../data.js"; export default function apiPackageRegister (req: express.Request, res: express.Response, next: express.NextFunction) { const kmp: DebugPackage = data.packages[req.body['id']]; diff --git a/developer/src/server/src/handlers/inc/keyboards-css.ts b/developer/src/server/src/handlers/inc/keyboards-css.ts index 9302fd11648..ed4cc7e15b8 100644 --- a/developer/src/server/src/handlers/inc/keyboards-css.ts +++ b/developer/src/server/src/handlers/inc/keyboards-css.ts @@ -1,6 +1,6 @@ -import express = require('express'); -import path = require('path'); -import { data, SiteData } from "../../data"; +import * as express from 'express'; +import * as path from 'path'; +import { data, SiteData } from "../../data.js"; export default function handleIncKeyboardsCss (req: express.Request, res: express.Response) { let headers = {"Content-Type": "text/css"}; diff --git a/developer/src/server/src/handlers/inc/keyboards-js.ts b/developer/src/server/src/handlers/inc/keyboards-js.ts index ee09fa0f377..3c42702bf95 100644 --- a/developer/src/server/src/handlers/inc/keyboards-js.ts +++ b/developer/src/server/src/handlers/inc/keyboards-js.ts @@ -1,5 +1,5 @@ -import express = require('express'); -import { SiteData, data } from "../../data"; +import * as express from 'express'; +import { SiteData, data } from "../../data.js"; export default function handleIncKeyboardsJs (req: express.Request, res: express.Response) { let headers = {"Content-Type": "application/javascript"}; diff --git a/developer/src/server/src/handlers/inc/packages-json.ts b/developer/src/server/src/handlers/inc/packages-json.ts index a27fb00615d..44449a5ef66 100644 --- a/developer/src/server/src/handlers/inc/packages-json.ts +++ b/developer/src/server/src/handlers/inc/packages-json.ts @@ -1,6 +1,6 @@ -import express = require('express'); -import { data } from "../../data"; -import { environment } from '../../environment'; +import * as express from 'express'; +import { data } from "../../data.js"; +import { environment } from '../../environment.js'; export default function handleIncPackagesJson (req: express.Request, res: express.Response) { const packages = Object.keys(data.packages).map(id => { return { id: id, filename: id+'.kmp', name: data.packages[id].name} }); diff --git a/developer/src/server/src/index.ts b/developer/src/server/src/index.ts index a6d109f1bad..a2975545730 100644 --- a/developer/src/server/src/index.ts +++ b/developer/src/server/src/index.ts @@ -1,21 +1,21 @@ -import { environment } from './environment'; +import { environment } from './environment.js'; -const Sentry = require("@sentry/node"); +import * as Sentry from '@sentry/node'; Sentry.init({ dsn: 'https://39b25a09410349a58fe12aaf721565af@o1005580.ingest.sentry.io/5983519', // Keyman Developer environment: environment.versionEnvironment, release: environment.versionGitTag }); -import express = require('express'); -import ws = require('ws'); -import os = require('os'); -import multer = require('multer'); -import fs = require('fs'); -import setupRoutes from './routes'; -import { configuration } from './config'; -import tray from './tray'; -import chalk = require('chalk'); +import express from 'express'; +import * as ws from 'ws'; +import * as os from 'os'; +import multer from 'multer'; +import * as fs from 'fs'; +import setupRoutes from './routes.js'; +import { configuration } from './config.js'; +import tray from './tray.js'; +import chalk from 'chalk'; const options = { ngrokLog: false, // Set this to true if you need to see ngrok logs in the console @@ -58,7 +58,7 @@ const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 102 /* Websockets */ -const wsServer = new ws.Server({ noServer: true }); +const wsServer = new ws.WebSocketServer({ noServer: true }); wsServer.on('connection', socket => { socket.on('message', (message) => { console.debug('wsServer.socket.onmessage: '+message.toString()); @@ -87,7 +87,7 @@ server.on('upgrade', (request, socket, head) => { configuration.ngrokEndpoint = ''; if(configuration.useNgrok && os.platform() == 'win32' && fs.existsSync(configuration.ngrokBinPath)) { - const ngrok = require('ngrok'); + const ngrok: any = await import('ngrok'); (async function() { configuration.ngrokEndpoint = await ngrok.connect({ proto: 'http', diff --git a/developer/src/server/src/ngrok.d.ts b/developer/src/server/src/ngrok.d.ts new file mode 100644 index 00000000000..61f00d7a0b3 --- /dev/null +++ b/developer/src/server/src/ngrok.d.ts @@ -0,0 +1 @@ +declare module 'ngrok'; \ No newline at end of file diff --git a/developer/src/server/src/routes.ts b/developer/src/server/src/routes.ts index fdd2e421aad..20c09f838f7 100644 --- a/developer/src/server/src/routes.ts +++ b/developer/src/server/src/routes.ts @@ -1,21 +1,21 @@ -import express = require('express'); -import multer = require('multer'); -import ws = require('ws'); -import handleIncKeyboardsJs from './handlers/inc/keyboards-js'; -import { data, DebugFont, DebugKeyboard, DebugModel, DebugObject, DebugPackage, isValidId } from './data'; -import apiGet from './handlers/api/debugobject/get'; -import apiRegister, { apiRegisterFile } from './handlers/api/debugobject/register'; -import apiKeyboardRegister from './handlers/api/keyboard/register'; -import apiFontRegister from './handlers/api/font/register'; -import apiUnregister from './handlers/api/debugobject/unregister'; -import handleIncPackagesJson from './handlers/inc/packages-json'; -import apiPackageRegister from './handlers/api/package/register'; -import handleIncKeyboardsCss from './handlers/inc/keyboards-css'; -import { Environment } from './version-data'; -import { configuration } from './config'; -import chalk = require('chalk'); - -export default function setupRoutes(app: express.Express, upload: multer.Multer, wsServer: ws.Server, environment: Environment ) { +import * as express from 'express'; +import * as ws from 'ws'; +import * as multer from 'multer'; +import handleIncKeyboardsJs from './handlers/inc/keyboards-js.js'; +import { data, DebugFont, DebugKeyboard, DebugModel, DebugObject, DebugPackage, isValidId } from './data.js'; +import apiGet from './handlers/api/debugobject/get.js'; +import apiRegister, { apiRegisterFile } from './handlers/api/debugobject/register.js'; +import apiKeyboardRegister from './handlers/api/keyboard/register.js'; +import apiFontRegister from './handlers/api/font/register.js'; +import apiUnregister from './handlers/api/debugobject/unregister.js'; +import handleIncPackagesJson from './handlers/inc/packages-json.js'; +import apiPackageRegister from './handlers/api/package/register.js'; +import handleIncKeyboardsCss from './handlers/inc/keyboards-css.js'; +import { Environment } from './version-data.js'; +import { configuration } from './config.js'; +import chalk from 'chalk'; + +export default function setupRoutes(app: express.Express, upload: multer.Multer, wsServer: ws.WebSocketServer, environment: Environment ) { /* Middleware - JSON and logging */ @@ -49,7 +49,7 @@ export default function setupRoutes(app: express.Express, upload: multer.Multer, /* All routes */ - app.use('/', express.static('dist/site')); + app.use('/', express.static('build/src/site')); app.post('/upload', localhostOnly, upload.single('file'), (req, res, next) => { const name = req.file.originalname; @@ -170,7 +170,7 @@ export default function setupRoutes(app: express.Express, upload: multer.Multer, /* Utility functions */ -function notifyClients(wsServer: ws.Server, res: express.Request, req: express.Response, next: express.NextFunction) { +function notifyClients(wsServer: ws.WebSocketServer, res: express.Request, req: express.Response, next: express.NextFunction) { wsServer.clients.forEach(c => { c.send('refresh', (err) => { if(err) console.error(chalk.red('Websocket send error '+err.message)); diff --git a/developer/src/server/src/tray.ts b/developer/src/server/src/tray.ts index 0c9c3bf2f65..780d9c792c4 100644 --- a/developer/src/server/src/tray.ts +++ b/developer/src/server/src/tray.ts @@ -1,4 +1,4 @@ -import os = require('os'); +import * as os from 'os'; class TrayStub { public start(localPort: number, ngrokAddress: string) {} @@ -9,7 +9,7 @@ class TrayStub { let tray = new TrayStub(); if(os.platform() == 'win32') { - const Win32Tray = require('./win32-tray'); + const { Win32Tray } = await import('./win32-tray.js'); tray = new Win32Tray(); } diff --git a/developer/src/server/src/win32-tray.ts b/developer/src/server/src/win32-tray.ts index b17c775f00c..02ef0a4db80 100644 --- a/developer/src/server/src/win32-tray.ts +++ b/developer/src/server/src/win32-tray.ts @@ -1,14 +1,15 @@ -import path = require("path"); +import * as path from "path"; +import * as url from 'url'; // TODO: this is a Windows-only tray icon. There are a number of // cross-platform solutions but none of them are wonderful. We // should replace this when we find a decent one. -const WindowsTrayicon = require("./win32/trayicon"); -const WindowsConsole = require("./win32/console"); -const open = require("open"); +import WindowsTrayicon from "./win32/trayicon/index.js"; +import WindowsConsole from "./win32/console/index.js"; +import open from 'open'; -module.exports = class Win32Tray { +export class Win32Tray { private myTrayApp: any; public restart(localPort: number, ngrokAddress: string) { @@ -55,7 +56,7 @@ module.exports = class Win32Tray { this.myTrayApp = new WindowsTrayicon({ title: "Keyman Developer Server", - icon: path.resolve(__dirname, "site", "favicon.ico"), + icon: path.resolve(url.fileURLToPath(new URL('.', import.meta.url)), "site", "favicon.ico"), menu: menu }); diff --git a/developer/src/server/src/win32/console/index.d.ts b/developer/src/server/src/win32/console/index.d.ts deleted file mode 100644 index ac9e647fa40..00000000000 --- a/developer/src/server/src/win32/console/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module "node-hide-console-window" { - function hideConsole(): void - - function showConsole(): void -} \ No newline at end of file diff --git a/developer/src/server/src/win32/console/index.js b/developer/src/server/src/win32/console/index.js deleted file mode 100644 index 26026657a0b..00000000000 --- a/developer/src/server/src/win32/console/index.js +++ /dev/null @@ -1,7 +0,0 @@ -const os = require('os'); -const {showConsole, hideConsole} = require(os.arch() == 'ia32' ? "./node-hide-console-window" : "./node-hide-console-window.x64"); - -module.exports = { - showConsole, - hideConsole -} \ No newline at end of file diff --git a/developer/src/server/src/win32/console/index.ts b/developer/src/server/src/win32/console/index.ts new file mode 100644 index 00000000000..1bee9ada877 --- /dev/null +++ b/developer/src/server/src/win32/console/index.ts @@ -0,0 +1,6 @@ +import * as os from 'os'; +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); +const {showConsole, hideConsole} = require(os.arch() == 'ia32' ? "./node-hide-console-window" : "./node-hide-console-window.x64"); + +export default { showConsole, hideConsole }; diff --git a/developer/src/server/src/win32/trayicon/index.js b/developer/src/server/src/win32/trayicon/index.ts similarity index 68% rename from developer/src/server/src/win32/trayicon/index.js rename to developer/src/server/src/win32/trayicon/index.ts index 1e50cef3da0..6b313af262f 100644 --- a/developer/src/server/src/win32/trayicon/index.js +++ b/developer/src/server/src/win32/trayicon/index.ts @@ -1,8 +1,17 @@ -const os = require('os'); +import * as os from 'os'; +import { createRequire } from 'node:module'; +const require = createRequire(import.meta.url); const { CTrayIconContainer } = require(os.arch() == 'ia32' ? "./addon" : "./addon.x64"); class WindowsTrayicon { - constructor(options) { + __itemCallbacks: any[]; + __icon: any; + __trayTitle: any; + __menuItems: any; + __nativeTray: any; + + + constructor(options: any) { this.__itemCallbacks = []; this.__icon = options.icon; this.__trayTitle = options.title || ""; @@ -12,7 +21,7 @@ class WindowsTrayicon { for (const item of this.__menuItems) { this.__nativeTray.AddMenuItem(item.id, item.caption); } - this.__nativeTray.OnMenuItem((id) => { + this.__nativeTray.OnMenuItem((id: any) => { for (const cb of this.__itemCallbacks) { cb(id); } @@ -23,15 +32,15 @@ class WindowsTrayicon { } this.__nativeTray.Start(); } - item(cb) { + item(cb: any) { if ("function" === typeof cb) { this.__itemCallbacks.push(cb); } } - balloon(title, text, timeout = 5000) { + balloon(title: any, text: any, timeout = 5000) { return new Promise((resolve) => { this.__nativeTray.ShowBalloon(title, text, timeout, () => { - resolve(); + resolve(null); }) }); } @@ -40,4 +49,4 @@ class WindowsTrayicon { } } -module.exports = WindowsTrayicon; +export default WindowsTrayicon; diff --git a/developer/src/server/test/tsconfig.json b/developer/src/server/test/tsconfig.json new file mode 100644 index 00000000000..fa377b0fde1 --- /dev/null +++ b/developer/src/server/test/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../../tsconfig.base.json", + + "compilerOptions": { + "rootDir": ".", + "rootDirs": ["./", "../src/"], + "outDir": "../build/test", + "baseUrl": ".", + }, + "include": [ + "**/*.test.ts" + ], + "references": [ + { "path": "../" }, + ] +} \ No newline at end of file diff --git a/developer/src/server/src/version-data.test.ts b/developer/src/server/test/version-data.test.ts similarity index 97% rename from developer/src/server/src/version-data.test.ts rename to developer/src/server/test/version-data.test.ts index 5f33932b3bd..74762b7d7f8 100644 --- a/developer/src/server/src/version-data.test.ts +++ b/developer/src/server/test/version-data.test.ts @@ -1,8 +1,6 @@ import {assert} from 'chai'; import 'mocha'; - -import { extractVersionData } from './version-data'; - +import { extractVersionData } from '../src/version-data.js'; describe('extractVersionData', function() { it('should parse version strings', function() { diff --git a/developer/src/server/tsconfig.json b/developer/src/server/tsconfig.json index 280b535dd9e..e5adde95784 100644 --- a/developer/src/server/tsconfig.json +++ b/developer/src/server/tsconfig.json @@ -1,13 +1,13 @@ { - "extends": "../../../tsconfig-base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { - "module": "commonjs", "target": "es2022", - "moduleResolution": "node", "sourceMap": true, - "outDir": "dist/", + "outDir": "build/src/", "rootDir": "src/", + "baseUrl": ".", + "declaration": true, "alwaysStrict": true, "noImplicitThis": true, @@ -16,6 +16,7 @@ "strictBindCallApply": true, "strictFunctionTypes": true, "noUnusedLocals": true, + "allowJs": true, "lib": ["es2022"] }, "include": [ diff --git a/developer/src/test/auto/Makefile b/developer/src/test/auto/Makefile index 03e66911013..8f815f3b7b7 100644 --- a/developer/src/test/auto/Makefile +++ b/developer/src/test/auto/Makefile @@ -15,11 +15,8 @@ test: developer-tests: \ compile-supplementary-support \ - keyboard-info \ keyboard-js-info \ - keyboard-package-versions \ kmcomp \ - kmcomp-empty-store-names \ kmcomp-x64-structures \ kmconvert \ kmx-file-languages \ @@ -38,10 +35,6 @@ compile-supplementary-support: .virtual cd $(DEVELOPER_ROOT)\src\test\auto\compile-supplementary-support $(MAKE) $(TARGET) -keyboard-info: .virtual - cd $(DEVELOPER_ROOT)\src\test\auto\keyboard-info - $(MAKE) $(TARGET) - keyboard-js-info: .virtual cd $(DEVELOPER_ROOT)\src\test\auto\keyboard-js-info $(MAKE) $(TARGET) @@ -50,19 +43,10 @@ kmx-file-languages: .virtual cd $(DEVELOPER_ROOT)\src\test\auto\kmx-file-languages $(MAKE) $(TARGET) -keyboard-package-versions: .virtual - cd $(DEVELOPER_ROOT)\src\test\auto\keyboard-package-versions - $(MAKE) $(TARGET) - kmcomp: .virtual cd $(DEVELOPER_ROOT)\src\test\auto\kmcomp $(MAKE) $(TARGET) -kmcomp-empty-store-names: .virtual - rem TODO -# cd $(DEVELOPER_ROOT)\src\test\auto\kmcomp-empty-store-names -# $(MAKE) $(TARGET) - kmcomp-x64-structures: .virtual cd $(DEVELOPER_ROOT)\src\test\auto\kmcomp-x64-structures $(MAKE) $(TARGET) diff --git a/developer/src/test/auto/compile-supplementary-support/Makefile b/developer/src/test/auto/compile-supplementary-support/Makefile index b41430d3d9c..f2bff60933d 100644 --- a/developer/src/test/auto/compile-supplementary-support/Makefile +++ b/developer/src/test/auto/compile-supplementary-support/Makefile @@ -2,38 +2,31 @@ # test if KS=1 is in the right files, both in debug and non-debug versions # -KMCOMP="..\..\..\..\bin\kmcomp.exe" -KMCOMPD=$(KMCOMP) -d +KMC="..\..\..\..\bin\kmc.cmd" build +KMC_DEBUG=$(KMC) -d test: test-nodebug test-debug +KMN=i3317_nosupp.kmn i3317_withsupp.kmn i3317_withsupp_incontext.kmn i3317_withsupp_inmatch.kmn i3317_withsupp_innomatch.kmn i3317_withsupp_instore.kmn + +# TODO: replace with build.sh script test-nodebug: - $(KMCOMP) i3317_nosupp.kmn i3317_nosupp-1.0.js - findstr /v /m "KS=1" i3317_nosupp-1.0.js - $(KMCOMP) i3317_withsupp.kmn i3317_withsupp-1.0.js - findstr /m "KS=1" i3317_withsupp-1.0.js - $(KMCOMP) i3317_withsupp_incontext.kmn i3317_withsupp_incontext-1.0.js - findstr /m "KS=1" i3317_withsupp_incontext-1.0.js - $(KMCOMP) i3317_withsupp_inmatch.kmn i3317_withsupp_inmatch-1.0.js - findstr /m "KS=1" i3317_withsupp_inmatch-1.0.js - $(KMCOMP) i3317_withsupp_innomatch.kmn i3317_withsupp_innomatch-1.0.js - findstr /m "KS=1" i3317_withsupp_innomatch-1.0.js - $(KMCOMP) i3317_withsupp_instore.kmn i3317_withsupp_instore-1.0.js - findstr /m "KS=1" i3317_withsupp_instore-1.0.js + $(KMC) $(KMN) + findstr /v /m "KS=1" i3317_nosupp.js + findstr /m "KS=1" i3317_withsupp.js + findstr /m "KS=1" i3317_withsupp_incontext.js + findstr /m "KS=1" i3317_withsupp_inmatch.js + findstr /m "KS=1" i3317_withsupp_innomatch.js + findstr /m "KS=1" i3317_withsupp_instore.js test-debug: - $(KMCOMPD) i3317_nosupp.kmn i3317_nosupp-1.0.js - findstr /v /m "KS=1" i3317_nosupp-1.0.js - $(KMCOMPD) i3317_withsupp.kmn i3317_withsupp-1.0.js - findstr /m "KS=1" i3317_withsupp-1.0.js - $(KMCOMPD) i3317_withsupp_incontext.kmn i3317_withsupp_incontext-1.0.js - findstr /m "KS=1" i3317_withsupp_incontext-1.0.js - $(KMCOMPD) i3317_withsupp_inmatch.kmn i3317_withsupp_inmatch-1.0.js - findstr /m "KS=1" i3317_withsupp_inmatch-1.0.js - $(KMCOMPD) i3317_withsupp_innomatch.kmn i3317_withsupp_innomatch-1.0.js - findstr /m "KS=1" i3317_withsupp_innomatch-1.0.js - $(KMCOMPD) i3317_withsupp_instore.kmn i3317_withsupp_instore-1.0.js - findstr /m "KS=1" i3317_withsupp_instore-1.0.js + $(KMC_DEBUG) $(KMN) + findstr /v /m "KS=1" i3317_nosupp.js + findstr /m "KS=1" i3317_withsupp.js + findstr /m "KS=1" i3317_withsupp_incontext.js + findstr /m "KS=1" i3317_withsupp_inmatch.js + findstr /m "KS=1" i3317_withsupp_innomatch.js + findstr /m "KS=1" i3317_withsupp_instore.js clean: -del *.js diff --git a/developer/src/test/auto/compile-supplementary-support/i3317_nosupp.kmn b/developer/src/test/auto/compile-supplementary-support/i3317_nosupp.kmn index 6d1a8d29f83..5385ff3ba55 100644 --- a/developer/src/test/auto/compile-supplementary-support/i3317_nosupp.kmn +++ b/developer/src/test/auto/compile-supplementary-support/i3317_nosupp.kmn @@ -1,12 +1,14 @@ c This should not generate KS=1 in the .js store(&VERSION) '9.0' store(&NAME) 'I3317 - no supp chars' +store(&TARGETS) 'web' + begin Unicode > use(main) store(something) 'abc' group(main) using keys - + 'abc' + 'd' > 'efg' match > 'abc' diff --git a/developer/src/test/auto/compile-supplementary-support/i3317_withsupp.kmn b/developer/src/test/auto/compile-supplementary-support/i3317_withsupp.kmn index 6aaec1c28a8..5ee0b474748 100644 --- a/developer/src/test/auto/compile-supplementary-support/i3317_withsupp.kmn +++ b/developer/src/test/auto/compile-supplementary-support/i3317_withsupp.kmn @@ -1,13 +1,15 @@ c This should generate KS=1 in the .js store(&VERSION) '9.0' store(&NAME) 'I3317 - with supp chars' +store(&TARGETS) 'web' + begin Unicode > use(main) store(something) 'abc' group(main) using keys - -U+13000 'abc' + 'd' > 'efg' + +U+13000 'abc' + 'd' > 'efg' match > 'abc' nomatch > 'abc' diff --git a/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_incontext.kmn b/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_incontext.kmn index cfe2eda9d73..0e20dbde08f 100644 --- a/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_incontext.kmn +++ b/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_incontext.kmn @@ -1,13 +1,15 @@ c This should generate KS=1 in the .js store(&VERSION) '9.0' store(&NAME) 'I3317 - with supp chars in context' +store(&TARGETS) 'web' + begin Unicode > use(main) store(something) 'abc' group(main) using keys - -'abc' + 'd' > 'efg' U+13000 + +'abc' + 'd' > 'efg' U+13000 match > 'abc' nomatch > 'abc' diff --git a/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_inmatch.kmn b/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_inmatch.kmn index 58d8b27e184..9fd10da7c39 100644 --- a/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_inmatch.kmn +++ b/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_inmatch.kmn @@ -1,14 +1,16 @@ c This should generate KS=1 in the .js store(&VERSION) '9.0' store(&NAME) 'I3317 - with supp chars' +store(&TARGETS) 'web' + begin Unicode > use(main) store(something) 'abc' group(main) using keys - - 'abc' + 'd' > 'efg' + + 'abc' + 'd' > 'efg' match > 'abc' U+13000 nomatch > 'abc' diff --git a/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_innomatch.kmn b/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_innomatch.kmn index f7d03523de6..ab086ddf7c9 100644 --- a/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_innomatch.kmn +++ b/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_innomatch.kmn @@ -1,6 +1,8 @@ c This should generate KS=1 in the .js store(&VERSION) '9.0' store(&NAME) 'I3317 - with supp chars' +store(&TARGETS) 'web' + begin Unicode > use(main) @@ -8,8 +10,8 @@ begin Unicode > use(main) store(something) 'abc' group(main) using keys - - 'abc' + 'd' > 'efg' -match > 'abc' + 'abc' + 'd' > 'efg' + +match > 'abc' nomatch > 'abc' U+13000 diff --git a/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_instore.kmn b/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_instore.kmn index fcbe73efece..427dd6b8a91 100644 --- a/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_instore.kmn +++ b/developer/src/test/auto/compile-supplementary-support/i3317_withsupp_instore.kmn @@ -1,6 +1,8 @@ c This should generate KS=1 in the .js store(&VERSION) '9.0' store(&NAME) 'I3317 - with supp chars' +store(&TARGETS) 'web' + begin Unicode > use(main) @@ -8,8 +10,8 @@ begin Unicode > use(main) store(something) 'abc' U+13000 group(main) using keys - - 'abc' + 'd' > 'efg' -match > 'abc' -nomatch > 'abc' + 'abc' + 'd' > 'efg' + +match > 'abc' +nomatch > 'abc' diff --git a/developer/src/test/auto/developer-test-auto.groupproj b/developer/src/test/auto/developer-test-auto.groupproj index cf31a8f8ced..b680dfb6aec 100644 --- a/developer/src/test/auto/developer-test-auto.groupproj +++ b/developer/src/test/auto/developer-test-auto.groupproj @@ -6,9 +6,6 @@ - - - @@ -41,15 +38,6 @@ - - - - - - - - - @@ -96,13 +84,13 @@ - + - + - + diff --git a/developer/src/test/auto/keyboard-info/Makefile b/developer/src/test/auto/keyboard-info/Makefile deleted file mode 100644 index b6935b2958e..00000000000 --- a/developer/src/test/auto/keyboard-info/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# -# Test that .keyboard_info validation works correctly -# -# Uses kmcomp.exe rather than attempting to run a separate test -# - -!include ..\..\..\Defines.mak - -test: build - test.bat "$(DEVELOPER_PROGRAM)\kmcomp.exe" - -build: - rem Nothing to do - -clean: def-clean - -rd /s/q Win32 - -!include ..\..\..\Target.mak diff --git a/developer/src/test/auto/keyboard-info/test-invalid-language-code.keyboard_info b/developer/src/test/auto/keyboard-info/test-invalid-language-code.keyboard_info deleted file mode 100644 index 79de65e9901..00000000000 --- a/developer/src/test/auto/keyboard-info/test-invalid-language-code.keyboard_info +++ /dev/null @@ -1,6 +0,0 @@ -{ - "license": "mit", - "languages": [ - "foooooooo=amh" - ] -} \ No newline at end of file diff --git a/developer/src/test/auto/keyboard-info/test-non-canonical-language-code.keyboard_info b/developer/src/test/auto/keyboard-info/test-non-canonical-language-code.keyboard_info deleted file mode 100644 index c245d3f524f..00000000000 --- a/developer/src/test/auto/keyboard-info/test-non-canonical-language-code.keyboard_info +++ /dev/null @@ -1,6 +0,0 @@ -{ - "license": "mit", - "languages": [ - "amh" - ] -} \ No newline at end of file diff --git a/developer/src/test/auto/keyboard-info/test-valid.keyboard_info b/developer/src/test/auto/keyboard-info/test-valid.keyboard_info deleted file mode 100644 index 6fe56a7abb8..00000000000 --- a/developer/src/test/auto/keyboard-info/test-valid.keyboard_info +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "Balochi Phonetic", - "jsFilename": "balochi_phonetic-1.1.js", - "packageFilename": "balochi_phonetic.kmp", - "platformSupport": { - "windows": "full", - "macos": "full", - "desktopWeb": "full", - "mobileWeb": "full", - "ios": "full", - "android": "full" - }, - "license": "mit", - "languages": [ - "bal", - "bgn", - "bgp" - ], - "lastModifiedDate": "2017-09-06" -} \ No newline at end of file diff --git a/developer/src/test/auto/keyboard-info/test.bat b/developer/src/test/auto/keyboard-info/test.bat deleted file mode 100644 index d7d896d38b5..00000000000 --- a/developer/src/test/auto/keyboard-info/test.bat +++ /dev/null @@ -1,58 +0,0 @@ -@echo off -rem We use test.bat so we can test errorlevels -setlocal -set ESC= - -if "%1"=="-h" goto usage -if "%1"=="--help" goto usage -if "%1"=="-?" goto usage - -if "%1"=="-c" ( - set RED=%ESC%[1;31m - set GREEN=%ESC%[1;32m - set WHITE=%ESC%[0;37m - set BLUE=%ESC%[1;36m - shift -) -if "%1"=="" ( - set compiler=..\..\..\..\bin\kmcomp.exe -) else ( - set compiler=%1 -) - -:test-1 -call :should-pass "Valid keyboard info file" test-valid.keyboard_info || exit /b 1 - -call :should-fail "Keyboard info file with invalid BCP-47 code" test-invalid-language-code.keyboard_info || exit /b 1 - -:: in 13.0, this test was a should-fail -:: in 14.0, we made non-canonical bcp 47 codes a hint instead of an error -:: see #4689. -call :should-pass "Keyboard info file with non-canonical BCP-47 code" test-non-canonical-language-code.keyboard_info || exit /b 1 - -goto :eof - -:should-pass -echo %BLUE%TEST: %1 %WHITE% -"%compiler%" -s -vs -schema-path "%KEYMAN_ROOT%\common\schemas\keyboard_info" "%2" -if %ERRORLEVEL% EQU 0 ( - echo %GREEN%TEST PASSED%WHITE% - exit /b 0 -) -echo %RED%FAILED: expected %2 to be valid.%WHITE% 1>&2 -exit /b 1 - -:should-fail -echo %BLUE%TEST: %1 %WHITE% -"%compiler%" -s -vs -schema-path "%KEYMAN_ROOT%\common\schemas\keyboard_info" "%2" -if %ERRORLEVEL% GTR 0 ( - echo %GREEN%TEST PASSED%WHITE% - exit /b 0 -) -echo %RED%FAILED: expected %2 to be invalid.%WHITE% 1>&2 -exit /b 1 - -:usage -echo Usage: test.bat [-c] [path-to-kmcomp.exe] -echo -c will add colour via ANSI escapes (don't use in redirected scripts) -echo path-to-kmcomp.exe, if not included will default to ..\..\..\..\bin\kmcomp.exe diff --git a/developer/src/test/auto/keyboard-package-versions/.gitignore b/developer/src/test/auto/keyboard-package-versions/.gitignore deleted file mode 100644 index 09bf1459b58..00000000000 --- a/developer/src/test/auto/keyboard-package-versions/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.kmp -test-?.0/*.js -test-?.0/*.kmx diff --git a/developer/src/test/auto/keyboard-package-versions/KeyboardPackageVersionsTestSuite.dpr b/developer/src/test/auto/keyboard-package-versions/KeyboardPackageVersionsTestSuite.dpr deleted file mode 100644 index 6122f21916e..00000000000 --- a/developer/src/test/auto/keyboard-package-versions/KeyboardPackageVersionsTestSuite.dpr +++ /dev/null @@ -1,142 +0,0 @@ -program KeyboardPackageVersionsTestSuite; - -{$IFNDEF TESTINSIGHT} -{$APPTYPE CONSOLE} -{$ENDIF}{$STRONGLINKTYPES ON} -uses - System.SysUtils, - {$IFDEF TESTINSIGHT} - TestInsight.DUnitX, - {$ENDIF } - DUnitX.Loggers.Console, - DUnitX.Loggers.Xml.NUnit, - DUnitX.TestFramework, - Keyman.Test.System.CompilePackageVersioningTest in 'Keyman.Test.System.CompilePackageVersioningTest.pas', - CompilePackage in '..\..\..\common\delphi\compiler\CompilePackage.pas', - VisualKeyboard in '..\..\..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboard.pas', - kmpinffile in '..\..\..\..\..\common\windows\delphi\packages\kmpinffile.pas', - kpsfile in '..\..\..\common\delphi\packages\kpsfile.pas', - PackageFileFormats in '..\..\..\..\..\common\windows\delphi\packages\PackageFileFormats.pas', - PackageInfo in '..\..\..\..\..\common\windows\delphi\packages\PackageInfo.pas', - utilfiletypes in '..\..\..\..\..\common\windows\delphi\general\utilfiletypes.pas', - StockFileNames in '..\..\..\..\..\common\windows\delphi\general\StockFileNames.pas', - utilstr in '..\..\..\..\..\common\windows\delphi\general\utilstr.pas', - Unicode in '..\..\..\..\..\common\windows\delphi\general\Unicode.pas', - JsonUtil in '..\..\..\..\..\common\windows\delphi\general\JsonUtil.pas', - KeymanVersion in '..\..\..\..\..\common\windows\delphi\general\KeymanVersion.pas', - utildir in '..\..\..\..\..\common\windows\delphi\general\utildir.pas', - utilsystem in '..\..\..\..\..\common\windows\delphi\general\utilsystem.pas', - RegistryKeys in '..\..\..\..\..\common\windows\delphi\general\RegistryKeys.pas', - utilexecute in '..\..\..\..\..\common\windows\delphi\general\utilexecute.pas', - GetOsVersion in '..\..\..\..\..\common\windows\delphi\general\GetOsVersion.pas', - VersionInfo in '..\..\..\..\..\common\windows\delphi\general\VersionInfo.pas', - ExtShiftState in '..\..\..\..\..\common\windows\delphi\visualkeyboard\ExtShiftState.pas', - VisualKeyboardLoaderBinary in '..\..\..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardLoaderBinary.pas', - VisualKeyboardLoaderXML in '..\..\..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardLoaderXML.pas', - VisualKeyboardSaverBinary in '..\..\..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardSaverBinary.pas', - VisualKeyboardSaverXML in '..\..\..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardSaverXML.pas', - VKeyChars in '..\..\..\..\..\common\windows\delphi\general\VKeyChars.pas', - VKeys in '..\..\..\..\..\common\windows\delphi\general\VKeys.pas', - ErrorControlledRegistry in '..\..\..\..\..\common\windows\delphi\vcl\ErrorControlledRegistry.pas', - kmxfile in '..\..\..\..\..\common\windows\delphi\keyboards\kmxfile.pas', - KeyNames in '..\..\..\..\..\common\windows\delphi\general\KeyNames.pas', - KeymanDeveloperOptions in '..\..\..\tike\main\KeymanDeveloperOptions.pas', - Keyman.System.PackageInfoRefreshKeyboards in '..\..\..\common\delphi\packages\Keyman.System.PackageInfoRefreshKeyboards.pas', - Keyman.System.KeyboardJSInfo in '..\..\..\common\delphi\keyboards\Keyman.System.KeyboardJSInfo.pas', - Keyman.System.KeyboardUtils in '..\..\..\common\delphi\keyboards\Keyman.System.KeyboardUtils.pas', - Keyman.System.KMXFileLanguages in '..\..\..\common\delphi\keyboards\Keyman.System.KMXFileLanguages.pas', - Keyman.System.Standards.ISO6393ToBCP47Registry in '..\..\..\..\..\common\windows\delphi\standards\Keyman.System.Standards.ISO6393ToBCP47Registry.pas', - Keyman.System.Standards.LCIDToBCP47Registry in '..\..\..\..\..\common\windows\delphi\standards\Keyman.System.Standards.LCIDToBCP47Registry.pas', - TempFileManager in '..\..\..\..\..\common\windows\delphi\general\TempFileManager.pas', - RedistFiles in '..\..\..\tike\main\RedistFiles.pas', - DebugPaths in '..\..\..\..\..\common\windows\delphi\general\DebugPaths.pas', - Upload_Settings in '..\..\..\..\..\common\windows\delphi\general\Upload_Settings.pas', - klog in '..\..\..\..\..\common\windows\delphi\general\klog.pas', - compile in '..\..\..\common\delphi\compiler\compile.pas', - Keyman.Developer.System.Project.kmnProjectFile in '..\..\..\tike\project\Keyman.Developer.System.Project.kmnProjectFile.pas', - Keyman.Developer.System.Project.kmnProjectFileAction in '..\..\..\tike\project\Keyman.Developer.System.Project.kmnProjectFileAction.pas', - Keyman.Developer.System.Project.Project in '..\..\..\tike\project\Keyman.Developer.System.Project.Project.pas', - Keyman.Developer.System.Project.ProjectFile in '..\..\..\tike\project\Keyman.Developer.System.Project.ProjectFile.pas', - Keyman.Developer.System.Project.ProjectFiles in '..\..\..\tike\project\Keyman.Developer.System.Project.ProjectFiles.pas', - Keyman.Developer.System.Project.ProjectFileType in '..\..\..\tike\project\Keyman.Developer.System.Project.ProjectFileType.pas', - mrulist in '..\..\..\tike\main\mrulist.pas', - kmxfileconsts in '..\..\..\..\..\common\windows\delphi\keyboards\kmxfileconsts.pas', - Keyman.Developer.System.Project.ProjectLoader in '..\..\..\tike\project\Keyman.Developer.System.Project.ProjectLoader.pas', - Keyman.Developer.System.Project.ProjectLog in '..\..\..\tike\project\Keyman.Developer.System.Project.ProjectLog.pas', - Keyman.Developer.System.Project.ProjectSaver in '..\..\..\tike\project\Keyman.Developer.System.Project.ProjectSaver.pas', - UKeymanTargets in '..\..\..\common\delphi\general\UKeymanTargets.pas', - CompileKeymanWeb in '..\..\..\tike\compile\CompileKeymanWeb.pas', - CompileErrorCodes in '..\..\..\common\delphi\compiler\CompileErrorCodes.pas', - KeyboardParser in '..\..\..\tike\main\KeyboardParser.pas', - WindowsLanguages in '..\..\..\common\delphi\general\WindowsLanguages.pas', - KeymanWebKeyCodes in '..\..\..\tike\compile\KeymanWebKeyCodes.pas', - kmxfileutils in '..\..\..\..\..\common\windows\delphi\keyboards\kmxfileutils.pas', - TouchLayoutDefinitions in '..\..\..\tike\oskbuilder\TouchLayoutDefinitions.pas', - TouchLayout in '..\..\..\tike\oskbuilder\TouchLayout.pas', - KeyboardFonts in '..\..\..\common\delphi\general\KeyboardFonts.pas', - Keyman.Developer.System.Project.kpsProjectFile in '..\..\..\tike\project\Keyman.Developer.System.Project.kpsProjectFile.pas', - Keyman.Developer.System.Project.kpsProjectFileAction in '..\..\..\tike\project\Keyman.Developer.System.Project.kpsProjectFileAction.pas', - CompilePackageInstaller in '..\..\..\common\delphi\compiler\CompilePackageInstaller.pas', - utilhttp in '..\..\..\..\..\common\windows\delphi\general\utilhttp.pas', - Keyman.System.LanguageCodeUtils in '..\..\..\..\..\common\windows\delphi\general\Keyman.System.LanguageCodeUtils.pas', - Keyman.System.RegExGroupHelperRSP19902 in '..\..\..\..\..\common\windows\delphi\vcl\Keyman.System.RegExGroupHelperRSP19902.pas', - DUnitX.Loggers.TeamCity in '..\..\..\..\..\common\windows\delphi\general\DUnitX.Loggers.TeamCity.pas', - BCP47Tag in '..\..\..\..\..\common\windows\delphi\general\BCP47Tag.pas', - Keyman.System.Standards.BCP47SubtagRegistry in '..\..\..\..\..\common\windows\delphi\standards\Keyman.System.Standards.BCP47SubtagRegistry.pas', - Keyman.System.Standards.BCP47SuppressScriptRegistry in '..\..\..\..\..\common\windows\delphi\standards\Keyman.System.Standards.BCP47SuppressScriptRegistry.pas', - Keyman.System.CanonicalLanguageCodeUtils in '..\..\..\..\..\common\windows\delphi\general\Keyman.System.CanonicalLanguageCodeUtils.pas', - TextFileFormat in '..\..\..\common\delphi\general\TextFileFormat.pas', - Keyman.System.LexicalModelUtils in '..\..\..\common\delphi\lexicalmodels\Keyman.System.LexicalModelUtils.pas', - Keyman.System.PackageInfoRefreshLexicalModels in '..\..\..\common\delphi\packages\Keyman.System.PackageInfoRefreshLexicalModels.pas', - Keyman.System.Standards.LangTagsRegistry in '..\..\..\..\..\common\windows\delphi\standards\Keyman.System.Standards.LangTagsRegistry.pas', - Keyman.Developer.System.Project.UrlRenderer in '..\..\..\tike\project\Keyman.Developer.System.Project.UrlRenderer.pas', - KeymanPaths in '..\..\..\..\..\common\windows\delphi\general\KeymanPaths.pas', - Keyman.Developer.System.ValidateKpsFile in '..\..\..\common\delphi\compiler\Keyman.Developer.System.ValidateKpsFile.pas', - Keyman.Developer.System.KeymanDeveloperPaths in '..\..\..\tike\main\Keyman.Developer.System.KeymanDeveloperPaths.pas'; - -var - runner : ITestRunner; - results : IRunResults; - logger : ITestLogger; - nunitLogger : ITestLogger; -begin -{$IFDEF TESTINSIGHT} - TestInsight.DUnitX.RunRegisteredTests; - exit; -{$ENDIF} - try - //Check command line options, will exit if invalid - TDUnitX.CheckCommandLine; - //Create the test runner - runner := TDUnitX.CreateRunner; - //Tell the runner to use RTTI to find Fixtures - runner.UseRTTI := True; - //tell the runner how we will log things - //Log to the console window - logger := TDUnitXConsoleLogger.Create(true); - runner.AddLogger(logger); - //Generate an NUnit compatible XML File - nunitLogger := TDUnitXXMLNUnitFileLogger.Create(TDUnitX.Options.XMLOutputFile); - runner.AddLogger(nunitLogger); - runner.FailsOnNoAsserts := False; //When true, Assertions must be made during tests; - - //Run tests - results := runner.Execute; - if not results.AllPassed then - System.ExitCode := EXIT_ERRORS; - - {$IFNDEF CI} - //We don't want this happening when running under CI. - if TDUnitX.Options.ExitBehavior = TDUnitXExitBehavior.Pause then - begin - System.Write('Done.. press key to quit.'); - System.Readln; - end; - {$ENDIF} - - DUnitX.Loggers.TeamCity.ReportToTeamCity; - except - on E: Exception do - System.Writeln(E.ClassName, ': ', E.Message); - end; -end. diff --git a/developer/src/test/auto/keyboard-package-versions/KeyboardPackageVersionsTestSuite.dproj b/developer/src/test/auto/keyboard-package-versions/KeyboardPackageVersionsTestSuite.dproj deleted file mode 100644 index 3bbafa79bf8..00000000000 --- a/developer/src/test/auto/keyboard-package-versions/KeyboardPackageVersionsTestSuite.dproj +++ /dev/null @@ -1,1034 +0,0 @@ - - - {7A8E88E4-B407-442B-9A53-DE6F43F1C0F1} - 18.8 - VCL - KeyboardPackageVersionsTestSuite.dpr - True - Debug - Win32 - 1 - Console - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Cfg_1 - true - true - - - true - Base - true - - - .\obj\$(Platform)\$(Config) - .\bin\$(Platform)\$(Config) - false - false - false - false - false - RESTComponents;FireDAC;FireDACSqliteDriver;soaprtl;FireDACIBDriver;soapmidas;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;$(DCC_UsePackage) - System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) - true - $(BDS)\bin\delphi_PROJECTICON.ico - $(BDS)\bin\delphi_PROJECTICNS.icns - $(DUnitX);$(DCC_UnitSearchPath) - KeyboardPackageVersionsTestSuite - $(CI);$(DCC_Define) - 3081 - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - - - DBXSqliteDriver;bindcompdbx;IndyIPCommon;DBXInterBaseDriver;vcl;IndyIPServer;vclactnband;vclFireDAC;IndySystem;tethering;svnui;mbColorLibD10;dsnapcon;FireDACADSDriver;scFontCombo;DCPdelphi2009;FireDACMSAccDriver;fmxFireDAC;vclimg;Jcl;vcltouch;JvCore;vcldb;bindcompfmx;svn;FireDACPgDriver;inetdb;DbxCommonDriver;fmx;fmxdae;xmlrtl;fmxobj;vclwinx;rtl;DbxClientDriver;CustomIPTransport;vcldsnap;dbexpress;IndyCore;vclx;bindcomp;appanalytics;dsnap;IndyIPClient;bindcompvcl;EmbeddedWebBrowser_XE;VCLRESTComponents;dbxcds;VclSmp;JvDocking;adortl;JclVcl;vclie;bindengine;DBXMySQLDriver;dsnapxml;FireDACMySQLDriver;dbrtl;inetdbxpress;IndyProtocols;keyman_components;FireDACCommonODBC;fmxase;$(DCC_UsePackage) - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - Debug - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - 1033 - - - DBXSqliteDriver;bindcompdbx;IndyIPCommon;DBXInterBaseDriver;vcl;IndyIPServer;vclactnband;vclFireDAC;IndySystem;tethering;dsnapcon;FireDACADSDriver;FireDACMSAccDriver;fmxFireDAC;vclimg;Jcl;vcltouch;vcldb;bindcompfmx;FireDACPgDriver;inetdb;DbxCommonDriver;fmx;fmxdae;xmlrtl;fmxobj;vclwinx;rtl;DbxClientDriver;CustomIPTransport;vcldsnap;dbexpress;IndyCore;vclx;bindcomp;appanalytics;dsnap;IndyIPClient;bindcompvcl;VCLRESTComponents;dbxcds;VclSmp;adortl;JclVcl;vclie;bindengine;DBXMySQLDriver;dsnapxml;FireDACMySQLDriver;dbrtl;inetdbxpress;IndyProtocols;FireDACCommonODBC;fmxase;$(DCC_UsePackage) - - - DEBUG;$(DCC_Define) - true - false - true - true - true - - - false - -l:Information - 1033 - (None) - - - false - RELEASE;$(DCC_Define) - 0 - 0 - - - - MainSource - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - - - - Delphi.Personality.12 - Console - - - - KeyboardPackageVersionsTestSuite.dpr - - - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - - - - - - true - - - - - true - - - - - true - - - - - true - - - - - KeyboardPackageVersionsTestSuite.exe - true - - - - - 1 - - - 0 - - - - - classes - 1 - - - classes - 1 - - - - - res\xml - 1 - - - res\xml - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\armeabi - 1 - - - library\lib\armeabi - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\mips - 1 - - - library\lib\mips - 1 - - - - - library\lib\armeabi-v7a - 1 - - - library\lib\arm64-v8a - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - res\drawable - 1 - - - res\drawable - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - res\values-v21 - 1 - - - res\values-v21 - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - res\drawable - 1 - - - res\drawable - 1 - - - - - res\drawable-xxhdpi - 1 - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-ldpi - 1 - - - res\drawable-ldpi - 1 - - - - - res\drawable-mdpi - 1 - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - res\drawable-xhdpi - 1 - - - - - res\drawable-mdpi - 1 - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - res\drawable-xhdpi - 1 - - - - - res\drawable-xxhdpi - 1 - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-xxxhdpi - 1 - - - res\drawable-xxxhdpi - 1 - - - - - res\drawable-small - 1 - - - res\drawable-small - 1 - - - - - res\drawable-normal - 1 - - - res\drawable-normal - 1 - - - - - res\drawable-large - 1 - - - res\drawable-large - 1 - - - - - res\drawable-xlarge - 1 - - - res\drawable-xlarge - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - 1 - - - 1 - - - 0 - - - - - 1 - .framework - - - 1 - .framework - - - 0 - - - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .dll;.bpl - - - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .bpl - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - - - - - - 1 - - - 1 - - - 1 - - - - - - - - Contents\Resources - 1 - - - Contents\Resources - 1 - - - - - library\lib\armeabi-v7a - 1 - - - library\lib\arm64-v8a - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 0 - - - - - library\lib\armeabi-v7a - 1 - - - - - 1 - - - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - - - - - - - - - - - True - False - - - 12 - - - - - diff --git a/developer/src/test/auto/keyboard-package-versions/Keyman.Test.System.CompilePackageVersioningTest.pas b/developer/src/test/auto/keyboard-package-versions/Keyman.Test.System.CompilePackageVersioningTest.pas deleted file mode 100644 index fae07d5eed8..00000000000 --- a/developer/src/test/auto/keyboard-package-versions/Keyman.Test.System.CompilePackageVersioningTest.pas +++ /dev/null @@ -1,163 +0,0 @@ -unit Keyman.Test.System.CompilePackageVersioningTest; - -interface - -uses - DUnitX.TestFramework, - Keyman.Developer.System.Project.ProjectLog; - -type - - [TestFixture] - TCompilePackageVersioningTest = class(TObject) - private - FRoot: string; - procedure PackageMessage(Sender: TObject; msg: string; - State: TProjectLogState); - public - [Setup] - procedure Setup; - [TearDown] - procedure TearDown; - // Sample Methods - // Simple single Test - // Test with TestCase Attribute to supply parameters. - [Test] - [TestCase('test-single-version-1-package', 'test1.kps,False')] - [TestCase('test-single-version-2-package', 'test2.kps,False')] - [TestCase('test-version-2-1-mismatch', 'test2-1.kps,True')] - [TestCase('test-version-1-2-mismatch', 'test1-2.kps,True')] - [TestCase('test-keyboard-1-package-2', 'test-keyboard-1-vs-package-2.kps,False')] - [TestCase('test-package-1-keyboard-2', 'test-package-1-vs-keyboard-2.kps,False')] - procedure TestPackageCompile(Path: string; ExpectError: Boolean); - end; - -implementation - -uses - System.SysUtils, - Winapi.ActiveX, - - compile, - CompilePackage, - Keyman.Developer.System.Project.kmnProjectFileAction, - Keyman.Developer.System.Project.ProjectFile, - kpsfile; - -type - TProjectConsole = class(TProject) - private - FSilent: Boolean; - FFullySilent: Boolean; - public - procedure Log(AState: TProjectLogState; Filename: string; Msg: string; MsgCode, line: Integer); override; // I4706 - function Save: Boolean; override; // I4709 - - property Silent: Boolean read FSilent write FSilent; - property FullySilent: Boolean read FFullySilent write FFullySilent; - end; - -procedure TCompilePackageVersioningTest.Setup; -var - p: TProjectConsole; - i: Integer; -begin - Assert.IgnoreCaseDefault := False; - - FRoot := ExtractFileDir(ExtractFileDir(ExtractFileDir(ExtractFileDir(ParamStr(0))))); - - // - // Force load development version of kmcmpdll - // Assumes it has already been built, of course... - // - FUnitTestKMCmpDllPath := FRoot + '\..\..\..\..\bin\'; - Assert.IsTrue(FileExists(FUnitTestKMCmpDllPath + 'kmcmpdll.dll'), - 'kmcmpdll.dll does not exist at ' + FUnitTestKMCmpDllPath + 'kmcmpdll.dll'); - - p := TProjectConsole.Create(ptUnknown, FRoot+'\test-1.0\test-1.0.kpj', False); - try - for i := 0 to p.Files.Count - 1 do - if p.Files[i] is TkmnProjectFileAction then - Assert.IsTrue((p.Files[i] as TkmnProjectFileAction).CompileKeyboard, 'Could not compile keyboard'); - finally - p.Free; - end; - - p := TProjectConsole.Create(ptUnknown, FRoot+'\test-2.0\test-2.0.kpj', False); - try - for i := 0 to p.Files.Count - 1 do - if p.Files[i] is TkmnProjectFileAction then - Assert.IsTrue((p.Files[i] as TkmnProjectFileAction).CompileKeyboard, 'Could not compile keyboard'); - finally - p.Free; - end; -end; - -procedure TCompilePackageVersioningTest.TearDown; -begin -end; - -procedure TCompilePackageVersioningTest.PackageMessage(Sender: TObject; msg: string; State: TProjectLogState); -const - Map: array[TProjectLogState] of TLogLevel = ( - {plsInfo} TLogLevel.Information, - {plsHint} TLogLevel.Information, - {plsWarning} TLogLevel.Warning, - {plsError} TLogLevel.Error, - {plsFatal} TLogLevel.Error, - {plsSuccess} TLogLevel.Information, - {plsFailure} TLogLevel.Error - ); -begin - Log(Map[State], msg); -end; - -procedure TCompilePackageVersioningTest.TestPackageCompile(Path: string; ExpectError: Boolean); -var - pack: TKPSFile; - res: Boolean; -begin - pack := TKPSFile.Create; - try - pack.FileName := FRoot+'\'+Path; - pack.LoadXML; - res := DoCompilePackage(pack, PackageMessage, False, False, FRoot+'\'+ChangeFileExt(Path,'.kmp')); - Assert.AreEqual(not ExpectError, res); - finally - pack.Free; - end; -end; - -procedure TProjectConsole.Log(AState: TProjectLogState; Filename, Msg: string; MsgCode, line: Integer); // I4706 -begin -{$IFDEF DEBUG_PROJECT} - case AState of - plsInfo, - plsSuccess, - plsFailure: - if not FSilent then - writeln(ExtractFileName(Filename)+': '+Msg); - plsWarning: - if not FFullySilent then - writeln(ExtractFileName(Filename)+': Warning: '+Msg); - plsError: - if not FFullySilent then - writeln(ExtractFileName(Filename)+': Error: '+Msg); - plsFatal: - writeln(ExtractFileName(Filename)+': Fatal error: '+Msg); - end; -{$ENDIF} -end; - -function TProjectConsole.Save: Boolean; // I4709 -begin - // We don't modify the project file in the console - Result := True; -end; - -initialization - CoInitializeEx(nil, COINIT_APARTMENTTHREADED); - TDUnitX.RegisterTestFixture(TCompilePackageVersioningTest); -finalization - CoUninitialize; -end. diff --git a/developer/src/test/auto/keyboard-package-versions/Makefile b/developer/src/test/auto/keyboard-package-versions/Makefile deleted file mode 100644 index c07f744018f..00000000000 --- a/developer/src/test/auto/keyboard-package-versions/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# -# Test that package compiler manages keyboard and package versions correctly -# -# NOTE: The .dproj needs $(CI) added to the Delphi Compiler/Conditional defines (All -# configurations - all platforms) section in order for the CI flag to be passed in. -# (It's best to make this change in Delphi IDE). -# - -!include ..\..\..\Defines.mak - -test: build - $(WIN32_TARGET_PATH)\keyboardpackageversionstestsuite.exe -b -exit:continue - -build: - $(DELPHI_MSBUILD) "/p:CI=CI" keyboardpackageversionstestsuite.dproj - -clean: def-clean - -del *.kmp - -del test-1.0\*.kmx - -del test-2.0\*.kmx - -del test-1.0\*.js - -del test-2.0\*.js - -!include ..\..\..\Target.mak diff --git a/developer/src/test/auto/kmcomp-empty-store-names/emptystorename.kmn b/developer/src/test/auto/kmcomp-empty-store-names/emptystorename.kmn deleted file mode 100644 index 4f3ce2b72ec..00000000000 --- a/developer/src/test/auto/kmcomp-empty-store-names/emptystorename.kmn +++ /dev/null @@ -1,8 +0,0 @@ -store(&NAME) 'Test Empty Store Name' -begin Unicode > use(main) - -group(main) using keys - -store(a) 'abc' - -any(a) + 'a' > index(a,1) diff --git a/developer/src/test/auto/kmcomp-x64-structures/Makefile b/developer/src/test/auto/kmcomp-x64-structures/Makefile index c1523c9538f..c54d804c14e 100644 --- a/developer/src/test/auto/kmcomp-x64-structures/Makefile +++ b/developer/src/test/auto/kmcomp-x64-structures/Makefile @@ -1,5 +1,5 @@ # -# Test that struct sizes in kmcomp and kmcmpdll match +# Test that struct sizes in tike and kmcmplib match # for each of x86 and x64 # diff --git a/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.cpp b/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.cpp index 630c88d80e4..2241323f60c 100644 --- a/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.cpp +++ b/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.cpp @@ -2,7 +2,7 @@ // #include "pch.h" -#include "../../../../kmcmpdll/compfile.h" +#include "../../../../kmcmplib/src/compfile.h" #include int main() diff --git a/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.vcxproj b/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.vcxproj index 92715db8184..6e43e65a0b0 100644 --- a/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.vcxproj +++ b/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.vcxproj @@ -23,32 +23,32 @@ {A2CAE0E1-703A-419C-B82D-24080839E395} Win32Proj cppstructsize - 10.0.17763.0 + 10.0 Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode @@ -74,21 +74,25 @@ true $(ProjectDir)bin\$(Platform)\$(Configuration)\ $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(ProjectDir)\..\..\..\..\..\..\common\include;$(ProjectDir)\..\..\..\..\kmcmplib\include;$(IncludePath) true $(ProjectDir)bin\$(Platform)\$(Configuration)\ $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(ProjectDir)\..\..\..\..\..\..\common\include;$(ProjectDir)\..\..\..\..\kmcmplib\include;$(IncludePath) false $(ProjectDir)bin\$(Platform)\$(Configuration)\ $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(ProjectDir)\..\..\..\..\..\..\common\include;$(ProjectDir)\..\..\..\..\kmcmplib\include;$(IncludePath) false $(ProjectDir)bin\$(Platform)\$(Configuration)\ $(ProjectDir)obj\$(Platform)\$(Configuration)\ + $(ProjectDir)\..\..\..\..\..\..\common\include;$(ProjectDir)\..\..\..\..\kmcmplib\include;$(IncludePath) @@ -159,7 +163,7 @@ - + diff --git a/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.vcxproj.filters b/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.vcxproj.filters index 03e55d50fd6..193b3bb1371 100644 --- a/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.vcxproj.filters +++ b/developer/src/test/auto/kmcomp-x64-structures/cppstructsize/cppstructsize.vcxproj.filters @@ -18,7 +18,7 @@ Header Files - + Header Files diff --git a/developer/src/test/auto/kmcomp/Makefile b/developer/src/test/auto/kmcomp/Makefile index 402991fb8de..cbbeb5c8a15 100644 --- a/developer/src/test/auto/kmcomp/Makefile +++ b/developer/src/test/auto/kmcomp/Makefile @@ -1,13 +1,13 @@ # -# Test that kmcomp passes various compile tests +# Test that kmc passes various compile tests # -# Uses kmcomp.exe rather than attempting to run a separate test +# Uses kmc.js (via kmc.cmd) rather than attempting to run a separate test # !include ..\..\..\Defines.mak test: build - test.bat -c "$(DEVELOPER_PROGRAM)\kmcomp.exe" + test.bat -c $(DEVELOPER_PROGRAM)\kmc.cmd build: rem Nothing to do diff --git a/developer/src/test/auto/kmcomp/test.bat b/developer/src/test/auto/kmcomp/test.bat index 808c2f9068b..107e0268ce8 100644 --- a/developer/src/test/auto/kmcomp/test.bat +++ b/developer/src/test/auto/kmcomp/test.bat @@ -23,7 +23,7 @@ if "%1"=="-t" ( ) if "%1"=="" ( - set compiler=..\..\..\..\bin\developer\kmcomp.exe + set compiler=..\..\..\..\bin\kmc.cmd ) else ( set compiler=%1 shift @@ -110,7 +110,7 @@ goto :eof :should-pass echo %BLUE%TEST: %1 %WHITE% -"%compiler%" -no-color -s -w tests.kpj -t "%2" +call "%compiler%" build --no-color --log-level hint --compiler-warnings-as-errors "%2" if !ERRORLEVEL! EQU 0 ( echo %GREEN%TEST PASSED%WHITE% exit /b 0 @@ -120,7 +120,7 @@ exit /b 1 :should-fail echo %BLUE%TEST: %1 %WHITE% -"%compiler%" -no-color -s -w tests.kpj -t "%2" +call "%compiler%" build --no-color --log-level hint --compiler-warnings-as-errors "%2" if !ERRORLEVEL! GTR 0 ( echo %GREEN%TEST PASSED%WHITE% exit /b 0 @@ -141,7 +141,7 @@ if %1 == -f ( echo %BLUE%TEST: %1 %WHITE% if exist error.log del error.log -"%compiler%" -no-color -nologo -ss -w tests.kpj -t "%2" > error.log +call "%compiler%" build --no-color --log-level hint --compiler-warnings-as-errors "%2" > error.log if !ERRORLEVEL! GTR 0 ( echo %RED%FAILED: expected %2 to be a valid keyboard.%WHITE% 1>&2 echo --------------------------------------------------------------------------- @@ -184,6 +184,6 @@ echo %GREEN%TEST PASSED%WHITE% exit /b 0 :usage -echo Usage: test.bat [-c] [path-to-kmcomp.exe] +echo Usage: test.bat [-c] [path-to-kmc.cmd] echo -c will add colour via ANSI escapes (don't use in redirected scripts) -echo path-to-kmcomp.exe, if not included will default to ..\..\..\..\bin\developer\kmcomp.exe +echo path-to-kmc.cmd, if not included will default to ..\..\..\..\bin\kmc.cmd diff --git a/developer/src/test/auto/kmcomp/test_194_Filename_case.kvk b/developer/src/test/auto/kmcomp/test_194_Filename_case.kvk deleted file mode 100644 index a46a1fffae9..00000000000 Binary files a/developer/src/test/auto/kmcomp/test_194_Filename_case.kvk and /dev/null differ diff --git a/developer/src/test/auto/kmcomp/test_194_filename_case.kmn b/developer/src/test/auto/kmcomp/test_194_filename_case.kmn index 6ef3940e4cc..4c128f7b50e 100644 --- a/developer/src/test/auto/kmcomp/test_194_filename_case.kmn +++ b/developer/src/test/auto/kmcomp/test_194_filename_case.kmn @@ -3,8 +3,8 @@ c c Test all the different types of files that may be referenced by a .kmn c for case mismatches -store(&VERSION) '10.0' -store(&TARGETS) 'windows' +store(&VERSION) '14.0' +store(&TARGETS) 'any' store(&BITMAP) 'test_194_Filename_case' store(&KMW_HELPFILE) 'test_194_Filename_case.html' store(&KMW_EMBEDJS) 'test_194_Filename_case.embed_js' c .embed_js just to avoid .js being deleted by nmake clean... diff --git a/developer/src/test/auto/kmcomp/test_194_filename_case.out.txt b/developer/src/test/auto/kmcomp/test_194_filename_case.out.txt index e95a2c50229..f4d233b724c 100644 --- a/developer/src/test/auto/kmcomp/test_194_filename_case.out.txt +++ b/developer/src/test/auto/kmcomp/test_194_filename_case.out.txt @@ -1,8 +1,11 @@ -test_194_filename_case.kmn (8): Hint: 10AF Casing differences may fail on some platforms: reference 'test_194_Filename_case.bmp' does not match actual filename 'test_194_filename_case.bmp' -test_194_filename_case.kmn (9): Hint: 10AF Casing differences may fail on some platforms: reference 'test_194_Filename_case.html' does not match actual filename 'test_194_filename_case.html' -test_194_filename_case.kmn (10): Hint: 10AF Casing differences may fail on some platforms: reference 'test_194_Filename_case.embed_js' does not match actual filename 'test_194_filename_case.embed_js' -test_194_filename_case.kmn (11): Hint: 10AF Casing differences may fail on some platforms: reference 'test_194_Filename_case.embed.css' does not match actual filename 'test_194_filename_case.embed.css' -test_194_filename_case.kmn (12): Hint: 10AF Casing differences may fail on some platforms: reference 'test_194_Filename_case.keyman-touch-layout' does not match actual filename 'test_194_filename_case.keyman-touch-layout' -test_194_filename_case.kmn (14): Hint: 10AF Casing differences may fail on some platforms: reference 'test_194_Filename_case.txt' does not match actual filename 'test_194_filename_case.txt' -test_194_filename_case.kmn (23): Hint: 10AF Casing differences may fail on some platforms: reference 'test_194_Filename_case_call_0.call_js' does not match actual filename 'test_194_filename_case_call_0.call_js' -test_194_filename_case.kmn (23): Hint: 10AF Casing differences may fail on some platforms: reference 'test_194_Filename_case_call_1.call_js' does not match actual filename 'test_194_filename_case_call_1.call_js' +test_194_filename_case.kmn - hint KM05009: File on disk 'test_194_filename_case.bmp' does not match case of 'test_194_Filename_case.bmp' in source file; this is an error on platforms with case-sensitive filesystems. +test_194_filename_case.kmn - hint KM05009: File on disk 'test_194_filename_case.txt' does not match case of 'test_194_Filename_case.txt' in source file; this is an error on platforms with case-sensitive filesystems. +test_194_filename_case.kmn - hint KM05009: File on disk 'test_194_filename_case.kvks' does not match case of 'test_194_Filename_case.kvks' in source file; this is an error on platforms with case-sensitive filesystems. +test_194_filename_case.kmn - hint KM05009: File on disk 'test_194_filename_case.bmp' does not match case of 'test_194_Filename_case.bmp' in source file; this is an error on platforms with case-sensitive filesystems. +test_194_filename_case.kmn - hint KM05009: File on disk 'test_194_filename_case.txt' does not match case of 'test_194_Filename_case.txt' in source file; this is an error on platforms with case-sensitive filesystems. +test_194_filename_case.kmn - hint KM05009: File on disk 'test_194_filename_case.html' does not match case of 'test_194_Filename_case.html' in source file; this is an error on platforms with case-sensitive filesystems. +test_194_filename_case.kmn - hint KM05009: File on disk 'test_194_filename_case.embed_js' does not match case of 'test_194_Filename_case.embed_js' in source file; this is an error on platforms with case-sensitive filesystems. +test_194_filename_case.kmn - hint KM05009: File on disk 'test_194_filename_case.embed.css' does not match case of 'test_194_Filename_case.embed.css' in source file; this is an error on platforms with case-sensitive filesystems. +test_194_filename_case.kmn - hint KM05009: File on disk 'test_194_filename_case.keyman-touch-layout' does not match case of 'test_194_Filename_case.keyman-touch-layout' in source file; this is an error on platforms with case-sensitive filesystems. +test_194_filename_case.kmn - hint KM05009: File on disk 'test_194_filename_case_call_0.call_js' does not match case of 'test_194_Filename_case_call_0.call_js' in source file; this is an error on platforms with case-sensitive filesystems. +test_194_filename_case.kmn - hint KM05009: File on disk 'test_194_filename_case_call_1.call_js' does not match case of 'test_194_Filename_case_call_1.call_js' in source file; this is an error on platforms with case-sensitive filesystems. diff --git a/developer/src/test/auto/kmcomp/test_valid.kps b/developer/src/test/auto/kmcomp/test_valid.kps index d64ee42848e..8877759a281 100644 --- a/developer/src/test/auto/kmcomp/test_valid.kps +++ b/developer/src/test/auto/kmcomp/test_valid.kps @@ -24,6 +24,12 @@ 0 .kmx
+ + test_valid.js + Keyboard Test Valid + 0 + .js +
diff --git a/developer/src/tike/Makefile b/developer/src/tike/Makefile index ce20a3673c6..c6a8cb16044 100644 --- a/developer/src/tike/Makefile +++ b/developer/src/tike/Makefile @@ -11,7 +11,6 @@ build: version.res manifest.res icons dirs xml xsd pull-core $(TDS2DBG) $(WIN32_TARGET_PATH)\tike.exe $(COPY) $(WIN32_TARGET_PATH)\tike.exe $(DEVELOPER_PROGRAM) $(COPY) kmlmc.cmd $(DEVELOPER_PROGRAM) - $(COPY) kmlmi.cmd $(DEVELOPER_PROGRAM) $(COPY) kmlmp.cmd $(DEVELOPER_PROGRAM) $(COPY) kmc.cmd $(DEVELOPER_PROGRAM) $(COPY) $(KEYMAN_ROOT)\core\build\x86\$(TARGET_PATH)\src\kmnkbp0-0.dll $(DEVELOPER_PROGRAM) diff --git a/developer/src/tike/Profiling/AQtimeModule1.aqt b/developer/src/tike/Profiling/AQtimeModule1.aqt deleted file mode 100644 index bfb4e702131..00000000000 Binary files a/developer/src/tike/Profiling/AQtimeModule1.aqt and /dev/null differ diff --git a/developer/src/tike/child/UfrmDebug.pas b/developer/src/tike/child/UfrmDebug.pas index 615cfebc809..30c91eba0c3 100644 --- a/developer/src/tike/child/UfrmDebug.pas +++ b/developer/src/tike/child/UfrmDebug.pas @@ -371,13 +371,13 @@ procedure TfrmDebug.memoMessage(Sender: TObject; var Message: TMessage; function TfrmDebug.SetKeyEventContext: Boolean; var - context: pkm_kbp_context; - context_items: TArray; + context: pkm_core_context; + context_items: TArray; n, i: Integer; ch: Char; dk: TDeadKeyInfo; begin - context := km_kbp_state_context(FDebugCore.State); + context := km_core_state_context(FDebugCore.State); if memo.SelLength > 0 then begin @@ -392,7 +392,7 @@ function TfrmDebug.SetKeyEventContext: Boolean; // backspace in the case of backspace key, rather // than deleting the last character of the selection // (Keyman Core is not aware of selection). - km_kbp_context_clear(context); + km_core_context_clear(context); Exit(True); end; @@ -405,28 +405,28 @@ function TfrmDebug.SetKeyEventContext: Boolean; if Uni_IsSurrogate1(ch) and (i < Length(memo.Text)) and Uni_IsSurrogate2(memo.Text[i+1]) then begin - context_items[n]._type := KM_KBP_CT_CHAR; + context_items[n]._type := KM_CORE_CT_CHAR; context_items[n].character := Uni_SurrogateToUTF32(ch, memo.Text[i+1]); Inc(i); end else if Ord(ch) = $FFFC then begin - context_items[n]._type := KM_KBP_CT_MARKER; + context_items[n]._type := KM_CORE_CT_MARKER; dk := FDeadkeys.GetFromPosition(i-1); Assert(Assigned(dk)); context_items[n].marker := dk.Deadkey.Value; end else begin - context_items[n]._type := KM_KBP_CT_CHAR; + context_items[n]._type := KM_CORE_CT_CHAR; context_items[n].character := Ord(ch); end; Inc(i); Inc(n); end; - context_items[n]._type := KM_KBP_CT_END; - Result := km_kbp_context_set(context, @context_items[0]) = KM_KBP_STATUS_OK; + context_items[n]._type := KM_CORE_CT_END; + Result := km_core_context_set(context, @context_items[0]) = KM_CORE_STATUS_OK; end; function TfrmDebug.ProcessKeyEvent(var Message: TMessage): Boolean; @@ -459,24 +459,24 @@ function TfrmDebug.ProcessKeyEvent(var Message: TMessage): Boolean; vkey := MapScanCodeToUSVK((Message.LParam and $FF0000) shr 16); modifier := 0; - if GetKeyState(VK_LCONTROL) < 0 then modifier := modifier or KM_KBP_MODIFIER_LCTRL; - if GetKeyState(VK_RCONTROL) < 0 then modifier := modifier or KM_KBP_MODIFIER_RCTRL; - if GetKeyState(VK_LMENU) < 0 then modifier := modifier or KM_KBP_MODIFIER_LALT; - if GetKeyState(VK_RMENU) < 0 then modifier := modifier or KM_KBP_MODIFIER_RALT; - if GetKeyState(VK_SHIFT) < 0 then modifier := modifier or KM_KBP_MODIFIER_SHIFT; - if (GetKeyState(VK_CAPITAL) and 1) = 1 then modifier := modifier or KM_KBP_MODIFIER_CAPS; + if GetKeyState(VK_LCONTROL) < 0 then modifier := modifier or KM_CORE_MODIFIER_LCTRL; + if GetKeyState(VK_RCONTROL) < 0 then modifier := modifier or KM_CORE_MODIFIER_RCTRL; + if GetKeyState(VK_LMENU) < 0 then modifier := modifier or KM_CORE_MODIFIER_LALT; + if GetKeyState(VK_RMENU) < 0 then modifier := modifier or KM_CORE_MODIFIER_RALT; + if GetKeyState(VK_SHIFT) < 0 then modifier := modifier or KM_CORE_MODIFIER_SHIFT; + if (GetKeyState(VK_CAPITAL) and 1) = 1 then modifier := modifier or KM_CORE_MODIFIER_CAPS; - if (modifier and (KM_KBP_MODIFIER_LCTRL or KM_KBP_MODIFIER_RALT)) = (KM_KBP_MODIFIER_LCTRL or KM_KBP_MODIFIER_RALT) then + if (modifier and (KM_CORE_MODIFIER_LCTRL or KM_CORE_MODIFIER_RALT)) = (KM_CORE_MODIFIER_LCTRL or KM_CORE_MODIFIER_RALT) then begin // #7506: Windows emits LCtrl+RAlt for AltGr for European keyboards; we want // to ignore this combination - modifier := modifier and not KM_KBP_MODIFIER_LCTRL; + modifier := modifier and not KM_CORE_MODIFIER_LCTRL; end; if not SetKeyEventContext then Exit(False); - if km_kbp_process_event(FDebugCore.State, Message.WParam, modifier, 1, KM_KBP_EVENT_FLAG_DEFAULT) = KM_KBP_STATUS_OK then + if km_core_process_event(FDebugCore.State, Message.WParam, modifier, 1, KM_CORE_EVENT_FLAG_DEFAULT) = KM_CORE_STATUS_OK then begin // Process keystroke -- true = swallow keystroke Result := True; @@ -531,7 +531,7 @@ procedure TfrmDebug.StepForward; StepTwice := (_FCurrentEvent < FEvents.Count) and (FEvents[_FCurrentEvent].EventType = etRuleMatch) and - (FEvents[_FCurrentEvent].Rule.ItemType = KM_KBP_DEBUG_SET_OPTION); + (FEvents[_FCurrentEvent].Rule.ItemType = KM_CORE_DEBUG_SET_OPTION); if _FCurrentEvent < FEvents.Count then begin @@ -646,11 +646,11 @@ procedure TfrmDebug.ExecuteEventRule(n: Integer); begin { Stop running, if breakpoint options are also met } if not (ItemType in [ - KM_KBP_DEBUG_GROUP_EXIT, - KM_KBP_DEBUG_RULE_EXIT, - KM_KBP_DEBUG_MATCH_EXIT, - KM_KBP_DEBUG_NOMATCH_EXIT, - KM_KBP_DEBUG_END + KM_CORE_DEBUG_GROUP_EXIT, + KM_CORE_DEBUG_RULE_EXIT, + KM_CORE_DEBUG_MATCH_EXIT, + KM_CORE_DEBUG_NOMATCH_EXIT, + KM_CORE_DEBUG_END ]) or FKeymanDeveloperOptions.DebuggerBreakWhenExitingLine then FFoundBreakpoint := True; end; @@ -659,21 +659,21 @@ procedure TfrmDebug.ExecuteEventRule(n: Integer); { Update call stack, execution point line } case ItemType of - KM_KBP_DEBUG_BEGIN: + KM_CORE_DEBUG_BEGIN: ExecuteBegin(FEvents[n]); - KM_KBP_DEBUG_GROUP_ENTER, - KM_KBP_DEBUG_RULE_ENTER, - KM_KBP_DEBUG_NOMATCH_ENTER, - KM_KBP_DEBUG_MATCH_ENTER: + KM_CORE_DEBUG_GROUP_ENTER, + KM_CORE_DEBUG_RULE_ENTER, + KM_CORE_DEBUG_NOMATCH_ENTER, + KM_CORE_DEBUG_MATCH_ENTER: frmDebugStatus.CallStack.CallStackPush(FEvents[n].Rule); - KM_KBP_DEBUG_RULE_EXIT, - KM_KBP_DEBUG_NOMATCH_EXIT, - KM_KBP_DEBUG_MATCH_EXIT, - KM_KBP_DEBUG_GROUP_EXIT: + KM_CORE_DEBUG_RULE_EXIT, + KM_CORE_DEBUG_NOMATCH_EXIT, + KM_CORE_DEBUG_MATCH_EXIT, + KM_CORE_DEBUG_GROUP_EXIT: frmDebugStatus.CallStack.CallStackPop; - KM_KBP_DEBUG_END: + KM_CORE_DEBUG_END: begin frmDebugStatus.CallStack.CallStackClear; ExecutionPointLine := -1; end; - KM_KBP_DEBUG_SET_OPTION: + KM_CORE_DEBUG_SET_OPTION: ExecuteSetOption(FEvents[n]); else Assert(False); @@ -723,7 +723,7 @@ TMemoSelectionState = record UpdateDeadkeys; end; - procedure DoBackspace(BackspaceType: km_kbp_backspace_type); + procedure DoBackspace(BackspaceType: km_core_backspace_type); var m, n: Integer; dk: TDeadKeyInfo; @@ -737,16 +737,16 @@ TMemoSelectionState = record if memo.SelLength > 0 then begin // If the memo has a selection, we have given Core an empty context, - // which forces it to emit a KM_KBP_BT_UNKNOWN backspace, which is + // which forces it to emit a KM_CORE_BT_UNKNOWN backspace, which is // exactly what we want here. We just delete the selection - Assert(BackspaceType = KM_KBP_BT_UNKNOWN); + Assert(BackspaceType = KM_CORE_BT_UNKNOWN); memo.SelText := ''; RealignMemoSelectionState(state); Exit; end; case BackspaceType of - KM_KBP_BT_MARKER: + KM_CORE_BT_MARKER: begin Assert(m >= 1); Assert(memo.Text[m] = #$FFFC); @@ -755,7 +755,7 @@ TMemoSelectionState = record dk.Delete; Dec(m); end; - KM_KBP_BT_CHAR: + KM_CORE_BT_CHAR: begin Assert(m >= 1); Assert(memo.Text[m] <> #$FFFC); @@ -766,7 +766,7 @@ TMemoSelectionState = record else Dec(m); end; - KM_KBP_BT_UNKNOWN: + KM_CORE_BT_UNKNOWN: begin while (m >= 1) and (memo.Text[m] = #$FFFC) do begin @@ -911,14 +911,14 @@ TMemoSelectionState = record with FEvents[n].Action do begin case ActionType of - KM_KBP_IT_EMIT_KEYSTROKE: DoEmitKeystroke(dwData); - KM_KBP_IT_CHAR: DoChar(Text); - KM_KBP_IT_MARKER: DoDeadkey(dwData); - KM_KBP_IT_ALERT: DoBell; - KM_KBP_IT_BACK: DoBackspace(km_kbp_backspace_type(dwData)); - KM_KBP_IT_PERSIST_OPT: ; //TODO - KM_KBP_IT_CAPSLOCK: ; //TODO - KM_KBP_IT_INVALIDATE_CONTEXT: ; // no-op + KM_CORE_IT_EMIT_KEYSTROKE: DoEmitKeystroke(dwData); + KM_CORE_IT_CHAR: DoChar(Text); + KM_CORE_IT_MARKER: DoDeadkey(dwData); + KM_CORE_IT_ALERT: DoBell; + KM_CORE_IT_BACK: DoBackspace(km_core_backspace_type(dwData)); + KM_CORE_IT_PERSIST_OPT: ; //TODO + KM_CORE_IT_CAPSLOCK: ; //TODO + KM_CORE_IT_INVALIDATE_CONTEXT: ; // no-op end; // AddDEBUG(Format('%d: %d [%s]', [ActionType, dwData, Text])); end; diff --git a/developer/src/tike/child/UfrmEditor.pas b/developer/src/tike/child/UfrmEditor.pas index 2610a2df9e7..9cf0ca5277d 100644 --- a/developer/src/tike/child/UfrmEditor.pas +++ b/developer/src/tike/child/UfrmEditor.pas @@ -118,7 +118,6 @@ implementation CharacterDragObject, CharMapDropTool, ClipBrd, - CompileKeymanWeb, dmActionsMain, dmActionsTextEditor, keymanstrings, diff --git a/developer/src/tike/child/UfrmKeymanWizard.pas b/developer/src/tike/child/UfrmKeymanWizard.pas index 9b68894b229..1e244164c45 100644 --- a/developer/src/tike/child/UfrmKeymanWizard.pas +++ b/developer/src/tike/child/UfrmKeymanWizard.pas @@ -572,7 +572,6 @@ implementation CharMapDropTool, Clipbrd, compile, - CompileKeymanWeb, dmActionsMain, KeymanDeveloperOptions, KeymanVersion, @@ -582,6 +581,7 @@ implementation Keyman.Developer.System.Project.ProjectLog, Keyman.Developer.System.Project.kmnProjectFileAction, Keyman.Developer.System.ServerAPI, + Keyman.Developer.System.KmcWrapper, Keyman.Developer.UI.Project.ProjectFileUI, Keyman.Developer.UI.UfrmMessageDlgWithSave, ErrorControlledRegistry, @@ -2950,6 +2950,7 @@ procedure TfrmKeymanWizard.OSKImportKMX(Sender: TObject; var KMXFileName: TTempF kbdparser: TKeyboardParser; FEncoding: TEncoding; FIncludeCodes: string; + w: TKmcWrapper; begin KMXFileName := TTempFileManager.Get('.kmx'); // I4181 KMNFileName := TTempFileManager.Get('.kmn'); // I4181 @@ -2997,11 +2998,16 @@ procedure TfrmKeymanWizard.OSKImportKMX(Sender: TObject; var KMXFileName: TTempF TProject.CompilerMessageFile := ProjectFile; frmMessages.Clear; - if CompileKeyboardFile(PChar(KMNFileName.Name), PChar(KMXFileName2), False, False, False, ProjectCompilerMessage) <= 0 then // I4181 // I4865 // I4866 - begin - frmMessages.DoShowForm; - ShowMessage('There were errors compiling the keyboard to convert to the On Screen Keyboard.'); - FreeAndNil(KMXFileName); // I4181 + w := TKmcWrapper.Create; + try + if not w.Compile(ProjectFile, KMNFileName.Name, KMXFileName2, False) then + begin + frmMessages.DoShowForm; + ShowMessage('There were errors compiling the keyboard to convert to the On Screen Keyboard.'); + FreeAndNil(KMXFileName); // I4181 + end; + finally + w.Free; end; FreeAndNil(KMNFileName); // I4181 TProject.CompilerMessageFile := nil; diff --git a/developer/src/tike/child/UfrmPackageEditor.dfm b/developer/src/tike/child/UfrmPackageEditor.dfm index 4808af032f3..911781c2986 100644 --- a/developer/src/tike/child/UfrmPackageEditor.dfm +++ b/developer/src/tike/child/UfrmPackageEditor.dfm @@ -174,6 +174,7 @@ inherited frmPackageEditor: TfrmPackageEditor 1FFFFFFF9FFF} KeyPreview = True OnKeyDown = FormKeyDown + OnResize = FormResize ExplicitWidth = 965 ExplicitHeight = 622 PixelsPerInch = 96 @@ -183,7 +184,7 @@ inherited frmPackageEditor: TfrmPackageEditor Top = 0 Width = 965 Height = 622 - ActivePage = pageFiles + ActivePage = pageDetails Align = alClient Images = modActionsMain.ilEditorPages MultiLine = True @@ -235,7 +236,7 @@ inherited frmPackageEditor: TfrmPackageEditor Width = 546 Height = 32 AutoSize = False - Caption = + Caption = 'A typical package will need keyboards, fonts, and documentation.' + ' You shouldn'#39't typically add source files. Also, don'#39't add any s' + 'tandard Keyman files (such as keyman.exe) here.' @@ -434,6 +435,7 @@ inherited frmPackageEditor: TfrmPackageEditor Width = 56 Height = 13 Caption = 'Languages' + FocusControl = gridKeyboardLanguages end object lblKeyboardRTL: TLabel Left = 15 @@ -444,9 +446,31 @@ inherited frmPackageEditor: TfrmPackageEditor Caption = 'Is right-to-left:' ExplicitTop = 420 end + object lblKeyboardExamples: TLabel + Left = 260 + Top = 363 + Width = 47 + Height = 13 + Caption = 'Examples' + FocusControl = gridKeyboardExamples + end + object lblWebOSKFonts: TLabel + Left = 600 + Top = 76 + Width = 87 + Height = 13 + Caption = 'lblWebOSKFonts' + end + object lblWebDisplayFonts: TLabel + Left = 600 + Top = 103 + Width = 103 + Height = 13 + Caption = 'lblWebDisplayFonts' + end object lbKeyboards: TListBox Left = 15 - Top = 72 + Top = 68 Width = 229 Height = 408 Anchors = [akLeft, akTop, akBottom] @@ -490,7 +514,7 @@ inherited frmPackageEditor: TfrmPackageEditor object cbKeyboardOSKFont: TComboBox Left = 339 Top = 72 - Width = 230 + Width = 174 Height = 21 Style = csDropDownList TabOrder = 5 @@ -499,7 +523,7 @@ inherited frmPackageEditor: TfrmPackageEditor object cbKeyboardDisplayFont: TComboBox Left = 339 Top = 99 - Width = 230 + Width = 174 Height = 21 Style = csDropDownList TabOrder = 6 @@ -509,8 +533,8 @@ inherited frmPackageEditor: TfrmPackageEditor Left = 260 Top = 144 Width = 593 - Height = 432 - Anchors = [akLeft, akTop, akRight, akBottom] + Height = 177 + Anchors = [akLeft, akTop, akRight] ColCount = 2 DefaultRowHeight = 16 FixedCols = 0 @@ -525,22 +549,20 @@ inherited frmPackageEditor: TfrmPackageEditor end object cmdKeyboardAddLanguage: TButton Left = 260 - Top = 587 + Top = 327 Width = 73 Height = 25 - Anchors = [akLeft, akBottom] Caption = '&Add...' TabOrder = 8 OnClick = cmdKeyboardAddLanguageClick end object cmdKeyboardRemoveLanguage: TButton Left = 418 - Top = 587 + Top = 327 Width = 72 Height = 25 - Anchors = [akLeft, akBottom] Caption = '&Remove' - TabOrder = 9 + TabOrder = 10 OnClick = cmdKeyboardRemoveLanguageClick end object editKeyboardRTL: TEdit @@ -556,14 +578,81 @@ inherited frmPackageEditor: TfrmPackageEditor end object cmdKeyboardEditLanguage: TButton Left = 339 - Top = 587 + Top = 327 Width = 73 Height = 25 - Anchors = [akLeft, akBottom] Caption = 'Ed&it...' - TabOrder = 10 + TabOrder = 9 OnClick = cmdKeyboardEditLanguageClick end + object gridKeyboardExamples: TStringGrid + Left = 260 + Top = 382 + Width = 593 + Height = 177 + Anchors = [akLeft, akTop, akRight, akBottom] + ColCount = 4 + DefaultRowHeight = 16 + FixedCols = 0 + RowCount = 9 + Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goColSizing, goRowSelect] + TabOrder = 11 + OnClick = gridKeyboardExamplesClick + OnDblClick = gridKeyboardExamplesDblClick + ColWidths = ( + 78 + 64 + 64 + 64) + end + object cmdKeyboardAddExample: TButton + Left = 260 + Top = 565 + Width = 73 + Height = 25 + Anchors = [akLeft, akBottom] + Caption = 'Add...' + TabOrder = 12 + OnClick = cmdKeyboardAddExampleClick + end + object cmdKeyboardEditExample: TButton + Left = 339 + Top = 565 + Width = 73 + Height = 25 + Anchors = [akLeft, akBottom] + Caption = 'Ed&it...' + TabOrder = 13 + OnClick = cmdKeyboardEditExampleClick + end + object cmdKeyboardRemoveExample: TButton + Left = 418 + Top = 565 + Width = 72 + Height = 25 + Anchors = [akLeft, akBottom] + Caption = '&Remove' + TabOrder = 14 + OnClick = cmdKeyboardRemoveExampleClick + end + object cmdKeyboardWebOSKFonts: TButton + Left = 519 + Top = 70 + Width = 75 + Height = 25 + Caption = 'Web fonts...' + TabOrder = 15 + OnClick = cmdKeyboardWebOSKFontsClick + end + object cmdKeyboardWebDisplayFonts: TButton + Left = 519 + Top = 97 + Width = 75 + Height = 25 + Caption = 'Web fonts...' + TabOrder = 16 + OnClick = cmdKeyboardWebDisplayFontsClick + end end end object pageLexicalModels: TTabSheet @@ -734,15 +823,15 @@ inherited frmPackageEditor: TfrmPackageEditor 622) object lblKMPImageFile: TLabel Left = 15 - Top = 327 + Top = 345 Width = 53 Height = 13 Caption = 'Image file:' Transparent = True end object lblKMPImageSize: TLabel - Left = 108 - Top = 348 + Left = 114 + Top = 366 Width = 92 Height = 13 Caption = 'Image size: (w x h)' @@ -750,7 +839,7 @@ inherited frmPackageEditor: TfrmPackageEditor end object lblReadme: TLabel Left = 15 - Top = 120 + Top = 148 Width = 63 Height = 13 Caption = '&Readme file:' @@ -764,35 +853,35 @@ inherited frmPackageEditor: TfrmPackageEditor end object lblStep2c: TLabel Left = 15 - Top = 155 + Top = 202 Width = 41 Height = 13 Caption = 'Version:' end object lblStep2d: TLabel Left = 15 - Top = 203 + Top = 254 Width = 54 Height = 13 Caption = 'Copyright:' end object lblStep2e: TLabel Left = 15 - Top = 235 + Top = 282 Width = 39 Height = 13 Caption = 'Author:' end object lblStep2f: TLabel - Left = 15 - Top = 259 + Left = 295 + Top = 282 Width = 77 Height = 13 Caption = 'E-mail address:' end object lblStep2g: TLabel Left = 15 - Top = 283 + Top = 309 Width = 49 Height = 13 Caption = 'Web Site:' @@ -837,97 +926,135 @@ inherited frmPackageEditor: TfrmPackageEditor ParentFont = False end object lblVersionHint: TLabel - Left = 616 - Top = 157 + Left = 622 + Top = 204 Width = 46 Height = 13 Anchors = [akTop, akRight] Caption = 'e.g. 1.0.2' - ExplicitLeft = 317 + end + object lblDescription: TLabel + Left = 15 + Top = 397 + Width = 62 + Height = 13 + Caption = 'Description:' + FocusControl = memoInfoDescription + end + object lblDescriptionMarkdown: TLabel + Left = 114 + Top = 497 + Width = 215 + Height = 13 + Caption = 'Markdown accepted, no embedded HTML' + Transparent = True + end + object lblRelatedPackages: TLabel + Left = 16 + Top = 533 + Width = 93 + Height = 13 + Caption = 'Related packages:' + FocusControl = gridRelatedPackages + end + object lblLicenseFile: TLabel + Left = 15 + Top = 175 + Width = 59 + Height = 13 + Caption = '&License file:' + FocusControl = cbLicense + end + object lblWelcomeFile: TLabel + Left = 14 + Top = 118 + Width = 69 + Height = 13 + Caption = '&Welcome file:' + FocusControl = cbWelcomeFile end object cbReadMe: TComboBox - Left = 108 - Top = 116 + Left = 114 + Top = 143 Width = 499 Height = 21 Style = csDropDownList Anchors = [akLeft, akTop, akRight] - TabOrder = 0 + TabOrder = 2 OnClick = cbReadMeClick end object editInfoName: TEdit - Left = 108 + Left = 114 Top = 64 Width = 499 Height = 21 Anchors = [akLeft, akTop, akRight] - TabOrder = 1 + TabOrder = 0 OnChange = editInfoNameChange end object editInfoVersion: TEdit - Left = 108 - Top = 152 + Left = 114 + Top = 199 Width = 499 Height = 21 Anchors = [akLeft, akTop, akRight] - TabOrder = 2 + TabOrder = 4 OnChange = editInfoVersionChange end object editInfoCopyright: TEdit - Left = 108 - Top = 200 + Left = 114 + Top = 251 Width = 499 Height = 21 Anchors = [akLeft, akTop, akRight] - TabOrder = 4 + TabOrder = 6 Text = #169 OnChange = editInfoCopyrightChange end object editInfoAuthor: TEdit - Left = 108 - Top = 232 - Width = 499 + Left = 114 + Top = 279 + Width = 175 Height = 21 - Anchors = [akLeft, akTop, akRight] - TabOrder = 5 + TabOrder = 8 OnChange = editInfoAuthorChange end object editInfoEmail: TEdit - Left = 108 - Top = 256 - Width = 499 + Left = 377 + Top = 279 + Width = 236 Height = 21 Anchors = [akLeft, akTop, akRight] - TabOrder = 6 + TabOrder = 9 OnChange = editInfoEmailChange end object editInfoWebSite: TEdit - Left = 108 - Top = 280 + Left = 114 + Top = 306 Width = 499 Height = 21 Anchors = [akLeft, akTop, akRight] - TabOrder = 7 + TabOrder = 10 OnChange = editInfoWebSiteChange end object cmdInsertCopyright: TButton - Left = 614 - Top = 200 + Left = 620 + Top = 251 Width = 49 Height = 21 Anchors = [akTop, akRight] Caption = '&Insert '#169 - TabOrder = 8 + TabOrder = 7 OnClick = cmdInsertCopyrightClick end object cbKMPImageFile: TComboBox - Left = 108 - Top = 324 + Left = 114 + Top = 342 Width = 499 Height = 21 Style = csDropDownList Anchors = [akLeft, akTop, akRight] - TabOrder = 9 + TabOrder = 11 OnClick = cbKMPImageFileClick end object panKMPImageSample: TPanel @@ -937,7 +1064,7 @@ inherited frmPackageEditor: TfrmPackageEditor Height = 251 Anchors = [akTop, akRight] BevelOuter = bvLowered - TabOrder = 10 + TabOrder = 12 object imgKMPSample: TImage Left = 1 Top = 0 @@ -946,14 +1073,85 @@ inherited frmPackageEditor: TfrmPackageEditor end end object chkFollowKeyboardVersion: TCheckBox - Left = 108 - Top = 178 + Left = 114 + Top = 225 Width = 237 Height = 17 Caption = 'Package version follows keyboard version' - TabOrder = 3 + TabOrder = 5 OnClick = chkFollowKeyboardVersionClick end + object memoInfoDescription: TMemo + Left = 114 + Top = 394 + Width = 499 + Height = 97 + TabOrder = 13 + OnChange = memoInfoDescriptionChange + end + object gridRelatedPackages: TStringGrid + Left = 114 + Top = 530 + Width = 415 + Height = 89 + ColCount = 2 + DefaultRowHeight = 16 + FixedCols = 0 + RowCount = 9 + Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goColSizing, goRowSelect] + TabOrder = 14 + OnDblClick = gridRelatedPackagesDblClick + ColWidths = ( + 78 + 64) + end + object cmdAddRelatedPackage: TButton + Left = 541 + Top = 530 + Width = 73 + Height = 25 + Caption = '&Add...' + TabOrder = 15 + OnClick = cmdAddRelatedPackageClick + end + object cmdEditRelatedPackage: TButton + Left = 540 + Top = 562 + Width = 73 + Height = 25 + Caption = 'Ed&it...' + TabOrder = 16 + OnClick = cmdEditRelatedPackageClick + end + object cmdRemoveRelatedPackage: TButton + Left = 541 + Top = 594 + Width = 72 + Height = 25 + Caption = '&Remove' + TabOrder = 17 + OnClick = cmdRemoveRelatedPackageClick + end + object cbLicense: TComboBox + Left = 114 + Top = 171 + Width = 499 + Height = 21 + Style = csDropDownList + Anchors = [akLeft, akTop, akRight] + TabOrder = 3 + OnClick = cbLicenseClick + end + object cbWelcomeFile: TComboBox + Left = 114 + Top = 115 + Width = 499 + Height = 21 + Style = csDropDownList + Anchors = [akLeft, akTop, akRight] + TabOrder = 1 + OnClick = cbWelcomeFileClick + end end end object pageShortcuts: TTabSheet @@ -1144,7 +1342,7 @@ inherited frmPackageEditor: TfrmPackageEditor Top = 48 Width = 551 Height = 13 - Caption = + Caption = 'Compiling the package takes all the files you have selected and ' + 'compresses them into a single package file.' end @@ -1169,7 +1367,7 @@ inherited frmPackageEditor: TfrmPackageEditor BevelOuter = bvNone Color = 15921906 ParentBackground = False - TabOrder = 4 + TabOrder = 3 object lblDebugHostCaption: TLabel Left = 12 Top = 70 @@ -1256,7 +1454,7 @@ inherited frmPackageEditor: TfrmPackageEditor Left = 15 Top = 274 Width = 295 - Height = 124 + Height = 276 BevelOuter = bvNone Color = 15921906 ParentBackground = False @@ -1302,74 +1500,6 @@ inherited frmPackageEditor: TfrmPackageEditor OnClick = cmdUninstallClick end end - object panBuildWindowsInstaller: TPanel - Left = 15 - Top = 418 - Width = 295 - Height = 132 - BevelOuter = bvNone - Color = 15921906 - ParentBackground = False - TabOrder = 3 - object Label9: TLabel - Left = 9 - Top = 6 - Width = 124 - Height = 17 - Caption = 'Windows Installer' - Font.Charset = DEFAULT_CHARSET - Font.Color = clWindowText - Font.Height = -14 - Font.Name = 'Tahoma' - Font.Style = [fsBold] - ParentFont = False - end - object lblBootstrapMSI: TLabel - Left = 9 - Top = 69 - Width = 64 - Height = 13 - Caption = 'Keyman MSI:' - FocusControl = editBootstrapMSI - end - object lblInstallerOutputFilename: TLabel - Left = 9 - Top = 37 - Width = 82 - Height = 13 - Caption = 'Target filename:' - FocusControl = editInstallerOutputFilename - end - object editBootstrapMSI: TEdit - Left = 125 - Top = 66 - Width = 156 - Height = 21 - TabStop = False - ParentColor = True - ReadOnly = True - TabOrder = 1 - end - object editInstallerOutputFilename: TEdit - Left = 125 - Top = 34 - Width = 156 - Height = 21 - TabStop = False - ParentColor = True - ReadOnly = True - TabOrder = 0 - end - object cmdInstallWith: TButton - Left = 125 - Top = 96 - Width = 156 - Height = 25 - Caption = 'Find Keyman MSI...' - TabOrder = 2 - OnClick = cmdInstallWithClick - end - end object panOpenInExplorer: TPanel Left = 15 Top = 187 @@ -1459,22 +1589,13 @@ inherited frmPackageEditor: TfrmPackageEditor ReadOnly = True TabOrder = 0 end - object cmdCompileInstaller: TButton - Left = 148 - Top = 32 - Width = 133 - Height = 25 - Caption = 'Compile I&nstaller' - TabOrder = 1 - OnClick = cmdCompileInstallerClick - end object cmdAddToProject: TButton - Left = 287 + Left = 148 Top = 32 Width = 133 Height = 25 Action = modActionsMain.actProjectAddCurrentEditorFile - TabOrder = 2 + TabOrder = 1 end object cmdBuildPackage: TButton Left = 9 @@ -1482,7 +1603,7 @@ inherited frmPackageEditor: TfrmPackageEditor Width = 133 Height = 25 Caption = 'Compile &Package' - TabOrder = 3 + TabOrder = 2 OnClick = cmdBuildPackageClick end end @@ -1503,12 +1624,4 @@ inherited frmPackageEditor: TfrmPackageEditor Left = 32 Top = 532 end - object dlgOpenProductInstaller: TOpenDialog - DefaultExt = 'msi' - Filter = 'Product Installer Files (*.msi)|*.msi|All Files (*.*)|*.*' - Options = [ofHideReadOnly, ofPathMustExist, ofFileMustExist, ofEnableSizing] - Title = 'Select Product Installer' - Left = 32 - Top = 456 - end end diff --git a/developer/src/tike/child/UfrmPackageEditor.pas b/developer/src/tike/child/UfrmPackageEditor.pas index 4b36d67170b..39ab96b5f81 100644 --- a/developer/src/tike/child/UfrmPackageEditor.pas +++ b/developer/src/tike/child/UfrmPackageEditor.pas @@ -69,7 +69,6 @@ interface TfrmPackageEditor = class(TfrmTikeEditor) // I4689 dlgFiles: TOpenDialog; dlgNewCustomisation: TSaveDialog; - dlgOpenProductInstaller: TOpenDialog; pages: TLeftTabbedPageControl; pageFiles: TTabSheet; pageDetails: TTabSheet; @@ -169,13 +168,6 @@ TfrmPackageEditor = class(TfrmTikeEditor) // I4689 lblCompileTargetHeader: TLabel; cmdInstall: TButton; cmdUninstall: TButton; - panBuildWindowsInstaller: TPanel; - Label9: TLabel; - lblBootstrapMSI: TLabel; - lblInstallerOutputFilename: TLabel; - editBootstrapMSI: TEdit; - editInstallerOutputFilename: TEdit; - cmdInstallWith: TButton; pageLexicalModels: TTabSheet; panLexicalModels: TPanel; lblLexlicalModels: TLabel; @@ -200,13 +192,33 @@ TfrmPackageEditor = class(TfrmTikeEditor) // I4689 panFileActions: TPanel; lblFileActions: TLabel; editOutPath: TEdit; - cmdCompileInstaller: TButton; cmdAddToProject: TButton; cmdBuildPackage: TButton; Label5: TLabel; cmdConfigureWebDebugger: TButton; cmdSendURLsToEmail: TButton; cmdCopyDebuggerLink: TButton; + lblDescription: TLabel; + memoInfoDescription: TMemo; + lblDescriptionMarkdown: TLabel; + gridKeyboardExamples: TStringGrid; + lblKeyboardExamples: TLabel; + cmdKeyboardAddExample: TButton; + cmdKeyboardEditExample: TButton; + cmdKeyboardRemoveExample: TButton; + lblRelatedPackages: TLabel; + gridRelatedPackages: TStringGrid; + cmdAddRelatedPackage: TButton; + cmdEditRelatedPackage: TButton; + cmdRemoveRelatedPackage: TButton; + cmdKeyboardWebOSKFonts: TButton; + cmdKeyboardWebDisplayFonts: TButton; + lblWebOSKFonts: TLabel; + lblWebDisplayFonts: TLabel; + lblLicenseFile: TLabel; + cbLicense: TComboBox; + lblWelcomeFile: TLabel; + cbWelcomeFile: TComboBox; procedure cmdCloseClick(Sender: TObject); procedure cmdAddFileClick(Sender: TObject); procedure cmdRemoveFileClick(Sender: TObject); @@ -237,8 +249,6 @@ TfrmPackageEditor = class(TfrmTikeEditor) // I4689 procedure cmdUninstallClick(Sender: TObject); procedure cmdOpenContainingFolderClick(Sender: TObject); procedure cmdOpenFileClick(Sender: TObject); - procedure cmdCompileInstallerClick(Sender: TObject); - procedure cmdInstallWithClick(Sender: TObject); procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure pagesChanging(Sender: TObject; var AllowChange: Boolean); @@ -271,6 +281,21 @@ TfrmPackageEditor = class(TfrmTikeEditor) // I4689 procedure cmdCopyDebuggerLinkClick(Sender: TObject); procedure sbDetailsMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean); + procedure memoInfoDescriptionChange(Sender: TObject); + procedure cmdKeyboardAddExampleClick(Sender: TObject); + procedure cmdKeyboardEditExampleClick(Sender: TObject); + procedure cmdKeyboardRemoveExampleClick(Sender: TObject); + procedure FormResize(Sender: TObject); + procedure gridKeyboardExamplesDblClick(Sender: TObject); + procedure gridKeyboardExamplesClick(Sender: TObject); + procedure gridRelatedPackagesDblClick(Sender: TObject); + procedure cmdAddRelatedPackageClick(Sender: TObject); + procedure cmdEditRelatedPackageClick(Sender: TObject); + procedure cmdRemoveRelatedPackageClick(Sender: TObject); + procedure cmdKeyboardWebOSKFontsClick(Sender: TObject); + procedure cmdKeyboardWebDisplayFontsClick(Sender: TObject); + procedure cbLicenseClick(Sender: TObject); + procedure cbWelcomeFileClick(Sender: TObject); private pack: TKPSFile; FSetup: Integer; @@ -286,6 +311,7 @@ TfrmPackageEditor = class(TfrmTikeEditor) // I4689 procedure EnableDetailsTabControls; procedure EnableCompileTabControls; function DoAction(action: TProjectFileAction): Boolean; + procedure UpdateWelcomeFile; procedure UpdateReadme; procedure UpdateImageFiles; procedure GetStartMenuEntries(var AName, AProg, AParams: WideString); @@ -317,6 +343,15 @@ TfrmPackageEditor = class(TfrmTikeEditor) // I4689 procedure ShowAddLanguageForm(grid: TStringGrid; langs: TPackageKeyboardLanguageList); procedure RefreshLexicalModelList; procedure UpdateQRCode; + procedure RefreshKeyboardExampleList(k: TPackageKeyboard); + procedure ShowAddExampleForm(k: TPackageKeyboard); + procedure ShowEditExampleForm(k: TPackageKeyboard; example: TPackageKeyboardExample); + function SelectedKeyboardExample: TPackageKeyboardExample; + procedure ResizeGridColumns; + procedure RefreshRelatedPackagesList; + function SelectedRelatedPackage: TPackageRelatedPackage; + procedure ShowEditWebFontsForm(Fonts: TPackageContentFileReferenceList); + procedure UpdateLicense; protected function GetHelpTopic: string; override; @@ -345,6 +380,7 @@ TfrmPackageEditor = class(TfrmTikeEditor) // I4689 implementation uses + System.Math, Vcl.Clipbrd, Vcl.Imaging.GifImg, @@ -352,7 +388,6 @@ implementation CharMapDropTool, CharMapInsertMode, - CompilePackageInstaller, Keyman.Developer.System.Project.kpsProjectFile, Keyman.Developer.System.Project.kpsProjectFileAction, Keyman.Developer.System.ServerAPI, @@ -373,6 +408,9 @@ implementation UfrmMessages, UfrmSendURLsToEmail, utilexecute, + Keyman.Developer.UI.UfrmEditLanguageExample, + Keyman.Developer.UI.UfrmEditPackageWebFonts, + Keyman.Developer.UI.UfrmEditRelatedPackage, Keyman.Developer.UI.UfrmSelectBCP47Language, xmldoc; @@ -409,12 +447,19 @@ procedure TfrmPackageEditor.FormCreate(Sender: TObject); EnableControls; + gridRelatedPackages.ColWidths[0] := 240; + gridRelatedPackages.Cells[0, 0] := 'ID'; + gridRelatedPackages.Cells[1, 0] := 'Relationship'; + UpdateStartMenuPrograms; + UpdateWelcomeFile; UpdateReadme; + UpdateLicense; UpdateImageFiles; UpdateImagePreviews; RefreshKeyboardList; RefreshLexicalModelList; + RefreshRelatedPackagesList; frameSource := TframeTextEditor.Create(Self); frameSource.Parent := pageSource; @@ -440,17 +485,6 @@ procedure TfrmPackageEditor.SourceChanged(Sender: TObject); Modified := True; end; -procedure TfrmPackageEditor.cmdCompileInstallerClick(Sender: TObject); -begin - if Untitled or Modified then // I2178 - ShowMessage('You must save the package before you can build it.') - else - begin - frmMessages.Clear; - DoAction(pfaCompileInstaller); - end; -end; - procedure TfrmPackageEditor.cmdCopyDebuggerLinkClick(Sender: TObject); begin try @@ -478,6 +512,20 @@ procedure TfrmPackageEditor.FormKeyDown(Sender: TObject; var Key: Word; end; end; +procedure TfrmPackageEditor.FormResize(Sender: TObject); +begin + inherited; + ResizeGridColumns; +end; + +procedure TfrmPackageEditor.ResizeGridColumns; +begin + gridKeyboardLanguages.ColWidths[1] := System.Math.Max(gridKeyboardLanguages.ClientWidth - 120 - 1, 80); + gridLexicalModelLanguages.ColWidths[1] := System.Math.Max(gridLexicalModelLanguages.ClientWidth - 120 - 1, 80); + gridKeyboardExamples.ColWidths[3] := System.Math.Max(gridKeyboardExamples.ClientWidth - 120 - 200 - 120 - 3, 80); + gridRelatedPackages.ColWidths[1] := System.Math.Max(gridRelatedPackages.ClientWidth - 240 - 1, 80); +end; + procedure TfrmPackageEditor.FormShow(Sender: TObject); begin inherited; @@ -725,7 +773,9 @@ procedure TfrmPackageEditor.AddFile(FileName: WideString); pack.Files.Add(f); lbFiles.ItemIndex := lbFiles.Items.AddObject(ExtractFileName(FileName), f); lbFilesClick(lbFiles); + UpdateWelcomeFile; UpdateReadme; + UpdateLicense; UpdateImageFiles; UpdateStartMenuPrograms; @@ -770,7 +820,9 @@ procedure TfrmPackageEditor.cmdRemoveFileClick(Sender: TObject); lbFiles.Items.Delete(lbFiles.ItemIndex); if lbFiles.Items.Count > 0 then lbFiles.ItemIndex := 0; lbFilesClick(lbFiles); + UpdateWelcomeFile; UpdateReadme; + UpdateLicense; UpdateImageFiles; UpdateStartMenuPrograms; @@ -897,6 +949,15 @@ procedure TfrmPackageEditor.lbFilesClick(Sender: TObject); - Options page - -------------------------------------------------------------------------------} +procedure TfrmPackageEditor.cbWelcomeFileClick(Sender: TObject); +begin + if FSetup > 0 then Exit; + if cbWelcomeFile.ItemIndex <= 0 + then pack.Options.WelcomeFile := nil + else pack.Options.WelcomeFile := cbWelcomeFile.Items.Objects[cbWelcomeFile.ItemIndex] as TPackageContentFile; + Modified := True; +end; + procedure TfrmPackageEditor.cbReadMeClick(Sender: TObject); begin if FSetup > 0 then Exit; @@ -906,6 +967,16 @@ procedure TfrmPackageEditor.cbReadMeClick(Sender: TObject); Modified := True; end; +procedure TfrmPackageEditor.cbLicenseClick(Sender: TObject); +begin + if FSetup > 0 then Exit; + if cbLicense.ItemIndex <= 0 + then pack.Options.LicenseFile := nil + else pack.Options.LicenseFile := cbLicense.Items.Objects[cbLicense.ItemIndex] as TPackageContentFile; + Modified := True; +end; + + procedure TfrmPackageEditor.editCmdLineChange(Sender: TObject); begin if FSetup > 0 then Exit; @@ -1014,6 +1085,13 @@ procedure TfrmPackageEditor.lbStartMenuEntriesClick(Sender: TObject); end; end; +procedure TfrmPackageEditor.memoInfoDescriptionChange(Sender: TObject); +begin + if FSetup > 0 then Exit; + pack.Info.Desc['Description'] := Trim(memoInfoDescription.Text); + Modified := True; +end; + procedure TfrmPackageEditor.pagesChange(Sender: TObject); begin if FSetup > 0 then Exit; @@ -1182,17 +1260,6 @@ procedure TfrmPackageEditor.cmdInstallClick(Sender: TObject); DoAction(pfaInstall); end; -procedure TfrmPackageEditor.cmdInstallWithClick(Sender: TObject); -begin - dlgOpenProductInstaller.FileName := pack.KPSOptions.MSIFileName; - if dlgOpenProductInstaller.Execute then - begin - pack.KPSOptions.MSIFileName := dlgOpenProductInstaller.FileName; - Modified := True; - UpdateData; - end; -end; - {------------------------------------------------------------------------------- - Display refresh routines - -------------------------------------------------------------------------------} @@ -1207,11 +1274,21 @@ procedure TfrmPackageEditor.UpdateStartMenuPrograms; cbStartMenuProgram.Items.Insert(3, '(About Product)'); end; +procedure TfrmPackageEditor.UpdateWelcomeFile; +begin + FillFileList(cbWelcomeFile, pack.Options.WelcomeFile); +end; + procedure TfrmPackageEditor.UpdateReadme; begin FillFileList(cbReadme, pack.Options.ReadmeFile); end; +procedure TfrmPackageEditor.UpdateLicense; +begin + FillFileList(cbLicense, pack.Options.LicenseFile); +end; + procedure TfrmPackageEditor.FillFileList(combo: TComboBox; obj: TObject; FileType: TKMFileType); var i: Integer; @@ -1241,7 +1318,9 @@ procedure TfrmPackageEditor.UpdateData; lbFiles.Items.AddObject(ExtractFileName(pack.Files[i].FileName), pack.Files[i]); UpdateOutPath; + UpdateWelcomeFile; UpdateReadme; + UpdateLicense; UpdateImageFiles; UpdateStartMenuPrograms; @@ -1259,10 +1338,6 @@ procedure TfrmPackageEditor.UpdateData; editStartMenuPath.Text := pack.StartMenu.Path; editOutPath.Text := (ProjectFile as TkpsProjectFile).TargetFilename; // I4688 - editBootstrapMSI.Text := pack.KPSOptions.MSIFileName; - if pack.KPSOptions.MSIFileName = '' - then editInstallerOutputFilename.Text := '' - else editInstallerOutputFilename.Text := (ProjectFile as TkpsProjectFile).TargetInstallerFileName; // I4688 if lbFiles.Items.Count > 0 then lbFiles.ItemIndex := 0; lbFilesClick(lbFiles); @@ -1271,6 +1346,7 @@ procedure TfrmPackageEditor.UpdateData; RefreshKeyboardList; RefreshLexicalModelList; + RefreshRelatedPackagesList; EnableControls; finally @@ -1290,6 +1366,7 @@ procedure TfrmPackageEditor.UpdateInfo(nm, desc, url: WideString); else if nmlc = 'version' then editInfoVersion.Text := desc // less 'mailto:' else if nmlc = 'author' then begin editInfoAuthor.Text := desc; editInfoEmail.Text := Copy(url,8,1024); end else if nmlc = 'website' then editInfoWebSite.Text := desc + else if nmlc = 'description' then memoInfoDescription.Text := desc finally Dec(FSetup); end; @@ -1510,6 +1587,20 @@ function TfrmPackageEditor.SelectedKeyboardLanguage: TPackageKeyboardLanguage; Result := gridKeyboardLanguages.Objects[0, gridKeyboardLanguages.Row] as TPackageKeyboardLanguage; end; +function TfrmPackageEditor.SelectedKeyboardExample: TPackageKeyboardExample; +var + k: TPackageKeyboard; +begin + k := SelectedKeyboard; + if not Assigned(k) then + Exit(nil); + + if gridKeyboardExamples.Row = 0 then + Exit(nil); + + Result := gridKeyboardExamples.Objects[0, gridKeyboardExamples.Row] as TPackageKeyboardExample; +end; + procedure TfrmPackageEditor.lbKeyboardsClick(Sender: TObject); var k: TPackageKeyboard; @@ -1522,6 +1613,15 @@ procedure TfrmPackageEditor.lbKeyboardsClick(Sender: TObject); gridKeyboardLanguages.ColWidths[0] := 120; gridKeyboardLanguages.ColWidths[1] := 10; + gridKeyboardExamples.Cells[0, 0] := 'BCP 47 tag'; + gridKeyboardExamples.Cells[1, 0] := 'Keys'; + gridKeyboardExamples.Cells[2, 0] := 'Text'; + gridKeyboardExamples.Cells[3, 0] := 'Note'; + gridKeyboardExamples.ColWidths[0] := 120; + gridKeyboardExamples.ColWidths[1] := 200; + gridKeyboardExamples.ColWidths[2] := 120; + gridKeyboardExamples.ColWidths[3] := 10; + k := SelectedKeyboard; if not Assigned(k) then begin @@ -1532,6 +1632,9 @@ procedure TfrmPackageEditor.lbKeyboardsClick(Sender: TObject); cbKeyboardOSKFont.ItemIndex := -1; cbKeyboardDisplayFont.ItemIndex := -1; gridKeyboardLanguages.RowCount := 1; + gridKeyboardExamples.RowCount := 1; + lblWebOSKFonts.Caption := ''; + lblWebDisplayFonts.Caption := ''; EnableKeyboardTabControls; Exit; end; @@ -1557,41 +1660,53 @@ procedure TfrmPackageEditor.lbKeyboardsClick(Sender: TObject); FillFileList(cbKeyboardOSKFont, k.OSKFont, ftFont); FillFileList(cbKeyboardDisplayFont, k.DisplayFont, ftFont); + lblWebOSKFonts.Caption := k.WebOSKFonts.GetAsString; + lblWebDisplayFonts.Caption := k.WebDisplayFonts.GetAsString; + // Languages RefreshKeyboardLanguageList(k); + RefreshKeyboardExampleList(k); EnableKeyboardTabControls; finally Dec(FSetup); end; end; -procedure TfrmPackageEditor.RefreshKeyboardLanguageList(k: TPackageKeyboard); +procedure TfrmPackageEditor.RefreshKeyboardExampleList(k: TPackageKeyboard); var i: Integer; begin Inc(FSetup); try - gridKeyboardLanguages.RowCount := k.Languages.Count + 1; - gridKeyboardLanguages.ColWidths[1] := gridKeyboardLanguages.ClientWidth - 120 - 1; + gridKeyboardExamples.RowCount := k.Examples.Count + 1; + ResizeGridColumns; - if k.Languages.Count > 0 then + if k.Examples.Count > 0 then begin - gridKeyboardLanguages.FixedRows := 1; + gridKeyboardExamples.FixedRows := 1; end else - gridKeyboardLanguages.Enabled := False; - for i := 0 to k.Languages.Count - 1 do + gridKeyboardExamples.Enabled := False; + + for i := 0 to k.Examples.Count - 1 do begin - gridKeyboardLanguages.Objects[0, i+1] := k.Languages[i]; - gridKeyboardLanguages.Cells[0, i+1] := k.Languages[i].ID; - gridKeyboardLanguages.Cells[1, i+1] := k.Languages[i].Name; + gridKeyboardExamples.Objects[0, i+1] := k.Examples[i]; + gridKeyboardExamples.Cells[0, i+1] := k.Examples[i].ID; + gridKeyboardExamples.Cells[1, i+1] := k.Examples[i].Keys; + gridKeyboardExamples.Cells[2, i+1] := k.Examples[i].Text; + gridKeyboardExamples.Cells[3, i+1] := k.Examples[i].Note; end; finally Dec(FSetup); end; end; +procedure TfrmPackageEditor.RefreshKeyboardLanguageList(k: TPackageKeyboard); +begin + RefreshLanguageList(gridKeyboardLanguages, k.Languages); +end; + procedure TfrmPackageEditor.cbKeyboardDisplayFontClick(Sender: TObject); var k: TPackageKeyboard; @@ -1647,6 +1762,13 @@ procedure TfrmPackageEditor.EnableDetailsTabControls; editInfoVersion.Enabled := e; lblStep2C.Enabled := e; lblVersionHint.Enabled := e; + + cmdAddRelatedPackage.Enabled := True; + + e := gridRelatedPackages.Row > 0; + gridRelatedPackages.Enabled := e; + cmdEditRelatedPackage.Enabled := e; + cmdRemoveRelatedPackage.Enabled := e; end; procedure TfrmPackageEditor.EnableKeyboardTabControls; @@ -1664,13 +1786,38 @@ procedure TfrmPackageEditor.EnableKeyboardTabControls; cbKeyboardOSKFont.Enabled := e; lblKeyboardDisplayFont.Enabled := e; cbKeyboardDisplayFont.Enabled := e; + + cmdKeyboardWebOSKFonts.Enabled := e; + cmdKeyboardWebDisplayFonts.Enabled := e; + lblWebOSKFonts.Enabled := e; + lblWebDisplayFonts.Enabled := e; + lblKeyboardLanguages.Enabled := e; cmdKeyboardAddLanguage.Enabled := e; + lblKeyboardExamples.Enabled := e; + cmdKeyboardAddExample.Enabled := e; e := e and (gridKeyboardLanguages.Row > 0); gridKeyboardLanguages.Enabled := e; cmdKeyboardRemoveLanguage.Enabled := e; cmdKeyboardEditLanguage.Enabled := e; + + e := (lbKeyboards.ItemIndex >= 0) and (gridKeyboardLanguages.Row > 0); + gridKeyboardExamples.Enabled := e; + cmdKeyboardRemoveExample.Enabled := e; + cmdKeyboardEditExample.Enabled := e; + +end; + +procedure TfrmPackageEditor.gridKeyboardExamplesClick(Sender: TObject); +begin + EnableKeyboardTabControls; +end; + +procedure TfrmPackageEditor.gridKeyboardExamplesDblClick(Sender: TObject); +begin + if SelectedKeyboardExample <> nil then + cmdKeyboardEditExample.Click; end; procedure TfrmPackageEditor.gridKeyboardLanguagesClick(Sender: TObject); @@ -1684,6 +1831,15 @@ procedure TfrmPackageEditor.gridKeyboardLanguagesDblClick(Sender: TObject); cmdKeyboardEditLanguage.Click; end; +procedure TfrmPackageEditor.cmdKeyboardAddExampleClick(Sender: TObject); +var + k: TPackageKeyboard; +begin + k := SelectedKeyboard; + Assert(Assigned(k)); + ShowAddExampleForm(k); +end; + procedure TfrmPackageEditor.cmdKeyboardAddLanguageClick(Sender: TObject); var k: TPackageKeyboard; @@ -1693,6 +1849,20 @@ procedure TfrmPackageEditor.cmdKeyboardAddLanguageClick(Sender: TObject); ShowAddLanguageForm(gridKeyboardLanguages, k.Languages); end; +procedure TfrmPackageEditor.cmdKeyboardEditExampleClick(Sender: TObject); +var + k: TPackageKeyboard; + example: TPackageKeyboardExample; +begin + k := SelectedKeyboard; + Assert(Assigned(k)); + + example := SelectedKeyboardExample; + Assert(Assigned(example)); + + ShowEditExampleForm(k, example); +end; + procedure TfrmPackageEditor.cmdKeyboardEditLanguageClick(Sender: TObject); var k: TPackageKeyboard; @@ -1707,6 +1877,22 @@ procedure TfrmPackageEditor.cmdKeyboardEditLanguageClick(Sender: TObject); ShowEditLanguageForm(gridKeyboardLanguages, k.Languages, lang); end; +procedure TfrmPackageEditor.cmdKeyboardRemoveExampleClick(Sender: TObject); +var + k: TPackageKeyboard; + example: TPackageKeyboardExample; +begin + k := SelectedKeyboard; + Assert(Assigned(k)); + example := SelectedKeyboardExample; + Assert(Assigned(example)); + + k.Examples.Remove(example); + RefreshKeyboardExampleList(k); + EnableKeyboardTabControls; + Modified := True; +end; + procedure TfrmPackageEditor.cmdKeyboardRemoveLanguageClick(Sender: TObject); var k: TPackageKeyboard; @@ -1723,6 +1909,42 @@ procedure TfrmPackageEditor.cmdKeyboardRemoveLanguageClick(Sender: TObject); Modified := True; end; +procedure TfrmPackageEditor.cmdKeyboardWebDisplayFontsClick(Sender: TObject); +var + k: TPackageKeyboard; +begin + k := SelectedKeyboard; + Assert(Assigned(k)); + ShowEditWebFontsForm(k.WebDisplayFonts); +end; + +procedure TfrmPackageEditor.cmdKeyboardWebOSKFontsClick(Sender: TObject); +var + k: TPackageKeyboard; +begin + k := SelectedKeyboard; + Assert(Assigned(k)); + ShowEditWebFontsForm(k.WebOSKFonts); +end; + +procedure TfrmPackageEditor.ShowEditWebFontsForm(Fonts: TPackageContentFileReferenceList); +var + frm: TfrmEditPackageWebFonts; +begin + frm := TfrmEditPackageWebFonts.Create(Application.MainForm); + try + frm.Files := pack.Files; + frm.SelectedFonts := Fonts; + if frm.ShowModal = mrOk then + begin + lbKeyboardsClick(lbKeyboards); + Modified := True; + end; + finally + frm.Free; + end; +end; + procedure TfrmPackageEditor.RefreshTargetPanels; var i: Integer; @@ -1745,7 +1967,6 @@ procedure TfrmPackageEditor.RefreshTargetPanels; end; panBuildDesktop.Visible := FHasDesktopTarget; - panBuildWindowsInstaller.Visible := FHasDesktopTarget; panBuildMobile.Visible := FHasMobileTarget; end; @@ -1768,7 +1989,7 @@ procedure TfrmPackageEditor.RefreshLanguageList(grid: TStringGrid; langs: TPacka Inc(FSetup); try grid.RowCount := langs.Count + 1; - grid.ColWidths[1] := grid.ClientWidth - 120 - 1; + ResizeGridColumns; if langs.Count > 0 then begin @@ -1863,6 +2084,172 @@ procedure TfrmPackageEditor.ShowEditLanguageForm(grid: TStringGrid; langs: TPack end; end; +procedure TfrmPackageEditor.ShowAddExampleForm(k: TPackageKeyboard); +var + example: TPackageKeyboardExample; + frm: TfrmEditLanguageExample; +begin + frm := TfrmEditLanguageExample.Create(Application.MainForm); + try + frm.SetTitle(True); + if frm.ShowModal = mrOk then + begin + example := TPackageKeyboardExample.Create(pack); + example.ID := frm.LanguageID; + example.Keys := frm.ExampleKeys; + example.Text := frm.ExampleText; + example.Note := frm.ExampleNote; + k.Examples.Add(example); + RefreshKeyboardExampleList(k); + gridKeyboardExamples.Row := gridKeyboardExamples.RowCount - 1; + Modified := True; + EnableControls; + end; + finally + frm.Free; + end; +end; + +procedure TfrmPackageEditor.ShowEditExampleForm(k: TPackageKeyboard; example: TPackageKeyboardExample); +var + frm: TfrmEditLanguageExample; +begin + frm := TfrmEditLanguageExample.Create(Application.MainForm); + try + frm.SetTitle(False); + frm.LanguageID := example.ID; + frm.ExampleKeys := example.Keys; + frm.ExampleText := example.Text; + frm.ExampleNote := example.Note; + if frm.ShowModal = mrOk then + begin + example.ID := frm.LanguageID; + example.Keys := frm.ExampleKeys; + example.Text := frm.ExampleText; + example.Note := frm.ExampleNote; + RefreshKeyboardExampleList(k); + EnableControls; + Modified := True; + end; + finally + frm.Free; + end; +end; + +{------------------------------------------------------------------------------- + - Related Packages + -------------------------------------------------------------------------------} + +procedure TfrmPackageEditor.RefreshRelatedPackagesList; +var + i: Integer; +begin + Inc(FSetup); + try + gridRelatedPackages.RowCount := pack.RelatedPackages.Count + 1; + ResizeGridColumns; + + if pack.RelatedPackages.Count > 0 then + begin + gridRelatedPackages.FixedRows := 1; + end; + + for i := 0 to pack.RelatedPackages.Count - 1 do + begin + gridRelatedPackages.Objects[0, i+1] := pack.RelatedPackages[i]; + gridRelatedPackages.Cells[0, i+1] := pack.RelatedPackages[i].ID; + if pack.RelatedPackages[i].Relationship = '' + then gridRelatedPackages.Cells[1, i+1] := 'related' // UI string + else gridRelatedPackages.Cells[1, i+1] := pack.RelatedPackages[i].Relationship; + end; + + EnableDetailsTabControls; + finally + Dec(FSetup); + end; +end; + +function TfrmPackageEditor.SelectedRelatedPackage: TPackageRelatedPackage; +begin + if gridRelatedPackages.Row = 0 then + Exit(nil); + + Result := gridRelatedPackages.Objects[0, gridRelatedPackages.Row] as TPackageRelatedPackage; +end; + +procedure TfrmPackageEditor.cmdAddRelatedPackageClick(Sender: TObject); +var + rp: TPackageRelatedPackage; + frm: TfrmEditRelatedPackage; +begin + frm := TfrmEditRelatedPackage.Create(Application.MainForm); + try + frm.SetTitle(True); + if frm.ShowModal = mrOk then + begin + rp := TPackageRelatedPackage.Create(pack); + rp.ID := frm.PackageID; + if frm.Deprecates + then rp.Relationship := S_RelatedPackage_Deprecates + else rp.Relationship := ''; + pack.RelatedPackages.Add(rp); + RefreshRelatedPackagesList; + gridRelatedPackages.Row := gridRelatedPackages.RowCount - 1; + Modified := True; + EnableControls; + end; + finally + frm.Free; + end; +end; + +procedure TfrmPackageEditor.cmdEditRelatedPackageClick(Sender: TObject); +var + rp: TPackageRelatedPackage; + frm: TfrmEditRelatedPackage; +begin + rp := SelectedRelatedPackage; + Assert(Assigned(rp)); + frm := TfrmEditRelatedPackage.Create(Application.MainForm); + try + frm.SetTitle(False); + frm.PackageID := rp.ID; + frm.Deprecates := rp.Relationship = S_RelatedPackage_Deprecates; + if frm.ShowModal = mrOk then + begin + rp.ID := frm.PackageID; + if frm.Deprecates + then rp.Relationship := S_RelatedPackage_Deprecates + else rp.Relationship := ''; + RefreshRelatedPackagesList; + Modified := True; + EnableControls; + end; + finally + frm.Free; + end; +end; + +procedure TfrmPackageEditor.cmdRemoveRelatedPackageClick(Sender: TObject); +var + rp: TPackageRelatedPackage; +begin + rp := SelectedRelatedPackage; + Assert(Assigned(rp)); + + pack.RelatedPackages.Remove(rp); + RefreshRelatedPackagesList; + EnableDetailsTabControls; + Modified := True; +end; + +procedure TfrmPackageEditor.gridRelatedPackagesDblClick(Sender: TObject); +begin + inherited; + if SelectedRelatedPackage <> nil then + cmdEditRelatedPackage.Click; +end; + {------------------------------------------------------------------------------- - Lexical Models tab -------------------------------------------------------------------------------} diff --git a/developer/src/tike/compile/CompileKeymanWeb.pas b/developer/src/tike/compile/CompileKeymanWeb.pas deleted file mode 100644 index 13d5219f706..00000000000 --- a/developer/src/tike/compile/CompileKeymanWeb.pas +++ /dev/null @@ -1,2844 +0,0 @@ -(* - Name: CompileKeymanWeb - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 26 Apr 2006 - - Modified Date: 24 Aug 2015 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 26 Apr 2006 - mcdurdin - Add support for ContextEx and Context, and lots of polish - 21 Jun 2006 - mcdurdin - Add embedded javascript output (for IMX support) - 21 Jun 2006 - mcdurdin - Add semicolons as necessary to javascript - 21 Jun 2006 - mcdurdin - Add right-to-left support - 06 Oct 2006 - mcdurdin - Merge forward v6.2 keymanweb compiler code (v7.0 code was old) - 04 Dec 2006 - mcdurdin - Add ContextEx, LHS ContextEx - 19 Mar 2007 - mcdurdin - I714 - Compiler not using correct system stores - 19 Mar 2007 - mcdurdin - I716 - Update to KMW build 83 - 19 Mar 2007 - mcdurdin - I732 - Added partial support for mnemonic layouts (compiler code only, KMW code under development) - 30 Apr 2007 - mcdurdin - I783 - Fix index() statement for KMW compiler - 05 Jun 2007 - mcdurdin - I853 - Avoid crash when compiling ANSI keyboards - 27 Mar 2008 - mcdurdin - I1370 - Rename keyboards to avoid invalid characters during compile - 27 Mar 2008 - mcdurdin - Refactor incxstr into kmxfileutils - 14 Jun 2008 - mcdurdin - I1474 - Strip UTF8 prolog from help text - 28 Jul 2008 - mcdurdin - Compile target support - 28 Aug 2008 - mcdurdin - I1585 - spacebar not being accepted in positional KMW keyboards - 28 Aug 2008 - mcdurdin - I783 - Problems with index/any in KMW keyboards - 16 Jan 2009 - mcdurdin - I1592 - Fix crash compiling KMW keyboard when file is missing - 25 May 2009 - mcdurdin - I1971 - Don't succeed when error compiling - 25 May 2009 - mcdurdin - I1520 - Don't allow specific unsupported functions in stores for 'any' - 25 May 2009 - mcdurdin - I1959 - Fix bugs with multiple groups - 25 May 2009 - mcdurdin - I1964 - Fix crash when compiling keyboard with empty group - 22 Mar 2010 - mcdurdin - I2224 - Fix nomatch not working in KMW Compiler - 22 Mar 2010 - mcdurdin - I2242 - Fixup CR, LF not compiling in correctly in KMW - 22 Mar 2010 - mcdurdin - I2243 - Add support for nul in context of rules - 19 Apr 2010 - mcdurdin - I2308 - KeymanWeb call functions could be out of sync due to sorted list in compiler - 26 Jul 2010 - mcdurdin - I2468 - Eliminate KeymanWeb Pack - 18 May 2012 - mcdurdin - I3306 - V9.0 - Remove TntControls + Win9x support - 18 May 2012 - mcdurdin - I3310 - V9.0 - Unicode in Delphi fixes - 08 Jun 2012 - mcdurdin - I3310 - V9.0 - Unicode in Delphi fixes - 08 Jun 2012 - mcdurdin - I3337 - V9.0 - Review of input/output for Unicode - 17 Aug 2012 - mcdurdin - I3429 - V9.0 - Add support for if, set, reset, save to KeymanWeb compiler - 17 Aug 2012 - mcdurdin - I3430 - V9.0 - Add support for if(&platform) and if(&baselayout) to compilers - 17 Aug 2012 - mcdurdin - I3310 - V9.0 - Unicode in Delphi fixes - 27 Aug 2012 - mcdurdin - I3437 - V9.0 - Add support for set(&layer) and layer() - 27 Aug 2012 - mcdurdin - I3438 - V9.0 - Add support for custom virtual keys - 19 Oct 2012 - mcdurdin - I3474 - V9.0 - KeymanWeb Compiler should no longer emit UTF-8 prologue - 24 Oct 2012 - mcdurdin - I3482 - V9.0 - If compiler dll is missing, TIKE crashes after showing error message - 24 Oct 2012 - mcdurdin - I3483 - V9.0 - Add support for compiling in the layout file - 13 Dec 2012 - mcdurdin - I3659 - V9.0 - KSAVE has logical design issue with how stores are reloaded from cookie - 13 Dec 2012 - mcdurdin - I3681 - V9.0 - KeymanWeb compiler should output formatted js when debug=1 - 13 Dec 2012 - mcdurdin - I3317 - Add KS to KeymanWeb keyboard compiler for keyboards with SMP chars - 13 Dec 2012 - mcdurdin - I3683 - V9.0 - If embedjs file is missing the kmw compiler will crash - 13 Dec 2012 - mcdurdin - I3684 - V9.0 - KeymanWeb compiler needs to emit only non-system stores and a couple of system stores - 01 Jan 2013 - mcdurdin - I3690 - V9.0 - KSAVE wrongly includes initial _ in cookie name - 11 Aug 2013 - mcdurdin - I3886 - V9.0 - KMW 2.0 compiler needs to publish DisplayUnderling for KVK as Keyboard.KDU - 11 Aug 2013 - mcdurdin - I3643 - V9.0 - KMW compiler does not validate format of &layoutfile - 15 Oct 2013 - mcdurdin - I3910 - KeymanWeb compiler calculates indexes incorrectly when deadkeys are on LHS - 07 Nov 2013 - mcdurdin - I3946 - V9.0 - KDU flag needs to be added to keyboard object, not VK object in kmw compiler - 07 Nov 2013 - mcdurdin - I3947 - V9.0 - If KVK file is missing then KMW compiler will crash - 08 Nov 2013 - mcdurdin - I3956 - V9.0 - I3946 KDU insertion fails due to syntax error in compiled file - 29 Nov 2013 - mcdurdin - I3980 - V9.0 - KeymanWeb compiler does not support context() on LHS - 29 Nov 2013 - mcdurdin - I3981 - V9.0 - Keyman Engine for Web Compiler does not support notany() - 21 Feb 2014 - mcdurdin - I4060 - V9.0 - KeymanWeb compiler should validate the layout file - 21 Feb 2014 - mcdurdin - I4061 - V9.0 - KeymanWeb compiler needs defined codes for some errors - 06 Mar 2014 - mcdurdin - I4118 - V9.0 - KMW compiler should warn when extended shift flags are used - 06 Mar 2014 - mcdurdin - I4119 - V9.0 - KMW compiler should only warn unassociated keys that are not special keys - 19 Mar 2014 - mcdurdin - I4139 - V9.0 - Compress layout file when compiling to KMW - 19 Mar 2014 - mcdurdin - I4140 - V9.0 - Add keyboard version information to keyboards - 19 Mar 2014 - mcdurdin - I4141 - V9.0 - Warn when unusable key ids are used - 19 Mar 2014 - mcdurdin - I4142 - V9.0 - Validate key ids are in an acceptable format - 21 Mar 2014 - mcdurdin - I4155 - V9.0 - Keyboard version is not compiled into kmw js - 21 Mar 2014 - mcdurdin - I4154 - V9.0 - keyboardversion is not read by KeymanWeb compiler and applied to filename. - 01 May 2014 - mcdurdin - I4198 - V9.0 - Block U_0000-U_001F and U_0080-U_009F in layout files - 10 Jun 2014 - mcdurdin - I4259 - V9.0 - Generate a .json file when compiling keyboard for web - 12 Jun 2014 - mcdurdin - I4263 - V9.0 - Compile to web and test fails when keyboard version is not 1.0 - 12 Aug 2014 - mcdurdin - I4368 - V9.0 - Add custom stylesheet to kmw compile - 12 Aug 2014 - mcdurdin - I4373 - V9.0 - Add line number comments to js when compiling with debug - 28 Aug 2014 - mcdurdin - I4384 - V9.0 - KMW compiler adds line number comment in non-debug mode in some cases - 13 Oct 2014 - mcdurdin - I4447 - V9.0 - The dismiss keyboard and tab buttons should not be required by Keyman Developer now - 04 Nov 2014 - mcdurdin - I4505 - V9.0 - Add JSON metadata editor to keyboard wizard - 07 Mar 2015 - mcdurdin - I4611 - V9.0 - context statement in output could fail in some situations in KeymanWeb compiler - 04 May 2015 - mcdurdin - I4688 - V9.0 - Add build path to project settings - 27 May 2015 - mcdurdin - I4724 - Compiler generates warnings for JSON files if output path is source path - 24 Aug 2015 - mcdurdin - I4872 - OSK font and Touch Layout font should be the same in Developer - 24 Aug 2015 - mcdurdin - I4866 - Add warn on deprecated features to project and compile - - 24 Aug 2015 - mcdurdin - I4865 - Add treat hints and warnings as errors into project - 28 Feb 2018 - jahorton - GH 281 - Changed the compilation targets for KMW 10 to better support deadkeys. - -*) -unit CompileKeymanWeb; // I3306 // I3310 - -interface - -uses - Winapi.Windows, - System.Character, - System.Classes, - System.Generics.Collections, - System.UITypes, - - compile, - kmxfile, - kmxfileconsts, - Keyman.Developer.System.Project.ProjectFile; - -type - TSentinelRecordAny = record - StoreIndex: Integer; - Store: PFILE_STORE; - end; - - TSentinelRecordIndex = record - StoreIndex: Integer; - Store: PFILE_STORE; - Index: Integer; - end; - - TSentinelRecordContextEx = record - Index: Integer; - end; - - TSentinelRecordDeadkey = record - DeadKey: Integer; - end; - - TSentinelRecordUse = record - GroupIndex: Integer; - Group: PFILE_GROUP; - end; - - TSentinelRecordCall = record - StoreIndex: Integer; - Store: PFILE_STORE; - end; - - TSentinelRecordIfOpt = record // I3429 - StoreIndex1: Integer; - Store1: PFILE_STORE; - StoreIndex2: Integer; - Store2: PFILE_STORE; - IsNot: Integer; - end; - - TSentinelRecordSetOpt = record // I3429 - StoreIndex1: Integer; - Store1: PFILE_STORE; - StoreIndex2: Integer; - Store2: PFILE_STORE; - end; - - TSentinelRecordResetOpt = record // I3429 - StoreIndex: Integer; - Store: PFILE_STORE; - end; - - TSentinelRecordSaveOpt = record // I3429 - StoreIndex: Integer; - Store: PFILE_STORE; - end; - - TSentinelRecordIfSystemStore = record // I3430 - dwSystemID: DWORD; - SystemStore: PFILE_STORE; - StoreIndex: Integer; - Store: PFILE_STORE; - IsNot: Integer; - end; - - TSentinelRecordSetSystemStore = record // I3437 - dwSystemID: DWORD; - SystemStore: PFILE_STORE; - StoreIndex: Integer; - Store: PFILE_STORE; - end; - - TSentinelRecord = record - IsSentinel: Boolean; - Code: Integer; - Any: TSentinelRecordAny; - Index: TSentinelRecordIndex; - Deadkey: TSentinelRecordDeadkey; - Use: TSentinelRecordUse; - Call: TSentinelRecordCall; - ContextEx: TSentinelRecordContextEx; - IfOpt: TSentinelRecordIfOpt; // I3429 - IfSystemStore: TSentinelRecordIfSystemStore; // I3430 - SetOpt: TSentinelRecordSetOpt; // I3429 - SetSystemStore: TSentinelRecordSetSystemStore; // I3437 - ResetOpt: TSentinelRecordResetOpt; // I3429 - SaveOpt: TSentinelRecordSaveOpt; // I3429 - //Use: Integer; - ChrVal: DWord; - end; - - TCompileKeymanWeb = class - private - FError: Boolean; // I1971 - FCallback: TCompilerCallbackW; - FCallFunctions: TStringList; - FOutFile, FInFile: string; - FKeyboardVersion: string; - fk: FILE_KEYBOARD; - nl: string; // I3681 - FDebug: Boolean; // I3681 - FTabStop: string; // I3681 - fMnemonic: Boolean; - FCompilerWarningsAsErrors: Boolean; - FTouchLayoutFont: string; - FFix183_LadderLength: Integer; - FCloseBrace: Boolean; // I4872 - FUnreachableKeys: TList; - - function JavaScript_String(ch: DWord): string; // I2242 - - function IsKeyboardVersion10OrLater: Boolean; - function IsKeyboardVersion14OrLater: Boolean; - - procedure ReportError(line: Integer; msgcode: LongWord; const text: string); // I1971 - function ExpandSentinel(pwsz: PWideChar): TSentinelRecord; - function CallFunctionName(s: WideString): WideString; - function JavaScript_Name(i: Integer; pwszName: PWideChar; KeepNameForPersistentStorage: Boolean = False): string; // I3659 - function JavaScript_Store(line: Integer; pwsz: PWideChar): string; - function JavaScript_Shift(fkp: PFILE_KEY; FMnemonic: Boolean): Integer; - function JavaScript_ShiftAsString(fkp: PFILE_KEY; FMnemonic: Boolean): string; // I4872 - function JavaScript_Key(fkp: PFILE_KEY; FMnemonic: Boolean): Integer; - function JavaScript_KeyAsString(fkp: PFILE_KEY; FMnemonic: Boolean): string; - function JavaScript_ContextLength(Context: PWideChar): Integer; - function JavaScript_OutputString(FTabstops: string; fkp: PFILE_KEY; pwszOutput: PWideChar; fgp: PFILE_GROUP): string; - function JavaScript_ContextMatch(fkp: PFILE_KEY; context: PWideChar): string; - function JavaScript_Rule(FTabStops, FElse: string; fgp: PFILE_GROUP; fkp: PFILE_KEY): string; - function JavaScript_Rules(fgp: PFILE_GROUP): string; - function JavaScript_CompositeContextValue(fkp: PFILE_KEY; pwsz: PWideChar): string; - function JavaScript_FullContextValue(fkp: PFILE_KEY; pwsz: PWideChar): string; - function RuleIsExcludedByPlatform(fkp: PFILE_KEY): Boolean; - function RequotedString(s: WideString; RequoteSingleQuotes: Boolean = False): string; - function VisualKeyboardFromFile( - const FVisualKeyboardFileName: string; var fDisplayUnderlying: Boolean): WideString; - function WriteCompiledKeyboard: string; - procedure CheckStoreForInvalidFunctions(key: PFILE_KEY; store: PFILE_STORE); // I1971 - function GetCodeName(code: Integer): string; // I3438 - function HasSupplementaryPlaneChars: Boolean; // I3317 - function ValidateLayoutFile(var sLayoutFile: string; const sVKDictionary: string): Boolean; // I4139 - function GetKeyboardModifierBitmask: string; - function FormatModifierAsBitflags(FBitMask: Cardinal): string; - function FormatKeyAsString(key: Integer): string; - function JavaScript_SetupDebug: string; - function JavaScript_SetupEpilog: string; - function JavaScript_SetupProlog: string; - function IsKeyboardVersion15OrLater: Boolean; - function WriteBeginStatement(const name: string; - groupIndex: Integer): string; - function FormatKeyForErrorMessage(fkp: PFILE_KEY; - FMnemonic: Boolean): string; - public - function Compile(AOwnerProject: TProject; const InFile: string; const OutFile: string; Debug: Boolean; Callback: TCompilerCallbackW): Boolean; // I3681 // I4140 // I4688 // I4866 - constructor Create; - destructor Destroy; override; - end; - -implementation - -uses - Vcl.Graphics, - System.JSON, - System.Math, - System.StrUtils, - System.SysUtils, - System.TypInfo, - - CompileErrorCodes, - JsonUtil, - KeymanDeveloperOptions, - Keyman.System.KeyboardUtils, - KeymanWebKeyCodes, - kmxfileutils, - TouchLayout, - Unicode, - utilstr, - VisualKeyboard, - VKeys; - -const - SValidIdentifierCharSet = ['A'..'Z','a'..'z','0'..'9','_']; - -var - GCallbackW: TCompilerCallbackW = nil; - -function WebCompilerMessageA(line: Integer; msgcode: LongWord; text: PAnsiChar): Integer; stdcall; // I3310 // I4694 -begin - if Assigned(GCallbackW) - then Result := GCallbackW(line, msgcode, PWideChar(WideString(AnsiString(text)))) - else Result := 1; -end; - -function TCompileKeymanWeb.Compile(AOwnerProject: TProject; const InFile: string; const OutFile: string; Debug: Boolean; Callback: TCompilerCallbackW): Boolean; // I3681 // I4140 // I4688 // I4866 // I4865 -var - WarnDeprecatedCode: Boolean; - Data: string; -begin - FUnreachableKeys.Clear; - - FCallback := Callback; - FInFile := InFile; - FOutFile := OutFile; // I4140 // I4155 // I4154 - FDebug := Debug; // I3681 - FError := False; // I1971 - - if FileExists(OutFile) then - DeleteFile(OutFile); - - if FDebug then // I3681 - begin - nl := #13#10; - FTabStop := ' '; - end - else - begin - nl := ''; - FTabStop := ''; - end; - - if Assigned(AOwnerProject) then // I4865 // I4866 - begin - FCompilerWarningsAsErrors := AOwnerProject.Options.CompilerWarningsAsErrors; - WarnDeprecatedCode := AOwnerProject.Options.WarnDeprecatedCode; - end - else - begin - FCompilerWarningsAsErrors := False; - WarnDeprecatedCode := True; - end; - - GCallbackW := Callback; - FCallFunctions := TStringList.Create; - try - if CompileKeyboardFileToBuffer(PChar(InFile), @fk, - FCompilerWarningsAsErrors, WarnDeprecatedCode, - WebCompilerMessageA, CKF_KEYMANWEB) > 0 then // I3482 // I4866 // I4865 - // TODO: Free fk - begin - if Assigned(AOwnerProject) and - Assigned(AOwnerProject.CompilerMessageFile) and - AOwnerProject.CompilerMessageFile.HasCompileWarning and - FCompilerWarningsAsErrors then - FError := True; - - if not FError then - begin - Data := WriteCompiledKeyboard; - - if not FError then - with TStringStream.Create(Data, TEncoding.UTF8) do - try - SaveToFile(OutFile); - finally - Free; - end; - end; - - Result := not FError; // I1971 - end - else - begin - Result := False; - end; - finally - FreeAndNil(FCallFunctions); - GCallbackW := nil; - end; -end; - -constructor TCompileKeymanWeb.Create; -begin - FUnreachableKeys := TList.Create; - FillChar(fk, sizeof(fk), 0); - FFix183_LadderLength := FKeymanDeveloperOptions.Fix183_LadderLength; // How frequently to break ladders -end; - -destructor TCompileKeymanWeb.Destroy; -begin - // TODO: Free FK values - FUnreachableKeys.Free; - inherited; -end; - -function TCompileKeymanWeb.JavaScript_ContextLength(Context: PWideChar): Integer; -begin - Result := xstrlen_printing(Context); -end; - -// Used when targeting versions prior to 10.0, before the introduction of FullContextMatch/KFCM. -function TCompileKeymanWeb.JavaScript_CompositeContextValue(fkp: PFILE_KEY; pwsz: PWideChar): string; - // Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions - function IsRegExpSpecialChar(ch: WideChar): Boolean; - begin - Result := Pos('"\^$*+?{}.()|[]/', ch) > 0; - end; - -var - StartQuotes, Len, Cur: Integer; - InQuotes: Boolean; - rec: TSentinelRecord; -begin - Result := ''; - - InQuotes := False; - Len := JavaScript_ContextLength(pwsz); - StartQuotes := -1; - Cur := 0; - - while pwsz^ <> #0 do - begin - rec := ExpandSentinel(pwsz); - if rec.IsSentinel then - begin - if InQuotes then - begin - Result := Result + Format('",%d)', [Cur - StartQuotes]); - InQuotes := False; - end; - if Result <> '' then Result := Result + '&&'; - - case rec.Code of - CODE_ANY: - begin - CheckStoreForInvalidFunctions(fkp, rec.Any.Store); // I1520 - Result := Result + Format('k.KA(%d,k.KC(%d,1,t),this.s%s)', [Cur, Len - Cur, JavaScript_Name(rec.Any.StoreIndex, rec.Any.Store.szName)]); - end; - CODE_DEADKEY: - begin - Result := Result + Format('k.KDM(%d,t,%d)', [Len-Cur, rec.Deadkey.Deadkey]); - Dec(Cur); // don't increment on deadkeys -- correlates with AdjustIndex function // I3910 - end; - CODE_NUL: // I2243 - begin - Result := Result + Format('k.KN(%d,t)', [Len-Cur]); - Dec(Cur); // don't increment on nul -- correlates with AdjustIndex function // I3910 - end; - CODE_IFOPT: // I3429 - begin - Result := Result + Format('this.s%s%sthis.s%s', - [JavaScript_Name(rec.IfOpt.StoreIndex1, rec.IfOpt.Store1.szName), - IfThen(rec.IfOpt.IsNot = 0, '!==', '==='), - JavaScript_Name(rec.IfOpt.StoreIndex2,rec.IfOpt.Store2.szName)]); // I3429 // I3659 // I3681 - Dec(Cur); // don't increment on ifopt -- correlates with AdjustIndex function // I3910 - end; - CODE_IFSYSTEMSTORE: // I3430 - begin - Result := Result + Format('%sk.KIFS(%d,this.s%s,t)', - [IfThen(rec.IfSystemStore.IsNot = 0, '!', ''), - rec.IfSystemStore.dwSystemID, - JavaScript_Name(rec.IfSystemStore.StoreIndex,rec.IfSystemStore.Store.szName)]); // I3430 // I3659 // I3681 - Dec(Cur); // don't increment on ifsystemstore -- correlates with AdjustIndex function // I3910 - end; - CODE_CONTEXTEX: // I3980 - begin - Result := Result + Format('k.KCCM(%d,%d,t)', [Len-Cur,Len-rec.ContextEx.Index+1]); - end; - CODE_NOTANY: // I3981 - begin - CheckStoreForInvalidFunctions(fkp, rec.Any.Store); // I1520 - Result := Result + Format('k.KC(%d,1,t)!=""&&!k.KA(%d,k.KC(%d,1,t),this.s%s)', [Len - Cur, Cur, Len - Cur, JavaScript_Name(rec.Any.StoreIndex, rec.Any.Store.szName)]); - end; - else - - begin - ReportError(fkp.Line, CERR_NotSupportedInKeymanWebContext, Format('Statement %s is not currently supported in context', [GetCodeName(rec.Code)])); // I1971 // I4061 - //CODE_NUL: ; // todo: check if context is longer than that... - Result := Result + '/*.*/ 0 '; - end; - end; - - -(* - switch(*(Context+1)) - { - case CODE_NUL: /* todo: check if context is longer than that... */ - break; - case CODE_INDEX: - /*todo: check index - s = &lpActiveKeyboard->Keyboard->dpStoreArray[(*(p+2))-1]; - *indexp = n = IndexStack[(*(p+3))-1]; - - for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--); - if(n != 0) return FALSE; - if(GetSuppChar(temp) != GetSuppChar(q)) return FALSE; - - */ - break; - case CODE_CONTEXT: - - /* if(GetSuppChar(q) != GetSuppChar(qbuf)) return FALSE; */ - 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(GetSuppChar(temp) != GetSuppChar(q)) return FALSE; */ - break; - default: - return ""; - } -*) - end - else - begin - if not InQuotes then - begin - if Result <> '' then Result := Result + '&&'; - Result := Result + Format('k.KCM(%d,t,"', [Len - Cur]); - StartQuotes := Cur; - InQuotes := True; - end; - if rec.ChrVal in [Ord('"'), Ord('\')] then Result := Result + '\'; - Result := Result + Javascript_String(rec.ChrVal); // I2242 - end; - - Inc(Cur); - pwsz := incxstr(pwsz); - end; - - if InQuotes then - Result := Result + Format('",%d)', [Cur - StartQuotes]); -end; - -// Used when targeting versions >= 10.0, after the introduction of FullContextMatch/KFCM. -function TCompileKeymanWeb.JavaScript_FullContextValue(fkp: PFILE_KEY; pwsz: PWideChar): string; - // Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions - function IsRegExpSpecialChar(ch: WideChar): Boolean; - begin - Result := Pos('"\^$*+?{}.()|[]/', ch) > 0; - end; - -var - Len: Integer; - rec: TSentinelRecord; - FullContext, Suffix: string; -begin - Result := ''; - FullContext := ''; - Suffix := ''; - Len := xstrlen(pwsz); - - while pwsz^ <> #0 do - begin - if FullContext <> '' then - FullContext := FullContext + ','; - - rec := ExpandSentinel(pwsz); - if rec.IsSentinel then - begin - case rec.Code of - CODE_ANY: - begin - CheckStoreForInvalidFunctions(fkp, rec.Any.Store); // I1520 - FullContext := FullContext + Format('{t:''a'',a:this.s%s}', [JavaScript_Name(rec.Any.StoreIndex, rec.Any.Store.szName)]); - end; - CODE_DEADKEY: - begin - FullContext := FullContext + Format('{t:''d'',d:%d}', [rec.Deadkey.Deadkey]); - end; - CODE_NUL: // I2243 - begin - FullContext := FullContext + '{t:''n''}'; - end; - CODE_IFOPT: // I3429 - begin - Dec(Len); - if Suffix <> '' then Suffix := Suffix + '&&'; - if FullContext = ',' then FullContext := ''; - Suffix := Suffix + Format('this.s%s%sthis.s%s', - [JavaScript_Name(rec.IfOpt.StoreIndex1, rec.IfOpt.Store1.szName), - IfThen(rec.IfOpt.IsNot = 0, '!==', '==='), - JavaScript_Name(rec.IfOpt.StoreIndex2,rec.IfOpt.Store2.szName)]); // I3429 // I3659 // I3681 - end; - CODE_IFSYSTEMSTORE: // I3430 - begin - Dec(Len); - if Suffix <> '' then Suffix := Suffix + '&&'; - if FullContext = ',' then FullContext := ''; - Suffix := Suffix + Format('%sk.KIFS(%d,this.s%s,t)', - [IfThen(rec.IfSystemStore.IsNot = 0, '!', ''), - rec.IfSystemStore.dwSystemID, - JavaScript_Name(rec.IfSystemStore.StoreIndex,rec.IfSystemStore.Store.szName)]); // I3430 // I3659 // I3681 - end; - CODE_NOTANY: // I3981 - begin - CheckStoreForInvalidFunctions(fkp, rec.Any.Store); // I1520 - FullContext := FullContext + Format('{t:''a'',a:this.s%s,n:1}', [JavaScript_Name(rec.Any.StoreIndex, rec.Any.Store.szName)]); - end; - CODE_CONTEXTEX: - begin - FullContext := FullContext + Format('{t:''c'',c:%d}', [rec.ContextEx.Index]); // I4611 - end; - CODE_INDEX: - begin - FullContext := FullContext + Format('{t:''i'',i:this.s%s,o:%d}', - [JavaScript_Name(rec.Index.StoreIndex, rec.Index.Store.szName), rec.Index.Index]); // I4611 - end; - else - - begin - ReportError(fkp.Line, CERR_NotSupportedInKeymanWebContext, Format('Statement %s is not currently supported in context', [GetCodeName(rec.Code)])); // I1971 // I4061 - //CODE_NUL: ; // todo: check if context is longer than that... - Result := Result + '/*.*/ 0 '; - end; - end; - end - else - begin // Simple context character. - FullContext := FullContext + ''''; - if rec.ChrVal in [Ord('"'), Ord('\'), Ord('''')] then - FullContext := FullContext + '\'; - FullContext := FullContext + Javascript_String(rec.ChrVal) + ''''; // I2242 - end; - - pwsz := incxstr(pwsz); - end; - - if FullContext <> '' then - Result := Format('k.KFCM(%d,t,[%s])', [Len, FullContext]); - - if (Result <> '') and (Suffix <> '') - then Result := Result + '&&' + Suffix - else if Suffix <> '' then - Result := Suffix; - -end; - -function TCompileKeymanWeb.JavaScript_ContextMatch(fkp: PFILE_KEY; context: PWideChar): string; -begin - if IsKeyboardVersion10OrLater - then Result := JavaScript_FullContextValue(fkp, context) - else Result := JavaScript_CompositeContextValue(fkp, context); -end; - -function TCompileKeymanWeb.JavaScript_Rule(FTabStops, FElse: string; fgp: PFILE_GROUP; fkp: PFILE_KEY): string; -var - predicate, linecomment: string; - FIndent: string; -begin - Result := ''; - - if (fkp.Line > 0) and FDebug // I4384 - then linecomment := Format(' // Line %d', [fkp.Line]) // I4373 - else linecomment := ''; - - if xstrlen(fkp.dpContext) > 0 - then predicate := JavaScript_ContextMatch(fkp, fkp.dpContext) - else predicate := '1'; // Always pass - - FIndent := FTabStops+FTabStop; - FCloseBrace := True; - Result := Result + Format('%s%sif(%s){%s', [ - FTabStops, - FElse, - predicate, - nl - ]); - - if(fgp.fUsingKeys) // I1959 - then Result := Result + Format('%sr=m=1;%s%s', [FIndent,linecomment,JavaScript_OutputString(FIndent, fkp, fkp.dpOutput, fgp)]) // I1959 // I3681 - else Result := Result + Format('%sm=1;%s%s', [FIndent,linecomment,JavaScript_OutputString(FIndent, fkp, fkp.dpOutput, fgp)]); // I1959 // I3681 - if not FCloseBrace - then Result := Result + nl - else Result := Result + Format('%s%s}%s', [nl, FTabStops, nl]); // I3681 -end; - -function TCompileKeymanWeb.JavaScript_Rules(fgp: PFILE_GROUP): string; - function IsEqualKey(k1, k2: PFILE_KEY): Boolean; - begin - Result := - (JavaScript_Key(k1, FMnemonic) = JavaScript_Key(k2, FMnemonic)) and - (JavaScript_Shift(k1, FMnemonic) = JavaScript_Shift(k2, FMnemonic)); - end; - -var - j, j2: Integer; - HasRules: Boolean; - fkp2, fkp: PFILE_KEY; - processed_rule: array of Boolean; - LocalHasRules: Boolean; - LocalCounter: Integer; - Counter: Integer; -begin - Result := ''; - fkp := fgp.dpKeyArray; - HasRules := False; - - SetLength(processed_rule, fgp.cxKeyArray); - for j := 0 to Integer(fgp.cxKeyArray) - 1 do - processed_rule[j] := False; - - j := 0; - Counter := 0; - while j < Integer(fgp.cxKeyArray) do // I1964 - begin - if not processed_rule[j] and not RuleIsExcludedByPlatform(fkp) then - begin - // Break down by key code - // We know the rules are sorted by context length and then key code. - // First pass, break the grouping down by key code. - - if fgp.fUsingKeys then - begin - Result := Result + Format('%s%sif(k.KKM(e,%s,%s)) {%s', - [ - FTabStop+FTabStop, - IfThen(HasRules, 'else ', ''), - JavaScript_ShiftAsString(fkp, fMnemonic), - JavaScript_KeyAsString(fkp, fMnemonic), - nl - ]); - - HasRules := True; - Inc(Counter); - - LocalHasRules := False; - fkp2 := fkp; - j2 := j; - LocalCounter := 0; - while (j < Integer(fgp.cxKeyArray)) do - begin - if not processed_rule[j] and not RuleIsExcludedByPlatform(fkp) and IsEqualKey(fkp, fkp2) then - begin - processed_rule[j] := True; - Result := Result + JavaScript_Rule(FTabStop + FTabStop + FTabStop, IfThen(LocalHasRules, 'else ', ''), fgp, fkp); - Inc(LocalCounter); - - if (FFix183_LadderLength <> 0) and ((LocalCounter mod FFix183_LadderLength) = 0) then - begin - // Break if/else ladders - Result := Result + Format('%sif(m) {}%s', [FTabStop + FTabStop + FTabStop, nl]); - end; - LocalHasRules := True; - end; - - Inc(fkp); - Inc(j); - end; - - Result := Result + FTabStop + FTabStop + '}' + nl; - fkp := fkp2; - j := j2 + 1; - Inc(fkp); - // Inc(j); - end - else - begin - // TODO: context character level switches instead of full context comparisons - Result := Result + JavaScript_Rule(FTabStop + FTabStop + FTabStop, IfThen(HasRules, 'else ', ''), fgp, fkp); - HasRules := True; - Inc(Counter); - Inc(fkp); - Inc(j); - end; - - if (FFix183_LadderLength <> 0) and ((Counter mod FFix183_LadderLength) = 0) then - begin - // Break if/else ladders - // We need to only match if no previous line is matched (i.e. m is false) - Result := Result + Format('%sif(m) {}%s', [FTabStop + FTabStop + FTabStop, nl]); - end; - end - else - begin - Inc(fkp); - Inc(j); - end; - end; -end; - -const // I1585 - add space to conversion - USEnglishUnshift: WideString = ' `' + '1234567890' + '-' + '=' + 'qwertyuiop' + '[' + ']' + '\' + 'asdfghjkl' + ';' + '''' + 'zxcvbnm' + ',' + '.' + '/'; - USEnglishShift: WideString = #$FF'~' + '!@#$%^&*()' + '_' + '+' + 'QWERTYUIOP' + '{' + '}' + '|' + 'ASDFGHJKL' + ':' + '"' + 'ZXCVBNM' + '<' + '>' + '?'; - USEnglishValues: WideString = #$20#$c0 + '1234567890' + #$bd + #$bb + 'QWERTYUIOP' + #$db + #$dd + #$dc + 'ASDFGHJKL' + #$ba + #$de + 'ZXCVBNM' + #$bc + #$be + #$bf; - UnreachableKeyCodes: array[0..19] of Integer = // I4141 - ($00, // &H0 - VK_LBUTTON, // &H1 - VK_RBUTTON, // &H2 - VK_CANCEL, // &H3 - VK_MBUTTON, // &H4 - $05, // &H5 - $06, // &H6 - $07, // &H7 - $0A, // &HA - $0B, // &HB - $0E, // &HE - $0F, // &HF - VK_SHIFT, // &H10 - VK_CONTROL, // &H11 - VK_MENU, // &H12 - VK_PAUSE, // &H13 - VK_CAPITAL, // &H14 - VK_ESCAPE, // &H1B - - VK_NUMLOCK, // &H90 - VK_SCROLL); // &H91 - -function TCompileKeymanWeb.FormatKeyForErrorMessage(fkp: PFILE_KEY; FMnemonic: Boolean): string; - function FormatShift(ShiftFlags: DWord): string; - const - mask: array[0..13] of string = ( - 'LCTRL', // 0X0001 - 'RCTRL', // 0X0002 - 'LALT', // 0X0004 - 'RALT', // 0X0008 - - 'SHIFT', // 0X0010 - 'CTRL', // 0X0020 - 'ALT', // 0X0040 - - '???', // Reserved - - 'CAPS', // 0X0100 - 'NCAPS', // 0X0200 - - 'NUMLOCK', // 0X0400 - 'NNUMLOCK', // 0X0800 - - 'SCROLLLOCK', // 0X1000 - 'NSCROLLLOCK' // 0X2000 - ); - var - i: Integer; - begin - Result := ''; - for i := 0 to High(mask) do - begin - if ShiftFlags and (1 shl i) <> 0 then - begin - Result := Result + mask[i] + ' '; - end; - end; - end; -begin - if not FMnemonic then - begin - if (fkp.ShiftFlags and KMX_ISVIRTUALKEY) = KMX_ISVIRTUALKEY then - begin - if Ord(fkp.Key) < 256 - then Result := Format('[%s%s]', [FormatShift(fkp.ShiftFlags), VKeyNames[Ord(fkp.Key)]]) - else Result := Format('[%sK_%x]', [FormatShift(fkp.ShiftFlags), Ord(fkp.Key)]); - end - else - begin - Result := Format('''%s''', [fkp.Key]); - end; - end - else - begin - if (fkp.ShiftFlags and KMX_VIRTUALCHARKEY) = KMX_VIRTUALCHARKEY - then Result := Format('[%s''%s'']', [FormatShift(fkp.ShiftFlags), fkp.Key]) - else Result := Format('''%s''', [fkp.Key]); - end; -end; - -function TCompileKeymanWeb.JavaScript_Key(fkp: PFILE_KEY; FMnemonic: Boolean): Integer; -var - n: Integer; - i: Integer; -begin - if not FMnemonic then - begin - if (fkp.ShiftFlags and KMX_ISVIRTUALKEY) = KMX_ISVIRTUALKEY then - begin - Result := Ord(fkp.Key); - end - else - begin - // Convert the character to a virtual key - n := Pos(fkp.Key, USEnglishShift); - if n = 0 then n := Pos(fkp.Key, USEnglishUnshift); - if n = 0 - then Result := 0 - else Result := Ord(USEnglishValues[n]); - end; - end - else - begin - Result := Ord(fkp.Key); - end; - - // Check that key is not unreachable (e.g. K_SHIFT, touch-specific special keys 50,000+) - - for i := 0 to High(UnreachableKeyCodes) do // I4141 - if Result = UnreachableKeyCodes[i] then - Result := 0; - - if (Result = 0) or (Result >= Ord(Low(TKeymanWebTouchStandardKey))) then // I4141 - begin - if not FUnreachableKeys.Contains(fkp) then - begin - ReportError(fkp.Line, CHINT_UnreachableKeyCode, - 'The rule will never be matched for key '+ - FormatKeyForErrorMessage(fkp,FMnemonic)+' because its key code is never fired.'); - FUnreachableKeys.Add(fkp); - end; - end; -end; - -/// -/// Returns a Javascript representation of a key value, either as a constant (debug mode) -/// or as an integer. -/// -/// @param fkp Pointer to key record -/// @param FMnemonic True if the keyboard is a mnemonic layout -/// -/// @return string representation of the key value, e.g. 'keyCodes.K_A /* 0x41 */' or '65' -/// -function TCompileKeymanWeb.JavaScript_KeyAsString(fkp: PFILE_KEY; FMnemonic: Boolean): string; -begin - if FDebug - then Result := ' '+FormatKeyAsString(JavaScript_Key(fkp, FMnemonic)) - else Result := IntToStr(JavaScript_Key(fkp, FMnemonic)); -end; - -function TCompileKeymanWeb.JavaScript_Name(i: Integer; pwszName: PWideChar; KeepNameForPersistentStorage: Boolean): string; // I3659 -var - FChanged: Boolean; - p: PWideChar; -begin - FChanged := False; - p := pwszName; - if not Assigned(pwszName) or (pwszName^ = #0) or (not Self.FDebug and not KeepNameForPersistentStorage) then // I3659 // I3681 - Result := IntToStr(i) // for uniqueness - else - begin - if KeepNameForPersistentStorage // I3659 - then Result := '' // Potential for overlap in theory but in practice we only use this for named option stores so can never overlap - else Result := '_'; // Ensures we cannot overlap numbered instances - while pwszName^ <> #0 do - begin - if CharInSet(PChar(pwszName)^, SValidIdentifierCharSet) then // I3681 - begin - Result := Result + PChar(pwszName)^ - end - else - begin - Result := Result + '_'; - FChanged := True; - end; - Inc(pwszName); - end; - if not KeepNameForPersistentStorage then - begin - // Ensure each transformed name is still unique - Result := Result + '_' + IntToStr(i); - if FChanged then - Result := Result + '/*'+string(p).Replace('*/', '*-/')+'*/' - end - else if FChanged then - begin - // For named option stores, we are only supporting the valid identifier - // character set, which is a breaking change in 14.0. - ReportError(0, CWARN_OptionStoreNameInvalid, Format('The option store %s should be named with characters in the range A-Z, a-z, 0-9 and _ only.', - [string(p)])); - end; - end; -end; - -function TCompileKeymanWeb.JavaScript_OutputString(FTabstops: string; fkp: PFILE_KEY; pwszOutput: PWideChar; fgp: PFILE_GROUP): string; -var - i, n, len: Integer; - InQuotes: Boolean; - rec: TSentinelRecord; - pwszcontext,pwsz: PWideChar; - Index: Integer; // I3910 - nlt: string; - - function AdjustIndex(pwszContext: PWideChar; Index: Integer): Integer; // I3910 - var - recContext: TSentinelRecord; - I: Integer; - begin - Result := Index; - for I := 1 to Index-1 do - begin - recContext := ExpandSentinel(pwszContext); - - if IsKeyboardVersion10OrLater then - begin - if recContext.IsSentinel and (recContext.Code in [CODE_NUL, CODE_IFOPT, CODE_IFSYSTEMSTORE]) then - Dec(Result); - end - else - begin - if recContext.IsSentinel and (recContext.Code in [CODE_DEADKEY, CODE_NUL, CODE_IFOPT, CODE_IFSYSTEMSTORE]) then - Dec(Result); - end; - pwszContext := incxstr(pwszContext); - end; - end; - - function ContextChar(ContextIndex: Integer; pwszContext: PWideChar): string; // I4611 - var - Index: Integer; - recContext: TSentinelRecord; - begin - Result := ''; - recContext := ExpandSentinel(pwszContext); - if recContext.IsSentinel then - begin - if InQuotes then // I4611 - begin - Result := Result + '");'; - InQuotes := False; - end; - - case recContext.Code of - CODE_ANY: - begin - Index := AdjustIndex(fkp.dpContext, ContextIndex); // I3910 // I4611 - Result := Result + nlt + Format('k.KIO(%d,this.s%s,%d,t);', [len, JavaScript_Name(recContext.Any.StoreIndex, recContext.Any.Store.szName), Index]); // I4611 - end; - CODE_DEADKEY: - Result := Result + nlt + Format('k.KDO(%d,t,%d);', [len, recContext.Deadkey.Deadkey]); // I4611 - CODE_NOTANY: - begin - // #917: Minimum version required is 14.0: the KCXO function was only added for 14.0 - // Note that this is checked in compiler.cpp as well, so this error can probably never occur - if not IsKeyboardVersion14OrLater then - ReportError(fkp.Line, CERR_NotSupportedInKeymanWebContext, Format('Statement notany in context() match requires version 14.0+ of KeymanWeb', [GetCodeName(recContext.Code)])); // I1971 // I4061 - Result := Result + nlt + Format('k.KCXO(%d,t,%d,%d);', [len, AdjustIndex(fkp.dpContext, xstrlen(fkp.dpContext)), AdjustIndex(fkp.dpContext, ContextIndex)]); - end; - CODE_IFOPT, - CODE_IFSYSTEMSTORE, - CODE_NUL: - // These have no output for a context emit - ; - else - begin - ReportError(fkp.Line, CERR_NotSupportedInKeymanWebContext, Format('Statement %s is not currently supported in context() match', [GetCodeName(recContext.Code)])); // I1971 // I4061 - //CODE_NUL: ; // todo: check if context is longer than that... - Result := Result + nlt + '/*.*/ '; // I4611 - end; - end; - end - else - begin - if not InQuotes then - begin - Result := Result + nlt + Format('k.KO(%d,t,"', [len]); // I4611 - InQuotes := True; - end; - - if recContext.ChrVal in [Ord('"'), Ord('\')] then Result := Result + '\'; - Result := Result + Javascript_String(recContext.ChrVal); // I2242 - end; - end; - -begin - nlt := nl + FTabStops; // I3681 - Result := ''; - InQuotes := False; - - pwsz := pwszOutput; - - if Assigned(fkp) then - begin - if IsKeyboardVersion10OrLater - // KMW >=10.0 use the full, sentinel-based length for context deletions. - then - begin - len := xstrlen(fkp.dpContext); - n := len; - pwszContext := fkp.dpContext; - - for i := 1 to n do - begin - rec := ExpandSentinel(pwszContext); - if rec.IsSentinel and (rec.Code in [CODE_NUL, CODE_IFOPT, CODE_IFSYSTEMSTORE]) then - Dec(len); - pwszContext := incxstr(pwszContext); - end; - - end - // KMW < 10.0 exclude all sentinel-based characters, including deadkeys, from direct context deletion. - // Deadkeys have alternative special handling. - else len := xstrlen_printing(fkp.dpContext); - end - else - len := -1; - - if IsKeyboardVersion10OrLater() and (pwsz^ <> #0) then - begin - if not fgp.fReadOnly then - begin - Result := Result + nlt+Format('k.KDC(%d,t);', [ len ] ); // I3681 - end; - len := -1; - end; - - while pwsz^ <> #0 do - begin - rec := ExpandSentinel(pwsz); - if rec.IsSentinel then - begin - if InQuotes then - begin - if not fgp.fReadOnly then - Result := Result + '");'; - InQuotes := False; - end; - - case rec.Code of - CODE_CONTEXT: - begin - if (pwsz <> pwszOutput) or (len = -1) then - begin - pwszContext := fkp.dpContext; - n := 1; - while pwszContext^ <> '' do // I4611 - begin - if not fgp.fReadOnly then - begin - Result := Result + ContextChar(n, pwszContext); - end; - Inc(n); - pwszContext := incxstr(pwszContext); - end; - - //Result := Result + Format('k.KO(%d,t,k.KC(%d,%d,t));', [len, xstrlen_printing(fkp.dpContext), xstrlen_printing(fkp.dpContext)]); - end; - { else, we don't need to output anything - just don't delete the context } - len := -1; - end; - CODE_CONTEXTEX: - begin - pwszContext := fkp.dpContext; - for i := 1 to rec.ContextEx.Index - 1 do - begin - pwszContext := incxstr(pwszContext); - end; - - if not fgp.fReadOnly then - begin - Result := Result + ContextChar(rec.ContextEx.Index, pwszContext); // I4611 - end; - len := -1; - end; - CODE_BEEP: - begin - if not fgp.fReadOnly then - begin - if len > 0 then Result := Result + nlt+Format('k.KO(%d,t,"");', [len]); // I3681 - Result := Result + nlt+'k.KB(t);'; // I3681 - end; - len := -1; - end; - CODE_NUL: - begin - if not fgp.fReadOnly then - begin - if len > 0 then Result := Result + nlt+Format('k.KO(%d,t,"");', [len]); // I3681 - end; - len := -1; - end; - CODE_INDEX: - begin - CheckStoreForInvalidFunctions(fkp, rec.Index.Store); // I1520 - - // This code was wrong. We need to ignore CODE_NUL, CODE_DEADKEY in LHS context index counter. - // This is why the compiler goes wrong -- and why the previous fix was inconsistent. - // The I783 test did not test either of these cases. It seems some of the keyboards were - // compiled in-between the original fix and I783 re-fix, and then happened to work due to - // their simplicity. - - Index := AdjustIndex(fkp.dpContext, rec.Index.Index); // I3910 - - if not fgp.fReadOnly then - begin - Result := Result + nlt+Format('k.KIO(%d,this.s%s,%d,t);', [len, JavaScript_Name(rec.Index.StoreIndex, rec.Index.Store.szName), - // I783 - was: rec.Index.Index [2007-06-04] - // I783 again. Returned to rec.Index.Index. Was previously: [2008-08-15] - // xstrlen(fkp.dpContext) + 1 - rec.Index.Index]); - // this was wrong. Can't find any reason why this change was made - // which suggests it was in response to another bug and poorly traced (bad Marc) - // and not properly tested (bad, bad Marc). Anyway, now tested with test_i783 - Index]); // I3681 // I3910 - end; - len := -1; - end; - CODE_DEADKEY: - begin - if not fgp.fReadOnly then - begin - Result := Result + nlt+Format('k.KDO(%d,t,%d);', [len, rec.Deadkey.Deadkey]); // I3681 - end; - len := -1; - end; - CODE_USE: - begin - if not fgp.fReadOnly then - begin - if len > 0 then - Result := Result + nlt+Format('k.KO(%d,t,"");', [len]); // I3681 - end; - Result := Result + nlt+Format('r=this.g%s(t,e);', [JavaScript_Name(rec.Use.GroupIndex, rec.Use.Group.szName)]); // I1959 // I3681 - Result := Result + nlt+'m=2;'; // #5440 - match desktop behavior - len := -1; - end; - CODE_CALL: - begin - if not fgp.fReadOnly then - begin - if len > 0 then - Result := Result + nlt+Format('k.KO(%d,t,"");', [len]); // I3681 - end; - n := FCallFunctions.IndexOf(CallFunctionName(rec.Call.Store.dpString)); - if n = -1 then - n := FCallFunctions.Add(CallFunctionName(rec.Call.Store.dpString)); - Result := Result + nlt+Format('r=this.c%d(t,e);', [n]); // I1959 // I3681 - Result := Result + nlt+'m=2;'; // #5440 - match desktop behavior - len := -1; - end; - CODE_SETOPT: // I3429 - begin - if not fgp.fReadOnly then - begin - if len > 0 then - Result := Result + nlt+Format('k.KO(%d,t,"");', [len]); // I3681 - end; - Result := Result + nlt+Format('this.s%s=this.s%s;', - [JavaScript_Name(rec.SetOpt.StoreIndex1,rec.SetOpt.Store1.szName), - JavaScript_Name(rec.SetOpt.StoreIndex2,rec.SetOpt.Store2.szName)]); // I3429 // I3681 - len := -1; - end; - CODE_RESETOPT: // I3429 - begin - if not fgp.fReadOnly then - begin - if len > 0 then - Result := Result + nlt+Format('k.KO(%d,t,"");', [len]); // I3681 - end; - - Result := Result + nlt+Format('this.s%s=k.KLOAD(this.KI,"%s",%s);', - [JavaScript_Name(rec.ResetOpt.StoreIndex,rec.ResetOpt.Store.szName), - JavaScript_Name(rec.ResetOpt.StoreIndex,rec.ResetOpt.Store.szName,True), - JavaScript_Store(fkp.Line, rec.ResetOpt.Store.dpString)]); // I3429 // I3681 // I3659 - len := -1; - end; - CODE_SAVEOPT: // I3429 - begin - if not fgp.fReadOnly then - begin - if len > 0 then - Result := Result + nlt+Format('k.KO(%d,t,"");', [len]); - end; - Result := Result + nlt+Format('k.KSAVE("%s",this.s%s);', - [JavaScript_Name(rec.SaveOpt.StoreIndex,rec.SaveOpt.Store.szName,True), // I3690 - JavaScript_Name(rec.SaveOpt.StoreIndex,rec.SaveOpt.Store.szName)]); // I3429 // I3659 // I3681 - len := -1; - end; - CODE_SETSYSTEMSTORE: // I3437 - begin - if not fgp.fReadOnly then - begin - if len > 0 then - Result := Result + nlt+Format('k.KO(%d,t,"");', [len]); // I3681 - end; - Result := Result + nlt+Format('k.KSETS(%d,this.s%s,t);', // I3681 - [rec.SetSystemStore.dwSystemID, - JavaScript_Name(rec.SetSystemStore.StoreIndex, rec.SetSystemStore.Store.szName)]); - len := -1; - end; - else - begin - if Assigned(fkp) - then ReportError(fkp.Line, CERR_NotSupportedInKeymanWebOutput, Format('Statement %s is not currently supported in output', [GetCodeName(rec.Code)])) // I1971 // I4061 - else ReportError(0, CERR_NotSupportedInKeymanWebOutput, Format('Statement %s is not currently supported in output', [GetCodeName(rec.Code)])); // I1971 // I4061 - Result := Result + ''; - end; - end; - end - else - begin - if not InQuotes then - begin - if not fgp.fReadOnly then - begin - Result := Result + nlt+Format('k.KO(%d,t,"', [len]); // I3681 - end; - InQuotes := True; len := -1; - end; - - if not fgp.fReadOnly then - begin - if rec.ChrVal in [Ord('"'), Ord('\')] then Result := Result + '\'; - Result := Result + Javascript_String(rec.ChrVal); // I2242 - end; - end; - - pwsz := incxstr(pwsz); - end; - - if InQuotes then - begin - if not fgp.fReadOnly then - begin - Result := Result + '");'; - end; - end; -end; - -function TCompileKeymanWeb.JavaScript_Shift(fkp: PFILE_KEY; FMnemonic: Boolean): Integer; -begin - if FMnemonic then - begin - if (fkp.ShiftFlags and KMX_VIRTUALCHARKEY) = KMX_VIRTUALCHARKEY then - begin - ReportError(fkp.Line, CERR_VirtualCharacterKeysNotSupportedInKeymanWeb, 'Virtual character keys not currently supported in KeymanWeb'); // I1971 // I4061 - Exit(0); - end; - - if ((fkp.ShiftFlags and KMX_ISVIRTUALKEY) = KMX_ISVIRTUALKEY) and (Ord(fkp.Key) <= 255) then - begin - // We prohibit K_ keys for mnemonic layouts. We don't block T_ and U_ keys. - // TODO: this doesn't resolve the issue of, e.g. SHIFT+K_SPACE - // https://github.com/keymanapp/keyman/issues/265 - ReportError(fkp.Line, CERR_VirtualKeysNotValidForMnemonicLayouts, 'Virtual keys are not valid for mnemonic layouts'); // I1971 // I4061 - Exit(0); - end; - end; - - if (fkp.ShiftFlags and KMX_ISVIRTUALKEY) = KMX_ISVIRTUALKEY then - begin - if IsKeyboardVersion10OrLater then - begin - // Full chiral modifier and state key support starts with KeymanWeb 10.0 - Result := fkp.ShiftFlags; - end - else - begin - // Non-chiral support only and no support for state keys - if (fkp.ShiftFlags and ( - KMX_LCTRLFLAG or KMX_RCTRLFLAG or KMX_LALTFLAG or KMX_RALTFLAG)) <> 0 then // I4118 - begin - ReportError(fkp.Line, CWARN_ExtendedShiftFlagsNotSupportedInKeymanWeb, 'Extended shift flags LALT, RALT, LCTRL, RCTRL are not supported in KeymanWeb'); - end; - - if (fkp.ShiftFlags and ( - KMX_CAPITALFLAG or KMX_NOTCAPITALFLAG or KMX_NUMLOCKFLAG or KMX_NOTNUMLOCKFLAG or KMX_SCROLLFLAG or KMX_NOTSCROLLFLAG)) <> 0 then // I4118 - begin - ReportError(fkp.Line, CWARN_ExtendedShiftFlagsNotSupportedInKeymanWeb, 'Extended shift flags CAPS and NCAPS are not supported in KeymanWeb'); - end; - - Result := KMX_ISVIRTUALKEY or (Integer(fkp.ShiftFlags) and (KMX_SHIFTFLAG or KMX_CTRLFLAG or KMX_ALTFLAG)); - end; - end - else - begin - Result := KMX_ISVIRTUALKEY; - if Pos(fkp.Key, USEnglishShift) > 0 then - Result := Result or KMX_SHIFTFLAG; - end; -end; - -/// -/// Returns a Javascript representation of a key modifier state, either as a constant (debug mode) -/// or as an integer. -/// -/// @param fkp Pointer to key record -/// @param FMnemonic True if the keyboard is a mnemonic layout -/// -/// @return string representation of the key modifier state, e.g. -/// 'modCodes.SHIFT | modCodes.CAPS | modCodes.VIRTUAL_KEY /* 0x4110 */' or -/// '16656' -/// -function TCompileKeymanWeb.JavaScript_ShiftAsString(fkp: PFILE_KEY; FMnemonic: Boolean): string; -begin - if not FDebug - then Result := IntToStr(JavaScript_Shift(fkp, FMnemonic)) - else Result := ' '+FormatModifierAsBitflags(JavaScript_Shift(fkp, FMnemonic)); -end; - -function TCompileKeymanWeb.JavaScript_Store(line: Integer; pwsz: PWideChar): string; -var - ch: DWord; - n: Integer; - rec: TSentinelRecord; -const - wcsentinel: WideString = #$FFFF; -begin - n := Pos(wcsentinel, pwsz); - - // Start: plain text store. Always use for < 10.0, conditionally for >= 10.0. - if (n = 0) or not IsKeyboardVersion10OrLater then - begin - Result := '"'; - while pwsz^ <> #0 do - begin - if PWord(pwsz)^ = UC_SENTINEL then - begin - Result := Result + '.'; // UC_SENTINEL values are not supported in stores for KMW < 10.0. - end - else - begin - ch := GetSuppChar(pwsz); - if ch in [Ord('"'), Ord('\')] then Result := Result + '\'; - Result := Result + Javascript_String(ch); // I2242 - end; - - pwsz := incxstr(pwsz); - end; - Result := Result + '"'; - end - else - begin - Result := '['; - while pwsz^ <> #0 do - begin - if Result <> '[' then Result := Result + ','; - - rec := ExpandSentinel(pwsz); - if rec.IsSentinel then - begin - if rec.Code = CODE_DEADKEY then - begin - Result := Result + Format('{t:''d'',d:%d}', [rec.Deadkey.DeadKey]); - end - else if rec.Code = CODE_BEEP then - begin - Result := Result + '{t:''b''}' - end - else //if rec.Code = CODE_EXTENDED then - begin - // At some point, we may wish to filter which codes are safe to stub out like this - // versus which ones should be an error. The commented-out-code shows the way to - // handle such cases. - Result := Result + ''''''; - end -// else -// begin -// //ReportError(line, CERR_SomewhereIGotItWrong, 'Internal Error: unexpected sentinel character in store definition'); -// end; - end - else - begin - ch := GetSuppChar(pwsz); - Result := Result + '"'; - // TODO: Refactor the section below into JavaScript_String, as it's - // quite common in our code base. - if ch in [Ord('"'), Ord('\')] then Result := Result + '\'; - Result := Result + Javascript_String(ch) + '"'; // I2242 - end; - - pwsz := incxstr(pwsz); - end; - Result := Result + ']'; - end; -end; - -function TCompileKeymanWeb.JavaScript_String(ch: DWord): string; // I2242 -begin - if ch < 32 then - begin - case ch of - 9: Result := '\t'; - 10: Result := '\n'; - 13: Result := '\r'; - else Result := '\x'+IntToHex(ch,2); - end; - end - else - try - Result := Char.ConvertFromUtf32(ch); // I3310 - except - on EArgumentOutOfRangeException do - begin - // #6480 - // This happens when there is an unpaired surrogate. Not technically supported - // by KeymanWeb, warning is issued by kmx compiler, so we won't re-warn here. - Result := Char(ch); - end; - end; -end; - -procedure TCompileKeymanWeb.ReportError(line: Integer; msgcode: LongWord; const text: string); // I1971 -var - flag: LongWord; -begin - flag := CERR_FLAG or CFATAL_FLAG; - if FCompilerWarningsAsErrors then - flag := flag or CWARN_FLAG; - if (msgcode and flag) <> 0 then FError := True; - FCallback(line, msgcode, PWideChar(text)); // I3310 -end; - -function TCompileKeymanWeb.RequotedString(s: WideString; RequoteSingleQuotes: Boolean = False): string; -var - i: Integer; -begin - i := 1; - while i <= Length(s) do - begin - if (s[i] = '"') or (s[i] = '\') then begin s := Copy(s, 1, i-1)+'\'+Copy(s, i, Length(s)); Inc(i); end // I4368 - else if (s[i] = '''') and RequoteSingleQuotes then begin s := Copy(s, 1, i-1)+'\'+Copy(s, i, Length(s)); Inc(i); end - else if (s[i] = #13) then // I4368 - begin - s := Copy(s, 1, i-1) + '\n' + Copy(s,i+1, Length(s)); - Inc(i); - end - else if (s[i] = #10) then // I4368 - s[i] := ' '; - Inc(i); - end; - Result := s; -end; - -{** - Determine if a rule should be ignored by the KeymanWeb compiler because it is - targeted at a .kmx-based or KMFL platform with the platform() or if(&platform) - statement. - - Parameters: fkp Pointer to the rule - - Return Value: True if the rule should be excluded by the compiler -*} -function TCompileKeymanWeb.RuleIsExcludedByPlatform(fkp: PFILE_KEY): Boolean; -var - rec: TSentinelRecord; - pwsz: PChar; -begin - pwsz := fkp.dpContext; - if not Assigned(pwsz) then - Exit(False); - - while pwsz^ <> #0 do - begin - rec := ExpandSentinel(pwsz); - if rec.IsSentinel and - (rec.Code = CODE_IFSYSTEMSTORE) and - (rec.IfSystemStore.dwSystemID = TSS_PLATFORM) and - (Pos('native', rec.IfSystemStore.Store.dpString) > 0) then - begin - if (Pos('windows', rec.IfSystemStore.Store.dpString) > 0) or - (Pos('desktop', rec.IfSystemStore.Store.dpString) > 0) or - (Pos('macosx', rec.IfSystemStore.Store.dpString) > 0) or - (Pos('linux', rec.IfSystemStore.Store.dpString) > 0) then - Exit(True); - end; - pwsz := incxstr(pwsz); - end; - - Result := False; -end; - -function TCompileKeymanWeb.VisualKeyboardFromFile(const FVisualKeyboardFileName: string; var fDisplayUnderlying: Boolean): WideString; - - function WideQuote(s: WideString): WideString; - var - i: Integer; - begin - Result := ''; - for i := 1 to Length(s) do - if (s[i] = '"') or (s[i] = '\') then Result := Result + '\'+s[i] else Result := Result + s[i]; - end; - - function VKShiftToLayerName(Shift: Integer): string; - const - masks: array[0..6] of string = ( - 'leftctrl', - 'rightctrl', - 'leftalt', - 'rightalt', - 'shift', - 'ctrl', - 'alt' - ); - var - i: Integer; - begin - shift := VkShiftStateToKmxShiftState(shift); - if shift = 0 then - Result := 'default' - else - begin - Result := ''; - for i := 0 to 6 do - if shift and (1 shl i) <> 0 then - Result := Result + masks[i] + '-'; - Delete(Result, Length(Result), 1); - end; - end; - - function VisualKeyboardToKLS(FVK: TVisualKeyboard): string; - type - TLayer = record - Shift: Integer; - Name: string; - Keys: array[0..64] of string; - end; - var - i: Integer; - layers: array of TLayer; // TDictionary may be faster but not worth the extra dev cost - n, j: Integer; - Found: Boolean; - begin - // Discover the layers used in the visual keyboard - - for i := 0 to FVK.Keys.Count - 1 do - begin - if kvkkUnicode in FVK.Keys[i].Flags then - begin - // Find the index of the key in KMW VK arrays - n := CKeymanWebKeyCodes[FVK.Keys[i].VKey]; - if n = $FF then Continue; - - Found := False; - for j := 0 to High(layers) do - if layers[j].Shift = FVK.Keys[i].Shift then - begin - Found := True; - layers[j].Keys[n] := FVK.Keys[i].Text; - Break; - end; - - if not Found then - begin - SetLength(layers, Length(layers)+1); - layers[High(Layers)].Shift := FVK.Keys[i].Shift; - layers[High(Layers)].Keys[n] := FVK.Keys[i].Text; - end; - end; - end; - - // Build the layer array - - Result := nl+FTabStop+'this.KV.KLS={'+nl; - - for i := 0 to High(layers) do - begin - Result := Result + Format('%s%s"%s": [', [FTabStop,FTabStop,VKShiftToLayerName(layers[i].Shift)]); - for j := 0 to High(layers[i].Keys)-1 do - begin - Result := Result + '"'+WideQuote(layers[i].Keys[j])+'",'; - end; - Result := Result + '"'+WideQuote(layers[i].Keys[High(layers[i].Keys)])+'"]'; - if i < High(Layers) then - Result := Result + ',' + nl; - end; - Result := Result + nl+FTabStop+'}'; - end; - - function BuildBKFromKLS: string; - const func = - 'function(x){var e=Array.apply(null,Array(65)).map(String.prototype.valueOf,"")'+ - ',r=[],v,i,m=[''default'',''shift'',''ctrl'',''shift-ctrl'',''alt'',''shift-alt'','+ - '''ctrl-alt'',''shift-ctrl-alt''];for(i=m.length-1;i>=0;i--)if((v=x[m[i]])||r.length)'+ - 'r=(v?v:e).slice().concat(r);return r}'; - func_debug = - 'function(x){'#13#10+ - ' var'#13#10+ - ' empty=Array.apply(null, Array(65)).map(String.prototype.valueOf,""),'#13#10+ - ' result=[], v, i,'#13#10+ - ' modifiers=[''default'',''shift'',''ctrl'',''shift-ctrl'',''alt'',''shift-alt'',''ctrl-alt'',''shift-ctrl-alt''];'#13#10+ - ' for(i=modifiers.length-1;i>=0;i--) {'#13#10+ - ' v = x[modifiers[i]];'#13#10+ - ' if(v || result.length > 0) {'#13#10+ - ' result=(v ? v : empty).slice().concat(result);'#13#10+ - ' }'#13#10+ - ' }'#13#10+ - ' return result;'#13#10+ - ' }'; - begin - Result := nl+FTabStop+'this.KV.BK=('+IfThen(FDebug,func_debug,func)+')(this.KV.KLS)'; - end; - -var - FVK: TVisualKeyboard; - f102, fbold, fitalic: string; -begin - Result := ''; - FVK := TVisualKeyboard.Create; - try - FVK.LoadFromFile(FVisualKeyboardFileName); - if fsBold in FVK.Header.UnicodeFont.Style then fbold := 'bold ' else fbold := ''; - if fsItalic in FVK.Header.UnicodeFont.Style then fitalic := 'italic ' else fitalic := ''; - if kvkh102 in FVK.Header.Flags then f102 := '1' else f102 := '0'; - fDisplayUnderlying := kvkhDisplayUnderlying in FVK.Header.Flags; - - Result := Format('{F:''%s%s 1em "%s"'',K102:%s}', [fitalic, fbold, RequotedString(FVK.Header.UnicodeFont.Name, True), f102]); // I3886 // I3956 - Result := Result + ';'+VisualKeyboardToKLS(FVK); - Result := Result + ';'+BuildBKFromKLS; - finally - FVK.Free; - end; -end; - -type - TRequiredKey = (K_LOPT, K_BKSP, K_ENTER); // I4447 - TRequiredKeys = set of TRequiredKey; - -function RequiredKeysToString(keys: TRequiredKeys): string; // I4060 -var - k: TRequiredKey; -begin - Result := ''; - for k in keys do - Result := Result + ', ' + GetEnumName(TypeInfo(TRequiredKey), Ord(k)); - Delete(Result, 1, 2); -end; - -function TCompileKeymanWeb.ValidateLayoutFile(var sLayoutFile: string; const sVKDictionary: string): Boolean; // I4060 // I4139 -type - TKeyIdType = (Key_Invalid, Key_Constant, Key_Touch, Key_Unicode, Key_Unicode_Multi); // I4142 -const - CRequiredKeys: TRequiredKeys = [K_LOPT, K_BKSP, K_ENTER]; // I4447 - - // See also builder.js: specialCharacters; web/source/osk/oskKey.ts: specialCharacters - CSpecialText10: string = - '*Shift*'#0'*Enter*'#0'*Tab*'#0'*BkSp*'#0'*Menu*'#0'*Hide*'#0'*Alt*'#0'*Ctrl*'#0'*Caps*'#0+ - '*ABC*'#0'*abc*'#0'*123*'#0'*Symbol*'#0'*Currency*'#0'*Shifted*'#0'*AltGr*'#0'*TabLeft*'; - - // these names were added in Keyman 14 - CSpecialText14: string = - '*LTREnter*'#0'*LTRBkSp*'#0'*RTLEnter*'#0'*RTLBkSp*'#0'*ShiftLock*'#0'*ShiftedLock*'#0'*ZWNJ*'#0'*ZWNJiOS*'#0'*ZWNJAndroid*'; - CSpecialText14ZWNJ: string = - '*ZWNJ*'#0'*ZWNJiOS*'#0'*ZWNJAndroid*'; - - CSpecialText14Map: array[0..8,0..1] of string = ( - ('*LTREnter*', '*Enter*'), - ('*LTRBkSp*', '*BkSp*'), - ('*RTLEnter*', '*Enter*'), - ('*RTLBkSp*', '*BkSp*'), - ('*ShiftLock*', '*Shift*'), - ('*ShiftedLock*', '*Shifted*'), - ('*ZWNJ*', '<|>'), - ('*ZWNJiOS*', '<|>'), - ('*ZWNJAndroid*', '<|>') - ); - -var - FPlatform: TTouchLayoutPlatform; - FLayer: TTouchLayoutLayer; - FRow: TTouchLayoutRow; - FKey: TTouchLayoutKey; - FSubKey: TTouchLayoutSubKey; - FRequiredKeys: set of TRequiredKey; - FDictionary: TStringList; - FDirection: TTouchLayoutFlickDirection; - - function IsValidUnicodeValue(ch: Integer): Boolean; // I4198 - begin - Result := - ((ch >= $0020) and (ch <= $7F)) or - ((ch >= $00A0) and (ch <= $10FFFF)); - end; - - function GetKeyIdUnicodeType(const value: string): TKeyIdType; - var - v: string; - values: TArray; - begin - values := value.Split(['_']); - for v in values do - if not IsValidUnicodeValue(StrToIntDef('$'+v, 0)) then - Exit(Key_Invalid); - if Length(values) > 1 then - Exit(Key_Unicode_Multi); - Result := Key_Unicode; - end; - - function KeyIdType(const FId: string): TKeyIdType; // I4142 - begin - Result := Key_Invalid; - case UpCase(FId[1]) of - 'T': Result := Key_Touch; - 'U': if (Copy(FId, 1, 2) = 'U_') then Result := GetKeyIdUnicodeType(FId.Substring(2)); - else if FindVKeyName(FId) <> $FFFF then Result := Key_Constant; - end; - end; - - procedure CheckKey(const FId, FText, FNextLayer: string; FKeyType: TTouchKeyType); // I4119 - var - FValid: TKeyIdType; - v: Integer; - begin - // - // Check that each touch layer has K_LOPT, K_ROPT, K_BKSP, K_ENTER - // - - v := GetEnumValue(TypeInfo(TRequiredKey), FId); - if v >= 0 then Include(FRequiredKeys, TRequiredKey(v)); - - // - // Check that each layer referenced exists - // - - if FNextLayer <> '' then - begin - if FPlatform.Layers.IndexOfId(FNextLayer) < 0 then - ReportError(0, CWARN_TouchLayoutMissingLayer, 'Key "'+FId+'" on platform "'+FPlatform.Name+'", layer "'+FLayer.Id+'", platform "'+FPlatform.Name+'", references a missing layer "'+FNextLayer+'".'); - end; - - // - // Check that the key has a valid id // I4142 - // - - if Trim(FId) = '' then - begin - if not (FKeyType in [tktBlank, tktSpacer]) and (FNextLayer = '') then - ReportError(0, CWARN_TouchLayoutUnidentifiedKey, 'A key on layer "'+FLayer.Id+'" has no identifier.'); - Exit; - end; - - FValid := KeyIdType(FId); - - if FValid = Key_Invalid then - begin - ReportError(0, CERR_TouchLayoutInvalidIdentifier, 'Key "'+FId+'" on "'+FPlatform.Name+'", layer "'+FLayer.Id+'" has an invalid identifier.'); - end - else if (FValid = Key_Unicode_Multi) and not IsKeyboardVersion15OrLater then - begin - ReportError(0, CERR_TouchLayoutInvalidIdentifier, 'Key "'+FId+'" on "'+FPlatform.Name+'", layer "'+FLayer.Id+'" has a multi-part identifier which requires version 15.0 or newer.'); - end; - // - // Check that each custom key code has at least *a* rule associated with it - // - - if (FValid = Key_Touch) and (FNextLayer = '') and (FKeyType in [tktNormal, tktDeadKey]) then // I4119 - begin - // Search for the key in the key dictionary - ignore K_LOPT, K_ROPT... - if FDictionary.IndexOf(FId) < 0 then - ReportError(0, CWARN_TouchLayoutCustomKeyNotDefined, 'Key "'+FId+'" on layer "'+FLayer.Id+'", platform "'+FPlatform.Name+'", is a custom key but has no corresponding rule in the source.'); - end; - - // - // Check that if the key has a *special* label, it is available in the target version - // - if FText.StartsWith('*') and FText.EndsWith('*') and (FText.Length > 2) then - begin - // Keyman versions before 14 do not support '*special*' labels on non-special keys. - // ZWNJ use, however, is safe because it will be transformed in function - // TransformSpecialKeys14 to '<|>', which does not require the custom OSK font. - if (CSpecialText10.Contains(FText) or CSpecialText14.Contains(FText)) and - not CSpecialText14ZWNJ.Contains(FText) and - not IsKeyboardVersion14OrLater and - not (FKeyType in [tktSpecial, tktSpecialActive]) then - begin - ReportError(0, CWARN_TouchLayoutSpecialLabelOnNormalKey, - Format('Key "%s" on layout "%s", platform "%s" does not have the key type "Special" or "Special (active)" but has the label "%s". This feature is only supported in Keyman 14 or later', [ - FId, FLayer.Id, FPlatform.Name, FText - ])); - end; - end; - end; - - procedure CheckDictionaryKeyValidity; // I4142 - var - gp, kp: Cardinal; - i: Integer; - fgp: PFILE_GROUP; - fkp: PFILE_KEY; - begin - for i := 0 to FDictionary.Count - 1 do - begin - if FDictionary[i] = '' then - Continue; - if KeyIdType(FDictionary[i]) in [Key_Invalid, Key_Constant] then - begin - gp := 0; - fgp := fk.dpGroupArray; - while gp < fk.cxGroupArray do - begin - if fgp.fUsingKeys then - begin - kp := 0; - fkp := fgp.dpKeyArray; - while kp < fgp.cxKeyArray do - begin - if JavaScript_Key(fkp, fMnemonic) = i+256 then - begin - ReportError(fkp.Line, CERR_InvalidKeyCode, 'Invalid key identifier "'+FDictionary[i]+'"'); - end; - Inc(kp); - Inc(fkp); - end; - end; - Inc(gp); - Inc(fgp); - end; - end; - end; - end; - - procedure TransformSpecialKeys14(var sLayoutFile: string); - var - i: Integer; - begin - // Rewrite Special key labels that are only supported in Keyman 14+ - // This code is a little ugly but effective. - if not IsKeyboardVersion14OrLater then - begin - for i := 0 to High(CSpecialText14Map) do - begin - // Assumes the JSON output format will not change - if FDebug - then sLayoutFile := ReplaceStr(sLayoutFile, '"text": "'+CSpecialText14Map[i][0]+'"', '"text": this._v>13 ? "'+CSpecialText14Map[i][0]+'" : "'+CSpecialText14Map[i][1]+'"') - else sLayoutFile := ReplaceStr(sLayoutFile, '"text":"'+CSpecialText14Map[i][0]+'"', '"text":this._v>13?"'+CSpecialText14Map[i][0]+'":"'+CSpecialText14Map[i][1]+'"'); - end; - end; - end; -begin - FDictionary := TStringList.Create; - try - FDictionary.Delimiter := ' '; - FDictionary.DelimitedText := sVKDictionary; - - CheckDictionaryKeyValidity; // I4142 - - with TTouchLayout.Create do - try - OnMessage := - procedure(Sender: TObject; const sMsg:string) - begin - ReportError(0, CERR_InvalidTouchLayoutFile, sMsg); - end; - - if not Load(sLayoutFile) then - Exit(False); - - FTouchLayoutFont := ''; // I4872 - - for FPlatform in Data.Platforms do - begin - // Test that the font matches on all platforms // I4872 - - if FTouchLayoutFont = '' then - FTouchLayoutFont := FPlatform.Font - else if not SameText(FPlatform.Font, FTouchLayoutFont) then - begin - ReportError(0, CWARN_TouchLayoutFontShouldBeSameForAllPlatforms, 'The touch layout font should be the same for all platforms.'); - end; - - // Test that all required keys are present - for FLayer in FPlatform.Layers do - begin - FRequiredKeys := []; - for FRow in FLayer.Rows do - for FKey in FRow.Keys do - begin - CheckKey(FKey.Id, FKey.Text, FKey.NextLayer, FKey.SpT); // I4119 - for FSubKey in FKey.Sk do - CheckKey(FSubKey.Id, FKey.Text, FSubKey.NextLayer, FSubKey.SpT); // I4119 - for FDirection in FKey.Flick.Keys do - CheckKey(FKey.Flick[FDirection].Id, FKey.Flick[FDirection].Text, - FKey.Flick[FDirection].NextLayer, FKey.Flick[FDirection].SpT); - for FSubKey in FKey.MultiTap do - CheckKey(FSubKey.Id, FKey.Text, FSubKey.NextLayer, FSubKey.SpT); - end; - - if FRequiredKeys <> CRequiredKeys then - ReportError(0, CWARN_TouchLayoutMissingRequiredKeys, 'Layer "'+FLayer.Id+'" on platform "'+FPlatform.Name+'" is missing the required key(s) '+RequiredKeysToString(CRequiredKeys-FRequiredKeys)+'.'); - end; - end; - - // If not debugging, then this strips out formatting for a big saving in file size - // This also normalises any values such as Pad or Width which should be strings - sLayoutFile := Write(FDebug); - - TransformSpecialKeys14(sLayoutFile); - finally - Free; - end; - finally - FDictionary.Free; - end; - - Result := True; -end; - -function TCompileKeymanWeb.WriteBeginStatement(const name: string; groupIndex: Integer): string; -var - fgp: PFILE_GROUP; -begin - fgp := fk.dpGroupArray; Inc(fgp, groupIndex); - Result := Format( - '%sthis.%s=function(t,e) {%s'+ - '%sreturn this.g%s(t,e);%s'+ - '%s};%s', - [FTabStop, name, nl, - FTabStop+FTabStop, JavaScript_Name(groupIndex, fgp.szName), nl, - FTabStop, nl]); // I3681 -end; - -//{$WARNINGS OFF} // bug in Delphi compiler returning W1035 return value undefined?!? -function TCompileKeymanWeb.WriteCompiledKeyboard: string; {UTF8} - function Requote(const S: string): string; - var - I: Integer; - begin - Result := S; - for I := Length(Result) downto 1 do - if CharInSet(Result[I], ['''', '\']) then Insert('\', Result, I); - Result := '''' + Result + ''''; - end; -var - fgp: PFILE_GROUP; - fsp: PFILE_STORE; - fkp: PFILE_KEY; - - i, j, n: Integer; // I1964 - crash with empty group - - vMnemonic: Integer; - s, sRTL, sHelp, sHelpFile, sName, sEmbedJS, sEmbedCSS: string; - sVisualKeyboard, sFullName: WideString; - sBegin_NewContext, sBegin_PostKeystroke: string; - sLayoutFile, sVKDictionary: string; - linecomment: string; // I3438 - HasRules: Boolean; - sModifierBitmask: string; - fDisplayUnderlying: Boolean; - FOptionStores: string; - rec: TSentinelRecord; -begin - Result := '';//UTF16SignatureW; // + '// compiled by Keyman Developer'+nl; // I3474 - { Locate the name of the keyboard } - fsp := fk.dpStoreArray; - sHelp := ''''''; - sFullName := ''; - sHelpFile := ''; - sEmbedJS := ''; - sEmbedCSS := ''; // I4368 - sVisualKeyboard := ''; - sLayoutFile := ''; // I3483 - FKeyboardVersion := '1.0'; // I4155 - sRTL := ''; - vMnemonic := 0; - for i := 0 to fk.cxStoreArray - 1 do - begin - if fsp.dwSystemID = TSS_NAME then - sFullName := fsp.dpString - else if fsp.dwSystemID = TSS_KEYBOARDVERSION then // I4155 - FKeyboardVersion := fsp.dpString // I4155 - else if (fsp.szName = 'HelpFile') or (fsp.dwSystemID = TSS_KMW_HELPFILE) then - sHelpFile := fsp.dpString - else if (fsp.szName = 'Help') or (fsp.dwSystemID = TSS_KMW_HELPTEXT) then - sHelp := '"'+RequotedString(fsp.dpString)+'"' - else if (fsp.szName = 'VisualKeyboard') or (fsp.dwSystemID = TSS_VISUALKEYBOARD) then - sVisualKeyboard := fsp.dpString - else if (fsp.szName = 'EmbedJS') or (fsp.dwSystemID = TSS_KMW_EMBEDJS) then - sEmbedJS := fsp.dpString - else if (fsp.szName = 'EmbedCSS') or (fsp.dwSystemID = TSS_KMW_EMBEDCSS) then // I4368 - sEmbedCSS := fsp.dpString - else if (fsp.szName = 'RTL') or (fsp.dwSystemID = TSS_KMW_RTL) then - if AnsiCompareText(fsp.dpString, '1') = 0 then sRTL := FTabStop+'this.KRTL=1;'+nl else sRTL := '' // I3681 - else if fsp.dwSystemID = TSS_MNEMONIC then - if AnsiCompareText(fsp.dpString, '1') = 0 then vMnemonic := 1 else vMnemonic := 0 - else if fsp.dwSystemID = TSS_VKDICTIONARY then // I3438 - sVKDictionary := fsp.dpString - else if fsp.dwSystemID = TSS_LAYOUTFILE then // I3483 - sLayoutFile := fsp.dpString - else if fsp.dwSystemID = TSS_BEGIN_NEWCONTEXT then - sBegin_NewContext := fsp.dpString - else if fsp.dwSystemID = TSS_BEGIN_POSTKEYSTROKE then - sBegin_PostKeystroke := fsp.dpString; - Inc(fsp); - end; - - sName := 'Keyboard_'+TKeyboardUtils.GetKeymanWebCompiledNameFromFileName(FInFile); - - if sHelpFile <> '' then - begin - sHelp := ''; - with TStringList.Create do - try - try - LoadFromFile(ExtractFilePath(FInFile) + sHelpFile, TEncoding.UTF8); // I3337 - for n := 0 to Count - 1 do - sHelp := sHelp + Strings[n] + ' '; - except - on E:EFOpenError do - begin - ReportError(0, CWARN_HelpFileMissing, E.Message); // I1971 // I4061 - sHelp := ''; - end; - end; - finally - Free; - end; - - sHelp := Requote(sHelp); - end; - - if sEmbedJS <> '' then - begin - try - with TStringList.Create do - try - LoadFromFile(ExtractFilePath(FInFile) + sEmbedJS, TEncoding.UTF8); // I3337 - sEmbedJS := Text; - finally - Free; - end; - except - on E:EFOpenError do // I3683 - begin - ReportError(0, CWARN_EmbedJsFileMissing, E.Message); // I4061 - sEmbedJS := ''; - end; - end; - end; - - if sEmbedCSS <> '' then // I4368 - begin - try - with TStringList.Create do - try - LoadFromFile(ExtractFilePath(FInFile) + sEmbedCSS, TEncoding.UTF8); // I3337 - sEmbedCSS := Text; - finally - Free; - end; - except - on E:EFOpenError do // I3683 - begin - ReportError(0, CWARN_EmbedJsFileMissing, E.Message); // I4061 - sEmbedCSS := ''; - end; - end; - end; - - if sLayoutFile <> '' then // I3483 - begin - try - with TStringList.Create do - try - LoadFromFile(ExtractFilePath(FInFile) + sLayoutFile, TEncoding.UTF8); - sLayoutFile := Text; - if not ValidateLayoutFile(sLayoutFile, sVKDictionary) then // I4060 - begin - sLayoutFile := ''; - end; - finally - Free; - end; - except - on E:EFOpenError do // I3683 - begin - ReportError(0, CWARN_TouchLayoutFileMissing, E.Message); // I4061 - sLayoutFile := ''; - end; - end; - end; - - // Default to hide underlying layout characters. This is overridden by touch - // layout platform.displayUnderlying property or, if that is not present for - // the given platform, by the OSK property. - fDisplayUnderlying := False; - - if sVisualKeyboard <> '' then - begin - try - // The Keyman .kmx compiler will change the value of this store from a - // .kvks to a .kvk during the build. Earlier in the build, the visual keyboard - // would have been compiled, so we need to account for that and use that file. - - sVisualKeyboard := VisualKeyboardFromFile(ExtractFilePath(FOutFile) + sVisualKeyboard, fDisplayUnderlying); - except - on E:EFOpenError do // I3947 - begin - ReportError(0, CWARN_VisualKeyboardFileMissing, E.Message); // I4061 - sVisualKeyboard := 'null'; - end; - end; - - { viskbd = array(...); viskbdfont = ... - sVisualKeyboard := 'null'; } - end - else - sVisualKeyboard := 'null'; - - sModifierBitmask := GetKeyboardModifierBitmask; - - fMnemonic := vMnemonic = 1; - - Result := Result + Format( - '%s%s'+ - 'KeymanWeb.KR(new %s());%s'+ - '%s%s'+ - 'function %s()%s'+ - '{%s'+ - '%s%s%s'+ - // Following line caches the Keyman major version - '%sthis._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;%s'+ - '%sthis.KI="%s";%s'+ - '%sthis.KN="%s";%s'+ - '%sthis.KMINVER="%d.%d";%s'+ - '%sthis.KV=%s;%s'+ - '%sthis.KDU=%s;%s'+ - '%sthis.KH=%s;%s'+ - '%sthis.KM=%d;%s'+ - '%sthis.KBVER="%s";%s'+ // I4155 - '%sthis.KMBM=%s;%s'+ - '%s', - [ - JavaScript_SetupProlog, nl, - sName, nl, - JavaScript_SetupEpilog, nl, - sName, nl, - nl, - FTabStop, JavaScript_SetupDebug, nl, - FTabStop, nl, - FTabStop, sName, nl, - FTabStop, RequotedString(sFullName), nl, - FTabStop, (fk.version and VERSION_MASK_MAJOR) shr 8, fk.version and VERSION_MASK_MINOR, nl, - FTabStop, sVisualKeyboard, nl, - FTabStop, IfThen(fDisplayUnderlying, '1', '0'), nl, - FTabStop, sHelp, nl, - FTabStop, vMnemonic, nl, - FTabStop, FKeyboardVersion, nl, // I4155 - FTabStop, sModifierBitmask, nl, - sRTL]); // I3681 - - if HasSupplementaryPlaneChars then - Result := Result + Format('%sthis.KS=1;%s', [FTabStop, nl]); // I3317 - - if sVKDictionary <> '' then // I3438 - Result := Result + Format('%sthis.KVKD="%s";%s', [FTabStop, RequotedString(sVKDictionary), nl]); // I3681 - - if sLayoutFile <> '' then // I3483 - begin - Result := Result + Format('%sthis.KVKL=%s;%s', [FTabStop, sLayoutFile, nl]); // I3681 - end; - - if sEmbedCSS <> '' then // I4368 - Result := Result + Format('%sthis.KCSS="%s";%s', [FTabStop, RequotedString(sEmbedCSS), nl]); - - { Write the stores out } - FOptionStores := ''; - fsp := fk.dpStoreArray; - for i := 0 to fk.cxStoreArray - 1 do - begin - // I3438 - Save all system stores to the keyboard, for now // I3684 - - if not fsp.fIsDebug then // and not (fsp.dwSystemID in [TSS_BITMAP, TSS_NAME, TSS_VERSION, TSS_CUSTOMKEYMANEDITION, TSS_CUSTOMKEYMANEDITIONNAME, TSS_KEYMANCOPYRIGHT]) then - begin - if fsp.dwSystemID = TSS_COMPARISON then - Result := Result + Format('%sthis.s%s=%s;%s', [FTabStop, JavaScript_Name(i, fsp.szName), JavaScript_Store(fsp.line, fsp.dpString), nl]) - else if fsp.dwSystemID = TSS_COMPILEDVERSION then - Result := Result + Format('%sthis.KVER=%s;%s', [FTabStop, JavaScript_Store(fsp.line, fsp.dpString), nl]) - //else if fsp.dwSystemID = TSS_VKDICTIONARY then // I3438, required for vkdictionary - // Result := Result + Format('%sthis.s%s=%s;%s', [FTabStop, JavaScript_Name(i, fsp.szName), JavaScript_Store(fsp.line, fsp.dpString), nl]) - else if fsp.fIsOption and not fsp.fIsReserved then - begin - Result := Result + Format('%sthis.s%s=KeymanWeb.KLOAD(this.KI,"%s",%s);%s', - [FTabstop, - JavaScript_Name(i,fsp.szName), - JavaScript_Name(i,fsp.szName,True), - JavaScript_Store(fsp.line, fsp.dpString), - nl]); // I3429 - - if FOptionStores <> '' then - FOptionStores := FOptionStores + ','; - FOptionStores := FOptionStores + Format('''s%s''', [JavaScript_Name(i,fsp.szName)]); - end - else if fsp.dwSystemID = TSS_NONE {aka not fsp.fIsReserved} then - Result := Result + Format('%sthis.s%s=%s;%s', [FTabStop, JavaScript_Name(i, fsp.szName), JavaScript_Store(fsp.line, fsp.dpString), nl]); // I3681 - end; - Inc(fsp); - end; - - Result := Result + Format('%sthis.KVS=[%s];%s', [FTabStop, FOptionStores, nl]); - - { Write the groups out } - - // I853 - begin unicode missing causes crash - if fk.StartGroup[BEGIN_UNICODE] = $FFFFFFFF then - begin - ReportError(0, CERR_InvalidBegin, 'A "begin unicode" statement is required to compile a KeymanWeb keyboard'); - Exit; - end; - - Result := Result + WriteBeginStatement('gs', fk.StartGroup[BEGIN_UNICODE]); - rec := ExpandSentinel(PChar(sBegin_NewContext)); - if rec.Code = CODE_USE then - Result := Result + WriteBeginStatement('gn', rec.Use.GroupIndex); - rec := ExpandSentinel(PChar(sBegin_PostKeystroke)); - if rec.Code = CODE_USE then - Result := Result + WriteBeginStatement('gpk', rec.Use.GroupIndex); - - fgp := fk.dpGroupArray; Inc(fgp, fk.StartGroup[BEGIN_UNICODE]); - Result := Result + Format( - '%sthis.gs=function(t,e) {%s'+ - '%sreturn this.g%s(t,e);%s'+ - '%s};%s', - [FTabStop, nl, - FTabStop+FTabStop, JavaScript_Name(fk.StartGroup[BEGIN_UNICODE], fgp.szName), nl, - FTabStop, nl]); // I3681 - - fgp := fk.dpGroupArray; - for i := 0 to Integer(fk.cxGroupArray)-1 do // I1964 - begin - { - Note on `r` and `m` variables in a group function: - - `m` can have one of three values: - 0: no rule from this group was matched - 1: a rule from this group was matched and did not include a `use` - statement - 2: a rule from this group matched and did include a `use` statement - (#5440) - - `m` is only used within a rule group to control the firing of the - `match` and `nomatch` rules. - - `r` can have one of two values: - 0: no rule from the final group matched (even if a rule from an - higher-level group did) - 1: a rule from the final group did match; - - `r` serves as the rule group's return value and is forwarded - recursively, best serving as a flag for whether or not default - output for a key should be emitted (0 means yes, emit the - default character output for that key). - } - - Result := Result + Format( - '%sthis.g%s=function(t,e) {%s'+ - '%svar k=KeymanWeb,r=%d,m=0;%s', //I1959 - [FTabstop, JavaScript_Name(i, fgp.szName), nl, - FTabstop+FTabstop, IfThen(fgp.fUsingKeys,0,1), nl]); // I3681 - - fkp := fgp.dpKeyArray; - HasRules := False; - - if FFix183_LadderLength <> 0 then - begin - Result := Result + JavaScript_Rules(fgp); - end - else - begin - for j := 0 to Integer(fgp.cxKeyArray) - 1 do // I1964 - begin - if not RuleIsExcludedByPlatform(fkp) then - begin - Result := Result + FTabstop+FTabstop; // I3681 - if HasRules then Result := Result + 'else '; - HasRules := TRue; - if fgp.fUsingKeys then - begin - Result := Result + Format('if(k.KKM(e,%s,%s)', - [JavaScript_ShiftAsString(fkp, fMnemonic), - JavaScript_KeyAsString(fkp, fMnemonic)]); - end; - - if xstrlen(fkp.dpContext) > 0 then - begin - if not fgp.fUsingKeys - then Result := Result + 'if(' - else Result := Result + '&&'; - - Result := Result + JavaScript_ContextMatch(fkp, fkp.dpContext); - end - else if not fgp.fUsingKeys then - Result := Result + 'if(1'; - - if (fkp.Line > 0) and FDebug // I4384 - then linecomment := Format(' // Line %d', [fkp.Line]) // I4373 - else linecomment := ''; - - Result := Result + Format( - ') {%s%s'+ - '%s', - [linecomment, nl, - FTabstop+FTabstop+FTabstop]); // I3681 - if(fgp.fUsingKeys) // I1959 - then Result := Result + Format('r=m=1;%s', [JavaScript_OutputString(FTabStop + FTabStop + FTabStop, fkp, fkp.dpOutput, fgp)]) // I1959 // I3681 - else Result := Result + Format('m=1;%s', [JavaScript_OutputString(FTabStop + FTabStop + FTabStop, fkp, fkp.dpOutput, fgp)]); // I1959 // I3681 - Result := Result + Format('%s%s}%s', [nl, FTabstop+FTabstop, nl]); // I3681 - end; - Inc(fkp); - end; - end; - - if Assigned(fgp.dpMatch) then - Result := Result + Format( - '%sif(m==1) {%s'+ - '%s%s%s'+ - '%s}%s', - [FTabstop+FTabstop, nl, - FTabstop+Ftabstop, JavaScript_OutputString(FTabStop + FTabStop + FTabStop, nil, fgp.dpMatch, fgp), nl, - FTabstop+FTabstop, nl]); // I3681 - if Assigned(fgp.dpNoMatch) then - if fgp.fUsingKeys then // I1382 - fixup m=1 to m=g() - Result := Result + Format( - '%sif(!m&&k.KIK(e)) {%s'+ - '%sr=1;%s%s'+ - '%s}%s', - [FTabstop+FTabstop, nl, - FTabstop+FTabstop+FTabstop, JavaScript_OutputString(FTabStop + FTabStop + FTabStop, nil, fgp.dpNoMatch, fgp), nl, - FTabstop+FTabstop, nl]) // I1959. part 2, I2224 // I3681 - else - Result := Result + Format( - '%sif(!m) {%s'+ - '%s%s%s'+ - '%s}%s', - [FTabstop+FTabstop, nl, - FTabstop+FTabstop, JavaScript_OutputString(FTabStop + FTabStop + FTabStop, nil, fgp.dpNoMatch, fgp), nl, - FTabstop+FTabstop, nl]); // I1959 // I3681 - - Result := Result + Format('%sreturn r;%s'+ - '%s};%s', - [FTabstop+FTabstop, nl, - FTabstop, nl]); // I1959 // I3681 - Inc(fgp); - end; - - for n := 0 to FCallFunctions.Count - 1 do - begin - s := ExtractFilePath(FInFile) + FCallFunctions[n] + '.call_js'; - if FileExists(s) then - with TStringList.Create do - try - LoadFromFile(s, TEncoding.UTF8); // I3337 - Result := Result + Format('%sthis.c%d=function(t,e){%s};%s', [FTabstop, n, Trim(Text), nl]); // I3681 - finally - Free; - end - else - Result := Result + Format('%sthis.c%d=function(t,e){alert("call(%s) not defined");};%s', [FTabstop, n, FCallFunctions[n], nl]); // I3681 - end; - - Result := Result + sEmbedJS + '}' + nl; // I3681 -end; - -function TCompileKeymanWeb.GetCodeName(code: Integer): string; // I1971 -begin - if (code >= Low(KMXCodeNames)) and (code <= High(KMXCodeNames)) - then Result := KMXCodeNames[code] - else Result := IntToStr(code); -end; - -function TCompileKeymanWeb.HasSupplementaryPlaneChars: Boolean; // I3317 - function StringHasSuppChars(p: PWideChar): Boolean; - begin - if not Assigned(p) then - Exit(False); - - while p^ <> #0 do - begin - if Char.IsSurrogate(p, 0) then - Exit(True); - p := incxstr(p); - end; - - Result := False; - end; - -var - I: Integer; - fsp: PFILE_STORE; - fgp: PFILE_GROUP; - j: Integer; - fkp: PFILE_KEY; -begin - fsp := fk.dpStoreArray; - for i := 0 to Integer(fk.cxStoreArray) - 1 do - begin - if StringHasSuppChars(fsp.dpString) then - Exit(True); - Inc(fsp); - end; - - fgp := fk.dpGroupArray; - for i := 0 to Integer(fk.cxGroupArray) - 1 do - begin - fkp := fgp.dpKeyArray; - for j := 0 to Integer(fgp.cxKeyArray) - 1 do - begin - if StringHasSuppChars(fkp.dpContext) or - StringHasSuppChars(fkp.dpOutput) then - Exit(True); - Inc(fkp); - end; - - if StringHasSuppChars(fgp.dpMatch) or - StringHasSuppChars(fgp.dpNoMatch) then - Exit(True); - - Inc(fgp); - end; - - Result := False; -end; - -function TCompileKeymanWeb.IsKeyboardVersion10OrLater: Boolean; -begin - Result := fk.version >= VERSION_100; -end; - -function TCompileKeymanWeb.IsKeyboardVersion14OrLater: Boolean; -begin - Result := fk.version >= VERSION_140; -end; - -function TCompileKeymanWeb.IsKeyboardVersion15OrLater: Boolean; -begin - Result := fk.version >= VERSION_150; -end; - -procedure TCompileKeymanWeb.CheckStoreForInvalidFunctions(key: PFILE_KEY; store: PFILE_STORE); // I1520 -var - n: Integer; - pwsz: PWideChar; - rec: TSentinelRecord; -const - wcsentinel: WideString = #$FFFF; -begin - n := Pos(wcsentinel, store.dpString); - // Disable the check with versions >= 10.0, since we now support deadkeys in stores. - if (n > 0) and not IsKeyboardVersion10OrLater then - begin - pwsz := PWideChar(store.dpString); - Inc(pwsz, n-1); - rec := ExpandSentinel(pwsz); - ReportError(key.Line, CERR_NotSupportedInKeymanWebStore, Format('%s is not currently supported in store ''%s'' when used by any or index', [GetCodeName(rec.Code), store.szName])); // I4061 - end; -end; - -//{$WARNINGS ON} // bug in Delphi compiler returning W1035 return value undefined?!? - - -function TCompileKeymanWeb.ExpandSentinel( - pwsz: PWideChar): TSentinelRecord; -var - i: Integer; - Found: Boolean; -begin - FillChar(Result, SizeOf(Result), 0); - if Ord(pwsz^) = UC_SENTINEL then - begin - Result.IsSentinel := True; - Inc(pwsz); - Result.Code := Ord(pwsz^); - Inc(pwsz); - case Result.Code of - CODE_ANY, CODE_NOTANY: // I3981 - begin - Result.Any.StoreIndex := Ord(pwsz^) - 1; - Result.Any.Store := fk.dpStoreArray; - Inc(Result.Any.Store, Result.Any.StoreIndex); - end; - CODE_INDEX: - begin - Result.Index.StoreIndex := Ord(pwsz^) - 1; - Result.Index.Store := fk.dpStoreArray; - Inc(Result.Index.Store, Result.Index.StoreIndex); - Inc(pwsz); - Result.Index.Index := Ord(pwsz^); - end; - CODE_DEADKEY: - Result.DeadKey.DeadKey := Ord(pwsz^) - 1; - CODE_USE: - begin - Result.Use.GroupIndex := Ord(pwsz^) - 1; - Result.Use.Group := fk.dpGroupArray; - Inc(Result.Use.Group, Result.Use.GroupIndex); - end; - CODE_CALL: - begin - Result.Call.StoreIndex := Ord(pwsz^) - 1; - Result.Call.Store := fk.dpStoreArray; - Inc(Result.Call.Store, Result.Call.StoreIndex); - end; - CODE_CONTEXTEX: - Result.ContextEx.Index := Ord(pwsz^); - CODE_SETOPT: // I3429 - begin - Result.SetOpt.StoreIndex1 := Ord(pwsz^) - 1; - Result.SetOpt.Store1 := fk.dpStoreArray; - Inc(Result.SetOpt.Store1, Result.SetOpt.StoreIndex1); - Inc(pwsz); - Result.SetOpt.StoreIndex2 := Ord(pwsz^) - 1; - Result.SetOpt.Store2 := fk.dpStoreArray; - Inc(Result.SetOpt.Store2, Result.SetOpt.StoreIndex2); - end; - CODE_SETSYSTEMSTORE: // I3437 - begin - Result.SetSystemStore.dwSystemID := Ord(pwsz^) - 1; - Result.SetSystemStore.SystemStore := fk.dpStoreArray; - Found := False; - - for i := 0 to fk.cxStoreArray - 1 do - begin - if Result.SetSystemStore.SystemStore.dwSystemID = Result.SetSystemStore.dwSystemID then - begin - Found := True; - Break; - end; - Inc(Result.SetSystemStore.SystemStore); - end; - - if not Found then Result.SetSystemStore.SystemStore := nil; - - Inc(pwsz); - Result.SetSystemStore.StoreIndex := Ord(pwsz^) - 1; - Result.SetSystemStore.Store := fk.dpStoreArray; - Inc(Result.SetSystemStore.Store, Result.SetSystemStore.StoreIndex); - end; - CODE_RESETOPT: // I3429 - begin - Result.ResetOpt.StoreIndex := Ord(pwsz^) - 1; - Result.ResetOpt.Store := fk.dpStoreArray; - Inc(Result.ResetOpt.Store, Result.ResetOpt.StoreIndex); - end; - CODE_SAVEOPT: // I3429 - begin - Result.SaveOpt.StoreIndex := Ord(pwsz^) - 1; - Result.SaveOpt.Store := fk.dpStoreArray; - Inc(Result.SaveOpt.Store, Result.SaveOpt.StoreIndex); - end; - CODE_IFOPT: // I3429 - begin - Result.IfOpt.StoreIndex1 := Ord(pwsz^) - 1; - Result.IfOpt.Store1 := fk.dpStoreArray; - Inc(Result.IfOpt.Store1, Result.IfOpt.StoreIndex1); - Inc(pwsz); - Result.IfOpt.IsNot := Ord(pwsz^) - 1; // I3429 - Inc(pwsz); - Result.IfOpt.StoreIndex2 := Ord(pwsz^) - 1; - Result.IfOpt.Store2 := fk.dpStoreArray; - Inc(Result.IfOpt.Store2, Result.IfOpt.StoreIndex2); - end; - CODE_IFSYSTEMSTORE: // I3430 - begin - Result.IfSystemStore.dwSystemID := Ord(pwsz^) - 1; - Result.IfSystemStore.SystemStore := fk.dpStoreArray; - - Found := False; - - for i := 0 to fk.cxStoreArray - 1 do - begin - if Result.IfSystemStore.SystemStore.dwSystemID = Result.IfSystemStore.dwSystemID then - begin - Found := True; - Break; - end; - Inc(Result.IfSystemStore.SystemStore); - end; - - if not Found then Result.IfSystemStore.SystemStore := nil; - Inc(pwsz); - Result.IfSystemStore.IsNot := Ord(pwsz^) - 1; // I3430 - Inc(pwsz); - Result.IfSystemStore.StoreIndex := Ord(pwsz^) - 1; - Result.IfSystemStore.Store := fk.dpStoreArray; - Inc(Result.IfSystemStore.Store, Result.IfSystemStore.StoreIndex); - end; - end; - end - else - Result.ChrVal := GetSuppChar(pwsz); -end; - -function TCompileKeymanWeb.CallFunctionName(s: WideString): WideString; -var - n: Integer; -begin - n := Pos(':', s); - if n = 0 then Result := s - else Result := Copy(s,n+1,Length(s)); -end; - -/// -/// If debug mode, then returns Javascript code necessary for -/// accessing constants in the compiled keyboard -/// -/// @return string of JavaScript code -/// -function TCompileKeymanWeb.JavaScript_SetupDebug: string; -begin - if IsKeyboardVersion10OrLater then - begin - if FDebug then - Result := 'var modCodes = keyman.osk.modifierCodes;'+nl+ - FTabStop+'var keyCodes = keyman.osk.keyCodes;'+nl - else - Result := ''; - end - else - Result := ''; -end; - -function TCompileKeymanWeb.JavaScript_SetupProlog: string; -begin - if IsKeyboardVersion10OrLater then - begin - Result := 'if(typeof keyman === ''undefined'') {'+nl+ - FTabStop+'console.log(''Keyboard requires KeymanWeb 10.0 or later'');'+nl+ - FTabStop+'if(typeof tavultesoft !== ''undefined'') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");'+nl+ - '} else {'; - end - else - Result := ''; -end; - -function TCompileKeymanWeb.JavaScript_SetupEpilog: string; -begin - if IsKeyboardVersion10OrLater then - Result := '}' - else - Result := ''; -end; -/// -/// Converts a modifier bit mask integer into its component bit flags -/// -/// @param FBitMask A KMX modifier bitmask value -/// -/// @return string of JavaScript code, e.g. 'modCodes.SHIFT | modCodes.CTRL /* 0x0030 */' -/// -function TCompileKeymanWeb.FormatModifierAsBitflags(FBitMask: Cardinal): string; -const - mask: array[0..14] of string = ( - 'LCTRL', // 0X0001 - 'RCTRL', // 0X0002 - 'LALT', // 0X0004 - 'RALT', // 0X0008 - - 'SHIFT', // 0X0010 - 'CTRL', // 0X0020 - 'ALT', // 0X0040 - - '???', // Reserved - - 'CAPS', // 0X0100 - 'NO_CAPS', // 0X0200 - - 'NUM_LOCK', // 0X0400 - 'NO_NUM_LOCK', // 0X0800 - - 'SCROLL_LOCK', // 0X1000 - 'NO_SCROLL_LOCK', // 0X2000 - - 'VIRTUAL_KEY' // 0X4000 - ); -var - i: Integer; -begin - //TODO: We need to think about mnemonic layouts which are incompletely supported at present - //tavultesoft.keymanweb.osk. - - if IsKeyboardVersion10OrLater then - begin - // This depends on flags defined in KeymanWeb 10.0 - Result := ''; - - for i := 0 to High(mask) do - begin - if FBitMask and (1 shl i) <> 0 then - begin - if Result <> '' then Result := Result + ' | '; - Result := Result + 'modCodes.'+mask[i]; - end; - end; - - if Result = '' then - Result := '0'; - - Result := Result + ' /* 0x' + IntToHex(FBitMask, 4) + ' */' - end - else - Result := '0x'+IntToHex(FBitMask, 4); -end; - -/// -/// Converts a key value into a constant -/// -/// @param key A virtual key code -/// -/// @return string of JavaScript code, e.g. 'keyCodes.K_A /* 0x41 */' -/// -function TCompileKeymanWeb.FormatKeyAsString(key: Integer): string; -begin - if IsKeyboardVersion10OrLater then - begin - // Depends on flags defined in KeymanWeb 10.0 - if (key <= 255) and (KMWVKeyNames[key] <> '') - then Result := 'keyCodes.'+KMWVKeyNames[key]+ ' /* 0x' + IntToHex(key, 2) + ' */' - else Result := '0x' + IntToHex(key, 2); - end - else - Result := '0x' + IntToHex(key, 2); -end; - -/// -/// Determine the modifiers used in the target keyboard and return a bitmask -/// representing them, or an integer value when not in debug mode -/// -/// @return string of JavaScript code, e.g. 'modCodes.SHIFT | modCodes.CTRL /* 0x0030 */' -/// -function TCompileKeymanWeb.GetKeyboardModifierBitmask: string; -var - i: Integer; - gp: PFILE_GROUP; - j: Integer; - kp: PFILE_KEY; - FBitMask: Integer; -begin - FBitMask := 0; - gp := fk.dpGroupArray; - if fk.cxGroupArray > 0 then - begin - for i := 0 to fk.cxGroupArray-1 do - begin - if gp.fUsingKeys then - begin - kp := gp.dpKeyArray; - if gp.cxKeyArray > 0 then - begin - for j := 0 to gp.cxKeyArray-1 do - begin - if not RuleIsExcludedByPlatform(kp) then - FBitMask := FBitMask or JavaScript_Shift(kp, fMnemonic); - Inc(kp); - end; - end; - end; - Inc(gp); - end; - end; - - if ((FBitMask and KMX_MASK_MODIFIER_CHIRAL) <> 0) and - ((FBitMask and KMX_MASK_MODIFIER_NONCHIRAL) <> 0) then - begin - ReportError(0, CWARN_DontMixChiralAndNonChiralModifiers, 'This keyboard contains Ctrl,Alt and LCtrl,LAlt,RCtrl,RAlt sets of modifiers. Use only one or the other set for web target.'); - end; - - if FDebug - then Result := FormatModifierAsBitflags(FBitMask and KMX_MASK_KEYS) // Exclude KMX_ISVIRTUALKEY, KMX_VIRTUALCHARKEY - else Result := '0x'+IntToHex(FBitMask and KMX_MASK_KEYS, 4); -end; - -end. diff --git a/developer/src/tike/compile/JsonExtractKeyboardInfo.pas b/developer/src/tike/compile/JsonExtractKeyboardInfo.pas deleted file mode 100644 index 7239b36b8c7..00000000000 --- a/developer/src/tike/compile/JsonExtractKeyboardInfo.pas +++ /dev/null @@ -1,68 +0,0 @@ -unit JsonExtractKeyboardInfo; - -interface - -uses - Keyman.Developer.System.Project.ProjectLog; - -type - TJsonExtractKeyboardInfo = class - class function Execute(JsonFile, Fields: string; FSilent: Boolean; FCallback: TProjectLogObjectEvent): Boolean; - end; - -implementation - -uses - System.Classes, - System.Json, - System.StrUtils, - System.SysUtils, - utilstr; - -{ TJsonExtractKeyboardInfo } - -class function TJsonExtractKeyboardInfo.Execute(JsonFile, Fields: string; - FSilent: Boolean; FCallback: TProjectLogObjectEvent): Boolean; -var - json: TJSONObject; - s: string; - v: TJSONValue; -begin - try - with TStringStream.Create('', TEncoding.UTF8) do - try - LoadFromFile(JsonFile); - json := TJSONObject.ParseJsonValue(DataString) as TJSONObject; - if not Assigned(json) then - begin - FCallback(plsError, JsonFile, 'Could not parse keyboard_info', 0, 0); - Exit(False); - end; - - try - while Fields <> '' do - begin - s := StrToken(Fields,','); - v := json.GetValue(s); - if Assigned(v) then - begin - writeln(s+'="'+ReplaceStr(v.Value, '"', '\"')+'"'); - end; - end; - finally - json.Free; - end; - finally - Free; - end; - except - on E:Exception do - begin - FCallback(plsError, JsonFile, E.Message, 0, 0); - Exit(False); - end; - end; - Result := True; -end; - -end. diff --git a/developer/src/tike/compile/Keyman.Developer.System.KmcWrapper.pas b/developer/src/tike/compile/Keyman.Developer.System.KmcWrapper.pas new file mode 100644 index 00000000000..60df52ee360 --- /dev/null +++ b/developer/src/tike/compile/Keyman.Developer.System.KmcWrapper.pas @@ -0,0 +1,121 @@ +unit Keyman.Developer.System.KmcWrapper; + +interface + +uses + Keyman.Developer.System.Project.ProjectFile; + +type + TKmcWrapper = class + public + function Compile(ProjectFile: TProjectFile; const infile, outfile: string; debug: Boolean): Boolean; + end; + +implementation + +uses + System.Classes, + System.SysUtils, + + Keyman.Developer.System.Project.ProjectLog, + Keyman.Developer.System.KeymanDeveloperPaths, + compile, + utilexecute; + +{ TKmcWrapper } + +function TKmcWrapper.Compile( + ProjectFile: TProjectFile; + const infile: string; + const outfile: string; + debug: Boolean +): Boolean; +var + s: TStringList; + logtext, cmdline: string; + ec: Integer; + line: string; + messageLine: TArray; + state: TProjectLogState; + msgFilename: string; + msgText: string; + msgCode: Integer; + msgLine: Integer; + msgType: string; +begin + ec := 0; + logtext := ''; + + // TODO: log-level as opt? + cmdline := Format('"%s" build --log-format tsv --log-level info "%s"', [TKeymanDeveloperPaths.KmcPath, infile]); + if outfile <> '' then + cmdline := cmdline + Format(' --out-file "%s"', [outfile]); + if ProjectFile.Project.Options.CompilerWarningsAsErrors then + cmdline := cmdline + ' --compiler-warnings-as-errors' + else + cmdline := cmdline + ' --no-compiler-warnings-as-errors'; + if not ProjectFile.Project.Options.WarnDeprecatedCode then + cmdline := cmdline + ' --no-warn-deprecated-code'; + + Result := TUtilExecute.Console(cmdline, ExtractFileDir(infile), logtext, ec); + + logtext := UTF8ToString(AnsiString(logtext)); + + if not Result then + begin + ProjectFile.Project.Log(plsError, infile, + Format('Compiler failed to start with error %d: %s', [GetLastError, SysErrorMessage(GetLastError)]), CERR_ERROR, 0); + end; + + Result := Result and (ec = 0); + + // TSV Format of messages emitted from kmc, see NodeCompilerCallbacks.ts:printTsvMessage: + // file line severity code message + // (tab separated) + s := TStringList.Create; + try + s.Text := logtext; + for line in s do + begin + if line.Trim = '' then + Continue; + messageLine := line.Split([#9]); + if Length(messageLine) = 5 then + begin + msgFilename := messageLine[0]; + msgLine := StrToIntDef(messageLine[1], 0); + msgType := messageLine[2]; + msgCode := StrToInt('$'+messageLine[3].Substring(2)); // KM12345 + msgText := messageLine[4]; + + if msgType = 'hint' then + state := plsHint + else if msgType = 'warn' then + begin + state := plsWarning; + if ProjectFile.Project.Options.CompilerWarningsAsErrors then + Result := False; + end + else if msgType = 'error' then + begin + state := plsError; + Result := False; + end + else if msgType = 'fatal' then + begin + state := plsFatal; + Result := False; + end + else // assume msgType = 'info' + state := plsInfo; + ProjectFile.Project.Log(state, msgFilename, msgText, msgCode, msgLine); + end + else + ProjectFile.Project.Log(plsInfo, infile, line, 0, 0); + end; + finally + s.Free; + end; +end; + +end. diff --git a/developer/src/tike/compile/KeymanWebKeyCodes.pas b/developer/src/tike/compile/KeymanWebKeyCodes.pas deleted file mode 100644 index ce087c3e90f..00000000000 --- a/developer/src/tike/compile/KeymanWebKeyCodes.pas +++ /dev/null @@ -1,297 +0,0 @@ -(* - Name: KeymanWebKeyCodes - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 20 Jun 2006 - - Modified Date: 20 Jun 2006 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 20 Jun 2006 - mcdurdin - Initial version -*) -unit KeymanWebKeyCodes; - -interface - -const - CKeymanWebKeyCodes: array[0..255] of Integer = ( - $FF, // L"K_?00", // &H0 - $FF, // L"K_LBUTTON", // &H1 - $FF, // L"K_RBUTTON", // &H2 - $FF, // L"K_CANCEL", // &H3 - $FF, // L"K_MBUTTON", // &H4 - $FF, // L"K_?05", // &H5 - $FF, // L"K_?06", // &H6 - $FF, // L"K_?07", // &H7 - $FF, // L"K_BKSP", // &H8 - $FF, // L"K_TAB", // &H9 - $FF, // L"K_?0A", // &HA - $FF, // L"K_?0B", // &HB - $FF, // L"K_KP5", // &HC - $FF, // L"K_ENTER", // &HD - $FF, // L"K_?0E", // &HE - $FF, // L"K_?0F", // &HF - $FF, // L"K_SHIFT", // &H10 - $FF, // L"K_CONTRO$00, // L", // &H11 - $FF, // L"K_ALT", // &H12 - $FF, // L"K_PAUSE", // &H13 - $FF, // L"K_CAPS", // &H14 - $FF, // L"K_KANJI?15", // &H15 - $FF, // L"K_KANJI?16", // &H16 - $FF, // L"K_KANJI?17", // &H17 - $FF, // L"K_KANJI?18", // &H18 - $FF, // L"K_KANJI?19", // &H19 - $FF, // L"K_?1A", // &H1A - $FF, // L"K_ESC", // &H1B - $FF, // L"K_KANJI?1C", // &H1C - $FF, // L"K_KANJI?1D", // &H1D - $FF, // L"K_KANJI?1E", // &H1E - $FF, // L"K_KANJI?1F", // &H1F - $40, // L"K_SPACE", // &H20 - $FF, // L"K_PGUP", // &H21 - $FF, // L"K_PGDN", // &H22 - $FF, // L"K_END", // &H23 - $FF, // L"K_HOME", // &H24 - $FF, // L"K_LEFT", // &H25 - $FF, // L"K_UP", // &H26 - $FF, // L"K_RIGHT", // &H27 - $FF, // L"K_DOWN", // &H28 - $FF, // L"K_SEL", // &H29 - $FF, // L"K_PRINT", // &H2A - $FF, // L"K_EXEC", // &H2B - $FF, // L"K_PRTSCN", // &H2C - $FF, // L"K_INS", // &H2D - $FF, // L"K_DEL", // &H2E - $FF, // L"K_HELP", // &H2F - $0A, // L"K_0", // &H30 - $01, // L"K_1", // &H31 - $02, // L"K_2", // &H32 - $03, // L"K_3", // &H33 - $04, // L"K_4", // &H34 - $05, // L"K_5", // &H35 - $06, // L"K_6", // &H36 - $07, // L"K_7", // &H37 - $08, // L"K_8", // &H38 - $09, // L"K_9", // &H39 - $FF, // L"K_?3A", // &H3A - $FF, // L"K_?3B", // &H3B - $FF, // L"K_?3C", // &H3C - $FF, // L"K_?3D", // &H3D - $FF, // L"K_?3E", // &H3E - $FF, // L"K_?3F", // &H3F - $FF, // L"K_?40", // &H40 - - $20, // L"K_A", // &H41 - $35, // L"K_B", // &H42 - $33, // L"K_C", // &H43 - $22, // L"K_D", // &H44 - $12, // L"K_E", // &H45 - $23, // L"K_F", // &H46 - $24, // L"K_G", // &H47 - $25, // L"K_H", // &H48 - $17, // L"K_I", // &H49 - $26, // L"K_J", // &H4A - $27, // L"K_K", // &H4B - $28, // L"K_L", // &H4C - $37, // L"K_M", // &H4D - $36, // L"K_N", // &H4E - $18, // L"K_O", // &H4F - $19, // L"K_P", // &H50 - $10, // L"K_Q", // &H51 - $13, // L"K_R", // &H52 - $21, // L"K_S", // &H53 - $14, // L"K_T", // &H54 - $16, // L"K_U", // &H55 - $34, // L"K_V", // &H56 - $11, // L"K_W", // &H57 - $32, // L"K_X", // &H58 - $15, // L"K_Y", // &H59 - $31, // L"K_Z", // &H5A - $FF, // L"K_?5B", // &H5B - $FF, // L"K_?5C", // &H5C - $FF, // L"K_?5D", // &H5D - $FF, // L"K_?5E", // &H5E - $FF, // L"K_?5F", // &H5F - $FF, // L"K_NP0", // &H60 - $FF, // L"K_NP1", // &H61 - $FF, // L"K_NP2", // &H62 - $FF, // L"K_NP3", // &H63 - $FF, // L"K_NP4", // &H64 - $FF, // L"K_NP5", // &H65 - $FF, // L"K_NP6", // &H66 - $FF, // L"K_NP7", // &H67 - $FF, // L"K_NP8", // &H68 - $FF, // L"K_NP9", // &H69 - $FF, // L"K_NPSTAR", // &H6A - $FF, // L"K_NPPLUS", // &H6B - $FF, // L"K_SEPARATOR", // &H6C - $FF, // L"K_NPMINUS", // &H6D - $FF, // L"K_NPDOT", // &H6E - $FF, // L"K_NPSLASH", // &H6F - $FF, // L"K_F1", // &H70 - $FF, // L"K_F2", // &H71 - $FF, // L"K_F3", // &H72 - $FF, // L"K_F4", // &H73 - $FF, // L"K_F5", // &H74 - $FF, // L"K_F6", // &H75 - $FF, // L"K_F7", // &H76 - $FF, // L"K_F8", // &H77 - $FF, // L"K_F9", // &H78 - $FF, // L"K_F10", // &H79 - $FF, // L"K_F11", // &H7A - $FF, // L"K_F12", // &H7B - $FF, // L"K_F13", // &H7C - $FF, // L"K_F14", // &H7D - $FF, // L"K_F15", // &H7E - $FF, // L"K_F16", // &H7F - $FF, // L"K_F17", // &H80 - $FF, // L"K_F18", // &H81 - $FF, // L"K_F19", // &H82 - $FF, // L"K_F20", // &H83 - $FF, // L"K_F21", // &H84 - $FF, // L"K_F22", // &H85 - $FF, // L"K_F23", // &H86 - $FF, // L"K_F24", // &H87 - - $FF, // L"K_?88", // &H88 - $FF, // L"K_?89", // &H89 - $FF, // L"K_?8A", // &H8A - $FF, // L"K_?8B", // &H8B - $FF, // L"K_?8C", // &H8C - $FF, // L"K_?8D", // &H8D - $FF, // L"K_?8E", // &H8E - $FF, // L"K_?8F", // &H8F - - $FF, // L"K_NUMLOCK", // &H90 - $FF, // L"K_SCROL$00, // L", // &H91 - - $FF, // L"K_?92", // &H92 - $FF, // L"K_?93", // &H93 - $FF, // L"K_?94", // &H94 - $FF, // L"K_?95", // &H95 - $FF, // L"K_?96", // &H96 - $FF, // L"K_?97", // &H97 - $FF, // L"K_?98", // &H98 - $FF, // L"K_?99", // &H99 - $FF, // L"K_?9A", // &H9A - $FF, // L"K_?9B", // &H9B - $FF, // L"K_?9C", // &H9C - $FF, // L"K_?9D", // &H9D - $FF, // L"K_?9E", // &H9E - $FF, // L"K_?9F", // &H9F - $FF, // L"K_?A0", // &HA0 - $FF, // L"K_?A1", // &HA1 - $FF, // L"K_?A2", // &HA2 - $FF, // L"K_?A3", // &HA3 - $FF, // L"K_?A4", // &HA4 - $FF, // L"K_?A5", // &HA5 - $FF, // L"K_?A6", // &HA6 - $FF, // L"K_?A7", // &HA7 - $FF, // L"K_?A8", // &HA8 - $FF, // L"K_?A9", // &HA9 - $FF, // L"K_?AA", // &HAA - $FF, // L"K_?AB", // &HAB - $FF, // L"K_?AC", // &HAC - $FF, // L"K_?AD", // &HAD - $FF, // L"K_?AE", // &HAE - $FF, // L"K_?AF", // &HAF - $FF, // L"K_?B0", // &HB0 - $FF, // L"K_?B1", // &HB1 - $FF, // L"K_?B2", // &HB2 - $FF, // L"K_?B3", // &HB3 - $FF, // L"K_?B4", // &HB4 - $FF, // L"K_?B5", // &HB5 - $FF, // L"K_?B6", // &HB6 - $FF, // L"K_?B7", // &HB7 - $FF, // L"K_?B8", // &HB8 - $FF, // L"K_?B9", // &HB9 - - $29, // L"K_COLON", // &HBA - $0C, // L"K_EQUA$00, // L", // &HBB - $38, // L"K_COMMA", // &HBC - $0B, // L"K_HYPHEN", // &HBD - $39, // L"K_PERIOD", // &HBE - $3A, // L"K_SLASH", // &HBF - $00, // L"K_BKQUOTE", // &HC0 - - $00, // L"K_?C1", // &HC1 - $00, // L"K_?C2", // &HC2 - $00, // L"K_?C3", // &HC3 - $00, // L"K_?C4", // &HC4 - $00, // L"K_?C5", // &HC5 - $00, // L"K_?C6", // &HC6 - $00, // L"K_?C7", // &HC7 - $00, // L"K_?C8", // &HC8 - $00, // L"K_?C9", // &HC9 - $00, // L"K_?CA", // &HCA - $00, // L"K_?CB", // &HCB - $00, // L"K_?CC", // &HCC - $00, // L"K_?CD", // &HCD - $00, // L"K_?CE", // &HCE - $00, // L"K_?CF", // &HCF - $00, // L"K_?D0", // &HD0 - $00, // L"K_?D1", // &HD1 - $00, // L"K_?D2", // &HD2 - $00, // L"K_?D3", // &HD3 - $00, // L"K_?D4", // &HD4 - $00, // L"K_?D5", // &HD5 - $00, // L"K_?D6", // &HD6 - $00, // L"K_?D7", // &HD7 - $00, // L"K_?D8", // &HD8 - $00, // L"K_?D9", // &HD9 - $00, // L"K_?DA", // &HDA - - $1A, // L"K_LBRKT", // &HDB - $1C, // L"K_BKSLASH", // &HDC - $1B, // L"K_RBRKT", // &HDD - $2A, // L"K_QUOTE", // &HDE - $00, // L"K_oDF", // &HDF - $00, // L"K_oE0", // &HE0 - $00, // L"K_oE1", // &HE1 - $30, // L"K_oE2", // &HE2 - $00, // L"K_oE3", // &HE3 - $00, // L"K_oE4", // &HE4 - - $00, // L"K_?E5", // &HE5 - - $00, // L"K_oE6", // &HE6 - - $00, // L"K_?E7", // &HE7 - $00, // L"K_?E8", // &HE8 - - $00, // L"K_oE9", // &HE9 - $00, // L"K_oEA", // &HEA - $00, // L"K_oEB", // &HEB - $00, // L"K_oEC", // &HEC - $00, // L"K_oED", // &HED - $00, // L"K_oEE", // &HEE - $00, // L"K_oEF", // &HEF - $00, // L"K_oF0", // &HF0 - $00, // L"K_oF1", // &HF1 - $00, // L"K_oF2", // &HF2 - $00, // L"K_oF3", // &HF3 - $00, // L"K_oF4", // &HF4 - $00, // L"K_oF5", // &HF5 - - $00, // L"K_?F6", // &HF6 - $00, // L"K_?F7", // &HF7 - $00, // L"K_?F8", // &HF8 - $00, // L"K_?F9", // &HF9 - $00, // L"K_?FA", // &HFA - $00, // L"K_?FB", // &HFB - $00, // L"K_?FC", // &HFC - $00, // L"K_?FD", // &HFD - $00, // L"K_?FE", // &HFE - $00 // L"K_?FF" // &HFF - ); - -implementation - -end. - diff --git a/developer/src/tike/compile/MergeKeyboardInfo.pas b/developer/src/tike/compile/MergeKeyboardInfo.pas deleted file mode 100644 index af0bc242f71..00000000000 --- a/developer/src/tike/compile/MergeKeyboardInfo.pas +++ /dev/null @@ -1,1136 +0,0 @@ -{ Merges a source .keyboard_info file with data programatically extracted from a .kmp file and a keyboard .js file - - - Command line parameters: - kmcomp -m [-add-help-link ] - - Note: if keyboard.kmp or keyboard.js do not exist, merge_compiled_keyboard_info will continue to work, just will - not attempt to pull data from them - - # - # There are a number of fields we can fill in programatically. - # - - # id -- from .keyboard_info name - # name -- from kmp.inf, js - # authorName -- from kmp.inf - # authorEmail -- from kmp.inf - # lastModifiedDate -- build time: this only gets refreshed when the version num increments so it's close enough then - # packageFilename -- from $keyboard_info_packageFilename - # packageFileSize -- get from the size of the file - # jsFilename -- from $keyboard_info_jsFilename - # jsFileSize -- get from the size of the file - # documentationFileSize -- get from the size of the file - # isRTL -- from .js, KRTL\s*=\s*1 - # encodings -- from .kmx (existence of .js implies unicode) - # packageIncludes -- from kmp.inf? - # version -- from kmp.inf, js - # minKeymanVersion -- from kmp.inf, kmx, js - # platformSupport -- deduce from whether kmp exists, js exists - # languages -- given the BCP 47 ids, generate the subtag names: - # displayName, languageName - (required) - # scriptName, regionName - if not blank -} -unit MergeKeyboardInfo; - -interface - -uses - System.Json, - compile, - kmpinffile, - packageinfo, - TempFileManager, - kmxfile, - kmxfileconsts, - Keyman.Developer.System.Project.ProjectLog, - UKeymanTargets; - -type - TKeyboardInfoMap = record - Filename: string; - Info: TKeyboardInfo; - end; - - TJSKeyboardInfoMap = record - Filename: string; - Data: string; - Standalone: Boolean; - end; - - TMergeKeyboardInfo = class - private - json: TJSONObject; - FSilent: Boolean; - FCallback: TProjectLogObjectEvent; - FBaseName, FJsFile, FKmpFile, FJsonFile: string; - FMergingValidateIds: Boolean; - FKMPInfFile: TKMPInfFile; - FPackageKMXFileInfos: array of TKeyboardInfoMap; - FPackageJSFileInfos: array of TKeyboardInfoMap; - FJSFileInfo: TJSKeyboardInfoMap; - FVersion: string; - FSourcePath: string; - FHelpLink: string; - function Failed(message: string): Boolean; - function Execute: Boolean; overload; - function LoadJsonFile: Boolean; - function LoadKMPFile: Boolean; - function LoadJSFile: Boolean; - constructor Create(ASourcePath, AJsFile, AKmpFile, AJsonFile, AHelpLink: string; AMergingValidateIds, ASilent: Boolean; ACallback: TProjectLogObjectEvent); - procedure AddAuthor; - procedure AddAuthorEmail; - procedure CheckOrAddEncodings; - procedure CheckOrAddFileSizes; - procedure CheckOrAddID; - procedure CheckOrAddJsFilename; - procedure AddLastModifiedDate; - procedure CheckOrAddMinKeymanVersion; - procedure AddName; - procedure CheckOrAddPackageFilename; - procedure AddPackageIncludes; - procedure AddHelpLink; - procedure AddPlatformSupport; - procedure CheckOrAddVersion; - procedure AddSubtagNames(id: String; o: TJSONObject); - procedure CheckOrMigrateLanguages; - function SaveJsonFile: Boolean; - procedure CheckPackageKeyboardFilenames; - procedure AddIsRTL; - procedure AddSourcePath; - public - destructor Destroy; override; - class function Execute(ASourcePath, AJsFile, AKmpFile, AJsonFile, AHelpLink: string; AMergingValidateIds, ASilent: Boolean; ACallback: TProjectLogObjectEvent): Boolean; overload; - end; - -implementation - -uses - Soap.XsBuiltIns, - - System.Classes, - System.Generics.Collections, - System.RegularExpressions, - System.SysUtils, - System.Zip, - - Keyman.System.RegExGroupHelperRSP19902, - - BCP47Tag, - JsonUtil, - Keyman.System.KeyboardInfoFile, - Keyman.System.KeyboardUtils, - Keyman.System.LanguageCodeUtils, - utilfiletypes, - VersionInfo; - -type - EInvalidKeyboardInfo = class(Exception) - end; - -{ TMergeKeyboardInfo } - -class function TMergeKeyboardInfo.Execute(ASourcePath, AJsFile, AKmpFile, AJsonFile, AHelpLink: string; - AMergingValidateIds, ASilent: Boolean; ACallback: TProjectLogObjectEvent): Boolean; -begin - with TMergeKeyboardInfo.Create(ASourcePath, AJsFile, AKmpFile, AJsonFile, AHelpLink, AMergingValidateIds, ASilent, ACallback) do - try - Result := Execute; - finally - Free; - end; -end; - -constructor TMergeKeyboardInfo.Create(ASourcePath, AJsFile, AKmpFile, AJsonFile, AHelpLink: string; - AMergingValidateIds, ASilent: Boolean; ACallback: TProjectLogObjectEvent); -begin - inherited Create; - - FSourcePath := ASourcePath; - FMergingValidateIds := AMergingValidateIds; - FHelpLink := AHelpLink; - FSilent := ASilent; - FCallback := ACallback; - - if not SameText(ExtractFileExt(AJsFile), '.js') then - begin - FKmpFile := AJsFile; - FJsFile := AKmpFile; - end - else - begin - FKmpFile := AKmpFile; - FJsFile := AJsFile; - end; - - FJsonFile := AJsonFile; - - FBaseName := ChangeFileExt(ExtractFileName(FJsonFile), ''); -end; - -destructor TMergeKeyboardInfo.Destroy; -begin - inherited Destroy; - json.Free; - FKMPInfFile.Free; -end; - -function TMergeKeyboardInfo.Execute: Boolean; -begin - try - if not LoadJsonFile then - Exit(Failed('Could not parse keyboard_info file '+FJsonFile)); - - if not LoadKMPFile then - Exit(Failed('Could not load KMP file '+FKmpFile)); - - if not LoadJSFile then - Exit(Failed('Could not load JS file '+FJsFile)); - - CheckPackageKeyboardFilenames; - - CheckOrAddID; - CheckOrMigrateLanguages; - AddName; - AddIsRTL; - AddAuthor; - AddAuthorEmail; - AddLastModifiedDate; - AddSourcePath; - - CheckOrAddVersion; // must be called before CheckOrAddJsFilename - - CheckOrAddPackageFilename; - CheckOrAddJsFilename; - CheckOrAddEncodings; - CheckOrAddFileSizes; - AddPackageIncludes; - CheckOrAddMinKeymanVersion; - AddHelpLink; - AddPlatformSupport; - - if not SaveJsonFile then - Exit(Failed('Could not save updated keyboard_info file '+FJsonFile)); - except - on E:EInvalidKeyboardInfo do - begin - Failed('Invalid .keyboard_info file: '+E.Message); - Exit(False); - end; - on E:Exception do - begin - Failed('Fatal error '+E.ClassName+': '+E.Message); - Exit(False); - end; - end; - - Result := True; -end; - -function TMergeKeyboardInfo.LoadJsonFile: Boolean; -begin - try - with TStringStream.Create('', TEncoding.UTF8) do - try - LoadFromFile(FJsonFile); - json := TJSONObject.ParseJsonValue(DataString) as TJSONObject; - finally - Free; - end; - except - on E:Exception do - Exit(Failed(E.Message)); - end; - - Result := Assigned(json); -end; - -function TMergeKeyboardInfo.LoadKMPFile: Boolean; -var - FKMXTempFile, FKMPInfTempFile: TTempFile; - i, j: Integer; - LocalHeader: TZipHeader; - Zip: TZipFile; - - procedure SaveMemberToFile(MemberFilename, OutFilename: string); - var - ZipMemberStream, OutFileStream: TStream; - begin - OutFileStream := TFileStream.Create(OutFilename, fmCreate); - try - Zip.Read(MemberFilename, ZipMemberStream, LocalHeader); - try - OutFileStream.CopyFrom(ZipMemberStream, 0); - finally - ZipMemberStream.Free; - end; - finally - OutFileStream.Free; - end; - end; - -begin - if FKMPFile = '' then - Exit(True); - - if not SameText(ExtractFileExt(FKMPFile), '.kmp') then - Exit(Failed('packageFile must be a .kmp file '+FKMPFile)); - - try - Zip := TZipFile.Create; - try - Zip.Open(FKMPFile, zmRead); - - FKMPInfFile := TKMPInfFile.Create; - - if Zip.IndexOf('kmp.json') >= 0 then - begin - FKMPInfTempFile := TTempFileManager.Get('.json'); - try - FKMPInfFile.FileName := FKMPInfTempFile.Name; - SaveMemberToFile('kmp.json', FKMPInfTempFile.Name); - FKMPInfFile.LoadJson; - finally - FKMPInfTempFile.Free; - end; - end - else - begin - FKMPInfTempFile := TTempFileManager.Get('.inf'); - try - FKMPInfFile.FileName := FKMPInfTempFile.Name; - SaveMemberToFile('kmp.inf', FKMPInfTempFile.Name); - FKMPInfFile.LoadIni; - finally - FKMPInfTempFile.Free; - end; - end; - - FKMXTempFile := TTempFileManager.Get('.kmx'); - try - if FKMPInfFile.Keyboards.Count > 0 then - begin - for i := 0 to FKMPInfFile.Keyboards.Count - 1 do - begin - for j := 0 to High(Zip.FileNames) do - begin - // Add the KMX to FPackageKMXFileinfos - if SameText(Zip.FileName[j], FKMPInfFile.Keyboards[i].ID + '.kmx') then - begin - SetLength(FPackageKMXFileInfos, Length(FPackageKMXFileInfos)+1); - SaveMemberToFile(Zip.FileNames[j], FKMXTempFile.Name); - FPackageKMXFileInfos[High(FPackageKMXFileInfos)].Filename := Zip.FileNames[j]; - GetKeyboardInfo(FKMXTempFile.Name, False, FPackageKMXFileInfos[High(FPackageKMXFileInfos)].Info, False); - end; - - // Add the JS to FPackageJSFileInfos - if SameText(Zip.FileNames[j], FKMPInfFile.Keyboards[i].ID + '.js') then - begin - SetLength(FPackageJSFileInfos, Length(FPackageJSFileInfos)+1); - FPackageJSFileInfos[High(FPackageJSFileInfos)].Filename := Zip.FileNames[j]; - - // Apply JS keyboard only to mobile targets, because web is not supported - // in a package. If a package does not support mobile, it should not include - // the .js. - // Not using GetKeyboardInfo because that only handles kmx files - FPackageJSFileInfos[High(FPackageJSFileInfos)].Info.Targets := 'mobile'; - end; - end; - end; - end - - // Handle keyboard packages which may not have [Keyboard] sections defined in kmp.inf. - // Note: They will never include any js keyboards. - else if FKMPInfFile.Keyboards.Count = 0 then - begin - for i := 0 to High(Zip.FileNames) do - begin - if IsKeyboardFile(Zip.FileName[i]) then - begin - SetLength(FPackageKMXFileInfos, Length(FPackageKMXFileInfos)+1); - SaveMemberToFile(Zip.FileNames[i], FKMXTempFile.Name); - FPackageKMXFileInfos[High(FPackageKMXFileInfos)].Filename := Zip.FileNames[i]; - GetKeyboardInfo(FKMXTempFile.Name, False, FPackageKMXFileInfos[High(FPackageKMXFileInfos)].Info, False); - end; - end; - end; - - finally - FKMXTempFile.Free; - end; - finally - FreeAndNil(Zip); - end; - except - on E:EZipException do - Exit(Failed(E.Message)); - end; - - Result := True; -end; - -function TMergeKeyboardInfo.LoadJSFile: Boolean; -begin - if FJsFile = '' then - Exit(True); - - with TStringStream.Create('', TEncoding.UTF8) do - try - LoadFromFile(FJsFile); - FJSFileInfo.Filename := FJsFile; - FJSFileInfo.Data := DataString; - finally - Free; - end; - - FJSFileInfo.Standalone := True; - Result := True; -end; - -function TMergeKeyboardInfo.SaveJsonFile: Boolean; -var - str: TStringList; -begin - str := TStringList.Create; - try - PrettyPrintJSON(json, str); - with TStringStream.Create(str.Text, TEncoding.UTF8) do - try - // Use TStringStream so we don't get a default BOM prolog - SaveToFile(FJsonFile); - finally - Free; - end; - finally - str.Free; - end; - Result := True; -end; - -function TMergeKeyboardInfo.Failed(message: string): Boolean; -begin - FCallback(plsError, FJsonFile, message, 0, 0); - Result := False; -end; - -// -// id -- from .keyboard_info name -// -procedure TMergeKeyboardInfo.CheckOrAddID; -var - v: TJSONValue; - FID: string; -begin - FID := ExtractFileName(ChangeFileExt(FJsonFile, '')); - - v := json.GetValue('id'); - if v <> nil then - begin - if v.Value <> FID then - raise EInvalidKeyboardInfo.CreateFmt('id field is "%s" but should be "%s"', [v.Value, FID]); - end - else - json.AddPair('id', FID); -end; - -procedure TMergeKeyboardInfo.AddSubtagNames(id: String; o: TJSONObject); -var - displayName, languageName, scriptName, regionName: String; - v: TJSONValue; - bcp47Tag: TBCP47Tag; -begin - if id = '' then - Exit; - bcp47Tag := TBCP47Tag.Create(id); - try - TLanguageCodeUtils.BCP47Languages.TryGetValue(bcp47Tag.Language, languageName); - TLanguageCodeUtils.BCP47Scripts.TryGetValue(bcp47Tag.Script, scriptName); - TLanguageCodeUtils.BCP47Regions.TryGetValue(bcp47Tag.Region, regionName); - finally - bcp47Tag.Free; - end; - - displayName := TLanguageCodeUtils.LanguageName(languageName, scriptName, regionName); - - v := o.Values[TKeyboardInfoFile.SDisplayName]; - if not Assigned(v) then - o.AddPair(TKeyboardInfoFile.SDisplayName, displayName); - - v := o.Values[TKeyboardInfoFile.SLanguageName]; - if not Assigned(v) then - o.AddPair(TKeyboardInfoFile.SLanguageName, languageName); - - if scriptName <> '' then - begin - v := o.Values[TKeyboardInfoFile.SScriptName]; - if not Assigned(v) then - o.AddPair(TKeyboardInfoFile.SScriptName, scriptName); - end; - - if regionName <> '' then - begin - v := o.Values[TKeyboardInfoFile.SRegionName]; - if not Assigned(v) then - o.AddPair(TKeyboardInfoFile.SRegionName, regionName); - end; -end; - -procedure TMergeKeyboardInfo.CheckOrMigrateLanguages; -var - v: TJSONValue; - alangs: TJSONArray; - olangs, o: TJSONObject; - pair: TJSONPair; - i: Integer; - id: string; -begin - v := json.GetValue(TKeyboardInfoFile.SLanguages); - - if v is TJSONArray then - begin - // Migrate languages[] array to Object - alangs := v as TJSONArray; - olangs := TJSONObject.Create; - for i := 0 to alangs.Count - 1 do - begin - id := alangs.Items[i].Value; - if id = '' then - continue; - - // Populate subtag names - o := TJSONObject.Create; - olangs.AddPair(id, o); - AddSubtagNames(id, o); - end; - - json.RemovePair(TKeyboardInfoFile.SLanguages); - json.AddPair(TKeyboardInfoFile.SLanguages, olangs); - end - else if v is TJSONObject then - begin - olangs := v as TJsonObject; - for i := 0 to olangs.Count - 1 do - begin - pair := olangs.Pairs[i]; - id := pair.JSONString.Value; - if id = '' then - continue; - - // Populate subtag names - o := pair.JsonValue as TJSONObject; - AddSubtagNames(id, o); - end; - end; -end; - -// -// name -- from kmp.inf, js -// -procedure TMergeKeyboardInfo.AddName; -var - FName: string; -begin - if json.GetValue('name') <> nil then Exit; - - if Assigned(FKMPInfFile) then - FName := FKMPInfFile.Info.Desc[PackageInfo_Name] - else if not FJSFileInfo.Data.IsEmpty then - begin - with TRegEx.Match(FJSFileInfo.Data, 'this\.KN="([^"]+)"') do - begin - if Success - then FName := TGroupHelperRSP19902.Create(Groups[1], FJSFileInfo.Data).FixedValue - else Exit; - end; - end - else - Exit; - - json.AddPair('name', FName); -end; - -// -// isRTL -- from js; if more than one, just first one -// -procedure TMergeKeyboardInfo.AddIsRTL; -begin - if json.GetValue('isRTL') <> nil then Exit; - - if (FJSFileInfo.Data <> '') then - begin - with TRegEx.Match(FJSFileInfo.Data, 'this\.KRTL=1') do - begin - if not Success then Exit; - end; - end - else - Exit; - - json.AddPair('isRTL', TJsonTrue.Create); -end; - -// -// authorName -- from kmp.inf -// -procedure TMergeKeyboardInfo.AddAuthor; -var - FName: string; -begin - if json.GetValue('authorName') <> nil then Exit; - if not Assigned(FKMPInfFile) then Exit; - - FName := Trim(FKMPInfFile.Info.Desc[PackageInfo_Author]); - if FName = '' then Exit; - json.AddPair('authorName', FName); -end; - -// -// authorEmail -- from kmp.inf -// -procedure TMergeKeyboardInfo.AddAuthorEmail; -var - FEmail: string; -begin - if json.GetValue('authorEmail') <> nil then Exit; - if not Assigned(FKMPInfFile) then Exit; - - FEmail := FKMPInfFile.Info.URL[PackageInfo_Author]; - if Copy(FEmail, 1, 7) <> 'mailto:' then Exit; - json.AddPair('authorEmail', Copy(FEMail,8,MaxInt)); -end; - -// -// lastModifiedDate -- build time? Is this -// good enough, and if not, how can we solve this? -// -procedure TMergeKeyboardInfo.AddLastModifiedDate; -var - FDateTime: string; -begin - if json.GetValue('lastModifiedDate') <> nil then Exit; - - with TXSDateTime.Create do - try - AsDateTime := Now; - AsUTCDateTime := AsUTCDateTime; // Converts to UTC - FDateTime := NativeToXS; - finally - Free; - end; - - json.AddPair('lastModifiedDate', FDateTime); -end; - -// -// packageFilename -- from $keyboard_info_packageFilename -// -procedure TMergeKeyboardInfo.CheckOrAddPackageFilename; -var - FFilename: string; - v: TJSONValue; -begin - FFilename := ExtractFileName(FKmpFile); - v := json.GetValue('packageFilename'); - if v <> nil then - begin - if FKmpFile = '' then - raise EInvalidKeyboardInfo.CreateFmt('packageFilename field is "%s" but that package is not present.', [v.Value]) - else if v.Value <> FFilename then - raise EInvalidKeyboardInfo.CreateFmt('packageFilename field is "%s" but should be "%s"', [v.Value, FFilename]); - end - else - begin - if FKmpFile = '' then Exit; - json.AddPair('packageFilename', FFilename); - end; - - // Check that the id of the keyboard matches the filename; used only for release/ keyboards - // in the keyboards repository - if FMergingValidateIds then - begin - if ChangeFileExt(FFilename, '') <> FBaseName then - raise EInvalidKeyboardInfo.CreateFmt('packageFilename field is "%s" but should be "%s.kmp"', - [FFilename, FBaseName]); - end; -end; - -// -// jsFilename -- from $keyboard_info_jsFilename -// -procedure TMergeKeyboardInfo.CheckOrAddJsFilename; -var - FFilename: string; - v: TJSONValue; -begin - FFilename := ExtractFileName(FJsFile); - v := json.GetValue('jsFilename'); - if v <> nil then - begin - if FJsFile = '' then - raise EInvalidKeyboardInfo.CreateFmt('jsFilename field is "%s" but that file is not present.', [v.Value]) - else if v.Value <> FFilename then - raise EInvalidKeyboardInfo.CreateFmt('jsFilename field is "%s" but should be "%s"', [v.Value, FFilename]); - end - else - begin - if FJsFile = '' then Exit; - json.AddPair('jsFilename', FFilename); - end; - - // Check that the id of the keyboard matches the filename; used only for release/ keyboards - // in the keyboards repository - if FMergingValidateIds then - begin - if ChangeFileExt(FFilename, '') <> FBaseName then - raise EInvalidKeyboardInfo.CreateFmt('jsFilename field is "%s" but should be "%s.js"', - [FFilename, FBaseName]); - end; -end; - -// -// encodings -- from .kmx (existence of .js implies unicode) -// -procedure TMergeKeyboardInfo.CheckOrAddEncodings; -var - i: Integer; - encodingsc, encodings: TKIEncodings; - v: TJSONArray; - vc: TJSONArray; -begin - encodings := []; - // For each .kmx, get encodings and add to the result - for i := 0 to High(FPackageKMXFileInfos) do - encodings := encodings + FPackageKMXFileInfos[i].Info.Encodings; - - if FJsFile <> '' then Include(encodings, keUnicode); - - - v := TJSONArray.Create; - if keANSI in encodings then - v.Add('ansi'); - - if keUnicode in encodings then - v.Add('unicode'); - - vc := json.GetValue('encodings') as TJSONArray; - if vc <> nil then - begin - encodingsc := []; - for i := 0 to vc.Count - 1 do - if vc.Items[i].Value = 'ansi' then Include(encodingsc, keANSI) - else if vc.Items[i].Value = 'unicode' then Include(encodingsc, keUnicode); - - if encodingsc <> encodings then - raise EInvalidKeyboardInfo.CreateFmt('encodings field is "%s" but should be "%s"', [vc.ToJSON, v.ToJSON]); - end - else - json.AddPair('encodings', v); -end; - -// -// packageFileSize, jsFileSize, documentationFileSize, all from the actual files -// -procedure TMergeKeyboardInfo.CheckOrAddFileSizes; - procedure DoFileSize(prefix: string); - var - vs, v: TJSONValue; - f: TSearchRec; - begin - v := json.GetValue(prefix+'Filename'); - if v <> nil then - begin - if FindFirst(ExtractFilePath(FJsonFile)+v.Value, 0, f) <> 0 then - raise EInvalidKeyboardInfo.CreateFmt('Unable to locate file %s to check its size', [v.Value]); - FindClose(f); - vs := json.GetValue(prefix+'FileSize'); - if vs = nil then - json.AddPair(prefix+'FileSize', TJSONNumber.Create(f.Size)) - else - begin - if f.Size <> (vs as TJSONNumber).AsInt64 then - raise EInvalidKeyboardInfo.CreateFmt('File size for %s is recorded as %d but should be %d.', - [v.Value, (vs as TJSONNumber).AsInt64, f.Size]); - end; - end; - end; - -begin - DoFileSize('js'); - DoFileSize('package'); - DoFileSize('documentation'); -end; - -// -// packageIncludes -- from kmp.inf? -// -procedure TMergeKeyboardInfo.AddPackageIncludes; -var - i: Integer; - id, ext, name: string; - v: TJSONArray; - j: Integer; - Found: Boolean; -begin - if json.GetValue('packageIncludes') <> nil then Exit; - if not Assigned(FKMPInfFile) then Exit; - - //"welcome", "documentation", "fonts", "visualKeyboard" - - v := TJSONArray.Create; - for i := 0 to FKMPInfFile.Files.Count - 1 do - begin - name := FKMPInffile.Files[i].FileName; - ext := ExtractFileExt(name); - id := ''; - - if SameText(name, 'welcome.htm') then id := 'welcome' - else if SameText(ext, '.kvk') then id := 'visualKeyboard' - else if SameText(ext, '.rtf') then id := 'documentation' - else if SameText(ext, '.html') then id := 'documentation' - else if SameText(ext, '.htm') then id := 'documentation' - else if SameText(ext, '.pdf') then id := 'documentation' - else if SameText(ext, '.ttf') then id := 'fonts' - else if SameText(ext, '.otf') then id := 'fonts' - else if SameText(ext, '.ttc') then id := 'fonts'; - - if (id <> '') then - begin - Found := False; - for j := 0 to v.Count - 1 do - if v.Items[j].Value = id then - begin - Found := True; - Break; - end; - if not Found then - v.Add(id); - end; - end; - - json.AddPair('packageIncludes', v); -end; - -// -// version -- from kmp.inf, js (if more than 1, then just first) -// -procedure TMergeKeyboardInfo.CheckOrAddVersion; -var - v: TJSONValue; -begin - if Assigned(FKMPInfFile) then - begin - FVersion := FKMPInfFile.Info.Desc[PackageInfo_Version]; - end - else if (FJSFileInfo.Data <> '') then - begin - with TRegEx.Match(FJSFileInfo.Data, 'this\.KBVER=([''"])([^''"]+)(\1)') do - if Success then FVersion := TGroupHelperRSP19902.Create(Groups[2], FJSFileInfo.Data).FixedValue; - end; - - if FVersion = '' then - FVersion := '1.0'; - - v := json.GetValue('version'); - if v <> nil then - begin - if v.Value <> FVersion then - raise EInvalidKeyboardInfo.CreateFmt('version field is "%s" but should be "%s"', [v.Value, FVersion]); - end - else - json.AddPair('version', FVersion); -end; - -// -// minKeymanVersion -- from kmp.inf, kmx, js -// -procedure TMergeKeyboardInfo.CheckOrAddMinKeymanVersion; -var - i: Integer; - MinVersion: Cardinal; - MinVersionString: string; - FJSMinVersionString: string; - v: TJSONValue; - s: string; -begin - MinVersion := $0500; - // For each .kmx, get minimum version and add to the result - for i := 0 to High(FPackageKMXFileInfos) do - if FPackageKMXFileInfos[i].Info.FileVersion > MinVersion then - MinVersion := FPackageKMXFileInfos[i].Info.FileVersion; - - FJSMinVersionString := ''; - // See also Keyman.System.KeyboardJSInfo.pas, CompilePackage.pas - with TRegEx.Match(FJSFileInfo.Data, 'this.KMINVER\s*=\s*([''"])(.*?)\1') do - begin - if Success then - begin - s := TGroupHelperRSP19902.Create(Groups[2], FJSFileInfo.Data).FixedValue; - if (FJSMinVersionString = '') or (CompareVersions(FJSMinVersionString, s) > 0) then - FJSMinVersionString := s; - end; - end; - - MinVersionString := Format('%d.%d', [(MinVersion and VERSION_MASK_MAJOR) shr 8, (MinVersion and VERSION_MASK_MINOR)]); - if FJSMinVersionString <> '' then - if CompareVersions(MinVersionString, FJSMinVersionString) > 0 then - MinVersionString := FJSMinVersionString; - - v := json.GetValue('minKeymanVersion'); - if v <> nil then - begin - if v.Value <> MinVersionString then - raise EInvalidKeyboardInfo.CreateFmt('minKeymanVersion field is "%s" but should be "%s"', [v.Value, MinVersionString]); - end - else - json.AddPair('minKeymanVersion', MinVersionString); -end; - -// -// Add helpLink, from commandline parameter -// -procedure TMergeKeyboardInfo.AddHelpLink; -var - v: TJSONValue; -begin - // Validate pre-existing helpLink - v := json.GetValue('helpLink'); - if v <> nil then - begin - if v.Value.IndexOf('https://help.keyman.com/keyboard/') < 0 then - raise EInvalidKeyboardInfo.Create('helpLink should be on https://help.keyman.com/keyboard/'); - end - else - begin - if FHelpLink = '' then - Exit; - - json.AddPair('helpLink', FHelpLink); - end; -end; - -// -// platformSupport -- deduce from whether kmp exists, js exists -// -procedure TMergeKeyboardInfo.AddPlatformSupport; -var - keyboardFile: TKeyboardInfoMap; - target: TKeymanTarget; - targets: TKeymanTargets; - p, v: TJSONObject; - numberKMXFiles, numberJSFiles : Integer; - - procedure AddNewPair(PairName: string; value: string); - begin - try - if (v.GetValue(PairName) = nil) then - v.AddPair(PairName, value); - finally - end; - end; - - function packageContainsKeyboardJs(const id: string): Boolean; - var - kf: TKeyboardInfoMap; - begin - for kf in FPackageJSFileInfos do - if SameText(TKeyboardUtils.KeyboardFileNameToID(kf.Filename), id) then - Exit(True); - - Result := False; - end; - -begin - try - // Validate pre-existing platformSupport - p := json.GetValue('platformSupport') as TJSONObject; - if p <> nil then - begin - numberKMXFiles := Length(FPackageKMXFileInfos); - if FJsFile <> '' then - begin - numberJSFiles := 1; - end - else - begin - numberJSFiles := Length(FPackageJSFileInfos); - end; - - // Validate any target - if (p.GetValue('any') <> nil) and ((numberKMXFiles = 0) or (numberJSFiles = 0)) then - raise EInvalidKeyboardInfo.Create( - '"Any" target should have at least 1 .kmx and 1 .js keyboard file in the package'); - - // Validate desktop targets - if ((p.GetValue('windows') <> nil) or (p.GetValue('macos') <> nil) or - (p.GetValue('linux') <> nil) or (p.GetValue('desktop') <> nil)) and - (numberKMXFiles = 0) then - raise EInvalidKeyboardInfo.Create( - 'Desktop targets should have at least 1 .kmx keyboard file in the package'); - - // Validate web/mobile targets. No kmx implies web/mobile targets - if ((numberKMXFiles = 0) or (p.GetValue('web') <> nil) or - (p.GetValue('iphone') <> nil) or (p.GetValue('ipad') <> nil) or - (p.GetValue('androidphone') <> nil) or (p.GetValue('androidtablet') <> nil) or - (p.GetValue('mobile') <> nil) or (p.GetValue('tablet') <> nil)) and - (numberJSFiles = 0) then - raise EInvalidKeyboardInfo.Create( - 'Web/mobile targets should have at least 1 .js keyboard file in the package'); - - Exit; - end; - finally - end; - - // Parse targets to populate platformSuppport - v := TJSONObject.Create; - - for keyboardFile in FPackageKMXFileInfos do - begin - targets := StringToKeymanTargets(keyboardFile.Info.Targets); - if ktAny in targets then - begin - AddNewPair('windows', 'full'); - AddNewPair('macos', 'full'); - AddNewPair('linux', 'full'); - if packageContainsKeyboardJs(TKeyboardUtils.KeyboardFileNameToId(keyboardFile.Filename)) then - begin - AddNewPair('android', 'full'); - AddNewPair('ios', 'full'); - end; - end - else - begin - // If targets empty, then include all desktop targets - if (targets = []) then - begin - AddNewPair('windows', 'full'); - AddNewPair('macos', 'full'); - AddNewPair('linux', 'full'); - end; - - for target in targets do - begin - if target = ktDesktop then - begin - AddNewPair('windows', 'full'); - AddNewPair('macos', 'full'); - AddNewPair('linux', 'full'); - end; - if target = ktWindows then - AddNewPair('windows', 'full'); - if target = ktMacosx then - AddNewPair('macos', 'full'); - if target = ktLinux then - AddNewPair('linux', 'full'); - - if packageContainsKeyboardJs(TKeyboardUtils.KeyboardFileNameToId(keyboardFile.Filename)) then - begin - // FPackageKMXFileInfos can contain target information for web/mobile targets. - // This is a current limitation of FPackageJSFileInfos if there's no kmx files - if (target = ktMobile) then - begin - AddNewPair('android', 'full'); - AddNewPair('ios', 'full'); - end; - if (target = ktIphone) or (target = ktIpad) then - begin - AddNewPair('ios', 'full'); - end; - if (target = ktAndroidphone) or (target = ktAndroidtablet) then - begin - AddNewPair('android', 'full'); - end; - end; - end; - end; - end; - - for keyboardFile in FPackageJSFileInfos do - begin - targets := StringToKeymanTargets(keyboardFile.Info.Targets); - for target in targets do - begin - if (target = ktMobile) then - begin - AddNewPair('android', 'full'); - AddNewPair('ios', 'full'); - end; - if (target = ktIphone) or (target = ktIpad) then - begin - AddNewPair('ios', 'full'); - end; - if (target = ktAndroidphone) or (target = ktAndroidtablet) then - begin - AddNewPair('android', 'full'); - end; - end; - end; - - // Handle JS file not in kmp. Because it is isolated, we cannot detect - // whether it supports mobile vs desktop web because that is not included - // in the .js. So, for now we assume both. - // - // We no longer assume that the presence of a .js means support for - // native mobile apps. These apps now work on the basis of having a - // .kmp file available - if (FJsFile <> '') then - begin - AddNewPair('desktopWeb', 'full'); - AddNewPair('mobileWeb', 'full'); - end; - - json.AddPair('platformSupport', v); -end; - -// -// Add sourcePath field, from commandline parameter -// -procedure TMergeKeyboardInfo.AddSourcePath; -begin - if FSourcePath = '' then - Exit; - - if not TRegEx.IsMatch(FSourcePath, '^(release|legacy|experimental)\/.+\/.+$') then - raise EInvalidKeyboardInfo.CreateFmt('The source path "%s" is an invalid format, '+ - 'expecting "(release|legacy|experimental)/n/name".', [FSourcePath]); - json.AddPair('sourcePath', FSourcePath); -end; - -procedure TMergeKeyboardInfo.CheckPackageKeyboardFilenames; -var - numberKMXFiles, numberJSFiles : Integer; -begin - // Check that the id of the kmx/js files matches the base filename; used only for release/ keyboards - // in the keyboards repository. This implies there should be only 1 kmx and/or 1 js in release/ keyboard - // packages; this check would not be done in the packages folder. - if FMergingValidateIds and Assigned(FKMPInfFile) then - begin - numberKMXFiles := Length(FPackageKMXFileInfos); - numberJSFiles := Length(FPackageJSFileInfos); - if (numberKMXFiles = 0) and (numberJSFiles = 0) then - raise EInvalidKeyboardInfo.Create('There should be at least 1 .kmx or .js keyboard file in the package.'); - - if numberKMXFiles > 1 then - begin - raise EInvalidKeyboardInfo.Create('There should be at most 1 .kmx keyboard file in the packge.'); - end - else if (numberKMXFiles = 1) and (ChangeFileExt(FPackageKMXFileInfos[0].Filename, '') <> FBaseName) then - begin - raise EInvalidKeyboardInfo.CreateFmt('The file "%s" file in the package has the wrong filename. It should be "%s.kmx"', - [FPackageKMXFileInfos[0].Filename, FBaseName]); - end; - - if numberJSFiles > 1 then - begin - raise EInvalidKeyboardInfo.Create('There should be at most 1 .js keyboard file in the package.'); - end - else if (numberJSFiles = 1) and (ChangeFileExt(FPackageJSFileInfos[0].Filename, '') <> FBaseName) then - begin - raise EInvalidKeyboardInfo.CreateFmt('The file "%s" file in the package has the wrong filename. It should be "%s.js"', - [FPackageJSFileInfos[0].Filename, FBaseName]); - end; - - end; -end; - - -end. diff --git a/developer/src/tike/compile/ValidateKeyboardInfo.pas b/developer/src/tike/compile/ValidateKeyboardInfo.pas deleted file mode 100644 index 0f0d141020d..00000000000 --- a/developer/src/tike/compile/ValidateKeyboardInfo.pas +++ /dev/null @@ -1,187 +0,0 @@ -unit ValidateKeyboardInfo; - -interface - -uses - System.JSON, - Keyman.Developer.System.Project.ProjectLog; - -type - TValidateKeyboardInfo = class - private - FJsonFile: string; - FSilent: Boolean; - json: TJSONObject; - function DoFieldValidation: Boolean; - function LoadJsonFile: Boolean; - constructor Create(AJsonFile: string; ASilent: Boolean); - function Failed(message: string): Boolean; - procedure Warning(message: string); - procedure Info(message: string); - procedure Hint(message: string); - public - class function Execute(JsonFile, JsonSchemaPath: string; FDistribution, FSilent: Boolean; FCallback: TProjectLogObjectEvent): Boolean; - end; - -implementation - -uses - System.Classes, - System.Generics.Collections, - System.SysUtils, - Winapi.Windows, - - BCP47Tag, - compile, -// Keyman.Developer.System.Project.ProjectLogConsole, - Keyman.System.CanonicalLanguageCodeUtils, - Keyman.System.KeyboardInfoFile, - Keyman.System.KMXFileLanguages; - -{ TValidateKeyboardInfo } - -const - SKeyboardInfoSourceSchemaJson = 'keyboard_info.source.json'; - SKeyboardInfoDistSchemaJson = 'keyboard_info.distribution.json'; - -var - GCallback: TProjectLogObjectEvent = nil; - GFilename: string; - -type TValidateJsonMessageProc = function (offset: Int64; message: PAnsiChar): BOOL; stdcall; - -// TODO: dynamically load this -function ValidateJsonFile(pwszSchemaFile, pwszJsonFile: PWideChar; MessageProc: TValidateJsonMessageProc): BOOL; - stdcall; external kmcmpdll_lib; - -function ValidateMessageProc(offset: Int64; message: PAnsiChar): BOOL; stdcall; -begin - GCallback(plsInfo, GFilename, string(AnsiString(message)), 0, 0); - Result := TRUE; -end; - -constructor TValidateKeyboardInfo.Create(AJsonFile: string; ASilent: Boolean); -begin - inherited Create; - FJsonFile := AJsonFile; - FSilent := ASilent; -end; - -function TValidateKeyboardInfo.DoFieldValidation: Boolean; -var - alangs: TJSONArray; - langs: TJSONValue; - i: Integer; - olangs: TJSONObject; - msg: string; -begin - if not LoadJsonFile then - Exit(False); - - // Test that language ids are valid and canonical - langs := json.Values[TKeyboardInfoFile.SLanguages]; - if not Assigned(langs) then - Exit(True); - - Result := True; - - if langs is TJSONArray then - begin - alangs := langs as TJSONArray; - for i := 0 to alangs.Count - 1 do - with TBCP47Tag.Create(alangs.Items[i].Value) do - try - if not IsValid(False, msg) then - Result := Failed(msg); - - if not TCanonicalLanguageCodeUtils.IsCanonical(Tag, msg, False, False) then - Warning(msg); - finally - Free; - end; - end - else - begin - olangs := langs as TJSONObject; - for i := 0 to olangs.Count - 1 do - with TBCP47Tag.Create(olangs.Pairs[i].JsonString.Value) do - try - if not IsValid(False, msg) then - Result := Failed(msg); - - if not TCanonicalLanguageCodeUtils.IsCanonical(Tag, msg, False, False) then - Warning(msg); - finally - Free; - end; - end; -end; - -function TValidateKeyboardInfo.Failed(message: string): Boolean; -begin - GCallback(plsError, FJsonFile, Message, 0, 0); - Result := False; -end; - -procedure TValidateKeyboardInfo.Hint(message: string); -begin - GCallback(plsHint, FJsonFile, Message, 0, 0); -end; - -procedure TValidateKeyboardInfo.Info(message: string); -begin - GCallback(plsInfo, FJsonFile, Message, 0, 0); -end; - -function TValidateKeyboardInfo.LoadJsonFile: Boolean; -begin - try - with TStringStream.Create('', TEncoding.UTF8) do - try - LoadFromFile(FJsonFile); - json := TJSONObject.ParseJsonValue(DataString) as TJSONObject; - finally - Free; - end; - except - on E:Exception do - Exit(Failed(E.Message)); - end; - - Result := Assigned(json); -end; - -procedure TValidateKeyboardInfo.Warning(message: string); -begin - GCallback(plsWarning, FJsonFile, Message, 0, 0); -end; - -class function TValidateKeyboardInfo.Execute(JsonFile, JsonSchemaPath: string; FDistribution, FSilent: Boolean; FCallback: TProjectLogObjectEvent): Boolean; -var - SchemaFile: string; - t: TValidateKeyboardInfo; -begin - GCallback := FCallback; - GFilename := JsonFile; - - if FDistribution - then SchemaFile := JsonSchemaPath + SKeyboardInfoDistSchemaJson - else SchemaFile := JsonSchemaPath + SKeyboardInfoSourceSchemaJson; - Result := ValidateJsonFile(PWideChar(SchemaFile), PWideChar(JsonFile), ValidateMessageProc); - - if Result then - begin - t := TValidateKeyboardInfo.Create(JsonFile, FSilent); - try - Result := t.DoFieldValidation; - finally - t.Free; - end; - end; - - if Result - then GCallback(plsSuccess, JsonFile, 'File '+JsonFile+' validated successfully.', 0, 0) - else GCallback(plsFailure, JsonFile, 'File '+JsonFile+' has errors.', 0, 0); -end; - -end. diff --git a/developer/src/tike/debug/Keyman.System.Debug.DebugCore.pas b/developer/src/tike/debug/Keyman.System.Debug.DebugCore.pas index 6b0a5b5fd5d..05ded33f79f 100644 --- a/developer/src/tike/debug/Keyman.System.Debug.DebugCore.pas +++ b/developer/src/tike/debug/Keyman.System.Debug.DebugCore.pas @@ -18,8 +18,8 @@ EDebugCore = class(Exception); TDebugCore = class private - FKeyboard: pkm_kbp_keyboard; - FState: pkm_kbp_state; + FKeyboard: pkm_core_keyboard; + FState: pkm_core_state; class var KeymanCoreLoaded: Boolean; class procedure InitKeymanCore; static; function GetKMXPlatform: string; @@ -30,8 +30,8 @@ TDebugCore = class function GetOption(const name: string): string; procedure SetOption(const name, value: string); property KMXPlatform: string read GetKMXPlatform write SetKMXPlatform; - property Keyboard: pkm_kbp_keyboard read FKeyboard; - property State: pkm_kbp_state read FState; + property Keyboard: pkm_core_keyboard read FKeyboard; + property State: pkm_core_state read FState; end; implementation @@ -43,7 +43,7 @@ implementation constructor TDebugCore.Create(const Filename: string; EnableDebug: Boolean); var - status: km_kbp_status; + status: km_core_status; begin inherited Create; @@ -52,18 +52,18 @@ constructor TDebugCore.Create(const Filename: string; EnableDebug: Boolean); FKeyboard := nil; FState := nil; - status := km_kbp_keyboard_load(PChar(FileName), FKeyboard); - if status <> KM_KBP_STATUS_OK then + status := km_core_keyboard_load(PChar(FileName), FKeyboard); + if status <> KM_CORE_STATUS_OK then raise EDebugCore.CreateFmt('Unable to start debugger -- keyboard load failed with error %x', [Ord(status)]); - status := km_kbp_state_create(FKeyboard, @KM_KBP_OPTIONS_END, FState); - if status <> KM_KBP_STATUS_OK then + status := km_core_state_create(FKeyboard, @KM_CORE_OPTIONS_END, FState); + if status <> KM_CORE_STATUS_OK then raise EDebugCore.CreateFmt('Unable to start debugger -- state creation failed with error %x', [Ord(status)]); if EnableDebug then begin - status := km_kbp_state_debug_set(FState, 1); - if status <> KM_KBP_STATUS_OK then + status := km_core_state_debug_set(FState, 1); + if status <> KM_CORE_STATUS_OK then raise EDebugCore.CreateFmt('Unable to start debugger -- enabling debug failed with error %x', [Ord(status)]); end; end; @@ -71,10 +71,10 @@ constructor TDebugCore.Create(const Filename: string; EnableDebug: Boolean); destructor TDebugCore.Destroy; begin if FState <> nil then - km_kbp_state_dispose(FState); + km_core_state_dispose(FState); FState := nil; if FKeyboard <> nil then - km_kbp_keyboard_dispose(FKeyboard); + km_core_keyboard_dispose(FKeyboard); FKeyboard := nil; inherited Destroy; end; @@ -87,7 +87,7 @@ class procedure TDebugCore.InitKeymanCore; begin path := TKeymanPaths.KeymanCoreLibraryPath(kmnkbp0); try - _km_kbp_set_library_path(path); + _km_core_set_library_path(path); except on E:Exception do raise EDebugCore.CreateFmt('Unable to load Keyman Core library at %s: %s %s', [path, E.ClassName, E.Message]); @@ -98,61 +98,61 @@ class procedure TDebugCore.InitKeymanCore; function TDebugCore.GetKMXPlatform: string; var - p: pkm_kbp_cp; - status: km_kbp_status; + p: pkm_core_cp; + status: km_core_status; begin - status := km_kbp_state_option_lookup( + status := km_core_state_option_lookup( FState, - KM_KBP_OPT_ENVIRONMENT, - pkm_kbp_cp(PWideChar(KM_KBP_KMX_ENV_PLATFORM)), + KM_CORE_OPT_ENVIRONMENT, + pkm_core_cp(PWideChar(KM_CORE_KMX_ENV_PLATFORM)), p ); - if status <> KM_KBP_STATUS_OK then + if status <> KM_CORE_STATUS_OK then raise EDebugCore.CreateFmt('Unable to locate platform, error %x', [Ord(status)]); Result := PWideChar(p); end; procedure TDebugCore.SetKMXPlatform(const Value: string); var - options: array[0..1] of km_kbp_option_item; - status: km_kbp_status; + options: array[0..1] of km_core_option_item; + status: km_core_status; begin - options[0].key := pkm_kbp_cp(PWideChar(KM_KBP_KMX_ENV_PLATFORM)); - options[0].value := pkm_kbp_cp(PWideChar(Value)); - options[0].scope := KM_KBP_OPT_ENVIRONMENT; - options[1] := KM_KBP_OPTIONS_END; - status := km_kbp_state_options_update(FState, @options[0]); - if status <> KM_KBP_STATUS_OK then + options[0].key := pkm_core_cp(PWideChar(KM_CORE_KMX_ENV_PLATFORM)); + options[0].value := pkm_core_cp(PWideChar(Value)); + options[0].scope := KM_CORE_OPT_ENVIRONMENT; + options[1] := KM_CORE_OPTIONS_END; + status := km_core_state_options_update(FState, @options[0]); + if status <> KM_CORE_STATUS_OK then raise EDebugCore.CreateFmt('Unable to set platform, error %x', [Ord(status)]); end; function TDebugCore.GetOption(const name: string): string; var - p: pkm_kbp_cp; - status: km_kbp_status; + p: pkm_core_cp; + status: km_core_status; begin - status := km_kbp_state_option_lookup( + status := km_core_state_option_lookup( FState, - KM_KBP_OPT_KEYBOARD, - pkm_kbp_cp(PWideChar(name)), + KM_CORE_OPT_KEYBOARD, + pkm_core_cp(PWideChar(name)), p ); - if status <> KM_KBP_STATUS_OK then + if status <> KM_CORE_STATUS_OK then raise EDebugCore.CreateFmt('Unable to locate option %s, error %x', [name, Ord(status)]); Result := PWideChar(p); end; procedure TDebugCore.SetOption(const name, value: string); var - options: array[0..1] of km_kbp_option_item; - status: km_kbp_status; + options: array[0..1] of km_core_option_item; + status: km_core_status; begin - options[0].key := pkm_kbp_cp(PWideChar(Name)); - options[0].value := pkm_kbp_cp(PWideChar(Value)); - options[0].scope := KM_KBP_OPT_KEYBOARD; - options[1] := KM_KBP_OPTIONS_END; - status := km_kbp_state_options_update(FState, @options[0]); - if status <> KM_KBP_STATUS_OK then + options[0].key := pkm_core_cp(PWideChar(Name)); + options[0].value := pkm_core_cp(PWideChar(Value)); + options[0].scope := KM_CORE_OPT_KEYBOARD; + options[1] := KM_CORE_OPTIONS_END; + status := km_core_state_options_update(FState, @options[0]); + if status <> KM_CORE_STATUS_OK then raise EDebugCore.CreateFmt('Unable to set option %s, error %x', [name, Ord(status)]); end; diff --git a/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas b/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas index 6a7e0b3f0ed..4f5ee402112 100644 --- a/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas +++ b/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas @@ -31,7 +31,7 @@ TAIDebugKeyInfo = record { DebugEvent structure -- an event has occurred } TDebugEventActionData = class - ActionType: km_kbp_action_type; + ActionType: km_core_action_type; dwData: Integer; Text: WideString; end; @@ -47,7 +47,7 @@ TDebugEventRuleData = class Context: WideString; StoreOffsets: array[0..20] of Word; //TKeymanStoreEx; nStores: Integer; - procedure FillStoreList(event: pkm_kbp_state_debug_item; KeyboardMemory: PChar); + procedure FillStoreList(event: pkm_core_state_debug_item; KeyboardMemory: PChar); end; TDebugEventType = (etAction, etRuleMatch); @@ -68,28 +68,28 @@ TDebugEvent = class TDebugEventList = class(TObjectList) private - procedure Action_Char(const character: km_kbp_usv); + procedure Action_Char(const character: km_core_usv); procedure Action_DeleteBack(expected_type: uint8_t; expected_value: uintptr_t); procedure Action_EmitKeystroke(const key: Word); procedure Action_Marker(marker: uintptr_t); - function AddActionItem(key: Word; action: pkm_kbp_action_item): Boolean; + function AddActionItem(key: Word; action: pkm_core_action_item): Boolean; procedure AddDebugItem( - debug: pkm_kbp_state_debug_item; + debug: pkm_core_state_debug_item; debugkeyboard: TDebugKeyboard; vk: uint16_t; modifier_state: uint16_t ); public function AddStateItems( - state: pkm_kbp_state; + state: pkm_core_state; vk: uint16_t; modifier_state: uint16_t; debugkeyboard: TDebugKeyboard ): Boolean; overload; function AddStateItems( - state: pkm_kbp_state; + state: pkm_core_state; vk: uint16_t; modifier_state: uint16_t ): Boolean; overload; @@ -133,18 +133,18 @@ procedure TDebugEvent.SetEventType(const Value: TDebugEventType); end; end; -function TDebugEventList.AddActionItem(key: Word; action: pkm_kbp_action_item): Boolean; +function TDebugEventList.AddActionItem(key: Word; action: pkm_core_action_item): Boolean; begin Result := True; case action._type of - KM_KBP_IT_CHAR: Action_Char(action.character); - KM_KBP_IT_MARKER: Action_Marker(action.marker); // Correlates to kmn's "deadkey" markers. - KM_KBP_IT_ALERT: ; // TODO: The keyboard has triggered a alert/beep/bell. - KM_KBP_IT_BACK: Action_DeleteBack(action.backspace.expected_type, action.backspace.expected_value); - KM_KBP_IT_PERSIST_OPT: ; // TODO: The indicated option needs to be stored. - KM_KBP_IT_EMIT_KEYSTROKE: Action_EmitKeystroke(key); - KM_KBP_IT_CAPSLOCK: ; // TODO: Caps lock state needs to be updated - KM_KBP_IT_INVALIDATE_CONTEXT: ; + KM_CORE_IT_CHAR: Action_Char(action.character); + KM_CORE_IT_MARKER: Action_Marker(action.marker); // Correlates to kmn's "deadkey" markers. + KM_CORE_IT_ALERT: ; // TODO: The keyboard has triggered a alert/beep/bell. + KM_CORE_IT_BACK: Action_DeleteBack(action.backspace.expected_type, action.backspace.expected_value); + KM_CORE_IT_PERSIST_OPT: ; // TODO: The indicated option needs to be stored. + KM_CORE_IT_EMIT_KEYSTROKE: Action_EmitKeystroke(key); + KM_CORE_IT_CAPSLOCK: ; // TODO: Caps lock state needs to be updated + KM_CORE_IT_INVALIDATE_CONTEXT: ; else Assert(False, 'Action type '+IntToStr(Ord(action._type))+' is unexpected.'); end; end; @@ -156,12 +156,12 @@ procedure TDebugEventList.Action_EmitKeystroke(const key: Word); case key of VK_TAB: Action_Char(9); // Emit a tab character VK_RETURN: Action_Char(13); // New line character - VK_BACK: Action_DeleteBack(Ord(KM_KBP_BT_UNKNOWN), 0); + VK_BACK: Action_DeleteBack(Ord(KM_CORE_BT_UNKNOWN), 0); else begin event := TDebugEvent.Create; event.EventType := etAction; - event.Action.ActionType := KM_KBP_IT_EMIT_KEYSTROKE; + event.Action.ActionType := KM_CORE_IT_EMIT_KEYSTROKE; event.Action.dwData := key; Add(event); end; @@ -172,13 +172,13 @@ procedure TDebugEventList.Action_EmitKeystroke(const key: Word); /// Insert a UTF-32 character at the insertion point. /// TODO: define behaviour around selection /// -procedure TDebugEventList.Action_Char(const character: km_kbp_usv); +procedure TDebugEventList.Action_Char(const character: km_core_usv); var event: TDebugEvent; begin event := TDebugEvent.Create; event.EventType := etAction; - event.Action.ActionType := KM_KBP_IT_CHAR; + event.Action.ActionType := KM_CORE_IT_CHAR; event.Action.Text := Uni_UTF32CharToUTF16(character); Add(event); end; @@ -188,7 +188,7 @@ procedure TDebugEventList.Action_Char(const character: km_kbp_usv); /// TODO: define behaviour around selection /// procedure TDebugEventList.Action_DeleteBack( - expected_type: uint8_t; /// one of KM_KBP_BT_CHAR, KM_KBP_BT_MARKER, KM_KBP_BT_UNKNOWN + expected_type: uint8_t; /// one of KM_CORE_BT_CHAR, KM_CORE_BT_MARKER, KM_CORE_BT_UNKNOWN expected_value: uintptr_t /// used mainly in unit tests ); var @@ -196,7 +196,7 @@ procedure TDebugEventList.Action_DeleteBack( begin event := TDebugEvent.Create; event.EventType := etAction; - event.Action.ActionType := KM_KBP_IT_BACK; + event.Action.ActionType := KM_CORE_IT_BACK; event.Action.dwData := expected_type; Add(event); end; @@ -214,13 +214,13 @@ procedure TDebugEventList.Action_Marker(marker: uintptr_t); event := TDebugEvent.Create; event.EventType := etAction; - event.Action.ActionType := KM_KBP_IT_MARKER; + event.Action.ActionType := KM_CORE_IT_MARKER; event.Action.dwData := marker; Add(event); end; procedure TDebugEventList.AddDebugItem( - debug: pkm_kbp_state_debug_item; + debug: pkm_core_state_debug_item; debugkeyboard: TDebugKeyboard; vk: uint16_t; modifier_state: uint16_t); @@ -245,14 +245,14 @@ procedure TDebugEventList.AddDebugItem( end; case debug._type of - KM_KBP_DEBUG_END: Result := debugkeyboard.BeginUnicodeLine; - KM_KBP_DEBUG_BEGIN: Result := debugkeyboard.BeginUnicodeLine; - KM_KBP_DEBUG_GROUP_ENTER, - KM_KBP_DEBUG_GROUP_EXIT: if grp > -1 then Result := debugkeyboard.Groups[grp].Line; - KM_KBP_DEBUG_MATCH_ENTER, - KM_KBP_DEBUG_MATCH_EXIT: if grp > -1 then Result := debugkeyboard.Groups[grp].MatchLine; - KM_KBP_DEBUG_NOMATCH_ENTER, - KM_KBP_DEBUG_NOMATCH_EXIT: if grp > -1 then Result := debugkeyboard.Groups[grp].NomatchLine; + KM_CORE_DEBUG_END: Result := debugkeyboard.BeginUnicodeLine; + KM_CORE_DEBUG_BEGIN: Result := debugkeyboard.BeginUnicodeLine; + KM_CORE_DEBUG_GROUP_ENTER, + KM_CORE_DEBUG_GROUP_EXIT: if grp > -1 then Result := debugkeyboard.Groups[grp].Line; + KM_CORE_DEBUG_MATCH_ENTER, + KM_CORE_DEBUG_MATCH_EXIT: if grp > -1 then Result := debugkeyboard.Groups[grp].MatchLine; + KM_CORE_DEBUG_NOMATCH_ENTER, + KM_CORE_DEBUG_NOMATCH_EXIT: if grp > -1 then Result := debugkeyboard.Groups[grp].NomatchLine; end; end; @@ -297,13 +297,13 @@ procedure TDebugEventList.AddDebugItem( { Update user interface } - if ev.Rule.ItemType = KM_KBP_DEBUG_BEGIN then + if ev.Rule.ItemType = KM_CORE_DEBUG_BEGIN then begin ev.Rule.Key.VirtualKey := vk; ev.Rule.Key.Modifiers := modifier_state; end; - if ev.Rule.ItemType = KM_KBP_DEBUG_SET_OPTION then + if ev.Rule.ItemType = KM_CORE_DEBUG_SET_OPTION then begin store := PKeymanStore(debug.kmx_info.option.store); ev.Rule.OptionStoreName := store.dpName; @@ -312,16 +312,16 @@ procedure TDebugEventList.AddDebugItem( end; function TDebugEventList.AddStateItems( - state: pkm_kbp_state; + state: pkm_core_state; vk: uint16_t; modifier_state: uint16_t ): Boolean; var - action: pkm_kbp_action_item; + action: pkm_core_action_item; begin Result := True; - action := km_kbp_state_action_items(state, nil); - while (action._type <> KM_KBP_IT_END) do + action := km_core_state_action_items(state, nil); + while (action._type <> KM_CORE_IT_END) do begin Result := Result and AddActionItem(vk, action); Inc(action); @@ -329,25 +329,25 @@ function TDebugEventList.AddStateItems( end; function TDebugEventList.AddStateItems( - state: pkm_kbp_state; + state: pkm_core_state; vk: uint16_t; modifier_state: uint16_t; debugkeyboard: TDebugKeyboard ): Boolean; var - action: pkm_kbp_action_item; - debug: pkm_kbp_state_debug_item; + action: pkm_core_action_item; + debug: pkm_core_state_debug_item; action_index: Integer; begin Result := True; - debug := km_kbp_state_debug_items(state, nil); - action := km_kbp_state_action_items(state, nil); + debug := km_core_state_debug_items(state, nil); + action := km_core_state_action_items(state, nil); action_index := 0; - while debug._type <> KM_KBP_DEBUG_END do + while debug._type <> KM_CORE_DEBUG_END do begin if debug.kmx_info.first_action > action_index then begin - while (action._type <> KM_KBP_IT_END) and (action_index < debug.kmx_info.first_action) do + while (action._type <> KM_CORE_IT_END) and (action_index < debug.kmx_info.first_action) do begin Result := Result and AddActionItem(vk, action); Inc(action); @@ -360,7 +360,7 @@ function TDebugEventList.AddStateItems( AddDebugItem(debug, debugkeyboard, vk, modifier_state); - if action._type = KM_KBP_IT_EMIT_KEYSTROKE then + if action._type = KM_CORE_IT_EMIT_KEYSTROKE then begin // The EMIT_KEYSTROKE action comes after all rules have completed processing Result := Result and AddActionItem(vk, action); @@ -369,12 +369,12 @@ function TDebugEventList.AddStateItems( // By the time we get to the end of rule processing, all actions should have // already been undertaken - Assert(action._type = KM_KBP_IT_END); + Assert(action._type = KM_CORE_IT_END); end; { TDebugEventRuleData } -procedure TDebugEventRuleData.FillStoreList(event: pkm_kbp_state_debug_item; KeyboardMemory: PChar); +procedure TDebugEventRuleData.FillStoreList(event: pkm_core_state_debug_item; KeyboardMemory: PChar); function StoreOffset(kfh: PKeyboardFileHeader; i: Word): PChar; begin Result := KeyboardMemory; diff --git a/developer/src/tike/debug/UfrmDebugStatus_CallStack.pas b/developer/src/tike/debug/UfrmDebugStatus_CallStack.pas index a46caa0d091..4d47fd49857 100644 --- a/developer/src/tike/debug/UfrmDebugStatus_CallStack.pas +++ b/developer/src/tike/debug/UfrmDebugStatus_CallStack.pas @@ -1,18 +1,18 @@ (* Name: UfrmDebugStatus_CallStack Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 14 Sep 2006 Modified Date: 14 Sep 2006 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 14 Sep 2006 - mcdurdin - Initial version *) unit UfrmDebugStatus_CallStack; @@ -73,9 +73,9 @@ procedure TfrmDebugStatus_CallStack.CallStackPush(rule: TDebugEventRuleData); s: string; begin case rule.ItemType of - KM_KBP_DEBUG_BEGIN: + KM_CORE_DEBUG_BEGIN: lbCallStack.Items.AddObject('begin Unicode', rule); - KM_KBP_DEBUG_GROUP_ENTER: + KM_CORE_DEBUG_GROUP_ENTER: if rule.Group.dpName <> '' then begin s := 'group('+rule.Group.dpName+')'; @@ -84,11 +84,11 @@ procedure TfrmDebugStatus_CallStack.CallStackPush(rule: TDebugEventRuleData); end else lbCallStack.Items.AddObject('Unknown group', rule); - KM_KBP_DEBUG_RULE_ENTER: + KM_CORE_DEBUG_RULE_ENTER: lbCallStack.Items.AddObject('rule at line '+IntToStr(rule.line), rule); - KM_KBP_DEBUG_MATCH_ENTER: + KM_CORE_DEBUG_MATCH_ENTER: lbCallStack.Items.AddObject('match rule', rule); - KM_KBP_DEBUG_NOMATCH_ENTER: + KM_CORE_DEBUG_NOMATCH_ENTER: lbCallStack.Items.AddObject('nomatch rule', rule); end; end; diff --git a/developer/src/tike/debug/UfrmDebugStatus_Events.pas b/developer/src/tike/debug/UfrmDebugStatus_Events.pas index 46a60e43d34..4726b081058 100644 --- a/developer/src/tike/debug/UfrmDebugStatus_Events.pas +++ b/developer/src/tike/debug/UfrmDebugStatus_Events.pas @@ -77,11 +77,11 @@ procedure TfrmDebugStatus_Events.AddRuleEvent(rule: TDebugEventRuleData); s: string; begin case rule.ItemType of - KM_KBP_DEBUG_BEGIN, - KM_KBP_DEBUG_END: - AddItem(rule.ItemType = KM_KBP_DEBUG_BEGIN, 'begin Unicode', rule); - KM_KBP_DEBUG_GROUP_ENTER, - KM_KBP_DEBUG_GROUP_EXIT: + KM_CORE_DEBUG_BEGIN, + KM_CORE_DEBUG_END: + AddItem(rule.ItemType = KM_CORE_DEBUG_BEGIN, 'begin Unicode', rule); + KM_CORE_DEBUG_GROUP_ENTER, + KM_CORE_DEBUG_GROUP_EXIT: begin if rule.Group.dpName <> '' then begin @@ -90,18 +90,18 @@ procedure TfrmDebugStatus_Events.AddRuleEvent(rule: TDebugEventRuleData); end else s := 'Unknown group'; - AddItem(rule.ItemType = KM_KBP_DEBUG_GROUP_ENTER, s, rule); + AddItem(rule.ItemType = KM_CORE_DEBUG_GROUP_ENTER, s, rule); end; - KM_KBP_DEBUG_RULE_ENTER, - KM_KBP_DEBUG_RULE_EXIT: - AddItem(rule.ItemType = KM_KBP_DEBUG_RULE_ENTER, 'rule at line '+IntToStr(rule.line), rule); - KM_KBP_DEBUG_MATCH_ENTER, - KM_KBP_DEBUG_MATCH_EXIT: - AddItem(rule.ItemType = KM_KBP_DEBUG_MATCH_ENTER, 'match rule', rule); - KM_KBP_DEBUG_NOMATCH_ENTER, - KM_KBP_DEBUG_NOMATCH_EXIT: - AddItem(rule.ItemType = KM_KBP_DEBUG_NOMATCH_ENTER, 'nomatch rule', rule); - KM_KBP_DEBUG_SET_OPTION: + KM_CORE_DEBUG_RULE_ENTER, + KM_CORE_DEBUG_RULE_EXIT: + AddItem(rule.ItemType = KM_CORE_DEBUG_RULE_ENTER, 'rule at line '+IntToStr(rule.line), rule); + KM_CORE_DEBUG_MATCH_ENTER, + KM_CORE_DEBUG_MATCH_EXIT: + AddItem(rule.ItemType = KM_CORE_DEBUG_MATCH_ENTER, 'match rule', rule); + KM_CORE_DEBUG_NOMATCH_ENTER, + KM_CORE_DEBUG_NOMATCH_EXIT: + AddItem(rule.ItemType = KM_CORE_DEBUG_NOMATCH_ENTER, 'nomatch rule', rule); + KM_CORE_DEBUG_SET_OPTION: AddItem(True, 'set option', rule); else lbCallStack.Items.AddObject('???', rule); @@ -115,16 +115,16 @@ procedure TfrmDebugStatus_Events.AddActionEvent(action: TDebugEventActionData); end; begin case action.ActionType of - KM_KBP_IT_EMIT_KEYSTROKE: AddItem('emit_keystroke', action); + KM_CORE_IT_EMIT_KEYSTROKE: AddItem('emit_keystroke', action); // QIT_VSHIFTDOWN: AddItem('vshiftdown', action); // QIT_VSHIFTUP: AddItem('vshiftup', action); - KM_KBP_IT_CHAR: AddItem('char', action); - KM_KBP_IT_MARKER: AddItem('marker', action); - KM_KBP_IT_ALERT: AddItem('alert', action); - KM_KBP_IT_BACK: AddItem('back', action); - KM_KBP_IT_PERSIST_OPT: AddItem('persist_opt', action); - KM_KBP_IT_INVALIDATE_CONTEXT: AddItem('invalidate_context', action); - KM_KBP_IT_CAPSLOCK: AddItem('capslock', action); + KM_CORE_IT_CHAR: AddItem('char', action); + KM_CORE_IT_MARKER: AddItem('marker', action); + KM_CORE_IT_ALERT: AddItem('alert', action); + KM_CORE_IT_BACK: AddItem('back', action); + KM_CORE_IT_PERSIST_OPT: AddItem('persist_opt', action); + KM_CORE_IT_INVALIDATE_CONTEXT: AddItem('invalidate_context', action); + KM_CORE_IT_CAPSLOCK: AddItem('capslock', action); else AddItem('Unknown action ???', action); end; end; diff --git a/developer/src/tike/dialogs/examples/Keyman.Developer.UI.UfrmEditLanguageExample.dfm b/developer/src/tike/dialogs/examples/Keyman.Developer.UI.UfrmEditLanguageExample.dfm new file mode 100644 index 00000000000..d4aa0a9bf64 --- /dev/null +++ b/developer/src/tike/dialogs/examples/Keyman.Developer.UI.UfrmEditLanguageExample.dfm @@ -0,0 +1,96 @@ +inherited frmEditLanguageExample: TfrmEditLanguageExample + BorderIcons = [biSystemMenu] + BorderStyle = bsDialog + Caption = 'Edit Language Example' + ClientHeight = 163 + ClientWidth = 289 + Position = poScreenCenter + ExplicitWidth = 295 + ExplicitHeight = 192 + PixelsPerInch = 96 + TextHeight = 13 + object lblLanguageTag: TLabel + Left = 8 + Top = 11 + Width = 70 + Height = 13 + Caption = '&Language tag:' + FocusControl = editLanguageID + end + object lblExampleNote: TLabel + Left = 8 + Top = 92 + Width = 27 + Height = 13 + Caption = '&Note:' + FocusControl = editExampleNote + end + object lblExampleKeys: TLabel + Left = 8 + Top = 38 + Width = 71 + Height = 13 + Caption = 'Key &sequence:' + FocusControl = editExampleKeys + end + object lblExampleText: TLabel + Left = 8 + Top = 65 + Width = 72 + Height = 13 + Caption = 'Expected &text:' + FocusControl = editExampleText + end + object cmdOK: TButton + Left = 124 + Top = 127 + Width = 75 + Height = 25 + Caption = 'OK' + Default = True + ModalResult = 1 + TabOrder = 4 + end + object cmdCancel: TButton + Left = 205 + Top = 127 + Width = 75 + Height = 25 + Cancel = True + Caption = 'Cancel' + ModalResult = 2 + TabOrder = 5 + end + object editExampleNote: TEdit + Left = 94 + Top = 89 + Width = 186 + Height = 21 + TabOrder = 3 + OnChange = FieldChange + end + object editLanguageID: TEdit + Left = 94 + Top = 8 + Width = 131 + Height = 21 + TabOrder = 0 + OnChange = FieldChange + end + object editExampleKeys: TEdit + Left = 94 + Top = 35 + Width = 186 + Height = 21 + TabOrder = 1 + OnChange = FieldChange + end + object editExampleText: TEdit + Left = 94 + Top = 62 + Width = 186 + Height = 21 + TabOrder = 2 + OnChange = FieldChange + end +end diff --git a/developer/src/tike/dialogs/examples/Keyman.Developer.UI.UfrmEditLanguageExample.pas b/developer/src/tike/dialogs/examples/Keyman.Developer.UI.UfrmEditLanguageExample.pas new file mode 100644 index 00000000000..3bd0ab3463c --- /dev/null +++ b/developer/src/tike/dialogs/examples/Keyman.Developer.UI.UfrmEditLanguageExample.pas @@ -0,0 +1,146 @@ +(* + Name: UfrmEditLanguageExample + Copyright: Copyright (C) SIL International. + Date: 14 Aug 2023 + Authors: mcdurdin +*) +unit Keyman.Developer.UI.UfrmEditLanguageExample; + +interface + +uses + System.Classes, + System.SysUtils, + System.Variants, + Winapi.Messages, + Winapi.Windows, + Vcl.Controls, + Vcl.Dialogs, + Vcl.Forms, + Vcl.Graphics, + Vcl.StdCtrls, + + UfrmTike; + +type + TfrmEditLanguageExample = class(TTikeForm) + cmdOK: TButton; + cmdCancel: TButton; + lblLanguageTag: TLabel; + lblExampleNote: TLabel; + editExampleNote: TEdit; + editLanguageID: TEdit; + lblExampleKeys: TLabel; + editExampleKeys: TEdit; + lblExampleText: TLabel; + editExampleText: TEdit; + procedure FormCreate(Sender: TObject); + procedure FieldChange(Sender: TObject); + private + procedure EnableControls; + function GetExampleKeys: string; + function GetExampleNote: string; + function GetExampleText: string; + function GetLanguageID: string; + procedure SetExampleKeys(const Value: string); + procedure SetExampleNote(const Value: string); + procedure SetExampleText(const Value: string); + procedure SetLanguageID(const Value: string); + protected + function GetHelpTopic: string; override; + public + { Public declarations } + procedure SetTitle(IsAdding: Boolean); + property LanguageID: string read GetLanguageID write SetLanguageID; + property ExampleKeys: string read GetExampleKeys write SetExampleKeys; + property ExampleText: string read GetExampleText write SetExampleText; + property ExampleNote: string read GetExampleNote write SetExampleNote; + end; + +implementation + +uses + System.Generics.Collections, + + Keyman.Developer.System.HelpTopics; + +{$R *.dfm} + +{ TfrmEditLanguageExample } + +procedure TfrmEditLanguageExample.FieldChange(Sender: TObject); +begin + inherited; + EnableControls; +end; + +procedure TfrmEditLanguageExample.FormCreate(Sender: TObject); +begin + inherited; + EnableControls; +end; + +function TfrmEditLanguageExample.GetExampleKeys: string; +begin + Result := Trim(editExampleKeys.Text); +end; + +function TfrmEditLanguageExample.GetExampleNote: string; +begin + Result := Trim(editExampleNote.Text); +end; + +function TfrmEditLanguageExample.GetExampleText: string; +begin + Result := Trim(editExampleText.Text); +end; + +function TfrmEditLanguageExample.GetHelpTopic: string; +begin + Result := SHelpTopic_Context_EditLanguageExample; +end; + +function TfrmEditLanguageExample.GetLanguageID: string; +begin + Result := Trim(editLanguageID.Text); +end; + +procedure TfrmEditLanguageExample.EnableControls; +begin + cmdOK.Enabled := + (LanguageID <> '') and + (ExampleKeys <> '') and + (ExampleText <> ''); +end; + +procedure TfrmEditLanguageExample.SetExampleKeys(const Value: string); +begin + editExampleKeys.Text := Value.Trim; + EnableControls; +end; + +procedure TfrmEditLanguageExample.SetExampleNote(const Value: string); +begin + editExampleNote.Text := Value.Trim; + EnableControls; +end; + +procedure TfrmEditLanguageExample.SetExampleText(const Value: string); +begin + editExampleText.Text := Value.Trim; + EnableControls; +end; + +procedure TfrmEditLanguageExample.SetLanguageID(const Value: string); +begin + editLanguageID.Text := Value.Trim; + EnableControls; +end; + +procedure TfrmEditLanguageExample.SetTitle(IsAdding: Boolean); +begin + if IsAdding then + Caption := 'Add Language Example'; +end; + +end. diff --git a/developer/src/tike/dialogs/packageWebFonts/Keyman.Developer.UI.UfrmEditPackageWebFonts.dfm b/developer/src/tike/dialogs/packageWebFonts/Keyman.Developer.UI.UfrmEditPackageWebFonts.dfm new file mode 100644 index 00000000000..9599e0c41ab --- /dev/null +++ b/developer/src/tike/dialogs/packageWebFonts/Keyman.Developer.UI.UfrmEditPackageWebFonts.dfm @@ -0,0 +1,40 @@ +inherited frmEditPackageWebFonts: TfrmEditPackageWebFonts + BorderIcons = [biSystemMenu] + BorderStyle = bsDialog + Caption = 'Select Web Fonts' + ClientHeight = 303 + ClientWidth = 289 + Position = poScreenCenter + ExplicitWidth = 295 + ExplicitHeight = 332 + PixelsPerInch = 96 + TextHeight = 13 + object cmdOK: TButton + Left = 125 + Top = 270 + Width = 75 + Height = 25 + Caption = 'OK' + Default = True + TabOrder = 0 + OnClick = cmdOKClick + end + object cmdCancel: TButton + Left = 206 + Top = 270 + Width = 75 + Height = 25 + Cancel = True + Caption = 'Cancel' + ModalResult = 2 + TabOrder = 1 + end + object clbFonts: TCheckListBox + Left = 8 + Top = 8 + Width = 273 + Height = 256 + ItemHeight = 13 + TabOrder = 2 + end +end diff --git a/developer/src/tike/dialogs/packageWebFonts/Keyman.Developer.UI.UfrmEditPackageWebFonts.pas b/developer/src/tike/dialogs/packageWebFonts/Keyman.Developer.UI.UfrmEditPackageWebFonts.pas new file mode 100644 index 00000000000..f4a01bb3611 --- /dev/null +++ b/developer/src/tike/dialogs/packageWebFonts/Keyman.Developer.UI.UfrmEditPackageWebFonts.pas @@ -0,0 +1,102 @@ +(* + Name: UfrmEditPackageWebFonts + Copyright: Copyright (C) SIL International. + Date: 15 Aug 2023 + Authors: mcdurdin +*) +unit Keyman.Developer.UI.UfrmEditPackageWebFonts; + +interface + +uses + System.Classes, + System.SysUtils, + System.Variants, + Winapi.Messages, + Winapi.Windows, + Vcl.CheckLst, + Vcl.Controls, + Vcl.Dialogs, + Vcl.Forms, + Vcl.Graphics, + Vcl.StdCtrls, + + packageinfo, + UfrmTike; + +type + TfrmEditPackageWebFonts = class(TTikeForm) + cmdOK: TButton; + cmdCancel: TButton; + clbFonts: TCheckListBox; + procedure cmdOKClick(Sender: TObject); + private + FSelectedFonts: TPackageContentFileReferenceList; + FFiles: TPackageContentFileList; + procedure SetFiles(const Value: TPackageContentFileList); + procedure SetSelectedFonts(const Value: TPackageContentFileReferenceList); + protected + function GetHelpTopic: string; override; + public + { Public declarations } + property Files: TPackageContentFileList read FFiles write SetFiles; + property SelectedFonts: TPackageContentFileReferenceList read FSelectedFonts write SetSelectedFonts; + end; + +implementation + +uses + System.Generics.Collections, + + Keyman.Developer.System.HelpTopics, + utilfiletypes; + +{$R *.dfm} + +{ TfrmEditPackageWebFonts } + +procedure TfrmEditPackageWebFonts.cmdOKClick(Sender: TObject); +var + i: Integer; +begin + FSelectedFonts.Clear; + for i := 0 to clbFonts.Items.Count - 1 do + if clbFonts.Checked[i] then + FSelectedFonts.Add(clbFonts.Items.Objects[i] as TPackageContentFile); + ModalResult := mrOk; +end; + +function TfrmEditPackageWebFonts.GetHelpTopic: string; +begin + Result := SHelpTopic_Context_EditPackageWebFonts; +end; + +procedure TfrmEditPackageWebFonts.SetFiles( + const Value: TPackageContentFileList); +var + f: TPackageContentFile; +begin + FFiles := Value; + for f in FFiles do + begin + if GetFileTypeFromFileName(f.FileName) = ftFont then + clbFonts.AddItem(ExtractFileName(f.FileName), f); + end; +end; + +procedure TfrmEditPackageWebFonts.SetSelectedFonts( + const Value: TPackageContentFileReferenceList); +var + n: Integer; + f: TPackageContentFile; +begin + FSelectedFonts := Value; + for f in FSelectedFonts do + begin + n := clbFonts.Items.IndexOfObject(f); + if n >= 0 then + clbFonts.Checked[n] := True; + end; +end; + +end. diff --git a/developer/src/tike/dialogs/relatedPackages/Keyman.Developer.UI.UfrmEditRelatedPackage.dfm b/developer/src/tike/dialogs/relatedPackages/Keyman.Developer.UI.UfrmEditRelatedPackage.dfm new file mode 100644 index 00000000000..fc59c98a343 --- /dev/null +++ b/developer/src/tike/dialogs/relatedPackages/Keyman.Developer.UI.UfrmEditRelatedPackage.dfm @@ -0,0 +1,57 @@ +inherited frmEditRelatedPackage: TfrmEditRelatedPackage + BorderIcons = [biSystemMenu] + BorderStyle = bsDialog + Caption = 'Edit Related Package' + ClientHeight = 163 + ClientWidth = 289 + Position = poScreenCenter + ExplicitWidth = 295 + ExplicitHeight = 192 + PixelsPerInch = 96 + TextHeight = 13 + object lblPackageID: TLabel + Left = 8 + Top = 11 + Width = 58 + Height = 13 + Caption = '&Package ID:' + FocusControl = editPackageID + end + object cmdOK: TButton + Left = 124 + Top = 127 + Width = 75 + Height = 25 + Caption = 'OK' + Default = True + ModalResult = 1 + TabOrder = 2 + end + object cmdCancel: TButton + Left = 205 + Top = 127 + Width = 75 + Height = 25 + Cancel = True + Caption = 'Cancel' + ModalResult = 2 + TabOrder = 3 + end + object editPackageID: TEdit + Left = 94 + Top = 8 + Width = 131 + Height = 21 + TabOrder = 0 + OnChange = FieldChange + end + object chkDeprecates: TCheckBox + Left = 94 + Top = 35 + Width = 187 + Height = 17 + Caption = 'Deprecated' + TabOrder = 1 + OnClick = FieldChange + end +end diff --git a/developer/src/tike/dialogs/relatedPackages/Keyman.Developer.UI.UfrmEditRelatedPackage.pas b/developer/src/tike/dialogs/relatedPackages/Keyman.Developer.UI.UfrmEditRelatedPackage.pas new file mode 100644 index 00000000000..9b71b27b1c8 --- /dev/null +++ b/developer/src/tike/dialogs/relatedPackages/Keyman.Developer.UI.UfrmEditRelatedPackage.pas @@ -0,0 +1,103 @@ +(* + Name: UfrmEditRelatedPackage + Copyright: Copyright (C) SIL International. + Date: 14 Aug 2023 + Authors: mcdurdin +*) +unit Keyman.Developer.UI.UfrmEditRelatedPackage; + +interface + +uses + System.Classes, + System.SysUtils, + Winapi.Messages, + Winapi.Windows, + Vcl.Controls, + Vcl.Forms, + Vcl.StdCtrls, + + UfrmTike; + +type + TfrmEditRelatedPackage = class(TTikeForm) + cmdOK: TButton; + cmdCancel: TButton; + lblPackageID: TLabel; + editPackageID: TEdit; + chkDeprecates: TCheckBox; + procedure FormCreate(Sender: TObject); + procedure FieldChange(Sender: TObject); + private + procedure EnableControls; + function GetDeprecates: Boolean; + function GetPackageID: string; + procedure SetDeprecates(const Value: Boolean); + procedure SetPackageID(const Value: string); + protected + function GetHelpTopic: string; override; + public + { Public declarations } + procedure SetTitle(IsAdding: Boolean); + property PackageID: string read GetPackageID write SetPackageID; + property Deprecates: Boolean read GetDeprecates write SetDeprecates; + end; + +implementation + +uses + Keyman.Developer.System.HelpTopics; + +{$R *.dfm} + +{ TfrmEditRelatedPackage } + +procedure TfrmEditRelatedPackage.FieldChange(Sender: TObject); +begin + inherited; + EnableControls; +end; + +procedure TfrmEditRelatedPackage.FormCreate(Sender: TObject); +begin + inherited; + EnableControls; +end; + +function TfrmEditRelatedPackage.GetDeprecates: Boolean; +begin + Result := chkDeprecates.Checked; +end; + +function TfrmEditRelatedPackage.GetHelpTopic: string; +begin + Result := SHelpTopic_Context_EditRelatedPackage; +end; + +function TfrmEditRelatedPackage.GetPackageID: string; +begin + Result := Trim(editPackageID.Text); +end; + +procedure TfrmEditRelatedPackage.EnableControls; +begin + cmdOK.Enabled := (PackageID <> ''); +end; + +procedure TfrmEditRelatedPackage.SetDeprecates(const Value: Boolean); +begin + chkDeprecates.Checked := Value; +end; + +procedure TfrmEditRelatedPackage.SetPackageID(const Value: string); +begin + editPackageID.Text := Value.Trim; +end; + +procedure TfrmEditRelatedPackage.SetTitle(IsAdding: Boolean); +begin + if IsAdding then + Caption := 'Add Related Package'; +end; + +end. diff --git a/developer/src/tike/help/Keyman.Developer.System.HelpTopics.pas b/developer/src/tike/help/Keyman.Developer.System.HelpTopics.pas index fcc955a8dc0..337ed507f36 100644 --- a/developer/src/tike/help/Keyman.Developer.System.HelpTopics.pas +++ b/developer/src/tike/help/Keyman.Developer.System.HelpTopics.pas @@ -47,6 +47,9 @@ interface SHelpTopic_Context_TouchLayoutBuilder = 'context/keyboard-editor#toc-touch-layout-tab'; SHelpTopic_Context_TestKeyboard = 'context/debug#toc-test-mode'; SHelpTopic_Context_WordlistEditor = 'context/wordlist-editor'; + SHelpTopic_Context_EditLanguageExample = 'context/edit-language-example'; + SHelpTopic_Context_EditRelatedPackage = 'context/edit-related-package'; + SHelpTopic_Context_EditPackageWebFonts = 'context/edit-package-web-fonts'; // For all .kmn language reference topics, prefix with this path: SHelpTopic_LanguageReference_Prefix = 'language/reference/'; diff --git a/developer/src/tike/kmlmi.cmd b/developer/src/tike/kmlmi.cmd deleted file mode 100644 index 32f6a256a95..00000000000 --- a/developer/src/tike/kmlmi.cmd +++ /dev/null @@ -1,20 +0,0 @@ -@echo off -setlocal -rem This script is based on /developer/src/node/inst/kmlmi.cmd. It is stored here -rem in order to allow TIKE to call out to the compiler while debugging. -if exist "%~dp0..\inst\node\dist\node.exe" ( - rem If running in developer/src/tike/: - set nodeexe="%~dp0..\inst\node\dist\node.exe" - set nodecli="%~dp0..\kmc\build\src\kmlmi.js" -) else if exist "%~dp0..\src\inst\node\dist\node.exe" ( - rem If running in developer/bin/: - set nodeexe="%~dp0..\src\inst\node\dist\node.exe" - set nodecli="%~dp0..\src\kmc\build\src\kmlmi.js" -) else ( - rem Cannot find node or kmlmi.js relative to execution path - echo Error: node.exe or kmlmi.js not found. - exit /b 1 -) - -%nodeexe% --enable-source-maps %nodecli% %* -exit /b %errorlevel% diff --git a/developer/src/tike/main/Keyman.Developer.System.KeymanDeveloperPaths.pas b/developer/src/tike/main/Keyman.Developer.System.KeymanDeveloperPaths.pas index 0e6b63c3794..2bae4475182 100644 --- a/developer/src/tike/main/Keyman.Developer.System.KeymanDeveloperPaths.pas +++ b/developer/src/tike/main/Keyman.Developer.System.KeymanDeveloperPaths.pas @@ -16,6 +16,9 @@ TKeymanDeveloperPaths = class sealed const S_LexicalModelCompiler = 'kmlmc.cmd'; class function LexicalModelCompilerPath: string; static; + const S_Kmc = 'kmc.cmd'; + class function KmcPath: string; static; + const S_ServerConfigJson = 'config.json'; class function ServerDataPath: string; static; class function ServerPath: string; static; @@ -54,6 +57,17 @@ class function TKeymanDeveloperPaths.ServerPath: string; else Result := ExtractFilePath(ParamStr(0)) + 'server\'; end; +class function TKeymanDeveloperPaths.KmcPath: string; +var + KeymanRoot: string; +begin + if TKeymanPaths.RunningFromSource(KeymanRoot) + then Result := KeymanRoot + 'developer\src\tike\' + else Result := ExtractFilePath(ParamStr(0)); + + Result := Result + S_Kmc; +end; + class function TKeymanDeveloperPaths.LexicalModelCompilerPath: string; var KeymanRoot: string; diff --git a/developer/src/tike/main/Keyman.System.KeymanCore.pas b/developer/src/tike/main/Keyman.System.KeymanCore.pas index 29a2341b182..8abe0733175 100644 --- a/developer/src/tike/main/Keyman.System.KeymanCore.pas +++ b/developer/src/tike/main/Keyman.System.KeymanCore.pas @@ -1,7 +1,7 @@ { * Keyman is copyright (C) SIL International. MIT License. * - * Interface for Keyman Core, matches keyboardprocessor.h + * Interface for Keyman Core, matches keyman_core_api.h } unit Keyman.System.KeymanCore; @@ -17,63 +17,63 @@ interface uintptr_t = NativeUInt; char16_t = Char; - km_kbp_virtual_key = uint16_t; + km_core_virtual_key = uint16_t; - km_kbp_usv = uint32_t; // UTF-32 - km_kbp_cp = WideChar; - pkm_kbp_cp = ^km_kbp_cp; + km_core_usv = uint32_t; // UTF-32 + km_core_cp = WideChar; + pkm_core_cp = ^km_core_cp; - km_kbp_context = record end; - pkm_kbp_context = ^km_kbp_context; + km_core_context = record end; + pkm_core_context = ^km_core_context; - km_kbp_keyboard = record end; - pkm_kbp_keyboard = ^km_kbp_keyboard; + km_core_keyboard = record end; + pkm_core_keyboard = ^km_core_keyboard; - km_kbp_state = record end; - pkm_kbp_state = ^km_kbp_state; + km_core_state = record end; + pkm_core_state = ^km_core_state; - km_kbp_options = record end; - pkm_kbp_options = ^km_kbp_options; + km_core_options = record end; + pkm_core_options = ^km_core_options; - km_kbp_path_name = PWideChar; // on Windows - pkm_kbp_path_name = ^km_kbp_path_name; + km_core_path_name = PWideChar; // on Windows + pkm_core_path_name = ^km_core_path_name; type - km_kbp_status = ( - KM_KBP_STATUS_OK = 0, - KM_KBP_STATUS_NO_MEM = 1, - KM_KBP_STATUS_IO_ERROR = 2, - KM_KBP_STATUS_INVALID_ARGUMENT = 3, - KM_KBP_STATUS_KEY_ERROR = 4, - KM_KBP_STATUS_INSUFFICENT_BUFFER = 5, - KM_KBP_STATUS_INVALID_UTF = 6, - KM_KBP_STATUS_INVALID_KEYBOARD = 7 + km_core_status = ( + KM_CORE_STATUS_OK = 0, + KM_CORE_STATUS_NO_MEM = 1, + KM_CORE_STATUS_IO_ERROR = 2, + KM_CORE_STATUS_INVALID_ARGUMENT = 3, + KM_CORE_STATUS_KEY_ERROR = 4, + KM_CORE_STATUS_INSUFFICENT_BUFFER = 5, + KM_CORE_STATUS_INVALID_UTF = 6, + KM_CORE_STATUS_INVALID_KEYBOARD = 7 ); type - km_kbp_context_type = ( - KM_KBP_CT_END = 0, - KM_KBP_CT_CHAR, - KM_KBP_CT_MARKER + km_core_context_type = ( + KM_CORE_CT_END = 0, + KM_CORE_CT_CHAR, + KM_CORE_CT_MARKER ); - km_kbp_context_item = record - _type: km_kbp_context_type; + km_core_context_item = record + _type: km_core_context_type; {$IFDEF WIN64} _reserved: array[0..6] of uint8_t; {$ELSE} _reserved: array[0..2] of uint8_t; {$ENDIF} case integer of - 1: (character: km_kbp_usv); + 1: (character: km_core_usv); 2: (marker: uint32_t); end; - pkm_kbp_context_item = ^km_kbp_context_item; + pkm_core_context_item = ^km_core_context_item; const - KM_KBP_CONTEXT_ITEM_END: km_kbp_context_item = ( - _type: KM_KBP_CT_END; + KM_CORE_CONTEXT_ITEM_END: km_core_context_item = ( + _type: KM_CORE_CT_END; _reserved: (0,0,0); character: 0 ); @@ -81,111 +81,111 @@ km_kbp_context_item = record const kmnkbp0 = 'kmnkbp0-0.dll'; -function km_kbp_context_items_from_utf16( - const text: pkm_kbp_cp; - var out_ptr: pkm_kbp_context_item -): km_kbp_status; cdecl; external kmnkbp0 delayed; +function km_core_context_items_from_utf16( + const text: pkm_core_cp; + var out_ptr: pkm_core_context_item +): km_core_status; cdecl; external kmnkbp0 delayed; -function km_kbp_context_items_from_utf8( +function km_core_context_items_from_utf8( const text: PAnsiChar; - var out_ptr: pkm_kbp_context_item -): km_kbp_status; cdecl; external kmnkbp0 delayed; + var out_ptr: pkm_core_context_item +): km_core_status; cdecl; external kmnkbp0 delayed; -function km_kbp_context_items_to_utf16( - const item: pkm_kbp_context_item; - buf: pkm_kbp_cp; +function km_core_context_items_to_utf16( + const item: pkm_core_context_item; + buf: pkm_core_cp; var buf_size: integer -): km_kbp_status; cdecl; external kmnkbp0 delayed; +): km_core_status; cdecl; external kmnkbp0 delayed; -function km_kbp_context_items_to_utf8( - const item: pkm_kbp_context_item; +function km_core_context_items_to_utf8( + const item: pkm_core_context_item; buf: pansichar; var buf_size: integer -): km_kbp_status; cdecl; external kmnkbp0 delayed; +): km_core_status; cdecl; external kmnkbp0 delayed; -procedure km_kbp_context_items_dispose( - const context_items: km_kbp_context_item +procedure km_core_context_items_dispose( + const context_items: km_core_context_item ); cdecl; external kmnkbp0 delayed; -function km_kbp_context_set( - context: pkm_kbp_context; - context_items: pkm_kbp_context_item -): km_kbp_status; cdecl; external kmnkbp0 delayed; +function km_core_context_set( + context: pkm_core_context; + context_items: pkm_core_context_item +): km_core_status; cdecl; external kmnkbp0 delayed; -function km_kbp_context_get( - context: pkm_kbp_context; - var context_items: pkm_kbp_context_item -): km_kbp_status; cdecl; external kmnkbp0 delayed; +function km_core_context_get( + context: pkm_core_context; + var context_items: pkm_core_context_item +): km_core_status; cdecl; external kmnkbp0 delayed; -procedure km_kbp_context_clear( - context: pkm_kbp_context +procedure km_core_context_clear( + context: pkm_core_context ); cdecl; external kmnkbp0 delayed; -function km_kbp_context_append( - context: pkm_kbp_context; - context_items: pkm_kbp_context_item -): km_kbp_status; cdecl; external kmnkbp0 delayed; +function km_core_context_append( + context: pkm_core_context; + context_items: pkm_core_context_item +): km_core_status; cdecl; external kmnkbp0 delayed; -function km_kbp_context_shrink( - context: pkm_kbp_context; +function km_core_context_shrink( + context: pkm_core_context; num: Integer; - prefix: pkm_kbp_context_item -): km_kbp_status; cdecl; external kmnkbp0 delayed; + prefix: pkm_core_context_item +): km_core_status; cdecl; external kmnkbp0 delayed; type - km_kbp_option_scope = ( - KM_KBP_OPT_UNKNOWN = 0, - KM_KBP_OPT_KEYBOARD = 1, - KM_KBP_OPT_ENVIRONMENT = 2, - KM_KBP_OPT_MAX_SCOPES + km_core_option_scope = ( + KM_CORE_OPT_UNKNOWN = 0, + KM_CORE_OPT_KEYBOARD = 1, + KM_CORE_OPT_ENVIRONMENT = 2, + KM_CORE_OPT_MAX_SCOPES ); - km_kbp_option_item = record - key: pkm_kbp_cp; - value: pkm_kbp_cp; - scope: km_kbp_option_scope; + km_core_option_item = record + key: pkm_core_cp; + value: pkm_core_cp; + scope: km_core_option_scope; end; - pkm_kbp_option_item = ^km_kbp_option_item; + pkm_core_option_item = ^km_core_option_item; const - KM_KBP_OPTIONS_END: km_kbp_option_item = (key: nil; value: nil; scope: KM_KBP_OPT_UNKNOWN); + KM_CORE_OPTIONS_END: km_core_option_item = (key: nil; value: nil; scope: KM_CORE_OPT_UNKNOWN); type - km_kbp_action_type = ( - KM_KBP_IT_END = 0, // Marks end of action items list. - KM_KBP_IT_CHAR = 1, // A Unicode character has been generated. - KM_KBP_IT_MARKER = 2, // Correlates to kmn's "deadkey" markers. - KM_KBP_IT_ALERT = 3, // The keyboard has triggered a alert/beep/bell. - KM_KBP_IT_BACK = 4, // Delete the codepoint preceding the insertion point. - KM_KBP_IT_PERSIST_OPT = 5, // The indicated option needs to be stored. - KM_KBP_IT_EMIT_KEYSTROKE = 6, // Emit the current keystroke to the application - KM_KBP_IT_INVALIDATE_CONTEXT = 7, + km_core_action_type = ( + KM_CORE_IT_END = 0, // Marks end of action items list. + KM_CORE_IT_CHAR = 1, // A Unicode character has been generated. + KM_CORE_IT_MARKER = 2, // Correlates to kmn's "deadkey" markers. + KM_CORE_IT_ALERT = 3, // The keyboard has triggered a alert/beep/bell. + KM_CORE_IT_BACK = 4, // Delete the codepoint preceding the insertion point. + KM_CORE_IT_PERSIST_OPT = 5, // The indicated option needs to be stored. + KM_CORE_IT_EMIT_KEYSTROKE = 6, // Emit the current keystroke to the application + KM_CORE_IT_INVALIDATE_CONTEXT = 7, // The processor requests that the context buffer be cleared; // for applications where context is cached, this clears the context; // for applications where context is read from the focused text store, // the context is just re-read and markers flushed. - KM_KBP_IT_CAPSLOCK = 8, // Enable or disable capsLock - KM_KBP_IT_MAX_TYPE_ID + KM_CORE_IT_CAPSLOCK = 8, // Enable or disable capsLock + KM_CORE_IT_MAX_TYPE_ID ); - km_kbp_backspace_type = ( - KM_KBP_BT_UNKNOWN = 0, // Used at beginning of context; user-initiated backspace - KM_KBP_BT_CHAR = 1, // Deleting a character prior to insertion point - KM_KBP_BT_MARKER = 2, // Deleting a marker prior to insertion point - KM_KBP_BT_MAX_TYPE_ID + km_core_backspace_type = ( + KM_CORE_BT_UNKNOWN = 0, // Used at beginning of context; user-initiated backspace + KM_CORE_BT_CHAR = 1, // Deleting a character prior to insertion point + KM_CORE_BT_MARKER = 2, // Deleting a marker prior to insertion point + KM_CORE_BT_MAX_TYPE_ID ); - km_kbp_backspace_item = record - expected_type: uint8_t; /// TODO: km_kbp_backspace_type + km_core_backspace_item = record + expected_type: uint8_t; /// TODO: km_core_backspace_type expected_value: uintptr_t; /// used mainly in unit tests end; - km_kbp_action_item = record - _type: km_kbp_action_type; + km_core_action_item = record + _type: km_core_action_type; {$IFDEF WIN64} _reserved: array[0..6] of uint8_t; {$ELSE} @@ -193,97 +193,97 @@ km_kbp_action_item = record {$ENDIF} case Integer of 0: (marker: uintptr_t); - 1: (option: pkm_kbp_option_item); - 2: (character: km_kbp_usv); - 3: (backspace: km_kbp_backspace_item); + 1: (option: pkm_core_option_item); + 2: (character: km_core_usv); + 3: (backspace: km_core_backspace_item); 4: (capsLock: uint8_t); // CAPSLOCK type, 1 to turn on, 0 to turn off end; - pkm_kbp_action_item = ^km_kbp_action_item; + pkm_core_action_item = ^km_core_action_item; // These types are used only for debugging convenience type - km_kbp_action_item_array = array[0..100] of km_kbp_action_item; - pkm_kbp_action_item_array = ^km_kbp_action_item_array; + km_core_action_item_array = array[0..100] of km_core_action_item; + pkm_core_action_item_array = ^km_core_action_item_array; -function km_kbp_options_list_size( - opts: pkm_kbp_option_item +function km_core_options_list_size( + opts: pkm_core_option_item ): Integer; cdecl; external kmnkbp0 delayed; -function km_kbp_state_option_lookup( - state: pkm_kbp_state; - scope: km_kbp_option_scope; - key: pkm_kbp_cp; - var value: pkm_kbp_cp -): km_kbp_status; cdecl; external kmnkbp0 delayed; +function km_core_state_option_lookup( + state: pkm_core_state; + scope: km_core_option_scope; + key: pkm_core_cp; + var value: pkm_core_cp +): km_core_status; cdecl; external kmnkbp0 delayed; -function km_kbp_state_options_update( - state: pkm_kbp_state; - new_opts: pkm_kbp_option_item -): km_kbp_status; cdecl; external kmnkbp0 delayed; +function km_core_state_options_update( + state: pkm_core_state; + new_opts: pkm_core_option_item +): km_core_status; cdecl; external kmnkbp0 delayed; -function km_kbp_state_options_to_json( - state: pkm_kbp_state; +function km_core_state_options_to_json( + state: pkm_core_state; buf: PAnsiChar; var space: Integer -): km_kbp_status; cdecl; external kmnkbp0 delayed; +): km_core_status; cdecl; external kmnkbp0 delayed; type - km_kbp_keyboard_attrs = record - version_string: pkm_kbp_cp; - id: pkm_kbp_cp; - folder_path: km_kbp_path_name; - default_optons: pkm_kbp_option_item + km_core_keyboard_attrs = record + version_string: pkm_core_cp; + id: pkm_core_cp; + folder_path: km_core_path_name; + default_optons: pkm_core_option_item end; - pkm_kbp_keyboard_attrs = ^km_kbp_keyboard_attrs; + pkm_core_keyboard_attrs = ^km_core_keyboard_attrs; -function km_kbp_keyboard_load( - kb_path: km_kbp_path_name; - var keyboard: pkm_kbp_keyboard -): km_kbp_status; cdecl; external kmnkbp0 delayed; +function km_core_keyboard_load( + kb_path: km_core_path_name; + var keyboard: pkm_core_keyboard +): km_core_status; cdecl; external kmnkbp0 delayed; -procedure km_kbp_keyboard_dispose( - keyboard: pkm_kbp_keyboard +procedure km_core_keyboard_dispose( + keyboard: pkm_core_keyboard ); cdecl; external kmnkbp0 delayed; -function km_kbp_keyboard_get_attrs( - keyboard: pkm_kbp_keyboard; - var out: pkm_kbp_keyboard_attrs -): km_kbp_status; cdecl; external kmnkbp0 delayed; +function km_core_keyboard_get_attrs( + keyboard: pkm_core_keyboard; + var out: pkm_core_keyboard_attrs +): km_core_status; cdecl; external kmnkbp0 delayed; -function km_kbp_state_create( - keyboard: pkm_kbp_keyboard; - env: pkm_kbp_option_item; - var out: pkm_kbp_state -): km_kbp_status; cdecl; external kmnkbp0 delayed; +function km_core_state_create( + keyboard: pkm_core_keyboard; + env: pkm_core_option_item; + var out: pkm_core_state +): km_core_status; cdecl; external kmnkbp0 delayed; -function km_kbp_state_clone( - state: pkm_kbp_state; - var out: pkm_kbp_state -): km_kbp_status; cdecl; external kmnkbp0 delayed; +function km_core_state_clone( + state: pkm_core_state; + var out: pkm_core_state +): km_core_status; cdecl; external kmnkbp0 delayed; -procedure km_kbp_state_dispose( - state: pkm_kbp_state +procedure km_core_state_dispose( + state: pkm_core_state ); cdecl; external kmnkbp0 delayed; -function km_kbp_state_context( - state: pkm_kbp_state -): pkm_kbp_context; cdecl; external kmnkbp0 delayed; +function km_core_state_context( + state: pkm_core_state +): pkm_core_context; cdecl; external kmnkbp0 delayed; -function km_kbp_state_action_items( - state: pkm_kbp_state; +function km_core_state_action_items( + state: pkm_core_state; num_items: pinteger -): pkm_kbp_action_item; cdecl; external kmnkbp0 delayed; +): pkm_core_action_item; cdecl; external kmnkbp0 delayed; -function km_kbp_state_to_json( - state: pkm_kbp_state; +function km_core_state_to_json( + state: pkm_core_state; buf: PAnsiChar; space: pinteger -): km_kbp_status; cdecl; external kmnkbp0 delayed; +): km_core_status; cdecl; external kmnkbp0 delayed; type - km_kbp_attr = record + km_core_attr = record max_context: Integer; // Maximum context size supported by processor. current: uint16_t; // Current API number supported. revision: uint16_t; // Implementation number of current API. @@ -293,344 +293,344 @@ km_kbp_attr = record vendor: pansichar // Implementor of the processor. end; - pkm_kbp_attr = ^km_kbp_attr; + pkm_core_attr = ^km_core_attr; - km_kbp_tech_value = ( - KM_KBP_TECH_UNSPECIFIED = 0, - KM_KBP_TECH_MOCK = 1 shl 0, - KM_KBP_TECH_KMX = 1 shl 1, - KM_KBP_TECH_LDML = 1 shl 2, - KM_KBP_TECH_RUST_MOCK = 1 shl 3 + km_core_tech_value = ( + KM_CORE_TECH_UNSPECIFIED = 0, + KM_CORE_TECH_MOCK = 1 shl 0, + KM_CORE_TECH_KMX = 1 shl 1, + KM_CORE_TECH_LDML = 1 shl 2, + KM_CORE_TECH_RUST_MOCK = 1 shl 3 ); -function km_kbp_get_engine_attrs( - state: pkm_kbp_state -): pkm_kbp_attr; cdecl; external kmnkbp0 delayed; +function km_core_get_engine_attrs( + state: pkm_core_state +): pkm_core_attr; cdecl; external kmnkbp0 delayed; function -km_kbp_process_event( - state: pkm_kbp_state; - vk: km_kbp_virtual_key; +km_core_process_event( + state: pkm_core_state; + vk: km_core_virtual_key; modifier_state: uint16_t; is_key_down: uint8_t; event_flags: uint16_t -): km_kbp_status; cdecl; external kmnkbp0 delayed; +): km_core_status; cdecl; external kmnkbp0 delayed; const - KM_KBP_EVENT_FLAG_DEFAULT = 0; // default value: hardware - KM_KBP_EVENT_FLAG_TOUCH = 1; // set if the event is touch, otherwise hardware + KM_CORE_EVENT_FLAG_DEFAULT = 0; // default value: hardware + KM_CORE_EVENT_FLAG_TOUCH = 1; // set if the event is touch, otherwise hardware -// keyboardprocessor_vkeys.h +// keyman_core_api_vkeys.h const -// enum km_kbp_modifier_state - matches Keyman32 shift states - KM_KBP_MODIFIER_LCTRL = 1 shl 0; - KM_KBP_MODIFIER_RCTRL = 1 shl 1; - KM_KBP_MODIFIER_LALT = 1 shl 2; - KM_KBP_MODIFIER_RALT = 1 shl 3; - KM_KBP_MODIFIER_SHIFT = 1 shl 4; - KM_KBP_MODIFIER_CTRL = 1 shl 5; - KM_KBP_MODIFIER_ALT = 1 shl 6; +// enum km_core_modifier_state - matches Keyman32 shift states + KM_CORE_MODIFIER_LCTRL = 1 shl 0; + KM_CORE_MODIFIER_RCTRL = 1 shl 1; + KM_CORE_MODIFIER_LALT = 1 shl 2; + KM_CORE_MODIFIER_RALT = 1 shl 3; + KM_CORE_MODIFIER_SHIFT = 1 shl 4; + KM_CORE_MODIFIER_CTRL = 1 shl 5; + KM_CORE_MODIFIER_ALT = 1 shl 6; { - KM_KBP_MODIFIER_META = 1 shl 7, // Either Meta-key flag (tentative). Not usable by keyboards currently + KM_CORE_MODIFIER_META = 1 shl 7, // Either Meta-key flag (tentative). Not usable by keyboards currently // Used internally (currently, only by KMW) to ensure Meta-key // shortcuts safely bypass rules // Meta key = Command key on macOS, Windows key on Windows } - KM_KBP_MODIFIER_CAPS = 1 shl 8; - KM_KBP_MODIFIER_NOCAPS = 1 shl 9; + KM_CORE_MODIFIER_CAPS = 1 shl 8; + KM_CORE_MODIFIER_NOCAPS = 1 shl 9; { - KM_KBP_MODIFIER_NUMLOCK = 1 << 10, - KM_KBP_MODIFIER_NONUMLOCK = 1 << 11, - KM_KBP_MODIFIER_SCROLLOCK = 1 << 12, - KM_KBP_MODIFIER_NOSCROLLOCK = 1 << 13, - KM_KBP_MODIFIER_VIRTUALKEY = 1 << 14, + KM_CORE_MODIFIER_NUMLOCK = 1 << 10, + KM_CORE_MODIFIER_NONUMLOCK = 1 << 11, + KM_CORE_MODIFIER_SCROLLOCK = 1 << 12, + KM_CORE_MODIFIER_NOSCROLLOCK = 1 << 13, + KM_CORE_MODIFIER_VIRTUALKEY = 1 << 14, } const -// enum km_kbp_modifier_mask - KM_KBP_MODIFIER_MASK_ALL = $7f; - KM_KBP_MODIFIER_MASK_ALT_GR_SIM = KM_KBP_MODIFIER_LCTRL or KM_KBP_MODIFIER_LALT; - KM_KBP_MODIFIER_MASK_CHIRAL = $1f; - KM_KBP_MODIFIER_MASK_IS_CHIRAL = $0f; - KM_KBP_MODIFIER_MASK_NON_CHIRAL = $7f; - KM_KBP_MODIFIER_MASK_CAPS = $0300; -{KM_KBP_MODIFIER_MASK_NUMLOCK = $0C00, - KM_KBP_MODIFIER_MASK_SCROLLLOCK = $3000,} +// enum km_core_modifier_mask + KM_CORE_MODIFIER_MASK_ALL = $7f; + KM_CORE_MODIFIER_MASK_ALT_GR_SIM = KM_CORE_MODIFIER_LCTRL or KM_CORE_MODIFIER_LALT; + KM_CORE_MODIFIER_MASK_CHIRAL = $1f; + KM_CORE_MODIFIER_MASK_IS_CHIRAL = $0f; + KM_CORE_MODIFIER_MASK_NON_CHIRAL = $7f; + KM_CORE_MODIFIER_MASK_CAPS = $0300; +{KM_CORE_MODIFIER_MASK_NUMLOCK = $0C00, + KM_CORE_MODIFIER_MASK_SCROLLLOCK = $3000,} // These are Windows API VKEYs, using Keyman VKEY names. const // enum km_kpb_virtual_key - KM_KBP_VKEY__00 = $00; - KM_KBP_VKEY_LBUTTON = $01; - KM_KBP_VKEY_RBUTTON = $02; - KM_KBP_VKEY_CANCEL = $03; - KM_KBP_VKEY_MBUTTON = $04; - KM_KBP_VKEY__05 = $05; - KM_KBP_VKEY__06 = $06; - KM_KBP_VKEY__07 = $07; - KM_KBP_VKEY_BKSP = $08; - KM_KBP_VKEY_TAB = $09; - KM_KBP_VKEY__0A = $0A; - KM_KBP_VKEY__0B = $0B; - KM_KBP_VKEY_KP5 = $0C; - KM_KBP_VKEY_ENTER = $0D; - KM_KBP_VKEY__0E = $0E; - KM_KBP_VKEY__0F = $0F; - KM_KBP_VKEY_SHIFT = $10; - KM_KBP_VKEY_CONTROL = $11; - KM_KBP_VKEY_ALT = $12; - KM_KBP_VKEY_PAUSE = $13; - KM_KBP_VKEY_CAPS = $14; - KM_KBP_VKEY__15 = $15; - KM_KBP_VKEY__16 = $16; - KM_KBP_VKEY__17 = $17; - KM_KBP_VKEY__18 = $18; - KM_KBP_VKEY__19 = $19; - KM_KBP_VKEY__1A = $1A; - KM_KBP_VKEY_ESC = $1B; - KM_KBP_VKEY__1C = $1C; - KM_KBP_VKEY__1D = $1D; - KM_KBP_VKEY__1E = $1E; - KM_KBP_VKEY__1F = $1F; - KM_KBP_VKEY_SPACE = $20; - KM_KBP_VKEY_PGUP = $21; - KM_KBP_VKEY_PGDN = $22; - KM_KBP_VKEY_END = $23; - KM_KBP_VKEY_HOME = $24; - KM_KBP_VKEY_LEFT = $25; - KM_KBP_VKEY_UP = $26; - KM_KBP_VKEY_RIGHT = $27; - KM_KBP_VKEY_DOWN = $28; - KM_KBP_VKEY_SEL = $29; - KM_KBP_VKEY_PRINT = $2A; - KM_KBP_VKEY_EXEC = $2B; - KM_KBP_VKEY_PRTSCN = $2C; - KM_KBP_VKEY_INS = $2D; - KM_KBP_VKEY_DEL = $2E; - KM_KBP_VKEY_HELP = $2F; - KM_KBP_VKEY_0 = $30; - KM_KBP_VKEY_1 = $31; - KM_KBP_VKEY_2 = $32; - KM_KBP_VKEY_3 = $33; - KM_KBP_VKEY_4 = $34; - KM_KBP_VKEY_5 = $35; - KM_KBP_VKEY_6 = $36; - KM_KBP_VKEY_7 = $37; - KM_KBP_VKEY_8 = $38; - KM_KBP_VKEY_9 = $39; - KM_KBP_VKEY__3A = $3A; - KM_KBP_VKEY__3B = $3B; - KM_KBP_VKEY__3C = $3C; - KM_KBP_VKEY__3D = $3D; - KM_KBP_VKEY__3E = $3E; - KM_KBP_VKEY__3F = $3F; - KM_KBP_VKEY__40 = $40; - KM_KBP_VKEY_A = $41; - KM_KBP_VKEY_B = $42; - KM_KBP_VKEY_C = $43; - KM_KBP_VKEY_D = $44; - KM_KBP_VKEY_E = $45; - KM_KBP_VKEY_F = $46; - KM_KBP_VKEY_G = $47; - KM_KBP_VKEY_H = $48; - KM_KBP_VKEY_I = $49; - KM_KBP_VKEY_J = $4A; - KM_KBP_VKEY_K = $4B; - KM_KBP_VKEY_L = $4C; - KM_KBP_VKEY_M = $4D; - KM_KBP_VKEY_N = $4E; - KM_KBP_VKEY_O = $4F; - KM_KBP_VKEY_P = $50; - KM_KBP_VKEY_Q = $51; - KM_KBP_VKEY_R = $52; - KM_KBP_VKEY_S = $53; - KM_KBP_VKEY_T = $54; - KM_KBP_VKEY_U = $55; - KM_KBP_VKEY_V = $56; - KM_KBP_VKEY_W = $57; - KM_KBP_VKEY_X = $58; - KM_KBP_VKEY_Y = $59; - KM_KBP_VKEY_Z = $5A; - KM_KBP_VKEY__5B = $5B; - KM_KBP_VKEY__5C = $5C; - KM_KBP_VKEY__5D = $5D; - KM_KBP_VKEY__5E = $5E; - KM_KBP_VKEY__5F = $5F; - KM_KBP_VKEY_NP0 = $60; - KM_KBP_VKEY_NP1 = $61; - KM_KBP_VKEY_NP2 = $62; - KM_KBP_VKEY_NP3 = $63; - KM_KBP_VKEY_NP4 = $64; - KM_KBP_VKEY_NP5 = $65; - KM_KBP_VKEY_NP6 = $66; - KM_KBP_VKEY_NP7 = $67; - KM_KBP_VKEY_NP8 = $68; - KM_KBP_VKEY_NP9 = $69; - KM_KBP_VKEY_NPSTAR = $6A; - KM_KBP_VKEY_NPPLUS = $6B; - KM_KBP_VKEY_SEPARATOR = $6C; - KM_KBP_VKEY_NPMINUS = $6D; - KM_KBP_VKEY_NPDOT = $6E; - KM_KBP_VKEY_NPSLASH = $6F; - KM_KBP_VKEY_F1 = $70; - KM_KBP_VKEY_F2 = $71; - KM_KBP_VKEY_F3 = $72; - KM_KBP_VKEY_F4 = $73; - KM_KBP_VKEY_F5 = $74; - KM_KBP_VKEY_F6 = $75; - KM_KBP_VKEY_F7 = $76; - KM_KBP_VKEY_F8 = $77; - KM_KBP_VKEY_F9 = $78; - KM_KBP_VKEY_F10 = $79; - KM_KBP_VKEY_F11 = $7A; - KM_KBP_VKEY_F12 = $7B; - KM_KBP_VKEY_F13 = $7C; - KM_KBP_VKEY_F14 = $7D; - KM_KBP_VKEY_F15 = $7E; - KM_KBP_VKEY_F16 = $7F; - KM_KBP_VKEY_F17 = $80; - KM_KBP_VKEY_F18 = $81; - KM_KBP_VKEY_F19 = $82; - KM_KBP_VKEY_F20 = $83; - KM_KBP_VKEY_F21 = $84; - KM_KBP_VKEY_F22 = $85; - KM_KBP_VKEY_F23 = $86; - KM_KBP_VKEY_F24 = $87; - KM_KBP_VKEY__88 = $88; - KM_KBP_VKEY__89 = $89; - KM_KBP_VKEY__8A = $8A; - KM_KBP_VKEY__8B = $8B; - KM_KBP_VKEY__8C = $8C; - KM_KBP_VKEY__8D = $8D; - KM_KBP_VKEY__8E = $8E; - KM_KBP_VKEY__8F = $8F; - KM_KBP_VKEY_NUMLOCK = $90; - KM_KBP_VKEY_SCROLL = $91; - KM_KBP_VKEY__92 = $92; - KM_KBP_VKEY__93 = $93; - KM_KBP_VKEY__94 = $94; - KM_KBP_VKEY__95 = $95; - KM_KBP_VKEY__96 = $96; - KM_KBP_VKEY__97 = $97; - KM_KBP_VKEY__98 = $98; - KM_KBP_VKEY__99 = $99; - KM_KBP_VKEY__9A = $9A; - KM_KBP_VKEY__9B = $9B; - KM_KBP_VKEY__9C = $9C; - KM_KBP_VKEY__9D = $9D; - KM_KBP_VKEY__9E = $9E; - KM_KBP_VKEY__9F = $9F; - KM_KBP_VKEY__A0 = $A0; - KM_KBP_VKEY__A1 = $A1; - KM_KBP_VKEY__A2 = $A2; - KM_KBP_VKEY__A3 = $A3; - KM_KBP_VKEY__A4 = $A4; - KM_KBP_VKEY__A5 = $A5; - KM_KBP_VKEY__A6 = $A6; - KM_KBP_VKEY__A7 = $A7; - KM_KBP_VKEY__A8 = $A8; - KM_KBP_VKEY__A9 = $A9; - KM_KBP_VKEY__AA = $AA; - KM_KBP_VKEY__AB = $AB; - KM_KBP_VKEY__AC = $AC; - KM_KBP_VKEY__AD = $AD; - KM_KBP_VKEY__AE = $AE; - KM_KBP_VKEY__AF = $AF; - KM_KBP_VKEY__B0 = $B0; - KM_KBP_VKEY__B1 = $B1; - KM_KBP_VKEY__B2 = $B2; - KM_KBP_VKEY__B3 = $B3; - KM_KBP_VKEY__B4 = $B4; - KM_KBP_VKEY__B5 = $B5; - KM_KBP_VKEY__B6 = $B6; - KM_KBP_VKEY__B7 = $B7; - KM_KBP_VKEY__B8 = $B8; - KM_KBP_VKEY__B9 = $B9; - KM_KBP_VKEY_COLON = $BA; - KM_KBP_VKEY_EQUAL = $BB; - KM_KBP_VKEY_COMMA = $BC; - KM_KBP_VKEY_HYPHEN = $BD; - KM_KBP_VKEY_PERIOD = $BE; - KM_KBP_VKEY_SLASH = $BF; - KM_KBP_VKEY_BKQUOTE = $C0; - KM_KBP_VKEY__C1 = $C1; - KM_KBP_VKEY__C2 = $C2; - KM_KBP_VKEY__C3 = $C3; - KM_KBP_VKEY__C4 = $C4; - KM_KBP_VKEY__C5 = $C5; - KM_KBP_VKEY__C6 = $C6; - KM_KBP_VKEY__C7 = $C7; - KM_KBP_VKEY__C8 = $C8; - KM_KBP_VKEY__C9 = $C9; - KM_KBP_VKEY__CA = $CA; - KM_KBP_VKEY__CB = $CB; - KM_KBP_VKEY__CC = $CC; - KM_KBP_VKEY__CD = $CD; - KM_KBP_VKEY__CE = $CE; - KM_KBP_VKEY__CF = $CF; - KM_KBP_VKEY__D0 = $D0; - KM_KBP_VKEY__D1 = $D1; - KM_KBP_VKEY__D2 = $D2; - KM_KBP_VKEY__D3 = $D3; - KM_KBP_VKEY__D4 = $D4; - KM_KBP_VKEY__D5 = $D5; - KM_KBP_VKEY__D6 = $D6; - KM_KBP_VKEY__D7 = $D7; - KM_KBP_VKEY__D8 = $D8; - KM_KBP_VKEY__D9 = $D9; - KM_KBP_VKEY__DA = $DA; - KM_KBP_VKEY_LBRKT = $DB; - KM_KBP_VKEY_BKSLASH = $DC; - KM_KBP_VKEY_RBRKT = $DD; - KM_KBP_VKEY_QUOTE = $DE; - KM_KBP_VKEY_oDF = $DF; - KM_KBP_VKEY_oE0 = $E0; - KM_KBP_VKEY_oE1 = $E1; - KM_KBP_VKEY_oE2 = $E2; // 102nd key on European layouts - KM_KBP_VKEY_oE3 = $E3; - KM_KBP_VKEY_oE4 = $E4; - KM_KBP_VKEY__E5 = $E5; - KM_KBP_VKEY_oE6 = $E6; - KM_KBP_VKEY__E7 = $E7; - KM_KBP_VKEY__E8 = $E8; - KM_KBP_VKEY_oE9 = $E9; - KM_KBP_VKEY_oEA = $EA; - KM_KBP_VKEY_oEB = $EB; - KM_KBP_VKEY_oEC = $EC; - KM_KBP_VKEY_oED = $ED; - KM_KBP_VKEY_oEE = $EE; - KM_KBP_VKEY_oEF = $EF; - KM_KBP_VKEY_oF0 = $F0; - KM_KBP_VKEY_oF1 = $F1; - KM_KBP_VKEY_oF2 = $F2; - KM_KBP_VKEY_oF3 = $F3; - KM_KBP_VKEY_oF4 = $F4; - KM_KBP_VKEY_oF5 = $F5; - KM_KBP_VKEY__F6 = $F6; - KM_KBP_VKEY__F7 = $F7; - KM_KBP_VKEY__F8 = $F8; - KM_KBP_VKEY__F9 = $F9; - KM_KBP_VKEY__FA = $FA; - KM_KBP_VKEY__FB = $FB; - KM_KBP_VKEY__FC = $FC; - KM_KBP_VKEY__FD = $FD; - KM_KBP_VKEY__FE = $FE; - KM_KBP_VKEY__FF = $FF; - -// keyboardprocessor_consts.h + KM_CORE_VKEY__00 = $00; + KM_CORE_VKEY_LBUTTON = $01; + KM_CORE_VKEY_RBUTTON = $02; + KM_CORE_VKEY_CANCEL = $03; + KM_CORE_VKEY_MBUTTON = $04; + KM_CORE_VKEY__05 = $05; + KM_CORE_VKEY__06 = $06; + KM_CORE_VKEY__07 = $07; + KM_CORE_VKEY_BKSP = $08; + KM_CORE_VKEY_TAB = $09; + KM_CORE_VKEY__0A = $0A; + KM_CORE_VKEY__0B = $0B; + KM_CORE_VKEY_KP5 = $0C; + KM_CORE_VKEY_ENTER = $0D; + KM_CORE_VKEY__0E = $0E; + KM_CORE_VKEY__0F = $0F; + KM_CORE_VKEY_SHIFT = $10; + KM_CORE_VKEY_CONTROL = $11; + KM_CORE_VKEY_ALT = $12; + KM_CORE_VKEY_PAUSE = $13; + KM_CORE_VKEY_CAPS = $14; + KM_CORE_VKEY__15 = $15; + KM_CORE_VKEY__16 = $16; + KM_CORE_VKEY__17 = $17; + KM_CORE_VKEY__18 = $18; + KM_CORE_VKEY__19 = $19; + KM_CORE_VKEY__1A = $1A; + KM_CORE_VKEY_ESC = $1B; + KM_CORE_VKEY__1C = $1C; + KM_CORE_VKEY__1D = $1D; + KM_CORE_VKEY__1E = $1E; + KM_CORE_VKEY__1F = $1F; + KM_CORE_VKEY_SPACE = $20; + KM_CORE_VKEY_PGUP = $21; + KM_CORE_VKEY_PGDN = $22; + KM_CORE_VKEY_END = $23; + KM_CORE_VKEY_HOME = $24; + KM_CORE_VKEY_LEFT = $25; + KM_CORE_VKEY_UP = $26; + KM_CORE_VKEY_RIGHT = $27; + KM_CORE_VKEY_DOWN = $28; + KM_CORE_VKEY_SEL = $29; + KM_CORE_VKEY_PRINT = $2A; + KM_CORE_VKEY_EXEC = $2B; + KM_CORE_VKEY_PRTSCN = $2C; + KM_CORE_VKEY_INS = $2D; + KM_CORE_VKEY_DEL = $2E; + KM_CORE_VKEY_HELP = $2F; + KM_CORE_VKEY_0 = $30; + KM_CORE_VKEY_1 = $31; + KM_CORE_VKEY_2 = $32; + KM_CORE_VKEY_3 = $33; + KM_CORE_VKEY_4 = $34; + KM_CORE_VKEY_5 = $35; + KM_CORE_VKEY_6 = $36; + KM_CORE_VKEY_7 = $37; + KM_CORE_VKEY_8 = $38; + KM_CORE_VKEY_9 = $39; + KM_CORE_VKEY__3A = $3A; + KM_CORE_VKEY__3B = $3B; + KM_CORE_VKEY__3C = $3C; + KM_CORE_VKEY__3D = $3D; + KM_CORE_VKEY__3E = $3E; + KM_CORE_VKEY__3F = $3F; + KM_CORE_VKEY__40 = $40; + KM_CORE_VKEY_A = $41; + KM_CORE_VKEY_B = $42; + KM_CORE_VKEY_C = $43; + KM_CORE_VKEY_D = $44; + KM_CORE_VKEY_E = $45; + KM_CORE_VKEY_F = $46; + KM_CORE_VKEY_G = $47; + KM_CORE_VKEY_H = $48; + KM_CORE_VKEY_I = $49; + KM_CORE_VKEY_J = $4A; + KM_CORE_VKEY_K = $4B; + KM_CORE_VKEY_L = $4C; + KM_CORE_VKEY_M = $4D; + KM_CORE_VKEY_N = $4E; + KM_CORE_VKEY_O = $4F; + KM_CORE_VKEY_P = $50; + KM_CORE_VKEY_Q = $51; + KM_CORE_VKEY_R = $52; + KM_CORE_VKEY_S = $53; + KM_CORE_VKEY_T = $54; + KM_CORE_VKEY_U = $55; + KM_CORE_VKEY_V = $56; + KM_CORE_VKEY_W = $57; + KM_CORE_VKEY_X = $58; + KM_CORE_VKEY_Y = $59; + KM_CORE_VKEY_Z = $5A; + KM_CORE_VKEY__5B = $5B; + KM_CORE_VKEY__5C = $5C; + KM_CORE_VKEY__5D = $5D; + KM_CORE_VKEY__5E = $5E; + KM_CORE_VKEY__5F = $5F; + KM_CORE_VKEY_NP0 = $60; + KM_CORE_VKEY_NP1 = $61; + KM_CORE_VKEY_NP2 = $62; + KM_CORE_VKEY_NP3 = $63; + KM_CORE_VKEY_NP4 = $64; + KM_CORE_VKEY_NP5 = $65; + KM_CORE_VKEY_NP6 = $66; + KM_CORE_VKEY_NP7 = $67; + KM_CORE_VKEY_NP8 = $68; + KM_CORE_VKEY_NP9 = $69; + KM_CORE_VKEY_NPSTAR = $6A; + KM_CORE_VKEY_NPPLUS = $6B; + KM_CORE_VKEY_SEPARATOR = $6C; + KM_CORE_VKEY_NPMINUS = $6D; + KM_CORE_VKEY_NPDOT = $6E; + KM_CORE_VKEY_NPSLASH = $6F; + KM_CORE_VKEY_F1 = $70; + KM_CORE_VKEY_F2 = $71; + KM_CORE_VKEY_F3 = $72; + KM_CORE_VKEY_F4 = $73; + KM_CORE_VKEY_F5 = $74; + KM_CORE_VKEY_F6 = $75; + KM_CORE_VKEY_F7 = $76; + KM_CORE_VKEY_F8 = $77; + KM_CORE_VKEY_F9 = $78; + KM_CORE_VKEY_F10 = $79; + KM_CORE_VKEY_F11 = $7A; + KM_CORE_VKEY_F12 = $7B; + KM_CORE_VKEY_F13 = $7C; + KM_CORE_VKEY_F14 = $7D; + KM_CORE_VKEY_F15 = $7E; + KM_CORE_VKEY_F16 = $7F; + KM_CORE_VKEY_F17 = $80; + KM_CORE_VKEY_F18 = $81; + KM_CORE_VKEY_F19 = $82; + KM_CORE_VKEY_F20 = $83; + KM_CORE_VKEY_F21 = $84; + KM_CORE_VKEY_F22 = $85; + KM_CORE_VKEY_F23 = $86; + KM_CORE_VKEY_F24 = $87; + KM_CORE_VKEY__88 = $88; + KM_CORE_VKEY__89 = $89; + KM_CORE_VKEY__8A = $8A; + KM_CORE_VKEY__8B = $8B; + KM_CORE_VKEY__8C = $8C; + KM_CORE_VKEY__8D = $8D; + KM_CORE_VKEY__8E = $8E; + KM_CORE_VKEY__8F = $8F; + KM_CORE_VKEY_NUMLOCK = $90; + KM_CORE_VKEY_SCROLL = $91; + KM_CORE_VKEY__92 = $92; + KM_CORE_VKEY__93 = $93; + KM_CORE_VKEY__94 = $94; + KM_CORE_VKEY__95 = $95; + KM_CORE_VKEY__96 = $96; + KM_CORE_VKEY__97 = $97; + KM_CORE_VKEY__98 = $98; + KM_CORE_VKEY__99 = $99; + KM_CORE_VKEY__9A = $9A; + KM_CORE_VKEY__9B = $9B; + KM_CORE_VKEY__9C = $9C; + KM_CORE_VKEY__9D = $9D; + KM_CORE_VKEY__9E = $9E; + KM_CORE_VKEY__9F = $9F; + KM_CORE_VKEY__A0 = $A0; + KM_CORE_VKEY__A1 = $A1; + KM_CORE_VKEY__A2 = $A2; + KM_CORE_VKEY__A3 = $A3; + KM_CORE_VKEY__A4 = $A4; + KM_CORE_VKEY__A5 = $A5; + KM_CORE_VKEY__A6 = $A6; + KM_CORE_VKEY__A7 = $A7; + KM_CORE_VKEY__A8 = $A8; + KM_CORE_VKEY__A9 = $A9; + KM_CORE_VKEY__AA = $AA; + KM_CORE_VKEY__AB = $AB; + KM_CORE_VKEY__AC = $AC; + KM_CORE_VKEY__AD = $AD; + KM_CORE_VKEY__AE = $AE; + KM_CORE_VKEY__AF = $AF; + KM_CORE_VKEY__B0 = $B0; + KM_CORE_VKEY__B1 = $B1; + KM_CORE_VKEY__B2 = $B2; + KM_CORE_VKEY__B3 = $B3; + KM_CORE_VKEY__B4 = $B4; + KM_CORE_VKEY__B5 = $B5; + KM_CORE_VKEY__B6 = $B6; + KM_CORE_VKEY__B7 = $B7; + KM_CORE_VKEY__B8 = $B8; + KM_CORE_VKEY__B9 = $B9; + KM_CORE_VKEY_COLON = $BA; + KM_CORE_VKEY_EQUAL = $BB; + KM_CORE_VKEY_COMMA = $BC; + KM_CORE_VKEY_HYPHEN = $BD; + KM_CORE_VKEY_PERIOD = $BE; + KM_CORE_VKEY_SLASH = $BF; + KM_CORE_VKEY_BKQUOTE = $C0; + KM_CORE_VKEY__C1 = $C1; + KM_CORE_VKEY__C2 = $C2; + KM_CORE_VKEY__C3 = $C3; + KM_CORE_VKEY__C4 = $C4; + KM_CORE_VKEY__C5 = $C5; + KM_CORE_VKEY__C6 = $C6; + KM_CORE_VKEY__C7 = $C7; + KM_CORE_VKEY__C8 = $C8; + KM_CORE_VKEY__C9 = $C9; + KM_CORE_VKEY__CA = $CA; + KM_CORE_VKEY__CB = $CB; + KM_CORE_VKEY__CC = $CC; + KM_CORE_VKEY__CD = $CD; + KM_CORE_VKEY__CE = $CE; + KM_CORE_VKEY__CF = $CF; + KM_CORE_VKEY__D0 = $D0; + KM_CORE_VKEY__D1 = $D1; + KM_CORE_VKEY__D2 = $D2; + KM_CORE_VKEY__D3 = $D3; + KM_CORE_VKEY__D4 = $D4; + KM_CORE_VKEY__D5 = $D5; + KM_CORE_VKEY__D6 = $D6; + KM_CORE_VKEY__D7 = $D7; + KM_CORE_VKEY__D8 = $D8; + KM_CORE_VKEY__D9 = $D9; + KM_CORE_VKEY__DA = $DA; + KM_CORE_VKEY_LBRKT = $DB; + KM_CORE_VKEY_BKSLASH = $DC; + KM_CORE_VKEY_RBRKT = $DD; + KM_CORE_VKEY_QUOTE = $DE; + KM_CORE_VKEY_oDF = $DF; + KM_CORE_VKEY_oE0 = $E0; + KM_CORE_VKEY_oE1 = $E1; + KM_CORE_VKEY_oE2 = $E2; // 102nd key on European layouts + KM_CORE_VKEY_oE3 = $E3; + KM_CORE_VKEY_oE4 = $E4; + KM_CORE_VKEY__E5 = $E5; + KM_CORE_VKEY_oE6 = $E6; + KM_CORE_VKEY__E7 = $E7; + KM_CORE_VKEY__E8 = $E8; + KM_CORE_VKEY_oE9 = $E9; + KM_CORE_VKEY_oEA = $EA; + KM_CORE_VKEY_oEB = $EB; + KM_CORE_VKEY_oEC = $EC; + KM_CORE_VKEY_oED = $ED; + KM_CORE_VKEY_oEE = $EE; + KM_CORE_VKEY_oEF = $EF; + KM_CORE_VKEY_oF0 = $F0; + KM_CORE_VKEY_oF1 = $F1; + KM_CORE_VKEY_oF2 = $F2; + KM_CORE_VKEY_oF3 = $F3; + KM_CORE_VKEY_oF4 = $F4; + KM_CORE_VKEY_oF5 = $F5; + KM_CORE_VKEY__F6 = $F6; + KM_CORE_VKEY__F7 = $F7; + KM_CORE_VKEY__F8 = $F8; + KM_CORE_VKEY__F9 = $F9; + KM_CORE_VKEY__FA = $FA; + KM_CORE_VKEY__FB = $FB; + KM_CORE_VKEY__FC = $FC; + KM_CORE_VKEY__FD = $FD; + KM_CORE_VKEY__FE = $FE; + KM_CORE_VKEY__FF = $FF; + +// keyman_core_api_consts.h const // Defined environment options for KMX processor - KM_KBP_KMX_ENV_PLATFORM = 'platform'; - KM_KBP_KMX_ENV_BASELAYOUT = 'baseLayout'; - KM_KBP_KMX_ENV_BASELAYOUTALT = 'baseLayoutAlt'; - KM_KBP_KMX_ENV_SIMULATEALTGR = 'simulateAltgr'; - KM_KBP_KMX_ENV_CAPSLOCK = 'capsLock'; - KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT = 'baseLayoutGivesCtrlRAltForRAlt'; + KM_CORE_KMX_ENV_PLATFORM = 'platform'; + KM_CORE_KMX_ENV_BASELAYOUT = 'baseLayout'; + KM_CORE_KMX_ENV_BASELAYOUTALT = 'baseLayoutAlt'; + KM_CORE_KMX_ENV_SIMULATEALTGR = 'simulateAltgr'; + KM_CORE_KMX_ENV_CAPSLOCK = 'capsLock'; + KM_CORE_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT = 'baseLayoutGivesCtrlRAltForRAlt'; -procedure _km_kbp_set_library_path(const path: string); +procedure _km_core_set_library_path(const path: string); implementation @@ -638,7 +638,7 @@ implementation System.SysUtils, Winapi.Windows; -procedure _km_kbp_set_library_path(const path: string); +procedure _km_core_set_library_path(const path: string); begin if LoadLibrary(PChar(path)) = 0 then RaiseLastOSError; diff --git a/developer/src/tike/main/Keyman.System.KeymanCoreDebug.pas b/developer/src/tike/main/Keyman.System.KeymanCoreDebug.pas index a1f63e3b38e..7aced230fbf 100644 --- a/developer/src/tike/main/Keyman.System.KeymanCoreDebug.pas +++ b/developer/src/tike/main/Keyman.System.KeymanCoreDebug.pas @@ -1,7 +1,7 @@ { * Keyman is copyright (C) SIL International. MIT License. * - * Debug interface for Keyman Core, matches keyboardprocessor_debug.h + * Debug interface for Keyman Core, matches keyman_core_api_debug.h } unit Keyman.System.KeymanCoreDebug; @@ -14,7 +14,7 @@ interface {$ALIGN 8} /// -/// The maximum size of context in km_kbp_cp units for a single debug +/// The maximum size of context in km_core_cp units for a single debug /// event. This is taken from MAXCONTEXT in keyman32 (Windows) and is purely /// a convenience value. We can increase it if there is a demonstrated need. /// @@ -32,32 +32,32 @@ interface /// These modifier flags are used internally in the kmx engine, so will be /// exposed only in debugging modifier states. /// -const KM_KBP_MODIFIER_VIRTUALKEY = $4000; -const KM_KBP_MODIFIER_VIRTUALCHARKEY = $8000; +const KM_CORE_MODIFIER_VIRTUALKEY = $4000; +const KM_CORE_MODIFIER_VIRTUALCHARKEY = $8000; /// /// Input key event data. The `character` member is derived from /// a US English key event for vk + modifier_state, and is 0 if /// the vk + modifier_state do not generate a character. /// -/// Used only in event type KM_KBP_DEBUG_BEGIN. +/// Used only in event type KM_CORE_DEBUG_BEGIN. /// type -km_kbp_state_debug_key_info = record +km_core_state_debug_key_info = record vk: uint16_t; modifier_state: uint16_t; character: char16_t; end; -pkm_kbp_state_debug_key_info = ^km_kbp_state_debug_key_info; +pkm_core_state_debug_key_info = ^km_core_state_debug_key_info; -km_kbp_state_debug_kmx_option_info = record +km_core_state_debug_kmx_option_info = record store: Pointer; // LPSTORE - value: array[0..DEBUG_MAX_CONTEXT-1] of km_kbp_cp; // value to be saved into the store + value: array[0..DEBUG_MAX_CONTEXT-1] of km_core_cp; // value to be saved into the store end; -pkm_kbp_state_debug_kmx_option_info = ^km_kbp_state_debug_kmx_option_info; +pkm_core_state_debug_kmx_option_info = ^km_core_state_debug_kmx_option_info; /// /// KMX processor data for each event. kmx_base.h defines the types that are @@ -67,11 +67,11 @@ km_kbp_state_debug_kmx_option_info = record /// The context value here will be an intermediate value, and may differ /// from event to event as the context can be rewritten for each rule match. /// -/// Used in all event types except KM_KBP_DEBUG_BEGIN, KM_KBP_DEBUG_END. +/// Used in all event types except KM_CORE_DEBUG_BEGIN, KM_CORE_DEBUG_END. /// -km_kbp_state_debug_kmx_info = record - context: array [0..DEBUG_MAX_CONTEXT-1] of km_kbp_cp; // The context matched by the rule (? may not need this?) // TODO: rename to context_matched +km_core_state_debug_kmx_info = record + context: array [0..DEBUG_MAX_CONTEXT-1] of km_core_cp; // The context matched by the rule (? may not need this?) // TODO: rename to context_matched group: Pointer; // LPGROUP rule: Pointer; // LPKEY store_offsets: array [0..DEBUG_STORE_OFFSETS_SIZE-1] of uint16_t; // pairs--store, char position, terminated by 0xFFFF // TODO use a better structure here @@ -80,10 +80,10 @@ km_kbp_state_debug_kmx_info = record /// the debugger; the debugger uses this to determine when to /// execute the actions when single-stepping. first_action: uint16_t; - option: km_kbp_state_debug_kmx_option_info; + option: km_core_state_debug_kmx_option_info; end; -pkm_kbp_state_debug_kmx_info = ^km_kbp_state_debug_kmx_info; +pkm_core_state_debug_kmx_info = ^km_core_state_debug_kmx_info; /// /// A single debug event. @@ -91,50 +91,50 @@ km_kbp_state_debug_kmx_info = record /// {$ALIGN 8} -km_kbp_state_debug_item = record +km_core_state_debug_item = record _type: uint32_t; // TODO: _type as enum, 8 bit with padding? 32 bits is better optimized than 8 bits flags: uint32_t; - key_info: km_kbp_state_debug_key_info; - kmx_info: km_kbp_state_debug_kmx_info; + key_info: km_core_state_debug_key_info; + kmx_info: km_core_state_debug_kmx_info; end; -pkm_kbp_state_debug_item = ^km_kbp_state_debug_item; +pkm_core_state_debug_item = ^km_core_state_debug_item; /// /// A single debug event. /// -km_kbp_debug_type = type uint32_t; +km_core_debug_type = type uint32_t; // These types are used only for debugging convenience type - km_kbp_state_debug_item_array = array[0..100] of km_kbp_state_debug_item; - pkm_kbp_state_debug_item_array = ^km_kbp_state_debug_item_array; + km_core_state_debug_item_array = array[0..100] of km_core_state_debug_item; + pkm_core_state_debug_item_array = ^km_core_state_debug_item_array; const - KM_KBP_DEBUG_BEGIN = 0; - //KM_KBP_DEBUG_BEGIN_ANSI: km_kbp_debug_type = 1, // not supported; instead rewrite ansi keyboards to Unicode with mcompile - KM_KBP_DEBUG_GROUP_ENTER = 2; - KM_KBP_DEBUG_GROUP_EXIT = 3; - KM_KBP_DEBUG_RULE_ENTER = 4; - KM_KBP_DEBUG_RULE_EXIT = 5; - KM_KBP_DEBUG_MATCH_ENTER = 6; - KM_KBP_DEBUG_MATCH_EXIT = 7; - KM_KBP_DEBUG_NOMATCH_ENTER = 8; - KM_KBP_DEBUG_NOMATCH_EXIT = 9; - KM_KBP_DEBUG_END = 10; - KM_KBP_DEBUG_SET_OPTION = 11; - -// Flags for KM_KBP_DEBUG_GROUP_EXIT -const KM_KBP_DEBUG_FLAG_RECURSIVE_OVERFLOW = $0001; -const KM_KBP_DEBUG_FLAG_NOMATCH = $0002; - -// Flags for KM_KBP_DEBUG_BEGIN + KM_CORE_DEBUG_BEGIN = 0; + //KM_CORE_DEBUG_BEGIN_ANSI: km_core_debug_type = 1, // not supported; instead rewrite ansi keyboards to Unicode with mcompile + KM_CORE_DEBUG_GROUP_ENTER = 2; + KM_CORE_DEBUG_GROUP_EXIT = 3; + KM_CORE_DEBUG_RULE_ENTER = 4; + KM_CORE_DEBUG_RULE_EXIT = 5; + KM_CORE_DEBUG_MATCH_ENTER = 6; + KM_CORE_DEBUG_MATCH_EXIT = 7; + KM_CORE_DEBUG_NOMATCH_ENTER = 8; + KM_CORE_DEBUG_NOMATCH_EXIT = 9; + KM_CORE_DEBUG_END = 10; + KM_CORE_DEBUG_SET_OPTION = 11; + +// Flags for KM_CORE_DEBUG_GROUP_EXIT +const KM_CORE_DEBUG_FLAG_RECURSIVE_OVERFLOW = $0001; +const KM_CORE_DEBUG_FLAG_NOMATCH = $0002; + +// Flags for KM_CORE_DEBUG_BEGIN // TODO: do we need this at all? -const KM_KBP_DEBUG_FLAG_UNICODE = $0001; // Always set +const KM_CORE_DEBUG_FLAG_UNICODE = $0001; // Always set -// Flags for KM_KBP_DEBUG_END -const KM_KBP_DEBUG_FLAG_OUTPUTKEYSTROKE = $0001; +// Flags for KM_CORE_DEBUG_END +const KM_CORE_DEBUG_FLAG_OUTPUTKEYSTROKE = $0001; /// /// Enable or disable debug tracing @@ -142,12 +142,12 @@ km_kbp_state_debug_item = record /// @param state Pointer to initialized state /// @param value Set to 1 to enable debugging, 0 to disable /// -/// @returns KM_KBP_STATUS_OK on success +/// @returns KM_CORE_STATUS_OK on success /// -function km_kbp_state_debug_set( - state: pkm_kbp_state; +function km_core_state_debug_set( + state: pkm_core_state; value: integer -): km_kbp_status; cdecl; external kmnkbp0 delayed; +): km_core_status; cdecl; external kmnkbp0 delayed; /// /// Get current debug tracing status @@ -156,8 +156,8 @@ function km_kbp_state_debug_set( /// /// @returns 1 if debugging is enabled, 0 otherwise /// -function km_kbp_state_debug_get( - state: pkm_kbp_state +function km_core_state_debug_get( + state: pkm_core_state ): uint8_t; cdecl; external kmnkbp0 delayed; /// @@ -168,12 +168,12 @@ function km_kbp_state_debug_get( /// nullptr if not required. /// /// @returns pointer to read only array of debug item events, -/// with last entry guaranteed to be KM_KBP_DEBUG_END. +/// with last entry guaranteed to be KM_CORE_DEBUG_END. /// -function km_kbp_state_debug_items( - state: pkm_kbp_state; +function km_core_state_debug_items( + state: pkm_core_state; num_items: PCardinal -): pkm_kbp_state_debug_item; cdecl; external kmnkbp0 delayed; +): pkm_core_state_debug_item; cdecl; external kmnkbp0 delayed; implementation @@ -181,18 +181,18 @@ initialization // Assertions generated by debug_api.cpp for win32 x86. // Usage: debug-api.exe --print-sizeof - //keyboardprocessor.h: - Assert(sizeof(km_kbp_context_item) = 8); - Assert(sizeof(km_kbp_context_item) = 8); - Assert(sizeof(km_kbp_action_item) = 12); - Assert(sizeof(km_kbp_option_item) = 12); - Assert(sizeof(km_kbp_keyboard_attrs) = 16); - Assert(sizeof(km_kbp_attr) = 16); - - //keyboardprocessor_debug.h: - Assert(sizeof(km_kbp_state_debug_item) = 432); - Assert(sizeof(km_kbp_state_debug_key_info) = 6); - Assert(sizeof(km_kbp_state_debug_kmx_info) = 416); - Assert(sizeof(km_kbp_state_debug_kmx_option_info) = 164); - Assert(sizeof(km_kbp_debug_type) = 4); + //keyman_core_api.h: + Assert(sizeof(km_core_context_item) = 8); + Assert(sizeof(km_core_context_item) = 8); + Assert(sizeof(km_core_action_item) = 12); + Assert(sizeof(km_core_option_item) = 12); + Assert(sizeof(km_core_keyboard_attrs) = 16); + Assert(sizeof(km_core_attr) = 16); + + //keyman_core_api_debug.h: + Assert(sizeof(km_core_state_debug_item) = 432); + Assert(sizeof(km_core_state_debug_key_info) = 6); + Assert(sizeof(km_core_state_debug_kmx_info) = 416); + Assert(sizeof(km_core_state_debug_kmx_option_info) = 164); + Assert(sizeof(km_core_debug_type) = 4); end. diff --git a/developer/src/tike/main/Keyman.System.VisualKeyboardImportKMX.pas b/developer/src/tike/main/Keyman.System.VisualKeyboardImportKMX.pas index e36216e1559..471162eded3 100644 --- a/developer/src/tike/main/Keyman.System.VisualKeyboardImportKMX.pas +++ b/developer/src/tike/main/Keyman.System.VisualKeyboardImportKMX.pas @@ -106,11 +106,11 @@ procedure TVisualKeyboardImportKMX.ImportKey(vk: TVKKey); var data: string; i: Integer; - context: pkm_kbp_context; + context: pkm_core_context; begin - context := km_kbp_state_context(FCore.State); - km_kbp_context_clear(context); - if km_kbp_process_event(FCore.State, vk.vkey, vk.kmshift, 1, KM_KBP_EVENT_FLAG_DEFAULT) = KM_KBP_STATUS_OK then + context := km_core_state_context(FCore.State); + km_core_context_clear(context); + if km_core_process_event(FCore.State, vk.vkey, vk.kmshift, 1, KM_CORE_EVENT_FLAG_DEFAULT) = KM_CORE_STATUS_OK then begin FEvents.Clear; FEvents.AddStateItems(FCore.State, vk.vkey, vk.kmshift); @@ -124,9 +124,9 @@ procedure TVisualKeyboardImportKMX.ImportKey(vk: TVKKey); end; case FEvents[i].Action.ActionType of - KM_KBP_IT_CHAR: + KM_CORE_IT_CHAR: data := data + FEvents[i].Action.Text; - KM_KBP_IT_BACK: + KM_CORE_IT_BACK: if data.Length > 0 then begin if (data.Length > 1) and @@ -201,7 +201,7 @@ procedure TVisualKeyboardImportKMX.Validate102Key; FShow102Key := False; for i := 0 to keys.Count - 1 do - if TVKKey(keys[i]).vkey = KM_KBP_VKEY_oE2 then + if TVKKey(keys[i]).vkey = KM_CORE_VKEY_oE2 then begin FShow102Key := True; Exit; diff --git a/developer/src/tike/main/RedistFiles.pas b/developer/src/tike/main/RedistFiles.pas index b02c956679e..1e22bb06ef8 100644 --- a/developer/src/tike/main/RedistFiles.pas +++ b/developer/src/tike/main/RedistFiles.pas @@ -63,7 +63,6 @@ function GetRedistSetupPath: string; function GetRedistProjectTemplatePath: string; function GetWixPath: string; function GetStockKCTPath: string; -function GetDebugKMCmpDllPath: string; function GetUnicodeDataSourcePath(DefaultPath: string = ''): string; // I3463 function GetXMLTemplatePath: string; function GetDeveloperRootPath: string; @@ -135,24 +134,6 @@ function GetRedistProjectTemplatePath: string; Result := GetDebugPath('Debug_RedistTemplatePath', Result); end; -function GetDebugKMCmpDllPath: string; -var - root: string; -const - DevPlatform={$IFDEF WIN64}'x64'{$ELSE}'Win32'{$ENDIF}; - DevConfig={$IFDEF DEBUG}'Debug'{$ELSE}'Release'{$ENDIF}; - DevSrcCompilerPath = 'developer\src\kmcmpdll\bin\'+DevPlatform+'\'+DevConfig; - DevCompilerPath = 'developer\bin'; -begin - if TKeymanPaths.RunningFromSource(root) and FileExists(root + DevSrcCompilerPath + '\kmcmpdll.dll') then - Result := root + DevSrcCompilerPath - else if TKeymanPaths.RunningFromSource(root) and FileExists(root + DevCompilerPath + '\kmcmpdll.dll') then - Result := root + DevCompilerPath - else - Result := ExtractFilePath(ParamStr(0)); - Result := GetDebugPath('Debug_KMCMPDLLPath', Result); -end; - function GetXMLTemplatePath: string; var root: string; diff --git a/developer/src/tike/main/UfrmMain.dfm b/developer/src/tike/main/UfrmMain.dfm index 0b88243caf9..6339840fac8 100644 --- a/developer/src/tike/main/UfrmMain.dfm +++ b/developer/src/tike/main/UfrmMain.dfm @@ -3127,10 +3127,6 @@ inherited frmKeymanDeveloper: TfrmKeymanDeveloper Caption = '&Exception Test' OnClick = mnuToolsDebugTestsExceptionTestClick end - object mnuToolsDebugTestsCompilerExceptionTest: TMenuItem - Caption = '&Compiler Exception Test' - OnClick = mnuToolsDebugTestsCompilerExceptionTestClick - end end end object Help1: TMenuItem diff --git a/developer/src/tike/main/UfrmMain.pas b/developer/src/tike/main/UfrmMain.pas index deff7780a74..4b8a7c235fa 100644 --- a/developer/src/tike/main/UfrmMain.pas +++ b/developer/src/tike/main/UfrmMain.pas @@ -272,7 +272,6 @@ TfrmKeymanDeveloper = class(TTikeForm, IUnicodeDataUIManager, IDragDrop) CompileModel1: TMenuItem; N2: TMenuItem; estLexicalModel1: TMenuItem; - mnuToolsDebugTestsCompilerExceptionTest: TMenuItem; mnuToolsDebugTestsShowDebuggerEventsPanel: TMenuItem; N3: TMenuItem; N4: TMenuItem; @@ -302,7 +301,6 @@ TfrmKeymanDeveloper = class(TTikeForm, IUnicodeDataUIManager, IDragDrop) procedure pagesChange(Sender: TObject); procedure pagesCloseTab(Sender: TObject; Index: Integer); procedure mnuToolsClick(Sender: TObject); - procedure mnuToolsDebugTestsCompilerExceptionTestClick(Sender: TObject); procedure mnuToolsDebugTestsShowDebuggerEventsPanelClick(Sender: TObject); procedure ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean); @@ -1437,12 +1435,6 @@ procedure TfrmKeymanDeveloper.cbTextFileFormatItemClick(Sender: TObject); modActionsMain.actToolsFileFormat.Execute; end; -procedure TfrmKeymanDeveloper.mnuToolsDebugTestsCompilerExceptionTestClick( - Sender: TObject); -begin - Compiler_Diagnostic(0); -end; - procedure TfrmKeymanDeveloper.mnuToolsDebugTestsExceptionTestClick(Sender: TObject); begin TKeymanSentryClient.Validate(True); diff --git a/developer/src/tike/main/UfrmMessages.pas b/developer/src/tike/main/UfrmMessages.pas index be0e38a90a4..9db9586855d 100644 --- a/developer/src/tike/main/UfrmMessages.pas +++ b/developer/src/tike/main/UfrmMessages.pas @@ -188,7 +188,7 @@ procedure TfrmMessages.memoMessageDblClick(Sender: TObject); begin pf := FGlobalProject.FindFile(FFileName); if Assigned(pf) then (pf.UI as TProjectFileUI).DefaultEvent(Self) // I4687 - else frmKeymanDeveloper.OpenFile(FFileName, False); + else if FileExists(FFileName) then frmKeymanDeveloper.OpenFile(FFileName, False); frm := frmKeymanDeveloper.FindEditorByFileName(FFileName); if not Assigned(frm) then begin diff --git a/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFile.pas b/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFile.pas index 933d6282ba3..84fd6f6ebc9 100644 --- a/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFile.pas +++ b/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFile.pas @@ -1096,7 +1096,7 @@ function TProject.FindFile(AFileName: string): TProjectFile; i: Integer; begin for i := 0 to Files.Count - 1 do - if Files[i].FileName = AFileName then + if SameText(Files[i].FileName, AFileName) then begin Result := Files[i]; Exit; diff --git a/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFiles.pas b/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFiles.pas index 87db87850ed..2f79dccbe38 100644 --- a/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFiles.pas +++ b/developer/src/tike/project/Keyman.Developer.System.Project.ProjectFiles.pas @@ -1,18 +1,18 @@ (* Name: Keyman.Developer.System.Project.ProjectFiles Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 1 Aug 2006 Modified Date: 4 May 2015 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 01 Aug 2006 - mcdurdin - Initial version 04 Dec 2006 - mcdurdin - Test if file is open - WindowOpen 16 Jan 2009 - mcdurdin - I1769 - Add support for HTM, HTLM, XML to open for edit in Keyman Developer @@ -29,8 +29,6 @@ interface Keyman.Developer.System.Project.ProjectFile; type - TProjectFileActionCompileSuccessEvent = procedure(Sender: TObject; const SourceFilename, DestFilename: string) of object; - TShellProjectFile = class(TProjectFile) protected function GetRelativeOrder: Integer; override; @@ -38,12 +36,8 @@ TShellProjectFile = class(TProjectFile) end; TOpenableProjectFile = class(TShellProjectFile) - private - class var FOnCompileSuccess: TProjectFileActionCompileSuccessEvent; protected function GetRelativeOrder: Integer; override; - public - class property OnCompileSuccess: TProjectFileActionCompileSuccessEvent read FOnCompileSuccess write FOnCompileSuccess; end; implementation @@ -71,7 +65,6 @@ initialization RegisterProjectFileType('.ico', TOpenableProjectFile); RegisterProjectFileType('.txt', TOpenableProjectFile); RegisterProjectFileType('.md', TOpenableProjectFile); - RegisterProjectFileType('.keyboard_info', TOpenableProjectFile); RegisterProjectFileType('.model_info', TOpenableProjectFile); RegisterProjectFileType('.htm', TOpenableProjectFile); // I1769 RegisterProjectFileType('.html', TOpenableProjectFile); // I1769 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 2b6dea94ec5..ce952d979ee 100644 --- a/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas +++ b/developer/src/tike/project/Keyman.Developer.System.Project.kmnProjectFileAction.pas @@ -14,13 +14,10 @@ interface type TkmnProjectFileAction = class(TkmnProjectFile) private - function CompileVisualKeyboard(const AKVKSourceFile, AKVKTargetFile: string): Boolean; procedure CheckFilenameConventions; public function CompileKeyboard: Boolean; function Clean: Boolean; - function DoSetCompilerOptions(addVersion, - useLegacyCompiler: Boolean): Boolean; end; implementation @@ -31,41 +28,10 @@ implementation System.Variants, Winapi.Windows, - CompileKeymanWeb, compile, Keyman.Developer.System.Project.ProjectLog, Keyman.System.KeyboardUtils, - VisualKeyboard; - -function TkmnProjectFileAction.CompileVisualKeyboard(const AKVKSourceFile, AKVKTargetFile: string): Boolean; -begin - with TVisualKeyboard.Create do - try - try - LoadFromFile(AKVKSourceFile); - except - on E:Exception do - begin - OwnerProject.Log(plsError, AKVKSourceFile, 'Invalid visual keyboard: '+E.Message, CERR_ERROR, 0); - Exit(False); - end; - end; - if not SameFileName(AKVKSourceFile, AKVKTargetFile) then - try - Header.AssociatedKeyboard := ChangeFileExt(ExtractFileName(Self.FileName), ''); - SaveToFile(AKVKTargetFile, kvksfBinary); - except - on E:Exception do - begin - OwnerProject.Log(plsError, AKVKSourceFile, 'Could not save visual keyboard '+AKVKSourceFile+' to '+AKVKTargetFile+': '+E.ClassName+','+E.Message, CERR_ERROR, 0); - Exit(False); - end; - end; - finally - Free; - end; - Result := True; -end; + Keyman.Developer.System.KmcWrapper; function TkmnProjectFileAction.Clean: Boolean; var @@ -80,8 +46,6 @@ function TkmnProjectFileAction.Clean: Boolean; begin FJS := TKeyboardUtils.GetKeymanWebCompiledFileName(FileName); CleanFile(FJS); // keyboard-x.y.js - CleanFile(ChangeFileExt(FJS, '') + '_load.js'); // keyboard-x.y_load.js - CleanFile(ChangeFileExt(FJS, '.json'), True); // keyboard-x.y_load.js end; Result := True; @@ -108,99 +72,15 @@ procedure TkmnProjectFileAction.CheckFilenameConventions; end; end; -function TkmnProjectFileAction.DoSetCompilerOptions(addVersion: Boolean; useLegacyCompiler: Boolean): Boolean; -var - options: COMPILER_OPTIONS; -begin - TProject.CompilerMessageFile := Self; - options.dwSize := sizeof(COMPILER_OPTIONS); - options.ShouldAddCompilerVersion := addVersion; - // 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); - Exit(False); - end; - Result := True; -end; - function TkmnProjectFileAction.CompileKeyboard: Boolean; var - KMXFileName: String; - FOutFileName: string; - ckw: TCompileKeymanWeb; - FKVKSourceFile: string; - FKVKTargetFile: string; - TargetNames: string; + w: TKmcWrapper; begin - TProject.CompilerMessageFile := Self; - HasCompileWarning := False; // I4706 - - FKVKSourceFile := ExtractFilePath(FileName) + ExtractFileName(KVKFileName); - FKVKTargetFile := OwnerProject.GetTargetFileName(ChangeFileExt(FKVKSourceFile, '.kvk'), FileName, FileVersion); - + w := TKmcWrapper.Create; try - CheckFilenameConventions; - - if Targets * KMXKeymanTargets <> [] then - begin - TargetNames := KeymanTargetsToNames(Targets * KMXKeymanTargets); - Log(plsInfo, Format('Compiling ''%s'' %sfor %s...', [Filename, IfThen(Debug, 'with debug symbols ', ''), TargetNames]), 0, 0); - - //compile the keyboard - KMXFileName := TargetFileName; - ForceDirectories(ExtractFileDir(KMXFileName)); - //KMXFileName := ChangeFileExt(FileName, '.kmx'); - Result := CompileKeyboardFile(PChar(FileName), PChar(KMXFileName), Debug, - OwnerProject.Options.CompilerWarningsAsErrors, OwnerProject.Options.WarnDeprecatedCode, // I4865 // I4866 - ProjectCompilerMessage) > 0; - - if Result then - begin - if KVKFileName <> '' then - Result := CompileVisualKeyboard(FKVKSourceFile, FKVKTargetFile); - end; - - if HasCompileWarning and (WarnAsError or OwnerProject.Options.CompilerWarningsAsErrors) then Result := False; // I4706 - - if Result - then Log(plsSuccess, Format('''%s'' was compiled successfully for %s to ''%s''.', [FileName, TargetNames, KMXFileName]), 0, 0) // I4504 - else Log(plsFailure, Format('''%s'' was not compiled successfully for %s.', [FileName, TargetNames]), 0, 0); // I4504 - end - else - Result := True; // I4564 - - // compile keyboard to web - if Result and (Targets * KMWKeymanTargets <> []) then - begin - TargetNames := KeymanTargetsToNames(Targets * KMWKeymanTargets); - Log(plsInfo, Format('Compiling ''%s'' %sfor %s...', [Filename, IfThen(Debug, 'with debug symbols ', ''), TargetNames]), 0, 0); - - ckw := TCompileKeymanWeb.Create; - try - FOutFilename := JSTargetFileName; - ForceDirectories(ExtractFileDir(FOutFileName)); - - if KVKFileName <> '' then - Result := CompileVisualKeyboard(FKVKSourceFile, FKVKTargetFile); - - if Result then - Result := ckw.Compile(OwnerProject, FileName, FOutFileName, Debug, ProjectCompilerMessageW); // I3681 // I4140 // I4865 // I4866 - - if HasCompileWarning and (WarnAsError or OwnerProject.Options.CompilerWarningsAsErrors) then Result := False; // I4706 - - if Result - then Log(plsSuccess, Format('''%s'' was compiled successfully for %s to ''%s''.', [FileName, TargetNames, FOutFileName]), 0, 0) // I4504 - else Log(plsFailure, Format('''%s'' was not compiled successfully for %s.', [FileName, TargetNames]), 0, 0); // I4504 - - if Result and Assigned(OnCompileSuccess) then - OnCompileSuccess(Self, FileName, FOutFileName); - finally - ckw.Free; - end; - end; + Result := w.Compile(Self, FileName, TargetFilename, Debug); finally - TProject.CompilerMessageFile := nil; + w.Free; end; end; diff --git a/developer/src/tike/project/Keyman.Developer.System.Project.kpsProjectFileAction.pas b/developer/src/tike/project/Keyman.Developer.System.Project.kpsProjectFileAction.pas index 5a88a47922b..c7a7cafcc5c 100644 --- a/developer/src/tike/project/Keyman.Developer.System.Project.kpsProjectFileAction.pas +++ b/developer/src/tike/project/Keyman.Developer.System.Project.kpsProjectFileAction.pas @@ -10,119 +10,28 @@ interface Keyman.Developer.System.Project.ProjectFiles, Keyman.Developer.System.Project.ProjectFileType, Keyman.Developer.System.Project.ProjectLog, + Keyman.Developer.System.KmcWrapper, KPSFile; type TkpsProjectFileAction = class(TkpsProjectFile) - private - procedure SelfMessage(Sender: TObject; msg: string; State: TProjectLogState); // I4706 - public - function CompilePackageInstaller(APack: TKPSFile; FSilent: Boolean): Boolean; - function CompilePackage(APack: TKPSFile; FSilent: Boolean): Boolean; + function CompilePackage: Boolean; function Clean: Boolean; end; implementation -uses - compile, - CompilePackage, - CompilePackageInstaller, - Keyman.Developer.System.ValidateKpsFile, - PackageInfo, - utilexecute; - -function TkpsProjectFileAction.CompilePackage(APack: TKPSFile; FSilent: Boolean): Boolean; -var - pack: TKPSFile; - ASchemaPath: string; -begin - HasCompileWarning := False; // I4706 - if APack = nil then - begin - pack := TKPSFile.Create; - pack.FileName := FileName; - pack.LoadXML; - end - else - pack := APack; - - ASchemaPath := ExtractFilePath(ParamStr(0)); - if (ASchemaPath <> '') and (FileExists(ASchemaPath + 'kps.xsd')) then - begin - if not TValidateKpsFile.Execute(FileName, ASchemaPath + 'kps.xsd', OwnerProject.Log) then - begin - Log(plsFailure, 'Package '+FileName+' had validation errors.', 0, 0); - Exit(False); - end; - end; - - try - try - Result := DoCompilePackage(pack, SelfMessage, FSilent, OwnerProject.Options.CheckFilenameConventions, TargetFilename); - if HasCompileWarning and (WarnAsError or OwnerProject.Options.CompilerWarningsAsErrors) then // I4706 - Result := False; - - if Result then - Log(plsSuccess, '''' + FileName + ''' compiled successfully to '''+TargetFileName+'''.', 0, 0) - else - begin - if FileExists(TargetFilename) then - System.SysUtils.DeleteFile(TargetFilename); - Log(plsFailure, '''' + FileName + ''' was not compiled successfully.', 0, 0); - end; - except - on E:Exception do - begin - Log(plsError, E.Message, CERR_Error, 0); - Log(plsFailure, '''' + FileName + ''' was not compiled successfully.', 0, 0); - Result := False; - end; - end; - - finally - if APack = nil then - pack.Free; - end; -end; - -function TkpsProjectFileAction.CompilePackageInstaller(APack: TKPSFile; FSilent: Boolean): Boolean; +function TkpsProjectFileAction.CompilePackage: Boolean; var - pack: TKPSFile; + w: TKmcWrapper; begin - HasCompileWarning := False; // I4706 - - if APack = nil then - begin - pack := TKPSFile.Create; - pack.FileName := FileName; - pack.LoadXML; - end - else - pack := APack; - + w := TKmcWrapper.Create; try - try - Result := DoCompilePackageInstaller(pack, SelfMessage, FSilent, '', TargetInstallerFilename, '', False, True, '', '', '', False, False); - if HasCompileWarning and (WarnAsError or OwnerProject.Options.CompilerWarningsAsErrors) then // I4706 - Result := False; - - if Result - then Log(plsSuccess, '''' + FileName + ''' compiled successfully.', 0, 0) - else Log(plsFailure, '''' + FileName + ''' was not compiled successfully.', 0, 0); - except - on E:Exception do - begin - Log(plsError, E.Message, CERR_ERROR, 0); - Log(plsFailure, '''' + FileName + ''' was not compiled successfully.', 0, 0); - Result := False; - end; - end; - + Result := w.Compile(Self, FileName, TargetFilename, False); + // TODO(lowpri): FDebug flag finally - if APack = nil then - pack.Free; + w.Free; end; end; @@ -133,13 +42,6 @@ function TkpsProjectFileAction.Clean: Boolean; Result := True; end; -procedure TkpsProjectFileAction.SelfMessage(Sender: TObject; msg: string; State: TProjectLogState); // I4706 -begin - if State = plsWarning then - HasCompileWarning := True; - Log(State, msg, 0, 0); -end; - initialization RegisterProjectFileType('.kps', TkpsProjectFileAction); end. diff --git a/developer/src/tike/project/Keyman.Developer.System.Project.modelTsProjectFileAction.pas b/developer/src/tike/project/Keyman.Developer.System.Project.modelTsProjectFileAction.pas index 7534a6526f8..399b127aa8c 100644 --- a/developer/src/tike/project/Keyman.Developer.System.Project.modelTsProjectFileAction.pas +++ b/developer/src/tike/project/Keyman.Developer.System.Project.modelTsProjectFileAction.pas @@ -29,7 +29,6 @@ implementation System.Variants, Winapi.Windows, - CompileKeymanWeb, compile, Keyman.System.LexicalModelUtils, Keyman.Developer.System.LexicalModelCompile, diff --git a/developer/src/tike/project/Keyman.Developer.UI.Project.ProjectFileUI.pas b/developer/src/tike/project/Keyman.Developer.UI.Project.ProjectFileUI.pas index 50321c6bf4c..6026fb2377e 100644 --- a/developer/src/tike/project/Keyman.Developer.UI.Project.ProjectFileUI.pas +++ b/developer/src/tike/project/Keyman.Developer.UI.Project.ProjectFileUI.pas @@ -34,7 +34,7 @@ interface type TProjectFileAction = (pfaCompile, pfaInstall, pfaUninstall, pfaDebug, - pfaTestKeymanWeb, pfaCompileInstaller, pfaFontHelper, pfaFontDialog, pfaClean); // I4057 + pfaTestKeymanWeb, pfaFontHelper, pfaFontDialog, pfaClean); // I4057 TProjectUI = class(TProject) private diff --git a/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProject.pas b/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProject.pas index ad0fc42ec32..992a4491c36 100644 --- a/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProject.pas +++ b/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProject.pas @@ -549,21 +549,6 @@ procedure TfrmProject.WebCommandProject(Command: WideString; Params: TStringList (FGlobalProject.Files[i].UI as TProjectFileUI).DoAction(pfaCompile, False); end; end - else if Command = 'package_compileallinstallers' then // I4734 - begin - ClearMessages; - for i := 0 to FGlobalProject.Files.Count - 1 do - begin - if FGlobalProject.Files[i] is TkpsProjectFile then - (FGlobalProject.Files[i].UI as TProjectFileUI).DoAction(pfaCompileInstaller, False); - end; - end - else if Command = 'package_compileinstaller' then - begin - ClearMessages; - pf := SelectedProjectFile; - if Assigned(pf) then (pf.UI as TProjectFileUI).DoAction(pfaCompileInstaller, False); - end else if Command = 'package_cleanall' then // I4692 begin ClearMessages; diff --git a/developer/src/tike/project/Keyman.Developer.UI.Project.kmnProjectFileUI.pas b/developer/src/tike/project/Keyman.Developer.UI.Project.kmnProjectFileUI.pas index 72c9b43fd47..24cfdd9e884 100644 --- a/developer/src/tike/project/Keyman.Developer.UI.Project.kmnProjectFileUI.pas +++ b/developer/src/tike/project/Keyman.Developer.UI.Project.kmnProjectFileUI.pas @@ -131,9 +131,6 @@ function TkmnProjectFileUI.CompileKeyboard(FSilent: Boolean): Boolean; frmMessages.DoShowForm; Result := - // Note: we do not surface the ability to exclude version information from - // the TIKE compiler; this must be done from kmcomp.exe. - ProjectFile.DoSetCompilerOptions(True, FKeymanDeveloperOptions.UseLegacyCompiler) and ProjectFile.CompileKeyboard; if Result and diff --git a/developer/src/tike/project/Keyman.Developer.UI.Project.kpsProjectFileUI.pas b/developer/src/tike/project/Keyman.Developer.UI.Project.kpsProjectFileUI.pas index da743acd67f..410a2727252 100644 --- a/developer/src/tike/project/Keyman.Developer.UI.Project.kpsProjectFileUI.pas +++ b/developer/src/tike/project/Keyman.Developer.UI.Project.kpsProjectFileUI.pas @@ -34,12 +34,10 @@ TkpsProjectFileUI = class(TOpenableProjectFileUI) function TestPackageOnline: Boolean; function InstallPackage: Boolean; function UninstallPackage: Boolean; - function CompilePackage(FSilent: Boolean): Boolean; - function CompilePackageInstaller(FSilent: Boolean): Boolean; + function CompilePackage: Boolean; - function GetPack: TKPSFile; function GetProjectFile: TkpsProjectFileAction; - function TestPackageState(FCompiledName: string; FSilent: Boolean): Boolean; + function TestPackageState(FCompiledName: string): Boolean; public function DoAction(action: TProjectFileAction; FSilent: Boolean): Boolean; override; property ProjectFile: TkpsProjectFileAction read GetProjectFile; @@ -69,13 +67,13 @@ implementation KeymanDeveloperUtils, PackageInfo; -function TkpsProjectFileUI.CompilePackage(FSilent: Boolean): Boolean; +function TkpsProjectFileUI.CompilePackage: Boolean; begin Result := False; if ProjectFile.Modified then if not modActionsMain.actFileSave.Execute then Exit; - Result := ProjectFile.CompilePackage(GetPack, FSilent); + Result := ProjectFile.CompilePackage; if Result and TServerDebugAPI.Running and @@ -83,22 +81,12 @@ function TkpsProjectFileUI.CompilePackage(FSilent: Boolean): Boolean; TestPackageOnline; end; -function TkpsProjectFileUI.CompilePackageInstaller(FSilent: Boolean): Boolean; -begin - Result := False; - if ProjectFile.Modified then - if not modActionsMain.actFileSave.Execute then Exit; - - Result := ProjectFile.CompilePackageInstaller(GetPack, FSilent); -end; - function TkpsProjectFileUI.DoAction(action: TProjectFileAction; FSilent: Boolean): Boolean; begin case action of - pfaCompile: Result := CompilePackage(FSilent); + pfaCompile: Result := CompilePackage; pfaInstall: Result := InstallPackage; pfaUninstall: Result := UninstallPackage; - pfaCompileInstaller: Result := CompilePackageInstaller(FSilent); pfaClean: Result := ProjectFile.Clean; pfaTestKeymanWeb: Result := TestPackageOnline; else @@ -106,13 +94,6 @@ function TkpsProjectFileUI.DoAction(action: TProjectFileAction; FSilent: Boolean end; end; -function TkpsProjectFileUI.GetPack: TKPSFile; -begin - if Assigned(MDIChild) - then with MDIChild as TfrmPackageEditor do Result := GetPack - else Result := nil; -end; - function TkpsProjectFileUI.GetProjectFile: TkpsProjectFileAction; begin Result := FOwner as TkpsProjectFileAction; @@ -124,7 +105,7 @@ function TkpsProjectFileUI.InstallPackage: Boolean; begin Result := False; FCompiledName := ProjectFile.TargetFilename; - if not TestPackageState(FCompiledName, False) then Exit; + if not TestPackageState(FCompiledName) then Exit; KeymanDeveloperUtils.InstallPackage(FCompiledName, True); Result := True; end; @@ -158,39 +139,29 @@ function TkpsProjectFileUI.UninstallPackage: Boolean; Result := KeymanDeveloperUtils.UninstallPackage(ChangeFileExt(ExtractFileName(ProjectFile.FileName), '')); end; -function TkpsProjectFileUI.TestPackageState(FCompiledName: string; FSilent: Boolean): Boolean; +function TkpsProjectFileUI.TestPackageState(FCompiledName: string): Boolean; var ftkps, ftkmp: TDateTime; begin Result := False; if not FileExists(FCompiledName) then - if FSilent then - begin - if not CompilePackage(FSilent) then Exit; - end - else - case MessageDlg('You need to compile the keyboard before you can test it. Compile now?', - mtConfirmation, mbOkCancel, 0) of - mrOk: if not CompilePackage(FSilent) then Exit; - mrCancel: Exit; - end; + case MessageDlg('You need to compile the keyboard before you can test it. Compile now?', + mtConfirmation, mbOkCancel, 0) of + mrOk: if not CompilePackage then Exit; + mrCancel: Exit; + end; FileAge(ProjectFile.FileName, ftkps); FileAge(FCompiledName, ftkmp); if ProjectFile.Modified or (ftkps > ftkmp) then - if FSilent then - begin - if not CompilePackage(FSilent) then Exit; - end - else - case MessageDlg('The source file has changed. Recompile before testing?', - mtConfirmation, mbYesNoCancel, 0) of - mrYes: if not CompilePackage(FSilent) then Exit; - mrNo: ; - mrCancel: Exit; - end; + case MessageDlg('The source file has changed. Recompile before testing?', + mtConfirmation, mbYesNoCancel, 0) of + mrYes: if not CompilePackage then Exit; + mrNo: ; + mrCancel: Exit; + end; Result := True; end; diff --git a/developer/src/tike/tests/Makefile b/developer/src/tike/tests/Makefile index a93126f3ab1..245e1bd6092 100644 --- a/developer/src/tike/tests/Makefile +++ b/developer/src/tike/tests/Makefile @@ -2,10 +2,10 @@ !include ..\..\Defines.mak all: - for %d in (*.kmn) do $(DEVELOPER_PROGRAM)\kmcomp "%d" + for %d in (*.kmn) do $(DEVELOPER_PROGRAM)\kmc.cmd build "%d" .kmn.kmx: - &$(DEVELOPER_PROGRAM)\kmcomp $** + &$(DEVELOPER_PROGRAM)\kmc.cmd build $** clean: -del *.kmx diff --git a/developer/src/tike/tike.dpr b/developer/src/tike/tike.dpr index 1a1b1a07fed..11ac819f88b 100644 --- a/developer/src/tike/tike.dpr +++ b/developer/src/tike/tike.dpr @@ -16,7 +16,6 @@ uses compile in '..\common\delphi\compiler\compile.pas', KeymanDeveloperOptions in 'main\KeymanDeveloperOptions.pas', UfrmKeyTest in 'debug\UfrmKeyTest.pas' {frmKeyTest}, - CompilePackage in '..\common\delphi\compiler\CompilePackage.pas', KeymanDeveloperUtils in 'main\KeymanDeveloperUtils.pas', UfrmEditor in 'child\UfrmEditor.pas' {frmEditor}, MenuImgList in '..\common\delphi\components\MenuImgList.pas', @@ -89,8 +88,6 @@ uses utilfiletypes in '..\..\..\common\windows\delphi\general\utilfiletypes.pas', UfrmNewFileDetails in 'dialogs\UfrmNewFileDetails.pas' {frmNewFileDetails}, StockFileNames in '..\..\..\common\windows\delphi\general\StockFileNames.pas', - CompileKeymanWeb in 'compile\CompileKeymanWeb.pas', - KeymanWebKeyCodes in 'compile\KeymanWebKeyCodes.pas', utildir in '..\..\..\common\windows\delphi\general\utildir.pas', ADOX_TLB in '..\..\..\common\windows\delphi\tlb\ADOX_TLB.pas', ADODB_TLB in '..\..\..\common\windows\delphi\tlb\ADODB_TLB.pas', @@ -139,7 +136,6 @@ uses VisualKeyboardExportPNG in '..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardExportPNG.pas', MSXML2_TLB in '..\..\..\common\windows\delphi\tlb\MSXML2_TLB.pas', CharacterInfo in 'main\CharacterInfo.pas', - CompilePackageInstaller in '..\common\delphi\compiler\CompilePackageInstaller.pas', UTikeDebugMode in 'main\UTikeDebugMode.pas', kmxfileconsts in '..\..\..\common\windows\delphi\keyboards\kmxfileconsts.pas', kmxfileutils in '..\..\..\common\windows\delphi\keyboards\kmxfileutils.pas', @@ -279,12 +275,15 @@ uses UfrmDebugStatus_Platform in 'debug\UfrmDebugStatus_Platform.pas' {frmDebugStatus_Platform}, UfrmDebugStatus_Options in 'debug\UfrmDebugStatus_Options.pas' {frmDebugStatus_Options}, Keyman.Developer.System.KeymanDeveloperPaths in 'main\Keyman.Developer.System.KeymanDeveloperPaths.pas', - Keyman.Developer.System.ValidateKpsFile in '..\common\delphi\compiler\Keyman.Developer.System.ValidateKpsFile.pas', Keyman.Developer.UI.UfrmServerOptions in 'dialogs\Keyman.Developer.UI.UfrmServerOptions.pas' {frmServerOptions}, Keyman.Developer.System.ServerAPI in 'http\Keyman.Developer.System.ServerAPI.pas', Keyman.System.FontLoadUtil in 'main\Keyman.System.FontLoadUtil.pas', Keyman.Developer.UI.ServerUI in 'http\Keyman.Developer.UI.ServerUI.pas', - Keyman.Developer.System.GenerateKeyboardIcon in '..\kmconvert\Keyman.Developer.System.GenerateKeyboardIcon.pas'; + Keyman.Developer.System.GenerateKeyboardIcon in '..\kmconvert\Keyman.Developer.System.GenerateKeyboardIcon.pas', + Keyman.Developer.UI.UfrmEditLanguageExample in 'dialogs\examples\Keyman.Developer.UI.UfrmEditLanguageExample.pas' {frmEditLanguageExample}, + Keyman.Developer.UI.UfrmEditRelatedPackage in 'dialogs\relatedPackages\Keyman.Developer.UI.UfrmEditRelatedPackage.pas' {frmEditRelatedPackage}, + Keyman.Developer.UI.UfrmEditPackageWebFonts in 'dialogs\packageWebFonts\Keyman.Developer.UI.UfrmEditPackageWebFonts.pas' {frmEditPackageWebFonts}, + Keyman.Developer.System.KmcWrapper in 'compile\Keyman.Developer.System.KmcWrapper.pas'; {$R *.RES} {$R ICONS.RES} @@ -314,7 +313,7 @@ begin Application.Title := 'Keyman Developer'; if TikeActive then Exit; Application.CreateForm(TmodWebHttpServer, modWebHttpServer); - try + try Application.CreateForm(TfrmKeymanDeveloper, frmKeymanDeveloper); Application.Run; finally diff --git a/developer/src/tike/tike.dproj b/developer/src/tike/tike.dproj index 46661e39ce1..3ff3d319ca9 100644 --- a/developer/src/tike/tike.dproj +++ b/developer/src/tike/tike.dproj @@ -139,7 +139,6 @@
frmKeyTest
-
frmEditor
@@ -236,8 +235,6 @@
frmNewFileDetails
- - @@ -320,7 +317,6 @@ - @@ -544,7 +540,6 @@ dfm -
frmServerOptions
dfm @@ -553,6 +548,19 @@ + +
frmEditLanguageExample
+ dfm +
+ +
frmEditRelatedPackage
+ dfm +
+ +
frmEditPackageWebFonts
+ dfm +
+ Cfg_2 @@ -614,30 +622,18 @@ False - - - tike.rsm - true - - tike.exe true - + tike.rsm true - - - tike.exe - true - - .\ diff --git a/developer/src/tike/xml/project/distribution.xsl b/developer/src/tike/xml/project/distribution.xsl index 6b78ee608f9..998065b8427 100644 --- a/developer/src/tike/xml/project/distribution.xsl +++ b/developer/src/tike/xml/project/distribution.xsl @@ -97,10 +97,6 @@ Build keyman:compilefile?id= - - Build installer - keyman:package_compileinstaller?id= - Clean keyman:cleanfile?id= diff --git a/developer/src/tike/xml/project/packages.xsl b/developer/src/tike/xml/project/packages.xsl index 94d45113ccf..ff5a8e520a6 100644 --- a/developer/src/tike/xml/project/packages.xsl +++ b/developer/src/tike/xml/project/packages.xsl @@ -9,7 +9,7 @@

- +
New package... @@ -90,7 +90,7 @@ auto - +

@@ -106,7 +106,7 @@
- + @@ -121,12 +121,6 @@ Build keyman:compilefile?id=
- - - Build installer - keyman:package_compileinstaller?id= - - Clean keyman:cleanfile?id= @@ -154,7 +148,7 @@ - + @@ -196,5 +190,5 @@
- + \ No newline at end of file diff --git a/developer/src/tools/sentry-upload-difs.sh b/developer/src/tools/sentry-upload-difs.sh index 6c1f489d2de..5dd22cc886a 100755 --- a/developer/src/tools/sentry-upload-difs.sh +++ b/developer/src/tools/sentry-upload-difs.sh @@ -30,10 +30,6 @@ cd "$KEYMAN_ROOT/developer/src" # [ -f ./samples/imsample/IMSample.pdb ] && rm -f ./samples/imsample/IMSample.pdb -[ -f ./kmcmpdll/Win32/Release/kcframe.pdb ] && rm -f ./kmcmpdll/Win32/Release/kcframe.pdb -[ -f ./kmcmpdll/Win32/Debug/kcframe.pdb ] && rm -f ./kmcmpdll/Win32/Debug/kcframe.pdb -[ -f ./kmcmpdll/x64/Release/kcframe.x64.pdb ] && rm -f ./kmcmpdll/x64/Release/kcframe.x64.pdb -[ -f ./kmcmpdll/x64/Debug/kcframe.x64.pdb ] && rm -f ./kmcmpdll/x64/Debug/kcframe.x64.pdb # # Upload the files diff --git a/developer/src/tools/verify_signatures/Makefile b/developer/src/tools/verify_signatures/Makefile new file mode 100644 index 00000000000..003f6aa5d50 --- /dev/null +++ b/developer/src/tools/verify_signatures/Makefile @@ -0,0 +1,19 @@ +# +# test for signatures and version information being correct in bin folder +# + +!include ..\..\Defines.mak + +test: prereq + $(SIGCHECK) $(DEVELOPER_PROGRAM)\* > sig1 + $(VERIFY_SIGNATURES) < sig1 + +# prereq may not be needed? +prereq: + cd $(VERIFY_SIGNATURES_PATH) + $(MAKE) + +clean: def-clean + -del sig1 + +!include ..\..\Target.mak diff --git a/docs/build/macos.md b/docs/build/macos.md index d670001e074..f5ff0409640 100644 --- a/docs/build/macos.md +++ b/docs/build/macos.md @@ -101,8 +101,8 @@ These dependencies are also listed below if you'd prefer to install manually. sdkmanager --licenses ``` -* kmcomp (optional): WINE - - Required to build keyboards using kmcomp +* kmc (optional): node + - Required to build keyboards using kmc ```bash brew tap homebrew/cask-versions diff --git a/docs/build/old/core-desktop-notes.md b/docs/build/old/core-desktop-notes.md index 902aa7abed6..f4c41231043 100644 --- a/docs/build/old/core-desktop-notes.md +++ b/docs/build/old/core-desktop-notes.md @@ -8,7 +8,7 @@ WARNING: these are old configuration notes. See [index.md](../index.md) for curr - ninja 1.8+ - C++14 or later compiler (VC++ 2019 or later for Windows). - lib std::fs -- kmcomp (for tests) -- must be added to path +- kmc (for tests) -- must be added to path For WASM builds: - Meson build system 0.56+ @@ -56,19 +56,6 @@ Windows SDK version, if it cannot be automatically detected. export SDKVER=10.0.19041.0 ``` -#### kmcomp - -Note on paths for kmcomp: - -The search path can be edited through System settings / Advanced system settings -/ Environment Variables / User environment variables. - -If you have Keyman Developer installed, kmcomp should be on the path already; -otherwise add `%KeymanDeveloperPath%` to your path. - -If you do not have Keyman Developer installed, add the path where you extracted -the kmcomp archive. - ### Linux #### Ubuntu and Debian @@ -88,7 +75,7 @@ the kmcomp archive. * Install Enscripten (including adding to path with `emsdk_env.sh`) (WASM builds): -You may also need the `kmcomp` wrapper - see below. +You may also need `kmc` - see below. #### Other Linux distributions @@ -109,22 +96,16 @@ You may also need the `kmcomp` wrapper - see below. * Add emcc to PATH (probably upstream/enscripten) -You may also need the `kmcomp` wrapper - see below. +You may also need `kmc` - see below. -#### kmcomp - All Linux platforms +#### kmc - All Linux platforms -If you want to rebuild keyboards for tests, you need a wrapper `kmcomp` shell -script: +If you want to rebuild keyboards for tests, you need to install `kmc`: ```bash -#!/usr/bin/env bash -wine `dirname "$0"`/kmcomp.exe "$@" +npm i -g @keymanapp/kmc ``` -Place this in the same folder as you extracted kmcomp.exe, and -`chmod +x kmcomp`. Add the folder to the path (e.g. -`export PATH=/path/to/kmcomp:$PATH`, which you can add to `.bashrc`). - ### macOS * Install Python 3 @@ -141,26 +122,20 @@ Place this in the same folder as you extracted kmcomp.exe, and * Install Enscripten (including environment update): -#### kmcomp +#### kmc -If you want to rebuild keyboards for tests, you'll also need WINE: +If you want to rebuild keyboards for tests, you'll also need node: ```bash -brew tap homebrew/cask-versions -brew install --cask --no-quarantine wine-stable +brew install node ``` -And you will also need a wrapper `kmcomp` shell script: +And you will also need to install `kmc`: ```bash -#!/usr/bin/env bash -wine64 `dirname "$0"`/kmcomp.exe "$@" +npm install -g @keymanapp/kmc ``` -Place this in the same folder as you extracted kmcomp.exe, and -`chmod +x kmcomp`. Add the folder to the path (e.g. -`export PATH=/path/to/kmcomp:$PATH`, which you can add to `.bashrc`). - ## Building -- all platforms On all platforms, use `build.sh`. diff --git a/docs/build/windows.md b/docs/build/windows.md index 9faebc3d632..81a96a4a209 100644 --- a/docs/build/windows.md +++ b/docs/build/windows.md @@ -101,7 +101,7 @@ Building: * Ant * Gradle * Maven -* OpenJDK 11 +* Optional: OpenJDK 11 (https://learn.microsoft.com/en-us/java/openjdk/download) ```ps1 # Elevated PowerShell @@ -254,8 +254,8 @@ choco install openjdk choco install visualstudio2019community visualstudio2019-workload-nativedesktop visualstudio2019buildtools ``` * Verify required build tools are installed - * Run `Visual Studio Installer` - * Check the `Individual components` tab + * Run `Visual Studio Installer` + * Check the `Individual components` tab * Verify `MSVC v142 - VS 2019 c++ x64/x86 build tools (Latest)` is installed. If not, install it. Recommended: configure Visual Studio to use two-space tab stops: diff --git a/docs/settings/linux/settings.json b/docs/settings/linux/settings.json index 891507dd2ec..f17739ece29 100644 --- a/docs/settings/linux/settings.json +++ b/docs/settings/linux/settings.json @@ -16,7 +16,7 @@ "python.pythonPath": "/usr/bin/python3", "files.associations": { "*.tcc": "cpp", - "keyboardprocessor.h": "c", + "keyman_core_api.h": "c", "engine.h": "c", "keyman-service.h": "c", "keymanutil.h": "c", diff --git a/docs/settings/linux/tasks.json b/docs/settings/linux/tasks.json index b4a376b508f..49a09bf1302 100644 --- a/docs/settings/linux/tasks.json +++ b/docs/settings/linux/tasks.json @@ -195,6 +195,23 @@ "group": "test", "detail": "unit test keyman-config" }, + { + "type": "shell", + "label": "keyman-config: report", + "command": "${workspaceFolder}/linux/keyman-config/build.sh", + "args": [ + "test", + "--report", + "--debug", + "--coverage", + "--no-integration" + ], + "options": { + "cwd": "${workspaceFolder}/linux/keyman-config", + }, + "group": "test", + "detail": "run tests and create unit test coverage report" + }, { "type": "shell", "label": "keyman-system-service: clean", @@ -265,6 +282,7 @@ "command": "${workspaceFolder}/linux/keyman-system-service/build.sh", "args": [ "--debug", + "--coverage", "configure" ], "options": { @@ -282,7 +300,7 @@ "command": "${workspaceFolder}/linux/keyman-system-service/build.sh", "args": [ "--debug", - "test:arch" + "test" ], "problemMatcher": [ "$gcc" @@ -290,5 +308,22 @@ "group": "test", "detail": "run tests for keyman-system-service" }, + { + "type": "shell", + "label": "keyman-system-service: report", + "command": "${workspaceFolder}/linux/keyman-system-service/build.sh", + "args": [ + "test", + "--report", + "--debug", + "--coverage", + "--no-integration", + ], + "problemMatcher": [ + "$gcc" + ], + "group": "test", + "detail": "run tests and create unit test coverage report" + }, ] } diff --git a/ios/exportAppStore.plist b/ios/exportAppStore.plist index 6393f3c4cbf..48ef58f532d 100644 --- a/ios/exportAppStore.plist +++ b/ios/exportAppStore.plist @@ -7,9 +7,9 @@ teamID 3YE4W86L3G signingCertificate - 05473BF50CCD4B78B304656FB4D93FDC7EE7ACD0 + BB842A0081298BBA6D034C85451750B08FC9EE8A installerSigningCertificate - 05473BF50CCD4B78B304656FB4D93FDC7EE7ACD0 + BB842A0081298BBA6D034C85451750B08FC9EE8A provisioningProfiles Tavultesoft.Keyman diff --git a/linux/build.sh b/linux/build.sh index 3b3648dd146..da2212609d2 100755 --- a/linux/build.sh +++ b/linux/build.sh @@ -23,7 +23,9 @@ builder_describe \ "test" \ "install install artifacts" \ "uninstall uninstall artifacts" \ - "--no-integration+ don't run integration tests" + "--no-integration+ don't run integration tests" \ + "--report+ create coverage report" \ + "--coverage+ capture test coverage" builder_parse "$@" diff --git a/linux/debian/libkmnkbp0-0.symbols b/linux/debian/libkmnkbp0-0.symbols index 0da5c771209..cbcbb6f4ced 100644 --- a/linux/debian/libkmnkbp0-0.symbols +++ b/linux/debian/libkmnkbp0-0.symbols @@ -2,42 +2,42 @@ libkmnkbp0.so.0 libkmnkbp0-0 #MINVER# * Build-Depends-Package: libkmnkbp-dev kbp_state_get_intermediate_context@Base 15.0 - km_kbp_context_append@Base 15.0 - km_kbp_context_clear@Base 15.0 - km_kbp_context_get@Base 15.0 - km_kbp_context_item_list_size@Base 15.0 - km_kbp_context_items_dispose@Base 15.0 - km_kbp_context_items_from_utf16@Base 15.0 - km_kbp_context_items_from_utf8@Base 15.0 - km_kbp_context_items_to_utf16@Base 15.0 - km_kbp_context_items_to_utf8@Base 15.0 - km_kbp_context_length@Base 15.0 - km_kbp_context_set@Base 15.0 - km_kbp_context_shrink@Base 15.0 - km_kbp_event@Base 16.0.105 - km_kbp_get_engine_attrs@Base 15.0 - km_kbp_keyboard_dispose@Base 15.0 - km_kbp_keyboard_get_attrs@Base 15.0 - km_kbp_keyboard_get_imx_list@Base 15.0 - km_kbp_keyboard_get_key_list@Base 15.0 - km_kbp_keyboard_imx_list_dispose@Base 15.0 - km_kbp_keyboard_key_list_dispose@Base 15.0 - km_kbp_keyboard_load@Base 15.0 - km_kbp_options_list_size@Base 15.0 - km_kbp_process_event@Base 15.0 - km_kbp_process_queued_actions@Base 15.0 - km_kbp_state_action_items@Base 15.0 - km_kbp_state_clone@Base 15.0 - km_kbp_state_context@Base 15.0 - km_kbp_state_create@Base 15.0 - km_kbp_state_debug_get@Base 15.0 - km_kbp_state_debug_items@Base 15.0 - km_kbp_state_debug_set@Base 15.0 - km_kbp_state_dispose@Base 15.0 - km_kbp_state_imx_deregister_callback@Base 15.0 - km_kbp_state_imx_register_callback@Base 15.0 - km_kbp_state_option_lookup@Base 15.0 - km_kbp_state_options_to_json@Base 15.0 - km_kbp_state_options_update@Base 15.0 - km_kbp_state_queue_action_items@Base 15.0 - km_kbp_state_to_json@Base 15.0 + km_core_context_append@Base 15.0 + km_core_context_clear@Base 15.0 + km_core_context_get@Base 15.0 + km_core_context_item_list_size@Base 15.0 + km_core_context_items_dispose@Base 15.0 + km_core_context_items_from_utf16@Base 15.0 + km_core_context_items_from_utf8@Base 15.0 + km_core_context_items_to_utf16@Base 15.0 + km_core_context_items_to_utf8@Base 15.0 + km_core_context_length@Base 15.0 + km_core_context_set@Base 15.0 + km_core_context_shrink@Base 15.0 + km_core_event@Base 16.0.105 + km_core_get_engine_attrs@Base 15.0 + km_core_keyboard_dispose@Base 15.0 + km_core_keyboard_get_attrs@Base 15.0 + km_core_keyboard_get_imx_list@Base 15.0 + km_core_keyboard_get_key_list@Base 15.0 + km_core_keyboard_imx_list_dispose@Base 15.0 + km_core_keyboard_key_list_dispose@Base 15.0 + km_core_keyboard_load@Base 15.0 + km_core_options_list_size@Base 15.0 + km_core_process_event@Base 15.0 + km_core_process_queued_actions@Base 15.0 + km_core_state_action_items@Base 15.0 + km_core_state_clone@Base 15.0 + km_core_state_context@Base 15.0 + km_core_state_create@Base 15.0 + km_core_state_debug_get@Base 15.0 + km_core_state_debug_items@Base 15.0 + km_core_state_debug_set@Base 15.0 + km_core_state_dispose@Base 15.0 + km_core_state_imx_deregister_callback@Base 15.0 + km_core_state_imx_register_callback@Base 15.0 + km_core_state_option_lookup@Base 15.0 + km_core_state_options_to_json@Base 15.0 + km_core_state_options_update@Base 15.0 + km_core_state_queue_action_items@Base 15.0 + km_core_state_to_json@Base 15.0 diff --git a/linux/debian/tests/test-build b/linux/debian/tests/test-build index db0a343849c..e9a989e36ff 100644 --- a/linux/debian/tests/test-build +++ b/linux/debian/tests/test-build @@ -11,8 +11,8 @@ cd "$WORKDIR" # Test all include files are available cat < keymantest.c -#include -km_kbp_context* c; +#include +km_core_context* c; EOF # shellcheck disable=SC2046 @@ -21,8 +21,8 @@ echo "build 1: OK" # Test pkg-config file - include without path cat < keymantest.c -#include -km_kbp_context* c; +#include +km_core_context* c; EOF # shellcheck disable=SC2046 diff --git a/linux/ibus-keyman/src/engine.c b/linux/ibus-keyman/src/engine.c index 49b9161f9a3..c7938c7a8c4 100644 --- a/linux/ibus-keyman/src/engine.c +++ b/linux/ibus-keyman/src/engine.c @@ -28,8 +28,8 @@ #include #include -#include -#include +#include +#include #include "config.h" #include "keymanutil.h" @@ -71,8 +71,8 @@ struct _IBusKeymanEngine { IBusEngine parent; /* members */ - km_kbp_keyboard *keyboard; - km_kbp_state *state; + km_core_keyboard *keyboard; + km_core_state *state; gchar *ldmlfile; gchar *kb_name; gboolean lctrl_pressed; @@ -204,18 +204,18 @@ ibus_keyman_engine_class_init (IBusKeymanEngineClass *klass) // engine_class->property_activate = ibus_keyman_engine_property_activate; } -static gchar *get_current_context_text(km_kbp_context *context) +static gchar *get_current_context_text(km_core_context *context) { size_t buf_size = 512; - km_kbp_context_item *context_items; + km_core_context_item *context_items; gchar *current_context_utf8 = g_new0(gchar, buf_size); - if (km_kbp_context_get(context, &context_items) == KM_KBP_STATUS_OK) { - km_kbp_context_items_to_utf8(context_items, + if (km_core_context_get(context, &context_items) == KM_CORE_STATUS_OK) { + km_core_context_items_to_utf8(context_items, current_context_utf8, &buf_size); } - km_kbp_context_items_dispose(context_items); - g_message("%s: current context is:%zu:%zu:%s:", __FUNCTION__, km_kbp_context_length(context), buf_size, current_context_utf8); + km_core_context_items_dispose(context_items); + g_message("%s: current context is:%zu:%zu:%s:", __FUNCTION__, km_core_context_length(context), buf_size, current_context_utf8); return current_context_utf8; } @@ -239,16 +239,17 @@ client_supports_surrounding_text(IBusEngine *engine) { static void reset_context(IBusEngine *engine) { IBusKeymanEngine *keyman = (IBusKeymanEngine *)engine; - km_kbp_context *context; + km_core_context *context; g_message("%s", __FUNCTION__); - context = km_kbp_state_context(keyman->state); + context = km_core_state_context(keyman->state); if (client_supports_surrounding_text(engine)) { IBusText *text; - gchar *surrounding_text, *current_context_utf8; + g_autofree gchar *surrounding_text = NULL; + g_autofree gchar *current_context_utf8 = NULL; guint cursor_pos, anchor_pos, context_start, context_end; - km_kbp_context_item *context_items; + km_core_context_item *context_items; ibus_engine_get_surrounding_text(engine, &text, &cursor_pos, &anchor_pos); @@ -261,19 +262,17 @@ reset_context(IBusEngine *engine) { current_context_utf8 = get_current_context_text(context); if (!(*current_context_utf8) || !g_str_has_suffix(surrounding_text, current_context_utf8)) { g_message("%s: setting context because it has changed from expected", __FUNCTION__); - enum km_kbp_status_codes status = km_kbp_context_items_from_utf8(surrounding_text, &context_items); - if (status == KM_KBP_STATUS_OK) { - km_kbp_context_set(context, context_items); - km_kbp_context_items_dispose(context_items); + enum km_core_status_codes status = km_core_context_items_from_utf8(surrounding_text, &context_items); + if (status == KM_CORE_STATUS_OK) { + km_core_context_set(context, context_items); + km_core_context_items_dispose(context_items); } else { - km_kbp_context_clear(context); + km_core_context_clear(context); g_message("%s: setting context failed with status code %d", __FUNCTION__, status); } } - g_free(surrounding_text); - g_free(current_context_utf8); } else { - km_kbp_context_clear(context); + km_core_context_clear(context); g_message("%s: client does not support surrounding text", __FUNCTION__); } } @@ -299,7 +298,7 @@ ibus_keyman_engine_init(IBusKeymanEngine *keyman) { keyman->state = NULL; } -static km_kbp_cp* get_base_layout() +static km_core_cp* get_base_layout() { return u"en-US"; @@ -327,44 +326,54 @@ static km_kbp_cp* get_base_layout() lang = strdup("en-US"); } g_message("lang is %s", lang); - km_kbp_cp *cp = g_utf8_to_utf16(lang, -1, NULL, NULL, NULL); + km_core_cp *cp = g_utf8_to_utf16(lang, -1, NULL, NULL, NULL); return cp; // g_free(lang); #endif } -static km_kbp_status +static km_core_status setup_environment(IBusKeymanEngine *keyman) { g_assert(keyman); g_message("%s: setting up environment", __FUNCTION__); // Allocate enough options for: 3 environments plus 1 pad struct of 0's - km_kbp_option_item environment_opts[4] = {0}; + km_core_option_item environment_opts[4] = {0}; - environment_opts[0].scope = KM_KBP_OPT_ENVIRONMENT; - environment_opts[0].key = KM_KBP_KMX_ENV_PLATFORM; + environment_opts[0].scope = KM_CORE_OPT_ENVIRONMENT; + environment_opts[0].key = KM_CORE_KMX_ENV_PLATFORM; environment_opts[0].value = u"linux desktop hardware native"; - environment_opts[1].scope = KM_KBP_OPT_ENVIRONMENT; - environment_opts[1].key = KM_KBP_KMX_ENV_BASELAYOUT; + environment_opts[1].scope = KM_CORE_OPT_ENVIRONMENT; + environment_opts[1].key = KM_CORE_KMX_ENV_BASELAYOUT; environment_opts[1].value = u"kbdus.dll"; - environment_opts[2].scope = KM_KBP_OPT_ENVIRONMENT; - environment_opts[2].key = KM_KBP_KMX_ENV_BASELAYOUTALT; + environment_opts[2].scope = KM_CORE_OPT_ENVIRONMENT; + environment_opts[2].key = KM_CORE_KMX_ENV_BASELAYOUTALT; environment_opts[2].value = get_base_layout(); // TODO: free when mnemonic layouts are to be supported - km_kbp_status status = km_kbp_state_create(keyman->keyboard, environment_opts, &(keyman->state)); - if (status != KM_KBP_STATUS_OK) { - g_warning("%s: problem creating km_kbp_state. Status is %u.", __FUNCTION__, status); + km_core_status status = km_core_state_create(keyman->keyboard, environment_opts, &(keyman->state)); + if (status != KM_CORE_STATUS_OK) { + g_warning("%s: problem creating km_core_state. Status is %u.", __FUNCTION__, status); } return status; } -static km_kbp_status -load_keyboard_options(IBusKeymanEngine *keyman) -{ +void +free_km_core_option_item(gpointer data) { + if (!data) + return; + + km_core_option_item *opt = (km_core_option_item *)data; + g_free((km_core_cp *)opt->key); + g_free((km_core_cp *)opt->value); + g_free(opt); +} + +static km_core_status +load_keyboard_options(IBusKeymanEngine *keyman) { g_assert(keyman); // Retrieve keyboard options from DConf @@ -373,32 +382,27 @@ load_keyboard_options(IBusKeymanEngine *keyman) GQueue *queue_options = keyman_get_options_queue_fromdconf(keyman->kb_name, keyman->kb_name); int num_options = g_queue_get_length(queue_options); if (num_options < 1) { - g_queue_free_full(queue_options, NULL); - return KM_KBP_STATUS_OK; + g_queue_free_full(queue_options, free_km_core_option_item); + return KM_CORE_STATUS_OK; } // Allocate enough options for: num_options plus 1 pad struct of 0's - km_kbp_option_item *keyboard_opts = g_new0(km_kbp_option_item, num_options + 1); + km_core_option_item *keyboard_opts = g_new0(km_core_option_item, num_options + 1); for (int i = 0; i < num_options; i++) { - km_kbp_option_item *item = g_queue_pop_head(queue_options); + km_core_option_item *item = g_queue_pop_head(queue_options); keyboard_opts[i].scope = item->scope; keyboard_opts[i].key = item->key; keyboard_opts[i].value = item->value; } - // once we have the option list we can then update the options using the public api call - km_kbp_status status = km_kbp_state_options_update(keyman->state, keyboard_opts); + km_core_status status = km_core_state_options_update(keyman->state, keyboard_opts); - if (status != KM_KBP_STATUS_OK) { - g_warning("%s: problem creating km_kbp_state. Status is %u.", __FUNCTION__, status); - } - for (int i = 0; i < num_options; i++) { - g_free((km_kbp_cp *)keyboard_opts[i].key); - g_free((km_kbp_cp *)keyboard_opts[i].value); + if (status != KM_CORE_STATUS_OK) { + g_warning("%s: problem creating km_core_state. Status is %u.", __FUNCTION__, status); } - g_queue_free_full(queue_options, NULL); + g_queue_free_full(queue_options, free_km_core_option_item); g_free(keyboard_opts); return status; } @@ -412,7 +416,8 @@ ibus_keyman_engine_constructor( IBusKeymanEngine *keyman; IBusEngine *engine; const gchar *engine_name; - gchar *p, *abs_kmx_path; + gchar *p; + g_autofree gchar *abs_kmx_path = NULL; g_debug("DAR: %s", __FUNCTION__); @@ -451,44 +456,40 @@ ibus_keyman_engine_constructor( g_strfreev(split_name); - gchar *kmx_file = g_path_get_basename(abs_kmx_path); + g_autofree gchar *kmx_file = g_path_get_basename(abs_kmx_path); p = rindex(kmx_file, '.'); // get id to use as dbus service name if (p) { keyman->kb_name = g_strndup(kmx_file, p-kmx_file); p = rindex(abs_kmx_path, '.'); if (p) { - gchar *dir = g_path_get_dirname(abs_kmx_path); - gchar *ldmlfile = g_strdup_printf("%s/%s.ldml", dir, keyman->kb_name); + g_autofree gchar *dir = g_path_get_dirname(abs_kmx_path); + g_autofree gchar *ldmlfile = g_strdup_printf("%s/%s.ldml", dir, keyman->kb_name); if (g_file_test(ldmlfile, G_FILE_TEST_EXISTS)) { keyman->ldmlfile = g_strdup(ldmlfile); } - g_free(dir); - g_free(ldmlfile); } } - g_free(kmx_file); - km_kbp_status status; + km_core_status status; - status = km_kbp_keyboard_load(abs_kmx_path, &(keyman->keyboard)); - g_free(abs_kmx_path); + status = km_core_keyboard_load(abs_kmx_path, &(keyman->keyboard)); - if (status != KM_KBP_STATUS_OK) { - g_warning("%s: problem creating km_kbp_keyboard. Status is %u.", __FUNCTION__, status); + if (status != KM_CORE_STATUS_OK) { + g_warning("%s: problem creating km_core_keyboard. Status is %u.", __FUNCTION__, status); ibus_keyman_engine_destroy(keyman); return NULL; } status = setup_environment(keyman); - if (status != KM_KBP_STATUS_OK) { + if (status != KM_CORE_STATUS_OK) { ibus_keyman_engine_destroy(keyman); return NULL; } status = load_keyboard_options(keyman); - if (status != KM_KBP_STATUS_OK) { + if (status != KM_CORE_STATUS_OK) { ibus_keyman_engine_destroy(keyman); return NULL; } @@ -522,12 +523,12 @@ ibus_keyman_engine_destroy (IBusKeymanEngine *keyman) } if (keyman->state) { - km_kbp_state_dispose(keyman->state); + km_core_state_dispose(keyman->state); keyman->state = NULL; } if (keyman->keyboard) { - km_kbp_keyboard_dispose(keyman->keyboard); + km_core_keyboard_dispose(keyman->keyboard); keyman->keyboard = NULL; } @@ -566,7 +567,7 @@ static void forward_backspace(IBusKeymanEngine *keyman, unsigned int state) static gboolean process_unicode_char_action( IBusKeymanEngine *keyman, - const km_kbp_action_item *action_item + const km_core_action_item *action_item ) { g_assert(g_unichar_type(action_item->character) != G_UNICODE_SURROGATE); gchar *utf8 = (gchar *)g_new0(gchar, 12); @@ -603,12 +604,12 @@ static gboolean process_alert_action() { static gboolean process_backspace_action( IBusEngine *engine, - const km_kbp_action_item *action_items, + const km_core_action_item *action_items, int i, size_t num_action_items ) { IBusKeymanEngine *keyman = (IBusKeymanEngine *)engine; - if (action_items[i].backspace.expected_type == KM_KBP_IT_MARKER) { + if (action_items[i].backspace.expected_type == KM_CORE_IT_MARKER) { g_message("%s: skipping marker type", __FUNCTION__); } else if (keyman->commit_item->char_buffer != NULL) { g_message("%s: removing one utf8 char from CHAR buffer", __FUNCTION__); @@ -635,12 +636,12 @@ process_backspace_action( g_message("%s: increment consecutive backspaces to %d", __FUNCTION__, keyman->consecutive_backspaces); } else { g_message("%s: forwarding backspace with reset context", __FUNCTION__); - km_kbp_context_item *context_items; - km_kbp_context_get(km_kbp_state_context(keyman->state), &context_items); + km_core_context_item *context_items; + km_core_context_get(km_core_state_context(keyman->state), &context_items); reset_context(engine); forward_backspace(keyman, 0); - km_kbp_context_set(km_kbp_state_context(keyman->state), context_items); - km_kbp_context_items_dispose(context_items); + km_core_context_set(km_core_state_context(keyman->state), context_items); + km_core_context_items_dispose(context_items); } } return TRUE; @@ -664,18 +665,18 @@ finish_backspace_action( static gboolean process_persist_action( IBusKeymanEngine *keyman, - const km_kbp_action_item *action_item + const km_core_action_item *action_item ) { // Save keyboard option if (!action_item->option) return TRUE; // Allocate for 1 option plus 1 pad struct of 0's - km_kbp_option_item *keyboard_opts = g_new0(km_kbp_option_item, 2); - memmove(&(keyboard_opts[0]), action_item->option, sizeof(km_kbp_option_item)); - km_kbp_status event_status = km_kbp_state_options_update(keyman->state, keyboard_opts); - if (event_status != KM_KBP_STATUS_OK) { - g_warning("%s: problem saving option for km_kbp_keyboard", __FUNCTION__); + km_core_option_item *keyboard_opts = g_new0(km_core_option_item, 2); + memmove(&(keyboard_opts[0]), action_item->option, sizeof(km_core_option_item)); + km_core_status event_status = km_core_state_options_update(keyman->state, keyboard_opts); + if (event_status != KM_CORE_STATUS_OK) { + g_warning("%s: problem saving option for km_core_keyboard", __FUNCTION__); } g_free(keyboard_opts); @@ -711,7 +712,7 @@ process_invalidate_context_action(IBusEngine *engine) { static gboolean process_capslock_action( IBusKeymanEngine *keyman, - const km_kbp_action_item *action_item + const km_core_action_item *action_item ) { g_message("%s: %s caps-lock", __FUNCTION__, action_item->capsLock ? "Enable" : "Disable"); @@ -788,55 +789,55 @@ process_end_action(IBusKeymanEngine *keyman) { static gboolean process_actions( IBusEngine *engine, - const km_kbp_action_item *action_items, + const km_core_action_item *action_items, size_t num_action_items ) { IBusKeymanEngine *keyman = (IBusKeymanEngine *)engine; for (int i = 0; i < num_action_items; i++) { gboolean continue_with_next_action = TRUE; - if (action_items[i].type != KM_KBP_IT_BACK && keyman->consecutive_backspaces > 0) { + if (action_items[i].type != KM_CORE_IT_BACK && keyman->consecutive_backspaces > 0) { finish_backspace_action(engine); } switch (action_items[i].type) { - case KM_KBP_IT_CHAR: + case KM_CORE_IT_CHAR: g_message("CHAR action %d/%d", i + 1, (int)num_action_items); continue_with_next_action = process_unicode_char_action(keyman, &action_items[i]); g_assert(continue_with_next_action == TRUE); break; - case KM_KBP_IT_MARKER: + case KM_CORE_IT_MARKER: g_message("MARKER action %d/%d", i + 1, (int)num_action_items); break; - case KM_KBP_IT_ALERT: + case KM_CORE_IT_ALERT: g_message("ALERT action %d/%d", i + 1, (int)num_action_items); continue_with_next_action = process_alert_action(); g_assert(continue_with_next_action == TRUE); break; - case KM_KBP_IT_BACK: + case KM_CORE_IT_BACK: g_message("BACK action %d/%d", i + 1, (int)num_action_items); continue_with_next_action = process_backspace_action(engine, action_items, i, num_action_items); g_assert(continue_with_next_action == TRUE); break; - case KM_KBP_IT_PERSIST_OPT: + case KM_CORE_IT_PERSIST_OPT: g_message("PERSIST_OPT action %d/%d", i + 1, (int)num_action_items); continue_with_next_action = process_persist_action(keyman, &action_items[i]); g_assert(continue_with_next_action == TRUE); break; - case KM_KBP_IT_EMIT_KEYSTROKE: + case KM_CORE_IT_EMIT_KEYSTROKE: g_message("EMIT_KEYSTROKE action %d/%d", i + 1, (int)num_action_items); continue_with_next_action = process_emit_keystroke_action(keyman); g_assert(continue_with_next_action == TRUE); break; - case KM_KBP_IT_INVALIDATE_CONTEXT: + case KM_CORE_IT_INVALIDATE_CONTEXT: g_message("INVALIDATE_CONTEXT action %d/%d", i + 1, (int)num_action_items); continue_with_next_action = process_invalidate_context_action(engine); g_assert(continue_with_next_action == TRUE); break; - case KM_KBP_IT_CAPSLOCK: + case KM_CORE_IT_CAPSLOCK: g_message("CAPSLOCK action %d/%d", i + 1, (int)num_action_items); continue_with_next_action = process_capslock_action(keyman, &action_items[i]); g_assert(continue_with_next_action == TRUE); break; - case KM_KBP_IT_END: + case KM_CORE_IT_END: g_message("END action %d/%d", i + 1, (int)num_action_items); continue_with_next_action = process_end_action(keyman); break; @@ -904,49 +905,49 @@ ibus_keyman_engine_process_key_event( // keyman modifiers are different from X11/ibus uint16_t km_mod_state = 0; if (state & IBUS_SHIFT_MASK) { - km_mod_state |= KM_KBP_MODIFIER_SHIFT; + km_mod_state |= KM_CORE_MODIFIER_SHIFT; } if (state & IBUS_MOD5_MASK) { - km_mod_state |= KM_KBP_MODIFIER_RALT; - g_message("%s: modstate KM_KBP_MODIFIER_RALT from IBUS_MOD5_MASK", __FUNCTION__); + km_mod_state |= KM_CORE_MODIFIER_RALT; + g_message("%s: modstate KM_CORE_MODIFIER_RALT from IBUS_MOD5_MASK", __FUNCTION__); } if (state & IBUS_MOD1_MASK) { if (keyman->ralt_pressed) { - km_mod_state |= KM_KBP_MODIFIER_RALT; - g_message("%s: modstate KM_KBP_MODIFIER_RALT from ralt_pressed", __FUNCTION__); + km_mod_state |= KM_CORE_MODIFIER_RALT; + g_message("%s: modstate KM_CORE_MODIFIER_RALT from ralt_pressed", __FUNCTION__); } if (keyman->lalt_pressed) { - km_mod_state |= KM_KBP_MODIFIER_LALT; - g_message("%s: modstate KM_KBP_MODIFIER_LALT from lalt_pressed", __FUNCTION__); + km_mod_state |= KM_CORE_MODIFIER_LALT; + g_message("%s: modstate KM_CORE_MODIFIER_LALT from lalt_pressed", __FUNCTION__); } } if (state & IBUS_CONTROL_MASK) { if (keyman->rctrl_pressed) { - km_mod_state |= KM_KBP_MODIFIER_RCTRL; - g_message("%s: modstate KM_KBP_MODIFIER_RCTRL from rctrl_pressed", __FUNCTION__); + km_mod_state |= KM_CORE_MODIFIER_RCTRL; + g_message("%s: modstate KM_CORE_MODIFIER_RCTRL from rctrl_pressed", __FUNCTION__); } if (keyman->lctrl_pressed) { - km_mod_state |= KM_KBP_MODIFIER_LCTRL; - g_message("%s: modstate KM_KBP_MODIFIER_LCTRL from lctrl_pressed", __FUNCTION__); + km_mod_state |= KM_CORE_MODIFIER_LCTRL; + g_message("%s: modstate KM_CORE_MODIFIER_LCTRL from lctrl_pressed", __FUNCTION__); } } if (state & IBUS_LOCK_MASK) { - km_mod_state |= KM_KBP_MODIFIER_CAPS; + km_mod_state |= KM_CORE_MODIFIER_CAPS; } g_message("%s: before process key event", __FUNCTION__); - km_kbp_context *context = km_kbp_state_context(keyman->state); + km_core_context *context = km_core_state_context(keyman->state); g_free(get_current_context_text(context)); g_message("DAR: %s - km_mod_state=0x%x", __FUNCTION__, km_mod_state); - km_kbp_process_event(keyman->state, keycode_to_vk[keycode], km_mod_state, isKeyDown, KM_KBP_EVENT_FLAG_DEFAULT); - context = km_kbp_state_context(keyman->state); + km_core_process_event(keyman->state, keycode_to_vk[keycode], km_mod_state, isKeyDown, KM_CORE_EVENT_FLAG_DEFAULT); + context = km_core_state_context(keyman->state); g_message("%s: after process key event", __FUNCTION__); g_free(get_current_context_text(context)); - // km_kbp_state_action_items to get action items + // km_core_state_action_items to get action items size_t num_action_items; g_free(keyman->commit_item->char_buffer); keyman->commit_item->char_buffer = NULL; - const km_kbp_action_item *action_items = km_kbp_state_action_items(keyman->state, &num_action_items); + const km_core_action_item *action_items = km_core_state_action_items(keyman->state, &num_action_items); if (!process_actions(engine, action_items, num_action_items) && (!client_supports_prefilter(engine) || client_supports_surrounding_text(engine))) { @@ -956,7 +957,7 @@ ibus_keyman_engine_process_key_event( return FALSE; } - context = km_kbp_state_context(keyman->state); + context = km_core_state_context(keyman->state); g_message("%s: after processing all actions", __FUNCTION__); g_free(get_current_context_text(context)); return TRUE; @@ -1009,7 +1010,7 @@ ibus_keyman_engine_focus_out (IBusEngine *engine) IBusKeymanEngine *keyman = (IBusKeymanEngine *) engine; g_message("%s", __FUNCTION__); - km_kbp_context_clear(km_kbp_state_context(keyman->state)); + km_core_context_clear(km_core_state_context(keyman->state)); parent_class->focus_out (engine); } diff --git a/linux/ibus-keyman/src/keycodes.h b/linux/ibus-keyman/src/keycodes.h index 2735da48cb2..add902a838e 100644 --- a/linux/ibus-keyman/src/keycodes.h +++ b/linux/ibus-keyman/src/keycodes.h @@ -1,101 +1,101 @@ #ifndef __KEYCODES_H__ #define __KEYCODES_H__ -#include +#include // from android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMScanCodeMap.java // uses kernel keycodes which are (X11 keycode - 8) // see /usr/include/linux/input-event-codes.h -static km_kbp_virtual_key const keycode_to_vk[256] = { +static km_core_virtual_key const keycode_to_vk[256] = { 0, // KEY_RESERVED = 0x00; - KM_KBP_VKEY_ESC, // KEY_ESC = 0x01; - KM_KBP_VKEY_1, // KEY_1 = 0x02; - KM_KBP_VKEY_2, // KEY_2 = 0x03; - KM_KBP_VKEY_3, // KEY_3 = 0x04; - KM_KBP_VKEY_4, // KEY_4 = 0x05; - KM_KBP_VKEY_5, // KEY_5 = 0x06; - KM_KBP_VKEY_6, // KEY_6 = 0x07; - KM_KBP_VKEY_7, // KEY_7 = 0x08; - KM_KBP_VKEY_8, // KEY_8 = 0x09; - KM_KBP_VKEY_9, // KEY_9 = 0x0A; - KM_KBP_VKEY_0, // KEY_0 = 0x0B; - KM_KBP_VKEY_HYPHEN, // KEY_MINUS = 0x0C; - KM_KBP_VKEY_EQUAL, // KEY_EQUALS = 0x0D; - KM_KBP_VKEY_BKSP, // KEY_BACKSPACE = 0x0E; - KM_KBP_VKEY_TAB, // KEY_TAB = 0x0F; - KM_KBP_VKEY_Q, // KEY_Q = 0x10; - KM_KBP_VKEY_W, // KEY_W = 0x11; - KM_KBP_VKEY_E, // KEY_E = 0x12; - KM_KBP_VKEY_R, // KEY_R = 0x13; - KM_KBP_VKEY_T, // KEY_T = 0x14; - KM_KBP_VKEY_Y, // KEY_Y = 0x15; - KM_KBP_VKEY_U, // KEY_U = 0x16; - KM_KBP_VKEY_I, // KEY_I = 0x17; - KM_KBP_VKEY_O, // KEY_O = 0x18; - KM_KBP_VKEY_P, // KEY_P = 0x19; - KM_KBP_VKEY_LBRKT, // KEY_LEFTBRACE = 0x1A; - KM_KBP_VKEY_RBRKT, // KEY_RIGHTBRACE = 0x1B; - KM_KBP_VKEY_ENTER, // KEY_ENTER = 0x1C; - KM_KBP_VKEY_CONTROL, // KEY_LEFTCTRL = 0x1D; - KM_KBP_VKEY_A, // KEY_A = 0x1E; - KM_KBP_VKEY_S, // KEY_S = 0x1F; - KM_KBP_VKEY_D, // KEY_D = 0x20; - KM_KBP_VKEY_F, // KEY_F = 0x21; - KM_KBP_VKEY_G, // KEY_G = 0x22; - KM_KBP_VKEY_H, // KEY_H = 0x23; - KM_KBP_VKEY_J, // KEY_J = 0x24; - KM_KBP_VKEY_K, // KEY_K = 0x25; - KM_KBP_VKEY_L, // KEY_L = 0x26; - KM_KBP_VKEY_COLON, // KEY_SEMICOLON = 0x27; - KM_KBP_VKEY_QUOTE, // KEY_APOSTROPHE = 0x28; - KM_KBP_VKEY_BKQUOTE, // KEY_GRAVE = 0x29; - KM_KBP_VKEY_SHIFT, // KEY_LEFTSHIFT = 0x2A; - KM_KBP_VKEY_BKSLASH, // KEY_BACKSLASH = 0x2B; - KM_KBP_VKEY_Z, // KEY_Z = 0x2C; - KM_KBP_VKEY_X, // KEY_X = 0x2D; - KM_KBP_VKEY_C, // KEY_C = 0x2E; - KM_KBP_VKEY_V, // KEY_V = 0x2F; - KM_KBP_VKEY_B, // KEY_B = 0x30; - KM_KBP_VKEY_N, // KEY_N = 0x31; - KM_KBP_VKEY_M, // KEY_M = 0x32; - KM_KBP_VKEY_COMMA, // KEY_COMMA = 0x33; - KM_KBP_VKEY_PERIOD, // KEY_DOT = 0x34; - KM_KBP_VKEY_SLASH, // KEY_SLASH = 0x35; - KM_KBP_VKEY_SHIFT, // KEY_RIGHTSHIFT = 0x36; - KM_KBP_VKEY_NPSTAR, // KEY_KPASTERISK = 0x37; - KM_KBP_VKEY_ALT, // KEY_LEFTALT = 0x38; - KM_KBP_VKEY_SPACE, // KEY_SPACE = 0x39; - KM_KBP_VKEY_CAPS, // KEY_CAPSLOCK = 0x3A; - KM_KBP_VKEY_F1, // KEY_F1 = 0x3B; - KM_KBP_VKEY_F2, // KEY_F2 = 0x3C; - KM_KBP_VKEY_F3, // KEY_F3 = 0x3D; - KM_KBP_VKEY_F4, // KEY_F4 = 0x3E; - KM_KBP_VKEY_F5, // KEY_F5 = 0x3F; - KM_KBP_VKEY_F6, // KEY_F6 = 0x40; - KM_KBP_VKEY_F7, // KEY_F7 = 0x41; - KM_KBP_VKEY_F8, // KEY_F8 = 0x42; - KM_KBP_VKEY_F9, // KEY_F9 = 0x43; - KM_KBP_VKEY_F10, // KEY_F10 = 0x44; + KM_CORE_VKEY_ESC, // KEY_ESC = 0x01; + KM_CORE_VKEY_1, // KEY_1 = 0x02; + KM_CORE_VKEY_2, // KEY_2 = 0x03; + KM_CORE_VKEY_3, // KEY_3 = 0x04; + KM_CORE_VKEY_4, // KEY_4 = 0x05; + KM_CORE_VKEY_5, // KEY_5 = 0x06; + KM_CORE_VKEY_6, // KEY_6 = 0x07; + KM_CORE_VKEY_7, // KEY_7 = 0x08; + KM_CORE_VKEY_8, // KEY_8 = 0x09; + KM_CORE_VKEY_9, // KEY_9 = 0x0A; + KM_CORE_VKEY_0, // KEY_0 = 0x0B; + KM_CORE_VKEY_HYPHEN, // KEY_MINUS = 0x0C; + KM_CORE_VKEY_EQUAL, // KEY_EQUALS = 0x0D; + KM_CORE_VKEY_BKSP, // KEY_BACKSPACE = 0x0E; + KM_CORE_VKEY_TAB, // KEY_TAB = 0x0F; + KM_CORE_VKEY_Q, // KEY_Q = 0x10; + KM_CORE_VKEY_W, // KEY_W = 0x11; + KM_CORE_VKEY_E, // KEY_E = 0x12; + KM_CORE_VKEY_R, // KEY_R = 0x13; + KM_CORE_VKEY_T, // KEY_T = 0x14; + KM_CORE_VKEY_Y, // KEY_Y = 0x15; + KM_CORE_VKEY_U, // KEY_U = 0x16; + KM_CORE_VKEY_I, // KEY_I = 0x17; + KM_CORE_VKEY_O, // KEY_O = 0x18; + KM_CORE_VKEY_P, // KEY_P = 0x19; + KM_CORE_VKEY_LBRKT, // KEY_LEFTBRACE = 0x1A; + KM_CORE_VKEY_RBRKT, // KEY_RIGHTBRACE = 0x1B; + KM_CORE_VKEY_ENTER, // KEY_ENTER = 0x1C; + KM_CORE_VKEY_CONTROL, // KEY_LEFTCTRL = 0x1D; + KM_CORE_VKEY_A, // KEY_A = 0x1E; + KM_CORE_VKEY_S, // KEY_S = 0x1F; + KM_CORE_VKEY_D, // KEY_D = 0x20; + KM_CORE_VKEY_F, // KEY_F = 0x21; + KM_CORE_VKEY_G, // KEY_G = 0x22; + KM_CORE_VKEY_H, // KEY_H = 0x23; + KM_CORE_VKEY_J, // KEY_J = 0x24; + KM_CORE_VKEY_K, // KEY_K = 0x25; + KM_CORE_VKEY_L, // KEY_L = 0x26; + KM_CORE_VKEY_COLON, // KEY_SEMICOLON = 0x27; + KM_CORE_VKEY_QUOTE, // KEY_APOSTROPHE = 0x28; + KM_CORE_VKEY_BKQUOTE, // KEY_GRAVE = 0x29; + KM_CORE_VKEY_SHIFT, // KEY_LEFTSHIFT = 0x2A; + KM_CORE_VKEY_BKSLASH, // KEY_BACKSLASH = 0x2B; + KM_CORE_VKEY_Z, // KEY_Z = 0x2C; + KM_CORE_VKEY_X, // KEY_X = 0x2D; + KM_CORE_VKEY_C, // KEY_C = 0x2E; + KM_CORE_VKEY_V, // KEY_V = 0x2F; + KM_CORE_VKEY_B, // KEY_B = 0x30; + KM_CORE_VKEY_N, // KEY_N = 0x31; + KM_CORE_VKEY_M, // KEY_M = 0x32; + KM_CORE_VKEY_COMMA, // KEY_COMMA = 0x33; + KM_CORE_VKEY_PERIOD, // KEY_DOT = 0x34; + KM_CORE_VKEY_SLASH, // KEY_SLASH = 0x35; + KM_CORE_VKEY_SHIFT, // KEY_RIGHTSHIFT = 0x36; + KM_CORE_VKEY_NPSTAR, // KEY_KPASTERISK = 0x37; + KM_CORE_VKEY_ALT, // KEY_LEFTALT = 0x38; + KM_CORE_VKEY_SPACE, // KEY_SPACE = 0x39; + KM_CORE_VKEY_CAPS, // KEY_CAPSLOCK = 0x3A; + KM_CORE_VKEY_F1, // KEY_F1 = 0x3B; + KM_CORE_VKEY_F2, // KEY_F2 = 0x3C; + KM_CORE_VKEY_F3, // KEY_F3 = 0x3D; + KM_CORE_VKEY_F4, // KEY_F4 = 0x3E; + KM_CORE_VKEY_F5, // KEY_F5 = 0x3F; + KM_CORE_VKEY_F6, // KEY_F6 = 0x40; + KM_CORE_VKEY_F7, // KEY_F7 = 0x41; + KM_CORE_VKEY_F8, // KEY_F8 = 0x42; + KM_CORE_VKEY_F9, // KEY_F9 = 0x43; + KM_CORE_VKEY_F10, // KEY_F10 = 0x44; 0, // KEY_NUMLOCK = 0x45; 0, // KEY_SCROLLLOCK = 0x46; - KM_KBP_VKEY_NP7, // KEY_KP7 = 0x47; - KM_KBP_VKEY_NP8, // KEY_KP8 = 0x48; - KM_KBP_VKEY_NP9, // KEY_KP9 = 0x49; - KM_KBP_VKEY_NPMINUS, // KEY_KPMINUS = 0x4A; - KM_KBP_VKEY_NP4, // KEY_KP4 = 0x4B; - KM_KBP_VKEY_NP5, // KEY_KP5 = 0x4C; - KM_KBP_VKEY_NP6, // KEY_KP6 = 0x4D; - KM_KBP_VKEY_NPPLUS, // KEY_KPPLUS = 0x4E; - KM_KBP_VKEY_NP1, // KEY_KP1 = 0x4F; - KM_KBP_VKEY_NP2, // KEY_KP2 = 0x50; - KM_KBP_VKEY_NP3, // KEY_KP3 = 0x51; - KM_KBP_VKEY_NP0, // KEY_KP0 = 0x52; - KM_KBP_VKEY_NPDOT, // KEY_KPDOT = 0x53; + KM_CORE_VKEY_NP7, // KEY_KP7 = 0x47; + KM_CORE_VKEY_NP8, // KEY_KP8 = 0x48; + KM_CORE_VKEY_NP9, // KEY_KP9 = 0x49; + KM_CORE_VKEY_NPMINUS, // KEY_KPMINUS = 0x4A; + KM_CORE_VKEY_NP4, // KEY_KP4 = 0x4B; + KM_CORE_VKEY_NP5, // KEY_KP5 = 0x4C; + KM_CORE_VKEY_NP6, // KEY_KP6 = 0x4D; + KM_CORE_VKEY_NPPLUS, // KEY_KPPLUS = 0x4E; + KM_CORE_VKEY_NP1, // KEY_KP1 = 0x4F; + KM_CORE_VKEY_NP2, // KEY_KP2 = 0x50; + KM_CORE_VKEY_NP3, // KEY_KP3 = 0x51; + KM_CORE_VKEY_NP0, // KEY_KP0 = 0x52; + KM_CORE_VKEY_NPDOT, // KEY_KPDOT = 0x53; 0, // padding 0x54; 0, // KEY_ZENKAKUHANKAKU = 0x55; - KM_KBP_VKEY_oE2, // KEY_102ND = 0x56; - KM_KBP_VKEY_F11, // KEY_F11 = 0x57; - KM_KBP_VKEY_F12, // KEY_F12 = 0x58; + KM_CORE_VKEY_oE2, // KEY_102ND = 0x56; + KM_CORE_VKEY_F11, // KEY_F11 = 0x57; + KM_CORE_VKEY_F12, // KEY_F12 = 0x58; 0, // KEY_RO = 0x59; 0, // KEY_KATAKANA = 0x5a; 0, // KEY_HIRAGANA = 0x5b; @@ -104,10 +104,10 @@ static km_kbp_virtual_key const keycode_to_vk[256] = { 0, // KEY_MUHENKAN = 0x5e; 0, // KEY_KPJPCOMMA = 0x5f; 0, // KEY_KPENTER = 0x60; - KM_KBP_VKEY_CONTROL, // KEY_RIGHTCTRL = 0x61; + KM_CORE_VKEY_CONTROL, // KEY_RIGHTCTRL = 0x61; 0, // KEY_KPSLASH = 0x62; 0, // KEY_SYSRQ = 0x63; - KM_KBP_VKEY_ALT // KEY_RIGHTALT = 0x64; + KM_CORE_VKEY_ALT // KEY_RIGHTALT = 0x64; // Many more KEYS currently not used by KMW... }; diff --git a/linux/ibus-keyman/src/keymanutil.c b/linux/ibus-keyman/src/keymanutil.c index 9eb42c173b2..4f797aa07b9 100644 --- a/linux/ibus-keyman/src/keymanutil.c +++ b/linux/ibus-keyman/src/keymanutil.c @@ -55,18 +55,19 @@ #include #include #include -#include +#include #include "bcp47util.h" -#include "keymanutil.h" #include "kmpdetails.h" #include "keyman-version.h" +#include "keymanutil.h" +#include "keymanutil_internal.h" #define N_(text) text // change to keyman_get_kmpdirs_fromdir // returns list of directories with kmp.json -GList * keyman_get_kmpdirs_fromdir( GList *keyboard_list, const gchar * path) +GList * keyman_get_kmpdirs_fromdir(GList *kmpdir_list, const gchar * path) { DIR *dir = opendir(path); @@ -74,39 +75,38 @@ GList * keyman_get_kmpdirs_fromdir( GList *keyboard_list, const gchar * path) struct dirent *file = readdir(dir); while (file != NULL) { struct stat filestat; - gchar * absfn = g_strdup_printf("%s/%s", path, file->d_name); + g_autofree gchar *absfn = g_strdup_printf("%s/%s", path, file->d_name); stat(absfn, &filestat); if (S_ISDIR(filestat.st_mode)) { if(g_strcmp0(file->d_name, ".") != 0 && g_strcmp0(file->d_name, "..") != 0) - keyboard_list = keyman_get_kmpdirs_fromdir(keyboard_list, absfn); + kmpdir_list = keyman_get_kmpdirs_fromdir(kmpdir_list, absfn); } // Looking for kmp.json else if (S_ISREG(filestat.st_mode) && g_strcmp0(file->d_name, "kmp.json") == 0) { g_message("adding kmp path %s", path); - keyboard_list=g_list_append(keyboard_list, g_strdup(path)); + kmpdir_list=g_list_append(kmpdir_list, g_strdup(path)); } - g_free(absfn); file = readdir(dir); } closedir(dir); } - return keyboard_list; + return kmpdir_list; } gchar * keyman_get_icon_file(const gchar *kmx_file) { // Now there will only be the .png // which at some point will get extracted from the .kmx during installation - gchar *filename, *full_path_to_icon_file, *p; + gchar *full_path_to_icon_file, *p; + g_autofree gchar *filename; - p=rindex(kmx_file,'.'); + p = rindex(kmx_file, '.'); filename = g_strndup(kmx_file, p-kmx_file); full_path_to_icon_file=g_strdup_printf("%s.bmp.png", filename); - g_free(filename); if (!g_file_test(full_path_to_icon_file, G_FILE_TEST_EXISTS)) { g_free(full_path_to_icon_file); @@ -115,164 +115,172 @@ gchar * keyman_get_icon_file(const gchar *kmx_file) return full_path_to_icon_file; } -static IBusEngineDesc * -ibus_keyman_engine_desc_new (gchar * file_name, - gchar *name, - gchar *description, - gchar *copyright, - gchar *lang, - gchar *license, - gchar *author, - gchar *icon, - gchar *layout, - gchar *version) -{ - IBusEngineDesc *engine_desc; - gchar * desc; - - if (description == NULL) { - desc = g_strdup_printf("%s", copyright); - } - else { - desc = g_strdup_printf("%s\n%s", description, copyright); - } - - engine_desc = ibus_engine_desc_new_varargs ("name", file_name, - "longname", name, - "description", desc, - "language", lang ? lang : "other", - "license", license ? license : "", - "author", author ? author : "", - "icon", icon, - "layout", layout, - "version", version ? version : "", - NULL); - g_free(desc); +IBusEngineDesc * +ibus_keyman_engine_desc_new( + gchar *file_name, + gchar *name, + gchar *description, + gchar *copyright, + gchar *lang, + gchar *license, + gchar *author, + gchar *icon, + gchar *layout, + gchar *version +) { + IBusEngineDesc *engine_desc; + g_autofree gchar *desc; + + if (description == NULL) { + desc = g_strdup_printf("%s", copyright); + } else { + desc = g_strdup_printf("%s\n%s", description, copyright); + } + + engine_desc = ibus_engine_desc_new_varargs( + "name", file_name, "longname", name, "description", desc, "language", lang ? lang : "other", "license", + license ? license : "", "author", author ? author : "", "icon", icon, "layout", layout, "version", version ? version : "", + NULL); + g_object_ref(engine_desc); + return engine_desc; +} +IBusEngineDesc * +get_engine_for_language( + kmp_keyboard *keyboard, + kmp_info *info, + keyboard_details *kbd_details, + gchar *kmp_dir, + gchar *lang_id, + gchar *lang_name) { + IBusEngineDesc* engine_desc = NULL; + if (!lang_id || !strlen(lang_id)) return engine_desc; -} -GList * -ibus_keyman_add_engines(GList * engines, GList * kmpdir_list) -{ - GList *p, *k, *l, *e; - - for (p=kmpdir_list; p != NULL; p = p->next) { - gchar * kmp_dir = (gchar *) p->data; - - kmp_details *details = g_new0(kmp_details, 1); - get_kmp_details(kmp_dir, details); - - for (k=details->keyboards; k != NULL; k = k->next) { - kmp_keyboard *keyboard = (kmp_keyboard *) k->data; - gboolean alreadyexists = FALSE; - - for (e=engines; e != NULL && alreadyexists == FALSE; e = e->next) { - IBusEngineDesc *engine_desc = (IBusEngineDesc *) e->data; - const gchar *version = ibus_engine_desc_get_version(engine_desc); - const gchar *engine_name = ibus_engine_desc_get_name(engine_desc); - gchar *kmx_file = g_path_get_basename(engine_name); - if (g_strcmp0(kmx_file, keyboard->kmx_file) == 0 && g_strcmp0(version, keyboard->version) >= 0) { - alreadyexists = TRUE; - g_debug("keyboard %s already exists at version %s which is newer or same as %s", kmx_file, version, keyboard->version); - } - g_free(kmx_file); - } + int capacity = 255; + g_autofree gchar *name_with_lang = NULL; + g_autofree gchar *minimized_tag = g_new0(gchar, capacity); + int result = bcp47_minimize(lang_id, minimized_tag, capacity); + if (result < 0) { + g_strlcpy(minimized_tag, lang_id, capacity); + } + + g_autofree gchar *lang_code = g_new0(gchar, capacity); + if (!bcp47_get_language_code(minimized_tag, lang_code, capacity)) { + g_strlcpy(lang_code, minimized_tag, capacity); + } + + // If ibus doesn't know about the language then append the + // language name to the keyboard name + if (lang_name != NULL) { + g_autofree gchar *ibus_lang = ibus_get_untranslated_language_name(lang_code); + g_debug("%s: untranslated ibus language for %s: %s", __FUNCTION__, minimized_tag, ibus_lang); + if (g_strcmp0(ibus_lang, "Other") == 0) { + name_with_lang = g_strjoin(" - ", keyboard->name, lang_name, NULL); + } + } + + g_autofree gchar *abs_kmx = g_strjoin("/", kmp_dir, keyboard->kmx_file, NULL); + g_autofree gchar *id_with_lang = g_strjoin(":", minimized_tag, abs_kmx, NULL); + + g_message("adding engine %s", id_with_lang); + engine_desc = ibus_keyman_engine_desc_new( + id_with_lang, // lang:kmx full path + name_with_lang ? name_with_lang : keyboard->name, // longname + kbd_details->description, // description + info->copyright, // copyright if available + lang_code, // language, most are ignored by ibus except major languages + kbd_details->license, // license + info->author_desc, // author name only, not email + keyman_get_icon_file(abs_kmx), // icon full path + "us", // layout defaulting to us (en-US) + keyboard->version); + return engine_desc; +} - if (!alreadyexists) { - gchar *abs_kmx = g_strjoin("/", kmp_dir, keyboard->kmx_file, NULL); - gchar *json_file = g_strjoin(".", keyboard->id, "json", NULL); - keyboard_details *kbd_details = g_new0(keyboard_details, 1); - get_keyboard_details(kmp_dir, json_file, kbd_details); - g_free(json_file); - - if (keyboard->languages != NULL) { - for (l=keyboard->languages; l != NULL; l = l->next) { - kmp_language *language = (kmp_language *) l->data; - if (language->id != NULL) { - int capacity = 255; - gchar *name_with_lang = NULL; - gchar *minimized_tag = g_new0(gchar, capacity); - int result = bcp47_minimize(language->id, minimized_tag, capacity); - if (result < 0) { - g_strlcpy(minimized_tag, language->id, capacity); - } - - gchar *lang_code = g_new0(gchar, capacity); - if (!bcp47_get_language_code(minimized_tag, lang_code, capacity)) { - g_strlcpy(lang_code, minimized_tag, capacity); - } - - // If ibus doesn't know about the language then append the - // language name to the keyboard name - if (language->name != NULL) { - gchar *ibus_lang = ibus_get_untranslated_language_name(lang_code); - g_debug("%s: untranslated ibus language for %s: %s", __FUNCTION__, minimized_tag, ibus_lang); - if (g_strcmp0(ibus_lang, "Other") == 0) { - name_with_lang = g_strjoin(" - ", keyboard->name, language->name, NULL); - } - g_free(ibus_lang); - } - - gchar *id_with_lang = g_strjoin(":", minimized_tag, abs_kmx, NULL); - - g_message("adding engine %s", id_with_lang); - engines = g_list_append( - engines, - ibus_keyman_engine_desc_new( - id_with_lang, // lang:kmx full path - name_with_lang ? name_with_lang : keyboard->name, // longname - kbd_details->description, // description - details->info.copyright, // copyright if available - lang_code, // language, most are ignored by ibus except major languages - kbd_details->license, // license - details->info.author_desc, // author name only, not email - keyman_get_icon_file(abs_kmx), // icon full path - "us", // layout defaulting to us (en-US) - keyboard->version)); - g_free(lang_code); - g_free(minimized_tag); - g_free(id_with_lang); - g_free(name_with_lang); - } - } - } - else { - g_message("adding engine %s", abs_kmx); - engines = g_list_append (engines, - ibus_keyman_engine_desc_new (abs_kmx, // kmx full path - keyboard->name, // longname - kbd_details->description, // description - details->info.copyright, // copyright if available - NULL, // language, most are ignored by ibus except major languages - kbd_details->license, // license - details->info.author_desc, // author name only, not email - keyman_get_icon_file(abs_kmx), // icon full path - "us", // layout defaulting to us (en-US) - keyboard->version)); - } - free_keyboard_details(kbd_details); - g_free(kbd_details); - g_free(abs_kmx); - } - } - free_kmp_details(details); - g_free(details); +// Add a keyboard (ibus engine) to the list of engines +void +keyman_add_keyboard(gpointer data, gpointer user_data) { + kmp_keyboard *keyboard = (kmp_keyboard *)data; + add_keyboard_data *kb_data = (add_keyboard_data *)user_data; + + for (GList *e = kb_data->engines_list; e != NULL; e = e->next) { + IBusEngineDesc *engine_desc = (IBusEngineDesc *)e->data; + const gchar *version = ibus_engine_desc_get_version(engine_desc); + const gchar *engine_name = ibus_engine_desc_get_name(engine_desc); + g_autofree gchar *kmx_file = g_path_get_basename(engine_name); + // If we already have an engine for this keyboard (in a different area), we + // don't want to add it again since we wouldn't add anything new + // if it's the same version + // TODO: fix version comparison (#9593) + if (g_strcmp0(kmx_file, keyboard->kmx_file) == 0 && g_strcmp0(version, keyboard->version) >= 0) { + g_debug("keyboard %s already exists at version %s which is newer or same as %s", kmx_file, version, keyboard->version); + return; } - return engines; + } + + g_autofree gchar *json_file = g_strjoin(".", keyboard->id, "json", NULL); + g_autoptr(keyboard_details) kbd_details = g_new0(keyboard_details, 1); + get_keyboard_details(kb_data->kmp_dir, json_file, kbd_details); + g_autofree gchar *abs_kmx = g_strjoin("/", kb_data->kmp_dir, keyboard->kmx_file, NULL); + + if (keyboard->languages != NULL) { + for (GList *l = keyboard->languages; l != NULL; l = l->next) { + kmp_language *language = (kmp_language *)l->data; + IBusEngineDesc *engine_desc = + get_engine_for_language(keyboard, kb_data->info, kbd_details, kb_data->kmp_dir, language->id, language->name); + if (engine_desc) { + kb_data->engines_list = g_list_append(kb_data->engines_list, engine_desc); + } + } + } else { + g_message("adding engine %s", abs_kmx); + kb_data->engines_list = g_list_append( + kb_data->engines_list, + ibus_keyman_engine_desc_new( + abs_kmx, // kmx full path + keyboard->name, // longname + kbd_details->description, // description + kb_data->info->copyright, // copyright if available + NULL, // language, most are ignored by ibus except major languages + kbd_details->license, // license + kb_data->info->author_desc, // author name only, not email + keyman_get_icon_file(abs_kmx), // icon full path + "us", // layout defaulting to us (en-US) + keyboard->version)); + } +} + +// Add keyboards found in {kmp_dir}/kmp.json to engines_list +void +keyman_add_keyboards_from_dir(gpointer data, gpointer user_data) { + gchar * kmp_dir = (gchar *) data; + GList ** engines_list = (GList **)user_data; + + g_autoptr(kmp_details) details = g_new0(kmp_details, 1); + if (get_kmp_details(kmp_dir, details) == JSON_OK) { + add_keyboard_data kb_data; + kb_data.engines_list = *engines_list; + kb_data.info = &details->info; + kb_data.kmp_dir = kmp_dir; + + g_list_foreach(details->keyboards, keyman_add_keyboard, &kb_data); + *engines_list = kb_data.engines_list; + } } GList * ibus_keyman_list_engines (void) { GList *engines = NULL; - GList *keyboard_list; - gchar *local_keyboard_path, *xdgenv; + GList *kmpdir_list; + gchar *xdgenv; + g_autofree gchar *local_keyboard_path; g_debug("adding from /usr/share/keyman"); - keyboard_list = keyman_get_kmpdirs_fromdir(NULL, "/usr/share/keyman"); + kmpdir_list = keyman_get_kmpdirs_fromdir(NULL, "/usr/share/keyman"); g_debug("adding from /usr/local/share/keyman"); - keyboard_list = keyman_get_kmpdirs_fromdir(keyboard_list, "/usr/local/share/keyman"); + kmpdir_list = keyman_get_kmpdirs_fromdir(kmpdir_list, "/usr/local/share/keyman"); xdgenv = getenv("XDG_DATA_HOME"); if (xdgenv != NULL){ local_keyboard_path= g_strdup_printf("%s/keyman", xdgenv); @@ -282,10 +290,9 @@ ibus_keyman_list_engines (void) local_keyboard_path= g_strdup_printf("%s/.local/share/keyman", xdgenv); } g_debug("adding from %s", local_keyboard_path); - keyboard_list = keyman_get_kmpdirs_fromdir(keyboard_list, local_keyboard_path); - g_free(local_keyboard_path); - engines = ibus_keyman_add_engines(engines, keyboard_list); - g_list_free(keyboard_list); + kmpdir_list = keyman_get_kmpdirs_fromdir(kmpdir_list, local_keyboard_path); + g_list_foreach(kmpdir_list, keyman_add_keyboards_from_dir, &engines); + g_list_free_full(kmpdir_list, g_free); return engines; } @@ -294,13 +301,12 @@ void add_engine(gpointer data, gpointer user_data) { IBusEngineDesc *desc = IBUS_ENGINE_DESC(data); IBusComponent *component = IBUS_COMPONENT(user_data); - ibus_component_add_engine(component, desc); + ibus_component_add_engine(component, g_object_ref(desc)); } IBusComponent * ibus_keyman_get_component (void) { - GList *engines; IBusComponent *component; component = ibus_component_new ("org.freedesktop.IBus.Keyman", @@ -312,9 +318,8 @@ ibus_keyman_get_component (void) "", "ibus-keyman"); - engines = ibus_keyman_list_engines (); + g_autolist(IBusEngineDesc) engines = ibus_keyman_list_engines(); g_list_foreach(engines, add_engine, component); - g_list_free (engines); return component; } @@ -335,7 +340,7 @@ keyman_get_options_fromdconf(gchar *package_id, g_message("keyman_get_options_fromdconf"); // Obtain keyboard options from DConf - gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, package_id, keyboard_id); + g_autofree gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, package_id, keyboard_id); GSettings *child_settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); gchar **options = NULL; if (child_settings != NULL) @@ -344,12 +349,11 @@ keyman_get_options_fromdconf(gchar *package_id, } g_object_unref(G_OBJECT(child_settings)); - g_free(path); return options; } -// Obtain Keyboard Options from DConf and parse into a GQueue of struct km_kbp_option_item +// Obtain Keyboard Options from DConf and parse into a GQueue of struct km_core_option_item // // Parameters: // package_id (gchar *): Package ID @@ -364,7 +368,7 @@ keyman_get_options_queue_fromdconf(gchar *package_id, GQueue *queue_options = g_queue_new(); // Obtain keyboard options from DConf - gchar **options = keyman_get_options_fromdconf(package_id, keyboard_id); + g_auto(GStrv) options = keyman_get_options_fromdconf(package_id, keyboard_id); // Parse options into queue_options if (options != NULL) @@ -372,13 +376,13 @@ keyman_get_options_queue_fromdconf(gchar *package_id, int index = 0; while (options[index] != NULL) { - gchar **option_tokens = g_strsplit(options[index], "=", 2); + g_auto(GStrv) option_tokens = g_strsplit(options[index], "=", 2); if (option_tokens != NULL && option_tokens[0] != NULL && option_tokens[1] != NULL) { g_message("Keyboard Option [%d], %s=%s", index, option_tokens[0], option_tokens[1]); - km_kbp_option_item *opt = g_new0(km_kbp_option_item, 1); - opt[0].scope = KM_KBP_OPT_KEYBOARD; - km_kbp_cp *ocp = g_utf8_to_utf16(option_tokens[0], -1, NULL, NULL, NULL); + km_core_option_item *opt = g_new0(km_core_option_item, 1); + opt[0].scope = KM_CORE_OPT_KEYBOARD; + km_core_cp *ocp = g_utf8_to_utf16(option_tokens[0], -1, NULL, NULL, NULL); opt[0].key = ocp; ocp = g_utf8_to_utf16 (option_tokens[1], -1, NULL, NULL, NULL); opt[0].value = ocp; @@ -386,7 +390,6 @@ keyman_get_options_queue_fromdconf(gchar *package_id, } index++; } - g_strfreev(options); } return queue_options; @@ -415,60 +418,46 @@ keyman_put_options_todconf(gchar *package_id, } // Obtain keyboard options from DConf - gchar **options = keyman_get_options_fromdconf(package_id, keyboard_id); - gchar *needle = g_strdup_printf("%s=", option_key); + g_auto(GStrv) options = keyman_get_options_fromdconf(package_id, keyboard_id); + g_autofree gchar *needle = g_strdup_printf("%s=", option_key); gchar *kvp = g_strdup_printf("%s=%s", option_key, option_value); - if (options != NULL) - { - int index = 0; - gboolean option_updated = FALSE; - while (options[index] != NULL) - { - // If option_key already exists, update value with option_value - if (g_strrstr(options[index], needle) != NULL) - { - g_free(options[index]); - options[index] = kvp; - option_updated = TRUE; - break; - } - index++; - } + g_assert(options != NULL); - if (!option_updated) + int index = 0; + gboolean option_updated = FALSE; + while (options[index] != NULL) + { + // If option_key already exists, update value with option_value + if (g_strrstr(options[index], needle) != NULL) { - // Resize to add new option and null-terminate - int size = index + 2; // old size: index + 1, plus 1 new - options = g_renew(gchar*, options, size); + g_free(options[index]); options[index] = kvp; - options[index+1] = NULL; + option_updated = TRUE; + break; } + index++; } - else + + if (!option_updated) { - // we never should come here - keyman_get_options_fromdconf will create empty - // options if they don't yet exist. - // Allocate space for new option and null-terminate - options = g_new(gchar *, 2); - options[0] = kvp; - options[1] = NULL; + // Resize to add new option and null-terminate + int size = index + 2; // old size: index + 1, plus 1 new + options = g_renew(gchar*, options, size); + options[index] = kvp; + options[index+1] = NULL; } // Write to DConf - gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, package_id, keyboard_id); - GSettings *child_settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); + g_autofree gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, package_id, keyboard_id); + g_autoptr(GSettings) child_settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); if (child_settings != NULL) { g_message("writing keyboard options to DConf"); g_settings_set_strv(child_settings, KEYMAN_DCONF_OPTIONS_KEY, (const gchar *const *)options); } - g_object_unref(G_OBJECT(child_settings)); - g_free(path); - g_free(needle); - g_strfreev(options); - // kvp got assigned to options[x] and so got freed by g_strfreev() + // kvp got assigned to options[x] and so gets freed when options are freed } diff --git a/linux/ibus-keyman/src/keymanutil.h b/linux/ibus-keyman/src/keymanutil.h index 4b358c02ead..de1aef49fab 100644 --- a/linux/ibus-keyman/src/keymanutil.h +++ b/linux/ibus-keyman/src/keymanutil.h @@ -55,7 +55,7 @@ #include #include -#include +#include // Number of default Keyboard processor environment options for: "platform", "baseLayout", and "baseLayoutAlt" #define KEYMAN_ENVIRONMENT_OPTIONS 3 @@ -87,7 +87,7 @@ gchar** keyman_get_options_fromdconf (gchar *package_id, gchar *keyboard_id); -// Obtain Keyboard Options from DConf and parse into a GQueue of struct km_kbp_option_item +// Obtain Keyboard Options from DConf and parse into a GQueue of struct km_core_option_item // // Parameters: // package_id (gchar *): Package ID diff --git a/linux/ibus-keyman/src/keymanutil_internal.h b/linux/ibus-keyman/src/keymanutil_internal.h new file mode 100644 index 00000000000..614bf737121 --- /dev/null +++ b/linux/ibus-keyman/src/keymanutil_internal.h @@ -0,0 +1,42 @@ +// Internal data structures used in the implementation of keymanutil methods +// and exposed for unit testing. + +#ifndef __KEYMANUTIL_INTERNAL_H__ +#define __KEYMANUTIL_INTERNAL_H__ + +#include +#include "kmpdetails.h" + +typedef struct { + GList *engines_list; + kmp_info *info; + gchar *kmp_dir; +} add_keyboard_data; + +IBusEngineDesc *ibus_keyman_engine_desc_new( + gchar *file_name, + gchar *name, + gchar *description, + gchar *copyright, + gchar *lang, + gchar *license, + gchar *author, + gchar *icon, + gchar *layout, + gchar *version); + +IBusEngineDesc *get_engine_for_language( + kmp_keyboard *keyboard, + kmp_info *info, + keyboard_details *kbd_details, + gchar *kmp_dir, + gchar *lang_id, + gchar *lang_name); + +void keyman_add_keyboard(gpointer data, gpointer user_data); +void keyman_add_keyboards_from_dir(gpointer data, gpointer user_data); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(IBusEngineDesc, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(IBusComponent, g_object_unref) + +#endif // __KEYMANUTIL_INTERNAL_H__ diff --git a/linux/ibus-keyman/src/kmpdetails.c b/linux/ibus-keyman/src/kmpdetails.c index eba95f4ef97..4748fc08bd4 100644 --- a/linux/ibus-keyman/src/kmpdetails.c +++ b/linux/ibus-keyman/src/kmpdetails.c @@ -158,7 +158,8 @@ void kmp_keyboards_foreach (JsonArray *array, JsonObject *keyboard_object; JsonNode *languages_node; GList *l; - gchar *l_kmx_file, *l_kvk_file; + g_autofree gchar *l_kmx_file = NULL; + g_autofree gchar *l_kvk_file = NULL; kmp_fileinfo *fileinfo; kmp_details *details; kmp_keyboard *keyboard; @@ -201,8 +202,6 @@ void kmp_keyboards_foreach (JsonArray *array, } } } - g_free(l_kmx_file); - g_free(l_kvk_file); } details->keyboards = g_list_append(details->keyboards, keyboard); } @@ -232,17 +231,16 @@ void kmp_files_foreach (JsonArray *array, kmp_json_status get_keyboard_details(const char *kmp_dir, const char *id, keyboard_details *keyboard) { - JsonParser *parser; + g_autoptr(JsonParser) parser; JsonObject *object; JsonNode *root; - gchar *json_file; - GError *error = NULL; + g_autofree gchar *json_file; + g_autoptr(GError) error = NULL; parser = json_parser_new (); json_file = g_strjoin("/", kmp_dir, id, NULL); if (!g_file_test(json_file, G_FILE_TEST_EXISTS)) { - g_free(json_file); return JSON_FILE_NOT_EXISTS; } @@ -252,12 +250,8 @@ kmp_json_status get_keyboard_details(const char *kmp_dir, const char *id, keyboa if (error) { g_warning ("Unable to parse `%s': %s\n", json_file, error->message); - g_free(json_file); - g_error_free (error); - g_object_unref (parser); return JSON_PARSE_ERROR; } - g_free(json_file); root = json_parser_get_root (parser); @@ -270,25 +264,23 @@ kmp_json_status get_keyboard_details(const char *kmp_dir, const char *id, keyboa get_detail_from_object(object, "license", &(keyboard->license)); } - g_object_unref(parser); return JSON_OK; } kmp_json_status get_kmp_details(const char *kmp_dir, kmp_details *details) { - JsonParser *parser; + g_autoptr(JsonParser) parser; JsonNode *root; JsonObject *root_object, *object; JsonArray *array; JsonNode *kmp_system, *kmp_info, *kmp_options, *kmp_files, *kmp_keyboards; - gchar *kmp_json; - GError *error = NULL; + g_autofree gchar *kmp_json; + g_autoptr(GError) error = NULL; parser = json_parser_new (); kmp_json = g_strdup_printf("%s/kmp.json", kmp_dir); if (!g_file_test(kmp_json, G_FILE_TEST_EXISTS)) { - g_free(kmp_json); return JSON_FILE_NOT_EXISTS; } @@ -298,12 +290,8 @@ kmp_json_status get_kmp_details(const char *kmp_dir, kmp_details *details) if (error) { g_warning ("Unable to parse `%s': %s\n", kmp_json, error->message); - g_free(kmp_json); - g_error_free (error); - g_object_unref (parser); return JSON_PARSE_ERROR; } - g_free(kmp_json); root = json_parser_get_root (parser); @@ -369,7 +357,6 @@ kmp_json_status get_kmp_details(const char *kmp_dir, kmp_details *details) } } } - g_object_unref(parser); return JSON_OK; } @@ -425,6 +412,7 @@ void free_language(gpointer data) kmp_language *language = (kmp_language *) data; g_free(language->name); g_free(language->id); + g_free(language); } void free_keyboard(gpointer data) @@ -438,6 +426,7 @@ void free_keyboard(gpointer data) if (keyboard->languages != NULL) { g_list_free_full(keyboard->languages, (GDestroyNotify)free_language); } + g_free(keyboard); } void free_fileinfo(gpointer data) @@ -445,6 +434,7 @@ void free_fileinfo(gpointer data) kmp_fileinfo *fileinfo = (kmp_fileinfo *) data; g_free(fileinfo->name); g_free(fileinfo->description); + g_free(fileinfo); } kmp_json_status free_keyboard_details(keyboard_details *kbd_details) @@ -452,28 +442,44 @@ kmp_json_status free_keyboard_details(keyboard_details *kbd_details) g_free(kbd_details->id); g_free(kbd_details->description); g_free(kbd_details->license); + g_free(kbd_details); return JSON_OK; } +void free_info(gpointer data) { + kmp_info *info = (kmp_info *)data; + if (info->name) + g_free(info->name); + if (info->version) + g_free(info->version); + if (info->copyright) + g_free(info->copyright); + if (info->author_desc) + g_free(info->author_desc); + if (info->author_url) + g_free(info->author_url); + if (info->website_desc) + g_free(info->website_desc); + if (info->website_url) + g_free(info->website_url); +} + kmp_json_status free_kmp_details(kmp_details * details) { - g_assert(details != NULL); + if (details == NULL) + return JSON_OK; + g_free(details->system.fileVersion); g_free(details->system.keymanDeveloperVersion); g_free(details->options.readmeFile); g_free(details->options.graphicFile); - g_free(details->info.name); - g_free(details->info.version); - g_free(details->info.copyright); - g_free(details->info.author_desc); - g_free(details->info.author_url); - g_free(details->info.website_desc); - g_free(details->info.website_url); + free_info(&details->info); if (details->keyboards != NULL) { - g_list_free_full(details->keyboards, (GDestroyNotify)free_keyboard); + g_list_free_full(details->keyboards, (GDestroyNotify)free_keyboard); } if (details->files != NULL) { g_list_free_full(details->files, (GDestroyNotify)free_fileinfo); } + g_free(details); return JSON_OK; } diff --git a/linux/ibus-keyman/src/kmpdetails.h b/linux/ibus-keyman/src/kmpdetails.h index 3bcf6a97390..dab05328cf6 100644 --- a/linux/ibus-keyman/src/kmpdetails.h +++ b/linux/ibus-keyman/src/kmpdetails.h @@ -1,3 +1,6 @@ +#ifndef __KMPDETAILS_H__ +#define __KMPDETAILS_H__ + // kmp details from json #include @@ -75,4 +78,13 @@ kmp_json_status get_kmp_details(const gchar *kmp_dir, kmp_details *details); kmp_json_status free_kmp_details(kmp_details * details); kmp_json_status get_keyboard_details(const gchar *kmp_dir, const gchar *id, keyboard_details *details); kmp_json_status free_keyboard_details(keyboard_details * details); -kmp_json_status print_kmp_details(kmp_details * details); \ No newline at end of file +kmp_json_status print_kmp_details(kmp_details * details); +void free_keyboard(gpointer data); +void free_info(gpointer data); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(kmp_keyboard, free_keyboard) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(kmp_info, free_info) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(kmp_details, free_kmp_details) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(keyboard_details, free_keyboard_details) + +#endif // __KMPDETAILS_H__ diff --git a/linux/ibus-keyman/src/main.c b/linux/ibus-keyman/src/main.c index 80e67458bc6..477d744d9ba 100644 --- a/linux/ibus-keyman/src/main.c +++ b/linux/ibus-keyman/src/main.c @@ -28,6 +28,7 @@ #include "engine.h" #include "keyman-service.h" #include "keymanutil.h" +#include "keymanutil_internal.h" static IBusBus *bus = NULL; static IBusFactory *factory = NULL; @@ -52,8 +53,8 @@ ibus_disconnected_cb(IBusBus *unused_bus, gpointer unused_data) { KeymanService *service = km_service_get_default(NULL); g_clear_object(&service); - g_object_unref(factory); - g_object_unref(bus); + if (factory) g_object_unref(factory); + if (bus) g_object_unref(bus); ibus_quit(); } @@ -66,26 +67,29 @@ add_single_keyboard(gpointer data, gpointer user_data) { #else const gchar *engine_name = engine->name; #endif /* !IBUS_CHECK_VERSION(1,3,99) */ - ibus_factory_add_engine(factory, engine_name, IBUS_TYPE_KEYMAN_ENGINE); + if (engine_name) { + ibus_factory_add_engine(factory, engine_name, IBUS_TYPE_KEYMAN_ENGINE); + } else { + g_error("%s: Trying to add NULL engine", __FUNCTION__); + } } static void add_keyboards(IBusBus *bus, gpointer user_data) { - GList *engines; - IBusComponent *component; + g_autolist(IBusEngineDesc) engines; + g_autoptr(IBusComponent) component; g_message("Adding keyboards to ibus"); component = ibus_keyman_get_component(); GDBusConnection *connection = ibus_bus_get_connection(bus); - factory = ibus_factory_new(connection); + factory = ibus_factory_new(g_object_ref(connection)); g_signal_connect(bus, "disconnected", G_CALLBACK(ibus_disconnected_cb), NULL); engines = ibus_component_get_engines(component); g_list_foreach(engines, add_single_keyboard, NULL); - g_list_free(engines); if (ibus) { ibus_bus_request_name(bus, "org.freedesktop.IBus.Keyman", 0); @@ -93,7 +97,6 @@ add_keyboards(IBusBus *bus, gpointer user_data) { ibus_bus_register_component(bus, component); } - g_object_unref(component); km_service_get_default(NULL); // initialise dbus service } @@ -117,8 +120,8 @@ start_component(void) { static void print_engines_xml(void) { - IBusComponent *component; - GString *output; + g_autoptr(IBusComponent) component; + g_autoptr(GString) output; ibus_init(); @@ -128,9 +131,6 @@ print_engines_xml(void) { ibus_component_output_engines(component, output, 0); fprintf(stdout, "%s", output->str); - - g_string_free(output, TRUE); - g_object_unref(component); } int @@ -145,7 +145,9 @@ main(gint argc, gchar **argv) { g_option_context_add_main_entries(context, entries, "ibus-keyman"); if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_assert(error != NULL); g_print("Option parsing failed: %s\n", error->message); + g_error_free(error); g_option_context_free(context); exit(-1); } diff --git a/linux/ibus-keyman/src/test/keymanutil_tests.c b/linux/ibus-keyman/src/test/keymanutil_tests.c index 80f77a72ea6..2be4c38bf26 100644 --- a/linux/ibus-keyman/src/test/keymanutil_tests.c +++ b/linux/ibus-keyman/src/test/keymanutil_tests.c @@ -1,77 +1,148 @@ #include #include #include +#include +#include "kmpdetails.h" #include "keymanutil.h" +#include "keymanutil_internal.h" #define TEST_FIXTURE "keymanutil-test" void -delete_options_key(gchar* testname) { - gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); - GSettings *settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); +_delete_tst_options_key(gchar* testname) { + g_autofree gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); + g_autoptr(GSettings) settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); g_settings_reset(settings, KEYMAN_DCONF_OPTIONS_KEY); - g_object_unref(G_OBJECT(settings)); - g_free(path); } void -set_options_key(gchar* testname, gchar** options) { - gchar *path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); - GSettings *settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); +_set_tst_options_key(gchar* testname, gchar** options) { + g_autofree gchar* path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); + g_autoptr(GSettings) settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); g_settings_set_strv(settings, KEYMAN_DCONF_OPTIONS_KEY, (const gchar* const*)options); - g_object_unref(G_OBJECT(settings)); - g_free(path); } gchar** -get_options_key(gchar* testname) { - gchar* path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); - GSettings* settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); +_get_tst_options_key(gchar* testname) { + g_autofree gchar* path = g_strdup_printf("%s%s/%s/", KEYMAN_DCONF_OPTIONS_PATH, TEST_FIXTURE, testname); + g_autoptr(GSettings) settings = g_settings_new_with_path(KEYMAN_DCONF_OPTIONS_CHILD_NAME, path); gchar** result = g_settings_get_strv(settings, KEYMAN_DCONF_OPTIONS_KEY); - g_object_unref(G_OBJECT(settings)); - g_free(path); return result; } +kmp_keyboard* +_get_tst_kmp_keyboard(gchar* version, gchar** languages) { + kmp_keyboard* keyboard = g_new0(kmp_keyboard, 1); + keyboard->name = g_strdup("Testing"); + keyboard->id = g_strdup("tst"); + keyboard->version = g_strdup(version); + keyboard->kmx_file = g_strdup("tst.kmx"); + keyboard->kvk_file = g_strdup(""); + keyboard->languages = NULL; + for (gchar* lang = *languages++; lang; lang = *languages++) { + gchar** tokens = g_strsplit(lang, ":", 2); + kmp_language* kmp_lang = g_new0(kmp_language, 1); + kmp_lang->id = g_strdup(tokens[0]); + kmp_lang->name = g_strdup(tokens[1]); + g_strfreev(tokens); + keyboard->languages = g_list_append(keyboard->languages, kmp_lang); + } + return keyboard; +} + +kmp_info* +_get_tst_kmp_info(gchar * copyright, gchar * author_desc, gchar * author_url) { + kmp_info* info = g_new0(kmp_info, 1); + info->copyright = g_strdup(copyright); + info->author_desc = g_strdup(author_desc); + info->author_url = g_strdup(author_url); + return info; +} + +keyboard_details* +_get_tst_keyboard_details(gchar * description, gchar * license) { + keyboard_details* details = g_new0(keyboard_details, 1); + details->id = g_strdup("tst"); + details->description = g_strdup(description); + details->license = g_strdup(license); + return details; +} + +add_keyboard_data* +_get_tst_keyboard_data() { + add_keyboard_data* kb_data = g_new0(add_keyboard_data, 1); + kb_data->engines_list = NULL; + kb_data->info = _get_tst_kmp_info(NULL, NULL, NULL); + kb_data->kmp_dir = "/tmp"; + return kb_data; +} + +void +_free_tst_kb_data(add_keyboard_data* kb_data) { + if (kb_data->engines_list) + g_list_free(kb_data->engines_list); + if (kb_data->info) + g_free(kb_data->info); + g_free(kb_data); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(add_keyboard_data, _free_tst_kb_data) + +//---------------------------------------------------------------------------------------------- +void +test_keyman_put_options_todconf__invalid() { + // Initialize + gchar* testname = "test_keyman_put_options_todconf__test_keyman_put_options_todconf__invalid"; + _delete_tst_options_key(testname); + + // Execute + keyman_put_options_todconf(TEST_FIXTURE, testname, "new_key", NULL); + + // Verify + g_auto(GStrv) options = _get_tst_options_key(testname); + g_assert_nonnull(options); + g_assert_null(options[0]); + + // Cleanup + _delete_tst_options_key(testname); +} + void test_keyman_put_options_todconf__new_key() { // Initialize gchar* testname = "test_keyman_put_options_todconf__new_key"; - delete_options_key(testname); - gchar* value = g_strdup_printf("%d", g_test_rand_int()); + _delete_tst_options_key(testname); + g_autofree gchar* value = g_strdup_printf("%d", g_test_rand_int()); // Execute keyman_put_options_todconf(TEST_FIXTURE, testname, "new_key", value); // Verify - gchar** options = get_options_key(testname); - gchar* expected = g_strdup_printf("new_key=%s", value); + g_auto(GStrv) options = _get_tst_options_key(testname); + g_autofree gchar* expected = g_strdup_printf("new_key=%s", value); g_assert_nonnull(options); g_assert_cmpstr(options[0], ==, expected); g_assert_null(options[1]); // Cleanup - g_free(expected); - g_free(value); - g_strfreev(options); - delete_options_key(testname); + _delete_tst_options_key(testname); } void test_keyman_put_options_todconf__other_keys() { // Initialize gchar* testname = "test_keyman_put_options_todconf__other_keys"; - delete_options_key(testname); + _delete_tst_options_key(testname); gchar* existingKeys[] = {"key1=val1", "key2=val2", NULL}; - set_options_key(testname, existingKeys); - gchar* value = g_strdup_printf("%d", g_test_rand_int()); + _set_tst_options_key(testname, existingKeys); + g_autofree gchar* value = g_strdup_printf("%d", g_test_rand_int()); // Execute keyman_put_options_todconf(TEST_FIXTURE, testname, "new_key", value); // Verify - gchar** options = get_options_key(testname); - gchar* expected = g_strdup_printf("new_key=%s", value); + g_auto(GStrv) options = _get_tst_options_key(testname); + g_autofree gchar* expected = g_strdup_printf("new_key=%s", value); g_assert_nonnull(options); g_assert_cmpstr(options[0], ==, "key1=val1"); g_assert_cmpstr(options[1], ==, "key2=val2"); @@ -79,50 +150,547 @@ test_keyman_put_options_todconf__other_keys() { g_assert_null(options[3]); // Cleanup - g_free(expected); - g_free(value); - g_strfreev(options); - delete_options_key(testname); + _delete_tst_options_key(testname); } void test_keyman_put_options_todconf__existing_key() { // Initialize gchar* testname = "test_keyman_put_options_todconf__existing_key"; - delete_options_key(testname); + _delete_tst_options_key(testname); gchar* existingKeys[] = {"key1=val1", "new_key=val2", NULL}; - set_options_key(testname, existingKeys); - gchar* value = g_strdup_printf("%d", g_test_rand_int()); + _set_tst_options_key(testname, existingKeys); + g_autofree gchar* value = g_strdup_printf("%d", g_test_rand_int()); // Execute keyman_put_options_todconf(TEST_FIXTURE, testname, "new_key", value); // Verify - gchar** options = get_options_key(testname); - gchar* expected = g_strdup_printf("new_key=%s", value); + g_auto(GStrv) options = _get_tst_options_key(testname); + g_autofree gchar* expected = g_strdup_printf("new_key=%s", value); g_assert_nonnull(options); g_assert_cmpstr(options[0], ==, "key1=val1"); g_assert_cmpstr(options[1], ==, expected); g_assert_null(options[2]); // Cleanup - g_free(expected); - g_free(value); - g_strfreev(options); - delete_options_key(testname); + _delete_tst_options_key(testname); } -int -main(int argc, char* argv[]) { +//---------------------------------------------------------------------------------------------- +void +test_ibus_keyman_engine_desc_new__all_set() { + // Execute + g_autoptr(IBusEngineDesc) desc = ibus_keyman_engine_desc_new("name", "longname", "description", "copyright", "lang", "license", "author", "icon", "layout", "version"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "name"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "longname"); + g_assert_cmpstr(ibus_engine_desc_get_description(desc), ==, "description\ncopyright"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "lang"); + g_assert_cmpstr(ibus_engine_desc_get_license(desc), ==, "license"); + g_assert_cmpstr(ibus_engine_desc_get_author(desc), ==, "author"); + g_assert_cmpstr(ibus_engine_desc_get_icon(desc), ==, "icon"); + g_assert_cmpstr(ibus_engine_desc_get_layout(desc), ==, "layout"); + g_assert_cmpstr(ibus_engine_desc_get_version(desc), ==, "version"); +} + +void +test_ibus_keyman_engine_desc_new__only_description() { + // Execute + g_autoptr(IBusEngineDesc) desc = ibus_keyman_engine_desc_new( + "name", "longname", "description", NULL, "lang", "license", "author", "icon", "layout", "version"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "name"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "longname"); + g_assert_cmpstr(ibus_engine_desc_get_description(desc), ==, "description\n(null)"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "lang"); + g_assert_cmpstr(ibus_engine_desc_get_license(desc), ==, "license"); + g_assert_cmpstr(ibus_engine_desc_get_author(desc), ==, "author"); + g_assert_cmpstr(ibus_engine_desc_get_icon(desc), ==, "icon"); + g_assert_cmpstr(ibus_engine_desc_get_layout(desc), ==, "layout"); + g_assert_cmpstr(ibus_engine_desc_get_version(desc), ==, "version"); +} + +void +test_ibus_keyman_engine_desc_new__only_copyright() { + // Execute + g_autoptr(IBusEngineDesc) desc = ibus_keyman_engine_desc_new( + "name", "longname", NULL, "copyright", "lang", "license", "author", "icon", "layout", "version"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "name"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "longname"); + g_assert_cmpstr(ibus_engine_desc_get_description(desc), ==, "copyright"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "lang"); + g_assert_cmpstr(ibus_engine_desc_get_license(desc), ==, "license"); + g_assert_cmpstr(ibus_engine_desc_get_author(desc), ==, "author"); + g_assert_cmpstr(ibus_engine_desc_get_icon(desc), ==, "icon"); + g_assert_cmpstr(ibus_engine_desc_get_layout(desc), ==, "layout"); + g_assert_cmpstr(ibus_engine_desc_get_version(desc), ==, "version"); +} + +void +test_ibus_keyman_engine_desc_new__no_language_license_author_version() { + // Execute + g_autoptr(IBusEngineDesc) desc = ibus_keyman_engine_desc_new( + "name", "longname", "description", "copyright", NULL, NULL, NULL, "icon", "layout", NULL); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "name"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "longname"); + g_assert_cmpstr(ibus_engine_desc_get_description(desc), ==, "description\ncopyright"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "other"); + g_assert_cmpstr(ibus_engine_desc_get_license(desc), ==, ""); + g_assert_cmpstr(ibus_engine_desc_get_author(desc), ==, ""); + g_assert_cmpstr(ibus_engine_desc_get_icon(desc), ==, "icon"); + g_assert_cmpstr(ibus_engine_desc_get_layout(desc), ==, "layout"); + g_assert_cmpstr(ibus_engine_desc_get_version(desc), ==, ""); +} + +//---------------------------------------------------------------------------------------------- +void +test_get_engine_for_language__null_language() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", NULL, NULL); + + // Verify + g_assert_null(desc); +} + +void +test_get_engine_for_language__empty_language() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "", ""); + + // Verify + g_assert_null(desc); +} + +void +test_get_engine_for_language__one_language() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "en", "English"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "Testing"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "en"); +} + +void +test_get_engine_for_language__one_unknown_language() { + // Initialize + gchar* languages[] = {"foo:Foo", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "foo", "Foo"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "foo:/tmp/tst.kmx"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "Testing - Foo"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "foo"); +} + +void +test_get_engine_for_language__different_languages() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "foo", "Foo"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "foo:/tmp/tst.kmx"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "Testing - Foo"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "foo"); +} + +void +test_get_engine_for_language__no_kbd_language() { + // Initialize + gchar* languages[] = {NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages); + g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com"); + g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT"); + + // Execute + g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "en", "English"); + + // Verify + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx"); + g_assert_cmpstr(ibus_engine_desc_get_longname(desc), ==, "Testing"); + g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "en"); +} + +//---------------------------------------------------------------------------------------------- +void +test_keyman_add_keyboard__no_language() { + // Initialize + gchar* languages[] = {NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages); + g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data(); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + g_assert_nonnull(kb_data->engines_list->data); + IBusEngineDesc* desc = (IBusEngineDesc*)kb_data->engines_list->data; + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "/tmp/tst.kmx"); + g_assert_null(kb_data->engines_list->next); +} + +void +test_keyman_add_keyboard__one_language() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages); + g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data(); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + g_assert_nonnull(kb_data->engines_list->data); + IBusEngineDesc* desc = (IBusEngineDesc*)kb_data->engines_list->data; + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx"); + g_assert_null(kb_data->engines_list->next); +} + +void +test_keyman_add_keyboard__two_languages() { + // Initialize + gchar* languages[] = {"en:English", "fr:French", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages); + g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data(); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + g_assert_nonnull(kb_data->engines_list->data); + IBusEngineDesc* desc = (IBusEngineDesc*)kb_data->engines_list->data; + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx"); + g_assert_nonnull(kb_data->engines_list->next); + g_assert_nonnull(kb_data->engines_list->next->data); + desc = (IBusEngineDesc*)kb_data->engines_list->next->data; + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "fr:/tmp/tst.kmx"); + g_assert_null(kb_data->engines_list->next->next); +} + +void +test_keyman_add_keyboard__prev_engine_adding_same_version() { + // Initialize + gchar* languages[] = {"en:English", NULL}; + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages); + g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data(); + IBusEngineDesc* desc = ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.0"); + kb_data->engines_list = g_list_append(kb_data->engines_list, desc); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + GList* list = kb_data->engines_list; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/usr/share/keyman/tst.kmx"); + g_assert_null(list->next); +} + +void +test_keyman_add_keyboard__prev_engine_adding_newer_version() { + // Initialize + gchar* languages[] = {"en:English", "fr:French", NULL}; // New version adds French + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.1", languages); + g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data(); + IBusEngineDesc* desc = + ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.0"); + kb_data->engines_list = g_list_append(kb_data->engines_list, desc); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + GList* list = kb_data->engines_list; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/usr/share/keyman/tst.kmx"); + g_assert_nonnull(list->next); + list = list->next; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/tmp/tst.kmx"); + g_assert_nonnull(list->next); + list = list->next; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "fr:/tmp/tst.kmx"); + g_assert_null(list->next); +} + +void +test_keyman_add_keyboard__prev_engine_adding_newer_version_9593() { + // This tests bug #9593: We add keyboard version 1.10 while 1.9 is already in + // the list. + + // Initialize + gchar* languages[] = {"en:English", "fr:French", NULL}; // New version adds French + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.10", languages); + g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data(); + IBusEngineDesc* desc = + ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.9"); + kb_data->engines_list = g_list_append(kb_data->engines_list, desc); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + GList* list = kb_data->engines_list; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/usr/share/keyman/tst.kmx"); + g_assert_nonnull(list->next); + list = list->next; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/tmp/tst.kmx"); + g_assert_nonnull(list->next); + list = list->next; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "fr:/tmp/tst.kmx"); + g_assert_null(list->next); +} + +void +test_keyman_add_keyboard__prev_engine_adding_older_version() { + // Initialize + gchar* languages[] = {"en:English", "fr:French", NULL}; // Old version has additional French + g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("0.9", languages); + g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data(); + IBusEngineDesc* desc = + ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.0"); + kb_data->engines_list = g_list_append(kb_data->engines_list, desc); + + // Execute + keyman_add_keyboard(keyboard, kb_data); + + // Verify + GList* list = kb_data->engines_list; + g_assert_nonnull(list->data); + g_assert_cmpstr(ibus_engine_desc_get_name((IBusEngineDesc*)list->data), ==, "en:/usr/share/keyman/tst.kmx"); + g_assert_null(list->next); +} + +//---------------------------------------------------------------------------------------------- +gchar* testdata_dir; + +gboolean +delete_directory(gchar* path) { + g_autoptr(GFile) file = g_file_new_for_path(path); + if (g_file_test(path, G_FILE_TEST_IS_DIR)) { + g_autoptr(GFileEnumerator) enumerator = NULL; + + enumerator = g_file_enumerate_children(file, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL); + + while (enumerator != NULL) { + GFile* child; + + if (!g_file_enumerator_iterate(enumerator, NULL, &child, NULL, NULL)) + return FALSE; + if (child == NULL) + break; + if (!delete_directory(g_file_get_path(child))) + return FALSE; + } + } + + return g_file_delete(file, NULL, NULL); +} + +void clear_kmpdir(gchar* dir) { + delete_directory(dir); + g_free(dir); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(gchar, clear_kmpdir); + +void copy_test_data(gchar* kmp_dir, gchar* name) { + g_autofree gchar* oldfile = g_strdup_printf("%s/%s", testdata_dir, name); + g_autoptr(GFile) source = g_file_new_for_path(oldfile); + g_autofree gchar* newfile = g_strdup_printf("%s/kmp.json", kmp_dir); + g_autoptr(GFile) dest = g_file_new_for_path(newfile); + g_file_copy(source, dest, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, NULL); +} + +void +test_keyman_add_keyboards_from_dir__no_kmpjson() { + // Initialize + g_autolist(GList) keyboards = NULL; + g_autoptr(gchar) kmp_dir = g_dir_make_tmp(NULL, NULL); + + // Execute + keyman_add_keyboards_from_dir(kmp_dir, &keyboards); + + // Verify + g_assert_cmpint(g_list_length(keyboards), ==, 0); +} + +void +test_keyman_add_keyboards_from_dir__one_dir() { + // Initialize + g_autoptr(GList) keyboards = NULL; + g_autoptr(gchar) kmp_dir = g_dir_make_tmp(NULL, NULL); + copy_test_data(kmp_dir, "kmp1.json"); + + // Execute + keyman_add_keyboards_from_dir(kmp_dir, &keyboards); + + // Verify + g_assert_cmpint(g_list_length(keyboards), ==, 1); + + IBusEngineDesc* desc = IBUS_ENGINE_DESC(keyboards->data); + g_autofree gchar* expected_name = g_strdup_printf("bza:%s/test1.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name); +} + +void +test_keyman_add_keyboards_from_dir__two_langs() { + // Initialize + g_autoptr(GList) keyboards = NULL; + g_autoptr(gchar) kmp_dir = g_dir_make_tmp(NULL, NULL); + copy_test_data(kmp_dir, "kmp2.json"); + + // Execute + keyman_add_keyboards_from_dir(kmp_dir, &keyboards); + + // Verify + g_assert_cmpint(g_list_length(keyboards), ==, 2); + + IBusEngineDesc* desc = IBUS_ENGINE_DESC(keyboards->data); + g_autofree gchar* expected_name1 = g_strdup_printf("bmf-Latn:%s/test2.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name1); + desc = IBUS_ENGINE_DESC(keyboards->next->data); + g_autofree gchar* expected_name2 = g_strdup_printf("bun-Latn:%s/test2.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name2); +} + +void +test_keyman_add_keyboards_from_dir__prev_keyboards() { + // Initialize + g_autoptr(GList) keyboards = NULL; + g_autoptr(gchar) kmp_dir = g_dir_make_tmp(NULL, NULL); + copy_test_data(kmp_dir, "kmp2.json"); + keyman_add_keyboards_from_dir(kmp_dir, &keyboards); + copy_test_data(kmp_dir, "kmp1.json"); + + // Execute + keyman_add_keyboards_from_dir(kmp_dir, &keyboards); + + // Verify + g_assert_cmpint(g_list_length(keyboards), ==, 3); + + IBusEngineDesc* desc = IBUS_ENGINE_DESC(keyboards->data); + g_autofree gchar* expected_name1 = g_strdup_printf("bmf-Latn:%s/test2.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name1); + desc = IBUS_ENGINE_DESC(keyboards->next->data); + g_autofree gchar* expected_name2 = g_strdup_printf("bun-Latn:%s/test2.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name2); + desc = IBUS_ENGINE_DESC(keyboards->next->next->data); + g_autofree gchar* expected_name = g_strdup_printf("bza:%s/test1.kmx", kmp_dir); + g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name); +} + +//---------------------------------------------------------------------------------------------- +void +print_usage() { + printf( + "Usage: %s --testdata \n\n", + g_get_prgname()); + printf("Arguments:\n"); + printf("\t--testdata \tThe directory containing test kmp.json files for the tests.\n\n"); +} + +int main(int argc, char* argv[]) { gtk_init(&argc, &argv); g_test_init(&argc, &argv, NULL); g_test_set_nonfatal_assertions(); + if (argc < 3 || strcmp(argv[1], "--testdata") != 0) { + print_usage(); + return 1; + } + + testdata_dir = argv[2]; + + if (!g_file_test(testdata_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + printf("ERROR: Testdata directory %s does not exist\n\n", testdata_dir); + print_usage(); + return 2; + } + // Add tests + g_test_add_func("/keymanutil/keyman_put_options_todconf/invalid", test_keyman_put_options_todconf__invalid); g_test_add_func("/keymanutil/keyman_put_options_todconf/new_key", test_keyman_put_options_todconf__new_key); g_test_add_func("/keymanutil/keyman_put_options_todconf/other_keys", test_keyman_put_options_todconf__other_keys); g_test_add_func("/keymanutil/keyman_put_options_todconf/existing_key", test_keyman_put_options_todconf__existing_key); + g_test_add_func("/keymanutil/ibus_keyman_engine_desc_new/all_set", test_ibus_keyman_engine_desc_new__all_set); + g_test_add_func("/keymanutil/ibus_keyman_engine_desc_new/only_description", test_ibus_keyman_engine_desc_new__only_description); + g_test_add_func("/keymanutil/ibus_keyman_engine_desc_new/only_copyright", test_ibus_keyman_engine_desc_new__only_copyright); + g_test_add_func( + "/keymanutil/ibus_keyman_engine_desc_new/no_language_license_author_version", + test_ibus_keyman_engine_desc_new__no_language_license_author_version); + + g_test_add_func("/keymanutil/get_engine_for_language/null_language", test_get_engine_for_language__null_language); + g_test_add_func("/keymanutil/get_engine_for_language/empty_language", test_get_engine_for_language__empty_language); + g_test_add_func("/keymanutil/get_engine_for_language/one_language", test_get_engine_for_language__one_language); + g_test_add_func("/keymanutil/get_engine_for_language/one_unknown_language", test_get_engine_for_language__one_unknown_language); + g_test_add_func("/keymanutil/get_engine_for_language/different_languages", test_get_engine_for_language__different_languages); + g_test_add_func("/keymanutil/get_engine_for_language/no_kbd_language", test_get_engine_for_language__no_kbd_language); + + g_test_add_func("/keymanutil/keyman_add_keyboard/no_language", test_keyman_add_keyboard__no_language); + g_test_add_func("/keymanutil/keyman_add_keyboard/one_language", test_keyman_add_keyboard__one_language); + g_test_add_func("/keymanutil/keyman_add_keyboard/two_languages", test_keyman_add_keyboard__two_languages); + g_test_add_func( + "/keymanutil/keyman_add_keyboard/prev_engine_adding_same_version", + test_keyman_add_keyboard__prev_engine_adding_same_version); + g_test_add_func( + "/keymanutil/keyman_add_keyboard/prev_engine_adding_newer_version", + test_keyman_add_keyboard__prev_engine_adding_newer_version); + // #9593 + // g_test_add_func( + // "/keymanutil/keyman_add_keyboard/prev_engine_adding_newer_version_versioncompare", + // test_keyman_add_keyboard__prev_engine_adding_newer_version_9593); + g_test_add_func( + "/keymanutil/keyman_add_keyboard/prev_engine_adding_older_version", + test_keyman_add_keyboard__prev_engine_adding_older_version); + + g_test_add_func("/keymanutil/keyman_add_keyboards_from_dir/no_kmpjson", test_keyman_add_keyboards_from_dir__no_kmpjson); + g_test_add_func("/keymanutil/keyman_add_keyboards_from_dir/one_dir", test_keyman_add_keyboards_from_dir__one_dir); + g_test_add_func("/keymanutil/keyman_add_keyboards_from_dir/two_langs", test_keyman_add_keyboards_from_dir__two_langs); + g_test_add_func("/keymanutil/keyman_add_keyboards_from_dir/prev_keyboards", test_keyman_add_keyboards_from_dir__prev_keyboards); + // Run tests int retVal = g_test_run(); diff --git a/linux/ibus-keyman/src/test/meson.build b/linux/ibus-keyman/src/test/meson.build index 3c8c1044c0d..02631e27f79 100644 --- a/linux/ibus-keyman/src/test/meson.build +++ b/linux/ibus-keyman/src/test/meson.build @@ -84,7 +84,7 @@ test( test( 'keymanutil-tests', run_src_test, - args: [ '--tap', '-k', '--env', env_file, '--', keymanutil_tests], + args: [ '--tap', '-k', '--env', env_file, '--', keymanutil_tests, '--testdata', meson.current_source_dir() / 'testdata'], env: test_env, priority: -2, is_parallel: false, @@ -103,7 +103,7 @@ test( test( 'print-kmp-test', run_src_test, - args: [ '--', print_kmp_test, meson.current_source_dir() / 'kmp.json' ], + args: [ '--', print_kmp_test, meson.current_source_dir() / 'testdata/kmp.json' ], env: test_env, priority: -2, protocol: 'exitcode', diff --git a/linux/ibus-keyman/src/test/print_kmpdetails.c b/linux/ibus-keyman/src/test/print_kmpdetails.c index 3aeea7fce11..f030e354534 100644 --- a/linux/ibus-keyman/src/test/print_kmpdetails.c +++ b/linux/ibus-keyman/src/test/print_kmpdetails.c @@ -13,8 +13,8 @@ int main (gint argc, gchar **argv) { struct stat filestat; - kmp_details details; - gchar *kmp_json; + g_autofree gchar *kmp_json = NULL; + g_autoptr(kmp_details) details = g_new0(kmp_details, 1); if (argc < 2) { @@ -29,16 +29,13 @@ main (gint argc, gchar **argv) { g_print ("Usage: kmpdetails \n"); g_print ("ERROR: file %s not found\n", kmp_json); - g_free(kmp_json); return EXIT_FAILURE; } - g_free(kmp_json); setlocale(LC_ALL, "C.UTF-8"); - get_kmp_details(argv[1], &details); - print_kmp_details(&details); - free_kmp_details(&details); + get_kmp_details(argv[1], details); + print_kmp_details(details); return EXIT_SUCCESS; } diff --git a/linux/ibus-keyman/src/test/run-tests.sh b/linux/ibus-keyman/src/test/run-tests.sh index efeb747ddc7..ae005fcbd6b 100755 --- a/linux/ibus-keyman/src/test/run-tests.sh +++ b/linux/ibus-keyman/src/test/run-tests.sh @@ -58,4 +58,4 @@ glib-compile-schemas "$SCHEMA_DIR" export GSETTINGS_BACKEND=memory -"${G_TEST_BUILDDIR:-.}/keymanutil-tests" "$@" +"${G_TEST_BUILDDIR:-.}/keymanutil-tests" "$@" --testdata "${SRCDIR}/src/test/testdata" diff --git a/linux/ibus-keyman/src/test/setup-tests.sh b/linux/ibus-keyman/src/test/setup-tests.sh index 07e7d5462b3..79bb765f509 100755 --- a/linux/ibus-keyman/src/test/setup-tests.sh +++ b/linux/ibus-keyman/src/test/setup-tests.sh @@ -3,4 +3,4 @@ set -eu . "$(dirname "$0")/../../tests/scripts/test-helper.inc.sh" -setup_display_server_only "$1" "$2" "$3" "$4" +setup_display_server_only "${1:-x11}" "${2:-/tmp/env-src-test.txt}" "${3:-/tmp/ibus-keyman-src-test-pids}" "${4:-/tmp/ibus-keyman-src-test-pids.pids}" diff --git a/linux/ibus-keyman/src/test/kmp.json b/linux/ibus-keyman/src/test/testdata/kmp.json similarity index 95% rename from linux/ibus-keyman/src/test/kmp.json rename to linux/ibus-keyman/src/test/testdata/kmp.json index dc659d324e7..d2ee4fa9e13 100644 --- a/linux/ibus-keyman/src/test/kmp.json +++ b/linux/ibus-keyman/src/test/testdata/kmp.json @@ -1,267 +1,267 @@ -{ - "system": { - "keymanDeveloperVersion": "10.0.1202.0", - "fileVersion": "7.0" - }, - "options": { - "readmeFile": "readme.htm" - }, - "info": { - "version": { - "description": "1.6.1" - }, - "name": { - "description": "LIBTRALO" - }, - "copyright": { - "description": "\u00A9 2008-2018 SIL International" - }, - "author": { - "description": "support@keyman.com", - "url": "mailto:support@keyman.com" - } - }, - "files": [ - { - "name": "libtralo.kmx", - "description": "Keyboard LIBTRALO" - }, - { - "name": "readme.htm", - "description": "File readme.htm" - }, - { - "name": "libtralo.png", - "description": "File libtralo.png" - }, - { - "name": "welcome.htm", - "description": "File welcome.htm" - }, - { - "name": "Charis_SIL_README.txt", - "description": "File Charis_SIL_README.txt" - }, - { - "name": "CharisSIL-B.ttf", - "description": "Font Charis SIL Bold" - }, - { - "name": "CharisSIL-BI.ttf", - "description": "Font Charis SIL Bold Italic" - }, - { - "name": "CharisSIL-I.ttf", - "description": "Font Charis SIL Italic" - }, - { - "name": "CharisSIL-R.ttf", - "description": "Font Charis SIL" - }, - { - "name": "FONTLOG.txt", - "description": "File FONTLOG.txt" - }, - { - "name": "OFL.txt", - "description": "File OFL.txt" - }, - { - "name": "OFL-FAQ.txt", - "description": "File OFL-FAQ.txt" - }, - { - "name": "CharisSILDan-Bold.ttf", - "description": "Font Charis SIL Dan Bold" - }, - { - "name": "CharisSILDan-BoldItalic.ttf", - "description": "Font Charis SIL Dan Bold Italic" - }, - { - "name": "CharisSILDan-Italic.ttf", - "description": "Font Charis SIL Dan Italic" - }, - { - "name": "CharisSILDan-Regular.ttf", - "description": "Font Charis SIL Dan" - }, - { - "name": "libtralo.js", - "description": "File libtralo.js" - }, - { - "name": "libtralo.kvk", - "description": "File libtralo.kvk" - }, - { - "name": "CharisSILDan_FONTLOG.txt", - "description": "File CharisSILDan_FONTLOG.txt" - }, - { - "name": "kmp.inf", - "description": "Package information" - }, - { - "name": "kmp.json", - "description": "Package information (JSON)" - } - ], - "keyboards": [ - { - "name": "LIBTRALO", - "id": "libtralo", - "version": "1.6.1", - "languages": [ - { - "name": "Bandi", - "id": "bza-Latn" - }, - { - "name": "Bassa", - "id": "bsq-Latn" - }, - { - "name": "Glio-Oubi", - "id": "oub-Latn" - }, - { - "name": "Gola", - "id": "gol-Latn" - }, - { - "name": "Northern Grebo", - "id": "gbo-Latn" - }, - { - "name": "Southern Kisi", - "id": "kss-Latn" - }, - { - "name": "Klao", - "id": "klu-Latn" - }, - { - "name": "Liberia Kpelle (Latin)", - "id": "xpe-Latn" - }, - { - "name": "Eastern Krahn", - "id": "kqo-Latn" - }, - { - "name": "Western Krahn", - "id": "krw-Latn" - }, - { - "name": "Kuwaa", - "id": "blh-Latn" - }, - { - "name": "Loma (Liberia) (Latin)", - "id": "lom-Latn" - }, - { - "name": "Mano", - "id": "mev-Latn" - }, - { - "name": "Manya", - "id": "mzj-Latn" - }, - { - "name": "Sapo", - "id": "krn-Latn" - }, - { - "name": "Bullom So", - "id": "buy-Latn" - }, - { - "name": "Kono (Sierra Leone)", - "id": "kno-Latn" - }, - { - "name": "Krio", - "id": "kri-Latn" - }, - { - "name": "Kuranko", - "id": "knk-Latn" - }, - { - "name": "West-Central Limba", - "id": "lia-Latn" - }, - { - "name": "Loko", - "id": "lok-Latn" - }, - { - "name": "Mende (Sierra Leone)", - "id": "men" - }, - { - "name": "Timne", - "id": "tem" - }, - { - "name": "Vai (Latin)", - "id": "vai-Latn" - }, - { - "name": "Dewoin (Latin)", - "id": "dee-Latn" - }, - { - "name": "Dan (Western) (Latin)", - "id": "dnj-Latn-LR" - }, - { - "name": "Gbii (Latin)", - "id": "ggb-Latn" - }, - { - "name": "Glaro-Twabo (Latin)", - "id": "glr-Latn" - }, - { - "name": "Grebo (Latin)", - "id": "grb-Latn" - }, - { - "name": "Barclayville Grebo (Latin)", - "id": "gry-Latn" - }, - { - "name": "Central Grebo (Latin)", - "id": "grv-Latn" - }, - { - "name": "Gboloo Grebo (Latin)", - "id": "gec-Latn" - }, - { - "name": "Southern Grebo (Latin)", - "id": "grj-Latn" - }, - { - "name": "Kpelle (Latin)", - "id": "kpe-Latn" - }, - { - "name": "Tajuasohn (Latin)", - "id": "tja-Latn" - }, - { - "name": "Bom-Kim (Latin)", - "id": "bmf-Latn" - }, - { - "name": "Sherbro (Latin)", - "id": "bun-Latn" - } - ] - } - ] -} +{ + "system": { + "keymanDeveloperVersion": "10.0.1202.0", + "fileVersion": "7.0" + }, + "options": { + "readmeFile": "readme.htm" + }, + "info": { + "version": { + "description": "1.6.1" + }, + "name": { + "description": "LIBTRALO" + }, + "copyright": { + "description": "\u00A9 2008-2018 SIL International" + }, + "author": { + "description": "support@keyman.com", + "url": "mailto:support@keyman.com" + } + }, + "files": [ + { + "name": "libtralo.kmx", + "description": "Keyboard LIBTRALO" + }, + { + "name": "readme.htm", + "description": "File readme.htm" + }, + { + "name": "libtralo.png", + "description": "File libtralo.png" + }, + { + "name": "welcome.htm", + "description": "File welcome.htm" + }, + { + "name": "Charis_SIL_README.txt", + "description": "File Charis_SIL_README.txt" + }, + { + "name": "CharisSIL-B.ttf", + "description": "Font Charis SIL Bold" + }, + { + "name": "CharisSIL-BI.ttf", + "description": "Font Charis SIL Bold Italic" + }, + { + "name": "CharisSIL-I.ttf", + "description": "Font Charis SIL Italic" + }, + { + "name": "CharisSIL-R.ttf", + "description": "Font Charis SIL" + }, + { + "name": "FONTLOG.txt", + "description": "File FONTLOG.txt" + }, + { + "name": "OFL.txt", + "description": "File OFL.txt" + }, + { + "name": "OFL-FAQ.txt", + "description": "File OFL-FAQ.txt" + }, + { + "name": "CharisSILDan-Bold.ttf", + "description": "Font Charis SIL Dan Bold" + }, + { + "name": "CharisSILDan-BoldItalic.ttf", + "description": "Font Charis SIL Dan Bold Italic" + }, + { + "name": "CharisSILDan-Italic.ttf", + "description": "Font Charis SIL Dan Italic" + }, + { + "name": "CharisSILDan-Regular.ttf", + "description": "Font Charis SIL Dan" + }, + { + "name": "libtralo.js", + "description": "File libtralo.js" + }, + { + "name": "libtralo.kvk", + "description": "File libtralo.kvk" + }, + { + "name": "CharisSILDan_FONTLOG.txt", + "description": "File CharisSILDan_FONTLOG.txt" + }, + { + "name": "kmp.inf", + "description": "Package information" + }, + { + "name": "kmp.json", + "description": "Package information (JSON)" + } + ], + "keyboards": [ + { + "name": "LIBTRALO", + "id": "libtralo", + "version": "1.6.1", + "languages": [ + { + "name": "Bandi", + "id": "bza-Latn" + }, + { + "name": "Bassa", + "id": "bsq-Latn" + }, + { + "name": "Glio-Oubi", + "id": "oub-Latn" + }, + { + "name": "Gola", + "id": "gol-Latn" + }, + { + "name": "Northern Grebo", + "id": "gbo-Latn" + }, + { + "name": "Southern Kisi", + "id": "kss-Latn" + }, + { + "name": "Klao", + "id": "klu-Latn" + }, + { + "name": "Liberia Kpelle (Latin)", + "id": "xpe-Latn" + }, + { + "name": "Eastern Krahn", + "id": "kqo-Latn" + }, + { + "name": "Western Krahn", + "id": "krw-Latn" + }, + { + "name": "Kuwaa", + "id": "blh-Latn" + }, + { + "name": "Loma (Liberia) (Latin)", + "id": "lom-Latn" + }, + { + "name": "Mano", + "id": "mev-Latn" + }, + { + "name": "Manya", + "id": "mzj-Latn" + }, + { + "name": "Sapo", + "id": "krn-Latn" + }, + { + "name": "Bullom So", + "id": "buy-Latn" + }, + { + "name": "Kono (Sierra Leone)", + "id": "kno-Latn" + }, + { + "name": "Krio", + "id": "kri-Latn" + }, + { + "name": "Kuranko", + "id": "knk-Latn" + }, + { + "name": "West-Central Limba", + "id": "lia-Latn" + }, + { + "name": "Loko", + "id": "lok-Latn" + }, + { + "name": "Mende (Sierra Leone)", + "id": "men" + }, + { + "name": "Timne", + "id": "tem" + }, + { + "name": "Vai (Latin)", + "id": "vai-Latn" + }, + { + "name": "Dewoin (Latin)", + "id": "dee-Latn" + }, + { + "name": "Dan (Western) (Latin)", + "id": "dnj-Latn-LR" + }, + { + "name": "Gbii (Latin)", + "id": "ggb-Latn" + }, + { + "name": "Glaro-Twabo (Latin)", + "id": "glr-Latn" + }, + { + "name": "Grebo (Latin)", + "id": "grb-Latn" + }, + { + "name": "Barclayville Grebo (Latin)", + "id": "gry-Latn" + }, + { + "name": "Central Grebo (Latin)", + "id": "grv-Latn" + }, + { + "name": "Gboloo Grebo (Latin)", + "id": "gec-Latn" + }, + { + "name": "Southern Grebo (Latin)", + "id": "grj-Latn" + }, + { + "name": "Kpelle (Latin)", + "id": "kpe-Latn" + }, + { + "name": "Tajuasohn (Latin)", + "id": "tja-Latn" + }, + { + "name": "Bom-Kim (Latin)", + "id": "bmf-Latn" + }, + { + "name": "Sherbro (Latin)", + "id": "bun-Latn" + } + ] + } + ] +} diff --git a/linux/ibus-keyman/src/test/testdata/kmp1.json b/linux/ibus-keyman/src/test/testdata/kmp1.json new file mode 100644 index 00000000000..5d9954c330e --- /dev/null +++ b/linux/ibus-keyman/src/test/testdata/kmp1.json @@ -0,0 +1,123 @@ +{ + "system": { + "keymanDeveloperVersion": "10.0.1202.0", + "fileVersion": "7.0" + }, + "options": { + "readmeFile": "readme.htm" + }, + "info": { + "version": { + "description": "1.6.1" + }, + "name": { + "description": "LIBTRALO" + }, + "copyright": { + "description": "\u00A9 2008-2018 SIL International" + }, + "author": { + "description": "support@keyman.com", + "url": "mailto:support@keyman.com" + } + }, + "files": [ + { + "name": "libtralo.kmx", + "description": "Keyboard LIBTRALO" + }, + { + "name": "readme.htm", + "description": "File readme.htm" + }, + { + "name": "libtralo.png", + "description": "File libtralo.png" + }, + { + "name": "welcome.htm", + "description": "File welcome.htm" + }, + { + "name": "Charis_SIL_README.txt", + "description": "File Charis_SIL_README.txt" + }, + { + "name": "CharisSIL-B.ttf", + "description": "Font Charis SIL Bold" + }, + { + "name": "CharisSIL-BI.ttf", + "description": "Font Charis SIL Bold Italic" + }, + { + "name": "CharisSIL-I.ttf", + "description": "Font Charis SIL Italic" + }, + { + "name": "CharisSIL-R.ttf", + "description": "Font Charis SIL" + }, + { + "name": "FONTLOG.txt", + "description": "File FONTLOG.txt" + }, + { + "name": "OFL.txt", + "description": "File OFL.txt" + }, + { + "name": "OFL-FAQ.txt", + "description": "File OFL-FAQ.txt" + }, + { + "name": "CharisSILDan-Bold.ttf", + "description": "Font Charis SIL Dan Bold" + }, + { + "name": "CharisSILDan-BoldItalic.ttf", + "description": "Font Charis SIL Dan Bold Italic" + }, + { + "name": "CharisSILDan-Italic.ttf", + "description": "Font Charis SIL Dan Italic" + }, + { + "name": "CharisSILDan-Regular.ttf", + "description": "Font Charis SIL Dan" + }, + { + "name": "libtralo.js", + "description": "File libtralo.js" + }, + { + "name": "libtralo.kvk", + "description": "File libtralo.kvk" + }, + { + "name": "CharisSILDan_FONTLOG.txt", + "description": "File CharisSILDan_FONTLOG.txt" + }, + { + "name": "kmp.inf", + "description": "Package information" + }, + { + "name": "kmp.json", + "description": "Package information (JSON)" + } + ], + "keyboards": [ + { + "name": "TEST1", + "id": "test1", + "version": "1.6.1", + "languages": [ + { + "name": "Bandi", + "id": "bza-Latn" + } + ] + } + ] +} diff --git a/linux/ibus-keyman/src/test/testdata/kmp2.json b/linux/ibus-keyman/src/test/testdata/kmp2.json new file mode 100644 index 00000000000..5e889091f72 --- /dev/null +++ b/linux/ibus-keyman/src/test/testdata/kmp2.json @@ -0,0 +1,127 @@ +{ + "system": { + "keymanDeveloperVersion": "10.0.1202.0", + "fileVersion": "7.0" + }, + "options": { + "readmeFile": "readme.htm" + }, + "info": { + "version": { + "description": "1.6.1" + }, + "name": { + "description": "LIBTRALO" + }, + "copyright": { + "description": "\u00A9 2008-2018 SIL International" + }, + "author": { + "description": "support@keyman.com", + "url": "mailto:support@keyman.com" + } + }, + "files": [ + { + "name": "libtralo.kmx", + "description": "Keyboard LIBTRALO" + }, + { + "name": "readme.htm", + "description": "File readme.htm" + }, + { + "name": "libtralo.png", + "description": "File libtralo.png" + }, + { + "name": "welcome.htm", + "description": "File welcome.htm" + }, + { + "name": "Charis_SIL_README.txt", + "description": "File Charis_SIL_README.txt" + }, + { + "name": "CharisSIL-B.ttf", + "description": "Font Charis SIL Bold" + }, + { + "name": "CharisSIL-BI.ttf", + "description": "Font Charis SIL Bold Italic" + }, + { + "name": "CharisSIL-I.ttf", + "description": "Font Charis SIL Italic" + }, + { + "name": "CharisSIL-R.ttf", + "description": "Font Charis SIL" + }, + { + "name": "FONTLOG.txt", + "description": "File FONTLOG.txt" + }, + { + "name": "OFL.txt", + "description": "File OFL.txt" + }, + { + "name": "OFL-FAQ.txt", + "description": "File OFL-FAQ.txt" + }, + { + "name": "CharisSILDan-Bold.ttf", + "description": "Font Charis SIL Dan Bold" + }, + { + "name": "CharisSILDan-BoldItalic.ttf", + "description": "Font Charis SIL Dan Bold Italic" + }, + { + "name": "CharisSILDan-Italic.ttf", + "description": "Font Charis SIL Dan Italic" + }, + { + "name": "CharisSILDan-Regular.ttf", + "description": "Font Charis SIL Dan" + }, + { + "name": "libtralo.js", + "description": "File libtralo.js" + }, + { + "name": "libtralo.kvk", + "description": "File libtralo.kvk" + }, + { + "name": "CharisSILDan_FONTLOG.txt", + "description": "File CharisSILDan_FONTLOG.txt" + }, + { + "name": "kmp.inf", + "description": "Package information" + }, + { + "name": "kmp.json", + "description": "Package information (JSON)" + } + ], + "keyboards": [ + { + "name": "TEST2", + "id": "test2", + "version": "1.6.1", + "languages": [ + { + "name": "Bom-Kim (Latin)", + "id": "bmf-Latn" + }, + { + "name": "Sherbro (Latin)", + "id": "bun-Latn" + } + ] + } + ] +} diff --git a/linux/ibus-keyman/tests/meson.build b/linux/ibus-keyman/tests/meson.build index a26b1268591..1637fbc9bc4 100644 --- a/linux/ibus-keyman/tests/meson.build +++ b/linux/ibus-keyman/tests/meson.build @@ -118,7 +118,7 @@ foreach kmx: kmxtest_files test( 'X11-' + testname + '__surrounding-text', run_test, - args: [ '--x11', '--surrounding-text', test_args], + args: [ '--testname', testname, '--x11', '--surrounding-text', test_args], env: test_env, depends: [test_exe], priority: -11, @@ -129,7 +129,7 @@ foreach kmx: kmxtest_files test( 'X11-' + testname + '__no-surrounding-text', run_test, - args: [ '--x11', '--no-surrounding-text', test_args], + args: [ '--testname', testname, '--x11', '--no-surrounding-text', test_args], env: test_env, depends: [test_exe], priority: -12, @@ -141,7 +141,7 @@ foreach kmx: kmxtest_files test( 'Wayland-' + testname + '__surrounding-text', run_test, - args: [ '--wayland', '--surrounding-text', test_args], + args: [ '--testname', testname, '--wayland', '--surrounding-text', test_args], env: test_env, depends: [test_exe], priority: -21, @@ -152,7 +152,7 @@ foreach kmx: kmxtest_files test( 'Wayland-' + testname + '__no-surrounding-text', run_test, - args: [ '--wayland', '--no-surrounding-text', test_args], + args: [ '--testname', testname, '--wayland', '--no-surrounding-text', test_args], env: test_env, depends: [test_exe], priority: -22, diff --git a/linux/ibus-keyman/tests/scripts/run-single-test.sh b/linux/ibus-keyman/tests/scripts/run-single-test.sh index e2b17f755f4..f2b2d37afff 100755 --- a/linux/ibus-keyman/tests/scripts/run-single-test.sh +++ b/linux/ibus-keyman/tests/scripts/run-single-test.sh @@ -36,6 +36,7 @@ function help() { echo " --env Name of the file containing environment variables to use" echo " --check Name of the file containing pids to check are running" echo " --cleanup Name of the file containing cleanup of processes" + echo " --testname Name of the test" exit 0 } @@ -69,6 +70,7 @@ while (( $# )); do --env) shift ; ARG_ENV=$1 ;; --check) shift; ARG_PIDS=$1 ;; --cleanup) shift; ARG_CLEANUP=$1 ;; + --testname) shift; ARG_TESTNAME=$1 ;; --) shift ; TESTFILE=$1; break ;; *) echo "Error: Unexpected argument \"$1\". Exiting." ; exit 4 ;; esac @@ -81,7 +83,7 @@ if [ -n "${ARG_PIDS:-}" ] && [ ! -n "${ARG_CLEANUP:-}" ]; then exit 6 fi -check_processes_running "$ARG_DISPLAY_SERVER" "$ARG_ENV" "$ARG_CLEANUP" "$ARG_PIDS" >&2 +check_processes_running "$ARG_DISPLAY_SERVER" "$ARG_ENV" "$ARG_CLEANUP" "$ARG_PIDS" "$ARG_TESTNAME" >&2 # shellcheck source=/dev/null . "$ARG_ENV" diff --git a/linux/ibus-keyman/tests/scripts/test-helper.inc.sh b/linux/ibus-keyman/tests/scripts/test-helper.inc.sh index 76dfda91a69..01626a04909 100755 --- a/linux/ibus-keyman/tests/scripts/test-helper.inc.sh +++ b/linux/ibus-keyman/tests/scripts/test-helper.inc.sh @@ -323,13 +323,9 @@ function exit_on_package_build() { fi } -function check_processes_running() { - local DISPLAY_SERVER ENV_FILE CLEANUP_FILE PID_FILE LINE PID MISSING MISSING_PROCS - DISPLAY_SERVER=$1 - ENV_FILE=$2 - CLEANUP_FILE=$3 - PID_FILE=$4 - MISSING=false +function _get_missing_processes() { + local PID_FILE MISSING_PROCS PID LINE + PID_FILE=$1 MISSING_PROCS="" while read -r LINE; do @@ -338,18 +334,33 @@ function check_processes_running() { fi PID=$(echo "$LINE" | cut -d' ' -f1) if ! ps --no-headers --pid="$PID" > /dev/null; then - MISSING=true - MISSING_PROCS="${MISSING_PROCS} $(echo "$LINE" | cut -d' ' -f2)\n" + MISSING_PROCS="${MISSING_PROCS} $(echo "$LINE" | cut -d' ' -f2)" break fi done < "${PID_FILE}" +} + +function check_processes_running() { + local DISPLAY_SERVER ENV_FILE CLEANUP_FILE PID_FILE MISSING_PROCS TEST_NAME + DISPLAY_SERVER=$1 + ENV_FILE=$2 + CLEANUP_FILE=$3 + PID_FILE=$4 + TEST_NAME=$5 + MISSING_PROCS=$(_get_missing_processes "$PID_FILE") - if $MISSING; then + if [ "$MISSING_PROCS" != "" ]; then echo "# Some background processes no longer running. Restarting..." - echo "Some background processes no longer running:" > /tmp/debug.output - echo "$MISSING_PROCS" >> /tmp/debug.output - echo "Restarting..." >> /tmp/debug.output + { echo "Some background processes no longer running (running ${TEST_NAME}):" ; \ + echo "$MISSING_PROCS" ; \ + echo "Restarting..." ; } >> /tmp/debug.output + mv /tmp/ibus-engine-keyman.log{,"-${TEST_NAME}-$(date -Iseconds)"} cleanup "${CLEANUP_FILE}" > /dev/null 2>&1 setup "${DISPLAY_SERVER}" "${ENV_FILE}" "${CLEANUP_FILE}" "${PID_FILE}" > /dev/null 2>&1 fi + + if [ "$(_get_missing_processes "$PID_FILE")" != "" ]; then + echo "# WARNING: Expected background processes are still missing after restart: ${MISSING_PROCS}" + echo "# Maybe an old process is still running?" + fi } diff --git a/linux/ibus-keyman/tests/testfixture.cpp b/linux/ibus-keyman/tests/testfixture.cpp index 84aab933c83..cfc1530a75a 100644 --- a/linux/ibus-keyman/tests/testfixture.cpp +++ b/linux/ibus-keyman/tests/testfixture.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include @@ -193,28 +193,28 @@ get_involved_keys(IBusKeymanTestsFixture *fixture, km::tests::key_event test_eve // process all modifiers std::list result; - if (test_event.modifier_state & KM_KBP_MODIFIER_SHIFT) { + if (test_event.modifier_state & KM_CORE_MODIFIER_SHIFT) { // we don't distinguish between R and L Shift result.push_back(get_key_event_for_modifier(IBUS_SHIFT_MASK, modifiers, GDK_KEY_Shift_L, KEY_LEFTSHIFT)); } - if (test_event.modifier_state & KM_KBP_MODIFIER_ALT || test_event.modifier_state & KM_KBP_MODIFIER_LALT) { + if (test_event.modifier_state & KM_CORE_MODIFIER_ALT || test_event.modifier_state & KM_CORE_MODIFIER_LALT) { result.push_back(get_key_event_for_modifier(IBUS_MOD1_MASK, modifiers, GDK_KEY_Alt_L, KEY_LEFTALT)); } - if (test_event.modifier_state & KM_KBP_MODIFIER_RALT) { + if (test_event.modifier_state & KM_CORE_MODIFIER_RALT) { if (test_event.vk == KEYMAN_RALT) result.push_back(get_key_event_for_modifier(IBUS_MOD1_MASK, modifiers, GDK_KEY_Alt_R, KEY_RIGHTALT)); else result.push_back(get_key_event_for_modifier(IBUS_MOD5_MASK, modifiers, GDK_KEY_Alt_R, KEY_RIGHTALT)); } - if (test_event.modifier_state & KM_KBP_MODIFIER_CTRL || test_event.modifier_state & KM_KBP_MODIFIER_LCTRL) { + if (test_event.modifier_state & KM_CORE_MODIFIER_CTRL || test_event.modifier_state & KM_CORE_MODIFIER_LCTRL) { result.push_back(get_key_event_for_modifier(IBUS_CONTROL_MASK, modifiers, GDK_KEY_Control_L, KEY_LEFTCTRL)); } - if (test_event.modifier_state & KM_KBP_MODIFIER_RCTRL) { + if (test_event.modifier_state & KM_CORE_MODIFIER_RCTRL) { result.push_back(get_key_event_for_modifier(IBUS_CONTROL_MASK, modifiers, GDK_KEY_Control_R, KEY_RIGHTCTRL)); } // process key - guint keyval = (test_event.modifier_state & KM_KBP_MODIFIER_SHIFT) ? gdk_keyval_to_upper(test_event.vk) + guint keyval = (test_event.modifier_state & KM_CORE_MODIFIER_SHIFT) ? gdk_keyval_to_upper(test_event.vk) : gdk_keyval_to_lower(test_event.vk); // REVIEW: the keyval we use here are not correct for < 0x20 and >= 0x7f @@ -244,7 +244,7 @@ get_context_keys(std::u16string context) { } result.push_back( {km::kbp::kmx::s_char_to_vkey[(int)utf8[0] - 32].vk, - (uint16_t)(km::kbp::kmx::s_char_to_vkey[(int)utf8[0] - 32].shifted ? KM_KBP_MODIFIER_SHIFT : 0)}); + (uint16_t)(km::kbp::kmx::s_char_to_vkey[(int)utf8[0] - 32].shifted ? KM_CORE_MODIFIER_SHIFT : 0)}); } return result; } diff --git a/linux/keyman-config/build.sh b/linux/keyman-config/build.sh index 5a01ead0da9..35608c69eb7 100755 --- a/linux/keyman-config/build.sh +++ b/linux/keyman-config/build.sh @@ -16,7 +16,9 @@ builder_describe \ "test" \ "install install artifacts" \ "uninstall uninstall artifacts" \ - "--no-integration don't run integration tests" + "--no-integration don't run integration tests" \ + "--report create coverage report" \ + "--coverage capture test coverage" builder_parse "$@" @@ -87,7 +89,19 @@ build_action() { } test_action() { - execute_with_temp_schema ./run-tests.sh + local options + + if builder_has_option --coverage; then + options="--coverage" + else + options="" + fi + execute_with_temp_schema ./run-tests.sh "${options}" + + if builder_has_option --report; then + builder_echo "Creating coverage report" + python3 -m coverage html --directory="$THIS_SCRIPT_PATH/build/coveragereport/" --data-file=build/.coverage + fi } install_action() { diff --git a/linux/keyman-config/keyman_config/sentry_handling.py b/linux/keyman-config/keyman_config/sentry_handling.py index c3bc10e4f41..20134685893 100644 --- a/linux/keyman-config/keyman_config/sentry_handling.py +++ b/linux/keyman-config/keyman_config/sentry_handling.py @@ -1,5 +1,6 @@ #!/usr/bin/python3 import getpass +import hashlib import importlib import logging import os @@ -124,13 +125,24 @@ def _sentry_sdk_initialize(self): integrations=[sentry_logging], before_send=self._before_send ) - set_user({'id': hash(getpass.getuser())}) + hash = hashlib.md5() + hash.update(getpass.getuser().encode()) + set_user({'id': hash.hexdigest()}) 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__) + scope.set_tag("device", platform.node()) + try: + os_release = platform.freedesktop_os_release() + scope.set_tag('os', os_release['PRETTY_NAME']) + scope.set_tag('os.name', os_release['NAME']) + if 'VERSION' in os_release: + scope.set_tag('os.version', os_release['VERSION']) + except OSError as e: + logging.debug(f'System does not have os_release file: {e.strerror}') logging.info("Initialized Sentry error reporting") def _raven_initialize(self): diff --git a/linux/keyman-config/km-config b/linux/keyman-config/km-config index dfdbb7d4517..604e7d17cf7 100755 --- a/linux/keyman-config/km-config +++ b/linux/keyman-config/km-config @@ -2,6 +2,12 @@ import argparse import logging +import sys +import gi + +gi.require_version('Gtk', '3.0') + +from gi.repository import Gtk from keyman_config import __versionwithtag__, __pkgversion__, add_standard_arguments, initialize_logging, initialize_sentry from keyman_config.handle_install import download_and_install_package @@ -30,6 +36,8 @@ if __name__ == '__main__': args = parser.parse_args() + Gtk.init(sys.argv[1:]) + initialize_logging(args) initialize_sentry() diff --git a/linux/keyman-config/run-tests.sh b/linux/keyman-config/run-tests.sh index db62cda3749..de81ec7f738 100755 --- a/linux/keyman-config/run-tests.sh +++ b/linux/keyman-config/run-tests.sh @@ -8,13 +8,18 @@ if [ -f /usr/libexec/ibus-memconf ]; then export GSETTINGS_BACKEND=keyfile fi +if [ "$1" == "--coverage" ]; then + coverage="-m coverage run --source=. --data-file=build/.coverage" +fi + if [ -n "$TEAMCITY_VERSION" ]; then - if ! pip3 list --format=columns | grep -q teamcity-messages; then - pip3 install teamcity-messages - fi - python3 -m teamcity.unittestpy discover -s tests -p test_*.py + if ! pip3 list --format=columns | grep -q teamcity-messages; then + pip3 install teamcity-messages + fi + python3 -m teamcity.unittestpy discover -s tests -p test_*.py else - python3 -m unittest discover -v -s tests -p test_*.py + # shellcheck disable=SC2086 + python3 ${coverage:-} -m unittest discover -v -s tests -p test_*.py fi rm -rf "$XDG_CONFIG_HOME" diff --git a/linux/keyman-system-service/build.sh b/linux/keyman-system-service/build.sh index 9bca26421c7..549b33596ef 100755 --- a/linux/keyman-system-service/build.sh +++ b/linux/keyman-system-service/build.sh @@ -17,7 +17,9 @@ builder_describe \ "test" \ "install install artifacts" \ "uninstall uninstall artifacts" \ - "--no-integration don't run integration tests" + "--no-integration don't run integration tests" \ + "--report create coverage report" \ + "--coverage capture test coverage" builder_parse "$@" @@ -37,10 +39,16 @@ builder_describe_outputs \ configure "${MESON_PATH}/build.ninja" \ build "${MESON_PATH}/src/keyman-system-service" +if builder_has_option --coverage; then + MESON_COVERAGE=-Db_coverage=true +else + MESON_COVERAGE= +fi + 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[@]}" +builder_run_action configure meson setup "$MESON_PATH" --werror --buildtype $MESON_TARGET ${MESON_COVERAGE} "${builder_extra_params[@]}" if builder_start_action build; then cd "$MESON_PATH" @@ -51,6 +59,10 @@ fi if builder_start_action test; then cd "$MESON_PATH" meson test --print-errorlogs $builder_verbose + if builder_has_option --coverage; then + # Note: requires lcov > 1.16 to properly work (see https://github.com/mesonbuild/meson/issues/6747) + ninja coverage-html + fi builder_finish_action success test fi diff --git a/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMBinaryFileFormat.h b/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMBinaryFileFormat.h index 97907c67c1f..d91f9b4f40e 100644 --- a/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMBinaryFileFormat.h +++ b/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMBinaryFileFormat.h @@ -45,8 +45,9 @@ struct COMP_KEYBOARD { #define VERSION_140 0x00000E00 #define VERSION_150 0x00000F00 #define VERSION_160 0x00001000 +#define VERSION_170 0x00001100 #define VERSION_MIN VERSION_50 -#define VERSION_MAX VERSION_160 +#define VERSION_MAX VERSION_170 struct COMP_GROUP { DWORD dpName; // string (only debug) diff --git a/oem/firstvoices/ios/build.sh b/oem/firstvoices/ios/build.sh index 061d7b760e1..03693589db0 100755 --- a/oem/firstvoices/ios/build.sh +++ b/oem/firstvoices/ios/build.sh @@ -113,8 +113,8 @@ function do_build() { -exportArchive \ -archivePath "$ARCHIVE_PATH" \ -exportOptionsPlist exportAppStore.plist \ - -exportPath "$BUILD_PATH/${CONFIG}-iphoneos" - -allowProvisioningUpdates \ + -exportPath "$BUILD_PATH/${CONFIG}-iphoneos" \ + -allowProvisioningUpdates \ VERSION=$VERSION \ VERSION_WITH_TAG=$VERSION_WITH_TAG else diff --git a/oem/firstvoices/windows/src/inst/Makefile b/oem/firstvoices/windows/src/inst/Makefile index 2f3be6990b9..488b837d255 100644 --- a/oem/firstvoices/windows/src/inst/Makefile +++ b/oem/firstvoices/windows/src/inst/Makefile @@ -8,9 +8,9 @@ DESKTOP_FILES=firstvoices.wixobj desktopui.wixobj MSI=firstvoices.msi +EXE_ZIP=firstvoices.zip EXE=firstvoices.exe KMP=fv_all.kmp -INTEXE=firstvoices-fv_all.exe APPTITLE="Keyman for FirstVoices" TITLEIMAGE=setuptitle.png @@ -48,12 +48,19 @@ prereq: cd $(FVROOT)\src\inst desktop: prereq + rem compile .msi $(MAKE) -fdownload.mak candle-desktop $(WIXLIGHT) -dWixUILicenseRtf=License.rtf -out $(MSI) -ext WixUIExtension $(DESKTOP_FILES) $(SIGNCODE) /d $(APPTITLE) $(MSI) - $(ROOT)\bin\buildtools\buildpkg -m $(MSI) -s $(ROOT)\bin\desktop -l license.html -a $(APPTITLE) -i $(TITLEIMAGE) -n "FirstVoices Keyboards" -startDisabled -startWithConfiguration $(KMP) - if exist $(EXE) del $(EXE) - ren $(INTEXE) $(EXE) + + rem build self-extracting archive + $(MAKE) -fdownload.mak setup-inf + $(WZZIP) $(EXE_ZIP) $(MSI) license.html setup.inf $(TITLEIMAGE) $(KMP) + -del setup.inf + $(COPY) /b $(ROOT)\bin\desktop\setup-redist.exe + $(EXE_ZIP) $(EXE) + -del $(EXE_ZIP) + + rem sign and copy files $(SIGNCODE) /d $(APPTITLE) $(EXE) $(MAKE) -fdownload.mak copyredist-desktop diff --git a/oem/firstvoices/windows/src/inst/download.in b/oem/firstvoices/windows/src/inst/download.in index f31c2fe3c40..98d23e1e286 100644 --- a/oem/firstvoices/windows/src/inst/download.in +++ b/oem/firstvoices/windows/src/inst/download.in @@ -22,3 +22,15 @@ candle-desktop: $(WIXHEAT) dir ..\xml -o desktopui.wxs -ag -cg DesktopUI -dr INSTALLDIR -suid -var var.DESKTOPUISOURCE -wx -nologo $(WIXCANDLE) -dOEMNAME="$(OEMNAME)" -dPRODUCTNAME="$(PRODUCTNAME)" -dROOT="$(ROOT)" -dVERSION=$VersionWin -dRELEASE=$VersionRelease -dPRODUCTID=$GUID1 -dDESKTOPUISOURCE=..\xml firstvoices.wxs desktopui.wxs +setup-inf: + echo [Setup] > setup.inf + echo Version=$VersionWin >> setup.inf + echo MSIFileName=firstvoices.msi >> setup.inf + echo MSIOptions= >> setup.inf + echo AppName=Keyman for FirstVoices >> setup.inf + echo License=license.html >> setup.inf + echo TitleImage=setuptitle.png >> setup.inf + echo StartDisabled=True >> setup.inf + echo StartWithConfiguration=True >> setup.inf + echo [Packages] >> setup.inf + echo fv_all.kmp=FirstVoices Keyboards >> setup.inf diff --git a/package-lock.json b/package-lock.json index 405d2154cfb..49b34fdc35d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "resources/build/version", "core/include/ldml", "developer/src/common/web/test-helpers", + "developer/src/common/web/utils", "developer/src/kmc-analyze", "developer/src/kmc-keyboard-info", "developer/src/kmc-kmn", @@ -19,6 +20,7 @@ "developer/src/kmc-package", "developer/src/kmc", "developer/src/server", + "developer/utils", "common/models/*", "common/test/resources", "common/tools/*", @@ -30,6 +32,7 @@ "dependencies": { "@keymanapp/common-types": "file:common/web/types", "@keymanapp/developer-test-helpers": "file:developer/src/common/web/test-helpers", + "@keymanapp/developer-utils": "file:developer/src/common/web/utils", "@keymanapp/hextobin": "file:common/tools/hextobin", "@keymanapp/keyman-version": "file:common/web/keyman-version", "@keymanapp/ldml-keyboard-constants": "file:core/include/ldml" @@ -37,6 +40,9 @@ "devDependencies": { "@types/chai": "^4.3.5", "@typescript-eslint/eslint-plugin": "^5.59.1", + "ajv": "^8.12.0", + "ajv-cli": "^5.0.0", + "ajv-formats": "^2.1.1", "chai": "^4.3.4", "esbuild": "^0.15.16", "eslint": "^8.39.0", @@ -271,16 +277,9 @@ "name": "@keymanapp/keyman-version", "license": "MIT", "devDependencies": { - "@types/node": "^20.4.1", "typescript": "^4.9.5" } }, - "common/web/keyman-version/node_modules/@types/node": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.1.tgz", - "integrity": "sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==", - "dev": true - }, "common/web/lm-message-types": { "name": "@keymanapp/lm-message-types", "license": "MIT", @@ -375,7 +374,6 @@ "license": "MIT", "dependencies": { "@keymanapp/keyman-version": "*", - "ajv": "^8.11.0", "restructure": "git+https://github.com/keymanapp/dependency-restructure.git#7a188a1e26f8f36a175d95b67ffece8702363dfc", "semver": "^7.5.2", "xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829" @@ -387,6 +385,9 @@ "@types/node": "^20.4.1", "@types/semver": "^7.3.12", "@types/xml2js": "^0.4.5", + "ajv": "^8.12.0", + "ajv-cli": "^5.0.0", + "ajv-formats": "^2.1.1", "c8": "^7.12.0", "chai": "^4.3.4", "chalk": "^2.4.2", @@ -408,20 +409,6 @@ "integrity": "sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==", "dev": true }, - "common/web/types/node_modules/ajv": { - "version": "8.11.2", - "license": "MIT", - "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" - } - }, "common/web/types/node_modules/ansi-styles": { "version": "3.2.1", "dev": true, @@ -470,10 +457,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "common/web/types/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, "common/web/types/node_modules/minimatch": { "version": "3.0.4", "dev": true, @@ -694,6 +677,162 @@ "typescript": ">=2.7" } }, + "developer/src/common/web/utils": { + "name": "@keymanapp/developer-utils", + "dependencies": { + "@keymanapp/common-types": "*" + }, + "devDependencies": { + "@types/node": "^20.4.1", + "c8": "^7.12.0", + "chai": "^4.3.4", + "mocha": "^8.4.0", + "ts-node": "^9.1.1", + "typescript": "^4.9.5" + } + }, + "developer/src/common/web/utils/node_modules/@types/node": { + "version": "20.5.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", + "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==", + "dev": true + }, + "developer/src/common/web/utils/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "developer/src/common/web/utils/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, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "developer/src/common/web/utils/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "developer/src/common/web/utils/node_modules/mocha": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "developer/src/common/web/utils/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/common/web/utils/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "developer/src/common/web/utils/node_modules/ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "dependencies": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": ">=2.7" + } + }, + "developer/src/common/web/utils/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==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "developer/src/kmc": { "name": "@keymanapp/kmc", "license": "MIT", @@ -716,7 +855,6 @@ "bin": { "kmc": "build/src/kmc.js", "kmlmc": "build/src/kmlmc.js", - "kmlmi": "build/src/kmlmi.js", "kmlmp": "build/src/kmlmp.js" }, "devDependencies": { @@ -724,7 +862,6 @@ "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", - "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", "esbuild": "^0.15.8", @@ -746,7 +883,6 @@ "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", - "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", "chalk": "^2.4.2", @@ -995,12 +1131,17 @@ }, "developer/src/kmc-keyboard-info": { "name": "@keymanapp/kmc-keyboard-info", + "bundleDependencies": [ + "@keymanapp/developer-utils" + ], "license": "MIT", "dependencies": { - "@keymanapp/common-types": "*" + "@keymanapp/common-types": "*", + "@keymanapp/developer-utils": "*", + "@keymanapp/kmc-package": "*" }, "devDependencies": { - "@types/chai": "^4.1.7", + "@types/chai": "^4.3.5", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", "c8": "^7.12.0", @@ -1236,7 +1377,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", @@ -1569,10 +1709,8 @@ "@keymanapp/keyman-version": "*", "@keymanapp/kmc-kmn": "*", "@keymanapp/ldml-keyboard-constants": "*", - "ajv": "^8.11.0", "restructure": "git+https://github.com/keymanapp/dependency-restructure.git#7a188a1e26f8f36a175d95b67ffece8702363dfc", - "semver": "^7.5.2", - "xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829" + "semver": "^7.5.2" }, "devDependencies": { "@keymanapp/developer-test-helpers": "*", @@ -1580,7 +1718,6 @@ "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", "@types/semver": "^7.3.12", - "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", "chalk": "^2.4.2", @@ -1601,21 +1738,6 @@ "integrity": "sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==", "dev": true }, - "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==", - "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-ldml/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1669,11 +1791,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "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-ldml/node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1832,15 +1949,13 @@ "@keymanapp/common-types": "*", "@keymanapp/keyman-version": "*", "@keymanapp/models-types": "*", - "typescript": "^4.9.5", - "xml2js": "^0.4.19" + "typescript": "^4.9.5" }, "devDependencies": { "@keymanapp/developer-test-helpers": "*", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", - "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", "chalk": "^2.4.2", @@ -1851,16 +1966,19 @@ }, "developer/src/kmc-model-info": { "name": "@keymanapp/kmc-model-info", + "bundleDependencies": [ + "@keymanapp/developer-utils" + ], "license": "MIT", "dependencies": { "@keymanapp/common-types": "*", + "@keymanapp/developer-utils": "*", "@keymanapp/models-types": "*" }, "devDependencies": { "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", "@types/node": "^20.4.1", - "@types/xml2js": "^0.4.5", "c8": "^7.12.0", "chai": "^4.3.4", "chalk": "^2.4.2", @@ -2138,7 +2256,9 @@ "license": "MIT", "dependencies": { "@keymanapp/common-types": "*", - "jszip": "^3.7.0" + "jszip": "^3.7.0", + "marked": "^7.0.0", + "xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829" }, "devDependencies": { "@keymanapp/developer-test-helpers": "*", @@ -2654,7 +2774,7 @@ "@types/express": "^4.17.13", "@types/mocha": "^9.1.0", "@types/multer": "^1.4.7", - "@types/node": "^17.0.0", + "@types/node": "^20.4.1", "@types/ws": "^8.2.2", "chai": "^4.3.4", "copyfiles": "^2.4.1", @@ -2674,9 +2794,10 @@ "license": "MIT" }, "developer/src/server/node_modules/@types/node": { - "version": "17.0.45", - "dev": true, - "license": "MIT" + "version": "20.8.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz", + "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==", + "dev": true }, "developer/src/server/node_modules/busboy": { "version": "1.6.0", @@ -2843,6 +2964,22 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@eslint/eslintrc/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2860,6 +2997,12 @@ } } }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "node_modules/@eslint/js": { "version": "8.39.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz", @@ -2958,6 +3101,10 @@ "resolved": "developer/src/common/web/test-helpers", "link": true }, + "node_modules/@keymanapp/developer-utils": { + "resolved": "developer/src/common/web/utils", + "link": true + }, "node_modules/@keymanapp/hextobin": { "resolved": "common/tools/hextobin", "link": true @@ -3830,6 +3977,21 @@ "@types/node": "*" } }, + "node_modules/@types/yargs": { + "version": "17.0.26", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.26.tgz", + "integrity": "sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==", + "dev": true + }, "node_modules/@types/yauzl": { "version": "2.9.2", "license": "MIT", @@ -4183,14 +4345,14 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" }, "funding": { @@ -4198,6 +4360,83 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-cli": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ajv-cli/-/ajv-cli-5.0.0.tgz", + "integrity": "sha512-LY4m6dUv44HTyhV+u2z5uX4EhPYTM38Iv1jdgDJJJCyOOuqB8KtZEGjPZ2T+sh5ZIJrXUfgErYx/j3gLd3+PlQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0", + "fast-json-patch": "^2.0.0", + "glob": "^7.1.0", + "js-yaml": "^3.14.0", + "json-schema-migrate": "^2.0.0", + "json5": "^2.1.3", + "minimist": "^1.2.0" + }, + "bin": { + "ajv": "dist/index.js" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/ajv-cli/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/ajv-cli/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/ajv-cli/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-colors": { "version": "4.1.1", "dev": true, @@ -4753,6 +4992,7 @@ }, "node_modules/camelcase": { "version": "5.3.1", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5148,6 +5388,7 @@ }, "node_modules/decamelize": { "version": "1.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5884,7 +6125,6 @@ }, "node_modules/escalade": { "version": "3.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -6239,6 +6479,22 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/eslint/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -6314,6 +6570,12 @@ "node": ">=10.13.0" } }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "node_modules/eslint/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -6343,6 +6605,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -6583,6 +6858,7 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", + "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -6601,6 +6877,24 @@ "node": ">=8.6.0" } }, + "node_modules/fast-json-patch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.2.1.tgz", + "integrity": "sha512-4j5uBaTnsYAV5ebkidvxiLUYOwjQ+JSFljeqfTxCrH9bDmlCQaOJFS84oDJ2rAXZq2yskmk3ORfoP9DCwqFNig==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^2.0.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fast-json-patch/node_modules/fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", + "dev": true + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -7392,8 +7686,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.0", - "license": "MIT", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dependencies": { "agent-base": "6", "debug": "4" @@ -7931,10 +8226,19 @@ "version": "3.0.1", "license": "MIT" }, + "node_modules/json-schema-migrate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-2.0.0.tgz", + "integrity": "sha512-r38SVTtojDRp4eD6WsCqiE0eNDt4v1WalBXb9cyZYw9ai5cGtBwzRNWjHzJl38w6TxFkXAIA7h+fyX3tnrAFhQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + } + }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { @@ -8512,6 +8816,17 @@ "markdown-it": "bin/markdown-it.js" } }, + "node_modules/marked": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.0.tgz", + "integrity": "sha512-7Gv1Ry8tqR352ElQOQfxdGpIh8kNZh/yYjNCxAQCN1DDbY4bCTG3qDCSkZWlRElSseeEILDxkY/G9w7cgziBNw==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 16" + } + }, "node_modules/mdurl": { "version": "1.0.1", "dev": true, @@ -9573,6 +9888,7 @@ }, "node_modules/p-try": { "version": "2.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -9603,6 +9919,7 @@ }, "node_modules/path-exists": { "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -9756,8 +10073,10 @@ } }, "node_modules/punycode": { - "version": "2.1.1", - "license": "MIT", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, "engines": { "node": ">=6" } @@ -9923,13 +10242,16 @@ }, "node_modules/require-from-string": { "version": "2.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/require-main-filename": { "version": "2.0.0", + "dev": true, "license": "ISC" }, "node_modules/requirejs": { @@ -10170,6 +10492,7 @@ }, "node_modules/set-blocking": { "version": "2.0.0", + "devOptional": true, "license": "ISC" }, "node_modules/set-immediate-shim": { @@ -10438,6 +10761,12 @@ "node": "*" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, "node_modules/ssri": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", @@ -11063,6 +11392,7 @@ }, "node_modules/uri-js": { "version": "4.4.1", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -11172,6 +11502,7 @@ }, "node_modules/which-module": { "version": "2.0.0", + "dev": true, "license": "ISC" }, "node_modules/which-typed-array": { @@ -11231,7 +11562,6 @@ }, "node_modules/wrap-ansi": { "version": "7.0.0", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -11247,7 +11577,6 @@ }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "5.0.1", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -11255,7 +11584,6 @@ }, "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -11263,7 +11591,6 @@ }, "node_modules/wrap-ansi/node_modules/string-width": { "version": "4.2.2", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -11276,7 +11603,6 @@ }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "6.0.0", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.0" @@ -11290,14 +11616,15 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.5.0", - "license": "MIT", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -11337,7 +11664,6 @@ }, "node_modules/y18n": { "version": "5.0.8", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -11497,16 +11823,14 @@ "@actions/core": "^1.9.1", "@actions/github": "^2.1.0", "typescript": "^4.9.5", - "yargs": "^15.1.0" + "yargs": "^17.7.2" }, "devDependencies": { "@types/node": "^13.7.0", "@types/semver": "^7.1.0", + "@types/yargs": "^17.0.26", "semver": "^7.5.2", "ts-node": "^10.9.1" - }, - "engines": { - "node": ">=16.0" } }, "resources/build/version/node_modules/@types/node": { @@ -11516,74 +11840,37 @@ }, "resources/build/version/node_modules/ansi-regex": { "version": "5.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { "node": ">=8" } }, "resources/build/version/node_modules/cliui": { - "version": "6.0.0", - "license": "ISC", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "resources/build/version/node_modules/find-up": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=12" } }, "resources/build/version/node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "resources/build/version/node_modules/locate-path": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "resources/build/version/node_modules/p-limit": { - "version": "2.3.0", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "resources/build/version/node_modules/p-locate": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "engines": { "node": ">=8" } }, "resources/build/version/node_modules/string-width": { "version": "4.2.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -11595,7 +11882,8 @@ }, "resources/build/version/node_modules/strip-ansi": { "version": "6.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -11603,51 +11891,29 @@ "node": ">=8" } }, - "resources/build/version/node_modules/wrap-ansi": { - "version": "6.2.0", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "resources/build/version/node_modules/y18n": { - "version": "4.0.3", - "license": "ISC" - }, "resources/build/version/node_modules/yargs": { - "version": "15.4.1", - "license": "MIT", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=8" + "node": ">=12" } }, "resources/build/version/node_modules/yargs-parser": { - "version": "18.1.3", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "engines": { - "node": ">=6" + "node": ">=12" } }, "resources/gosh": { diff --git a/package.json b/package.json index 0afd9ade6eb..01f3f387f0b 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,9 @@ "devDependencies": { "@types/chai": "^4.3.5", "@typescript-eslint/eslint-plugin": "^5.59.1", + "ajv": "^8.12.0", + "ajv-cli": "^5.0.0", + "ajv-formats": "^2.1.1", "chai": "^4.3.4", "esbuild": "^0.15.16", "eslint": "^8.39.0", @@ -23,6 +26,7 @@ "resources/build/version", "core/include/ldml", "developer/src/common/web/test-helpers", + "developer/src/common/web/utils", "developer/src/kmc-analyze", "developer/src/kmc-keyboard-info", "developer/src/kmc-kmn", @@ -32,6 +36,7 @@ "developer/src/kmc-package", "developer/src/kmc", "developer/src/server", + "developer/utils", "common/models/*", "common/test/resources", "common/tools/*", @@ -43,6 +48,7 @@ "dependencies": { "@keymanapp/common-types": "file:common/web/types", "@keymanapp/developer-test-helpers": "file:developer/src/common/web/test-helpers", + "@keymanapp/developer-utils": "file:developer/src/common/web/utils", "@keymanapp/hextobin": "file:common/tools/hextobin", "@keymanapp/keyman-version": "file:common/web/keyman-version", "@keymanapp/ldml-keyboard-constants": "file:core/include/ldml" diff --git a/resources/build/build-utils-ci.inc.sh b/resources/build/build-utils-ci.inc.sh index 973f6f8aca7..47ba7a587c4 100644 --- a/resources/build/build-utils-ci.inc.sh +++ b/resources/build/build-utils-ci.inc.sh @@ -168,7 +168,7 @@ function _builder_prepublish() { local link_source=node_modules/$package # lookup the link_target from top-level package.json/dependencies - local link_target="$(cat "$KEYMAN_ROOT/builder_package_publish.json" | jq -r .dependencies.\"$package\")" + local link_target="$(cat "$KEYMAN_ROOT/builder_package_publish.json" | "$JQ" -r .dependencies.\"$package\")" if [[ $link_target =~ ^file: ]]; then link_target="$KEYMAN_ROOT"/${link_target#file:} diff --git a/resources/build/build-utils.sh b/resources/build/build-utils.sh index 6ee8b662129..d2424db0ec8 100755 --- a/resources/build/build-utils.sh +++ b/resources/build/build-utils.sh @@ -29,6 +29,11 @@ # Note: keep changes to version, tier and tag determination in sync with mkver (windows/src/buildutils/mkver) # +# Note: set -eu and SHLVL are deliberately set both here and in builder.inc.sh, +# because we want them set as early as possible, and because +# builder.inc.sh is shared to other repos, this keeps the usage consistent +# there as well. + # Exit on command failure and when using unset variables: set -eu diff --git a/resources/build/increment-version.sh b/resources/build/increment-version.sh index 445459da161..5c9a133b832 100755 --- a/resources/build/increment-version.sh +++ b/resources/build/increment-version.sh @@ -88,16 +88,14 @@ echo "increment-version.sh: building resources/build/version" pushd "$KEYMAN_ROOT" npm ci -pushd "$KEYMAN_ROOT/resources/build/version" -npm run build:ts -popd +"$KEYMAN_ROOT/resources/build/version/build.sh" echo "increment-version.sh: running resources/build/version" pushd "$KEYMAN_ROOT" ABORT=0 -node resources/build/version/lib/index.js history version -t "$GITHUB_TOKEN" -b "$base" $HISTORY_FORCE || ABORT=$? +node resources/build/version/build/src/index.js history version -t "$GITHUB_TOKEN" -b "$base" $HISTORY_FORCE || ABORT=$? -if [[ $ABORT = 1 ]]; then +if [[ $ABORT = 50 ]]; then if [[ $FORCE = 0 ]]; then echo "Skipping version increment from $VERSION: no recently merged pull requests were found" if [ ! -z "${TEAMCITY_VERSION-}" ]; then @@ -109,7 +107,7 @@ if [[ $ABORT = 1 ]]; then echo "Force specified; building even though no changes were detected" fi elif [[ $ABORT != 0 ]]; then - echo "Failed to complete version history check" + echo "Failed to complete version history check (node version/lib/index.js failed with error $ABORT)" exit $ABORT fi popd > /dev/null diff --git a/resources/build/version/.gitignore b/resources/build/version/.gitignore index 687e3299fe0..3e42060cc69 100644 --- a/resources/build/version/.gitignore +++ b/resources/build/version/.gitignore @@ -47,3 +47,4 @@ coverage # Build output dist lib +build diff --git a/resources/build/version/build.sh b/resources/build/version/build.sh new file mode 100755 index 00000000000..64892803bc8 --- /dev/null +++ b/resources/build/version/build.sh @@ -0,0 +1,24 @@ +#!/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 + +. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" + +# This script runs from its own folder +cd "$THIS_SCRIPT_PATH" + +################################ Main script ################################ + +builder_describe "Build version tooling" clean configure build +builder_describe_outputs \ + configure /resources/build/version/node_modules \ + build /resources/build/version/build/src/index.js + +builder_parse "$@" + +builder_run_action clean rm -rf build/ node_modules/ dist/ lib/ +builder_run_action configure verify_npm_setup +builder_run_action build tsc --build diff --git a/resources/build/version/package.json b/resources/build/version/package.json index 93911e79701..57264f02e44 100644 --- a/resources/build/version/package.json +++ b/resources/build/version/package.json @@ -1,25 +1,24 @@ { + "description": "Automatically updates HISTORY.md based on pull requests", + "type": "module", "dependencies": { "@actions/core": "^1.9.1", "@actions/github": "^2.1.0", "typescript": "^4.9.5", - "yargs": "^15.1.0" + "yargs": "^17.7.2" }, - "description": "Automatically updates HISTORY.md based on pull requests", "devDependencies": { "@types/node": "^13.7.0", "@types/semver": "^7.1.0", + "@types/yargs": "^17.0.26", "semver": "^7.5.2", "ts-node": "^10.9.1" }, - "engines": { - "node": ">=16.0" - }, "files": [ "src" ], "license": "MIT", - "main": "lib/index.js", + "main": "build/src/index.js", "name": "@keymanapp/auto-history-action", "private": true, "scripts": { diff --git a/resources/build/version/src/fixupHistory.ts b/resources/build/version/src/fixupHistory.ts index 2145fa94656..9f48f4851dd 100644 --- a/resources/build/version/src/fixupHistory.ts +++ b/resources/build/version/src/fixupHistory.ts @@ -5,7 +5,7 @@ import { import { GitHub } from '@actions/github'; import { readFileSync, writeFileSync } from 'fs'; import { gt } from 'semver'; -import { reportHistory } from './reportHistory'; +import { reportHistory } from './reportHistory.js'; interface PRInformation { title: string; @@ -42,7 +42,8 @@ const splicePullsIntoHistory = async (pulls: PRInformation[]): Promise<{count: n const versionMatch = new RegExp(`^## ${version} ${tier}`, 'i'); let historyChunks: { heading: string[]; newer: string[]; current: string[]; older: string[] } = {heading:[], newer:[], current:[], older:[]}; - let state = "heading"; + type ChunkType = keyof typeof historyChunks; + let state: ChunkType = "heading"; for(const line of history) { if(line.match(versionMatch)) { // We are in the correct section of history @@ -90,7 +91,7 @@ const splicePullsIntoHistory = async (pulls: PRInformation[]): Promise<{count: n let found = false; // Look for the PR in any other chunk -- can be found with merges of master into PR or chained PRs - for(const chunk of ['newer','current','older']) { + for(const chunk of ['newer','current','older'] as ChunkType[]) { for(const line of historyChunks[chunk]) { if(line.match(pullNumberRe)) { found = true; diff --git a/resources/build/version/src/index.ts b/resources/build/version/src/index.ts index 3d5f896650d..31e1a2cd046 100644 --- a/resources/build/version/src/index.ts +++ b/resources/build/version/src/index.ts @@ -1,13 +1,14 @@ import { info as logInfo } from '@actions/core'; import { GitHub } from '@actions/github'; -import { sendCommentToPullRequestAndRelatedIssues, fixupHistory } from './fixupHistory'; -import { incrementVersion } from './incrementVersion'; -const yargs = require('yargs'); +import { sendCommentToPullRequestAndRelatedIssues, fixupHistory } from './fixupHistory.js'; +import { incrementVersion } from './incrementVersion.js'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; import { readFileSync } from 'fs'; -import { reportHistory } from './reportHistory'; +import { reportHistory } from './reportHistory.js'; -const argv = yargs +const argv = await yargs(hideBin(process.argv)) .command(['history'], 'Fixes up HISTORY.md with pull request data') .command(['version'], 'Increments the current patch version in VERSION.md') .command(['report-history'], 'Print list of outstanding PRs waiting for the next build') @@ -81,10 +82,10 @@ const main = async (): Promise => { if(argv._.includes('report-history')) { let pulls = await reportHistory(octokit, argv.base, argv.force, argv['github-pr'], argv.from, argv.to); - let versions = {}; + let versions: {[index:string]:any} = {}; pulls.forEach((item) => { if(typeof item.version == 'string') { - if(typeof versions[item.version] == 'undefined') { + if(typeof (versions)[item.version] == 'undefined') { versions[item.version] = {data: item.tag_data, pulls: []}; } // We want to invert the order of the pulls as we go to @@ -122,7 +123,7 @@ const main = async (): Promise => { process.exit(0); // Tell shell that we have updated files } - process.exit(1); // Tell shell that we haven't updated files + process.exit(50); // Tell shell that we haven't updated files }; @@ -131,5 +132,5 @@ main().then( ) .catch((error: Error): void => { console.error(`An unexpected error occurred: ${error.message}, ${error.stack ?? 'no stack trace'}.`); - process.exit(2); + process.exit(1); }); diff --git a/resources/build/version/src/reportHistory.ts b/resources/build/version/src/reportHistory.ts index bf726063339..2fae1ab628e 100644 --- a/resources/build/version/src/reportHistory.ts +++ b/resources/build/version/src/reportHistory.ts @@ -2,8 +2,8 @@ import { warning as logWarning, info as logInfo } from '@actions/core'; import { GitHub } from '@actions/github'; -import { findLastHistoryPR, getAssociatedPR} from './graphql/queries'; -import { spawnChild } from './util/spawnAwait'; +import { findLastHistoryPR, getAssociatedPR} from './graphql/queries.js'; +import { spawnChild } from './util/spawnAwait.js'; const getPullRequestInformation = async ( octokit: GitHub, base: string @@ -67,7 +67,7 @@ const getAssociatedPRInformation = async ( } }: any = response; - const node = nodes.find(node => node.state == 'MERGED'); + const node = nodes.find((node:any) => node.state == 'MERGED'); return node ? { title: node.title, number: node.number } : undefined; }; diff --git a/resources/build/version/tsconfig.json b/resources/build/version/tsconfig.json index 655da5e8afc..2d99762ca9b 100644 --- a/resources/build/version/tsconfig.json +++ b/resources/build/version/tsconfig.json @@ -1,7 +1,8 @@ { + "extends": "../../../tsconfig.base.json", "compilerOptions": { - "rootDir": "." + "rootDir": ".", + "outDir": "build/", }, - "extends": "./tsconfig.production.json", - "include": ["./**/*"] + "include": ["./**/*.ts"] } diff --git a/resources/build/version/tsconfig.production.json b/resources/build/version/tsconfig.production.json deleted file mode 100644 index 2189a3042d4..00000000000 --- a/resources/build/version/tsconfig.production.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "allowSyntheticDefaultImports": true, - "alwaysStrict": true, - "charset": "utf-8", - "declaration": true, - "declarationMap": true, - "downlevelIteration": true, - "forceConsistentCasingInFileNames": true, - "lib": ["es2017"], - "module": "commonjs", - "noImplicitAny": false, - "noImplicitReturns": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "outDir": "lib", - "pretty": true, - "rootDir": "src", - "sourceMap": true, - "strict": true, - "strictNullChecks": true, - "target": "es2017" - }, - "files": ["./src/index.ts"] -} diff --git a/resources/builder.inc.sh b/resources/builder.inc.sh index 0eb4b995b63..b9972e266ba 100755 --- a/resources/builder.inc.sh +++ b/resources/builder.inc.sh @@ -21,6 +21,14 @@ # builder.inc.sh directly, or, just in the Keyman repo, via build-utils.sh. # +# Exit on command failure and when using unset variables: +set -eu + +# +# Prevents 'clear' on exit of mingw64 bash shell +# +SHLVL=0 + # _builder_init is called internally at the bottom of this file after we have # all function declarations in place. function _builder_init() { @@ -659,14 +667,14 @@ builder_trim() { # `/` is expected to be relative to repo root, not filesystem root. Otherwise, # it's relative to current script path, not current working directory. The # returned path will not have a prefix `/`, and will be relative to -# `$KEYMAN_ROOT`. Assumes realpath is installed (brew coreutils on macOS). +# `$REPO_ROOT`. Assumes realpath is installed (brew coreutils on macOS). # _builder_expand_relative_path() { local path="$1" if [[ "$path" =~ ^/ ]]; then echo "${path:1}" else - realpath --canonicalize-missing --relative-to="$KEYMAN_ROOT" "$THIS_SCRIPT_PATH/$path" + realpath --canonicalize-missing --relative-to="$REPO_ROOT" "$THIS_SCRIPT_PATH/$path" fi } @@ -1076,7 +1084,7 @@ _builder_add_chosen_action_target_dependencies() { # If there is a defined output for this dependency if [[ ! -z ${_builder_dep_path[$dep_output]+x} ]]; then # If the output for the dependency is missing, or we have --force-deps - if [[ ! -e "$KEYMAN_ROOT/${_builder_dep_path[$dep_output]}" ]] || builder_is_full_dep_build; then + if [[ ! -e "$REPO_ROOT/${_builder_dep_path[$dep_output]}" ]] || builder_is_full_dep_build; then # Add the dependency to the chosen action:target list if ! _builder_item_in_array "$dep_output" "${_builder_chosen_action_targets[@]}"; then _builder_chosen_action_targets+=($dep_output) @@ -1591,7 +1599,7 @@ _builder_dep_output_defined() { } _builder_dep_output_exists() { - if _builder_dep_output_defined $1 && [[ -e "$KEYMAN_ROOT/${_builder_dep_path[$1]}" ]]; then + if _builder_dep_output_defined $1 && [[ -e "$REPO_ROOT/${_builder_dep_path[$1]}" ]]; then return 0 else return 1 @@ -1670,7 +1678,7 @@ _builder_do_build_deps() { fi builder_set_module_has_been_built "$dep" - "$KEYMAN_ROOT/$dep/build.sh" "configure$dep_target" "build$dep_target" \ + "$REPO_ROOT/$dep/build.sh" "configure$dep_target" "build$dep_target" \ $builder_verbose \ $builder_debug \ $_builder_build_deps \ diff --git a/resources/devbox/macos/macos.sh b/resources/devbox/macos/macos.sh index 19ebea01bc1..edbfb43a6fc 100755 --- a/resources/devbox/macos/macos.sh +++ b/resources/devbox/macos/macos.sh @@ -15,7 +15,7 @@ REQUIRE_MACOS=false REQUIRE_WEB=false # Optional components -REQUIRE_KMCOMP=false +REQUIRE_KMC=false REQUIRE_PANDOC=false REQUIRE_SENTRYCLI=false @@ -38,7 +38,7 @@ function print_help() { echo " android ios macos web" echo echo " optional targets:" - echo " kmcomp Keyman keyboard compiler" + echo " kmc Keyman keyboard compiler" echo " pandoc Documentation compiler" echo " sentry-cli sentry.keyman.com debug symbol uploader" echo @@ -69,8 +69,8 @@ while [[ $# -gt 0 ]] ; do REQUIRE_WEB=true PARAMFOUND=true ;; - kmcomp) - REQUIRE_KMCOMP=true + kmc) + REQUIRE_KMC=true PARAMFOUND=true ;; pandoc) @@ -93,7 +93,7 @@ while [[ $# -gt 0 ]] ; do REQUIRE_IOS=true REQUIRE_MACOS=true REQUIRE_WEB=true - REQUIRE_KMCOMP=true + REQUIRE_KMC=true REQUIRE_PANDOC=true REQUIRE_SENTRYCLI=true PARAMFOUND=true @@ -166,12 +166,6 @@ $REQUIRE_SENTRYCLI && brew install getsentry/tools/sentry-cli pyenv install 2.7.18 pyenv global 2.7.18 -# Install WINE -$REQUIRE_KMCOMP && ( - brew tap homebrew/cask-versions - brew install --cask --no-quarantine wine-stable -) - source "$THIS_DIR/keyman.macos.env.sh" $REQUIRE_ANDROID && ( diff --git a/resources/standards-data/ldml-keyboards/techpreview/3.0/fr-t-k0-azerty.xml b/resources/standards-data/ldml-keyboards/techpreview/3.0/fr-t-k0-azerty.xml index 4ccedc062f3..190700d496a 100644 --- a/resources/standards-data/ldml-keyboards/techpreview/3.0/fr-t-k0-azerty.xml +++ b/resources/standards-data/ldml-keyboards/techpreview/3.0/fr-t-k0-azerty.xml @@ -23,22 +23,6 @@ - - - - - - - - - - - - - - - - @@ -34,15 +35,15 @@ - + - - + + diff --git a/resources/standards-data/ldml-keyboards/techpreview/cldr_info.json b/resources/standards-data/ldml-keyboards/techpreview/cldr_info.json index 601f341a251..e9907a447a3 100644 --- a/resources/standards-data/ldml-keyboards/techpreview/cldr_info.json +++ b/resources/standards-data/ldml-keyboards/techpreview/cldr_info.json @@ -1,5 +1,5 @@ { - "sha": "f3daab445e2e3ee88f8b94c6b46d380777035051", - "description": "release-44-alpha3-4-gf3daab445e", - "date": "Wed, 20 Sep 2023 22:27:35 +0000" + "sha": "2aa2275158c29e8e41c7f0b67c8fb786a453b89c", + "description": "release-44-alpha3-14-g2aa2275158", + "date": "Mon, 09 Oct 2023 19:18:13 +0000" } diff --git a/resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard3.dtd b/resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard3.dtd index 25d03368f86..92cda0eddf7 100644 --- a/resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard3.dtd +++ b/resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard3.dtd @@ -10,7 +10,7 @@ The CLDR Keyboard Subcommittee is currently developing major changes to the CLDR Please view the subcommittee page for the most recent information. --> - + @@ -77,17 +77,6 @@ Please view the subcommittee page for the most recent information. - - - - - - - - - - - diff --git a/resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard3.xsd b/resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard3.xsd index f3a7b8bed8e..47f299571bf 100644 --- a/resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard3.xsd +++ b/resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard3.xsd @@ -25,7 +25,6 @@ Note: DTD @-annotations are not currently converted to .xsd. For full CLDR file - @@ -148,26 +147,6 @@ Note: DTD @-annotations are not currently converted to .xsd. For full CLDR file - - - - - - - - - - - - - - - - - - - - diff --git a/resources/standards-data/ldml-keyboards/techpreview/fixup-schema.js b/resources/standards-data/ldml-keyboards/techpreview/fixup-schema.js index 84f00f6711a..e1a2d1f393a 100644 --- a/resources/standards-data/ldml-keyboards/techpreview/fixup-schema.js +++ b/resources/standards-data/ldml-keyboards/techpreview/fixup-schema.js @@ -80,6 +80,12 @@ if (data.title.endsWith('ldmlKeyboard3.xsd')) { } } +if (data.title.endsWith('ldmlKeyboardTest3.xsd')) { + if (data?.properties?.keyboardTest3) { + data.properties.keyboardTest3.type = 'object'; + } +} + // Write stuff const outstr = JSON.stringify(data, null, " "); writeFileSync(argv[2] || 1, outstr, "utf-8"); diff --git a/resources/standards-data/ldml-keyboards/techpreview/import/scanCodes-implied.xml b/resources/standards-data/ldml-keyboards/techpreview/import/scanCodes-implied.xml index 2ebb0ac85aa..e1ba3309d64 100644 --- a/resources/standards-data/ldml-keyboards/techpreview/import/scanCodes-implied.xml +++ b/resources/standards-data/ldml-keyboards/techpreview/import/scanCodes-implied.xml @@ -9,6 +9,7 @@ Values are space separated hex bytes. Frame keys are not included. --> +
@@ -16,13 +17,37 @@ - -
- - + + + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + - \ No newline at end of file + diff --git a/resources/standards-data/ldml-keyboards/techpreview/ldml-keyboard3.schema.json b/resources/standards-data/ldml-keyboards/techpreview/ldml-keyboard3.schema.json index c5d13311ef8..6d6ba9d2a9d 100644 --- a/resources/standards-data/ldml-keyboards/techpreview/ldml-keyboard3.schema.json +++ b/resources/standards-data/ldml-keyboards/techpreview/ldml-keyboard3.schema.json @@ -635,46 +635,6 @@ } }, "type": "object" - }, - "vkey": { - "additionalProperties": false, - "properties": { - "from": { - "type": "string" - }, - "to": { - "type": "string" - } - }, - "required": [ - "from", - "to" - ], - "type": "object" - }, - "vkeys": { - "additionalProperties": false, - "properties": { - "import": { - "items": { - "$ref": "#/definitions/import" - }, - "type": "array" - }, - "special": { - "items": { - "$ref": "#/definitions/special" - }, - "type": "array" - }, - "vkey": { - "items": { - "$ref": "#/definitions/vkey" - }, - "type": "array" - } - }, - "type": "object" } }, "properties": { @@ -740,9 +700,6 @@ }, "version": { "$ref": "#/definitions/version" - }, - "vkeys": { - "$ref": "#/definitions/vkeys" } }, "required": [ diff --git a/resources/standards-data/ldml-keyboards/techpreview/ldml-keyboardtest3.schema.json b/resources/standards-data/ldml-keyboards/techpreview/ldml-keyboardtest3.schema.json index cbf09b39d5b..476574112be 100644 --- a/resources/standards-data/ldml-keyboards/techpreview/ldml-keyboardtest3.schema.json +++ b/resources/standards-data/ldml-keyboards/techpreview/ldml-keyboardtest3.schema.json @@ -210,7 +210,8 @@ "required": [ "info", "conformsTo" - ] + ], + "type": "object" } }, "required": [ diff --git a/tsconfig-base.json b/tsconfig.base.json similarity index 64% rename from tsconfig-base.json rename to tsconfig.base.json index 8e3eafb6cf4..7630ddbfc4a 100644 --- a/tsconfig-base.json +++ b/tsconfig.base.json @@ -1,5 +1,19 @@ { "compilerOptions": { + "module": "ES2022", + "target": "es2022", + "moduleResolution": "node16", + "forceConsistentCasingInFileNames": true, + "sourceMap": true, + "alwaysStrict": true, + "noImplicitThis": true, + "noImplicitReturns": true, + "noImplicitAny": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "noUnusedLocals": true, + "allowJs": true, + "rootDir": ".", // TODO: move all compiler options here @@ -9,7 +23,9 @@ "declarationMap": true, "baseUrl": ".", + "paths": { + "@keymanapp/common-types": ["./common/web/types/src/main"], "@keymanapp/input-processor": ["./common/web/input-processor/src"], "@keymanapp/keyboard-processor": ["./common/web/keyboard-processor/src"], "@keymanapp/keyman": ["./web" ], diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json deleted file mode 100644 index 3dff61f8b64..00000000000 --- a/tsconfig.cjs.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - // Lists only CommonJS or 'none' modules; as we move modules from cjs/none to - // esm, we should move them from here into tsconfig.esm.json. Eventually, when - // we have only ES modules, we'll delete this file. - "files": [], - "include": [], - "references": [ - { "path": "./common/predictive-text/testing/one-stage-embedded-webworker/tsconfig.json" }, - { "path": "./common/predictive-text/testing/two-stage-embedded-webworker/tsconfig.json" }, - { "path": "./common/predictive-text/testing/two-stage-embedded-webworker/worker/tsconfig.json" }, - - { "path": "./developer/src/server/tsconfig.json" }, - - { "path": "./resources/build/version/tsconfig.json" }, - { "path": "./resources/build/version/tsconfig.production.json" }, - // { "path": "./web/bulk_rendering/tsconfig.json" }, - ] -} \ No newline at end of file diff --git a/tsconfig.esm-base.json b/tsconfig.esm-base.json deleted file mode 100644 index d17d505e5c2..00000000000 --- a/tsconfig.esm-base.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "extends": "./tsconfig-base.json", - - "compilerOptions": { - "module": "ES2022", - "target": "es2022", - "moduleResolution": "Node16", - "forceConsistentCasingInFileNames": true, - "sourceMap": true, - "alwaysStrict": true, - "noImplicitThis": true, - "noImplicitReturns": true, - "noImplicitAny": true, - "strictBindCallApply": true, - "strictFunctionTypes": true, - "noUnusedLocals": true, - - "paths": { - "@keymanapp/keyman-version": ["./common/web/keyman-version/keyman-version.mts"], - "@keymanapp/common-types": ["./common/web/types/src/main"], - // "@keymanapp/": ["core/include/ldml/ldml-keyboard-constants"], - }, - }, -} diff --git a/tsconfig.esm.json b/tsconfig.json similarity index 85% rename from tsconfig.esm.json rename to tsconfig.json index e0f72d8f8d9..38e908a6850 100644 --- a/tsconfig.esm.json +++ b/tsconfig.json @@ -1,19 +1,30 @@ { - // Lists only ES modules; as we move modules from cjs/none to esm, we should - // move them from tsconfig.cjs.json to here. Eventually, when we have only ES - // modules, we can rename this to tsconfig.json. - "files": [], - "include": [], "references": [ { "path": "./core/include/ldml/tsconfig.json" }, - //{ "path": "./developer/src/kmc/test/tsconfig.json" }, + { "path": "./common/models/templates/tsconfig.json" }, + { "path": "./common/models/types/tsconfig.json" }, + { "path": "./common/models/wordbreakers/tsconfig.json" }, + { "path": "./common/predictive-text/tsconfig.json" }, + { "path": "./common/tools/hextobin/" }, + { "path": "./common/web/input-processor/tsconfig.json" }, + { "path": "./common/web/keyboard-processor/tsconfig.json" }, + { "path": "./common/web/keyman-version" }, + { "path": "./common/web/lm-message-types/" }, + { "path": "./common/web/lm-worker/" }, + { "path": "./common/web/recorder/tsconfig.json" }, + { "path": "./common/web/sentry-manager/src/tsconfig.json" }, + { "path": "./common/web/types/" }, + { "path": "./common/web/utils/tsconfig.json" }, + { "path": "./developer/src/common/web/test-helpers/tsconfig.json" }, + { "path": "./developer/src/common/web/utils/tsconfig.json" }, + { "path": "./developer/src/common/web/utils/test/tsconfig.json" }, { "path": "./developer/src/kmc/tsconfig.json" }, - - // { "path": "./developer/src/kmc-analyze/test/tsconfig.json" }, + { "path": "./developer/src/kmc/test/tsconfig.json" }, { "path": "./developer/src/kmc-analyze/tsconfig.json" }, + // { "path": "./developer/src/kmc-analyze/test/tsconfig.json" }, { "path": "./developer/src/kmc-kmn/test/tsconfig.json" }, { "path": "./developer/src/kmc-kmn/tsconfig.json" }, { "path": "./developer/src/kmc-keyboard-info/test/tsconfig.json" }, @@ -26,25 +37,12 @@ { "path": "./developer/src/kmc-model-info/tsconfig.json" }, { "path": "./developer/src/kmc-package/test/tsconfig.json" }, { "path": "./developer/src/kmc-package/tsconfig.json" }, + { "path": "./developer/src/server/tsconfig.json" }, - { "path": "./common/web/keyman-version" }, - { "path": "./common/web/types/" }, - - { "path": "./common/web/input-processor/tsconfig.json" }, - { "path": "./common/web/keyboard-processor/tsconfig.json" }, - { "path": "./common/web/recorder/tsconfig.json" }, - { "path": "./common/web/sentry-manager/src/tsconfig.json" }, - { "path": "./common/web/utils/tsconfig.json" }, - - { "path": "./common/models/templates/tsconfig.json" }, - { "path": "./common/models/types/tsconfig.json" }, - { "path": "./common/models/wordbreakers/tsconfig.json" }, - { "path": "./common/predictive-text/tsconfig.json" }, + { "path": "./resources/build/version/" }, { "path": "./web/src/tsconfig.all.json" }, // { "path": "./web/tools/recorder/tsconfig.json" }, // { "path": "./web/tools/sourcemap-root/tsconfig.json" }, - { "path": "./common/web/lm-message-types/" }, - { "path": "./common/web/lm-worker/" }, ] } \ No newline at end of file diff --git a/web/build.sh b/web/build.sh index e01c95dc5ee..51df8a346da 100755 --- a/web/build.sh +++ b/web/build.sh @@ -97,10 +97,11 @@ builder_run_child_actions build:app/ui # Needs both app/browser and app/ui. builder_run_child_actions build:samples -builder_run_child_actions build:test-pages - builder_run_child_actions build:tools +# Some test pages refer to KMW tools. +builder_run_child_actions build:test-pages + builder_run_child_actions test if builder_start_action test; then diff --git a/web/src/app/ui/kmwuitoolbar.ts b/web/src/app/ui/kmwuitoolbar.ts index 0be3f2cf0f3..68799cd35cc 100644 --- a/web/src/app/ui/kmwuitoolbar.ts +++ b/web/src/app/ui/kmwuitoolbar.ts @@ -824,10 +824,12 @@ if(!keyman?.ui?.name) { this.selectedLanguage = kbd.LanguageCode; // Return focus to input area and activate the selected keyboard - this.setLastFocus(); //*****this seems out of sequence??? this.addKeyboardToList(lang, kbd); if(updateKeyman) { - keymanweb.setActiveKeyboard(kbd.InternalName, kbd.LanguageCode); + keymanweb.setActiveKeyboard(kbd.InternalName, kbd.LanguageCode).then(() => { + // Restore focus _after_ the keyboard finishes loading. + this.setLastFocus(); + }); } this.listedKeyboards[this.findListedKeyboard(lang)].buttonNode.className = 'kmw_button_selected'; diff --git a/web/src/engine/osk/src/input/gestures/browser/subkeyPopup.ts b/web/src/engine/osk/src/input/gestures/browser/subkeyPopup.ts index e36b24b0e82..8482aa85e85 100644 --- a/web/src/engine/osk/src/input/gestures/browser/subkeyPopup.ts +++ b/web/src/engine/osk/src/input/gestures/browser/subkeyPopup.ts @@ -36,6 +36,9 @@ export default class SubkeyPopup implements RealizedGesture { public readonly baseKey: KeyElement; public readonly promise: Promise; + private initialX: number; + private initialY: number; + // Resolves the promise that generated this SubkeyPopup. private resolver: (keyEvent: KeyEvent) => void; @@ -219,25 +222,26 @@ export default class SubkeyPopup implements RealizedGesture { let skElement = popupBase.childNodes[i].firstChild; // Preference order: - // #1: if a default subkey has been specified, select it. (pending, for 15.0+) + // #1: if a default subkey has been specified, select it. // #2: if no default subkey is specified, default to a subkey with the same // key ID and layer / modifier spec. - //if(skSpec.isDefault) { TODO for 15.0 - // bk = skElement; - // break; - //} else - if(!baseKey.key || !baseKey.key.spec) { + if(skSpec.default) { + bk = skElement; + break; + } else if(!baseKey.key || !baseKey.key.spec) { continue; } if(skSpec.elementID == baseKey.key.spec.elementID) { bk = skElement; - break; // Best possible match has been found. (Disable 'break' once above block is implemented.) } } if(bk) { + // Prevent sticky-highlighting should the default key be selected. + vkbd.keyPending?.key.highlight(false); vkbd.keyPending = bk; + this.currentSelection = bk; // Subkeys never get key previews, so we can directly highlight the subkey. bk.key.highlight(true); } @@ -268,7 +272,33 @@ export default class SubkeyPopup implements RealizedGesture { } updateTouch(input: InputEventCoordinate) { - this.currentSelection = null; + // For 'default' subkey handling, we want a small fudge factor. + if(this.initialX === undefined || this.initialY === undefined) { + this.initialX = input.x; + this.initialY = input.y; + } + + const deltaX = this.initialX - input.x; + const deltaY = this.initialY - input.y; + const dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + + if(dist > 5) { + this.initialX = Number.MAX_SAFE_INTEGER; // it'll always exceed the threshold hereafter. + this.currentSelection = null; + } else { + // The function that calls this to perform subkey updates auto-unhighlights the active selection; + // make sure that highlighting is maintained if no new key was selected, but we haven't cancelled + // default-selection mode yet. + this.currentSelection.key.highlight(true); + + // Even if we technically have a different subkey underneath the touchpoint, we're still in + // default-selection mode. Require more movement before cancelling default-selection mode. + // + // Can occur for large subkey menus or when subkey menus are "constrained" within OSK bounds, + // as with the iOS app. + return; + } + this.baseKey.key.highlight(false); for(let i=0; i < this.baseKey['subKeys'].length; i++) { diff --git a/web/src/engine/osk/src/keyboard-layout/oskKey.ts b/web/src/engine/osk/src/keyboard-layout/oskKey.ts index b99d2076253..77d8927b60a 100644 --- a/web/src/engine/osk/src/keyboard-layout/oskKey.ts +++ b/web/src/engine/osk/src/keyboard-layout/oskKey.ts @@ -25,6 +25,7 @@ export class OSKKeySpec implements LayoutKey { nextlayer?: string; pad?: number; sk?: OSKKeySpec[]; + default?: boolean; constructor(id: string, text?: string, width?: number, sp?: ButtonClass, nextlayer?: string, pad?: number) { this.id = id; diff --git a/web/src/engine/osk/src/specialCharacters.ts b/web/src/engine/osk/src/specialCharacters.ts index 0f2a3adb955..ebd17ab06cb 100644 --- a/web/src/engine/osk/src/specialCharacters.ts +++ b/web/src/engine/osk/src/specialCharacters.ts @@ -1,5 +1,5 @@ -// Defines the PUA code mapping for the various 'special' modifier/control keys on keyboards. -// `specialCharacters` must be kept in sync with the same variable in builder.js. See also CompileKeymanWeb.pas: CSpecialText10 +// Defines the PUA code mapping for the various 'special' modifier/control/non-printing keys on keyboards. +// `specialCharacters` must be kept in sync with the same variable in constants.js. See also CompileKeymanWeb.pas: CSpecialText10 let specialCharacters = { '*Shift*': 8, '*Enter*': 5, @@ -43,6 +43,30 @@ let specialCharacters = { '*ZWNJ*': 0x75, // If this one is specified, auto-detection will kick in. '*ZWNJiOS*': 0x75, // The iOS version will be used by default, but the '*ZWNJAndroid*': 0x76, // Android platform has its own default glyph. + // Added in Keyman 17.0. + // Reference: https://github.com/silnrsi/font-symchar/blob/v4.000/documentation/encoding.md + '*ZWNJGeneric*': 0x79, // Generic version of ZWNJ (no override) + '*Sp*': 0x80, // Space + '*NBSp*': 0x82, // No-break Space + '*NarNBSp*': 0x83, // Narrow No-break Space + '*EnQ*': 0x84, // En Quad + '*EmQ*': 0x85, // Em Quad + '*EnSp*': 0x86, // En Space + '*EmSp*': 0x87, // Em Space + // TODO: Skipping #-per-em-space + '*PunctSp*': 0x8c, // Punctuation Space + '*ThSp*': 0x8d, // Thin Space + '*HSp*': 0x8e, // Hair Space + '*ZWSp*': 0x81, // Zero Width Space + '*ZWJ*': 0x77, // Zero Width Joiner + '*WJ*': 0x78, // Word Joiner + '*CGJ*': 0x7a, // Combining Grapheme Joiner + '*LTRM*': 0x90, // Left-to-right Mark + '*RTLM*': 0x91, // Right-to-left Mark + '*SH*': 0xa1, // Soft Hyphen + '*HTab*': 0xa2, // Horizontal Tabulation + // TODO: Skipping size references + }; export default specialCharacters; \ No newline at end of file diff --git a/web/src/resources/osk/keymanweb-osk.ttf b/web/src/resources/osk/keymanweb-osk.ttf index 5b3c6219797..e0651b14d4f 100644 Binary files a/web/src/resources/osk/keymanweb-osk.ttf and b/web/src/resources/osk/keymanweb-osk.ttf differ diff --git a/web/src/test/manual/web/default-subkey/default_subkey.js b/web/src/test/manual/web/default-subkey/default_subkey.js new file mode 100644 index 00000000000..556acda19a2 --- /dev/null +++ b/web/src/test/manual/web/default-subkey/default_subkey.js @@ -0,0 +1 @@ +if(typeof keyman === 'undefined') {console.log('Keyboard requires KeymanWeb 10.0 or later');if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");} else {KeymanWeb.KR(new Keyboard_default_subkey());}function Keyboard_default_subkey(){this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;this.KI="Keyboard_default_subkey";this.KN="default-subkey";this.KMINVER="10.0";this.KV={F:' 1em "Arial"',K102:0};this.KV.KLS={};this.KV.BK=(function(x){var e=Array.apply(null,Array(65)).map(String.prototype.valueOf,""),r=[],v,i,m=['default','shift','ctrl','shift-ctrl','alt','shift-alt','ctrl-alt','shift-ctrl-alt'];for(i=m.length-1;i>=0;i--)if((v=x[m[i]])||r.length)r=(v?v:e).slice().concat(r);return r})(this.KV.KLS);this.KDU=0;this.KH='';this.KM=0;this.KBVER="1.0";this.KMBM=0x0000;this.KVKD="T_d_default";this.KVKL={"tablet":{"font":"Tahoma","displayUnderlying":false,"layer":[{"id":"default","row":[{"id":"1","key":[{"id":"K_Q","text":"q"},{"id":"K_W","text":"w"},{"id":"K_E","text":"e"},{"id":"K_R","text":"r"},{"id":"K_T","text":"t"},{"id":"K_Y","text":"y"},{"id":"K_U","text":"u"},{"id":"K_I","text":"i"},{"id":"K_O","text":"o"},{"id":"K_P","text":"p"}]},{"id":"2","key":[{"id":"K_A","pad":"70","text":"a","sk":[{"id":"U_00E1","text":"\u00E1"},{"id":"U_00E0","text":"\u00E0"},{"id":"K_A","text":"a"},{"id":"U_00E4","text":"\u00E4"},{"id":"U_00E5","text":"\u00E5"},{"id":"U_00E2","text":"\u00E2"},{"id":"U_00E3","text":"\u00E3"}]},{"id":"K_S","text":"s"},{"id":"K_D","text":"d","sk":[{"id":"K_D","text":"d"},{"id":"U_010F","text":"\u010F"},{"id":"U_0111","text":"\u0111"},{"id":"U_0257","text":"\u0257"},{"id":"T_d_default","text":"default","default":true},{"id":"U_0221","text":"\u0221"}]},{"id":"K_F","text":"f"},{"id":"K_G","text":"g"},{"id":"K_H","text":"h"},{"id":"K_J","text":"j"},{"id":"K_K","text":"k"},{"id":"K_L","text":"l"},{"width":"10","id":"T_new_88","sp":"10"}]},{"id":"3","key":[{"nextlayer":"shift","width":"110","id":"K_SHIFT","sp":"1","text":"*Shift*"},{"id":"K_Z","text":"z"},{"id":"K_X","text":"x"},{"id":"K_C","text":"c"},{"id":"K_V","text":"v"},{"id":"K_B","text":"b"},{"id":"K_N","text":"n"},{"id":"K_M","text":"m"},{"id":"K_PERIOD","text":".","sk":[{"id":"K_COMMA","text":","},{"layer":"shift","id":"K_1","text":"!"},{"layer":"shift","id":"K_SLASH","text":"?"},{"id":"K_QUOTE","text":"'"},{"layer":"shift","id":"K_QUOTE","text":"\""},{"id":"K_BKSLASH","text":"\\"},{"layer":"shift","id":"K_COLON","text":":"},{"id":"K_COLON","text":";"}]},{"width":"90","id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"4","key":[{"nextlayer":"numeric","width":"140","id":"K_NUMLOCK","sp":"1","text":"*123*"},{"width":"120","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"630","id":"K_SPACE"},{"width":"140","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]},{"id":"shift","row":[{"id":"1","key":[{"id":"K_Q","text":"Q"},{"id":"K_W","text":"W"},{"id":"K_E","text":"E"},{"id":"K_R","text":"R"},{"id":"K_T","text":"T"},{"id":"K_Y","text":"Y"},{"id":"K_U","text":"U"},{"id":"K_I","text":"I"},{"id":"K_O","text":"O"},{"id":"K_P","text":"P"}]},{"id":"2","key":[{"id":"K_A","pad":"70","text":"A"},{"id":"K_S","text":"S"},{"id":"K_D","text":"D"},{"id":"K_F","text":"F"},{"id":"K_G","text":"G"},{"id":"K_H","text":"H"},{"id":"K_J","text":"J"},{"id":"K_K","text":"K"},{"id":"K_L","text":"L"},{"width":"10","sp":"10"}]},{"id":"3","key":[{"nextlayer":"default","width":"110","id":"K_SHIFT","sp":"2","text":"*Shift*"},{"id":"K_Z","text":"Z"},{"id":"K_X","text":"X"},{"id":"K_C","text":"C"},{"id":"K_V","text":"V"},{"id":"K_B","text":"B"},{"id":"K_N","text":"N"},{"id":"K_M","text":"M"},{"layer":"default","id":"K_PERIOD","text":".","sk":[{"layer":"default","id":"K_COMMA","text":","},{"layer":"shift","id":"K_1","text":"!"},{"layer":"shift","id":"K_SLASH","text":"?"},{"layer":"default","id":"K_QUOTE","text":"'"},{"layer":"shift","id":"K_QUOTE","text":"\""},{"layer":"default","id":"K_BKSLASH","text":"\\"},{"layer":"shift","id":"K_COLON","text":":"},{"layer":"default","id":"K_COLON","text":";"}]},{"width":"90","id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"4","key":[{"nextlayer":"numeric","width":"140","id":"K_NUMLOCK","sp":"1","text":"*123*"},{"width":"120","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"630","id":"K_SPACE"},{"width":"140","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]},{"id":"numeric","row":[{"id":"1","key":[{"id":"K_1","text":"1"},{"id":"K_2","text":"2"},{"id":"K_3","text":"3"},{"id":"K_4","text":"4"},{"id":"K_5","text":"5"},{"id":"K_6","text":"6"},{"id":"K_7","text":"7"},{"id":"K_8","text":"8"},{"id":"K_9","text":"9"},{"id":"K_0","text":"0"}]},{"id":"2","key":[{"layer":"shift","id":"K_4","pad":"70","text":"$"},{"layer":"shift","id":"K_2","text":"@"},{"layer":"shift","id":"K_3","text":"#"},{"layer":"shift","id":"K_5","text":"%"},{"layer":"shift","id":"K_7","text":"&"},{"layer":"shift","id":"K_HYPHEN","text":"_"},{"layer":"default","id":"K_EQUAL","text":"="},{"layer":"shift","id":"K_BKSLASH","text":"|"},{"layer":"default","id":"K_BKSLASH","text":"\\"},{"width":"10","sp":"10"}]},{"id":"3","key":[{"width":"110","id":"K_SHIFT","sp":"1","text":"*Shift*"},{"id":"K_LBRKT","text":"[","sk":[{"id":"U_00AB","text":"\u00AB"},{"layer":"shift","id":"K_COMMA","text":"<"},{"layer":"shift","id":"K_LBRKT","text":"{"}]},{"layer":"shift","id":"K_9","text":"("},{"layer":"shift","id":"K_0","text":")"},{"id":"K_RBRKT","text":"]","sk":[{"id":"U_00BB","text":"\u00BB"},{"layer":"shift","id":"K_PERIOD","text":">"},{"layer":"shift","id":"K_RBRKT","text":"}"}]},{"layer":"shift","id":"K_EQUAL","text":"+"},{"layer":"default","id":"K_HYPHEN","text":"-"},{"layer":"shift","id":"K_8","text":"*"},{"layer":"default","id":"K_SLASH","text":"\/"},{"width":"90","id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"4","key":[{"nextlayer":"default","width":"140","id":"K_LOWER","sp":"1","text":"*abc*"},{"width":"120","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"630","id":"K_SPACE"},{"width":"140","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]}]}};this.KVER="17.0.162.0";this.KVS=[];this.gs=function(t,e) {return this.g0(t,e);};this.gs=function(t,e) {return this.g0(t,e);};this.g0=function(t,e) {var k=KeymanWeb,r=0,m=0;if(k.KKM(e,16384,256)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"default");}}return r;};} \ No newline at end of file diff --git a/web/src/test/manual/web/default-subkey/index.html b/web/src/test/manual/web/default-subkey/index.html new file mode 100644 index 00000000000..728355c43b4 --- /dev/null +++ b/web/src/test/manual/web/default-subkey/index.html @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + KeymanWeb Testing Page - Predictive Text: robust testing + + + + + + + + + + + + + + + + +

KeymanWeb Sample Page - default subkey bootstrap-testing

+

Note: the intended tests require use of a touch-form-factor device or emulation thereof. +

+

+ The primary test is held on the default layer's 'd' key - the key labeled 'default' should + be autoselected. +

+

+ A secondary test is held on the default layer's 'a' key - the key labeled 'a', matching the + base key, should be autoselected. +

+
+ +
+ +

Type in your language in this text area:

+ + +

or in this input field:

+ + +

Return to testing home page

+
+ + + + + diff --git a/web/src/test/manual/web/default-subkey/kbd_source/HISTORY.md b/web/src/test/manual/web/default-subkey/kbd_source/HISTORY.md new file mode 100644 index 00000000000..cbd807699c4 --- /dev/null +++ b/web/src/test/manual/web/default-subkey/kbd_source/HISTORY.md @@ -0,0 +1,6 @@ +default-subkey Change History +==================== + +1.0 (2023-08-21) +---------------- +* Created by SIL International diff --git a/web/src/test/manual/web/default-subkey/kbd_source/LICENSE.md b/web/src/test/manual/web/default-subkey/kbd_source/LICENSE.md new file mode 100644 index 00000000000..b582b3f9086 --- /dev/null +++ b/web/src/test/manual/web/default-subkey/kbd_source/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +© 2023 SIL International + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/web/src/test/manual/web/default-subkey/kbd_source/README.md b/web/src/test/manual/web/default-subkey/kbd_source/README.md new file mode 100644 index 00000000000..9fb82bd00ba --- /dev/null +++ b/web/src/test/manual/web/default-subkey/kbd_source/README.md @@ -0,0 +1,30 @@ +default-subkey keyboard +============== + +Version 1.0 + +Description +----------- +default-subkey generated from template + +Links +----- + +Copyright +--------- +See [LICENSE.md](LICENSE.md) + +Supported Platforms +------------------- + * Windows + * macOS + * Linux + * Web + * iPhone + * iPad + * Android phone + * Android tablet + * Mobile devices + * Desktop devices + * Tablet devices + diff --git a/web/src/test/manual/web/issue3701/test3701/test3701.keyboard_info b/web/src/test/manual/web/default-subkey/kbd_source/default_subkey.keyboard_info similarity index 50% rename from web/src/test/manual/web/issue3701/test3701/test3701.keyboard_info rename to web/src/test/manual/web/default-subkey/kbd_source/default_subkey.keyboard_info index b2b40a5d1e8..1677ce4c2f4 100644 --- a/web/src/test/manual/web/issue3701/test3701/test3701.keyboard_info +++ b/web/src/test/manual/web/default-subkey/kbd_source/default_subkey.keyboard_info @@ -3,5 +3,5 @@ "languages": [ ], - "description": "test3701 generated from template" + "description": "default-subkey generated from template" } diff --git a/web/src/test/manual/web/default-subkey/kbd_source/default_subkey.kpj b/web/src/test/manual/web/default-subkey/kbd_source/default_subkey.kpj new file mode 100644 index 00000000000..4de714dbd3b --- /dev/null +++ b/web/src/test/manual/web/default-subkey/kbd_source/default_subkey.kpj @@ -0,0 +1,110 @@ + + + + $PROJECTPATH\build + True + True + True + keyboard + + + + id_8305ac63cf6c1388736030b6b94f996d + default_subkey.kmn + source\default_subkey.kmn + 1.0 + .kmn +
+ default-subkey + © SIL International +
+
+ + id_f586f51f683bde64c75f566e643e59bd + default_subkey.kps + source\default_subkey.kps + + .kps +
+ default-subkey + © SIL International +
+
+ + id_ede98e4633e239f933cbfd1f4e1b766c + HISTORY.md + HISTORY.md + + .md + + + id_53e892b8b41cc4caece1cfd5ef21d6e7 + LICENSE.md + LICENSE.md + + .md + + + id_0730bb7c2e8f9ea2438b52e419dd86c9 + README.md + README.md + + .md + + + id_9cc75be74cee7f9a4937893e80b91b5f + default_subkey.keyboard_info + default_subkey.keyboard_info + + .keyboard_info + + + id_d8483af80632f8b611983c11137efccd + default_subkey.ico + source\default_subkey.ico + + .ico + id_8305ac63cf6c1388736030b6b94f996d + + + id_5086f2e8ef1d731c015a78f418aba1e8 + default_subkey.kmx + source\..\build\default_subkey.kmx + + .kmx + id_f586f51f683bde64c75f566e643e59bd + + + id_c8c9acd9c28010e45856034bff6c9d90 + default_subkey.js + source\..\build\default_subkey.js + + .js + id_f586f51f683bde64c75f566e643e59bd + + + id_7b38ddd4f3836ebf9365696475ade08a + default_subkey.kvk + source\..\build\default_subkey.kvk + + .kvk + id_f586f51f683bde64c75f566e643e59bd + + + id_356e5d149c1e539356d72698c1e401a6 + welcome.htm + source\welcome.htm + + .htm + id_f586f51f683bde64c75f566e643e59bd + + + id_8da344c4cea6f467013357fe099006f5 + readme.htm + source\readme.htm + + .htm + id_f586f51f683bde64c75f566e643e59bd + +
+
diff --git a/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.ico b/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.ico new file mode 100644 index 00000000000..e3628ccc9b2 Binary files /dev/null and b/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.ico differ diff --git a/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.keyman-touch-layout b/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.keyman-touch-layout new file mode 100644 index 00000000000..41cbf16e1d9 --- /dev/null +++ b/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.keyman-touch-layout @@ -0,0 +1,721 @@ +{ + "tablet": { + "font": "Tahoma", + "layer": [ + { + "id": "default", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_Q", + "text": "q" + }, + { + "id": "K_W", + "text": "w" + }, + { + "id": "K_E", + "text": "e" + }, + { + "id": "K_R", + "text": "r" + }, + { + "id": "K_T", + "text": "t" + }, + { + "id": "K_Y", + "text": "y" + }, + { + "id": "K_U", + "text": "u" + }, + { + "id": "K_I", + "text": "i" + }, + { + "id": "K_O", + "text": "o" + }, + { + "id": "K_P", + "text": "p" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_A", + "text": "a", + "pad": 70, + "sk": [ + { + "text": "á", + "id": "U_00E1" + }, + { + "text": "à", + "id": "U_00E0" + }, + { + "text": "a", + "id": "K_A" + }, + { + "text": "ä", + "id": "U_00E4" + }, + { + "text": "å", + "id": "U_00E5" + }, + { + "text": "â", + "id": "U_00E2" + }, + { + "text": "ã", + "id": "U_00E3" + } + ] + }, + { + "id": "K_S", + "text": "s" + }, + { + "id": "K_D", + "text": "d", + "sk": [ + { + "text": "d", + "id": "K_D" + }, + { + "text": "ď", + "id": "U_010F" + }, + { + "text": "đ", + "id": "U_0111" + }, + { + "text": "ɗ", + "id": "U_0257" + }, + { + "text": "default", + "id": "T_d_default", + "default": true + }, + { + "text": "ȡ", + "id": "U_0221" + } + ] + }, + { + "id": "K_F", + "text": "f" + }, + { + "id": "K_G", + "text": "g" + }, + { + "id": "K_H", + "text": "h" + }, + { + "id": "K_J", + "text": "j" + }, + { + "id": "K_K", + "text": "k" + }, + { + "id": "K_L", + "text": "l" + }, + { + "id": "T_new_88", + "width": 10, + "sp": 10 + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Shift*", + "width": 110, + "sp": 1, + "nextlayer": "shift" + }, + { + "id": "K_Z", + "text": "z" + }, + { + "id": "K_X", + "text": "x" + }, + { + "id": "K_C", + "text": "c" + }, + { + "id": "K_V", + "text": "v" + }, + { + "id": "K_B", + "text": "b" + }, + { + "id": "K_N", + "text": "n" + }, + { + "id": "K_M", + "text": "m" + }, + { + "id": "K_PERIOD", + "text": ".", + "sk": [ + { + "text": ",", + "id": "K_COMMA" + }, + { + "text": "!", + "id": "K_1", + "layer": "shift" + }, + { + "text": "?", + "id": "K_SLASH", + "layer": "shift" + }, + { + "text": "'", + "id": "K_QUOTE" + }, + { + "text": "\"", + "id": "K_QUOTE", + "layer": "shift" + }, + { + "text": "\\", + "id": "K_BKSLASH" + }, + { + "text": ":", + "id": "K_COLON", + "layer": "shift" + }, + { + "text": ";", + "id": "K_COLON" + } + ] + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "width": 90, + "sp": 1 + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_NUMLOCK", + "text": "*123*", + "width": 140, + "sp": 1, + "nextlayer": "numeric" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": 120, + "sp": 1 + }, + { + "id": "K_SPACE", + "text": "", + "width": 630, + "sp": 0 + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": 140, + "sp": 1 + } + ] + } + ] + }, + { + "id": "shift", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_Q", + "text": "Q" + }, + { + "id": "K_W", + "text": "W" + }, + { + "id": "K_E", + "text": "E" + }, + { + "id": "K_R", + "text": "R" + }, + { + "id": "K_T", + "text": "T" + }, + { + "id": "K_Y", + "text": "Y" + }, + { + "id": "K_U", + "text": "U" + }, + { + "id": "K_I", + "text": "I" + }, + { + "id": "K_O", + "text": "O" + }, + { + "id": "K_P", + "text": "P" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_A", + "text": "A", + "pad": 70 + }, + { + "id": "K_S", + "text": "S" + }, + { + "id": "K_D", + "text": "D" + }, + { + "id": "K_F", + "text": "F" + }, + { + "id": "K_G", + "text": "G" + }, + { + "id": "K_H", + "text": "H" + }, + { + "id": "K_J", + "text": "J" + }, + { + "id": "K_K", + "text": "K" + }, + { + "id": "K_L", + "text": "L" + }, + { + "sp": 10, + "width": 10 + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Shift*", + "width": 110, + "sp": 2, + "nextlayer": "default" + }, + { + "id": "K_Z", + "text": "Z" + }, + { + "id": "K_X", + "text": "X" + }, + { + "id": "K_C", + "text": "C" + }, + { + "id": "K_V", + "text": "V" + }, + { + "id": "K_B", + "text": "B" + }, + { + "id": "K_N", + "text": "N" + }, + { + "id": "K_M", + "text": "M" + }, + { + "id": "K_PERIOD", + "text": ".", + "layer": "default", + "sk": [ + { + "text": ",", + "id": "K_COMMA", + "layer": "default" + }, + { + "text": "!", + "id": "K_1", + "layer": "shift" + }, + { + "text": "?", + "id": "K_SLASH", + "layer": "shift" + }, + { + "text": "'", + "id": "K_QUOTE", + "layer": "default" + }, + { + "text": "\"", + "id": "K_QUOTE", + "layer": "shift" + }, + { + "text": "\\", + "id": "K_BKSLASH", + "layer": "default" + }, + { + "text": ":", + "id": "K_COLON", + "layer": "shift" + }, + { + "text": ";", + "id": "K_COLON", + "layer": "default" + } + ] + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "width": 90, + "sp": 1 + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_NUMLOCK", + "text": "*123*", + "width": 140, + "sp": 1, + "nextlayer": "numeric" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": 120, + "sp": 1 + }, + { + "id": "K_SPACE", + "text": "", + "width": 630, + "sp": 0 + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": 140, + "sp": 1 + } + ] + } + ] + }, + { + "id": "numeric", + "row": [ + { + "id": 1, + "key": [ + { + "id": "K_1", + "text": "1" + }, + { + "id": "K_2", + "text": "2" + }, + { + "id": "K_3", + "text": "3" + }, + { + "id": "K_4", + "text": "4" + }, + { + "id": "K_5", + "text": "5" + }, + { + "id": "K_6", + "text": "6" + }, + { + "id": "K_7", + "text": "7" + }, + { + "id": "K_8", + "text": "8" + }, + { + "id": "K_9", + "text": "9" + }, + { + "id": "K_0", + "text": "0" + } + ] + }, + { + "id": 2, + "key": [ + { + "id": "K_4", + "text": "$", + "layer": "shift", + "pad": 70 + }, + { + "id": "K_2", + "text": "@", + "layer": "shift" + }, + { + "id": "K_3", + "text": "#", + "layer": "shift" + }, + { + "id": "K_5", + "text": "%", + "layer": "shift" + }, + { + "id": "K_7", + "text": "&", + "layer": "shift" + }, + { + "id": "K_HYPHEN", + "text": "_", + "layer": "shift" + }, + { + "id": "K_EQUAL", + "text": "=", + "layer": "default" + }, + { + "id": "K_BKSLASH", + "text": "|", + "layer": "shift" + }, + { + "id": "K_BKSLASH", + "text": "\\", + "layer": "default" + }, + { + "text": "", + "width": 10, + "sp": 10 + } + ] + }, + { + "id": 3, + "key": [ + { + "id": "K_SHIFT", + "text": "*Shift*", + "width": 110, + "sp": 1 + }, + { + "id": "K_LBRKT", + "text": "[", + "sk": [ + { + "id": "U_00AB", + "text": "«" + }, + { + "id": "K_COMMA", + "text": "<", + "layer": "shift" + }, + { + "id": "K_LBRKT", + "text": "{", + "layer": "shift" + } + ] + }, + { + "id": "K_9", + "text": "(", + "layer": "shift" + }, + { + "id": "K_0", + "text": ")", + "layer": "shift" + }, + { + "id": "K_RBRKT", + "text": "]", + "sk": [ + { + "id": "U_00BB", + "text": "»" + }, + { + "id": "K_PERIOD", + "text": ">", + "layer": "shift" + }, + { + "id": "K_RBRKT", + "text": "}", + "layer": "shift" + } + ] + }, + { + "id": "K_EQUAL", + "text": "+", + "layer": "shift" + }, + { + "id": "K_HYPHEN", + "text": "-", + "layer": "default" + }, + { + "id": "K_8", + "text": "*", + "layer": "shift" + }, + { + "id": "K_SLASH", + "text": "/", + "layer": "default" + }, + { + "id": "K_BKSP", + "text": "*BkSp*", + "width": 90, + "sp": 1 + } + ] + }, + { + "id": 4, + "key": [ + { + "id": "K_LOWER", + "text": "*abc*", + "width": 140, + "sp": 1, + "nextlayer": "default" + }, + { + "id": "K_LOPT", + "text": "*Menu*", + "width": 120, + "sp": 1 + }, + { + "id": "K_SPACE", + "text": "", + "width": 630, + "sp": 0 + }, + { + "id": "K_ENTER", + "text": "*Enter*", + "width": 140, + "sp": 1 + } + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.kmn b/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.kmn new file mode 100644 index 00000000000..f1ac06ea5e9 --- /dev/null +++ b/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.kmn @@ -0,0 +1,16 @@ +c default_subkey generated from template at 2023-08-21 10:14:50 +c with name "default-subkey" +store(&VERSION) '10.0' +store(&NAME) 'default-subkey' +store(©RIGHT) '© SIL International' +store(&KEYBOARDVERSION) '1.0' +store(&TARGETS) 'any' +store(&BITMAP) 'default_subkey.ico' +store(&VISUALKEYBOARD) 'default_subkey.kvks' +store(&LAYOUTFILE) 'default_subkey.keyman-touch-layout' + +begin Unicode > use(main) + +group(main) using keys + ++ [ T_d_default ] > "default" diff --git a/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.kps b/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.kps new file mode 100644 index 00000000000..97cbafecd72 --- /dev/null +++ b/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.kps @@ -0,0 +1,67 @@ + + + + 17.0.162.0 + 7.0 + + + + readme.htm + + + + + + + + + + default-subkey + © SIL International + SIL International + + + + + ..\build\default_subkey.kmx + + 0 + .kmx + + + ..\build\default_subkey.js + + 0 + .js + + + ..\build\default_subkey.kvk + + 0 + .kvk + + + welcome.htm + + 0 + .htm + + + readme.htm + + 0 + .htm + + + + + default-subkey + default_subkey + 1.0 + + English + + + + + diff --git a/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.kvks b/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.kvks new file mode 100644 index 00000000000..830237b17e9 --- /dev/null +++ b/web/src/test/manual/web/default-subkey/kbd_source/source/default_subkey.kvks @@ -0,0 +1,8 @@ + + +
+ 10.0 + default_subkey + +
+
diff --git a/web/src/test/manual/web/default-subkey/kbd_source/source/readme.htm b/web/src/test/manual/web/default-subkey/kbd_source/source/readme.htm new file mode 100644 index 00000000000..8bce21ff5d7 --- /dev/null +++ b/web/src/test/manual/web/default-subkey/kbd_source/source/readme.htm @@ -0,0 +1,24 @@ + + + + + + default-subkey + + + + +

default-subkey

+ +

+ default-subkey 1.0 generated from template. +

+ +

© SIL International

+ + + diff --git a/web/src/test/manual/web/default-subkey/kbd_source/source/welcome.htm b/web/src/test/manual/web/default-subkey/kbd_source/source/welcome.htm new file mode 100644 index 00000000000..cdf7e1e234b --- /dev/null +++ b/web/src/test/manual/web/default-subkey/kbd_source/source/welcome.htm @@ -0,0 +1,26 @@ + + + + + + Start Using default-subkey + + + + +

Start Using default-subkey

+ +

+ default-subkey 1.0 generated from template. +

+ +

Keyboard Layout

+ + + + + \ No newline at end of file diff --git a/web/src/test/manual/web/index.html b/web/src/test/manual/web/index.html index feb1334a7a4..2597d8caac5 100644 --- a/web/src/test/manual/web/index.html +++ b/web/src/test/manual/web/index.html @@ -64,6 +64,7 @@

Test Caps Lock Layer (#3620)

Test Start of Sentence (#3621)

Test start of sentence keyboard rules (#5963)

Tests predictive text & other handling of rule matching when the final rule group does not match (#6005)

+

Tests handling of new default-subkey feature (#9430)

Test special characters rendering with keymanweb-osk.ttf (#9469)

Other

Keystroke processing regression test engine.

diff --git a/web/src/test/manual/web/issue2924/source/dmg.dv.test/dmg.dv.test.kpj b/web/src/test/manual/web/issue2924/source/dmg.dv.test/dmg.dv.test.kpj index e4d5c9c567a..855646cf269 100644 --- a/web/src/test/manual/web/issue2924/source/dmg.dv.test/dmg.dv.test.kpj +++ b/web/src/test/manual/web/issue2924/source/dmg.dv.test/dmg.dv.test.kpj @@ -48,13 +48,6 @@ .md
- - id_4b12eec2a4d0bfc7b5441a4a59691eab - dmg.dv.test.model_info - dmg.dv.test.model_info - - .model_info - id_6ee5fa96871fb96ecebf2cbe9ec7425b dmg.dv.test.model.js diff --git a/web/src/test/manual/web/issue2924/source/dmg.dv.test/dmg.dv.test.model_info b/web/src/test/manual/web/issue2924/source/dmg.dv.test/dmg.dv.test.model_info deleted file mode 100644 index 03399a9f8e5..00000000000 --- a/web/src/test/manual/web/issue2924/source/dmg.dv.test/dmg.dv.test.model_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "dv" - ], - "description": "test generated from template" -} diff --git a/web/src/test/manual/web/issue2924/source/testvariable/testvariable.keyboard_info b/web/src/test/manual/web/issue2924/source/testvariable/testvariable.keyboard_info deleted file mode 100644 index 5e543492076..00000000000 --- a/web/src/test/manual/web/issue2924/source/testvariable/testvariable.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "en" - ], - "description": "testvariable generated from template" -} diff --git a/web/src/test/manual/web/issue2924/source/testvariable/testvariable.kpj b/web/src/test/manual/web/issue2924/source/testvariable/testvariable.kpj index a138eda11b6..bc3e6a52ff1 100644 --- a/web/src/test/manual/web/issue2924/source/testvariable/testvariable.kpj +++ b/web/src/test/manual/web/issue2924/source/testvariable/testvariable.kpj @@ -51,13 +51,6 @@ .md - - id_efaa79249320cda8d8e2886ddbf0475e - testvariable.keyboard_info - testvariable.keyboard_info - - .keyboard_info - id_0c280146c90477c06029a32b48581f90 testvariable.kmx diff --git a/web/src/test/manual/web/issue3701/test3701/test3701.kpj b/web/src/test/manual/web/issue3701/test3701/test3701.kpj index 5db48cd1155..39071cf00b3 100644 --- a/web/src/test/manual/web/issue3701/test3701/test3701.kpj +++ b/web/src/test/manual/web/issue3701/test3701/test3701.kpj @@ -51,13 +51,6 @@ .md - - id_e18bba9285544da2bad5cb8bf0b153d6 - test3701.keyboard_info - test3701.keyboard_info - - .keyboard_info - id_e944ee9b3d06f36294030601f3bd33ae test3701.js diff --git a/web/src/test/manual/web/issue917-context-and-notany/test_917/test_917.keyboard_info b/web/src/test/manual/web/issue917-context-and-notany/test_917/test_917.keyboard_info deleted file mode 100644 index ac397323086..00000000000 --- a/web/src/test/manual/web/issue917-context-and-notany/test_917/test_917.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - - ], - "description": "test_917 generated from template" -} diff --git a/web/src/test/manual/web/issue917-context-and-notany/test_917/test_917.kpj b/web/src/test/manual/web/issue917-context-and-notany/test_917/test_917.kpj index 9c3cf4ad2fb..ca0459a53bf 100644 --- a/web/src/test/manual/web/issue917-context-and-notany/test_917/test_917.kpj +++ b/web/src/test/manual/web/issue917-context-and-notany/test_917/test_917.kpj @@ -51,13 +51,6 @@ .md - - id_e7fcb5e5546db6914d2f9d00de067f23 - test_917.keyboard_info - test_917.keyboard_info - - .keyboard_info - id_00188325e13e88dc8f51b78b3fcd756e test_917.kmx diff --git a/web/src/test/manual/web/issue9469/test9469/HISTORY.md b/web/src/test/manual/web/issue9469/test9469/HISTORY.md index 8e3fdbb7ced..d520e029426 100644 --- a/web/src/test/manual/web/issue9469/test9469/HISTORY.md +++ b/web/src/test/manual/web/issue9469/test9469/HISTORY.md @@ -1,6 +1,10 @@ test9469 Change History ==================== +1.1 (2023-09-12) +---------------- +* Add non-printing space characters + 1.0 (2023-08-17) ---------------- * Created by SIL International diff --git a/web/src/test/manual/web/issue9469/test9469/build/test9469.js b/web/src/test/manual/web/issue9469/test9469/build/test9469.js index 624b9990bd7..10e028fdd68 100644 --- a/web/src/test/manual/web/issue9469/test9469/build/test9469.js +++ b/web/src/test/manual/web/issue9469/test9469/build/test9469.js @@ -1 +1 @@ -if(typeof keyman === 'undefined') {console.log('Keyboard requires KeymanWeb 10.0 or later');if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");} else {KeymanWeb.KR(new Keyboard_test9469());}function Keyboard_test9469(){this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;this.KI="Keyboard_test9469";this.KN="test9469";this.KMINVER="10.0";this.KV={F:' 1em "Arial"',K102:0};this.KV.KLS={};this.KV.BK=(function(x){var e=Array.apply(null,Array(65)).map(String.prototype.valueOf,""),r=[],v,i,m=['default','shift','ctrl','shift-ctrl','alt','shift-alt','ctrl-alt','shift-ctrl-alt'];for(i=m.length-1;i>=0;i--)if((v=x[m[i]])||r.length)r=(v?v:e).slice().concat(r);return r})(this.KV.KLS);this.KDU=0;this.KH='';this.KM=0;this.KBVER="1.0";this.KMBM=0x0000;this.KVKL={"phone":{"font":"Tahoma","displayUnderlying":false,"layer":[{"id":"default","row":[{"id":"1","key":[{"id":"K_TAB","text":"*Tab*"},{"id":"T_TABLEFT","text":"*TabLeft*"},{"id":"K_E","text":"e"},{"id":"K_R","text":"r"},{"id":"K_T","text":"t"},{"id":"K_Y","text":"y"},{"id":"K_U","text":"u"},{"id":"K_I","text":"i"},{"id":"K_O","text":"o"},{"id":"K_P","text":"p"}]},{"id":"2","key":[{"id":"K_A","pad":"50","text":"a"},{"id":"K_S","text":"s"},{"id":"K_D","text":"d"},{"id":"K_F","text":"f"},{"id":"K_G","text":"g"},{"id":"K_H","text":"h"},{"id":"K_J","text":"j"},{"id":"K_K","text":"k"},{"id":"T_RTLBKSP","sp":"1","text":this._v>13?"*RTLBkSp*":"*BkSp*"},{"id":"T_new_338","sp":"1","text":this._v>13?"*LTRBkSp*":"*BkSp*"}]},{"id":"3","key":[{"nextlayer":"shift","id":"K_SHIFT","sp":"1","text":"*Shift*","sk":[{"id":"T_new_206","text":"*Alt*"},{"id":"T_new_205","text":"*Ctrl*"},{"id":"T_new_207","text":"*Caps*"},{"id":"T_new_208","text":"*ABC*"},{"id":"T_new_209","text":"*abc*"},{"id":"T_new_210","text":"*Symbol*"},{"id":"T_new_211","text":"*AltGr*"},{"id":"T_new_212","text":"*LAlt*"},{"id":"T_new_258","text":"*RAlt*"},{"id":"T_new_259","text":"*LCtrl*"},{"id":"T_new_260","text":"*RCtrl*"},{"id":"T_new_261","text":"*LAltCtrlShift*"},{"id":"T_new_262","text":"*RAltCtrlShift*"},{"id":"T_new_263","text":"*AltShift*"},{"id":"T_new_264","text":"*CtrlShift*"},{"id":"T_new_265","text":"*AltCtrlShift*"},{"id":"T_new_266","text":"*LAltShift*"},{"id":"T_new_267","text":"*RAltShift*"},{"id":"T_new_268","text":"*LCtrlShift*"},{"id":"T_new_269","text":"*RCtrlShift*"},{"id":"T_new_292","text":this._v>13?"*ShiftLock*":"*Shift*"},{"id":"T_new_293","text":this._v>13?"*ShiftedLock*":"*Shifted*"}]},{"id":"T_ZWNJ","text":this._v>13?"*ZWNJ*":"<|>"},{"id":"T_ZWNJIOS","text":this._v>13?"*ZWNJiOS*":"<|>"},{"id":"T_ZWNJANDROID","text":this._v>13?"*ZWNJAndroid*":"<|>"},{"id":"K_V","text":"v"},{"id":"K_B","text":"b"},{"id":"K_N","text":"n"},{"id":"K_M","text":"m"},{"id":"K_PERIOD","text":".","sk":[{"id":"K_COMMA","text":","},{"layer":"shift","id":"K_1","text":"!"},{"layer":"shift","id":"K_SLASH","text":"?"},{"id":"K_QUOTE","text":"'"},{"layer":"shift","id":"K_QUOTE","text":"\""},{"id":"K_BKSLASH","text":"\\"},{"layer":"shift","id":"K_COLON","text":":"},{"id":"K_COLON","text":";"}]},{"width":"100","id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"4","key":[{"nextlayer":"numeric","width":"150","id":"K_NUMLOCK","sp":"1","text":"*123*"},{"width":"120","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"610","id":"K_SPACE"},{"id":"K_ROPT","text":"*Hide*"},{"width":"150","id":"K_ENTER","sp":"1","text":"*Enter*","sk":[{"id":"T_new_270","text":this._v>13?"*RTLEnter*":"*Enter*"},{"id":"T_new_295","text":this._v>13?"*LTREnter*":"*Enter*"}]}]}]},{"id":"shift","row":[{"id":"1","key":[{"id":"K_Q","text":"*Alt*"},{"id":"K_W","text":"*Ctrl*"},{"id":"K_E","text":"*Caps*"},{"id":"K_R","text":"*ABC*"},{"id":"K_T","text":"*abc*"},{"id":"K_Y","text":"*123*"},{"id":"K_U","text":"*Symbol*"},{"id":"K_I","text":"*Currency*"},{"id":"K_O","text":"*Shifted*"},{"id":"K_P","text":"*AltGr*"}]},{"id":"2","key":[{"id":"K_A","pad":"50","text":"*LAlt*"},{"id":"K_S","text":"*RAlt*"},{"id":"K_D","text":"*LCtrl*"},{"id":"K_F","text":"*RCtrl*"},{"id":"K_G","text":"*LAltCtrl*"},{"id":"K_H","text":"*RAltCtrl*"},{"id":"K_J","text":"*LAltCtrlShift*"},{"id":"K_K","text":"*RAltCtrlShift*"},{"id":"K_L","text":"*AltShift*"},{"id":"T_new_232","text":"*CtrlShift*"}]},{"id":"3","key":[{"nextlayer":"default","id":"K_SHIFT","sp":"2","text":"*Shifted*"},{"id":"K_Z","text":"*AltCtrlShift*"},{"id":"K_X","text":"*LAltShift*"},{"id":"K_C","text":"*RAltShift*"},{"id":"K_V","text":"*RAltShift*"},{"id":"K_B","text":"*RCtrlShift*"},{"id":"K_N","text":this._v>13?"*RTLEnter*":"*Enter*"},{"id":"K_M","text":this._v>13?"*LTREnter*":"*Enter*"},{"layer":"default","id":"K_PERIOD","text":".","sk":[{"layer":"default","id":"K_COMMA","text":","},{"layer":"shift","id":"K_1","text":"!"},{"layer":"shift","id":"K_SLASH","text":"?"},{"layer":"default","id":"K_QUOTE","text":"'"},{"layer":"shift","id":"K_QUOTE","text":"\""},{"layer":"default","id":"K_BKSLASH","text":"\\"},{"layer":"shift","id":"K_COLON","text":":"},{"layer":"default","id":"K_COLON","text":";"}]},{"id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"4","key":[{"nextlayer":"numeric","width":"150","id":"K_NUMLOCK","sp":"1","text":"*123*"},{"width":"120","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"610","id":"K_SPACE"},{"width":"150","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]},{"id":"numeric","row":[{"id":"1","key":[{"id":"K_1","text":"1"},{"id":"K_2","text":"2"},{"id":"K_3","text":"3"},{"id":"K_4","text":"4"},{"id":"K_5","text":"5"},{"id":"K_6","text":"6"},{"id":"K_7","text":"7"},{"id":"K_8","text":"8"},{"id":"K_9","text":"9"},{"id":"K_0","text":"0"}]},{"id":"2","key":[{"layer":"shift","id":"K_4","pad":"50","text":"$"},{"layer":"shift","id":"K_2","text":"@"},{"layer":"shift","id":"K_3","text":"#"},{"layer":"shift","id":"K_5","text":"%"},{"layer":"shift","id":"K_7","text":"&"},{"layer":"shift","id":"K_HYPHEN","text":"_"},{"layer":"default","id":"K_EQUAL","text":"="},{"layer":"shift","id":"K_BKSLASH","text":"|"},{"layer":"default","id":"K_BKSLASH","text":"\\"},{"width":"10","sp":"10"}]},{"id":"3","key":[{"id":"K_LBRKT","pad":"110","text":"[","sk":[{"id":"U_00AB","text":"\u00AB"},{"layer":"shift","id":"K_COMMA","text":"<"},{"layer":"shift","id":"K_LBRKT","text":"{"}]},{"layer":"shift","id":"K_9","text":"("},{"layer":"shift","id":"K_0","text":")"},{"id":"K_RBRKT","text":"]","sk":[{"id":"U_00BB","text":"\u00BB"},{"layer":"shift","id":"K_PERIOD","text":">"},{"layer":"shift","id":"K_RBRKT","text":"}"}]},{"layer":"shift","id":"K_EQUAL","text":"+"},{"id":"K_HYPHEN","text":"-"},{"layer":"shift","id":"K_8","text":"*"},{"id":"K_SLASH","text":"\/"},{"width":"100","id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"4","key":[{"nextlayer":"default","width":"150","id":"K_LOWER","sp":"1","text":"*abc*"},{"width":"120","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"610","id":"K_SPACE"},{"width":"150","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]}]}};this.KVER="16.0.141.0";this.KVS=[];this.gs=function(t,e) {return this.g0(t,e);};this.gs=function(t,e) {return this.g0(t,e);};this.g0=function(t,e) {var k=KeymanWeb,r=0,m=0;return r;};} \ No newline at end of file +if(typeof keyman === 'undefined') {console.log('Keyboard requires KeymanWeb 10.0 or later');if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");} else {KeymanWeb.KR(new Keyboard_test9469());}function Keyboard_test9469(){this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;this.KI="Keyboard_test9469";this.KN="test9469";this.KMINVER="10.0";this.KV={F:' 1em "Arial"',K102:0};this.KV.KLS={};this.KV.BK=(function(x){var e=Array.apply(null,Array(65)).map(String.prototype.valueOf,""),r=[],v,i,m=['default','shift','ctrl','shift-ctrl','alt','shift-alt','ctrl-alt','shift-ctrl-alt'];for(i=m.length-1;i>=0;i--)if((v=x[m[i]])||r.length)r=(v?v:e).slice().concat(r);return r})(this.KV.KLS);this.KDU=0;this.KH='';this.KM=0;this.KBVER="1.1";this.KMBM=0x0000;this.KVKD="T_ZWNJ T_ZWNJIOS T_ZWNJANDROID";this.KVKL={"phone":{"font":"Tahoma","displayUnderlying":false,"layer":[{"id":"default","row":[{"id":"1","key":[{"id":"K_TAB","text":"*Tab*"},{"id":"T_TABLEFT","text":"*TabLeft*"},{"id":"U_0020","text":"*Sp*"},{"id":"U_00A0","text":"*NBSp*"},{"id":"U_202F","text":"*NarNBSp*"},{"id":"U_2000","text":"*EnQ*"},{"id":"U_2001","text":"*EmQ*"},{"id":"U_2002","text":"*EnSp*"},{"id":"U_2003","text":"*EmSp*"},{"id":"U_2008","text":"*PunctSp*"}]},{"id":"2","key":[{"id":"U_2009","pad":"50","text":"*ThSp*"},{"id":"U_200A","text":"*HSp*"},{"id":"U_200B","text":"*ZWSp*"},{"id":"U_200E","text":"*LTRM*"},{"id":"U_200F","text":"*RTLM*"},{"id":"U_00AD","text":"*SH*"},{"id":"T_0009","text":"*HTab*"},{"id":"U_200F","text":"*RTLM*"},{"id":"T_RTLBKSP","sp":"1","text":this._v>13?"*RTLBkSp*":"*BkSp*"},{"id":"T_new_338","sp":"1","text":this._v>13?"*LTRBkSp*":"*BkSp*"}]},{"id":"3","key":[{"nextlayer":"shift","id":"K_SHIFT","sp":"1","text":"*Shift*","sk":[{"id":"T_new_206","text":"*Alt*"},{"id":"T_new_205","text":"*Ctrl*"},{"id":"T_new_207","text":"*Caps*"},{"id":"T_new_208","text":"*ABC*"},{"id":"T_new_209","text":"*abc*"},{"id":"T_new_210","text":"*Symbol*"},{"id":"T_new_211","text":"*AltGr*"},{"id":"T_new_212","text":"*LAlt*"},{"id":"T_new_258","text":"*RAlt*"},{"id":"T_new_259","text":"*LCtrl*"},{"id":"T_new_260","text":"*RCtrl*"},{"id":"T_new_261","text":"*LAltCtrlShift*"},{"id":"T_new_262","text":"*RAltCtrlShift*"},{"id":"T_new_263","text":"*AltShift*"},{"id":"T_new_264","text":"*CtrlShift*"},{"id":"T_new_265","text":"*AltCtrlShift*"},{"id":"T_new_266","text":"*LAltShift*"},{"id":"T_new_267","text":"*RAltShift*"},{"id":"T_new_268","text":"*LCtrlShift*"},{"id":"T_new_269","text":"*RCtrlShift*"},{"id":"T_new_292","text":this._v>13?"*ShiftLock*":"*Shift*"},{"id":"T_new_293","text":this._v>13?"*ShiftedLock*":"*Shifted*"}]},{"id":"T_ZWNJ","text":"*ZWNJGeneric*"},{"id":"T_ZWNJIOS","text":this._v>13?"*ZWNJiOS*":"<|>"},{"id":"T_ZWNJANDROID","text":this._v>13?"*ZWNJAndroid*":"<|>"},{"id":"U_200D","text":"*ZWJ*"},{"id":"U_2060","text":"*WJ*"},{"id":"U_034F","text":"*CGJ*"},{"id":"K_M","text":"m"},{"id":"K_PERIOD","text":".","sk":[{"id":"K_COMMA","text":","},{"layer":"shift","id":"K_1","text":"!"},{"layer":"shift","id":"K_SLASH","text":"?"},{"id":"K_QUOTE","text":"'"},{"layer":"shift","id":"K_QUOTE","text":"\""},{"id":"K_BKSLASH","text":"\\"},{"layer":"shift","id":"K_COLON","text":":"},{"id":"K_COLON","text":";"}]},{"width":"100","id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"4","key":[{"nextlayer":"numeric","width":"150","id":"K_NUMLOCK","sp":"1","text":"*123*"},{"width":"120","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"590","id":"K_SPACE"},{"id":"K_ROPT","text":"*Hide*"},{"width":"150","id":"K_ENTER","sp":"1","text":"*Enter*","sk":[{"id":"T_new_270","text":this._v>13?"*RTLEnter*":"*Enter*"},{"id":"T_new_295","text":this._v>13?"*LTREnter*":"*Enter*"}]}]}]},{"id":"shift","row":[{"id":"1","key":[{"id":"K_Q","text":"*Alt*"},{"id":"K_W","text":"*Ctrl*"},{"id":"K_E","text":"*Caps*"},{"id":"K_R","text":"*ABC*"},{"id":"K_T","text":"*abc*"},{"id":"K_Y","text":"*123*"},{"id":"K_U","text":"*Symbol*"},{"id":"K_I","text":"*Currency*"},{"id":"K_O","text":"*Shifted*"},{"id":"K_P","text":"*AltGr*"}]},{"id":"2","key":[{"id":"K_A","pad":"50","text":"*LAlt*"},{"id":"K_S","text":"*RAlt*"},{"id":"K_D","text":"*LCtrl*"},{"id":"K_F","text":"*RCtrl*"},{"id":"K_G","text":"*LAltCtrl*"},{"id":"K_H","text":"*RAltCtrl*"},{"id":"K_J","text":"*LAltCtrlShift*"},{"id":"K_K","text":"*RAltCtrlShift*"},{"id":"K_L","text":"*AltShift*"},{"id":"T_new_232","text":"*CtrlShift*"}]},{"id":"3","key":[{"nextlayer":"default","id":"K_SHIFT","sp":"2","text":"*Shifted*"},{"id":"K_Z","text":"*AltCtrlShift*"},{"id":"K_X","text":"*LAltShift*"},{"id":"K_C","text":"*RAltShift*"},{"id":"K_V","text":"*RAltShift*"},{"id":"K_B","text":"*RCtrlShift*"},{"id":"K_N","text":this._v>13?"*RTLEnter*":"*Enter*"},{"id":"K_M","text":this._v>13?"*LTREnter*":"*Enter*"},{"layer":"default","id":"K_PERIOD","text":".","sk":[{"layer":"default","id":"K_COMMA","text":","},{"layer":"shift","id":"K_1","text":"!"},{"layer":"shift","id":"K_SLASH","text":"?"},{"layer":"default","id":"K_QUOTE","text":"'"},{"layer":"shift","id":"K_QUOTE","text":"\""},{"layer":"default","id":"K_BKSLASH","text":"\\"},{"layer":"shift","id":"K_COLON","text":":"},{"layer":"default","id":"K_COLON","text":";"}]},{"id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"4","key":[{"nextlayer":"numeric","width":"150","id":"K_NUMLOCK","sp":"1","text":"*123*"},{"width":"120","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"610","id":"K_SPACE"},{"width":"150","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]},{"id":"numeric","row":[{"id":"1","key":[{"id":"K_1","text":"1"},{"id":"K_2","text":"2"},{"id":"K_3","text":"3"},{"id":"K_4","text":"4"},{"id":"K_5","text":"5"},{"id":"K_6","text":"6"},{"id":"K_7","text":"7"},{"id":"K_8","text":"8"},{"id":"K_9","text":"9"},{"id":"K_0","text":"0"}]},{"id":"2","key":[{"layer":"shift","id":"K_4","pad":"50","text":"$"},{"layer":"shift","id":"K_2","text":"@"},{"layer":"shift","id":"K_3","text":"#"},{"layer":"shift","id":"K_5","text":"%"},{"layer":"shift","id":"K_7","text":"&"},{"layer":"shift","id":"K_HYPHEN","text":"_"},{"layer":"default","id":"K_EQUAL","text":"="},{"layer":"shift","id":"K_BKSLASH","text":"|"},{"layer":"default","id":"K_BKSLASH","text":"\\"},{"width":"10","sp":"10"}]},{"id":"3","key":[{"id":"K_LBRKT","pad":"110","text":"[","sk":[{"id":"U_00AB","text":"\u00AB"},{"layer":"shift","id":"K_COMMA","text":"<"},{"layer":"shift","id":"K_LBRKT","text":"{"}]},{"layer":"shift","id":"K_9","text":"("},{"layer":"shift","id":"K_0","text":")"},{"id":"K_RBRKT","text":"]","sk":[{"id":"U_00BB","text":"\u00BB"},{"layer":"shift","id":"K_PERIOD","text":">"},{"layer":"shift","id":"K_RBRKT","text":"}"}]},{"layer":"shift","id":"K_EQUAL","text":"+"},{"id":"K_HYPHEN","text":"-"},{"layer":"shift","id":"K_8","text":"*"},{"id":"K_SLASH","text":"\/"},{"width":"100","id":"K_BKSP","sp":"1","text":"*BkSp*"}]},{"id":"4","key":[{"nextlayer":"default","width":"150","id":"K_LOWER","sp":"1","text":"*abc*"},{"width":"120","id":"K_LOPT","sp":"1","text":"*Menu*"},{"width":"610","id":"K_SPACE"},{"width":"150","id":"K_ENTER","sp":"1","text":"*Enter*"}]}]}]}};this.KVER="16.0.141.0";this.KVS=[];this.gs=function(t,e) {return this.g0(t,e);};this.gs=function(t,e) {return this.g0(t,e);};this.g0=function(t,e) {var k=KeymanWeb,r=0,m=0;if(k.KKM(e,16384,256)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"‌");}}else if(k.KKM(e,16384,257)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"‌");}}else if(k.KKM(e,16384,258)) {if(1){r=m=1;k.KDC(0,t);k.KO(-1,t,"‌");}}return r;};} \ No newline at end of file diff --git a/web/src/test/manual/web/issue9469/test9469/source/test9469.keyman-touch-layout b/web/src/test/manual/web/issue9469/test9469/source/test9469.keyman-touch-layout index b771afc4e7d..5f8ac2101a1 100644 --- a/web/src/test/manual/web/issue9469/test9469/source/test9469.keyman-touch-layout +++ b/web/src/test/manual/web/issue9469/test9469/source/test9469.keyman-touch-layout @@ -17,36 +17,36 @@ "text": "*TabLeft*" }, { - "id": "K_E", - "text": "e" + "id": "U_0020", + "text": "*Sp*" }, { - "id": "K_R", - "text": "r" + "id": "U_00A0", + "text": "*NBSp*" }, { - "id": "K_T", - "text": "t" + "id": "U_202F", + "text": "*NarNBSp*" }, { - "id": "K_Y", - "text": "y" + "id": "U_2000", + "text": "*EnQ*" }, { - "id": "K_U", - "text": "u" + "id": "U_2001", + "text": "*EmQ*" }, { - "id": "K_I", - "text": "i" + "id": "U_2002", + "text": "*EnSp*" }, { - "id": "K_O", - "text": "o" + "id": "U_2003", + "text": "*EmSp*" }, { - "id": "K_P", - "text": "p" + "id": "U_2008", + "text": "*PunctSp*" } ] }, @@ -54,37 +54,37 @@ "id": 2, "key": [ { - "id": "K_A", - "text": "a", + "id": "U_2009", + "text": "*ThSp*", "pad": 50 }, { - "id": "K_S", - "text": "s" + "id": "U_200A", + "text": "*HSp*" }, { - "id": "K_D", - "text": "d" + "id": "U_200B", + "text": "*ZWSp*" }, { - "id": "K_F", - "text": "f" + "id": "U_200E", + "text": "*LTRM*" }, { - "id": "K_G", - "text": "g" + "id": "U_200F", + "text": "*RTLM*" }, { - "id": "K_H", - "text": "h" + "id": "U_00AD", + "text": "*SH*" }, { - "id": "K_J", - "text": "j" + "id": "T_0009", + "text": "*HTab*" }, { - "id": "K_K", - "text": "k" + "id": "U_200F", + "text": "*RTLM*" }, { "id": "T_RTLBKSP", @@ -199,7 +199,7 @@ }, { "id": "T_ZWNJ", - "text": "*ZWNJ*" + "text": "*ZWNJGeneric*" }, { "id": "T_ZWNJIOS", @@ -210,16 +210,16 @@ "text": "*ZWNJAndroid*" }, { - "id": "K_V", - "text": "v" + "id": "U_200D", + "text": "*ZWJ*" }, { - "id": "K_B", - "text": "b" + "id": "U_2060", + "text": "*WJ*" }, { - "id": "K_N", - "text": "n" + "id": "U_034F", + "text": "*CGJ*" }, { "id": "K_M", @@ -294,7 +294,7 @@ { "id": "K_SPACE", "text": "", - "width": 610, + "width": 590, "sp": 0 }, { diff --git a/web/src/test/manual/web/issue9469/test9469/source/test9469.kmn b/web/src/test/manual/web/issue9469/test9469/source/test9469.kmn index 966efcc6f9a..d0f84c749af 100644 --- a/web/src/test/manual/web/issue9469/test9469/source/test9469.kmn +++ b/web/src/test/manual/web/issue9469/test9469/source/test9469.kmn @@ -3,7 +3,7 @@ c with name "test9469" store(&VERSION) '10.0' store(&NAME) 'test9469' store(©RIGHT) '© 2023 SIL International' -store(&KEYBOARDVERSION) '1.0' +store(&KEYBOARDVERSION) '1.1' store(&TARGETS) 'any' store(&BITMAP) 'test9469.ico' store(&VISUALKEYBOARD) 'test9469.kvks' @@ -14,4 +14,7 @@ begin Unicode > use(main) group(main) using keys -c This keyboard is just to visually inspect the touch layout key caps / longpress so none of the T_new_ keys are defined +c This keyboard is just to visually inspect the touch layout key caps / longpress so nearly all of the T_new_ keys are not defined ++ [T_ZWNJ] > U+200C ++ [T_ZWNJIOS] > U+200C ++ [T_ZWNJANDROID] > U+200C diff --git a/web/src/test/manual/web/issue9469/test9469/test9469.kpj b/web/src/test/manual/web/issue9469/test9469/test9469.kpj index a1608952d87..d5530227c2f 100644 --- a/web/src/test/manual/web/issue9469/test9469/test9469.kpj +++ b/web/src/test/manual/web/issue9469/test9469/test9469.kpj @@ -12,7 +12,7 @@ id_9f818ee943306f4c53feb65bb5cadcce test9469.kmn source\test9469.kmn - 1.0 + 1.1 .kmn
test9469 diff --git a/web/src/test/manual/web/regression-tests/README.md b/web/src/test/manual/web/regression-tests/README.md index eebf3274f8b..031f4694891 100644 --- a/web/src/test/manual/web/regression-tests/README.md +++ b/web/src/test/manual/web/regression-tests/README.md @@ -7,8 +7,7 @@ https://github.com/keymanapp/keyboards. Currently, the regression tests run only on Windows because we have a dependency on the `kmanalyze` program which is not yet available outside the keyman source repo. This -is the only Windows dependency (the `kmcomp` compiler is also used, but this runs under -WINE and is available in binary form outside the source repo). +is the only Windows dependency (the `kmc` compiler is also used). The test suite runs in Node.js and launches Chrome for the tests. In the future, the tests may be able to be run in native Node.js. @@ -34,7 +33,7 @@ the keyman repository, for example: - testing - regression-tests -Keyman Engine for Web, Keyman Developer (kmcomp, kmcmpdll, kmanalyze) must be built +Keyman Engine for Web, Keyman Developer (kmc, kmanalyze) must be built prior to running the tests. (If not testing source versions, only kmanalyze is required). See the build documentation for each project for details; summary below: diff --git a/web/src/test/manual/web/regression-tests/test.js b/web/src/test/manual/web/regression-tests/test.js index 227583f9b0a..1205bf7b0e6 100644 --- a/web/src/test/manual/web/regression-tests/test.js +++ b/web/src/test/manual/web/regression-tests/test.js @@ -58,11 +58,10 @@ const bash = process.platform == 'win32' const compilerDestPath = path.join(config.KEYBOARDS_ROOT, 'tools'); const kmcompDestPath = path.join(compilerDestPath, 'kmcomp', 'kmcomp.exe'); -const kmcmpdllDestPath = path.join(compilerDestPath, 'kmcomp', 'kmcmpdll.dll'); let testedCompilerVersions = [], testedEngineVersions = [], firstCompile = true; -// Build keyboards -- get kmcomp.exe, kmcmpdll.dll from appropriate location +// Build keyboards -- get kmcomp.exe from appropriate location process.on('SIGINT', () => { console.log('Received Ctrl+C, aborting'); @@ -119,16 +118,12 @@ if(!program.skipAnalysis) { cleanKeyboards.then(() => { fs.renameSync(kmcompDestPath, kmcompDestPath + '.bak'); - fs.renameSync(kmcmpdllDestPath, kmcmpdllDestPath + '.bak'); process.on('exit', () => { console.log('Restoring original compiler files'); if(fs.existsSync(kmcompDestPath + '.bak')) { fs.renameSync(kmcompDestPath + '.bak', kmcompDestPath); } - if(fs.existsSync(kmcmpdllDestPath + '.bak')) { - fs.renameSync(kmcmpdllDestPath + '.bak', kmcmpdllDestPath); - } }); }).then(() => forEachPromise(program.compilerVersions, version => { // @@ -146,7 +141,6 @@ cleanKeyboards.then(() => { reject(); } fs.copyFileSync(path.join(config.KEYMAN_REPO_BASE_RELATIVE_PATH, 'developer', 'bin', 'kmcomp.exe'), kmcompDestPath); - fs.copyFileSync(path.join(config.KEYMAN_REPO_BASE_RELATIVE_PATH, 'developer', 'bin', 'kmcmpdll.dll'), kmcmpdllDestPath); resolve(); }); break; @@ -162,7 +156,6 @@ cleanKeyboards.then(() => { console.log('Unzipping compiler'); let zip = new AdmZip(response.body); zip.extractEntryTo('kmcomp.exe', compilerDestPath, false, true); - zip.extractEntryTo('kmcmpdll.dll', compilerDestPath, false, true); }); break; default: @@ -173,7 +166,6 @@ cleanKeyboards.then(() => { console.log('Unzipping compiler'); let zip = new AdmZip(response.body); zip.extractEntryTo('kmcomp.exe', compilerDestPath, false, true); - zip.extractEntryTo('kmcmpdll.dll', compilerDestPath, false, true); }); } diff --git a/web/src/tools/build.sh b/web/src/tools/build.sh index 4ac7860bd51..55375f05c21 100755 --- a/web/src/tools/build.sh +++ b/web/src/tools/build.sh @@ -31,42 +31,12 @@ builder_describe "Builds the Keyman Engine for Web's development & unit-testing builder_parse "$@" -### CLEAN ACTIONS +builder_run_child_actions clean -if builder_start_action clean:bulk_rendering; then - testing/bulk_rendering/build.sh clean - - builder_finish_action success clean:bulk_rendering -fi - -if builder_start_action clean:recorder; then - testing/recorder/build.sh clean - - builder_finish_action success clean:recorder +# Some of the web/src/test section uses this script as a dependency. We can skip +# the configure sections in such a case. +if ! builder_is_dep_build && ! builder_is_child_build; then + builder_run_child_actions configure fi -if builder_start_action clean:sourcemap-root; then - building/sourcemap-root/build.sh clean - - builder_finish_action success clean:sourcemap-root -fi - -### BUILD ACTIONS - -if builder_start_action build:bulk_rendering; then - testing/bulk_rendering/build.sh - - builder_finish_action success build:bulk_rendering -fi - -if builder_start_action build:recorder; then - testing/recorder/build.sh - - builder_finish_action success build:recorder -fi - -if builder_start_action build:sourcemap-root; then - building/sourcemap-root/build.sh - - builder_finish_action success build:sourcemap-root -fi \ No newline at end of file +builder_run_child_actions build \ No newline at end of file diff --git a/web/tsconfig.base.json b/web/tsconfig.base.json index a2b78b7b15f..ccb568c867c 100644 --- a/web/tsconfig.base.json +++ b/web/tsconfig.base.json @@ -1,15 +1,24 @@ { - "extends": "../tsconfig-base.json", + "extends": "../tsconfig.base.json", + // TODO: eliminate settings duplicated in ../tsconfig.base.json "compilerOptions": { // Primary settings - the version of ES6 we can target in TS, our downcompile target, // and our module-related settings. "allowSyntheticDefaultImports": true, + "lib": ["es6"], "module": "es6", - "moduleResolution": "Node16", "target": "es5", + // TODO: These override ../tsconfig.base.json settings, and so should be removed if possible, + // but existing code in web/ breaks some of these settinsg + "noImplicitThis": false, + "noImplicitReturns": false, + "noImplicitAny": false, + "strictFunctionTypes": false, + "noUnusedLocals": false, + // Other settings - declaration files, sourcemapping, and other miscellaneous bits. "allowJs": false, "declaration": true, diff --git a/windows/src/Defines.mak b/windows/src/Defines.mak index bf8f77b9987..9506dfa6df0 100644 --- a/windows/src/Defines.mak +++ b/windows/src/Defines.mak @@ -322,3 +322,12 @@ SYMSTORE="C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\symstore.exe" add /compress /f CLEAN=-del /S /Q + +# +# Signature checks +# + +VERIFY_SIGNATURES_PATH=$(KEYMAN_ROOT)\common\windows\delphi\tools\verify_signatures +# Note: hyphen prefix is important to ignore return value from sigcheck +SIGCHECK=-$(VERIFY_SIGNATURES_PATH)\$(WIN32_TARGET_PATH)\sigcheck -q -s -e -v -accepteula +VERIFY_SIGNATURES=$(VERIFY_SIGNATURES_PATH)\$(WIN32_TARGET_PATH)\verify_signatures -d $(KEYMAN_ROOT)\VERSION.md diff --git a/windows/src/Makefile b/windows/src/Makefile index 7dda8852632..e9048fcf71b 100644 --- a/windows/src/Makefile +++ b/windows/src/Makefile @@ -81,7 +81,7 @@ support: global $(MAKE) $(TARGET) cd $(ROOT)\src -test: +test: .virtual cd $(ROOT)\src\test $(MAKE) $(TARGET) cd $(ROOT)\src diff --git a/windows/src/buildtools/Makefile b/windows/src/buildtools/Makefile index d6621a889de..06053170500 100644 --- a/windows/src/buildtools/Makefile +++ b/windows/src/buildtools/Makefile @@ -7,7 +7,7 @@ NOTARGET_SIGNCODE=yes !ifdef NODELPHI TARGETS=.virtual !else -TARGETS=common buildpkg +TARGETS=common !endif CLEANS=clean-buildtools @@ -20,15 +20,10 @@ common: .virtual cd $(KEYMAN_ROOT)\common\windows\delphi\tools $(MAKE) $(TARGET) -buildpkg: .virtual - cd $(ROOT)\src\buildtools\buildpkg - $(MAKE) $(TARGET) - # ---------------------------------------------------------------------- clean-buildtools: - cd $(ROOT)\src\buildtools - -del version.txt + rem no action !include ..\Target.mak diff --git a/windows/src/buildtools/buildpkg/Makefile b/windows/src/buildtools/buildpkg/Makefile deleted file mode 100644 index 056011b0bdd..00000000000 --- a/windows/src/buildtools/buildpkg/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# -# Buildpkg Makefile -# - -!include ..\..\Defines.mak - -build: version.res dirs - $(DELPHI_MSBUILD) buildpkg.dproj "/p:Platform=Win32" - $(SENTRYTOOL_DELPHIPREP) $(WIN32_TARGET_PATH)\buildpkg.exe -dpr buildpkg.dpr - $(TDS2DBG) $(WIN32_TARGET_PATH)\buildpkg.exe - $(COPY) $(WIN32_TARGET_PATH)\Buildpkg.exe $(PROGRAM)\online - $(COPY) $(WIN32_TARGET_PATH)\Buildpkg.exe $(PROGRAM)\buildtools - if exist $(WIN32_TARGET_PATH)\buildpkg.dbg $(COPY) $(WIN32_TARGET_PATH)\buildpkg.dbg $(DEBUGPATH)\buildtools - -clean: def-clean - -signcode: - $(SIGNCODE) /d "Package Installer Creator for Server" $(PROGRAM)\online\Buildpkg.exe - -!include ..\..\Target.mak diff --git a/windows/src/buildtools/buildpkg/buildpkg.dpr b/windows/src/buildtools/buildpkg/buildpkg.dpr deleted file mode 100644 index d2c7d21cfa7..00000000000 --- a/windows/src/buildtools/buildpkg/buildpkg.dpr +++ /dev/null @@ -1,74 +0,0 @@ -program buildpkg; - -{$APPTYPE CONSOLE} - -uses - SysUtils, - buildpkgmain in 'buildpkgmain.pas', - CompilePackageInstaller in '..\..\..\..\developer\src\common\delphi\compiler\CompilePackageInstaller.pas', - utilsystem in '..\..\..\..\common\windows\delphi\general\utilsystem.pas', - CompilePackage in '..\..\..\..\developer\src\common\delphi\compiler\CompilePackage.pas', - kmpinffile in '..\..\..\..\common\windows\delphi\packages\kmpinffile.pas', - kpsfile in '..\..\..\..\developer\src\common\delphi\packages\kpsfile.pas', - PackageInfo in '..\..\..\..\common\windows\delphi\packages\PackageInfo.pas', - utildir in '..\..\..\..\common\windows\delphi\general\utildir.pas', - utilstr in '..\..\..\..\common\windows\delphi\general\utilstr.pas', - utilfiletypes in '..\..\..\..\common\windows\delphi\general\utilfiletypes.pas', - Unicode in '..\..\..\..\common\windows\delphi\general\Unicode.pas', - VersionInfo in '..\..\..\..\common\windows\delphi\general\VersionInfo.pas', - PackageFileFormats in '..\..\..\..\common\windows\delphi\packages\PackageFileFormats.pas', - GetOsVersion in '..\..\..\..\common\windows\delphi\general\GetOsVersion.pas', - RegistryKeys in '..\..\..\..\common\windows\delphi\general\RegistryKeys.pas', - KeymanDeveloperOptions in '..\..\..\..\developer\src\tike\main\KeymanDeveloperOptions.pas', - RedistFiles in '..\..\..\..\developer\src\tike\main\RedistFiles.pas', - DebugPaths in '..\..\..\..\common\windows\delphi\general\DebugPaths.pas', - CustomisationStorage in '..\..\global\delphi\cust\CustomisationStorage.pas', - StockFileNames in '..\..\..\..\common\windows\delphi\general\StockFileNames.pas', - klog in '..\..\..\..\common\windows\delphi\general\klog.pas', - httpuploader in '..\..\..\..\common\windows\delphi\general\httpuploader.pas', - Upload_Settings in '..\..\..\..\common\windows\delphi\general\Upload_Settings.pas', - UfrmTike in '..\..\..\..\developer\src\tike\main\UfrmTike.pas' {TikeForm: TTntForm}, - utilhttp in '..\..\..\..\common\windows\delphi\general\utilhttp.pas', - kmxfile in '..\..\..\..\common\windows\delphi\keyboards\kmxfile.pas', - utilkeyboard in '..\..\..\..\common\windows\delphi\keyboards\utilkeyboard.pas', - KeyNames in '..\..\..\..\common\windows\delphi\general\KeyNames.pas', - wininet5 in '..\..\..\..\common\windows\delphi\general\wininet5.pas', - GlobalProxySettings in '..\..\..\..\common\windows\delphi\general\GlobalProxySettings.pas', - ErrorControlledRegistry in '..\..\..\..\common\windows\delphi\vcl\ErrorControlledRegistry.pas', - utilexecute in '..\..\..\..\common\windows\delphi\general\utilexecute.pas', - KeymanVersion in '..\..\..\..\common\windows\delphi\general\KeymanVersion.pas', - Glossary in '..\..\..\..\common\windows\delphi\general\Glossary.pas', - Keyman.Developer.System.Project.ProjectLog in '..\..\..\..\developer\src\tike\project\Keyman.Developer.System.Project.ProjectLog.pas', - UserMessages in '..\..\..\..\common\windows\delphi\general\UserMessages.pas', - VisualKeyboard in '..\..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboard.pas', - VisualKeyboardLoaderBinary in '..\..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardLoaderBinary.pas', - VisualKeyboardLoaderXML in '..\..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardLoaderXML.pas', - VisualKeyboardSaverBinary in '..\..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardSaverBinary.pas', - VisualKeyboardSaverXML in '..\..\..\..\common\windows\delphi\visualkeyboard\VisualKeyboardSaverXML.pas', - TempFileManager in '..\..\..\..\common\windows\delphi\general\TempFileManager.pas', - ExtShiftState in '..\..\..\..\common\windows\delphi\visualkeyboard\ExtShiftState.pas', - VKeyChars in '..\..\..\..\common\windows\delphi\general\VKeyChars.pas', - VKeys in '..\..\..\..\common\windows\delphi\general\VKeys.pas', - JsonUtil in '..\..\..\..\common\windows\delphi\general\JsonUtil.pas', - Keyman.System.PackageInfoRefreshKeyboards in '..\..\..\..\developer\src\common\delphi\packages\Keyman.System.PackageInfoRefreshKeyboards.pas', - Keyman.System.KMXFileLanguages in '..\..\..\..\developer\src\common\delphi\keyboards\Keyman.System.KMXFileLanguages.pas', - Keyman.System.Standards.ISO6393ToBCP47Registry in '..\..\..\..\common\windows\delphi\standards\Keyman.System.Standards.ISO6393ToBCP47Registry.pas', - Keyman.System.Standards.LCIDToBCP47Registry in '..\..\..\..\common\windows\delphi\standards\Keyman.System.Standards.LCIDToBCP47Registry.pas', - Keyman.System.KeyboardJSInfo in '..\..\..\..\developer\src\common\delphi\keyboards\Keyman.System.KeyboardJSInfo.pas', - Keyman.System.KeyboardUtils in '..\..\..\..\developer\src\common\delphi\keyboards\Keyman.System.KeyboardUtils.pas', - Keyman.System.LanguageCodeUtils in '..\..\..\..\common\windows\delphi\general\Keyman.System.LanguageCodeUtils.pas', - Keyman.System.RegExGroupHelperRSP19902 in '..\..\..\..\common\windows\delphi\vcl\Keyman.System.RegExGroupHelperRSP19902.pas', - kmxfileconsts in '..\..\..\..\common\windows\delphi\keyboards\kmxfileconsts.pas', - BCP47Tag in '..\..\..\..\common\windows\delphi\general\BCP47Tag.pas', - Keyman.System.Standards.BCP47SubtagRegistry in '..\..\..\..\common\windows\delphi\standards\Keyman.System.Standards.BCP47SubtagRegistry.pas', - Keyman.System.Standards.BCP47SuppressScriptRegistry in '..\..\..\..\common\windows\delphi\standards\Keyman.System.Standards.BCP47SuppressScriptRegistry.pas', - Keyman.System.CanonicalLanguageCodeUtils in '..\..\..\..\common\windows\delphi\general\Keyman.System.CanonicalLanguageCodeUtils.pas', - Keyman.System.LexicalModelUtils in '..\..\..\..\developer\src\common\delphi\lexicalmodels\Keyman.System.LexicalModelUtils.pas', - Keyman.System.PackageInfoRefreshLexicalModels in '..\..\..\..\developer\src\common\delphi\packages\Keyman.System.PackageInfoRefreshLexicalModels.pas', - Keyman.System.Standards.LangTagsRegistry in '..\..\..\..\common\windows\delphi\standards\Keyman.System.Standards.LangTagsRegistry.pas', - KeymanPaths in '..\..\..\..\common\windows\delphi\general\KeymanPaths.pas', - Keyman.Developer.System.KeymanDeveloperPaths in '..\..\..\..\developer\src\tike\main\Keyman.Developer.System.KeymanDeveloperPaths.pas'; - -begin - Run; -end. diff --git a/windows/src/buildtools/buildpkg/buildpkg.dproj b/windows/src/buildtools/buildpkg/buildpkg.dproj deleted file mode 100644 index 16dbddf62fb..00000000000 --- a/windows/src/buildtools/buildpkg/buildpkg.dproj +++ /dev/null @@ -1,1053 +0,0 @@ - - - {662A6FEA-4D2E-42E8-9292-1B3EE5A1E480} - buildpkg.dpr - True - Debug - 1 - Console - None - 18.8 - Win32 - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Cfg_2 - true - true - - - buildpkg - $(BDS)\bin\delphi_PROJECTICON.ico - $(BDS)\bin\delphi_PROJECTICNS.icns - true - true - $(DELPHI)\lib;$(DCC_UnitSearchPath) - false - 28 - true - 5 - System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;System.Win;Vcl.Imaging;$(DCC_Namespace) - true - VISUALKEYBOARD_NOCUSTOMBITMAP;VERSION_KEYMAN_REDIST;RELEASE_KEYMAN;EXCMAGIC_GUI;$(DCC_Define) - 1 - 3 - false - false - 3081 - false - true - false - CompanyName=;FileDescription=Tavultesoft Keyboard Manager;FileVersion=5.0.0.28;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= - Vcl40;Vclx40;vcljpg40;comp51;VCLZipD4;dclkmn;Vcl50;Vclx50;vclie50;Inetdb50;Inet50;Vcldb50;$(DCC_UsePackage) - 2C400000 - false - true - None - true - .\obj\$(Platform)\$(Config) - .\bin\$(Platform)\$(Config) - - - Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - 1033 - - - false - 0 - 0 - RELEASE;$(DCC_Define) - - - DEBUG;$(DCC_Define) - false - - - -m c:\temp\xx\keymandesktop90.msi -l c:\temp\xx\license.html -n "GFF Amharic" -s c:\temp\xx\ c:\temp\xx\gff-amh-powerpack-7.kmp - c:\temp\xx\ - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - 1033 - - - - MainSource - - - - - - - - - - - - - - - - - - - - - - - - - -
TikeForm - TTntForm -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - -
- - Delphi.Personality.12 - - - - - buildpkg.dpr - - - $00000C09 - - - False - True - 5 - 0 - 0 - 32 - False - False - False - False - False - 3081 - 1252 - - - - Tavultesoft Keyboard Manager - 5.0.0.32 - - - - - - 1.0.0.0 - - - - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - - - - True - False - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - buildpkg.exe - true - - - - - 1 - - - 0 - - - - - classes - 1 - - - classes - 1 - - - - - res\xml - 1 - - - res\xml - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\armeabi - 1 - - - library\lib\armeabi - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\mips - 1 - - - library\lib\mips - 1 - - - - - library\lib\armeabi-v7a - 1 - - - library\lib\arm64-v8a - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - res\drawable - 1 - - - res\drawable - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - res\values-v21 - 1 - - - res\values-v21 - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - res\drawable - 1 - - - res\drawable - 1 - - - - - res\drawable-xxhdpi - 1 - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-ldpi - 1 - - - res\drawable-ldpi - 1 - - - - - res\drawable-mdpi - 1 - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - res\drawable-xhdpi - 1 - - - - - res\drawable-mdpi - 1 - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - res\drawable-xhdpi - 1 - - - - - res\drawable-xxhdpi - 1 - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-xxxhdpi - 1 - - - res\drawable-xxxhdpi - 1 - - - - - res\drawable-small - 1 - - - res\drawable-small - 1 - - - - - res\drawable-normal - 1 - - - res\drawable-normal - 1 - - - - - res\drawable-large - 1 - - - res\drawable-large - 1 - - - - - res\drawable-xlarge - 1 - - - res\drawable-xlarge - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - 1 - - - 1 - - - 0 - - - - - 1 - .framework - - - 1 - .framework - - - 0 - - - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .dll;.bpl - - - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .bpl - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - - - - - - 1 - - - 1 - - - 1 - - - - - - - - Contents\Resources - 1 - - - Contents\Resources - 1 - - - - - library\lib\armeabi-v7a - 1 - - - library\lib\arm64-v8a - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 0 - - - - - library\lib\armeabi-v7a - 1 - - - - - 1 - - - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - - - - - - - - - - - 12 - - - - -
diff --git a/windows/src/buildtools/buildpkg/buildpkg.res b/windows/src/buildtools/buildpkg/buildpkg.res deleted file mode 100644 index 6876088a664..00000000000 Binary files a/windows/src/buildtools/buildpkg/buildpkg.res and /dev/null differ diff --git a/windows/src/buildtools/buildpkg/buildpkgmain.pas b/windows/src/buildtools/buildpkg/buildpkgmain.pas deleted file mode 100644 index 29882895a62..00000000000 --- a/windows/src/buildtools/buildpkg/buildpkgmain.pas +++ /dev/null @@ -1,214 +0,0 @@ -(* - Name: buildpkgmain - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 23 Aug 2007 - - Modified Date: 22 Jun 2015 - Authors: mcdurdin - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: 23 Aug 2007 - mcdurdin - Initial version - 17 Sep 2007 - mcdurdin - Support output path parameter - 30 Dec 2010 - mcdurdin - I2562 - EULA as part of setup bootstrapper - 04 May 2012 - mcdurdin - I3306 - V9.0 - Remove TntControls - 23 Feb 2015 - mcdurdin - I4598 - V9.0 - Keyman installer does not show EULA when bundled with a keyboard - 04 May 2015 - mcdurdin - I4694 - V9.0 - Split UI actions from non-UI actions in projects - 11 May 2015 - mcdurdin - I4706 - V9.0 - Update compile logging for silent and warning-as-error cleanness - 22 Jun 2015 - mcdurdin - I4763 - Package compiler (buildpkg) needs to specify username and password on command line - 22 Jun 2015 - mcdurdin - I4764 - Buildpkg needs to support different paths for msi and kmp files - -*) -unit buildpkgmain; // I3306 - -interface - -procedure Run; - -implementation - -uses - Winapi.Windows, - - CompilePackageInstaller, - kpsfile, - Keyman.Developer.System.Project.ProjectLog, - SysUtils; - -type - TParams = class - private - TooManyParams: Boolean; - public - OutputPath, RedistPath, MSIFileName, KMPFileName, KMPName, AppName, LicenseFileName, TitleImageFileName: WideString; // I2562 // I4763 - StartDisabled, StartWithConfiguration: Boolean; - constructor Create; - function Validate: Boolean; - function Completed: Boolean; - - procedure CompilerMessage(Sender: TObject; msg: string; State: TProjectLogState); // I4706 - end; - -procedure Run; -var - Params: TParams; - pack: TKPSFile; -begin - Params := TParams.Create; - - if not Params.Completed then - begin - writeln('buildpkg.exe 1.0'); - writeln('Usage: '+ExtractFileName(ParamStr(0))+' -m [-l ] [-i ] [-a ] (-s )|(-n )'); // I2562 // I4763 - writeln('If setup-redist.exe is in setup folder location, it will be used in preference to setup.exe'); - ExitCode := 3; - Exit; - end; - - if not Params.Validate then - begin - writeln('buildpkg.exe 1.0'); - ExitCode := 4; - Exit; - end; - - if Params.KMPFileName <> '' then - begin - pack := TKPSFile.Create; - pack.KPSOptions.MSIFileName := Params.MSIFileName; - pack.FileName := ChangeFileExt(Params.KMPFileName, '.kps'); - pack.Info.Desc['Name'] := Params.KMPName; - - try - if not DoCompilePackageInstaller(pack, Params.CompilerMessage, False, Params.MSIFileName, - ChangeFileExt(Params.MSIFileName,'')+'-'+ChangeFileExt(ExtractFileName(Params.KMPFileName),'')+'.exe', Params.RedistPath, - False, False, Params.LicenseFileName, Params.TitleImageFileName, Params.AppName, - Params.StartDisabled, Params.StartWithConfiguration) then // I4598 // I4694 // I4764 - ExitCode := 1 - else - ExitCode := 0; - except - on E:Exception do - begin - writeln(E.Message); - ExitCode := 2; - end; - end; - end - else - begin - try - - if Params.OutputPath = '' then - Params.OutputPath := ChangeFileExt(Params.MSIFileName, '.exe'); - - if not DoCompileMSIInstaller(Params.CompilerMessage, False, Params.MSIFileName, Params.OutputPath, Params.RedistPath, - Params.LicenseFileName, Params.TitleImageFileName, Params.AppName, - Params.StartDisabled, Params.StartWithConfiguration) then // I2562 - ExitCode := 1 - else - ExitCode := 0; - except - on E:Exception do - begin - writeln(E.Message); - ExitCode := 2; - end; - end; - end; -end; - -{ TParams } - -procedure TParams.CompilerMessage(Sender: TObject; msg: string; - State: TProjectLogState); // I4706 -begin - if State in [plsError, plsFatal] - then raise Exception.Create(msg) - else writeln(msg); -end; - -function TParams.Completed: Boolean; -begin - Result := - not TooManyParams and - (MSIFileName <> '') and - ( - ((KMPFileName = '') and (RedistPath <> '')) or - ((KMPFileName <> '') and (KMPName <> '')) - ); -end; - -constructor TParams.Create; -var - i: Integer; - Flag: WideString; -begin - { Takes a .kmp file and turns it into a .msi installer } - i := 1; - while i <= ParamCount do - begin - Flag := ParamStr(i); Inc(i); - if Flag = '-m' then - MSIFileName := ParamStr(i) - else if Flag = '-n' then - KMPName := ParamStr(i) - else if Flag = '-s' then - RedistPath := IncludeTrailingPathDelimiter(ParamStr(i)) - else if Flag = '-o' then - OutputPath := ParamStr(i) - else if Flag = '-l' then // I2562 - LicenseFileName := ParamStr(i) - else if Flag = '-i' then - TitleImageFileName := ParamStr(i) - else if Flag = '-a' then - AppName := ParamStr(i) - else if SameText(Flag, '-startDisabled') then - begin - Dec(i); StartDisabled := True; - end - else if SameText(Flag, '-startWithConfiguration') then - begin - Dec(i); StartWithConfiguration := True; - end - else - begin - KMPFileName := Flag; - Break; - end; - Inc(i); - end; - TooManyParams := i <= ParamCount; -end; - -function TParams.Validate: Boolean; -begin - Result := True; - if not FileExists(MSIFileName) then - begin - Result := False; - writeln('File '+MSIFileName+' does not exist.'); - end; - if (KMPFileName <> '') and not FileExists(KMPFileName) then - begin - Result := False; - writeln('File '+KMPFileName+' does not exist.'); - end; - if (LicenseFileName <> '') and not FileExists(LicenseFileName) then // I2562 - begin - Result := False; - writeln('License File '+LicenseFileName+' does not exist.'); - end; - if (RedistPath <> '') and (not FileExists(RedistPath + 'setup.exe') or not FileExists(RedistPath + 'setup-redist.exe')) then - begin - Result := False; - writeln('File '+RedistPath+'setup.exe and '+RedistPath+'setup-redist.exe do not exist.'); - end; -end; - -end. diff --git a/windows/src/buildtools/buildpkg/version.rc b/windows/src/buildtools/buildpkg/version.rc deleted file mode 100644 index 134c4bf8c3c..00000000000 --- a/windows/src/buildtools/buildpkg/version.rc +++ /dev/null @@ -1,32 +0,0 @@ -#include "../../../../common/windows/cpp/include/keymanversion.h" - -1 VERSIONINFO - FILEVERSION KV_FILEVERSION - PRODUCTVERSION KV_PRODUCTVERSION - FILEFLAGSMASK 0x3fL - FILEFLAGS 0x0L - FILEOS 0x4L - FILETYPE 0x1L - FILESUBTYPE 0x0L - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0C0904E4" - BEGIN - VALUE "CompanyName", KV_COMPANY_NAME - VALUE "FileDescription", "Buildpkg\0" - VALUE "FileVersion", KV_VERSION_STRING - VALUE "InternalName", "BUILDPKG\0" - VALUE "LegalCopyright", KV_LEGAL_COPYRIGHT - VALUE "LegalTrademarks", KV_LEGAL_TRADEMARKS - VALUE "OriginalFilename", "BUILDPKG.EXE\0" - VALUE "ProductName", "Keyman Build Environment\0" - VALUE "ProductVersion", KV_VERSION_STRING - VALUE "Comments", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1252 - END - END diff --git a/windows/src/desktop/inst/Makefile b/windows/src/desktop/inst/Makefile index 50866447816..68bb12a7352 100644 --- a/windows/src/desktop/inst/Makefile +++ b/windows/src/desktop/inst/Makefile @@ -28,11 +28,19 @@ prereq-resources: $(SIGNCODE) /d "Keyman Resources" desktop_resources.dll desktop: prereq + rem compile .msi $(MAKE) -fdownload.mak candle $(WIXLIGHT) -dWixUILicenseRtf=License.rtf -out keymandesktop.msi -ext WixUIExtension $(DESKTOP_FILES) $(SIGNCODE) /d "Keyman" keymandesktop.msi -# TODO: replace buildpkg with kmcomp and remove buildpkg from repository - $(ROOT)\bin\buildtools\buildpkg -m keymandesktop.msi -s $(ROOT)\bin\desktop -l license.html + + rem build self-extracting archive + $(MAKE) -fdownload.mak setup-inf + $(WZZIP) keymandesktop.zip keymandesktop.msi license.html setup.inf + -del setup.inf + $(COPY) /b $(ROOT)\bin\desktop\setup-redist.exe + keymandesktop.zip keymandesktop.exe + -del keymandesktop.zip + + rem sign and copy files $(SIGNCODE) /d "Keyman" keymandesktop.exe $(MAKE) -fdownload.mak copyredist-desktop @@ -51,6 +59,8 @@ clean: -del /Q desktopui.wxs -del /Q cef.wxs -del /Q locale.wxs + -del keymandesktop.zip + -del setup.inf check: if not exist $(ROOT)\src\engine\inst\keymanengine.msm $(MAKE) check-engine diff --git a/windows/src/desktop/inst/download.in b/windows/src/desktop/inst/download.in index 0bf062eeba4..aab048d8508 100644 --- a/windows/src/desktop/inst/download.in +++ b/windows/src/desktop/inst/download.in @@ -17,6 +17,10 @@ copyredist-desktop: copy /Y keymandesktop.exe $(ROOT)\release\$Version\keyman-$Version.exe copy /Y $(ROOT)\bin\desktop\setup.exe $(ROOT)\release\$Version\setup.exe + $(SIGCHECK) $(ROOT)\release\$Version\* > sig1 + $(VERIFY_SIGNATURES) < sig1 + -del sig1 + # Copy the unsigned setup.exe for use in bundling scenarios; zip it up for clarity $(WZZIP) $(ROOT)\release\$Version\setup-redist.zip $(ROOT)\bin\desktop\setup-redist.exe @@ -63,3 +67,15 @@ candle-locale: # locale files are in desktop/locale/* $(WIXHEAT) dir ..\kmshell\locale -o locale.wxs -ag -cg Locale -dr INSTALLDIR -var var.LOCALESOURCE -wx -nologo $(WIXCANDLE) -dVERSION=$VersionWin -dRELEASE=$VersionRelease -dPRODUCTID=$GUID1 -dLOCALESOURCE=..\kmshell\locale locale.wxs + +# +# Build setup.inf +# + +setup-inf: + echo [Setup] > setup.inf + echo Version=$VersionWin >> setup.inf + echo MSIFileName=keymandesktop.msi >> setup.inf + echo MSIOptions= >> setup.inf + echo License=license.html >> setup.inf + echo [Packages] >> setup.inf diff --git a/windows/src/desktop/insthelp/Makefile b/windows/src/desktop/insthelp/Makefile index b3d4dcdf070..b1ca957636e 100644 --- a/windows/src/desktop/insthelp/Makefile +++ b/windows/src/desktop/insthelp/Makefile @@ -11,9 +11,9 @@ build: version.res dirs $(COPY) $(WIN32_TARGET_PATH)\insthelp.exe $(ROOT)\bin\desktop\insthelp.exe test-manifest: - # test that (a) linked manifest exists and correct, and (b) has uiAccess=true +# test that (a) linked manifest exists and correct, and (b) has uiAccess=true @rem $(MT) -nologo -inputresource:$(PROGRAM)\desktop\insthelp.exe -validate_manifest - # TODO: Investigate why no manifest included? +# TODO: Investigate why no manifest included? clean: def-clean diff --git a/windows/src/desktop/kmconfig/Makefile b/windows/src/desktop/kmconfig/Makefile index d7f308f9d65..98028758335 100644 --- a/windows/src/desktop/kmconfig/Makefile +++ b/windows/src/desktop/kmconfig/Makefile @@ -21,7 +21,7 @@ wrap-symbols: $(SYMSTORE) $(DEBUGPATH)\desktop\kmconfig.dbg /t keyman-windows test-manifest: - # test that linked manifest exists and correct +# test that linked manifest exists and correct $(MT) -nologo -inputresource:$(PROGRAM)\desktop\kmconfig.exe -validate_manifest install: diff --git a/windows/src/desktop/kmshell/Makefile b/windows/src/desktop/kmshell/Makefile index caaa7da54e0..773942d0a1b 100644 --- a/windows/src/desktop/kmshell/Makefile +++ b/windows/src/desktop/kmshell/Makefile @@ -39,7 +39,7 @@ wrap-symbols: $(SYMSTORE) $(DEBUGPATH)\desktop\kmshell.dbg /t keyman-windows test-manifest: - # test that (a) linked manifest exists and correct, and (b) has uiAccess=true +# test that (a) linked manifest exists and correct, and (b) has uiAccess=true $(MT) -nologo -inputresource:$(PROGRAM)\desktop\kmshell.exe -validate_manifest install: .virtual diff --git a/windows/src/desktop/setup/locale/kn/strings.xml b/windows/src/desktop/setup/locale/kn/strings.xml index c6b39409a27..f2e84438277 100644 --- a/windows/src/desktop/setup/locale/kn/strings.xml +++ b/windows/src/desktop/setup/locale/kn/strings.xml @@ -72,7 +72,7 @@ %0:s ಡೌನ್ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ %0:s ಡೌನ್ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ - $APPNAME Setup could not connect to keyman.com to download additional resources. - Please check that you are online, and give $APPNAME Setup permission to access the Internet in your firewall settings. - Click Abort to exit Setup, Retry to try and download resources again, or Ignore to continue offline. + $APPNAME ಸೆಟ್‌ಅಪ್ ಗೆ ಹೆಚ್ಚುವರಿ ಸಂಪನ್ಮೂಲಗಳನ್ನು ಡೌನ್ಲೋಡ್ ಮಾಡಲು keyman.com ಅನ್ನು ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. + ದಯವಿಟ್ಟು ನೀವು ಆನ್‌ಲೈನ್‌ನಲ್ಲಿರುವಿರಿ ಎಂಬುದನ್ನು ಪರಿಶೀಲಿಸಿ ಮತ್ತು ನಿಮ್ಮ ಫೈರ್‌ವಾಲ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಇಂಟರ್ನೆಟ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು $APPNAME ಸೆಟ್‌ಅಪ್ ಗೆ ಅನುಮತಿಯನ್ನು ನೀಡಿ. + ಸೆಟ್‌ಅಪ್‌ನಿಂದ ನಿರ್ಗಮಿಸಲು \"ಸ್ಥಗಿತಗೊಳಿಸು\" ಕ್ಲಿಕ್ ಮಾಡಿ, ಸಂಪನ್ಮೂಲಗಳನ್ನು ಪುನಃ ಪ್ರಯತ್ನಿಸಲು ಮತ್ತು ಡೌನ್ಲೋಡ್ ಮಾಡಲು ಮರುಪ್ರಯತ್ನಿಸಿ ಅಥವಾ ಇದನ್ನು ನಿರ್ಲಕ್ಷಿಸಿ ಆಫ್‌ಲೈನ್‌ನಲ್ಲಿ ಮುಂದುವರಿಸಿ. diff --git a/windows/src/engine/keyman32-ver/CoreEnvironment.cpp b/windows/src/engine/keyman32-ver/CoreEnvironment.cpp index cd0cfc13573..108de26bc09 100644 --- a/windows/src/engine/keyman32-ver/CoreEnvironment.cpp +++ b/windows/src/engine/keyman32-ver/CoreEnvironment.cpp @@ -2,36 +2,36 @@ #define WINDOWS_PLATFORM_ENV u"windows hardware desktop native" -BOOL SetupCoreEnvironment(km_kbp_option_item **core_environment) { - km_kbp_option_item *items = new km_kbp_option_item[6]; +BOOL SetupCoreEnvironment(km_core_option_item **core_environment) { + km_core_option_item *items = new km_core_option_item[6]; - items[0].scope = KM_KBP_OPT_ENVIRONMENT; - items[0].key = KM_KBP_KMX_ENV_BASELAYOUT; - items[0].value = reinterpret_cast(Globals::get_BaseKeyboardName()); + items[0].scope = KM_CORE_OPT_ENVIRONMENT; + items[0].key = KM_CORE_KMX_ENV_BASELAYOUT; + items[0].value = reinterpret_cast(Globals::get_BaseKeyboardName()); - items[1].scope = KM_KBP_OPT_ENVIRONMENT; - items[1].key = KM_KBP_KMX_ENV_BASELAYOUTALT; - items[1].value = reinterpret_cast(Globals::get_BaseKeyboardNameAlt()); + items[1].scope = KM_CORE_OPT_ENVIRONMENT; + items[1].key = KM_CORE_KMX_ENV_BASELAYOUTALT; + items[1].value = reinterpret_cast(Globals::get_BaseKeyboardNameAlt()); - items[2].scope = KM_KBP_OPT_ENVIRONMENT; - items[2].key = KM_KBP_KMX_ENV_SIMULATEALTGR; + items[2].scope = KM_CORE_OPT_ENVIRONMENT; + items[2].key = KM_CORE_KMX_ENV_SIMULATEALTGR; items[2].value = Globals::get_SimulateAltGr() ? u"1" : u"0"; - items[3].scope = KM_KBP_OPT_ENVIRONMENT; - items[3].key = KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT; + items[3].scope = KM_CORE_OPT_ENVIRONMENT; + items[3].key = KM_CORE_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT; items[3].value = KeyboardGivesCtrlRAltForRAlt() ? u"1" : u"0"; - items[4].scope = KM_KBP_OPT_ENVIRONMENT; - items[4].key = KM_KBP_KMX_ENV_PLATFORM; + items[4].scope = KM_CORE_OPT_ENVIRONMENT; + items[4].key = KM_CORE_KMX_ENV_PLATFORM; items[4].value = WINDOWS_PLATFORM_ENV; - items[5] = KM_KBP_OPTIONS_END; + items[5] = KM_CORE_OPTIONS_END; *core_environment = items; return TRUE; } -void DeleteCoreEnvironment(km_kbp_option_item *core_environment) { +void DeleteCoreEnvironment(km_core_option_item *core_environment) { // All keys and values are pointers to owned memory, so only need to delete // the array of options delete [] core_environment; diff --git a/windows/src/engine/keyman32-ver/K32_load.cpp b/windows/src/engine/keyman32-ver/K32_load.cpp index e29e504b2a1..999d8941c9c 100644 --- a/windows/src/engine/keyman32-ver/K32_load.cpp +++ b/windows/src/engine/keyman32-ver/K32_load.cpp @@ -79,44 +79,44 @@ BOOL LoadlpKeyboard(int i) if (_td->lpActiveKeyboard == &_td->lpKeyboards[i]) _td->lpActiveKeyboard = NULL; // I822 TSF not working if (_td->lpKeyboards[i].lpCoreKeyboardState) { - 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); + SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: a keyboard km_core_state exits without matching keyboard - disposing of state"); + km_core_state_dispose(_td->lpKeyboards[i].lpCoreKeyboardState); _td->lpKeyboards[i].lpCoreKeyboardState = NULL; } char buf[256]; if (!GetKeyboardFileName(_td->lpKeyboards[i].Name, buf, 255)) return FALSE; 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, "LoadlpKeyboard: km_kbp_keyboard_load failed for %ls with error status [%d]", keyboardPath, err_status); + km_core_status err_status = km_core_keyboard_load(keyboardPath, &_td->lpKeyboards[i].lpCoreKeyboard); + if (err_status != KM_CORE_STATUS_OK) { + SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: km_core_keyboard_load failed for %ls with error status [%d]", keyboardPath, err_status); delete keyboardPath; return FALSE; } delete keyboardPath; - km_kbp_option_item *core_environment = nullptr; + km_core_option_item *core_environment = nullptr; if(!SetupCoreEnvironment(&core_environment)) { SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: Unable to set environment options for keyboard %ls", keyboardPath); return FALSE; } - err_status = km_kbp_state_create(_td->lpKeyboards[i].lpCoreKeyboard, core_environment, &_td->lpKeyboards[i].lpCoreKeyboardState); + err_status = km_core_state_create(_td->lpKeyboards[i].lpCoreKeyboard, core_environment, &_td->lpKeyboards[i].lpCoreKeyboardState); DeleteCoreEnvironment(core_environment); - if (err_status != KM_KBP_STATUS_OK) { + if (err_status != KM_CORE_STATUS_OK) { SendDebugMessageFormat( - 0, sdmLoad, 0, "LoadlpKeyboard: km_kbp_state_create failed with error status [%d]", err_status); + 0, sdmLoad, 0, "LoadlpKeyboard: km_core_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; } // 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, "LoadlpKeyboard: km_kbp_keyboard_get_imx_list failed with error status [%d]", err_status); + err_status = km_core_keyboard_get_imx_list(_td->lpKeyboards[i].lpCoreKeyboard, &_td->lpKeyboards[i].lpIMXList); + if (err_status != KM_CORE_STATUS_OK) { + SendDebugMessageFormat(0, sdmLoad, 0, "LoadlpKeyboard: km_core_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; diff --git a/windows/src/engine/keyman32-ver/appint/aiTIP.cpp b/windows/src/engine/keyman32-ver/appint/aiTIP.cpp index 34f3afc867f..b6f591845b5 100644 --- a/windows/src/engine/keyman32-ver/appint/aiTIP.cpp +++ b/windows/src/engine/keyman32-ver/appint/aiTIP.cpp @@ -160,7 +160,7 @@ extern "C" __declspec(dllexport) BOOL WINAPI TIPProcessKey(WPARAM wParam, LPARAM DWORD LocalShiftState = Globals::get_ShiftState(); // Only the modifer flag 'f_ShiftState' is changed before sending the key stroke to the // 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 + // queue an action 'KM_CORE_IT_CAPSLOCK'. In processing the action the Windows engine will synthesise keystrokes // to ensure caps lock is in the correct state. if (!Preserved) { switch (wParam) { diff --git a/windows/src/engine/keyman32-ver/appint/appint.cpp b/windows/src/engine/keyman32-ver/appint/appint.cpp index d23c962d941..8a2b92e5aa3 100644 --- a/windows/src/engine/keyman32-ver/appint/appint.cpp +++ b/windows/src/engine/keyman32-ver/appint/appint.cpp @@ -227,11 +227,11 @@ AppIntegration::AppIntegration() FShiftFlags = 0; } -BOOL ContextItemsFromAppContext(WCHAR const* buf, km_kbp_context_item** outPtr) +BOOL ContextItemsFromAppContext(WCHAR const* buf, km_core_context_item** outPtr) { assert(buf); assert(outPtr); - km_kbp_context_item* context_items = new km_kbp_context_item[wcslen(buf) + 1]; + km_core_context_item* context_items = new km_core_context_item[wcslen(buf) + 1]; WCHAR const *p = buf; uint8_t contextIndex = 0; while (*p) { @@ -240,18 +240,18 @@ BOOL ContextItemsFromAppContext(WCHAR const* buf, km_kbp_context_item** outPtr) // we know the only uc_sentinel code in the context is code_deadkey, which has only 1 parameter: uc_sentinel code_deadkey // setup dead key context item p += 2; - context_items[contextIndex++] = km_kbp_context_item{ KM_KBP_CT_MARKER, {0,}, {*p} }; + context_items[contextIndex++] = km_core_context_item{ KM_CORE_CT_MARKER, {0,}, {*p} }; } else if (Uni_IsSurrogate1(*p) && Uni_IsSurrogate2(*(p + 1))) { // handle surrogate - context_items[contextIndex++] = km_kbp_context_item{ KM_KBP_CT_CHAR, {0,}, {(char32_t)Uni_SurrogateToUTF32(*p, *(p + 1))} }; + context_items[contextIndex++] = km_core_context_item{ KM_CORE_CT_CHAR, {0,}, {(char32_t)Uni_SurrogateToUTF32(*p, *(p + 1))} }; p++; } else { - context_items[contextIndex++] = km_kbp_context_item{ KM_KBP_CT_CHAR, {0,}, {*p} }; + context_items[contextIndex++] = km_core_context_item{ KM_CORE_CT_CHAR, {0,}, {*p} }; } p++; } // terminate the context_items array. - context_items[contextIndex] = km_kbp_context_item KM_KBP_CONTEXT_ITEM_END; + context_items[contextIndex] = km_core_context_item KM_CORE_CONTEXT_ITEM_END; *outPtr = context_items; return true; @@ -259,34 +259,34 @@ BOOL ContextItemsFromAppContext(WCHAR const* buf, km_kbp_context_item** outPtr) BOOL -ContextItemToAppContext(km_kbp_context_item *contextItems, PWSTR outBuf, DWORD len) { +ContextItemToAppContext(km_core_context_item *contextItems, PWSTR outBuf, DWORD len) { assert(contextItems); assert(outBuf); - km_kbp_context_item *km_kbp_context_it = contextItems; + km_core_context_item *km_core_context_it = contextItems; uint8_t contextLen = 0; - for (; km_kbp_context_it->type != KM_KBP_CT_END; ++km_kbp_context_it) { + for (; km_core_context_it->type != KM_CORE_CT_END; ++km_core_context_it) { ++contextLen; } WCHAR *buf = new WCHAR[(contextLen*3)+ 1 ]; // *3 if every context item was a deadkey uint8_t idx = 0; - km_kbp_context_it = contextItems; - for (; km_kbp_context_it->type != KM_KBP_CT_END; ++km_kbp_context_it) { - switch (km_kbp_context_it->type) { - case KM_KBP_CT_CHAR: - if (Uni_IsSMP(km_kbp_context_it->character)) { - buf[idx++] = static_cast Uni_UTF32ToSurrogate1(km_kbp_context_it->character); - buf[idx++] = static_cast Uni_UTF32ToSurrogate2(km_kbp_context_it->character); + km_core_context_it = contextItems; + for (; km_core_context_it->type != KM_CORE_CT_END; ++km_core_context_it) { + switch (km_core_context_it->type) { + case KM_CORE_CT_CHAR: + if (Uni_IsSMP(km_core_context_it->character)) { + buf[idx++] = static_cast Uni_UTF32ToSurrogate1(km_core_context_it->character); + buf[idx++] = static_cast Uni_UTF32ToSurrogate2(km_core_context_it->character); } else { - buf[idx++] = (km_kbp_cp)km_kbp_context_it->character; + buf[idx++] = (km_core_cp)km_core_context_it->character; } break; - case KM_KBP_CT_MARKER: - assert(km_kbp_context_it->marker > 0); + case KM_CORE_CT_MARKER: + assert(km_core_context_it->marker > 0); buf[idx++] = UC_SENTINEL; buf[idx++] = CODE_DEADKEY; - buf[idx++] = static_cast(km_kbp_context_it->marker); + buf[idx++] = static_cast(km_core_context_it->marker); break; } } diff --git a/windows/src/engine/keyman32-ver/appint/appint.h b/windows/src/engine/keyman32-ver/appint/appint.h index 6fe7ca7e201..8048c4a8a94 100644 --- a/windows/src/engine/keyman32-ver/appint/appint.h +++ b/windows/src/engine/keyman32-ver/appint/appint.h @@ -201,21 +201,21 @@ class AppIntegration:public AppActionQueue * @param outPtr The ouput array of context items. caller to free memory * @return BOOL True if array created successfully */ -BOOL ContextItemsFromAppContext(WCHAR const* buf, km_kbp_context_item** outPtr); +BOOL ContextItemsFromAppContext(WCHAR const* buf, km_core_context_item** outPtr); /** - * Convert km_kbp_context_item array into an kmx char buffer. + * Convert km_core_context_item array into an kmx char buffer. * Caller is responsible for freeing the memory. * The length is restricted to a maximum of MAXCONTEXT length. If the number - * of input km_kbp_context_items exceeds this length the characters furthest + * of input km_core_context_items exceeds this length the characters furthest * from the caret will be truncated. * - * @param contextItems the input core context array. (km_kbp_context_item) + * @param contextItems the input core context array. (km_core_context_item) * @param [out] outBuf the kmx character array output. caller to free memory. * * @return BOOL True if array created successfully */ -BOOL ContextItemToAppContext(km_kbp_context_item *contextItems, PWSTR outBuf, DWORD len); +BOOL ContextItemToAppContext(km_core_context_item *contextItems, PWSTR outBuf, DWORD len); extern const LPSTR ItemTypes[]; diff --git a/windows/src/engine/keyman32-ver/calldll.cpp b/windows/src/engine/keyman32-ver/calldll.cpp index 5ed6182b6df..f9be5ce3172 100644 --- a/windows/src/engine/keyman32-ver/calldll.cpp +++ b/windows/src/engine/keyman32-ver/calldll.cpp @@ -99,33 +99,33 @@ static LPIMDLL AddIMDLL(LPINTKEYBOARDINFO lpkbi, LPSTR kbdpath, LPSTR dllfilenam return imd; } -static km_kbp_action_item* +static km_core_action_item* kmnToCoreActionItem(int ItemType, DWORD dwData, WORD wVkey) { - km_kbp_action_item *actionItems = new km_kbp_action_item[2]; - actionItems[0].type = KM_KBP_IT_END; - actionItems[1].type = KM_KBP_IT_END; + km_core_action_item *actionItems = new km_core_action_item[2]; + actionItems[0].type = KM_CORE_IT_END; + actionItems[1].type = KM_CORE_IT_END; switch (ItemType) { case QIT_CHAR: - actionItems[0].type = KM_KBP_IT_CHAR; + actionItems[0].type = KM_CORE_IT_CHAR; actionItems[0].character = dwData; break; case QIT_DEADKEY: - actionItems[0].type = KM_KBP_IT_MARKER; + actionItems[0].type = KM_CORE_IT_MARKER; actionItems[0].marker = dwData; break; case QIT_BELL: - actionItems[0].type = KM_KBP_IT_ALERT; + actionItems[0].type = KM_CORE_IT_ALERT; break; case QIT_BACK: switch (dwData) { case BK_DEFAULT: - actionItems[0].type = KM_KBP_IT_BACK; - actionItems[0].backspace.expected_type = KM_KBP_BT_CHAR; + actionItems[0].type = KM_CORE_IT_BACK; + actionItems[0].backspace.expected_type = KM_CORE_BT_CHAR; break; case BK_DEADKEY: - actionItems[0].type = KM_KBP_IT_BACK; - actionItems[0].backspace.expected_type = KM_KBP_BT_MARKER; + actionItems[0].type = KM_CORE_IT_BACK; + actionItems[0].backspace.expected_type = KM_CORE_BT_MARKER; break; } break; @@ -133,7 +133,7 @@ kmnToCoreActionItem(int ItemType, DWORD dwData, WORD wVkey) { case QIT_VKEYUP: if (dwData == wVkey) { SendDebugMessageFormat(0, sdmKeyboard, 0, "kmnToCoreActionItem: Emit Action:[%s] Key:[%x] ", ItemTypes[ItemType], dwData); - actionItems[0].type = KM_KBP_IT_EMIT_KEYSTROKE; + actionItems[0].type = KM_CORE_IT_EMIT_KEYSTROKE; actionItems[0].character = dwData; } else{ SendDebugMessageFormat( @@ -144,7 +144,7 @@ kmnToCoreActionItem(int ItemType, DWORD dwData, WORD wVkey) { SendDebugMessageFormat(0, sdmKeyboard, 0, "kmnToCoreActionItem: Unhandled Action: [%s] [%x] ", ItemTypes[ItemType], dwData); break; ; case QIT_INVALIDATECONTEXT: - actionItems[0].type = KM_KBP_IT_INVALIDATE_CONTEXT; + actionItems[0].type = KM_CORE_IT_INVALIDATE_CONTEXT; break; } @@ -156,40 +156,40 @@ kmnToCoreActionItem(int ItemType, DWORD dwData, WORD wVkey) { #define CONTEXT_CORE 0 #define CONTEXT_INT 1 static BOOL -LogContext(km_kbp_state *lpCoreKeyboardState, uint8_t context_type) { +LogContext(km_core_state *lpCoreKeyboardState, uint8_t context_type) { if (!lpCoreKeyboardState) { return FALSE; } - km_kbp_context_item *citems = nullptr; - km_kbp_status error_status; + km_core_context_item *citems = nullptr; + km_core_status error_status; char *log_str_title = nullptr; char* const int_context = "Intermediate Context"; char* const core_context = "Core Context"; switch(context_type){ case CONTEXT_CORE: - error_status = (km_kbp_status_codes)km_kbp_context_get(km_kbp_state_context(lpCoreKeyboardState), &citems); + error_status = (km_core_status_codes)km_core_context_get(km_core_state_context(lpCoreKeyboardState), &citems); log_str_title = int_context; break; case CONTEXT_INT: - error_status = (km_kbp_status_codes)kbp_state_get_intermediate_context(lpCoreKeyboardState, &citems); + error_status = (km_core_status_codes)kbp_state_get_intermediate_context(lpCoreKeyboardState, &citems); log_str_title = core_context; break; default: - error_status = KM_KBP_STATUS_INVALID_ARGUMENT; + error_status = KM_CORE_STATUS_INVALID_ARGUMENT; } - if (error_status != KM_KBP_STATUS_OK){ - km_kbp_context_items_dispose(citems); + if (error_status != KM_CORE_STATUS_OK){ + km_core_context_items_dispose(citems); return FALSE; } - DWORD context_length = (DWORD)km_kbp_context_item_list_size(citems); + DWORD context_length = (DWORD)km_core_context_item_list_size(citems); WCHAR *buf = new WCHAR[(context_length * 3) +1 ]; // *3 if every context item was a deadkey if (!ContextItemToAppContext(citems, buf, context_length)) { - km_kbp_context_items_dispose(citems); + km_core_context_items_dispose(citems); delete[] buf; return FALSE; } - km_kbp_context_items_dispose(citems); + km_core_context_items_dispose(citems); SendDebugMessageFormat(0, sdmKeyboard, 0, "%s: [%s]", log_str_title, Debug_UnicodeString(buf)); delete[] buf; return TRUE; @@ -232,7 +232,7 @@ BOOL UnloadDLLs(LPINTKEYBOARDINFO lpkbi) lpkbi->nIMDLLs = 0; if (lpkbi->lpCoreKeyboardState) { - km_kbp_state_imx_deregister_callback(lpkbi->lpCoreKeyboardState); + km_core_state_imx_deregister_callback(lpkbi->lpCoreKeyboardState); PKEYMAN64THREADDATA _td = ThreadGlobals(); if (!_td) { return FALSE; @@ -241,6 +241,7 @@ BOOL UnloadDLLs(LPINTKEYBOARDINFO lpkbi) FreeLibrary(_td->hModuleProxy); _td->hModuleProxy = NULL; } + } return TRUE; } @@ -268,7 +269,7 @@ BOOL DeactivateDLLs(LPINTKEYBOARDINFO lpkbi) } // The callback function called by the Core Keyboardprocessor -extern "C" uint8_t IM_CallBackCore(km_kbp_state *km_state, uint32_t UniqueStoreNo, void *callbackObject) { +extern "C" uint8_t IM_CallBackCore(km_core_state *km_state, uint32_t UniqueStoreNo, void *callbackObject) { //SendDebugMessageFormat(0, sdmKeyboard, 0, "IM_CallBackCore: Enter"); if (callbackObject == NULL) { return FALSE; @@ -326,32 +327,32 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMSetOutput(PWSTR buf, DWORD backlen } DWORD numActions = backlen + (DWORD)wcslen(buf); DWORD idx = 0; - km_kbp_action_item *actionItems = new km_kbp_action_item[numActions + 1]; + km_core_action_item *actionItems = new km_core_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)) { + km_core_context_item *citems = nullptr; + if (KM_CORE_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); + DWORD context_length = (DWORD)km_core_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); + km_core_context_items_dispose(citems); delete[] contextString; delete[] actionItems; return FALSE; } - km_kbp_context_items_dispose(citems); + km_core_context_items_dispose(citems); AppContext context; context.Set(contextString); delete[] contextString; while (backlen-- > 0) { - actionItems[idx].type = KM_KBP_IT_BACK; + actionItems[idx].type = KM_CORE_IT_BACK; WCHAR *CodeUnitPtr; const int DeadKeyLength = 3; const int SurrogateLength = 2; @@ -359,18 +360,18 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMSetOutput(PWSTR buf, DWORD backlen if (context.CharIsDeadkey()) { CodeUnitPtr = context.BufMax(DeadKeyLength); CodeUnitPtr += 2; - actionItems[idx].backspace.expected_type = KM_KBP_BT_MARKER; + actionItems[idx].backspace.expected_type = KM_CORE_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_type = KM_CORE_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_type = KM_CORE_BT_CHAR; actionItems[idx].backspace.expected_value = (DWORD)*CodeUnitPtr; } else { - actionItems[idx].backspace.expected_type = KM_KBP_BT_UNKNOWN; + actionItems[idx].backspace.expected_type = KM_CORE_BT_UNKNOWN; actionItems[idx].backspace.expected_value = 0; } context.Delete(); @@ -378,7 +379,7 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMSetOutput(PWSTR buf, DWORD backlen } while (*buf) { - actionItems[idx].type = KM_KBP_IT_CHAR; + actionItems[idx].type = KM_CORE_IT_CHAR; if (Uni_IsSurrogate1(*buf) && Uni_IsSurrogate2(*(buf + 1))) { actionItems[idx].character = Uni_SurrogateToUTF32(*buf, *(buf + 1)); buf++; @@ -388,8 +389,8 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMSetOutput(PWSTR buf, DWORD backlen buf++; idx++; } - actionItems[idx].type = KM_KBP_IT_END; - if (KM_KBP_STATUS_OK != km_kbp_state_queue_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, actionItems)) { + actionItems[idx].type = KM_CORE_IT_END; + if (KM_CORE_STATUS_OK != km_core_state_queue_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, actionItems)) { delete[] actionItems; return FALSE; } @@ -411,10 +412,10 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMQueueAction(int ItemType, DWORD dw 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) { + km_core_action_item *actionItem = kmnToCoreActionItem(ItemType, dwData, _td->state.vkey); + km_core_status_codes error_status = + (km_core_status_codes)km_core_state_queue_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, actionItem); + if (error_status != KM_CORE_STATUS_OK) { delete[] actionItem; SendDebugMessageFormat(0, sdmKeyboard, 0, "KMQueueAction: Error core queue_action_items error status:[%lu]",error_status); return FALSE; @@ -441,16 +442,16 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMGetContext(PWSTR buf, DWORD len) return FALSE; } - km_kbp_context_item *citems = nullptr; - if (KM_KBP_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) { + km_core_context_item *citems = nullptr; + if (KM_CORE_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) { return FALSE; } if (!ContextItemToAppContext(citems, buf, len)) { - km_kbp_context_items_dispose(citems); + km_core_context_items_dispose(citems); return FALSE; } - km_kbp_context_items_dispose(citems); + km_core_context_items_dispose(citems); //SendDebugMessageFormat(0, sdmKeyboard, 0, "KMGetContext: Exit"); return TRUE; @@ -598,7 +599,7 @@ LoadDLLs(LPINTKEYBOARDINFO lpkbi) { return FALSE; } - km_kbp_keyboard_imx *imx_list = lpkbi->lpIMXList; + km_core_keyboard_imx *imx_list = lpkbi->lpIMXList; // return early if the list empty avoiding loading the proxy dll into memory if (!imx_list->library_name) { return FALSE; @@ -643,7 +644,7 @@ LoadDLLs(LPINTKEYBOARDINFO lpkbi) { } // If result is true register a callback with the core if (result) { - km_kbp_state_imx_register_callback(lpkbi->lpCoreKeyboardState, IM_CallBackCore, (void *)lpkbi); + km_core_state_imx_register_callback(lpkbi->lpCoreKeyboardState, IM_CallBackCore, (void *)lpkbi); } //SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadDLLs: Exit"); return TRUE; diff --git a/windows/src/engine/keyman32-ver/calldll.h b/windows/src/engine/keyman32-ver/calldll.h index d3c38a93673..aceb37f9a29 100644 --- a/windows/src/engine/keyman32-ver/calldll.h +++ b/windows/src/engine/keyman32-ver/calldll.h @@ -37,7 +37,7 @@ This is only needed if there is imx keyboard */ BOOL IsIMWindow(HWND hwnd); // 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); +extern "C" uint8_t IM_CallBackCore(km_core_state *km_state, uint32_t UniqueStoreNo, void *callbackObject); extern "C" BOOL _declspec(dllexport) WINAPI KMDisplayIM(HWND hwnd, BOOL FShowAlways); extern "C" BOOL _declspec(dllexport) WINAPI KMHideIM(); diff --git a/windows/src/engine/keyman32-ver/keyboardoptions.cpp b/windows/src/engine/keyman32-ver/keyboardoptions.cpp index 96deb57590d..cc838286254 100644 --- a/windows/src/engine/keyman32-ver/keyboardoptions.cpp +++ b/windows/src/engine/keyman32-ver/keyboardoptions.cpp @@ -19,18 +19,18 @@ */ #include "pch.h" -BOOL IntLoadKeyboardOptionsRegistrytoCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* const state); +BOOL IntLoadKeyboardOptionsRegistrytoCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_core_state* const state); void IntSaveKeyboardOptionCoretoRegistry(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value); -static km_kbp_cp* CloneKMKBPCP(const km_kbp_cp* cp) { +static km_core_cp* CloneKMKBPCP(const km_core_cp* cp) { LPCWSTR buf = reinterpret_cast(cp); - km_kbp_cp* clone = new km_kbp_cp[wcslen(buf) + 1]; + km_core_cp* clone = new km_core_cp[wcslen(buf) + 1]; wcscpy_s(reinterpret_cast(clone), wcslen(buf) + 1, buf); return clone; } -static km_kbp_cp* CloneKMKBPCPFromWSTR(LPWSTR buf) { - km_kbp_cp* clone = new km_kbp_cp[wcslen(buf) + 1]; +static km_core_cp* CloneKMKBPCPFromWSTR(LPWSTR buf) { + km_core_cp* clone = new km_core_cp[wcslen(buf) + 1]; wcscpy_s(reinterpret_cast(clone), wcslen(buf) + 1, buf); return clone; } @@ -54,38 +54,38 @@ void IntSaveKeyboardOptionCoretoRegistry(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LP } } -void LoadKeyboardOptionsRegistrytoCore(LPINTKEYBOARDINFO kp, km_kbp_state* const state) +void LoadKeyboardOptionsRegistrytoCore(LPINTKEYBOARDINFO kp, km_core_state* const state) { SendDebugMessageFormat(0, sdmKeyboard, 0, "LoadKeyboardOptionsRegistrytoCore: Enter"); IntLoadKeyboardOptionsRegistrytoCore(REGSZ_KeyboardOptions, kp, state); } -BOOL IntLoadKeyboardOptionsRegistrytoCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_kbp_state* const state) +BOOL IntLoadKeyboardOptionsRegistrytoCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_core_state* const state) { assert(key != NULL); assert(kp != NULL); // 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) { + const km_core_keyboard_attrs* keyboardAttrs; + km_core_status err_status = km_core_keyboard_get_attrs(kp->lpCoreKeyboard, &keyboardAttrs); + if (err_status != KM_CORE_STATUS_OK) { SendDebugMessageFormat( - 0, sdmKeyboard, 0, "LoadKeyboardOptionsRegistrytoCore: km_kbp_keyboard_get_attrs failed with error status [%d]", err_status); + 0, sdmKeyboard, 0, "LoadKeyboardOptionsRegistrytoCore: km_core_keyboard_get_attrs failed with error status [%d]", err_status); return FALSE; } - size_t listSize = km_kbp_options_list_size(keyboardAttrs->default_options); + size_t listSize = km_core_options_list_size(keyboardAttrs->default_options); if (listSize == 0){ return TRUE; } - km_kbp_option_item* keyboardOpts = new km_kbp_option_item[listSize + 1]; + km_core_option_item* keyboardOpts = new km_core_option_item[listSize + 1]; RegistryReadOnly r(HKEY_CURRENT_USER); BOOL hasData = r.OpenKeyReadOnly(REGSZ_KeymanActiveKeyboards) && r.OpenKeyReadOnly(kp->Name) && r.OpenKeyReadOnly(key); int n = 0; for (auto kpc = keyboardAttrs->default_options; kpc->key; kpc++) { - keyboardOpts[n].scope = KM_KBP_OPT_KEYBOARD; + keyboardOpts[n].scope = KM_CORE_OPT_KEYBOARD; keyboardOpts[n].key = kpc->key; LPCWSTR coreKey = reinterpret_cast(kpc->key); WCHAR val[256]; @@ -96,13 +96,13 @@ BOOL IntLoadKeyboardOptionsRegistrytoCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_k } n++; } - keyboardOpts[n] = KM_KBP_OPTIONS_END; + keyboardOpts[n] = KM_CORE_OPTIONS_END; // once we have the option list we can then update the options using the public api call - err_status = km_kbp_state_options_update(state, keyboardOpts); - if (err_status != KM_KBP_STATUS_OK) { + err_status = km_core_state_options_update(state, keyboardOpts); + if (err_status != KM_CORE_STATUS_OK) { SendDebugMessageFormat( - 0, sdmKeyboard, 0, "LoadKeyboardOptionsRegistrytoCore: km_kbp_state_options_update failed with error status [%d]", err_status); + 0, sdmKeyboard, 0, "LoadKeyboardOptionsRegistrytoCore: km_core_state_options_update failed with error status [%d]", err_status); } for (int i = 0; i < n; i++) { delete[] keyboardOpts[i].value; diff --git a/windows/src/engine/keyman32-ver/keyboardoptions.h b/windows/src/engine/keyman32-ver/keyboardoptions.h index f55b0bcc77d..919b0bb71fa 100644 --- a/windows/src/engine/keyman32-ver/keyboardoptions.h +++ b/windows/src/engine/keyman32-ver/keyboardoptions.h @@ -24,7 +24,7 @@ * @param kp keyboard info object with options to be updated * @param state core keyboard state used to update keyboard options */ -void LoadKeyboardOptionsRegistrytoCore(LPINTKEYBOARDINFO kp, km_kbp_state* state); +void LoadKeyboardOptionsRegistrytoCore(LPINTKEYBOARDINFO kp, km_core_state* state); /** * Saves the keyboard option to the windows registry diff --git a/windows/src/engine/keyman32-ver/keyman32.cpp b/windows/src/engine/keyman32-ver/keyman32.cpp index 033b1856efb..75a30c410d5 100644 --- a/windows/src/engine/keyman32-ver/keyman32.cpp +++ b/windows/src/engine/keyman32-ver/keyman32.cpp @@ -562,17 +562,17 @@ BOOL ReleaseKeyboardMemory(LPKEYBOARD kbd) return TRUE; } -BOOL ReleaseStateMemoryCore(km_kbp_state **state) { +BOOL ReleaseStateMemoryCore(km_core_state **state) { if (!*state) return TRUE; - km_kbp_state_dispose(*state); + km_core_state_dispose(*state); *state = NULL; return TRUE; } -BOOL ReleaseKeyboardMemoryCore(km_kbp_keyboard **kbd) { +BOOL ReleaseKeyboardMemoryCore(km_core_keyboard **kbd) { if (!*kbd) return TRUE; - km_kbp_keyboard_dispose(*kbd); + km_core_keyboard_dispose(*kbd); *kbd = NULL; return TRUE; } diff --git a/windows/src/engine/keyman32-ver/keymanengine.h b/windows/src/engine/keyman32-ver/keymanengine.h index f40aeaf3728..65fc9ec1e58 100644 --- a/windows/src/engine/keyman32-ver/keymanengine.h +++ b/windows/src/engine/keyman32-ver/keymanengine.h @@ -18,11 +18,11 @@ #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT 1 #endif -// For keyboardprocessor_bits.h +// For keyman_core_api_bits.h #ifndef KMN_KBP_STATIC #define KMN_KBP_STATIC #endif -// For keyboardprocessor_bits.h +// For keyman_core_api_bits.h #ifndef _WIN32 #define _WIN32 1 #endif @@ -39,8 +39,8 @@ #include #include #include "../../../../common/windows/cpp/include/legacy_kmx_file.h" -#include -#include +#include +#include /***************************************************************************/ @@ -86,10 +86,10 @@ typedef struct tagINTKEYBOARDINFO int __filler2; // makes same as KEYBOARDINFO int nProfiles; LPINTKEYBOARDPROFILE Profiles; - km_kbp_keyboard* lpCoreKeyboard; - km_kbp_option_item* lpCoreKeyboardOptions; - km_kbp_state* lpCoreKeyboardState; - km_kbp_keyboard_imx* lpIMXList; + km_core_keyboard* lpCoreKeyboard; + km_core_option_item* lpCoreKeyboardOptions; + km_core_state* lpCoreKeyboardState; + km_core_keyboard_imx* lpIMXList; } INTKEYBOARDINFO, * LPINTKEYBOARDINFO; typedef struct tagINI @@ -107,7 +107,7 @@ typedef struct tagKMSTATE WCHAR charCode; // I4582 BOOL windowunicode; // I4287 BOOL isDown; - km_kbp_keyboard* lpCoreKb; // future use with IMDLL + km_core_keyboard* lpCoreKb; // future use with IMDLL } KMSTATE; // I3616 @@ -121,8 +121,8 @@ LRESULT CALLBACK kmnLowLevelKeyboardProc( // I4124 _In_ LPARAM lParam ); -BOOL ReleaseStateMemoryCore(km_kbp_state** state); -BOOL ReleaseKeyboardMemoryCore(km_kbp_keyboard** kbd); +BOOL ReleaseStateMemoryCore(km_core_state** state); +BOOL ReleaseKeyboardMemoryCore(km_core_keyboard** kbd); void PostGETNEXT(HWND hwnd); BOOL CompareMsg(LPMSG MsgA, LPMSG MsgB); @@ -264,7 +264,7 @@ BOOL SelectKeyboardTSF(DWORD KeymanID, BOOL foreground); // I3933 // I3949 BOOL ReportKeyboardChanged(WORD wCommand, DWORD dwProfileType, UINT langid, HKL hkl, GUID clsid, GUID guidProfile); void ProcessModifierChange(UINT key, BOOL isUp, BOOL isExtended); // I4793 -BOOL SetupCoreEnvironment(km_kbp_option_item **test_env_opts); -void DeleteCoreEnvironment(km_kbp_option_item *test_env_opts); +BOOL SetupCoreEnvironment(km_core_option_item **test_env_opts); +void DeleteCoreEnvironment(km_core_option_item *test_env_opts); #endif // _KEYMANENGINE_H diff --git a/windows/src/engine/keyman32-ver/kmhook_getmessage.cpp b/windows/src/engine/keyman32-ver/kmhook_getmessage.cpp index 562c455abb3..e129a0b8da3 100644 --- a/windows/src/engine/keyman32-ver/kmhook_getmessage.cpp +++ b/windows/src/engine/keyman32-ver/kmhook_getmessage.cpp @@ -287,7 +287,7 @@ LRESULT _kmnGetMessageProc(int nCode, WPARAM wParam, LPARAM lParam) if (!_td->lpActiveKeyboard) { return CallNextHookEx(Globals::get_hhookGetMessage(), nCode, wParam, lParam); } - if (KM_KBP_STATUS_OK != km_kbp_process_queued_actions(_td->lpActiveKeyboard->lpCoreKeyboardState)) { + if (KM_CORE_STATUS_OK != km_core_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); } diff --git a/windows/src/engine/keyman32-ver/kmprocess.cpp b/windows/src/engine/keyman32-ver/kmprocess.cpp index 64a6a48e98d..5772836de2a 100644 --- a/windows/src/engine/keyman32-ver/kmprocess.cpp +++ b/windows/src/engine/keyman32-ver/kmprocess.cpp @@ -99,20 +99,20 @@ char *getcontext_debug() { static BOOL Process_Event_Core(PKEYMAN64THREADDATA _td) { PWSTR contextBuf = _td->app->ContextBufMax(MAXCONTEXT); - km_kbp_context_item *citems = nullptr; + km_core_context_item *citems = nullptr; ContextItemsFromAppContext(contextBuf, &citems); - if (KM_KBP_STATUS_OK != km_kbp_context_set(km_kbp_state_context(_td->lpActiveKeyboard->lpCoreKeyboardState), citems)) { - km_kbp_context_items_dispose(citems); + if (KM_CORE_STATUS_OK != km_core_context_set(km_core_state_context(_td->lpActiveKeyboard->lpCoreKeyboardState), citems)) { + km_core_context_items_dispose(citems); return FALSE; } - km_kbp_context_items_dispose(citems); + km_core_context_items_dispose(citems); SendDebugMessageFormat( 0, sdmGlobal, 0, "ProcessEvent: vkey[%d] ShiftState[%d] isDown[%d]", _td->state.vkey, - static_cast(Globals::get_ShiftState() & (KM_KBP_MODIFIER_MASK_ALL | KM_KBP_MODIFIER_MASK_CAPS)), (uint8_t)_td->state.isDown); - // Mask the bits supported according to `km_kbp_modifier_state` enum, update the mask if this enum is expanded. - if (KM_KBP_STATUS_OK != km_kbp_process_event( + static_cast(Globals::get_ShiftState() & (KM_CORE_MODIFIER_MASK_ALL | KM_CORE_MODIFIER_MASK_CAPS)), (uint8_t)_td->state.isDown); + // Mask the bits supported according to `km_core_modifier_state` enum, update the mask if this enum is expanded. + if (KM_CORE_STATUS_OK != km_core_process_event( _td->lpActiveKeyboard->lpCoreKeyboardState, _td->state.vkey, - static_cast(Globals::get_ShiftState() & (KM_KBP_MODIFIER_MASK_ALL | KM_KBP_MODIFIER_MASK_CAPS)), (uint8_t)_td->state.isDown, KM_KBP_EVENT_FLAG_DEFAULT)) { + static_cast(Globals::get_ShiftState() & (KM_CORE_MODIFIER_MASK_ALL | KM_CORE_MODIFIER_MASK_CAPS)), (uint8_t)_td->state.isDown, KM_CORE_EVENT_FLAG_DEFAULT)) { SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessEvent CoreProcessEvent Result:False %d ", FALSE); return FALSE; } diff --git a/windows/src/engine/keyman32-ver/kmprocessactions.cpp b/windows/src/engine/keyman32-ver/kmprocessactions.cpp index 33df6d96ea5..c9d607a596c 100644 --- a/windows/src/engine/keyman32-ver/kmprocessactions.cpp +++ b/windows/src/engine/keyman32-ver/kmprocessactions.cpp @@ -6,7 +6,7 @@ */ #include "pch.h" -static BOOL processUnicodeChar(AITIP* app, const km_kbp_action_item* actionItem) { +static BOOL processUnicodeChar(AITIP* app, const km_core_action_item* actionItem) { if (Uni_IsSMP(actionItem->character)) { app->QueueAction(QIT_CHAR, (Uni_UTF32ToSurrogate1(actionItem->character))); app->QueueAction(QIT_CHAR, (Uni_UTF32ToSurrogate2(actionItem->character))); @@ -17,7 +17,7 @@ static BOOL processUnicodeChar(AITIP* app, const km_kbp_action_item* actionItem) return TRUE; } -static BOOL processMarker(AITIP* app, const km_kbp_action_item* actionItem) { +static BOOL processMarker(AITIP* app, const km_core_action_item* actionItem) { app->QueueAction(QIT_DEADKEY, (DWORD)actionItem->marker); return TRUE; } @@ -27,10 +27,10 @@ static BOOL processAlert(AITIP* app) { return TRUE; } -static BOOL processBack(AITIP* app, const km_kbp_action_item* actionItem) { - if (actionItem->backspace.expected_type == KM_KBP_BT_MARKER) { +static BOOL processBack(AITIP* app, const km_core_action_item* actionItem) { + if (actionItem->backspace.expected_type == KM_CORE_BT_MARKER) { app->QueueAction(QIT_BACK, BK_DEADKEY); - } else if(actionItem->backspace.expected_type == KM_KBP_BT_CHAR) { + } else if(actionItem->backspace.expected_type == KM_CORE_BT_CHAR) { // If this is a TSF-aware app we need to set the BK_SURROGATE flag to delete // both parts of the surrogate pair. Legacy apps receive a BKSP WM_KEYDOWN event // which results in deleting both parts in one action. @@ -39,27 +39,27 @@ static BOOL processBack(AITIP* app, const km_kbp_action_item* actionItem) { } else { app->QueueAction(QIT_BACK, BK_DEFAULT); } - } else { // KM_KBP_BT_UNKNOWN + } else { // KM_CORE_BT_UNKNOWN app->QueueAction(QIT_BACK, BK_DEFAULT); } return TRUE; } static BOOL processPersistOpt( - const km_kbp_action_item* actionItem, - km_kbp_state* keyboardState, + const km_core_action_item* actionItem, + km_core_state* keyboardState, LPINTKEYBOARDINFO activeKeyboard ) { if (actionItem->option != NULL) { - // Allocate for 1 option plus 1 pad struct of 0's for KM_KBP_IT_END - km_kbp_option_item keyboardOpts[2] = { 0 }; + // Allocate for 1 option plus 1 pad struct of 0's for KM_CORE_IT_END + km_core_option_item keyboardOpts[2] = { 0 }; keyboardOpts[0].key = actionItem->option->key; keyboardOpts[0].value = actionItem->option->value; - km_kbp_status eventStatus = (km_kbp_status_codes)km_kbp_state_options_update(keyboardState, keyboardOpts); - if (eventStatus != KM_KBP_STATUS_OK) + km_core_status eventStatus = (km_core_status_codes)km_core_state_options_update(keyboardState, keyboardOpts); + if (eventStatus != KM_CORE_STATUS_OK) { - // log warning "problem saving option for km_kbp_keyboard"); + // log warning "problem saving option for km_core_keyboard"); SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessHook: Error %d saving option for keyboard [%s].", eventStatus, activeKeyboard->Name); } @@ -79,15 +79,15 @@ static BOOL processPersistOpt( static BOOL processInvalidateContext( AITIP* app, - km_kbp_state* keyboardState + km_core_state* keyboardState ) { - km_kbp_context_clear(km_kbp_state_context(keyboardState)); + km_core_context_clear(km_core_state_context(keyboardState)); app->ResetContext(); return TRUE; } static BOOL -processCapsLock(const km_kbp_action_item* actionItem, BOOL isUp, BOOL Updateable, BOOL externalEvent) { +processCapsLock(const km_core_action_item* actionItem, BOOL isUp, BOOL Updateable, BOOL externalEvent) { // We only want to process the Caps Lock key event once -- // in the first pass (!Updateable). @@ -134,36 +134,36 @@ BOOL ProcessActions(BOOL* emitKeyStroke) // Process the action items from the core. This actions will modify the windows context (AppContext). // Therefore it is not required to copy the context from the core to the windows context. - for (auto act = km_kbp_state_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, nullptr); act->type != KM_KBP_IT_END; act++) { + for (auto act = km_core_state_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, nullptr); act->type != KM_CORE_IT_END; act++) { BOOL continueProcessingActions = TRUE; SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActions : act->type=%d", act->type); switch (act->type) { - case KM_KBP_IT_CHAR: + case KM_CORE_IT_CHAR: continueProcessingActions = processUnicodeChar(_td->app, act); break; - case KM_KBP_IT_MARKER: + case KM_CORE_IT_MARKER: continueProcessingActions = processMarker(_td->app, act); break; - case KM_KBP_IT_ALERT: + case KM_CORE_IT_ALERT: continueProcessingActions = processAlert(_td->app); break; - case KM_KBP_IT_BACK: + case KM_CORE_IT_BACK: continueProcessingActions = processBack(_td->app, act); break; - case KM_KBP_IT_PERSIST_OPT: + case KM_CORE_IT_PERSIST_OPT: continueProcessingActions = processPersistOpt(act, _td->lpActiveKeyboard->lpCoreKeyboardState, _td->lpActiveKeyboard); break; - case KM_KBP_IT_EMIT_KEYSTROKE: + case KM_CORE_IT_EMIT_KEYSTROKE: *emitKeyStroke = TRUE; continueProcessingActions = TRUE; break; - case KM_KBP_IT_INVALIDATE_CONTEXT: + case KM_CORE_IT_INVALIDATE_CONTEXT: continueProcessingActions = processInvalidateContext(_td->app, _td->lpActiveKeyboard->lpCoreKeyboardState); break; - case KM_KBP_IT_CAPSLOCK: + case KM_CORE_IT_CAPSLOCK: continueProcessingActions = processCapsLock(act, !_td->state.isDown, _td->TIPFUpdateable, FALSE); break; - case KM_KBP_IT_END: + case KM_CORE_IT_END: // fallthrough default: assert(false); // NOT SUPPORTED @@ -190,18 +190,18 @@ ProcessActionsNonUpdatableParse(BOOL* emitKeyStroke) { _td->CoreProcessEventRun = TRUE; BOOL continueProcessingActions = TRUE; - for (auto act = km_kbp_state_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, nullptr); act->type != KM_KBP_IT_END; act++) { + for (auto act = km_core_state_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, nullptr); act->type != KM_CORE_IT_END; act++) { switch (act->type) { - case KM_KBP_IT_EMIT_KEYSTROKE: + case KM_CORE_IT_EMIT_KEYSTROKE: *emitKeyStroke = TRUE; SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActionsNonUpdatableParse EMIT_KEYSTROKE: act->type=[%d]", act->type); continueProcessingActions = TRUE; _td->CoreProcessEventRun = FALSE; // If we emit the key stroke on this parse we don't need the second parse break; - case KM_KBP_IT_CAPSLOCK: + case KM_CORE_IT_CAPSLOCK: continueProcessingActions = processCapsLock(act, !_td->state.isDown, _td->TIPFUpdateable, FALSE); break; - case KM_KBP_IT_INVALIDATE_CONTEXT: + case KM_CORE_IT_INVALIDATE_CONTEXT: continueProcessingActions = processInvalidateContext(_td->app, _td->lpActiveKeyboard->lpCoreKeyboardState); break; } @@ -221,13 +221,13 @@ ProcessActionsExternalEvent() { // Currently only a subset of actions are handled. // Other actions will be added when needed. BOOL continueProcessingActions = TRUE; - for (auto act = km_kbp_state_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, nullptr); act->type != KM_KBP_IT_END; + for (auto act = km_core_state_action_items(_td->lpActiveKeyboard->lpCoreKeyboardState, nullptr); act->type != KM_CORE_IT_END; act++) { switch (act->type) { - case KM_KBP_IT_CAPSLOCK: + case KM_CORE_IT_CAPSLOCK: continueProcessingActions = processCapsLock(act, !_td->state.isDown, FALSE, TRUE); break; - case KM_KBP_IT_INVALIDATE_CONTEXT: + case KM_CORE_IT_INVALIDATE_CONTEXT: continueProcessingActions = processInvalidateContext(_td->app, _td->lpActiveKeyboard->lpCoreKeyboardState); break; } diff --git a/windows/src/engine/keyman32-ver/kmprocessactions.h b/windows/src/engine/keyman32-ver/kmprocessactions.h index ceac83ba77e..66bb70570c2 100644 --- a/windows/src/engine/keyman32-ver/kmprocessactions.h +++ b/windows/src/engine/keyman32-ver/kmprocessactions.h @@ -30,7 +30,7 @@ BOOL ProcessActionsNonUpdatableParse(BOOL* emitKeyStroke); * This function processes the actions queued in the core processor in * the external event case. That is actions not caused by a keystroke but from selecting a * different keyboard for example. - * Currently only KM_KBP_IT_CAPSLOCK and KM_KBP_IT_INVALIDATE_CONTEXT are processed. + * Currently only KM_CORE_IT_CAPSLOCK and KM_CORE_IT_INVALIDATE_CONTEXT are processed. * * @return BOOL TRUE if actions were successfully processed */ diff --git a/windows/src/engine/keyman32-ver/preservedkeymap.cpp b/windows/src/engine/keyman32-ver/preservedkeymap.cpp index 1fd803838d7..6170d40bda7 100644 --- a/windows/src/engine/keyman32-ver/preservedkeymap.cpp +++ b/windows/src/engine/keyman32-ver/preservedkeymap.cpp @@ -49,13 +49,13 @@ class PreservedKeyMap * @param cPreservedKeys number of preserved keys in pPreservedKeys - or the size pPreservedKeys needs to be * @return BOOL return TRUE on success */ - BOOL MapKeyboard(km_kbp_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys); + BOOL MapKeyboard(km_core_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys); private: BOOL m_BaseKeyboardUsesAltGr; // I4592 UINT ShiftToTSFShift(UINT ShiftFlags); BOOL MapUSCharToVK(UINT *puKey, UINT *puShiftFlags); - BOOL MapKeyRule(km_kbp_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey); + BOOL MapKeyRule(km_core_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey); BOOL IsMatchingKey(PreservedKey *pKey, PreservedKey *pKeys, size_t cKeys); }; @@ -198,7 +198,7 @@ UINT PreservedKeyMap::ShiftToTSFShift(UINT ShiftFlags) } BOOL -PreservedKeyMap::MapKeyRule(km_kbp_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey) { +PreservedKeyMap::MapKeyRule(km_core_keyboard_key *pKeyRule, TF_PRESERVEDKEY *pPreservedKey) { UINT ShiftFlags; UINT Key; @@ -242,7 +242,7 @@ BOOL PreservedKeyMap::IsMatchingKey(PreservedKey *pKey, PreservedKey *pKeys, siz } BOOL -PreservedKeyMap::MapKeyboard(km_kbp_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys) { +PreservedKeyMap::MapKeyboard(km_core_keyboard *pKeyboard, PreservedKey **pPreservedKeys, size_t *cPreservedKeys) { size_t cKeys = 0, cRules = 0, n = 0; DWORD i; @@ -259,20 +259,20 @@ PreservedKeyMap::MapKeyboard(km_kbp_keyboard *pKeyboard, PreservedKey **pPreserv const UINT RALT_MATCHING_MASK = TF_MOD_CONTROL | TF_MOD_ALT | TF_MOD_LCONTROL | TF_MOD_RCONTROL | TF_MOD_LALT | TF_MOD_RALT; // This is where we will call down to the api to get list of keys used in the keyboard rules - km_kbp_keyboard_key *kb_key_list; + km_core_keyboard_key *kb_key_list; - km_kbp_status err_status = km_kbp_keyboard_get_key_list(pKeyboard, &kb_key_list); - if ((err_status != KM_KBP_STATUS_OK) || (kb_key_list ==nullptr)) { + km_core_status err_status = km_core_keyboard_get_key_list(pKeyboard, &kb_key_list); + if ((err_status != KM_CORE_STATUS_OK) || (kb_key_list ==nullptr)) { return FALSE; } - km_kbp_keyboard_key *key_rule_it = kb_key_list; + km_core_keyboard_key *key_rule_it = kb_key_list; for (; key_rule_it->key; ++key_rule_it) { ++cRules; } cKeys = cRules; if (cKeys == 0) { - km_kbp_keyboard_key_list_dispose(kb_key_list); + km_core_keyboard_key_list_dispose(kb_key_list); return FALSE; } @@ -283,12 +283,12 @@ PreservedKeyMap::MapKeyboard(km_kbp_keyboard *pKeyboard, PreservedKey **pPreserv if (pPreservedKeys == NULL) { *cPreservedKeys = cKeys; - km_kbp_keyboard_key_list_dispose(kb_key_list); + km_core_keyboard_key_list_dispose(kb_key_list); return TRUE; } if (*cPreservedKeys < cKeys) { - km_kbp_keyboard_key_list_dispose(kb_key_list); + km_core_keyboard_key_list_dispose(kb_key_list); return FALSE; } @@ -315,7 +315,7 @@ PreservedKeyMap::MapKeyboard(km_kbp_keyboard *pKeyboard, PreservedKey **pPreserv } } - km_kbp_keyboard_key_list_dispose(kb_key_list); + km_core_keyboard_key_list_dispose(kb_key_list); *cPreservedKeys = n; // return actual count of allocated keys, usually smaller than allocated count return TRUE; diff --git a/windows/src/engine/keyman32-ver/selectkeyboard.cpp b/windows/src/engine/keyman32-ver/selectkeyboard.cpp index bfa08cea034..61222856d40 100644 --- a/windows/src/engine/keyman32-ver/selectkeyboard.cpp +++ b/windows/src/engine/keyman32-ver/selectkeyboard.cpp @@ -102,9 +102,9 @@ BOOL SelectKeyboard(DWORD KeymanID) ActivateDLLs(_td->lpActiveKeyboard); - if (KM_KBP_STATUS_OK != - km_kbp_event(_td->lpActiveKeyboard->lpCoreKeyboardState, KM_KBP_EVENT_KEYBOARD_ACTIVATED, nullptr)) { - SendDebugMessageFormat(0, sdmGlobal, 0, "km_kbp_event Failed Result: %d ", FALSE); + if (KM_CORE_STATUS_OK != + km_core_event(_td->lpActiveKeyboard->lpCoreKeyboardState, KM_CORE_EVENT_KEYBOARD_ACTIVATED, nullptr)) { + SendDebugMessageFormat(0, sdmGlobal, 0, "km_core_event Failed Result: %d ", FALSE); } else { ProcessActionsExternalEvent(); } diff --git a/windows/src/engine/keyman32-ver/tests/keyman-engine-tests/keyboardoptionstests.cpp b/windows/src/engine/keyman32-ver/tests/keyman-engine-tests/keyboardoptionstests.cpp index 795f89fe447..308f86d0fa6 100644 --- a/windows/src/engine/keyman32-ver/tests/keyman-engine-tests/keyboardoptionstests.cpp +++ b/windows/src/engine/keyman32-ver/tests/keyman-engine-tests/keyboardoptionstests.cpp @@ -6,44 +6,44 @@ #include -// Test SetupCoreEnvironment and also test km_kbp_state_options_update +// Test SetupCoreEnvironment and also test km_core_state_options_update TEST(KEYBOARDOPTIONS, SetupCoreEnvironment) { LPINTKEYBOARDINFO kp = new INTKEYBOARDINFO; memset(kp, 0, sizeof(INTKEYBOARDINFO)); - km_kbp_option_item *core_env_opts = nullptr; + km_core_option_item *core_env_opts = nullptr; - km_kbp_path_name dummyPath = L"dummyActions.mock"; + km_core_path_name dummyPath = L"dummyActions.mock"; EXPECT_EQ(SetupCoreEnvironment(&core_env_opts), TRUE); - EXPECT_EQ(km_kbp_keyboard_load(dummyPath, &kp->lpCoreKeyboard), KM_KBP_STATUS_OK); - EXPECT_EQ(km_kbp_state_create(kp->lpCoreKeyboard, core_env_opts, &kp->lpCoreKeyboardState), KM_KBP_STATUS_OK); + EXPECT_EQ(km_core_keyboard_load(dummyPath, &kp->lpCoreKeyboard), KM_CORE_STATUS_OK); + EXPECT_EQ(km_core_state_create(kp->lpCoreKeyboard, core_env_opts, &kp->lpCoreKeyboardState), KM_CORE_STATUS_OK); - km_kbp_option_item *expected_items = new km_kbp_option_item[5]; + km_core_option_item *expected_items = new km_core_option_item[5]; // These are taken from SetupCoreEnvironment in CoreEnvironment.cpp - expected_items[0].scope = KM_KBP_OPT_ENVIRONMENT; - expected_items[0].key = KM_KBP_KMX_ENV_BASELAYOUT; - expected_items[0].value = reinterpret_cast(Globals::get_BaseKeyboardName()); + expected_items[0].scope = KM_CORE_OPT_ENVIRONMENT; + expected_items[0].key = KM_CORE_KMX_ENV_BASELAYOUT; + expected_items[0].value = reinterpret_cast(Globals::get_BaseKeyboardName()); - expected_items[1].scope = KM_KBP_OPT_ENVIRONMENT; - expected_items[1].key = KM_KBP_KMX_ENV_BASELAYOUTALT; - expected_items[1].value = reinterpret_cast(Globals::get_BaseKeyboardNameAlt()); + expected_items[1].scope = KM_CORE_OPT_ENVIRONMENT; + expected_items[1].key = KM_CORE_KMX_ENV_BASELAYOUTALT; + expected_items[1].value = reinterpret_cast(Globals::get_BaseKeyboardNameAlt()); - expected_items[2].scope = KM_KBP_OPT_ENVIRONMENT; - expected_items[2].key = KM_KBP_KMX_ENV_SIMULATEALTGR; + expected_items[2].scope = KM_CORE_OPT_ENVIRONMENT; + expected_items[2].key = KM_CORE_KMX_ENV_SIMULATEALTGR; expected_items[2].value = Globals::get_SimulateAltGr() ? u"1" : u"0"; - expected_items[3].scope = KM_KBP_OPT_ENVIRONMENT; - expected_items[3].key = KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT; + expected_items[3].scope = KM_CORE_OPT_ENVIRONMENT; + expected_items[3].key = KM_CORE_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT; expected_items[3].value = KeyboardGivesCtrlRAltForRAlt() ? u"1" : u"0"; - expected_items[4] = KM_KBP_OPTIONS_END; - km_kbp_cp const* retValue = nullptr; + expected_items[4] = KM_CORE_OPTIONS_END; + km_core_cp const* retValue = nullptr; std::u16string value = u""; std::u16string expectedValue = u""; for (int i = 0; i < 4; ++i) { - EXPECT_EQ(km_kbp_state_option_lookup(kp->lpCoreKeyboardState, KM_KBP_OPT_ENVIRONMENT, expected_items[i].key, &retValue), KM_KBP_STATUS_OK); + EXPECT_EQ(km_core_state_option_lookup(kp->lpCoreKeyboardState, KM_CORE_OPT_ENVIRONMENT, expected_items[i].key, &retValue), KM_CORE_STATUS_OK); value = retValue; expectedValue = expected_items[i].value; EXPECT_TRUE(expectedValue == value); @@ -53,12 +53,12 @@ TEST(KEYBOARDOPTIONS, SetupCoreEnvironment) { expected_items[0].value = u"updated"; expected_items[3].value = u"updated"; - EXPECT_EQ(km_kbp_state_options_update(kp->lpCoreKeyboardState, expected_items), KM_KBP_STATUS_OK); + EXPECT_EQ(km_core_state_options_update(kp->lpCoreKeyboardState, expected_items), KM_CORE_STATUS_OK); for (int i = 0; i < 4; ++i) { EXPECT_EQ( - km_kbp_state_option_lookup(kp->lpCoreKeyboardState, KM_KBP_OPT_ENVIRONMENT, expected_items[i].key, &retValue), - KM_KBP_STATUS_OK); + km_core_state_option_lookup(kp->lpCoreKeyboardState, KM_CORE_OPT_ENVIRONMENT, expected_items[i].key, &retValue), + KM_CORE_STATUS_OK); value = retValue; expectedValue = expected_items[i].value; EXPECT_TRUE(expectedValue == value); diff --git a/windows/src/engine/keyman32-ver/tests/keyman-engine-tests/kmprocessactionstests.cpp b/windows/src/engine/keyman32-ver/tests/keyman-engine-tests/kmprocessactionstests.cpp index 5b4e2bfff85..7af218cd77b 100644 --- a/windows/src/engine/keyman32-ver/tests/keyman-engine-tests/kmprocessactionstests.cpp +++ b/windows/src/engine/keyman32-ver/tests/keyman-engine-tests/kmprocessactionstests.cpp @@ -2,7 +2,7 @@ #include "kmprocessactions.cpp" // Test the Process Actions private functions -// TODO: The following actions are not tested KM_KBP_IT_ALERT, KM_KBP_IT_PERSIST_OPT, KM_KBP_IT_EMIT_KEYSTROKE +// TODO: The following actions are not tested KM_CORE_IT_ALERT, KM_CORE_IT_PERSIST_OPT, KM_CORE_IT_EMIT_KEYSTROKE // Fixture for kmprocessactons tests class KMPROCESSACTIONS : public ::testing::Test { @@ -23,62 +23,62 @@ class KMPROCESSACTIONS : public ::testing::Test { ~KMPROCESSACTIONS() {} }; -// KM_KBP_IT_CHAR - processUnicodeChar +// KM_CORE_IT_CHAR - processUnicodeChar TEST_F(KMPROCESSACTIONS, processUnicodeChartest) { WCHAR callbuf[MAXCONTEXT]; AITIP testApp; WCHAR *expectedContext = L"A"; - km_kbp_action_item itemAddChar = { KM_KBP_IT_CHAR, {0,}, {'A'}}; + km_core_action_item itemAddChar = { KM_CORE_IT_CHAR, {0,}, {'A'}}; processUnicodeChar(&testApp, &itemAddChar); WCHAR *contextBuf = testApp.ContextBufMax(MAXCONTEXT); EXPECT_STREQ(contextBuf, expectedContext); - km_kbp_usv testSurrogateChar = Uni_SurrogateToUTF32(0xD801, 0xDC37); //𐐷'; - km_kbp_action_item itemAddChar2 = {KM_KBP_IT_CHAR, {0,}, {testSurrogateChar}}; + km_core_usv testSurrogateChar = Uni_SurrogateToUTF32(0xD801, 0xDC37); //𐐷'; + km_core_action_item itemAddChar2 = {KM_CORE_IT_CHAR, {0,}, {testSurrogateChar}}; WCHAR expectedStringSurrogate[] = {'A', 0xD801, 0xDC37, 0}; processUnicodeChar(&testApp, &itemAddChar2); contextBuf = testApp.ContextBufMax(MAXCONTEXT); EXPECT_STREQ(contextBuf, &expectedStringSurrogate[0]); } -// KM_KBP_IT_MARKER - processMarker Deadkey +// KM_CORE_IT_MARKER - processMarker Deadkey TEST_F(KMPROCESSACTIONS, processMarkertest) { WCHAR callbuf[MAXCONTEXT]; AITIP testApp; WCHAR expectedContext[] = {UC_SENTINEL, CODE_DEADKEY, 2, 0}; uintptr_t marker = 2; - km_kbp_action_item itemAddMarker = {KM_KBP_IT_MARKER, {0,}, {marker}}; + km_core_action_item itemAddMarker = {KM_CORE_IT_MARKER, {0,}, {marker}}; processMarker(&testApp, &itemAddMarker); WCHAR *contextBuf = testApp.ContextBufMax(MAXCONTEXT); EXPECT_STREQ(contextBuf, expectedContext); } -// KM_KBP_IT_BACK - processBack +// KM_CORE_IT_BACK - processBack // First test processing a backspace for a deadkey TEST_F(KMPROCESSACTIONS, processBackDeadkeytest) { WCHAR callbuf[MAXCONTEXT]; AITIP testApp; WCHAR expectedContext[] = {'A', 0}; - km_kbp_action_item itemAddChar = {KM_KBP_IT_CHAR, {0,}, {'A'}}; + km_core_action_item itemAddChar = {KM_CORE_IT_CHAR, {0,}, {'A'}}; processUnicodeChar(&testApp, &itemAddChar); uintptr_t marker = 2; - km_kbp_action_item itemAddMarker = {KM_KBP_IT_MARKER, {0,}, {marker}}; + km_core_action_item itemAddMarker = {KM_CORE_IT_MARKER, {0,}, {marker}}; processMarker(&testApp, &itemAddMarker); - km_kbp_action_item itemBackSpace = {KM_KBP_IT_BACK}; - itemBackSpace.backspace.expected_type = KM_KBP_IT_MARKER; + km_core_action_item itemBackSpace = {KM_CORE_IT_BACK}; + itemBackSpace.backspace.expected_type = KM_CORE_IT_MARKER; itemBackSpace.backspace.expected_value = marker; processBack(&testApp, &itemBackSpace); WCHAR *contextBuf = testApp.ContextBufMax(MAXCONTEXT); EXPECT_STREQ(contextBuf, expectedContext); } -// KM_KBP_IT_BACK - processBack +// KM_CORE_IT_BACK - processBack // Press Backspace for a normal character // Also test for Unknown Character TEST_F(KMPROCESSACTIONS, processBackCharactertest) { @@ -87,42 +87,42 @@ TEST_F(KMPROCESSACTIONS, processBackCharactertest) { AITIP testApp; WCHAR expectedContext[] = {'A', 0}; WCHAR expectedContextFinal[] = {0}; - km_kbp_action_item itemAddChar = {KM_KBP_IT_CHAR, {0,}, {'A'}}; + km_core_action_item itemAddChar = {KM_CORE_IT_CHAR, {0,}, {'A'}}; processUnicodeChar(&testApp, &itemAddChar); itemAddChar.character = 'B'; processUnicodeChar(&testApp, &itemAddChar); - km_kbp_action_item itemBackSpace = {KM_KBP_IT_BACK}; - itemBackSpace.backspace.expected_type = KM_KBP_IT_CHAR; + km_core_action_item itemBackSpace = {KM_CORE_IT_BACK}; + itemBackSpace.backspace.expected_type = KM_CORE_IT_CHAR; itemBackSpace.backspace.expected_value = 'B'; // backspace processBack(&testApp, &itemBackSpace); WCHAR *contextBuf = testApp.ContextBufMax(MAXCONTEXT); EXPECT_STREQ(contextBuf, expectedContext); // backspace for unknown it should backspace a character - itemBackSpace.type = KM_KBP_BT_UNKNOWN; + itemBackSpace.type = KM_CORE_BT_UNKNOWN; processBack(&testApp, &itemBackSpace); contextBuf = testApp.ContextBufMax(MAXCONTEXT); EXPECT_STREQ(contextBuf, expectedContextFinal); } -// KM_KBP_IT_BACK - processBack +// KM_CORE_IT_BACK - processBack // Press Backspace for a surrogate character. // TODO: This test doesn't test for the PostKey handling -// of TSF and two backspaces being sent to the App. +// of TSF and two backspaces being sent to the App. TEST_F(KMPROCESSACTIONS, processBackSurrogateTSFtest) { WCHAR callbuf[MAXCONTEXT]; AITIP testApp; WCHAR expectedContext[] = { 0 }; WCHAR expectedStringSurrogate[] = {0xD801, 0xDC37, 0}; - km_kbp_usv testSurrogateChar = Uni_SurrogateToUTF32(0xD801, 0xDC37); //𐐷'; - km_kbp_action_item itemAddChar = {KM_KBP_IT_CHAR, {0,}, {testSurrogateChar}}; + km_core_usv testSurrogateChar = Uni_SurrogateToUTF32(0xD801, 0xDC37); //𐐷'; + km_core_action_item itemAddChar = {KM_CORE_IT_CHAR, {0,}, {testSurrogateChar}}; processUnicodeChar(&testApp, &itemAddChar); - km_kbp_action_item itemBackSpace = {KM_KBP_IT_BACK}; - itemBackSpace.backspace.expected_type = KM_KBP_IT_CHAR; + km_core_action_item itemBackSpace = {KM_CORE_IT_BACK}; + itemBackSpace.backspace.expected_type = KM_CORE_IT_CHAR; itemBackSpace.backspace.expected_value = testSurrogateChar; WCHAR *contextBuf = testApp.ContextBufMax(MAXCONTEXT); EXPECT_STREQ(contextBuf, expectedStringSurrogate); @@ -132,7 +132,7 @@ TEST_F(KMPROCESSACTIONS, processBackSurrogateTSFtest) { EXPECT_STREQ(contextBuf, expectedContext); } -// KM_KBP_IT_BACK - processBack +// KM_CORE_IT_BACK - processBack // Press Backspace for a character doesn't match expected character // Note currently we don't check for a character match this should be updated @@ -141,13 +141,13 @@ TEST_F(KMPROCESSACTIONS, processBackUnexpectedChartest) { WCHAR callbuf[MAXCONTEXT]; AITIP testApp; WCHAR expectedContext[] = {'A', 0}; - km_kbp_action_item itemAddChar = {KM_KBP_IT_CHAR, {0,}, {'A'}}; + km_core_action_item itemAddChar = {KM_CORE_IT_CHAR, {0,}, {'A'}}; processUnicodeChar(&testApp, &itemAddChar); itemAddChar.character = 'C'; processUnicodeChar(&testApp, &itemAddChar); - km_kbp_action_item itemBackSpace = {KM_KBP_IT_BACK}; - itemBackSpace.backspace.expected_type = KM_KBP_IT_CHAR; + km_core_action_item itemBackSpace = {KM_CORE_IT_BACK}; + itemBackSpace.backspace.expected_type = KM_CORE_IT_CHAR; itemBackSpace.backspace.expected_value = 'B'; // backspace processBack(&testApp, &itemBackSpace); @@ -155,32 +155,32 @@ TEST_F(KMPROCESSACTIONS, processBackUnexpectedChartest) { EXPECT_STREQ(contextBuf, expectedContext); } -// KM_KBP_IT_INVALIDATE_CONTEXT - processInvalidateContext +// KM_CORE_IT_INVALIDATE_CONTEXT - processInvalidateContext TEST_F(KMPROCESSACTIONS, processInvalidateContextTest) { WCHAR callbuf[MAXCONTEXT]; AITIP testApp; WCHAR expectedContext[] = {0}; - km_kbp_action_item itemAddChar = {KM_KBP_IT_CHAR, {0,}, {'A'}}; + km_core_action_item itemAddChar = {KM_CORE_IT_CHAR, {0,}, {'A'}}; processUnicodeChar(&testApp, &itemAddChar); itemAddChar.character = 'B'; processUnicodeChar(&testApp, &itemAddChar); // A keyboard a state is need to test processInvalidateContext - km_kbp_option_item test_env_opts[] = {{u"hello", u"world", KM_KBP_OPT_KEYBOARD}, KM_KBP_OPTIONS_END}; - km_kbp_keyboard *testKB = nullptr; - km_kbp_state *testState = nullptr; - km_kbp_path_name dummyPath = L"dummyActions.mock"; - EXPECT_EQ(km_kbp_keyboard_load(dummyPath, &testKB), KM_KBP_STATUS_OK); - EXPECT_EQ(km_kbp_state_create(testKB, test_env_opts, &testState), KM_KBP_STATUS_OK); + km_core_option_item test_env_opts[] = {{u"hello", u"world", KM_CORE_OPT_KEYBOARD}, KM_CORE_OPTIONS_END}; + km_core_keyboard *testKB = nullptr; + km_core_state *testState = nullptr; + km_core_path_name dummyPath = L"dummyActions.mock"; + EXPECT_EQ(km_core_keyboard_load(dummyPath, &testKB), KM_CORE_STATUS_OK); + EXPECT_EQ(km_core_state_create(testKB, test_env_opts, &testState), KM_CORE_STATUS_OK); processInvalidateContext(&testApp,testState); WCHAR *contextBuf = testApp.ContextBufMax(MAXCONTEXT); EXPECT_STREQ(contextBuf, expectedContext); // dispose keyboard - km_kbp_state_dispose(testState); - km_kbp_keyboard_dispose(testKB); + km_core_state_dispose(testState); + km_core_keyboard_dispose(testKB); } diff --git a/windows/src/global/delphi/general/Keyman.System.Settings.pas b/windows/src/global/delphi/general/Keyman.System.Settings.pas index b22e47056f0..b021e56cddf 100644 --- a/windows/src/global/delphi/general/Keyman.System.Settings.pas +++ b/windows/src/global/delphi/general/Keyman.System.Settings.pas @@ -95,7 +95,7 @@ TKeymanSettings = class(TObjectList) ValueType: kstInteger ); - BaseKeymanSettings: array[0..34] of TKeymanSettingBase = ( + BaseKeymanSettings: array[0..33] of TKeymanSettingBase = ( // TIKE:UTikeDebugMode.TikeDebugMode ( @@ -231,15 +231,6 @@ TKeymanSettings = class(TObjectList) Description: 'Source files for Unicode data' ), - // TIKE:RedistFiles.GetDebugKMCmpDllPath - ( - ID: 'development.paths.developer.kmcmpdll'; - Name: 'Debug_KMCMPDLLPath'; - RootKey: HKCU; - Key: SRegKey_KeymanDebug_CU; - Description: 'Path to debug version of kmcmpdll.dll' - ), - // TIKE:RedistFiles.GetRedistAddinsPath ( ID: 'development.paths.developer.redistributable_addins'; diff --git a/windows/src/support/charident/Makefile b/windows/src/support/charident/Makefile index dd6721c5c0a..cff67416776 100644 --- a/windows/src/support/charident/Makefile +++ b/windows/src/support/charident/Makefile @@ -15,6 +15,6 @@ signcode: wrap-symbols: $(SYMSTORE) $(PROGRAM)\support\charident.exe /t keyman-windows - #TODO: $(SYMSTORE) $(DEBUGPATH)\support\charident.dbg /t keyman-windows +#TODO: $(SYMSTORE) $(DEBUGPATH)\support\charident.dbg /t keyman-windows !include ..\..\Target.mak diff --git a/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cv.pas b/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cv.pas deleted file mode 100644 index 56649c862a1..00000000000 --- a/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cv.pas +++ /dev/null @@ -1,129 +0,0 @@ -unit cv; - -interface - -procedure Run; - -implementation - -uses - System.Classes, - System.JSON, - System.SysUtils, - - JsonUtil, - - Keyman.System.KMXFileLanguages; - - -function LoadJsonFile(f: string): TJSONObject; -begin - with TStringStream.Create('', TEncoding.UTF8) do - try - LoadFromFile(f); - Result := TJSONObject.ParseJsonValue(DataString) as TJSONObject; - finally - Free; - end; -end; - -procedure SaveJsonFile(o: TJSONObject; f: string); -var - str: TStringList; -begin - str := TStringList.Create; - try - PrettyPrintJSON(o, str); - with TStringStream.Create(str.Text, TEncoding.UTF8) do - try - // Use TStringStream so we don't get a default BOM prolog - SaveToFile(f); - finally - Free; - end; - finally - str.Free; - end; -end; - -procedure ProcessFile(f: string); -var - o: TJSONObject; - a, b: TJSONArray; - i: Integer; - FNewCode: string; - FChanged: Boolean; - olin, o2, ol: TJSONObject; -begin - writeln(f); - FChanged := False; - b := nil; - ol := nil; - o := LoadJsonFile(f); - if o.Values['languages'] is TJSONArray then - begin - a := o.Values['languages'] as TJSONArray; - b := TJSONArray.Create; - for i := 0 to a.Count - 1 do - begin - FNewCode := TKMXFileLanguages.TranslateISO6393ToBCP47(a.Items[i].Value); - if FNewCode <> a.Items[i].Value then - FChanged := True; - b.Add(FNewCode); - end; - end - else if o.Values['languages'] is TJSONObject then - begin - olin := o.Values['languages'] as TJSONObject; - ol := TJSONObject.Create; - for i := 0 to olin.Count - 1 do - begin - FNewCode := TKMXFileLanguages.TranslateISO6393ToBCP47(olin.Pairs[i].JsonString.Value); - ol.AddPair(FNewCode, olin.Pairs[i].JsonValue.Clone as TJSONValue); - if FNewCode <> olin.Pairs[i].JsonString.Value then - FChanged := True; - end; - end; - - if not FChanged then - Exit; - - o2 := TJSONObject.Create; - for i := 0 to o.Count - 1 do - begin - if o.Pairs[i].JsonString.Value = 'languages' then - if Assigned(b) then - o2.AddPair(o.Pairs[i].JsonString.Value, b) - else - o2.AddPair(o.Pairs[i].JsonString.Value, ol) - else - o2.AddPair(o.Pairs[i].JsonString.Value, o.Pairs[i].JsonValue.Clone as TJSONValue); - end; - - if FChanged then - SaveJsonFile(o2, f); -end; - -procedure ProcessFolder(f: string); -var - ff: TSearchRec; -begin - if FindFirst(f+'\*', faDirectory, ff) = 0 then - begin - repeat - if (ff.Name <> '.') and (ff.Name <> '..') and ((ff.Attr and faDirectory) <> 0) then - ProcessFolder(f+'\'+ff.Name) - else if SameText(ExtractFileExt(ff.Name), '.keyboard_info') then - ProcessFile(f+'\'+ff.Name) - until FindNext(ff) <> 0; - FindClose(ff); - end; -end; - -procedure Run; -begin -// ProcessFile('c:\temp\convert-bcp47-for-keyboards\sample.keyboard_info'); - ProcessFolder('c:\projects\keyman\keyboards'); -end; - -end. diff --git a/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cvt.dpr b/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cvt.dpr deleted file mode 100644 index 975eda527d0..00000000000 --- a/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cvt.dpr +++ /dev/null @@ -1,31 +0,0 @@ -program cvt; - -{$APPTYPE CONSOLE} - -{$R *.res} - -uses - System.SysUtils, - cv in 'cv.pas', - Keyman.System.Standards.ISO6393ToBCP47Registry in 'C:\Projects\keyman\open\common\windows\delphi\standards\Keyman.System.Standards.ISO6393ToBCP47Registry.pas', - Keyman.System.KMXFileLanguages in 'C:\Projects\keyman\open\windows\src\global\delphi\keyboards\Keyman.System.KMXFileLanguages.pas', - utilstr in 'C:\Projects\keyman\open\common\windows\delphi\general\utilstr.pas', - Keyman.System.Standards.LCIDToBCP47Registry in 'C:\Projects\keyman\open\common\windows\delphi\standards\Keyman.System.Standards.LCIDToBCP47Registry.pas', - utilfiletypes in 'C:\Projects\keyman\open\common\windows\delphi\general\utilfiletypes.pas', - utildir in 'C:\Projects\keyman\open\common\windows\delphi\general\utildir.pas', - kmxfile in 'C:\Projects\keyman\open\common\windows\delphi\keyboards\kmxfile.pas', - KeyNames in 'C:\Projects\keyman\open\common\windows\delphi\general\KeyNames.pas', - KeymanVersion in 'C:\Projects\keyman\open\common\windows\delphi\general\KeymanVersion.pas', - StockFileNames in 'C:\Projects\keyman\open\windows\src\..\..\common\windows\delphi\general\StockFileNames.pas', - Unicode in 'C:\Projects\keyman\open\common\windows\delphi\general\Unicode.pas', - JsonUtil in 'C:\Projects\keyman\open\common\windows\delphi\general\JsonUtil.pas'; - -begin - try - Run; - { TODO -oUser -cConsole Main : Insert code here } - except - on E: Exception do - Writeln(E.ClassName, ': ', E.Message); - end; -end. diff --git a/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cvt.dproj b/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cvt.dproj deleted file mode 100644 index 2c70ec7ca61..00000000000 --- a/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cvt.dproj +++ /dev/null @@ -1,589 +0,0 @@ - - - {A273278D-9A16-4CEE-ACDA-89B30B8B61F1} - 18.2 - None - cvt.dpr - True - Debug - Win32 - 1 - Console - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Cfg_1 - true - true - - - true - Base - true - - - .\$(Platform)\$(Config) - .\$(Platform)\$(Config) - false - false - false - false - false - RESTComponents;FireDAC;FireDACSqliteDriver;soaprtl;FireDACIBDriver;soapmidas;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;$(DCC_UsePackage) - System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) - cvt - - - DBXSqliteDriver;bindcompdbx;IndyIPCommon;DBXInterBaseDriver;IndyIPServer;IndySystem;tethering;fmxFireDAC;bindcompfmx;FireDACPgDriver;inetdb;DbxCommonDriver;fmx;fmxdae;xmlrtl;fmxobj;rtl;DbxClientDriver;CustomIPTransport;dbexpress;IndyCore;bindcomp;dsnap;IndyIPClient;dbxcds;bindengine;DBXMySQLDriver;dsnapxml;FireDACMySQLDriver;dbrtl;inetdbxpress;IndyProtocols;FireDACCommonODBC;fmxase;$(DCC_UsePackage) - true - - - DBXSqliteDriver;bindcompdbx;IndyIPCommon;DBXInterBaseDriver;vcl;IndyIPServer;vclactnband;vclFireDAC;IndySystem;tethering;svnui;mbColorLibD10;dsnapcon;FireDACADSDriver;scFontCombo;DCPdelphi2009;FireDACMSAccDriver;fmxFireDAC;vclimg;Jcl;vcltouch;JvCore;vcldb;bindcompfmx;svn;FireDACPgDriver;inetdb;DbxCommonDriver;fmx;fmxdae;xmlrtl;fmxobj;vclwinx;rtl;DbxClientDriver;CustomIPTransport;vcldsnap;dbexpress;IndyCore;vclx;bindcomp;appanalytics;dsnap;IndyIPClient;bindcompvcl;EmbeddedWebBrowser_XE;VCLRESTComponents;dbxcds;VclSmp;JvDocking;adortl;JclVcl;vclie;bindengine;DBXMySQLDriver;dsnapxml;FireDACMySQLDriver;dbrtl;inetdbxpress;IndyProtocols;keyman_components;FireDACCommonODBC;fmxase;$(DCC_UsePackage) - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - Debug - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - 1033 - true - $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png - $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png - - - DBXSqliteDriver;bindcompdbx;IndyIPCommon;DBXInterBaseDriver;vcl;IndyIPServer;vclactnband;vclFireDAC;IndySystem;tethering;dsnapcon;FireDACADSDriver;FireDACMSAccDriver;fmxFireDAC;vclimg;Jcl;vcltouch;vcldb;bindcompfmx;FireDACPgDriver;inetdb;DbxCommonDriver;fmx;fmxdae;xmlrtl;fmxobj;vclwinx;rtl;DbxClientDriver;CustomIPTransport;vcldsnap;dbexpress;IndyCore;vclx;bindcomp;appanalytics;dsnap;IndyIPClient;bindcompvcl;VCLRESTComponents;dbxcds;VclSmp;adortl;JclVcl;vclie;bindengine;DBXMySQLDriver;dsnapxml;FireDACMySQLDriver;dbrtl;inetdbxpress;IndyProtocols;FireDACCommonODBC;fmxase;$(DCC_UsePackage) - true - $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png - $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png - - - DEBUG;$(DCC_Define) - true - false - true - true - true - - - false - - - false - RELEASE;$(DCC_Define) - 0 - 0 - - - - MainSource - - - - - - - - - - - - - - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - - - - Delphi.Personality.12 - Application - - - - cvt.dpr - - - - - - true - - - - - true - - - - - true - - - - - true - - - - - cvt.exe - true - - - - - 1 - - - Contents\MacOS - 1 - - - Contents\MacOS - 0 - - - - - classes - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\armeabi - 1 - - - - - library\lib\mips - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - res\drawable - 1 - - - - - res\values - 1 - - - - - res\drawable - 1 - - - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-ldpi - 1 - - - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - - - res\drawable-small - 1 - - - - - res\drawable-normal - 1 - - - - - res\drawable-large - 1 - - - - - res\drawable-xlarge - 1 - - - - - 1 - - - Contents\MacOS - 1 - - - 0 - - - - - Contents\MacOS - 1 - .framework - - - 0 - - - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - Contents\MacOS - 1 - .dylib - - - 0 - .dll;.bpl - - - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - Contents\MacOS - 1 - .dylib - - - 0 - .bpl - - - - - 0 - - - 0 - - - 0 - - - 0 - - - Contents\Resources\StartUp\ - 0 - - - 0 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - - - 1 - - - 1 - - - - - ..\ - 1 - - - ..\ - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - ..\ - 1 - - - - - Contents - 1 - - - - - Contents\Resources - 1 - - - - - library\lib\armeabi-v7a - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - Contents\MacOS - 1 - - - 0 - - - - - 1 - - - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - - - - - - - - - False - False - True - False - - - 12 - - - - - diff --git a/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cvt.res b/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cvt.res deleted file mode 100644 index 743599575b0..00000000000 Binary files a/windows/src/support/convert-iso6393-to-bcp47-for-keyboards/cvt.res and /dev/null differ diff --git a/windows/src/support/kdebug/Makefile b/windows/src/support/kdebug/Makefile index 2655466a54f..1a13a18ebe4 100644 --- a/windows/src/support/kdebug/Makefile +++ b/windows/src/support/kdebug/Makefile @@ -1,5 +1,5 @@ # -# KMCmpDll Makefile +# KDebug Makefile # !include ..\..\Defines.mak diff --git a/windows/src/support/km_yim/Makefile b/windows/src/support/km_yim/Makefile index f0f93e6fe22..1aef5f80f02 100644 --- a/windows/src/support/km_yim/Makefile +++ b/windows/src/support/km_yim/Makefile @@ -4,12 +4,13 @@ !include ..\..\Defines.mak -build: # version.res +build: +# version.res $(DCC32) km_yim.dpr rem $(TDSPACK) $(PROGRAM)\desktop\km_yim.exe km_yim.tds rem $(TDS2DBG) $(PROGRAM)\desktop\km_yim.exe $(WZZIP) inst_km_yim.zip km_yim.exe - # $(WZSE) inst_km_yim -setup -t inst_km_yim.dialog.txt -st "Tavultesoft Keyman Desktop Yahoo Messenger Addin" -c km_yim.exe +# $(WZSE) inst_km_yim -setup -t inst_km_yim.dialog.txt -st "Tavultesoft Keyman Desktop Yahoo Messenger Addin" -c km_yim.exe clean: def-clean if exist inst_km_yim.zip del inst_km_yim.zip diff --git a/windows/src/test/Makefile b/windows/src/test/Makefile index 5df2892dfad..2087f3d8a00 100644 --- a/windows/src/test/Makefile +++ b/windows/src/test/Makefile @@ -4,12 +4,14 @@ # ---------------------------------------------------------------------- +# TODO: both test-manifest-exec and verify_signatures are really part of buildtools + !ifdef NODELPHI TARGETS=.virtual !else -TARGETS=test_i3633 +TARGETS=verify_signatures !endif -CLEAN=test_i3633 +CLEAN=verify_signatures test: test-manifest-exec $(MAKE) "TARGET=test" $(TARGETS) @@ -22,10 +24,10 @@ test-manifest-exec: $(MAKE) test-manifest cd test -# test_i3633: validate certificates and binary metadata on executables -# TODO: Move this to buildtools -test_i3633: - cd $(ROOT)\src\test\test_i3633 +# validate certificates and binary metadata on executables +# TODO: move to buildtools? +verify_signatures: .virtual + cd $(ROOT)\src\test\verify_signatures $(MAKE) $(TARGET) # ---------------------------------------------------------------------- diff --git a/windows/src/test/manual-tests/GH-4275 - contextex-mismatch-with-if/context_mismatch_with_if/context_mismatch_with_if.keyboard_info b/windows/src/test/manual-tests/GH-4275 - contextex-mismatch-with-if/context_mismatch_with_if/context_mismatch_with_if.keyboard_info deleted file mode 100644 index 5ce314be007..00000000000 --- a/windows/src/test/manual-tests/GH-4275 - contextex-mismatch-with-if/context_mismatch_with_if/context_mismatch_with_if.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "din-Latn" - ], - "description": "context_mismatch_with_if generated from template" -} diff --git a/windows/src/test/manual-tests/GH-4275 - contextex-mismatch-with-if/context_mismatch_with_if/context_mismatch_with_if.kpj b/windows/src/test/manual-tests/GH-4275 - contextex-mismatch-with-if/context_mismatch_with_if/context_mismatch_with_if.kpj index 88a7bc90ba1..78dff6f55f8 100644 --- a/windows/src/test/manual-tests/GH-4275 - contextex-mismatch-with-if/context_mismatch_with_if/context_mismatch_with_if.kpj +++ b/windows/src/test/manual-tests/GH-4275 - contextex-mismatch-with-if/context_mismatch_with_if/context_mismatch_with_if.kpj @@ -51,13 +51,6 @@ .md - - id_1213256b2009e2b32ee7d551a63402c6 - context_mismatch_with_if.keyboard_info - context_mismatch_with_if.keyboard_info - - .keyboard_info - id_ea1380e11b2491a95b183d9ea2bcd813 context_mismatch_with_if.kmx diff --git a/windows/src/test/manual-tests/caps-lock-stores/caps_always_off/caps_always_off.keyboard_info b/windows/src/test/manual-tests/caps-lock-stores/caps_always_off/caps_always_off.keyboard_info deleted file mode 100644 index bb9f739f704..00000000000 --- a/windows/src/test/manual-tests/caps-lock-stores/caps_always_off/caps_always_off.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "en" - ], - "description": "Caps Always Off generated from template" -} diff --git a/windows/src/test/manual-tests/caps-lock-stores/caps_always_off/caps_always_off.kpj b/windows/src/test/manual-tests/caps-lock-stores/caps_always_off/caps_always_off.kpj index 095264252bb..a153f343167 100644 --- a/windows/src/test/manual-tests/caps-lock-stores/caps_always_off/caps_always_off.kpj +++ b/windows/src/test/manual-tests/caps-lock-stores/caps_always_off/caps_always_off.kpj @@ -91,12 +91,5 @@ .md - - id_60f11cd4dd88c1ad232f0099b86b071f - caps_always_off.keyboard_info - caps_always_off.keyboard_info - - .keyboard_info - diff --git a/windows/src/test/manual-tests/caps-lock-stores/capslock/capslock.keyboard_info b/windows/src/test/manual-tests/caps-lock-stores/capslock/capslock.keyboard_info deleted file mode 100644 index b05d23ac133..00000000000 --- a/windows/src/test/manual-tests/caps-lock-stores/capslock/capslock.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "en" - ], - "description": "Caps Lock generated from template" -} diff --git a/windows/src/test/manual-tests/caps-lock-stores/capslock/capslock.kpj b/windows/src/test/manual-tests/caps-lock-stores/capslock/capslock.kpj index 34c03b5ce43..3700856001e 100644 --- a/windows/src/test/manual-tests/caps-lock-stores/capslock/capslock.kpj +++ b/windows/src/test/manual-tests/caps-lock-stores/capslock/capslock.kpj @@ -51,13 +51,6 @@ .md - - id_621d2fc48f94f0e3862c706c37ff7a8b - capslock.keyboard_info - capslock.keyboard_info - - .keyboard_info - id_894c97d2a53dc029f8ca18fd3e0c2ff0 capslock.kmx diff --git a/windows/src/test/manual-tests/caps-lock-stores/shift_frees_caps/shift_frees_caps.keyboard_info b/windows/src/test/manual-tests/caps-lock-stores/shift_frees_caps/shift_frees_caps.keyboard_info deleted file mode 100644 index 4be7edab2c9..00000000000 --- a/windows/src/test/manual-tests/caps-lock-stores/shift_frees_caps/shift_frees_caps.keyboard_info +++ /dev/null @@ -1,7 +0,0 @@ -{ - "license": "mit", - "languages": [ - "en" - ], - "description": "CapsOnOnly and ShiftFreesCaps features generated from template" -} diff --git a/windows/src/test/manual-tests/caps-lock-stores/shift_frees_caps/shift_frees_caps.kpj b/windows/src/test/manual-tests/caps-lock-stores/shift_frees_caps/shift_frees_caps.kpj index 86e840119a8..1fd0f54cf1a 100644 --- a/windows/src/test/manual-tests/caps-lock-stores/shift_frees_caps/shift_frees_caps.kpj +++ b/windows/src/test/manual-tests/caps-lock-stores/shift_frees_caps/shift_frees_caps.kpj @@ -51,13 +51,6 @@ .md - - id_5e7df09f2deea5c5789153c0e7bca89f - shift_frees_caps.keyboard_info - shift_frees_caps.keyboard_info - - .keyboard_info - id_f5c18d42ae1a4a0cad4fa87bc4e65767 shift_frees_caps.kmx diff --git a/windows/src/test/manual-tests/regressiontest/tests/Makefile b/windows/src/test/manual-tests/regressiontest/tests/Makefile index 61931f73167..01f87053998 100644 --- a/windows/src/test/manual-tests/regressiontest/tests/Makefile +++ b/windows/src/test/manual-tests/regressiontest/tests/Makefile @@ -2,10 +2,10 @@ !include ..\..\..\Defines.mak all: - for %d in (*.kmn) do $(PROGRAM)\dev\kmcomp "%d" + for %d in (*.kmn) do $(PROGRAM)\bin\kmc.cmd build "%d" .kmn.kmx: - &$(PROGRAM)\dev\kmcomp $** + &$(PROGRAM)\bin\kmc.cmd build $** clean: -del *.kmx diff --git a/windows/src/test/manual-tests/test_httpuploader/test_httpuploader.dproj b/windows/src/test/manual-tests/test_httpuploader/test_httpuploader.dproj index d66af4279da..594c25faaf4 100644 --- a/windows/src/test/manual-tests/test_httpuploader/test_httpuploader.dproj +++ b/windows/src/test/manual-tests/test_httpuploader/test_httpuploader.dproj @@ -186,9 +186,6 @@ 'C:\Program Files\Microsoft Visual Studio 8\VC' 'C:\Program Files\Microsoft Visual Studio 8' - - 'C:\keyman\7.0\src\addins;C:\keyman\7.0\src\buildtools;C:\keyman\7.0\src\desktop;C:\keyman\7.0\src\developer;C:\keyman\7.0\src\engine;C:\keyman\7.0\src\global;C:\keyman\7.0\src\online;C:\keyman\7.0\src\support;C:\keyman\7.0\src\test;C:\keyman\7.0\src\addins\inst;C:\keyman\7.0\src\addins\kmtip;C:\keyman\7.0\src\addins\richedit;C:\keyman\7.0\src\addins\wordlink;C:\keyman\7.0\src\addins\inst\addininst;C:\keyman\7.0\src\addins\wordlink\dot;C:\keyman\7.0\src\addins\wordlink\wordlink_wll;C:\keyman\7.0\src\buildtools\buildunidata;C:\keyman\7.0\src\buildtools\cvscommit;C:\keyman\7.0\src\buildtools\delphiprojectmanager;C:\keyman\7.0\src\buildtools\devreg;C:\keyman\7.0\src\buildtools\genkmnp;C:\keyman\7.0\src\buildtools\getfilelocks;C:\keyman\7.0\src\buildtools\help;C:\keyman\7.0\src\buildtools\InitProductStatus;C:\keyman\7.0\src\buildtools\inst;C:\keyman\7.0\src\buildtools\isadmin;C:\keyman\7.0\src\buildtools\kmdebug;C:\keyman\7.0\src\buildtools\makecert;C:\keyman\7.0\src\buildtools\makeeval;C:\keyman\7.0\src\buildtools\makestockkct;C:\keyman\7.0\src\buildtools\mkver;C:\keyman\7.0\src\buildtools\msimsp;C:\keyman\7.0\src\buildtools\productstatus;C:\keyman\7.0\src\buildtools\stockeditor;C:\keyman\7.0\src\buildtools\test-checkcert;C:\keyman\7.0\src\buildtools\versionhistory;C:\keyman\7.0\src\buildtools\xslt;C:\keyman\7.0\src\buildtools\help\idx;C:\keyman\7.0\src\buildtools\help\img;C:\keyman\7.0\src\buildtools\help\inc;C:\keyman\7.0\src\buildtools\help\main;C:\keyman\7.0\src\desktop\branding;C:\keyman\7.0\src\desktop\help;C:\keyman\7.0\src\desktop\inst;C:\keyman\7.0\src\desktop\kmshell;C:\keyman\7.0\src\desktop\help\context;C:\keyman\7.0\src\desktop\help\idx;C:\keyman\7.0\src\desktop\help\img;C:\keyman\7.0\src\desktop\help\inc;C:\keyman\7.0\src\desktop\help\main;C:\keyman\7.0\src\desktop\help\rtf;C:\keyman\7.0\src\desktop\inst\Bitmaps;C:\keyman\7.0\src\desktop\kmshell\install;C:\keyman\7.0\src\desktop\kmshell\main;C:\keyman\7.0\src\desktop\kmshell\product;C:\keyman\7.0\src\desktop\kmshell\rel;C:\keyman\7.0\src\desktop\kmshell\render;C:\keyman\7.0\src\desktop\kmshell\startup;C:\keyman\7.0\src\desktop\kmshell\tsysinfo;C:\keyman\7.0\src\desktop\kmshell\uninstall;C:\keyman\7.0\src\desktop\kmshell\util;C:\keyman\7.0\src\desktop\kmshell\view;C:\keyman\7.0\src\desktop\kmshell\xml;C:\keyman\7.0\src\developer\help;C:\keyman\7.0\src\developer\inst;C:\keyman\7.0\src\developer\kmcmpdll;C:\keyman\7.0\src\developer\kmcomp;C:\keyman\7.0\src\developer\samples;C:\keyman\7.0\src\developer\stock;C:\keyman\7.0\src\developer\TIKE;C:\keyman\7.0\src\developer\uitemplates;C:\keyman\7.0\src\developer\help\context;C:\keyman\7.0\src\developer\help\guide;C:\keyman\7.0\src\developer\help\idx;C:\keyman\7.0\src\developer\help\img;C:\keyman\7.0\src\developer\help\imx;C:\keyman\7.0\src\developer\help\inc;C:\keyman\7.0\src\developer\help\main;C:\keyman\7.0\src\developer\help\reference;C:\keyman\7.0\src\developer\help\rtf;C:\keyman\7.0\src\developer\help\tutorial;C:\keyman\7.0\src\developer\inst\Bitmaps;C:\keyman\7.0\src\developer\inst\data;C:\keyman\7.0\src\developer\inst\devinsthelper;C:\keyman\7.0\src\developer\inst\fonts;C:\keyman\7.0\src\developer\inst\old;C:\keyman\7.0\src\developer\inst\prodinsthelp;C:\keyman\7.0\src\developer\inst\version_upgrade;C:\keyman\7.0\src\developer\inst\wix;C:\keyman\7.0\src\developer\inst\version_upgrade\old_cpp;C:\keyman\7.0\src\developer\inst\wix\Bitmaps;C:\keyman\7.0\src\developer\inst\wix\doc;C:\keyman\7.0\src\developer\inst\wix\examples;C:\keyman\7.0\src\developer\inst\wix\inc;C:\keyman\7.0\src\developer\inst\wix\lib;C:\keyman\7.0\src\developer\inst\wix\examples\first;C:\keyman\7.0\src\developer\inst\wix\examples\simple;C:\keyman\7.0\src\developer\inst\wix\examples\simple\bin;C:\keyman\7.0\src\developer\kmcmpdll\debug;C:\keyman\7.0\src\developer\kmcmpdll\Release;C:\keyman\7.0\src\developer\samples\Examples;C:\keyman\7.0\src\developer\samples\imsample;C:\keyman\7.0\src\developer\samples\Products;C:\keyman\7.0\src\developer\samples\Tests;C:\keyman\7.0\src\developer\samples\Products\LaoUnicode;C:\keyman\7.0\src\developer\samples\Products\LaoUnicode\LaoUnicode;C:\keyman\7.0\src\developer\samples\Products\LaoUnicode\LaoUnicode\bin;C:\keyman\7.0\src\developer\samples\Products\LaoUnicode\LaoUnicode\My Project;C:\keyman\7.0\src\developer\samples\Products\LaoUnicode\LaoUnicode\obj;C:\keyman\7.0\src\developer\samples\Products\LaoUnicode\LaoUnicode\bin\Debug;C:\keyman\7.0\src\developer\samples\Products\LaoUnicode\LaoUnicode\bin\Release;C:\keyman\7.0\src\developer\samples\Products\LaoUnicode\LaoUnicode\obj\Debug;C:\keyman\7.0\src\developer\samples\Products\LaoUnicode\LaoUnicode\obj\Release;C:\keyman\7.0\src\developer\samples\Products\LaoUnicode\LaoUnicode\obj\Debug\TempPE;C:\keyman\7.0\src\developer\samples\Products\LaoUnicode\LaoUnicode\obj\Release\TempPE;C:\keyman\7.0\src\developer\TIKE\actions;C:\keyman\7.0\src\developer\TIKE\activation;C:\keyman\7.0\src\developer\TIKE\child;C:\keyman\7.0\src\developer\TIKE\compile;C:\keyman\7.0\src\developer\TIKE\crm;C:\keyman\7.0\src\developer\TIKE\debug;C:\keyman\7.0\src\developer\TIKE\dialogs;C:\keyman\7.0\src\developer\TIKE\dockforms;C:\keyman\7.0\src\developer\TIKE\help;C:\keyman\7.0\src\developer\TIKE\kct;C:\keyman\7.0\src\developer\TIKE\main;C:\keyman\7.0\src\developer\TIKE\project;C:\keyman\7.0\src\developer\TIKE\rel;C:\keyman\7.0\src\developer\TIKE\tests;C:\keyman\7.0\src\developer\TIKE\xml;C:\keyman\7.0\src\developer\TIKE\kct\Editors;C:\keyman\7.0\src\developer\TIKE\kct\icons;C:\keyman\7.0\src\developer\TIKE\xml\help;C:\keyman\7.0\src\developer\TIKE\xml\product;C:\keyman\7.0\src\developer\TIKE\xml\project;C:\keyman\7.0\src\engine\inst;C:\keyman\7.0\src\engine\keyman;C:\keyman\7.0\src\engine\keyman32;C:\keyman\7.0\src\engine\kmcomapi;C:\keyman\7.0\src\engine\inst\insthelper;C:\keyman\7.0\src\engine\keyman\viskbd;C:\keyman\7.0\src\engine\keyman32\appint;C:\keyman\7.0\src\engine\kmcomapi\com;C:\keyman\7.0\src\engine\kmcomapi\idl;C:\keyman\7.0\src\engine\kmcomapi\processes;C:\keyman\7.0\src\engine\kmcomapi\util;C:\keyman\7.0\src\engine\kmcomapi\com\addins;C:\keyman\7.0\src\engine\kmcomapi\com\customisation;C:\keyman\7.0\src\engine\kmcomapi\com\errors;C:\keyman\7.0\src\engine\kmcomapi\com\hotkeys;C:\keyman\7.0\src\engine\kmcomapi\com\keyboards;C:\keyman\7.0\src\engine\kmcomapi\com\languages;C:\keyman\7.0\src\engine\kmcomapi\com\licences;C:\keyman\7.0\src\engine\kmcomapi\com\options;C:\keyman\7.0\src\engine\kmcomapi\com\packages;C:\keyman\7.0\src\engine\kmcomapi\com\products;C:\keyman\7.0\src\engine\kmcomapi\com\system;C:\keyman\7.0\src\engine\kmcomapi\processes\addin;C:\keyman\7.0\src\engine\kmcomapi\processes\font;C:\keyman\7.0\src\engine\kmcomapi\processes\keyboard;C:\keyman\7.0\src\engine\kmcomapi\processes\package;C:\keyman\7.0\src\engine\kmcomapi\processes\product;C:\keyman\7.0\src\engine\kmcomapi\processes\visualkeyboard;C:\keyman\7.0\src\global\delphi;C:\keyman\7.0\src\global\help;C:\keyman\7.0\src\global\inc;C:\keyman\7.0\src\global\inst;C:\keyman\7.0\src\global\res;C:\keyman\7.0\src\global\vc;C:\keyman\7.0\src\global\wix;C:\keyman\7.0\src\global\delphi\charmap;C:\keyman\7.0\src\global\delphi\comp;C:\keyman\7.0\src\global\delphi\crypt;C:\keyman\7.0\src\global\delphi\cust;C:\keyman\7.0\src\global\delphi\debug;C:\keyman\7.0\src\global\delphi\dragdrop;C:\keyman\7.0\src\global\delphi\excmagic;C:\keyman\7.0\src\global\delphi\folderdlg;C:\keyman\7.0\src\global\delphi\general;C:\keyman\7.0\src\global\delphi\htmlhelp;C:\keyman\7.0\src\global\delphi\hwkey;C:\keyman\7.0\src\global\delphi\indy;C:\keyman\7.0\src\global\delphi\jedi;C:\keyman\7.0\src\global\delphi\online;C:\keyman\7.0\src\global\delphi\plusmemou;C:\keyman\7.0\src\global\delphi\png;C:\keyman\7.0\src\global\delphi\productactivation;C:\keyman\7.0\src\global\delphi\tlb;C:\keyman\7.0\src\global\delphi\vcl;C:\keyman\7.0\src\global\delphi\vclzip;C:\keyman\7.0\src\global\delphi\visualkeyboard;C:\keyman\7.0\src\global\delphi\xdom;C:\keyman\7.0\src\global\delphi\xstringgrid;C:\keyman\7.0\src\global\delphi\comp\bitmapeditor;C:\keyman\7.0\src\global\delphi\crypt\Ciphers;C:\keyman\7.0\src\global\delphi\crypt\Docs;C:\keyman\7.0\src\global\delphi\crypt\Hashes;C:\keyman\7.0\src\global\delphi\excmagic\unit;C:\keyman\7.0\src\global\delphi\excmagic\unit\RC;C:\keyman\7.0\src\global\delphi\indy\Core;C:\keyman\7.0\src\global\delphi\indy\Protocols;C:\keyman\7.0\src\global\delphi\indy\SuperCore;C:\keyman\7.0\src\global\delphi\indy\System;C:\keyman\7.0\src\global\inst\data;C:\keyman\7.0\src\global\inst\data\Unihan;C:\keyman\7.0\src\global\res\70;C:\keyman\7.0\src\global\res\bmp;C:\keyman\7.0\src\global\res\cimu;C:\keyman\7.0\src\global\res\geezword;C:\keyman\7.0\src\global\res\keyman;C:\keyman\7.0\src\global\res\lswin;C:\keyman\7.0\src\global\res\paul;C:\keyman\7.0\src\global\res\70\desktop;C:\keyman\7.0\src\global\res\70\developer;C:\keyman\7.0\src\global\res\70\filetypes;C:\keyman\7.0\src\global\res\bmp\splash;C:\keyman\7.0\src\global\res\geezword\bmp;C:\keyman\7.0\src\global\res\geezword\ico;C:\keyman\7.0\src\global\res\geezword\other;C:\keyman\7.0\src\global\res\geezword\ico\16x16;C:\keyman\7.0\src\global\res\geezword\ico\32x32;C:\keyman\7.0\src\global\res\geezword\ico\unused;C:\keyman\7.0\src\global\res\keyman\bmp;C:\keyman\7.0\src\global\res\keyman\ico;C:\keyman\7.0\src\global\res\keyman\other;C:\keyman\7.0\src\global\res\keyman\bmp\startup;C:\keyman\7.0\src\global\res\keyman\bmp\tip;C:\keyman\7.0\src\global\res\keyman\ico\16x16;C:\keyman\7.0\src\global\res\keyman\ico\32x32;C:\keyman\7.0\src\global\res\keyman\ico\unused;C:\keyman\7.0\src\global\res\lswin\bmp;C:\keyman\7.0\src\global\res\lswin\ico;C:\keyman\7.0\src\global\res\lswin\other;C:\keyman\7.0\src\global\res\lswin\ico\16x16;C:\keyman\7.0\src\global\res\lswin\ico\32x32;C:\keyman\7.0\src\global\res\lswin\ico\unused;C:\keyman\7.0\src\global\res\paul\dev-images;C:\keyman\7.0\src\global\res\paul\dev-images\6.0-icons;C:\keyman\7.0\src\global\res\paul\dev-images\filetypes;C:\keyman\7.0\src\global\res\paul\dev-images\icons;C:\keyman\7.0\src\global\res\paul\dev-images\install;C:\keyman\7.0\src\global\res\paul\dev-images\keyman;C:\keyman\7.0\src\global\res\paul\dev-images\tike;C:\keyman\7.0\src\global\res\paul\dev-images\6.0-icons\16x16;C:\keyman\7.0\src\global\res\paul\dev-images\6.0-icons\32x32;C:\keyman\7.0\src\global\res\paul\dev-images\6.0-icons\unused;C:\keyman\7.0\src\global\res\paul\dev-images\keyman\icons;C:\keyman\7.0\src\global\res\paul\dev-images\keyman\xml;C:\keyman\7.0\src\global\res\paul\dev-images\keyman\xml\img;C:\keyman\7.0\src\global\res\paul\dev-images\keyman\xml\kbimg;C:\keyman\7.0\src\global\res\paul\dev-images\tike\xml;C:\keyman\7.0\src\global\res\paul\dev-images\tike\xml\kmg;C:\keyman\7.0\src\global\wix\wixui;C:\keyman\7.0\src\global\wix\wixui\Bitmaps;C:\keyman\7.0\src\global\wix\wixui\featuretree;C:\keyman\7.0\src\global\wix\wixui\installdir;C:\keyman\7.0\src\global\wix\wixui\minimal;C:\keyman\7.0\src\global\wix\wixui\mondo;C:\keyman\7.0\src\online\certificate;C:\keyman\7.0\src\online\k61licence;C:\keyman\7.0\src\online\kencrypt;C:\keyman\7.0\src\online\klicence;C:\keyman\7.0\src\support\braziliantest;C:\keyman\7.0\src\support\imtest;C:\keyman\7.0\src\support\kdebug;C:\keyman\7.0\src\support\keycodetester;C:\keyman\7.0\src\support\kmdecomp;C:\keyman\7.0\src\support\richedit_examine;C:\keyman\7.0\src\support\testcryptdepend;C:\keyman\7.0\src\support\windowinfo;C:\keyman\7.0\src\support\xstringtest;C:\keyman\7.0\src\test\regressiontest;C:\keyman\7.0\src\test\test-comapi;C:\keyman\7.0\src\test\test_alphanumcode;C:\keyman\7.0\src\test\test_uuidgen;C:\keyman\7.0\src\test\regressiontest\tests;C:\keyman\7.0\src\test\test_uuidgen\del_uuidgen;C:\keyman\7.0\src\test\test_uuidgen\vc_uuidgen;C:\keyman\7.0\src\test\test_uuidgen\vc_uuidgen\vc_uuidgen' - $00000C09 diff --git a/windows/src/test/test_i3633/Makefile b/windows/src/test/test_i3633/Makefile deleted file mode 100644 index 575d77e7e83..00000000000 --- a/windows/src/test/test_i3633/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# -# test for signatures and version information being correct in ?install directory or ?bin directory -# - -!include ..\..\Defines.mak - -SIGCHECK=-$(WIN32_TARGET_PATH)\sigcheck -q -s -e -v -accepteula -VERIFY=$(WIN32_TARGET_PATH)\verify -d $(KEYMAN_ROOT)\VERSION.md - -test: test-bin - -test-bin: prereq - $(SIGCHECK) $(ROOT)\bin\desktop\* > sig1 - $(SIGCHECK) $(ROOT)\bin\developer\* >> sig1 - $(SIGCHECK) $(ROOT)\bin\engine\* >> sig1 - $(SIGCHECK) $(ROOT)\bin\inst\* >> sig1 - $(VERIFY) < sig1 - -test-install: prereq - - -prereq: - $(DELPHI_MSBUILD) verify.dproj "/p:Platform=Win32" - copy sigcheck.bin $(WIN32_TARGET_PATH)\sigcheck.exe - - -clean: def-clean - -del sig1 - -del sig2 - -!include ..\..\Target.mak diff --git a/windows/src/test/test_i3633/verify.res b/windows/src/test/test_i3633/verify.res deleted file mode 100644 index 6876088a664..00000000000 Binary files a/windows/src/test/test_i3633/verify.res and /dev/null differ diff --git a/windows/src/test/verify_signatures/Makefile b/windows/src/test/verify_signatures/Makefile new file mode 100644 index 00000000000..607e5d22cf9 --- /dev/null +++ b/windows/src/test/verify_signatures/Makefile @@ -0,0 +1,21 @@ +# +# test for signatures and version information being correct in bin folder +# + +!include ..\..\Defines.mak + +test: prereq + $(SIGCHECK) $(ROOT)\bin\desktop\* > sig1 + $(SIGCHECK) $(ROOT)\bin\engine\* >> sig1 + $(SIGCHECK) $(ROOT)\bin\inst\* >> sig1 + $(VERIFY_SIGNATURES) < sig1 + +# prereq may not be needed? +prereq: + cd $(VERIFY_SIGNATURES_PATH) + $(MAKE) + +clean: def-clean + -del sig1 + +!include ..\..\Target.mak