mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-12 03:45:34 +00:00
chore(web): Merge branch 'refactor/web/transcription-cache' into feat/web/test-kbd-prototyping
This commit is contained in:
commit
bfb31a6918
216 changed files with 4590 additions and 1307 deletions
14
.github/workflows/deb-packaging.yml
vendored
14
.github/workflows/deb-packaging.yml
vendored
|
|
@ -105,10 +105,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
# Currently not building mantic until ibus version on mantic stabilizied
|
||||
# and we can provide a patched version
|
||||
# dist: [focal, jammy, lunar, mantic]
|
||||
dist: [focal, jammy, lunar]
|
||||
dist: [focal, jammy, lunar, mantic]
|
||||
arch: [amd64]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -222,7 +219,7 @@ jobs:
|
|||
echo "::endgroup::"
|
||||
|
||||
api_verification:
|
||||
name: Verify API for libkmnkbp0.so
|
||||
name: Verify API for libkeymancore.so
|
||||
needs: [sourcepackage, binary_packages]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
|
@ -245,16 +242,17 @@ jobs:
|
|||
- name: Verify API
|
||||
run: |
|
||||
cd linux
|
||||
PKG_NAME=libkeymancore
|
||||
SRC_PKG="${GITHUB_WORKSPACE}/artifacts/keyman-srcpkg/keyman_${{ needs.sourcepackage.outputs.VERSION }}-1.debian.tar.xz" \
|
||||
BIN_PKG="${GITHUB_WORKSPACE}/artifacts/keyman-binarypkgs/libkmnkbp0-0_${{ needs.sourcepackage.outputs.VERSION }}-1${{ needs.sourcepackage.outputs.PRERELEASE_TAG }}+jammy1_amd64.deb" \
|
||||
BIN_PKG="${GITHUB_WORKSPACE}/artifacts/keyman-binarypkgs/${PKG_NAME}_${{ needs.sourcepackage.outputs.VERSION }}-1${{ needs.sourcepackage.outputs.PRERELEASE_TAG }}+jammy1_amd64.deb" \
|
||||
PKG_VERSION="${{ needs.sourcepackage.outputs.VERSION }}" \
|
||||
./scripts/deb-packaging.sh --gha verify 2>> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Archive .symbols file
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: libkmnkbp0-0.symbols
|
||||
path: linux/debian/libkmnkbp0-0.symbols
|
||||
name: libkeymancore.symbols
|
||||
path: linux/debian/libkeymancore.symbols
|
||||
if: always()
|
||||
|
||||
set_status:
|
||||
|
|
|
|||
90
HISTORY.md
90
HISTORY.md
|
|
@ -1,5 +1,76 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 17.0.199 alpha 2023-10-26
|
||||
|
||||
* fix(developer): handle xml errors in package compiler (#9821)
|
||||
* fix(developer): server download Keyman link (#9822)
|
||||
* chore(common): handle invalid XML in kpj-file-reader (#9824)
|
||||
* fix(developer): reduce confusion in Unicode fields in touch layout editor (#9839)
|
||||
|
||||
## 17.0.198 alpha 2023-10-25
|
||||
|
||||
* chore(common): Add entries from 16.0 HISTORY.md (#9826)
|
||||
* feat(core): new actions APIs (#9828)
|
||||
|
||||
## 17.0.197 alpha 2023-10-24
|
||||
|
||||
* chore(linux): Rename (lib)kmnkbp to (lib)keymancore ️ (#9793)
|
||||
* chore(linux): Rename `namespace kbp` to `core` ️ (#9792)
|
||||
* chore(linux): rename keyboardprocessor_ldml.* to keyman_core_ldml ️ (#9791)
|
||||
* chore(linux): Rename libkmnkbp0-0 package ️ (#9795)
|
||||
* fix(web): proper disabling of prediction timeout for prediction unit tests (#9835)
|
||||
|
||||
## 17.0.196 alpha 2023-10-20
|
||||
|
||||
* fix(mac): move keyboard menu items to main Input Menu from submenu (#9777)
|
||||
* (#9728)
|
||||
* (#9759)
|
||||
* docs(common): macos build update (#9809)
|
||||
* fix(web): enhances integrated test stability (#9718)
|
||||
* chore(linux): Update packaging GHA ️ (#9812)
|
||||
* chore(linux): Rename KBP to CORE ️ (#9794)
|
||||
* chore(linux): Fix build by adding one character (#9817)
|
||||
|
||||
## 17.0.195 alpha 2023-10-19
|
||||
|
||||
* chore(linux): Allow to collect coverage on TC (#9790)
|
||||
* fix(common): don't use URL in common/web/types (#9798)
|
||||
* chore: update kmp.schema.json and docs for kps schema (#9800)
|
||||
* docs(windows): update text and images for windows 11 (#9689)
|
||||
|
||||
## 17.0.194 alpha 2023-10-18
|
||||
|
||||
* chore(linux): Re-enable building for Ubuntu 23.10 Mantic (#9780)
|
||||
* chore(linux): Add missing tests (#9783)
|
||||
* fix(web): fixes touch form-factor default kbd on cookieless keymanweb.com page load (#9786)
|
||||
* fix(developer): three kmc .keyboard_info generation bugs (#9784)
|
||||
* fix(developer): handle invalid project folders cleanly (#9785)
|
||||
* chore(linux): Fix build scripts (#9781)
|
||||
|
||||
## 17.0.193 alpha 2023-10-17
|
||||
|
||||
* fix(developer): kmc crash on start (#9771)
|
||||
* fix(core): don't use double newlines in debuglog (#9258)
|
||||
* chore(linux): Add code coverage index page (#9758)
|
||||
* feat(linux): Allow installing keyboards with arbitrary language ️ (#9756)
|
||||
* fix(linux): Fix crash initializing Sentry with Python < 3.10 (#9774)
|
||||
* chore(linux): Rename `kbp_state_get_intermediate_context` to `km_core…` ️ (#9775)
|
||||
|
||||
## 17.0.192 alpha 2023-10-16
|
||||
|
||||
* fix(ios): missing backslash in build script (#9765)
|
||||
* chore(linux): Speed up ibus-util tests (#9754)
|
||||
* feat(linux): Allow loading of keyboards with arbitrary language ️ (#9735)
|
||||
|
||||
## 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)
|
||||
|
|
@ -1099,6 +1170,22 @@
|
|||
* chore: move to 17.0-alpha (#7577)
|
||||
* chore: Move to 17.0 alpha
|
||||
|
||||
## 16.0.142 stable 2023-10-20
|
||||
|
||||
* chore(linux): Update debian changelog (#9326)
|
||||
* chore(linux): Remove obsolete dist from uploading to launchpad (#9340)
|
||||
* chore(linux): Stop building Kinetic on Jenkins (#9354)
|
||||
* chore(linux): Work around build failure on Debian mips64el (#9342)
|
||||
* chore(linux): Update debian changelog (#9359)
|
||||
* chore(android): Update targetSDKVersion to 33 (#9494)
|
||||
* chore(linux): Remove workaround for mips64el (#9519)
|
||||
* chore(linux): Workaround: Don't run Wayland tests (#9530)
|
||||
* chore(linux): Add `clean` target to `rules` (#9532)
|
||||
* fix(linux): Prevent exception if neither USER, LOGNAME nor SUDO_USER set (#9544)
|
||||
* chore(linux): Ignore tests that fail on ba-jammy-64-ta (#9556)
|
||||
* chore(ios): renew certificate (#9811)
|
||||
* fix(mac): move keyboard menu items to main Input Menu from submenu (#9810)
|
||||
|
||||
## 16.0.141 stable 2023-07-25
|
||||
|
||||
* chore(mac): fix corrupt installer (#9332)
|
||||
|
|
@ -1486,6 +1573,7 @@
|
|||
* fix(linux): Fix upload script (#7494)
|
||||
* docs(linux): Update sample vscode settings (#7491)
|
||||
* fix(android/engine): Dismiss key preview and subkeys on globe action (#7472)
|
||||
* refactor(linux): Cleanup whitespace (#7500)
|
||||
* chore(linux): Improve debug output (#7499)
|
||||
|
||||
## 16.0.84 alpha 2022-10-19
|
||||
|
|
@ -1773,7 +1861,7 @@
|
|||
## 16.0.34 alpha 2022-07-15
|
||||
|
||||
* fix(developer): set contextDevice in web debugger ️ (#6906)
|
||||
* fix(web): postkeystroke processing should ignore key-event source ️ (#6901)
|
||||
* fix(web): postkeystroke processing should ignore key-event source (#6901)
|
||||
* fix(web): layer-setting ops should not trigger for hardware keystroke processing ️ (#6902)
|
||||
* fix(developer): kmdecomp virtual character key output (#6940)
|
||||
* fix(developer): crash on exit when checking for updates (#6941)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
17.0.190
|
||||
17.0.200
|
||||
|
|
@ -25,10 +25,10 @@ typedef KMX_WORD __attribute__((aligned(1))) KMX_WORD_unaligned;
|
|||
#define KMX_WORD_unaligned KMX_WORD
|
||||
#endif
|
||||
|
||||
#ifdef KMN_KBP
|
||||
#ifdef KM_CORE_LIBRARY
|
||||
// TODO: move this to a common namespace keyman::common::kmx_file or similar in the future
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
#endif
|
||||
|
||||
|
|
@ -402,8 +402,8 @@ static_assert(sizeof(COMP_KEY) == KEYBOARDFILEKEY_SIZE, "COMP_KEY must be KEYBOA
|
|||
static_assert(sizeof(COMP_GROUP) == KEYBOARDFILEGROUP_SIZE, "COMP_GROUP must be KEYBOARDFILEGROUP_SIZE bytes");
|
||||
static_assert(sizeof(COMP_KEYBOARD) == KEYBOARDFILEHEADER_SIZE, "COMP_KEYBOARD must be KEYBOARDFILEHEADER_SIZE bytes");
|
||||
|
||||
#ifdef KMN_KBP
|
||||
#ifdef KM_CORE_LIBRARY
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -12,7 +12,19 @@ Documentation at https://help.keyman.com/developer/cloud/keyboard_info
|
|||
# .keyboard_info version history
|
||||
|
||||
## 2023-08-11 2.0 stable
|
||||
* Removed legacyId, documentationFilename, documentationFileSize. Source vs distribution keyboard_info distinction is removed. Example key sequences are simplified.
|
||||
* 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.).
|
||||
|
|
|
|||
24
common/schemas/kmp/README.md
Normal file
24
common/schemas/kmp/README.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# kmp.schema.json
|
||||
|
||||
* kmp.json file format, metadata included in Keyman .kmp package files
|
||||
|
||||
Documentation at https://help.keyman.com/developer/current-version/reference/file-types/metadata
|
||||
|
||||
# kmp.schema.json version history
|
||||
|
||||
## 2023-10-19 2.0
|
||||
* Add relatedPackages, options.licenseFile, options.welcomeFile,
|
||||
keyboard.examples, keyboard.webOskFonts, keyboard.webDisplayFonts,
|
||||
info.description (all of these formerly were stored in .keyboard_info)
|
||||
|
||||
## 2019-01-31 1.1.0
|
||||
* Add lexicalModels properties (note: `version` is optional and currently unused)
|
||||
|
||||
## 2018-02-13 1.0.2
|
||||
* Add rtl property for keyboard layouts
|
||||
|
||||
## 2018-01-22 1.0.1
|
||||
* Remove id field as it is derived from the filename anyway
|
||||
|
||||
## 2017-11-30 1.0 beta
|
||||
* Initial version
|
||||
353
common/schemas/kmp/kmp.schema.json
Normal file
353
common/schemas/kmp/kmp.schema.json
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"$ref": "#/definitions/package",
|
||||
"definitions": {
|
||||
"package": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"system": {
|
||||
"$ref": "#/definitions/system"
|
||||
},
|
||||
"options": {
|
||||
"$ref": "#/definitions/options"
|
||||
},
|
||||
"startMenu": {
|
||||
"$ref": "#/definitions/startMenu"
|
||||
},
|
||||
"strings": {
|
||||
"$ref": "#/definitions/strings"
|
||||
},
|
||||
"files": {
|
||||
"$ref": "#/definitions/files"
|
||||
},
|
||||
"keyboards": {
|
||||
"$ref": "#/definitions/keyboards"
|
||||
},
|
||||
"lexicalModels": {
|
||||
"$ref": "#/definitions/lexicalModels"
|
||||
},
|
||||
"info": {
|
||||
"$ref": "#/definitions/info"
|
||||
},
|
||||
"relatedPackages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/relatedPackage"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"options",
|
||||
"system"
|
||||
]
|
||||
},
|
||||
"system": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"keymanDeveloperVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"fileVersion": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"fileVersion"
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"readmeFile": {
|
||||
"type": "string"
|
||||
},
|
||||
"graphicFile": {
|
||||
"type": "string"
|
||||
},
|
||||
"licenseFile": {
|
||||
"type": "string"
|
||||
},
|
||||
"welcomeFile": {
|
||||
"type": "string"
|
||||
},
|
||||
"executeProgram": {
|
||||
"type": "string"
|
||||
},
|
||||
"msiFilename": {
|
||||
"type": "string"
|
||||
},
|
||||
"msiOptions": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"startMenu": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"folder": {
|
||||
"type": "string"
|
||||
},
|
||||
"addUninstallEntry": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/startMenuItem"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"startMenuItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"filename": {
|
||||
"type": "string"
|
||||
},
|
||||
"arguments": {
|
||||
"type": "string"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string"
|
||||
},
|
||||
"location": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"filename"
|
||||
]
|
||||
},
|
||||
"strings": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
".": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"files": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/file"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"copyLocation": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"description"
|
||||
]
|
||||
},
|
||||
"keyboards": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/keyboard"
|
||||
}
|
||||
},
|
||||
"keyboard": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"oskFont": {
|
||||
"type": "string"
|
||||
},
|
||||
"displayFont": {
|
||||
"type": "string"
|
||||
},
|
||||
"rtl": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"languages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/keyboardLanguage"
|
||||
}
|
||||
},
|
||||
"examples": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/keyboardExample"
|
||||
}
|
||||
},
|
||||
"webOskFonts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"webDisplayFonts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"id",
|
||||
"version"
|
||||
]
|
||||
},
|
||||
"keyboardLanguage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"id",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"website": {
|
||||
"$ref": "#/definitions/infoItem"
|
||||
},
|
||||
"version": {
|
||||
"$ref": "#/definitions/infoItem"
|
||||
},
|
||||
"name": {
|
||||
"$ref": "#/definitions/infoItem"
|
||||
},
|
||||
"copyright": {
|
||||
"$ref": "#/definitions/infoItem"
|
||||
},
|
||||
"author": {
|
||||
"$ref": "#/definitions/infoItem"
|
||||
},
|
||||
"description": {
|
||||
"$ref": "#/definitions/infoItem"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"infoItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"description"
|
||||
]
|
||||
},
|
||||
"lexicalModels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/lexicalModel"
|
||||
}
|
||||
},
|
||||
"lexicalModel": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"rtl": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"languages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/keyboardLanguage"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"id",
|
||||
"languages"
|
||||
]
|
||||
},
|
||||
"keyboardExample": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"keys": {
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"note": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"id",
|
||||
"keys"
|
||||
]
|
||||
},
|
||||
"relatedPackage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"relationship": {
|
||||
"type": "string",
|
||||
"enum": ["deprecates", "related"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"id",
|
||||
"relationship"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,22 @@
|
|||
# kps.xsd
|
||||
|
||||
Master version: https://github.com/keymanapp/api.keyman.com/blob/master/schemas/kps/7.0/kps.xsd
|
||||
Master version: https://github.com/keymanapp/api.keyman.com/blob/master/schemas/kps/17.0/kps.xsd
|
||||
|
||||
## 2023-10-19 17.0
|
||||
* Version 17.0 adds:
|
||||
- LicenseFile - a .md file, usually named LICENSE.md
|
||||
- WelcomeFile - a .htm file, usually named welcome.htm (later versions will support .md)
|
||||
- Info/Description - a short Markdown description of the content of the package, e.g. shown in search results on keyman.com
|
||||
- RelatedPackages - a list of other packages which relate to this one, or are deprecated by it
|
||||
- Keyboards/Keyboard/Examples - a list of typing examples for the keyboard
|
||||
- Keyboarsd/Keyboard/WebOSKFonts - a list of font filenames (not necessarily in package) suitable for rendering the on screen keyboard
|
||||
- Keyboarsd/Keyboard/WebDisplayFonts - a list of font filenames (not necessarily in package) suitable for use with the keyboard
|
||||
* Version 17.0 removes:
|
||||
- LexicalModels/LexicalModel/Version - version information is not stored in the models, but only in the package metadata (was unused)
|
||||
|
||||
## 2023-04-21 7.0.1
|
||||
* Removes LexicalModel.Version, as it was never read or written
|
||||
|
||||
## 2021-07-19 7.0
|
||||
* Initial version 7.0
|
||||
|
||||
## 2023-04-21 7.0.1
|
||||
* Removes LexicalModel.Version, as it was never read or written
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
private pushedSelector?: MatcherSelector<Type, StateToken>;
|
||||
|
||||
private gestureConfig: GestureModelDefs<Type, StateToken>;
|
||||
private markedComplete: boolean = false;
|
||||
|
||||
// Note: the first stage will be available under `stageReports` after awaiting a simple Promise.resolve().
|
||||
constructor(
|
||||
|
|
@ -110,6 +111,16 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
this.selector = selector;
|
||||
this.selector.on('rejectionwithaction', this.modelResetHandler);
|
||||
this.once('complete', () => {
|
||||
if(this.pushedSelector) {
|
||||
// The `popSelector` method is responsible for triggering cascading cancellations if
|
||||
// there are nested GestureSequences.
|
||||
//
|
||||
// As this tends to affect which gestures are permitted, it's important this is done
|
||||
// any time the GestureSequence is cancelled or completed, for any reason.
|
||||
this.touchpointCoordinator?.popSelector(this.pushedSelector);
|
||||
this.pushedSelector = null;
|
||||
}
|
||||
|
||||
this.selector.off('rejectionwithaction', this.modelResetHandler);
|
||||
this.selector.dropSourcesWithIds(this.allSourceIds);
|
||||
|
||||
|
|
@ -193,13 +204,10 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
});
|
||||
|
||||
if(!selection.result.matched) {
|
||||
if(this.pushedSelector) {
|
||||
// The `popSelector` method is responsible for triggering cascading cancellations if
|
||||
// there are nested GestureSequences.
|
||||
this.touchpointCoordinator?.popSelector(this.pushedSelector);
|
||||
if(!this.markedComplete) {
|
||||
this.markedComplete = true;
|
||||
this.emit('complete');
|
||||
}
|
||||
|
||||
this.emit('complete');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -276,13 +284,11 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if(this.pushedSelector) {
|
||||
this.touchpointCoordinator?.popSelector(this.pushedSelector);
|
||||
this.pushedSelector = null;
|
||||
}
|
||||
|
||||
// Any extra finalization stuff should go here, before the event, if needed.
|
||||
this.emit('complete');
|
||||
if(!this.markedComplete) {
|
||||
this.markedComplete = true;
|
||||
this.emit('complete');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +316,11 @@ export class GestureSequence<Type, StateToken = any> extends EventEmitter<EventM
|
|||
|
||||
public cancel() {
|
||||
const sources = this.stageReports[this.stageReports.length - 1].sources;
|
||||
sources.forEach((src) => src.terminate(true));
|
||||
sources.forEach((src) => src.baseSource.isPathComplete || src.baseSource.terminate(true));
|
||||
if(!this.markedComplete) {
|
||||
this.markedComplete = true;
|
||||
this.emit('complete');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -428,22 +428,10 @@ export class MatcherSelector<Type, StateToken = any> extends EventEmitter<EventM
|
|||
return async (result: MatchResult<Type>) => {
|
||||
// Note: is only called by GestureMatcher Promises that are resolving.
|
||||
|
||||
/*
|
||||
* If we already had a gesture stage match, this will have already been fulfilled;
|
||||
* bypass all match-handling. Capturing `matchSynchronization` in a closure in this
|
||||
* manner is important to ensure that the returned handler is "locked" to the
|
||||
* currently-processing gesture stage.
|
||||
*/
|
||||
for(let synchronizer of matchSynchronizers) {
|
||||
if(synchronizer.isFulfilled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Do not bypass match handling just because a synchronization promise is fulfilled.
|
||||
// If a source was force-cancelled, cascading to a call of this handler, we still
|
||||
// need to perform internal state cleanup.
|
||||
|
||||
/* If cancellation was requested but not pre-filtered by the synchronizer setup, replace
|
||||
* the result object. The matcher's Promise may have resolved simultaneously with the
|
||||
* winner but 'lost', a scenario that may require careful handling to clean up.
|
||||
*/
|
||||
if(matcher.isCancelled) {
|
||||
result = {
|
||||
matched: false,
|
||||
|
|
@ -474,11 +462,6 @@ export class MatcherSelector<Type, StateToken = any> extends EventEmitter<EventM
|
|||
return;
|
||||
}
|
||||
|
||||
if(matcher.isCancelled) {
|
||||
// Fortunately, the rest of the code will help us recover from the state.
|
||||
console.warn("Unexpected state: a cancelled GestureMatcher was still listed as a possibility");
|
||||
}
|
||||
|
||||
this.potentialMatchers.splice(matcherIndex, 1);
|
||||
|
||||
/*
|
||||
|
|
@ -583,14 +566,6 @@ export class MatcherSelector<Type, StateToken = any> extends EventEmitter<EventM
|
|||
|
||||
/*
|
||||
* Fulfills the contract set by `matchGesture`.
|
||||
*
|
||||
* Also, fulfilling the ManagedPromise acts as a synchronizer, partially facilitating the
|
||||
* guarantee at the start of this closure. It's set synchronously, so other gesture-matchers
|
||||
* that call into this method will know that a match has already fulfilled for the matched
|
||||
* source(s). Any further matchers will be silently ignored, effectively cancelling them.
|
||||
* However, this fails to handle the case where two separate calls to matcherSelectionFilter
|
||||
* occur for the same matcher due to one source being added at a later point in time;
|
||||
* this is what the `cancel`
|
||||
*/
|
||||
tracker.matchPromise.resolve({matcher, result});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,12 @@ export class TouchpointCoordinator<HoveredItemType, StateToken=any> extends Even
|
|||
|
||||
touchpoint.setGestureMatchInspector(buildGestureMatchInspector(selector));
|
||||
|
||||
// We wait for the source to fully pass through the gesture-model spin-up phase; there's a chanc
|
||||
/* We wait for the source to fully pass through the gesture-model spin-up phase; there's
|
||||
* a chance that the new source will complete an existing gesture instantly without being
|
||||
* locked to it, resulting in activation of a different `stateToken`.
|
||||
*
|
||||
* This, in turn, can affect what the initial 'item' for the new gesture will be.
|
||||
*/
|
||||
const modelingSpinupPromise = selector.matchGesture(touchpoint, getGestureModelSet(modelDefs, selector.baseGestureSetId));
|
||||
modelingSpinupPromise.then(async (selectionPromiseHost) => {
|
||||
this.emit('inputstart', touchpoint);
|
||||
|
|
@ -181,6 +186,15 @@ export class TouchpointCoordinator<HoveredItemType, StateToken=any> extends Even
|
|||
touchpoint.path.on('invalidated', () => {
|
||||
// GestureSequence _should_ handle any other cleanup internally as fallout
|
||||
// from the path being cancelled.
|
||||
//
|
||||
// That said, it's handled asynchronously... but we can give a synchronous signal
|
||||
// through the next block of code, allowing cleanup to occur earlier during
|
||||
// recovery states.
|
||||
|
||||
const owningSequence = this.activeGestures.find((entry) => entry.allSourceIds.includes(touchpoint.identifier));
|
||||
if(owningSequence) {
|
||||
owningSequence.cancel();
|
||||
}
|
||||
|
||||
// To consider: should it specially mark if it 'completed' due to cancellation,
|
||||
// or is that safe to infer from the tracked GestureSource(s)?
|
||||
|
|
@ -191,8 +205,6 @@ export class TouchpointCoordinator<HoveredItemType, StateToken=any> extends Even
|
|||
this._activeSources = this._activeSources.splice(i, 1);
|
||||
});
|
||||
touchpoint.path.on('complete', () => {
|
||||
// TODO: on cancellation, is there any other cleanup to be done?
|
||||
|
||||
// Also mark the touchpoint as no longer active.
|
||||
let i = this._activeSources.indexOf(touchpoint);
|
||||
this._activeSources = this._activeSources.splice(i, 1);
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ export class TouchEventEngine<HoveredItemType, StateToken = any> extends InputEv
|
|||
const newTouches = touchListToArray(event.changedTouches);
|
||||
// Maintain all touches in the `.touches` array that are NOT marked as `.changedTouches` (and therefore, new)
|
||||
this.maintainTouchpointsWithIds(allTouches
|
||||
.filter((touch) => (newTouches.indexOf(touch) == -1))
|
||||
.filter((touch1) => newTouches.findIndex(touch2 => touch1.identifier == touch2.identifier) == -1)
|
||||
.map((touch) => touch.identifier)
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import Codes from "../text/codes.js";
|
||||
import KeyEvent, { KeyEventSpec } from "../text/keyEvent.js";
|
||||
import KeyMapping from "../text/keyMapping.js";
|
||||
import { Layouts } from "./defaultLayouts.js";
|
||||
import type { KeyDistribution } from "../text/keyEvent.js";
|
||||
import { ButtonClasses, Layouts } from "./defaultLayouts.js";
|
||||
import type { LayoutKey, LayoutSubKey, LayoutRow, LayoutLayer, LayoutFormFactor, ButtonClass } from "./defaultLayouts.js";
|
||||
import type Keyboard from "./keyboard.js";
|
||||
|
||||
|
|
@ -69,7 +70,7 @@ export class ActiveKeyBase {
|
|||
static readonly DEFAULT_KEY = {
|
||||
text: '',
|
||||
width: ActiveKeyBase.DEFAULT_KEY_WIDTH,
|
||||
sp: Layouts.buttonClasses.DEFAULT,
|
||||
sp: ButtonClasses.normal,
|
||||
pad: ActiveKeyBase.DEFAULT_PAD
|
||||
};
|
||||
|
||||
|
|
@ -139,7 +140,7 @@ export class ActiveKeyBase {
|
|||
public get isPadding(): boolean {
|
||||
// Does not include 9 (class: blank) as that may be an intentional 'catch' for misplaced
|
||||
// keystrokes.
|
||||
return this.sp == Layouts.buttonClasses.HIDDEN; // Button class: hidden.
|
||||
return this.sp == ButtonClasses.spacer; // Button class: hidden.
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -544,17 +545,17 @@ export class ActiveRow implements LayoutRow {
|
|||
}
|
||||
|
||||
// Modify the key type for special keys with non-standard labels
|
||||
// to allow the keyboard font to ovveride the SpecialOSK font.
|
||||
// to allow the keyboard font to overide the SpecialOSK font.
|
||||
// Blank keys are no longer reclassed - can use before/after CSS to add text
|
||||
switch(key['sp']) {
|
||||
case Layouts.buttonClasses.SHIFT:
|
||||
case ButtonClasses.special:
|
||||
if(!ActiveRow.SPECIAL_LABEL.test(key['text']) && key['text'] != '') {
|
||||
key.sp=Layouts.buttonClasses.SPECIAL;
|
||||
key.sp=ButtonClasses.customSpecial;
|
||||
}
|
||||
break;
|
||||
case Layouts.buttonClasses['SHIFT-ON']:
|
||||
case ButtonClasses.specialActive:
|
||||
if(!ActiveRow.SPECIAL_LABEL.test(key['text']) && key['text'] != '') {
|
||||
key.sp=Layouts.buttonClasses['SPECIAL-ON'];
|
||||
key.sp=ButtonClasses.customSpecialActive;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ export type LayoutRow = TouchLayout.TouchLayoutRow;
|
|||
export type LayoutKey = TouchLayout.TouchLayoutKey;
|
||||
export type LayoutSubKey = TouchLayout.TouchLayoutSubKey;
|
||||
|
||||
import ButtonClasses = TouchLayout.TouchLayoutKeySp;
|
||||
|
||||
export { ButtonClasses };
|
||||
|
||||
import Codes from "../text/codes.js";
|
||||
import type Keyboard from "./keyboard.js";
|
||||
|
|
@ -20,7 +23,6 @@ export type KLS = {[layerName: string]: string[]};
|
|||
|
||||
// The following types provide type definitions for the full JSON format we use for visual keyboard definitions.
|
||||
export type ButtonClass = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
|
||||
export type ButtonClassString = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"|"10";
|
||||
|
||||
export interface LayoutLayer extends LayoutLayerBase {
|
||||
// Post-processing elements.
|
||||
|
|
@ -61,19 +63,7 @@ export class Layouts {
|
|||
|
||||
static readonly DEFAULT_RAW_SPEC = {'F':'Tahoma', 'BK': Layouts.dfltText} as const;
|
||||
|
||||
// Cross-reference with the ids in osk.setButtonClass.
|
||||
static readonly buttonClasses: {[name: string]: ButtonClass} = {
|
||||
'DEFAULT':0,
|
||||
'SHIFT':1, // special-key / frame key styling: uses our custom, PUA OSK font
|
||||
'SHIFT-ON':2,
|
||||
'SPECIAL':3, // special-key / frame key styling: uses the keyboard's font
|
||||
'SPECIAL-ON':4,
|
||||
'DEADKEY':8,
|
||||
'BLANK':9,
|
||||
'HIDDEN':10
|
||||
};
|
||||
|
||||
static readonly modifierSpecials = {
|
||||
static modifierSpecials = {
|
||||
'leftalt': '*LAlt*',
|
||||
'rightalt': '*RAlt*',
|
||||
'alt': '*Alt*',
|
||||
|
|
@ -323,7 +313,7 @@ export class Layouts {
|
|||
// Set modifier key appearance and behaviour for non-desktop devices using the default layout
|
||||
if(formFactor != 'desktop') {
|
||||
if(n > 0 && shiftKey != null) {
|
||||
shiftKey['sp']=Layouts.buttonClasses['SHIFT-ON'];
|
||||
shiftKey['sp']=ButtonClasses.specialActive;
|
||||
shiftKey['sk']=null;
|
||||
shiftKey['text'] = Layouts.modifierSpecials[layers[n].id] ? Layouts.modifierSpecials[layers[n].id] : "*Shift*";
|
||||
}
|
||||
|
|
@ -407,7 +397,6 @@ export class Layouts {
|
|||
*/
|
||||
static formatDefaultLayer(layer: LayoutLayer, chiral: boolean, formFactor: string, key102: boolean) {
|
||||
var layerId = layer['id'];
|
||||
let buttonClasses = Layouts.buttonClasses;
|
||||
|
||||
// Correct appearance of state-dependent modifier keys according to group
|
||||
for(var i=0; i<layer['row'].length; i++) {
|
||||
|
|
@ -420,7 +409,7 @@ export class Layouts {
|
|||
case 'K_LSHIFT':
|
||||
case 'K_RSHIFT':
|
||||
if(layerId.indexOf('shift') != -1) {
|
||||
key['sp'] = buttonClasses['SHIFT-ON'];
|
||||
key['sp'] = ButtonClasses.specialActive;
|
||||
}
|
||||
if(formFactor != 'desktop') {
|
||||
if(layerId != 'default') {
|
||||
|
|
@ -434,7 +423,7 @@ export class Layouts {
|
|||
case 'K_LCONTROL':
|
||||
if(chiral) {
|
||||
if(layerId.indexOf('leftctrl') != -1) {
|
||||
key['sp'] = buttonClasses['SHIFT-ON'];
|
||||
key['sp'] = ButtonClasses.specialActive;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -442,35 +431,35 @@ export class Layouts {
|
|||
case 'K_RCONTROL':
|
||||
if(chiral) {
|
||||
if(layerId.indexOf('rightctrl') != -1) {
|
||||
key['sp'] = buttonClasses['SHIFT-ON'];
|
||||
key['sp'] = ButtonClasses.specialActive;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'K_CONTROL':
|
||||
if(layerId.indexOf('ctrl') != -1) {
|
||||
if(!chiral || (layerId.indexOf('leftctrl') != -1 && layerId.indexOf('rightctrl') != -1)) {
|
||||
key['sp'] = buttonClasses['SHIFT-ON'];
|
||||
key['sp'] = ButtonClasses.specialActive;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'K_LALT':
|
||||
if(chiral) {
|
||||
if(layerId.indexOf('leftalt') != -1) {
|
||||
key['sp'] = buttonClasses['SHIFT-ON'];
|
||||
key['sp'] = ButtonClasses.specialActive;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'K_RALT':
|
||||
if(chiral) {
|
||||
if(layerId.indexOf('rightalt') != -1) {
|
||||
key['sp'] = buttonClasses['SHIFT-ON'];
|
||||
key['sp'] = ButtonClasses.specialActive;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'K_ALT':
|
||||
if(layerId.indexOf('alt') != -1) {
|
||||
if(!chiral || (layerId.indexOf('leftalt') != -1 && layerId.indexOf('rightalt') != -1)) {
|
||||
key['sp'] = buttonClasses['SHIFT-ON'];
|
||||
key['sp'] = ButtonClasses.specialActive;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -480,7 +469,7 @@ export class Layouts {
|
|||
keys.splice(j--, 1);
|
||||
keys[0]['width']=KEY_102_WIDTH;
|
||||
} else {
|
||||
keys[j]['sp']=buttonClasses['HIDDEN'];
|
||||
keys[j]['sp']=ButtonClasses.spacer;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ describe('Custom Punctuation', function () {
|
|||
|
||||
// The model compositor is responsible for adding this to the display as
|
||||
// string.
|
||||
var composite = new ModelCompositor(model);
|
||||
var composite = new ModelCompositor(model, true);
|
||||
var suggestions = composite.predict([{ sample: { insert: 'o', deleteLeft: 0 }, p: 1.00 }], {
|
||||
left: 'Hrll', startOfBuffer: false, endOfBuffer: true
|
||||
});
|
||||
|
|
@ -81,7 +81,7 @@ describe('Custom Punctuation', function () {
|
|||
|
||||
// The model compositor is responsible for adding this to the display as
|
||||
// string.
|
||||
var composite = new ModelCompositor(model);
|
||||
var composite = new ModelCompositor(model, true);
|
||||
var suggestions = composite.predict([{ sample: { insert: 'ᚋ', deleteLeft: 0 }, p: 1.00 }], {
|
||||
left: '᚛ᚈᚑ', startOfBuffer: false, endOfBuffer: true
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ describe('ModelCompositor', function() {
|
|||
);
|
||||
|
||||
it('generates suggestions with expected properties', function() {
|
||||
let compositor = new ModelCompositor(plainModel);
|
||||
let compositor = new ModelCompositor(plainModel, true);
|
||||
let context = {
|
||||
left: 'th', startOfBuffer: true, endOfBuffer: true,
|
||||
};
|
||||
|
|
@ -56,7 +56,7 @@ describe('ModelCompositor', function() {
|
|||
});
|
||||
|
||||
it('strongly avoids corrections for single-character roots', function() {
|
||||
let compositor = new ModelCompositor(plainModel);
|
||||
let compositor = new ModelCompositor(plainModel, true);
|
||||
let context = {
|
||||
left: '', startOfBuffer: true, endOfBuffer: true,
|
||||
};
|
||||
|
|
@ -95,7 +95,7 @@ describe('ModelCompositor', function() {
|
|||
});
|
||||
|
||||
it('properly handles suggestions after a backspace', function() {
|
||||
let compositor = new ModelCompositor(plainModel);
|
||||
let compositor = new ModelCompositor(plainModel, true);
|
||||
let context = {
|
||||
left: 'the ', startOfBuffer: true, endOfBuffer: true,
|
||||
};
|
||||
|
|
@ -118,7 +118,7 @@ describe('ModelCompositor', function() {
|
|||
});
|
||||
|
||||
it('properly handles suggestions for the first letter after a ` `', function() {
|
||||
let compositor = new ModelCompositor(plainModel);
|
||||
let compositor = new ModelCompositor(plainModel, true);
|
||||
let context = {
|
||||
left: 'the', startOfBuffer: true, endOfBuffer: true,
|
||||
};
|
||||
|
|
@ -137,7 +137,7 @@ describe('ModelCompositor', function() {
|
|||
});
|
||||
|
||||
it('properly handles suggestions for the first letter after a `\'`', function() {
|
||||
let compositor = new ModelCompositor(plainModel);
|
||||
let compositor = new ModelCompositor(plainModel, true);
|
||||
let context = {
|
||||
left: "the '", startOfBuffer: true, endOfBuffer: true,
|
||||
};
|
||||
|
|
@ -186,7 +186,7 @@ describe('ModelCompositor', function() {
|
|||
);
|
||||
|
||||
it('properly cases suggestions with no suggestion root', function() {
|
||||
var compositor = new ModelCompositor(plainCasedModel);
|
||||
var compositor = new ModelCompositor(plainCasedModel, true);
|
||||
|
||||
let suggestion = {
|
||||
transform: {
|
||||
|
|
@ -226,7 +226,7 @@ describe('ModelCompositor', function() {
|
|||
});
|
||||
|
||||
it('properly cases suggestions that fully replace the suggestion root', function() {
|
||||
var compositor = new ModelCompositor(plainCasedModel);
|
||||
var compositor = new ModelCompositor(plainCasedModel, true);
|
||||
|
||||
let suggestion = {
|
||||
transform: {
|
||||
|
|
@ -266,7 +266,7 @@ describe('ModelCompositor', function() {
|
|||
});
|
||||
|
||||
it('properly cases suggestions that do not fully replace the suggestion root', function() {
|
||||
var compositor = new ModelCompositor(plainCasedModel);
|
||||
var compositor = new ModelCompositor(plainCasedModel, true);
|
||||
|
||||
let suggestion = {
|
||||
transform: {
|
||||
|
|
@ -322,7 +322,7 @@ describe('ModelCompositor', function() {
|
|||
|
||||
it('should produce suggestions from uncased input', function() {
|
||||
let model = uncasedModel;
|
||||
var composite = new ModelCompositor(model);
|
||||
var composite = new ModelCompositor(model, true);
|
||||
|
||||
// Initialize context
|
||||
let context = {
|
||||
|
|
@ -350,7 +350,7 @@ describe('ModelCompositor', function() {
|
|||
|
||||
it('should not produce suggestions from cased input', function() {
|
||||
let model = uncasedModel;
|
||||
var composite = new ModelCompositor(model);
|
||||
var composite = new ModelCompositor(model, true);
|
||||
|
||||
// Initialize context
|
||||
let context = {
|
||||
|
|
@ -397,7 +397,7 @@ describe('ModelCompositor', function() {
|
|||
|
||||
it('should produce suggestions from uncased input', function() {
|
||||
let model = casedModel;
|
||||
var composite = new ModelCompositor(model);
|
||||
var composite = new ModelCompositor(model, true);
|
||||
|
||||
// Initialize context
|
||||
let context = {
|
||||
|
|
@ -425,7 +425,7 @@ describe('ModelCompositor', function() {
|
|||
|
||||
it('should produce capitalized suggestions from fully-uppercased input', function() {
|
||||
let model = casedModel;
|
||||
var composite = new ModelCompositor(model);
|
||||
var composite = new ModelCompositor(model, true);
|
||||
|
||||
// Initialize context
|
||||
let context = {
|
||||
|
|
@ -453,7 +453,7 @@ describe('ModelCompositor', function() {
|
|||
|
||||
it('should produce "initial-case" suggestions from input with an initial capital', function() {
|
||||
let model = casedModel;
|
||||
var composite = new ModelCompositor(model);
|
||||
var composite = new ModelCompositor(model, true);
|
||||
|
||||
// Initialize context
|
||||
let context = {
|
||||
|
|
@ -481,7 +481,7 @@ describe('ModelCompositor', function() {
|
|||
|
||||
it('also from input with partial capitalization when including an initial capital', function() {
|
||||
let model = casedModel;
|
||||
var composite = new ModelCompositor(model);
|
||||
var composite = new ModelCompositor(model, true);
|
||||
|
||||
// Initialize context
|
||||
let context = {
|
||||
|
|
@ -512,7 +512,7 @@ describe('ModelCompositor', function() {
|
|||
var model = new TrieModel(
|
||||
jsonFixture('models/tries/english-1000')
|
||||
);
|
||||
var composite = new ModelCompositor(model);
|
||||
var composite = new ModelCompositor(model, true);
|
||||
|
||||
// Initialize context
|
||||
let context = {
|
||||
|
|
@ -542,7 +542,7 @@ describe('ModelCompositor', function() {
|
|||
var model = new TrieModel(
|
||||
jsonFixture('models/tries/english-1000')
|
||||
);
|
||||
var composite = new ModelCompositor(model);
|
||||
var composite = new ModelCompositor(model, true);
|
||||
|
||||
// Initialize context
|
||||
let context = {
|
||||
|
|
@ -602,7 +602,7 @@ describe('ModelCompositor', function() {
|
|||
};
|
||||
|
||||
let model = new models.DummyModel(options);
|
||||
let compositor = new ModelCompositor(model);
|
||||
let compositor = new ModelCompositor(model, true);
|
||||
|
||||
var keep;
|
||||
if(quoteStyle) {
|
||||
|
|
@ -646,7 +646,7 @@ describe('ModelCompositor', function() {
|
|||
};
|
||||
|
||||
let model = new models.DummyModel(options);
|
||||
let compositor = new ModelCompositor(model);
|
||||
let compositor = new ModelCompositor(model, true);
|
||||
|
||||
return compositor.acceptSuggestion(suggestion, context, postTransform);
|
||||
}
|
||||
|
|
@ -829,7 +829,7 @@ describe('ModelCompositor', function() {
|
|||
|
||||
describe('acceptReversion', function() {
|
||||
let executeAcceptance = function(model, suggestion, context, postTransform) {
|
||||
let compositor = new ModelCompositor(model);
|
||||
let compositor = new ModelCompositor(model, true);
|
||||
|
||||
return {compositor: compositor, reversion: compositor.acceptSuggestion(suggestion, context, postTransform)};
|
||||
}
|
||||
|
|
@ -869,7 +869,7 @@ describe('ModelCompositor', function() {
|
|||
}
|
||||
|
||||
let model = new models.DummyModel({punctuation: englishPunctuation});
|
||||
let compositor = new ModelCompositor(model);
|
||||
let compositor = new ModelCompositor(model, true);
|
||||
|
||||
let reversion = compositor.acceptSuggestion(baseSuggestion, baseContext, postTransform);
|
||||
assert.equal(reversion.transformId, baseSuggestion.transformId);
|
||||
|
|
@ -909,7 +909,7 @@ describe('ModelCompositor', function() {
|
|||
}
|
||||
|
||||
let model = new models.TrieModel(jsonFixture('models/tries/english-1000'), {punctuation: englishPunctuation});
|
||||
let compositor = new ModelCompositor(model);
|
||||
let compositor = new ModelCompositor(model, true);
|
||||
|
||||
let initialSuggestions = compositor.predict(postTransform, baseContext);
|
||||
let keepSuggestion = initialSuggestions[0];
|
||||
|
|
@ -944,7 +944,7 @@ describe('ModelCompositor', function() {
|
|||
}
|
||||
|
||||
let model = new models.TrieModel(jsonFixture('models/tries/english-1000'), {punctuation: englishPunctuation});
|
||||
let compositor = new ModelCompositor(model);
|
||||
let compositor = new ModelCompositor(model, true);
|
||||
|
||||
let initialSuggestions = compositor.predict(postTransform, baseContext);
|
||||
let keepSuggestion = initialSuggestions[0];
|
||||
|
|
|
|||
|
|
@ -64,7 +64,30 @@ export interface TouchLayoutKey {
|
|||
hint?: string;
|
||||
};
|
||||
|
||||
export enum TouchLayoutKeySp { normal=0, special=1, specialActive=2, deadkey=8, blank=9, spacer=10 };
|
||||
export const enum TouchLayoutKeySp {
|
||||
normal=0,
|
||||
/** A 'frame' key, such as Shift or Enter, which is styled accordingly; uses
|
||||
* the 'KeymanwebOsk' font on KeymanWeb */
|
||||
special=1,
|
||||
/** A 'frame' key, such as Shift or Enter, which is styled accordingly and is
|
||||
* highlighted to indicate it is active, such as the shift key on a shift
|
||||
* layer; uses the 'KeymanwebOsk' font on KeymanWeb */
|
||||
specialActive=2,
|
||||
/** **KeymanWeb runtime private use:** a variant of `special` with the
|
||||
* keyboard font rather than 'KeymanwebOsk' font */
|
||||
customSpecial=3,
|
||||
/** **KeymanWeb runtime private use:** a variant of `specialActive` with the
|
||||
* keyboard font rather than 'KeymanwebOsk' font. */
|
||||
customSpecialActive=4,
|
||||
/** A styling signal to indicate that the key may have 'deadkey' type
|
||||
* behaviour. */
|
||||
deadkey=8,
|
||||
/** A key which is rendered as a blank keycap, blocks any interaction */
|
||||
blank=9,
|
||||
/** Renders the key only as a gap or spacer, blocks any interaction */
|
||||
spacer=10
|
||||
};
|
||||
|
||||
export type TouchLayoutKeyPad = number; // 0-100000
|
||||
export type TouchLayoutKeyWidth = number; // 0-100000
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ export class KeymanDeveloperProject {
|
|||
throw new Error('populateFiles can only be called on a v2.0 project');
|
||||
}
|
||||
let sourcePath = this.resolveProjectPath(this.options.sourcePath);
|
||||
if(!this.callbacks.fs.existsSync(sourcePath)) {
|
||||
return false;
|
||||
}
|
||||
let files = this.callbacks.fs.readdirSync(sourcePath);
|
||||
for(let filename of files) {
|
||||
let fullPath = this.callbacks.path.join(sourcePath, filename);
|
||||
|
|
@ -44,6 +47,7 @@ export class KeymanDeveloperProject {
|
|||
this.files.push(file);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public isKeyboardProject() {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ export class KPJFileReader {
|
|||
emptyTag: ''
|
||||
});
|
||||
|
||||
parser.parseString(file, (e: unknown, r: unknown) => { data = r as KPJFile });
|
||||
parser.parseString(file, (e: unknown, r: unknown) => {
|
||||
if(e) {
|
||||
throw e;
|
||||
}
|
||||
data = r as KPJFile;
|
||||
});
|
||||
data = this.boxArrays(data);
|
||||
for(let file of data.KeymanDeveloperProject?.Files?.File) {
|
||||
// xml2js imports <Details/> as '' so we will just delete the empty string
|
||||
|
|
|
|||
|
|
@ -41,34 +41,41 @@ export interface LKTTests {
|
|||
export interface LKTTest {
|
||||
name?: string;
|
||||
startContext?: LKTStartContext;
|
||||
actions?: LKTAction[]; // differs from XML, to represent order of actions
|
||||
actions?: LKTAnyAction[]; // differs from XML, to represent order of actions
|
||||
};
|
||||
|
||||
export interface LKTStartContext {
|
||||
to?: string;
|
||||
};
|
||||
|
||||
export interface LKTCheck {
|
||||
result?: string;
|
||||
};
|
||||
|
||||
export interface LKTEmit {
|
||||
to?: string;
|
||||
};
|
||||
|
||||
export interface LKTKeystroke {
|
||||
key?: string;
|
||||
flick?: string;
|
||||
longPress?: string;
|
||||
tapCount?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test Actions.
|
||||
* The expectation is that each LKTAction object will have exactly one non-falsy field.
|
||||
*/
|
||||
export interface LKTAction {
|
||||
check?: LKTCheck;
|
||||
emit?: LKTEmit;
|
||||
keystroke?: LKTKeystroke;
|
||||
type?: "check" | "emit" | "keystroke" | "backspace";
|
||||
};
|
||||
|
||||
export interface LKTCheck extends LKTAction {
|
||||
type: "check";
|
||||
result?: string;
|
||||
};
|
||||
|
||||
export interface LKTEmit extends LKTAction {
|
||||
type: "emit";
|
||||
to?: string;
|
||||
};
|
||||
|
||||
export interface LKTKeystroke extends LKTAction {
|
||||
type: "keystroke";
|
||||
key?: string;
|
||||
flick?: string;
|
||||
longPress?: string;
|
||||
tapCount?: string;
|
||||
};
|
||||
|
||||
export interface LKTBackspace extends LKTAction {
|
||||
type: "backspace";
|
||||
}
|
||||
|
||||
export type LKTAnyAction = LKTCheck | LKTEmit | LKTKeystroke | LKTBackspace;
|
||||
|
|
|
|||
|
|
@ -393,15 +393,12 @@ export class LDMLKeyboardXMLSourceFileReader {
|
|||
r.stuffBoxes(test, $$, 'startContext'); // singleton
|
||||
// now the actions
|
||||
test.actions = $$.map(v => {
|
||||
const subtag = v['#name'];
|
||||
const subv = LDMLKeyboardXMLSourceFileReader.defaultMapper(v, r);
|
||||
switch(subtag) {
|
||||
case 'keystroke': return { keystroke: subv };
|
||||
case 'check': return { check: subv };
|
||||
case 'emit': return { emit: subv };
|
||||
case 'startContext': return null; // handled above
|
||||
default: this.callbacks.reportMessage(CommonTypesMessages.Error_TestDataUnexpectedAction({ subtag })); return null;
|
||||
const type = v['#name']; // element name
|
||||
if (type === 'startContext') {
|
||||
return null; // handled above
|
||||
}
|
||||
const subv = LDMLKeyboardXMLSourceFileReader.defaultMapper(v, r);
|
||||
return Object.assign({ type }, subv);
|
||||
}).filter(v => v !== null);
|
||||
return test;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export { TouchLayoutFileWriter, TouchLayoutFileWriterOptions } from './keyman-to
|
|||
|
||||
export * as KPJ from './kpj/kpj-file.js';
|
||||
export { KPJFileReader } from './kpj/kpj-file-reader.js';
|
||||
export { KeymanDeveloperProject, KeymanDeveloperProjectFile, } from './kpj/keyman-developer-project.js';
|
||||
export { KeymanDeveloperProject, KeymanDeveloperProjectFile, KeymanDeveloperProjectType, } from './kpj/keyman-developer-project.js';
|
||||
|
||||
export * as KpsFile from './package/kps-file.js';
|
||||
export * as KmpJsonFile from './package/kmp-json-file.js';
|
||||
|
|
|
|||
|
|
@ -43,8 +43,4 @@ export class CommonTypesMessages {
|
|||
m(this.ERROR_TestDataUnexpectedArray,
|
||||
`Problem reading test data: expected single ${o.subtag} element, found multiple`);
|
||||
static ERROR_TestDataUnexpectedArray = SevError | 0x0007;
|
||||
static Error_TestDataUnexpectedAction = (o: {subtag: string}) =>
|
||||
m(this.ERROR_TestDataUnexpectedAction,
|
||||
`Problem reading test data: unexpected action element ${o.subtag}`);
|
||||
static ERROR_TestDataUnexpectedAction = SevError | 0x0008;
|
||||
};
|
||||
|
|
|
|||
8
common/web/types/test/fixtures/test-fr.xml
vendored
8
common/web/types/test/fixtures/test-fr.xml
vendored
|
|
@ -1,6 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE keyboardTest3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboardTest3.dtd">
|
||||
<keyboardTest3 conformsTo="techpreview">
|
||||
<!--
|
||||
Read by:
|
||||
- common/web/types/test/ldml-keyboard/test-ldml-keyboard-testdata-reader.ts
|
||||
|
||||
Also see the other test-fr.xml in developer
|
||||
-->
|
||||
<info keyboard="fr-t-k0-azerty.xml" author="Team Keyboard" name="fr-test" />
|
||||
<repertoire name="simple-repertoire" chars="[a b c d e \u{22}]" type="simple" /> <!-- verify that these outputs are all available from simple keys on any layer, for all form factors -->
|
||||
<repertoire name="chars-repertoire" chars="[á é ó]" type="gesture" /> <!-- verify that these outputs are all available from simple or gesture keys on any layer, for touch -->
|
||||
|
|
@ -17,6 +23,8 @@
|
|||
<!-- tests by specifying 'to' output char -->
|
||||
<emit to="v"/>
|
||||
<check result="abc\u0022...stuv" />
|
||||
<backspace />
|
||||
<check result="abc\u0022...stu" />
|
||||
</test>
|
||||
</tests>
|
||||
</keyboardTest3>
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ import { constants } from '@keymanapp/ldml-keyboard-constants';
|
|||
import { assert } from 'chai';
|
||||
import 'mocha';
|
||||
import { testTestdataReaderCases } from '../helpers/reader-callback-test.js';
|
||||
import { LKTAnyAction } from './ldml-keyboard-testdata-xml.js';
|
||||
|
||||
describe('ldml keyboard xml reader tests', function () {
|
||||
this.slow(500); // 0.5 sec -- json schema validation takes a while
|
||||
|
||||
testTestdataReaderCases([
|
||||
{
|
||||
// Note! There's another test case against similar data, in developer/src/kmc-ldml/test/test-testdata-e2e.ts using test-fr.json
|
||||
subpath: 'test-fr.xml',
|
||||
callback: (data, source) => {
|
||||
assert.ok(source);
|
||||
|
|
@ -35,16 +37,19 @@ describe('ldml keyboard xml reader tests', function () {
|
|||
const test0 = source.keyboardTest3.tests[0].test[0];
|
||||
assert.equal('key-test', test0.name);
|
||||
assert.equal('abc\\u0022...', test0.startContext?.to);
|
||||
assert.sameDeepOrderedMembers([
|
||||
{ keystroke: { key: 's' } },
|
||||
{ check: { result: 'abc\\u0022...s' } },
|
||||
{ keystroke: { key: 't' } },
|
||||
{ check: { result: 'abc\\u0022...st' } },
|
||||
{ keystroke: { key: 'u' } },
|
||||
{ check: { result: 'abc\\u0022...stu' } },
|
||||
{ emit: { to: 'v' } },
|
||||
{ check: { result: 'abc\\u0022...stuv' } },
|
||||
], test0.actions);
|
||||
const expectedActions : LKTAnyAction[] = [
|
||||
{ type: "keystroke", key: 's' },
|
||||
{ type: "check", result: 'abc\\u0022...s' },
|
||||
{ type: "keystroke", key: 't' },
|
||||
{ type: "check", result: 'abc\\u0022...st' },
|
||||
{ type: "keystroke", key: 'u' },
|
||||
{ type: "check", result: 'abc\\u0022...stu' },
|
||||
{ type: "emit", to: 'v' },
|
||||
{ type: "check", result: 'abc\\u0022...stuv' },
|
||||
{ type: "backspace" },
|
||||
{ type: "check", result: 'abc\\u0022...stu' },
|
||||
];
|
||||
assert.sameDeepOrderedMembers(expectedActions, test0.actions, 'Static data in .ts file should match parsed test-fr.xml');
|
||||
},
|
||||
}
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -107,13 +107,13 @@ builder_describe_outputs \
|
|||
configure:mac-arm64 /core/build/mac-arm64/$BUILDER_CONFIGURATION/build.ninja \
|
||||
configure:arch /core/build/arch/$BUILDER_CONFIGURATION/build.ninja \
|
||||
configure:wasm /core/build/wasm/$BUILDER_CONFIGURATION/build.ninja \
|
||||
build:x86 /core/build/x86/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:x64 /core/build/x64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:mac /core/build/mac/$BUILDER_CONFIGURATION/libkmnkbp0.a \
|
||||
build:mac-x86_64 /core/build/mac-x86_64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:mac-arm64 /core/build/mac-arm64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:arch /core/build/arch/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:wasm /core/build/wasm/$BUILDER_CONFIGURATION/src/libkmnkbp0.a
|
||||
build:x86 /core/build/x86/$BUILDER_CONFIGURATION/src/libkeymancore.a \
|
||||
build:x64 /core/build/x64/$BUILDER_CONFIGURATION/src/libkeymancore.a \
|
||||
build:mac /core/build/mac/$BUILDER_CONFIGURATION/libkeymancore.a \
|
||||
build:mac-x86_64 /core/build/mac-x86_64/$BUILDER_CONFIGURATION/src/libkeymancore.a \
|
||||
build:mac-arm64 /core/build/mac-arm64/$BUILDER_CONFIGURATION/src/libkeymancore.a \
|
||||
build:arch /core/build/arch/$BUILDER_CONFIGURATION/src/libkeymancore.a \
|
||||
build:wasm /core/build/wasm/$BUILDER_CONFIGURATION/src/libkeymancore.a
|
||||
|
||||
# Import our standard compiler defines; this is copied from
|
||||
# /resources/build/meson/standard.meson.build by build.sh, because meson doesn't
|
||||
|
|
@ -157,9 +157,9 @@ do_action build
|
|||
|
||||
if builder_start_action build:mac; then
|
||||
lipo -create \
|
||||
"$KEYMAN_ROOT/core/build/mac-x86_64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a" \
|
||||
"$KEYMAN_ROOT/core/build/mac-arm64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a" \
|
||||
-output "$KEYMAN_ROOT/core/build/mac/$BUILDER_CONFIGURATION/libkmnkbp0.a"
|
||||
"$KEYMAN_ROOT/core/build/mac-x86_64/$BUILDER_CONFIGURATION/src/libkeymancore.a" \
|
||||
"$KEYMAN_ROOT/core/build/mac-arm64/$BUILDER_CONFIGURATION/src/libkeymancore.a" \
|
||||
-output "$KEYMAN_ROOT/core/build/mac/$BUILDER_CONFIGURATION/libkeymancore.a"
|
||||
builder_finish_action success build:mac
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
Copyright: © 2018 SIL International.
|
||||
Description: Cross platform API C/C++ declarations for libkmnkbp keyboard
|
||||
Description: Cross platform API C/C++ declarations for libkeymancore keyboard
|
||||
processor.
|
||||
Create Date: 2 Oct 2018
|
||||
Authors: Tim Eves (TSE)
|
||||
|
|
@ -253,8 +253,8 @@ km_core_context_items_from_utf8(char const *text,
|
|||
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
|
||||
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.
|
||||
number of codeunits required is returned. Any markers in the context will
|
||||
not be included in the output buffer.
|
||||
##### Return status:
|
||||
- `KM_CORE_STATUS_OK`: On success.
|
||||
- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
|
||||
|
|
@ -285,8 +285,8 @@ km_core_context_items_to_utf16(km_core_context_item const *item,
|
|||
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
|
||||
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.
|
||||
number of codeunits required is returned. Any markers in the context will
|
||||
not be included in the output buffer.
|
||||
##### Return status:
|
||||
- `KM_CORE_STATUS_OK`: On success.
|
||||
- `KM_CORE_STATUS_INVALID_ARGUMENT`: If non-optional parameters are null.
|
||||
|
|
@ -310,6 +310,38 @@ km_core_context_items_to_utf8(km_core_context_item const *item,
|
|||
char *buf,
|
||||
size_t *buf_size);
|
||||
|
||||
/*
|
||||
```
|
||||
### `km_core_context_items_to_utf32`
|
||||
##### Description:
|
||||
Convert a context item array into a UTF-32 encoded string placing it into
|
||||
the supplied buffer of specified size, and return the number of codepoints
|
||||
actually used in the conversion. If null is passed as the buffer the
|
||||
number of codepoints required is returned. Any markers in the context will
|
||||
not be included in the output buffer.
|
||||
##### Return status:
|
||||
- `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_core_context_item`.
|
||||
Must be terminated with a type of `KM_CORE_CT_END`.
|
||||
- __buf__: A pointer to the buffer to place the UTF-32 string into.
|
||||
May be null to request size calculation.
|
||||
- __buf_size__: a pointer to the result variable:
|
||||
The size of the supplied buffer in codepoints if `buf` is given.
|
||||
On return will be the size required if `buf` is null.
|
||||
|
||||
```c
|
||||
*/
|
||||
KMN_API
|
||||
km_core_status
|
||||
km_core_context_items_to_utf32(km_core_context_item const *item,
|
||||
km_core_usv *buf,
|
||||
size_t *buf_size);
|
||||
|
||||
/*
|
||||
```
|
||||
### `km_core_context_items_dispose`
|
||||
|
|
@ -501,10 +533,10 @@ typedef struct {
|
|||
uint8_t type;
|
||||
uint8_t _reserved[sizeof(void*)-sizeof(uint8_t)];
|
||||
union {
|
||||
uintptr_t marker; // MARKER type
|
||||
uint32_t marker; // MARKER type
|
||||
km_core_option_item const * option; // OPT types
|
||||
km_core_usv character; // CHAR type
|
||||
uint8_t capsLock; // CAPSLOCK type, 1 to turn on, 0 to turn off
|
||||
uint8_t capsLock; // CAPSLOCK type, 1 to turn on, 0 to turn off; re name see #9833
|
||||
km_core_backspace_item backspace; // BACKSPACE type
|
||||
};
|
||||
} km_core_action_item;
|
||||
|
|
@ -526,6 +558,161 @@ enum km_core_action_type {
|
|||
KM_CORE_IT_MAX_TYPE_ID
|
||||
};
|
||||
|
||||
/*
|
||||
```
|
||||
### Actions
|
||||
This structure provides the results of processing a key event to the Platform layer and
|
||||
should be processed by the Platform layer to issue commands to the os text
|
||||
services framework to transform the text store in the Client Application, among
|
||||
other actions.
|
||||
|
||||
This API replaces the Action items APIs, which is now deprecated and will be
|
||||
removed in the future.
|
||||
```c
|
||||
*/
|
||||
|
||||
typedef enum { KM_CORE_FALSE = 0, KM_CORE_TRUE = 1 } km_core_bool;
|
||||
typedef enum { KM_CORE_CAPS_UNCHANGED = -1, KM_CORE_CAPS_OFF = 0, KM_CORE_CAPS_ON = 1 } km_core_caps_state;
|
||||
|
||||
typedef struct {
|
||||
// number of codepoints (not codeunits!) to delete from app context.
|
||||
unsigned int code_points_to_delete;
|
||||
|
||||
// null-term string of characters to insert into document
|
||||
km_core_usv* output;
|
||||
|
||||
// list of options to persist, terminated with KM_CORE_OPTIONS_END
|
||||
km_core_option_item* persist_options;
|
||||
|
||||
// issue a beep, 0 = no, 1 = yes
|
||||
km_core_bool do_alert;
|
||||
|
||||
// emit the (unmodified) input keystroke to the application, 0 = no, 1 = yes
|
||||
km_core_bool emit_keystroke;
|
||||
|
||||
// -1=unchanged, 0=off, 1=on
|
||||
km_core_caps_state new_caps_lock_state;
|
||||
} km_core_actions;
|
||||
|
||||
/*
|
||||
```
|
||||
### `km_core_state_get_actions`
|
||||
##### Description:
|
||||
Returns a pointer to an actions object which details all the actions
|
||||
that the Platform layer must take after a keystroke. The `code_points_to_delete`
|
||||
action must be performed before the `output` action, but the other
|
||||
actions may be performed in any order.
|
||||
##### Return:
|
||||
A pointer to a `km_core_actions` object, which must be freed with
|
||||
`km_core_actions_dispose`.
|
||||
##### Parameters:
|
||||
- __state__: An opaque pointer to a state object.
|
||||
|
||||
```c
|
||||
*/
|
||||
KMN_API
|
||||
km_core_actions*
|
||||
km_core_state_get_actions(
|
||||
km_core_state const *state
|
||||
);
|
||||
|
||||
/*
|
||||
```
|
||||
### `km_core_actions_dispose`
|
||||
##### Description:
|
||||
Free the allocated memory belonging to an actions object previously
|
||||
returned by `km_core_state_get_actions`.
|
||||
##### Parameters:
|
||||
- __actions__: A pointer to the actions object to be disposed of.
|
||||
|
||||
```c
|
||||
*/
|
||||
KMN_API
|
||||
km_core_status
|
||||
km_core_actions_dispose(
|
||||
km_core_actions* actions
|
||||
);
|
||||
|
||||
/*
|
||||
```
|
||||
### `km_core_context_status`
|
||||
##### Description:
|
||||
Return values for `km_core_state_context_set_if_needed`.
|
||||
|
||||
```c
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
KM_CORE_CONTEXT_STATUS_UNCHANGED = 0, // Cached context change was not needed
|
||||
KM_CORE_CONTEXT_STATUS_UPDATED = 1, // Cached context was set to application context
|
||||
KM_CORE_CONTEXT_STATUS_CLEARED = 2, // Application context was invalid, context was cleared
|
||||
KM_CORE_CONTEXT_STATUS_ERROR = 3, // Internal error
|
||||
KM_CORE_CONTEXT_STATUS_INVALID_ARGUMENT = 4, // Invalid arguments
|
||||
} km_core_context_status;
|
||||
|
||||
/*
|
||||
```
|
||||
### `km_core_state_context_set_if_needed`
|
||||
##### Description:
|
||||
Sets the internal cached context for the state object, to the passed-in
|
||||
application context string, if it differs from the codepoints in the
|
||||
cached context. For the purposes of comparison, (1) cached markers are
|
||||
ignored, (2) if the cached context is shorter than the application
|
||||
context, it is considered identical, but (3) if the cached context is
|
||||
longer, then it is considered different.
|
||||
|
||||
If a difference is found, then the cached context will be set to the
|
||||
application context, and thus any cached markers will be cleared.
|
||||
|
||||
`km_core_state_context_set_if_needed` and `km_core_state_context_clear`
|
||||
will replace most uses of the existing Core context APIs.
|
||||
|
||||
##### Parameters:
|
||||
- __state__: An opaque pointer to a state object.
|
||||
- __application_context__: A pointer to an null-terminated `km_core_cp`
|
||||
string representing the current context from the application.
|
||||
##### Return status:
|
||||
- `KM_CORE_CONTEXT_STATUS_UNCHANGED`: Cached context change was not needed
|
||||
- `KM_CORE_CONTEXT_STATUS_UPDATED`: Cached context was set to application
|
||||
context
|
||||
- `KM_CORE_CONTEXT_STATUS_CLEARED`: Application context was invalid, perhaps
|
||||
had unpaired surrogates, and so cached context was cleared instead
|
||||
- `KM_CORE_CONTEXT_STATUS_ERROR`: Internal error
|
||||
- `KM_CORE_CONTEXT_STATUS_INVALID_ARGUMENT`: One or more parameters was null
|
||||
|
||||
```c
|
||||
*/
|
||||
|
||||
KMN_API
|
||||
km_core_context_status
|
||||
km_core_state_context_set_if_needed(
|
||||
km_core_state *state,
|
||||
km_core_cp const *application_context
|
||||
);
|
||||
|
||||
/*
|
||||
```
|
||||
### `km_core_state_context_clear`
|
||||
##### Description:
|
||||
Clears the internal cached context for the state. This is the same as
|
||||
`km_core_context_clear(km_core_state_context(&state))`.
|
||||
|
||||
`km_core_state_context_set_if_needed` and `km_core_state_context_clear`
|
||||
will replace most uses of the existing Core context APIs.
|
||||
|
||||
##### Parameters:
|
||||
- __state__: An opaque pointer to a state object.
|
||||
##### Return status:
|
||||
- `KM_CORE_STATUS_OK`: On success.
|
||||
- `KM_CORE_STATUS_INVALID_ARGUMENT`: If any parameters are null.
|
||||
|
||||
```c
|
||||
*/
|
||||
KMN_API
|
||||
km_core_status
|
||||
km_core_state_context_clear(
|
||||
km_core_state *state
|
||||
);
|
||||
|
||||
/*
|
||||
```
|
||||
|
|
@ -936,7 +1123,7 @@ km_core_state_context(km_core_state *state);
|
|||
|
||||
/*
|
||||
```
|
||||
### `kbp_state_get_intermediate_context`
|
||||
### `km_core_state_get_intermediate_context`
|
||||
##### Description:
|
||||
Get access to the state object's keyboard processor's intermediate context. This context
|
||||
is used during an IMX callback, part way through processing a keystroke.
|
||||
|
|
@ -950,7 +1137,7 @@ to `km_core_context_items_dispose`.
|
|||
*/
|
||||
KMN_API
|
||||
km_core_status
|
||||
kbp_state_get_intermediate_context(km_core_state *state, km_core_context_item ** context_items);
|
||||
km_core_state_get_intermediate_context(km_core_state *state, km_core_context_item ** context_items);
|
||||
|
||||
/*
|
||||
```
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@
|
|||
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
typedef wchar_t const * km_core_path_name;
|
||||
#define _KM_KBP_PATH_SEPARATOR (L'\\')
|
||||
#define _KM_KBP_EXT_SEPARATOR (L'.')
|
||||
#define _KM_CORE_PATH_SEPARATOR (L'\\')
|
||||
#define _KM_CORE_EXT_SEPARATOR (L'.')
|
||||
#if defined __GNUC__ // These three will be redefined for Windows
|
||||
#undef _kmn_export_flag
|
||||
#undef _kmn_import_flag
|
||||
|
|
@ -44,14 +44,14 @@
|
|||
#define _kmn_static_flag
|
||||
#else
|
||||
typedef char const * km_core_path_name;
|
||||
#define _KM_KBP_PATH_SEPARATOR ('/')
|
||||
#define _KM_KBP_EXT_SEPARATOR ('.')
|
||||
#define _KM_CORE_PATH_SEPARATOR ('/')
|
||||
#define _KM_CORE_EXT_SEPARATOR ('.')
|
||||
#endif
|
||||
|
||||
#if defined KMN_KBP_STATIC
|
||||
#if defined KM_CORE_LIBRARY_STATIC
|
||||
#define KMN_API _kmn_tag_fn(_kmn_static_flag)
|
||||
#define KMN_DEPRECATED_API _kmn_tag_fn(_kmn_deprecated_flag _kmn_and _kmn_static_flag)
|
||||
#elif defined KMN_KBP_EXPORTING
|
||||
#elif defined KM_CORE_LIBRARY_EXPORTING
|
||||
#define KMN_API _kmn_tag_fn(_kmn_export_flag)
|
||||
#define KMN_DEPRECATED_API _kmn_tag_fn(_kmn_deprecated_flag _kmn_and _kmn_export_flag)
|
||||
#else
|
||||
|
|
@ -59,8 +59,8 @@
|
|||
#define KMN_DEPRECATED_API _kmn_tag_fn(_kmn_deprecated_flag _kmn_and _kmn_import_flag)
|
||||
#endif
|
||||
|
||||
#ifndef KMN_KBP
|
||||
#define KMN_KBP
|
||||
#ifndef KM_CORE_LIBRARY
|
||||
#define KM_CORE_LIBRARY
|
||||
#endif
|
||||
#ifndef USE_CHAR16_T
|
||||
#define USE_CHAR16_T
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ builder_describe "Build Keyman ldml-keyboard-constants package" \
|
|||
|
||||
builder_describe_outputs \
|
||||
configure /node_modules \
|
||||
build /core/include/ldml/build/keyboardprocessor_ldml.js
|
||||
build /core/include/ldml/build/keyman_core_ldml.js
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
// Generated File - do not edit
|
||||
//
|
||||
// This file is generated by core/tools/ldml-const-builder/build.sh
|
||||
// based on core/include/ldml/keyboardprocessor_ldml.ts
|
||||
// based on core/include/ldml/keyman_core_ldml.ts
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
//
|
||||
// core/tools/ldml-const-builder/build.sh clean build run
|
||||
//
|
||||
// To update keyboardprocessor_ldml.h, and commit the result.
|
||||
// To update keyman_core_ldml.h, and commit the result.
|
||||
//
|
||||
// It is not updated automatically.
|
||||
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
Copyright: Copyright (C) 2022 SIL International.
|
||||
Authors: srl295
|
||||
This tool generates a .h version of the keyboardprocessor_ldml.ts file
|
||||
This tool generates a .h version of the keyman_core_ldml.ts file
|
||||
*/
|
||||
|
||||
import { constants } from './keyboardprocessor_ldml.js';
|
||||
import { constants } from './keyman_core_ldml.js';
|
||||
|
||||
const keys = Object.keys(constants);
|
||||
keys.sort();
|
||||
|
|
@ -20,7 +20,7 @@ console.log(`
|
|||
// Generated File - do not edit
|
||||
//
|
||||
// This file is generated by core/tools/ldml-const-builder/build.sh
|
||||
// based on core/include/ldml/keyboardprocessor_ldml.ts
|
||||
// based on core/include/ldml/keyman_core_ldml.ts
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
"unicode"
|
||||
],
|
||||
"files": [
|
||||
"/build/keyboardprocessor_ldml.*"
|
||||
"/build/keyman_core_ldml.*"
|
||||
],
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "build/keyboardprocessor_ldml.js",
|
||||
"main": "build/keyman_core_ldml.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/keymanapp/keyman.git"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"node_modules"
|
||||
],
|
||||
"files": [
|
||||
"keyboardprocessor_ldml.ts",
|
||||
"keyman_core_ldml.ts",
|
||||
"ldml-const-builder.ts"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,6 @@
|
|||
"node_modules"
|
||||
],
|
||||
"files": [
|
||||
"keyboardprocessor_ldml.ts"
|
||||
"keyman_core_ldml.ts"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
# Copyright: © 2018-2022 SIL International.
|
||||
# Description: Cross platform build script to compile libkmnkbp, documentation
|
||||
# Description: Cross platform build script to compile libkeymancore, documentation
|
||||
# and tests.
|
||||
# Create Date: 2 Oct 2018
|
||||
# Authors: Tim Eves (TSE)
|
||||
#
|
||||
|
||||
project('keyboardprocessor', 'cpp', 'c',
|
||||
project('keyman_core', 'cpp', 'c',
|
||||
version: run_command(find_program('getversion.bat', 'getversion.sh'), check:true).stdout().strip(),
|
||||
license: 'MIT',
|
||||
default_options : ['buildtype=release',
|
||||
|
|
@ -19,14 +19,14 @@ project('keyboardprocessor', 'cpp', 'c',
|
|||
# meson doesn't allow us to reference a file outside its root
|
||||
subdir('resources')
|
||||
|
||||
lib_version = '0.0.0'
|
||||
lib_version = '1.0.0'
|
||||
|
||||
py = import('python')
|
||||
python = py.find_installation()
|
||||
|
||||
# TODO: Shared includes may use namespaces, etc which need future tidyup.
|
||||
# For now, we use KMN_KBP to inject the km::kbp::kmx namespace
|
||||
defns += ['-DKMN_KBP']
|
||||
# For now, we use KM_CORE_LIBRARY to inject the km::core::kmx namespace
|
||||
defns += ['-DKM_CORE_LIBRARY']
|
||||
|
||||
# #define DEBUG when we are on a debug build
|
||||
if get_option('buildtype') == 'debug'
|
||||
|
|
@ -36,4 +36,4 @@ endif
|
|||
subdir('doc')
|
||||
subdir('include')
|
||||
subdir('src')
|
||||
subdir('tests')
|
||||
subdir('tests')
|
||||
|
|
|
|||
138
core/src/action.cpp
Normal file
138
core/src/action.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
Copyright: © 2023 SIL International.
|
||||
Description: Implementation of the action API functions using internal
|
||||
data structures and functions.
|
||||
Create Date: 23 Oct 2023
|
||||
Authors: Marc Durdin (MCD)
|
||||
History: 23 Oct 2023 - MCD - Initial implementation from #9720
|
||||
*/
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
|
||||
#include <keyman/keyman_core_api.h>
|
||||
|
||||
#include "action.hpp"
|
||||
#include "state.hpp"
|
||||
#include "option.hpp"
|
||||
|
||||
km_core_actions * km::core::action_item_list_to_actions_object(
|
||||
km_core_action_item const *action_items
|
||||
) {
|
||||
assert(action_items != nullptr);
|
||||
if(action_items == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
km_core_status status = KM_CORE_STATUS_OK;
|
||||
|
||||
std::unique_ptr<km_core_actions> actions(new km_core_actions);
|
||||
|
||||
// Set actions default values
|
||||
std::vector<km_core_context_item> output;
|
||||
std::vector<km_core_option_item> options;
|
||||
actions->code_points_to_delete = 0;
|
||||
actions->do_alert = KM_CORE_FALSE;
|
||||
actions->emit_keystroke = KM_CORE_FALSE;
|
||||
actions->new_caps_lock_state = KM_CORE_CAPS_UNCHANGED;
|
||||
|
||||
// Clear output pointers, will be set later once we have sizes
|
||||
actions->output = nullptr;
|
||||
actions->persist_options = nullptr;
|
||||
|
||||
for (; action_items->type != KM_CORE_IT_END; ++action_items) {
|
||||
assert(action_items->type < KM_CORE_IT_MAX_TYPE_ID);
|
||||
|
||||
switch(action_items->type) {
|
||||
case KM_CORE_IT_ALERT:
|
||||
actions->do_alert = KM_CORE_TRUE;
|
||||
break;
|
||||
case KM_CORE_IT_BACK:
|
||||
switch(action_items->backspace.expected_type) {
|
||||
case KM_CORE_BT_UNKNOWN:
|
||||
// this is equivalent to emit_keystroke, because the only time we
|
||||
// are allowed to do an unknown bksp is when a bksp is passed in
|
||||
actions->emit_keystroke = KM_CORE_TRUE;
|
||||
break;
|
||||
case KM_CORE_BT_CHAR:
|
||||
if(output.empty()) {
|
||||
actions->code_points_to_delete++;
|
||||
} else {
|
||||
auto last_context_item = output.back();
|
||||
output.pop_back();
|
||||
assert(last_context_item.type == KM_CORE_CT_CHAR);
|
||||
assert(last_context_item.character == action_items->backspace.expected_value);
|
||||
}
|
||||
break;
|
||||
case KM_CORE_BT_MARKER:
|
||||
if(output.empty()) {
|
||||
// deleting a marker has no effect on the application
|
||||
} else {
|
||||
auto last_context_item = output.back();
|
||||
output.pop_back();
|
||||
assert(last_context_item.type == KM_CORE_CT_MARKER);
|
||||
assert(last_context_item.marker == action_items->backspace.expected_value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
case KM_CORE_IT_CAPSLOCK:
|
||||
actions->new_caps_lock_state = action_items->capsLock ? KM_CORE_CAPS_ON : KM_CORE_CAPS_OFF;
|
||||
break;
|
||||
case KM_CORE_IT_CHAR:
|
||||
output.push_back({KM_CORE_CT_CHAR,{0},{action_items->character}});
|
||||
break;
|
||||
case KM_CORE_IT_EMIT_KEYSTROKE:
|
||||
actions->emit_keystroke = KM_CORE_TRUE;
|
||||
break;
|
||||
case KM_CORE_IT_INVALIDATE_CONTEXT:
|
||||
// no-op
|
||||
break;
|
||||
case KM_CORE_IT_MARKER:
|
||||
output.push_back({KM_CORE_CT_MARKER,{0},{action_items->marker}});
|
||||
break;
|
||||
case KM_CORE_IT_PERSIST_OPT:
|
||||
// TODO: lowpri: replace existing item if already present in options vector?
|
||||
options.push_back(km::core::option(
|
||||
static_cast<km_core_option_scope>(action_items->option->scope),
|
||||
action_items->option->key,
|
||||
action_items->option->value
|
||||
));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Strip the markers from the output, and convert to an string of UTF-32
|
||||
|
||||
output.push_back(KM_CORE_CONTEXT_ITEM_END);
|
||||
|
||||
size_t buf_size;
|
||||
|
||||
if((status = km_core_context_items_to_utf32(output.data(), nullptr, &buf_size)) != KM_CORE_STATUS_OK) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<km_core_usv[]> output_usv(new km_core_usv[buf_size]);
|
||||
|
||||
if((status = km_core_context_items_to_utf32(output.data(), output_usv.get(), &buf_size)) != KM_CORE_STATUS_OK) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
actions->output = output_usv.release();
|
||||
|
||||
// Create an array of the persisted options
|
||||
|
||||
options.push_back(KM_CORE_OPTIONS_END);
|
||||
actions->persist_options = new km_core_option_item[options.size()];
|
||||
std::copy(options.begin(), options.end(), actions->persist_options);
|
||||
|
||||
// We now have a complete set of actions
|
||||
|
||||
return actions.release();
|
||||
}
|
||||
20
core/src/action.hpp
Normal file
20
core/src/action.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright: © 2023 SIL International.
|
||||
Description: Internal actions methods for Keyman Core
|
||||
Create Date: 23 Oct 2023
|
||||
Authors: Marc Durdin (MCD)
|
||||
History: 23 Oct 2023 - MCD - Initial implementation
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <keyman/keyman_core_api.h>
|
||||
|
||||
namespace km {
|
||||
namespace core
|
||||
{
|
||||
km_core_actions* action_item_list_to_actions_object(
|
||||
km_core_action_item const *action_items
|
||||
);
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
class json;
|
||||
|
||||
namespace km {
|
||||
namespace kbp
|
||||
namespace core
|
||||
{
|
||||
|
||||
// This will likely be replaced with a class implementing a more space
|
||||
|
|
@ -39,13 +39,13 @@ void context::push_marker(uint32_t marker) {
|
|||
emplace_back(km_core_context_item { KM_CORE_CT_MARKER, {0,}, {marker} });
|
||||
}
|
||||
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
||||
json & operator << (json &, km::kbp::context const &);
|
||||
json & operator << (json &, km::core::context const &);
|
||||
json & operator << (json &, km_core_context_item const &);
|
||||
|
||||
|
||||
struct km_core_context : public km::kbp::context
|
||||
struct km_core_context : public km::core::context
|
||||
{
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
#include <keyman/keyman_core_api_debug.h>
|
||||
|
||||
namespace km {
|
||||
namespace kbp
|
||||
namespace core
|
||||
{
|
||||
|
||||
class debug_items : public std::vector<km_core_state_debug_item>
|
||||
|
|
@ -66,6 +66,6 @@ void debug_items::set_enabled(bool value) noexcept {
|
|||
_is_enabled = value;
|
||||
}
|
||||
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#include "debuglog.h"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
#define TAB "\t"
|
||||
|
|
@ -358,7 +358,7 @@ int DebugLog_1(const char *file, int line, const char *function, const char *fmt
|
|||
"%ld" TAB //"TickCount" TAB
|
||||
"%s:%d" TAB //"SourceFile" TAB
|
||||
"%s" TAB //"Function"
|
||||
"%s" NL, //"Message"
|
||||
"%s", //"Message"
|
||||
|
||||
GetTickCount(), //"TickCount" TAB
|
||||
file, line, //"SourceFile" TAB
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
/* Debugging */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <keyman/keyman_core_api_bits.h>
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
extern KMX_BOOL g_debug_ToConsole, g_debug_KeymanLog, g_silent;
|
||||
|
|
@ -17,11 +19,13 @@ extern const struct modifier_names s_modifier_names[];
|
|||
extern const char *s_key_names[];
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define DebugLog(msg,...) (km::kbp::kmx::ShouldDebug() ? km::kbp::kmx::DebugLog_1(__FILE__, __LINE__, __FUNCTION__, (msg),__VA_ARGS__) : 0)
|
||||
#define DebugLog(msg,...) (km::core::kmx::ShouldDebug() ? km::core::kmx::DebugLog_1(__FILE__, __LINE__, __FUNCTION__, (msg),__VA_ARGS__) : 0)
|
||||
#define DebugLog2(file,line,function,msg,...) (km::core::kmx::ShouldDebug() ? km::core::kmx::DebugLog_1(file, line, function, (msg),__VA_ARGS__) : 0)
|
||||
#define console_error(msg,...) write_console(TRUE, (msg), __VA_ARGS__)
|
||||
#define console_log(msg,...) write_console(FALSE, (msg), __VA_ARGS__)
|
||||
#else
|
||||
#define DebugLog(msg,...) (km::kbp::kmx::ShouldDebug() ? km::kbp::kmx::DebugLog_1(__FILE__, __LINE__, __FUNCTION__, (msg), ##__VA_ARGS__) : 0)
|
||||
#define DebugLog(msg,...) (km::core::kmx::ShouldDebug() ? km::core::kmx::DebugLog_1(__FILE__, __LINE__, __FUNCTION__, (msg), ##__VA_ARGS__) : 0)
|
||||
#define DebugLog2(file,line,function,msg,...) (km::core::kmx::ShouldDebug() ? km::core::kmx::DebugLog_1(file, line, function, (msg), ##__VA_ARGS__) : 0)
|
||||
#define console_error(msg,...) write_console(TRUE, (msg), ##__VA_ARGS__)
|
||||
#define console_log(msg,...) write_console(FALSE, (msg), ##__VA_ARGS__)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#include "keyboard.hpp"
|
||||
#include "jsonpp.hpp"
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
|
||||
|
||||
inline
|
||||
|
|
@ -54,7 +54,7 @@ keyboard_attributes & keyboard_attributes::operator = (keyboard_attributes &&rhs
|
|||
}
|
||||
|
||||
|
||||
json & km::kbp::operator << (json & j, km::kbp::keyboard_attributes const & kb)
|
||||
json & km::core::operator << (json & j, km::core::keyboard_attributes const & kb)
|
||||
{
|
||||
j << json::object
|
||||
<< "id" << kb.id
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@
|
|||
class json;
|
||||
|
||||
namespace km {
|
||||
namespace kbp
|
||||
namespace core
|
||||
{
|
||||
class keyboard_attributes : public km_core_keyboard_attrs
|
||||
{
|
||||
std::u16string _keyboard_id;
|
||||
std::u16string _version_string;
|
||||
kbp::path _folder_path;
|
||||
core::path _folder_path;
|
||||
std::vector<option> _default_opts;
|
||||
|
||||
void render();
|
||||
|
|
@ -48,7 +48,7 @@ namespace kbp
|
|||
keyboard_attributes & operator = (keyboard_attributes const &) = delete;
|
||||
keyboard_attributes & operator = (keyboard_attributes &&);
|
||||
|
||||
friend json & operator << (json &, km::kbp::keyboard_attributes const &);
|
||||
friend json & operator << (json &, km::core::keyboard_attributes const &);
|
||||
|
||||
options_store const & default_opts_store() const noexcept { return _default_opts; }
|
||||
options_store & default_opts_store() noexcept { return _default_opts; }
|
||||
|
|
@ -56,7 +56,7 @@ namespace kbp
|
|||
path_type const & path() const noexcept { return _folder_path; }
|
||||
};
|
||||
|
||||
json & operator << (json &, km::kbp::keyboard_attributes const &);
|
||||
json & operator << (json &, km::core::keyboard_attributes const &);
|
||||
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
64
core/src/km_core_action_api.cpp
Normal file
64
core/src/km_core_action_api.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
Copyright: © 2023 SIL International.
|
||||
Description: Implementation of the action API functions using internal
|
||||
data structures and functions.
|
||||
Create Date: 23 Oct 2023
|
||||
Authors: Marc Durdin (MCD)
|
||||
History: 23 Oct 2023 - MCD - Initial implementation.
|
||||
*/
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <keyman/keyman_core_api.h>
|
||||
#include "jsonpp.hpp"
|
||||
|
||||
#include "processor.hpp"
|
||||
#include "state.hpp"
|
||||
#include "action.hpp"
|
||||
|
||||
using namespace km::core;
|
||||
|
||||
km_core_actions* km_core_state_get_actions(
|
||||
km_core_state const *state
|
||||
) {
|
||||
assert(state);
|
||||
if(!state) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
km_core_actions* actions = nullptr;
|
||||
auto action_items = km_core_state_action_items(state, nullptr);
|
||||
if(!action_items) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
actions = action_item_list_to_actions_object(action_items);
|
||||
return actions;
|
||||
}
|
||||
|
||||
km_core_status km_core_actions_dispose(
|
||||
km_core_actions* actions
|
||||
) {
|
||||
if(actions == nullptr) {
|
||||
return KM_CORE_STATUS_OK;
|
||||
}
|
||||
|
||||
if(actions->output) {
|
||||
delete[] actions->output;
|
||||
}
|
||||
|
||||
if(actions->persist_options) {
|
||||
for(auto option = actions->persist_options; option->scope; option++) {
|
||||
delete[] option->key;
|
||||
delete[] option->value;
|
||||
}
|
||||
delete[] actions->persist_options;
|
||||
}
|
||||
|
||||
delete actions;
|
||||
|
||||
return KM_CORE_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -140,6 +140,13 @@ km_core_status km_core_context_items_to_utf16(km_core_context_item const *ci,
|
|||
sz_ptr);
|
||||
}
|
||||
|
||||
km_core_status km_core_context_items_to_utf32(km_core_context_item const *ci,
|
||||
km_core_usv *buf, size_t * sz_ptr)
|
||||
{
|
||||
return _context_items_to<utf32>(ci,
|
||||
reinterpret_cast<utf32::codeunit_t *>(buf),
|
||||
sz_ptr);
|
||||
}
|
||||
|
||||
void km_core_context_items_dispose(km_core_context_item *ci)
|
||||
{
|
||||
|
|
@ -252,7 +259,7 @@ km_core_context_item_list_size(km_core_context_item const *context_items)
|
|||
return n;
|
||||
}
|
||||
|
||||
json & operator << (json & j, km::kbp::context const & ctxt) {
|
||||
json & operator << (json & j, km::core::context const & ctxt) {
|
||||
j << json::array;
|
||||
for (auto & i: ctxt) j << i;
|
||||
return j << json::close;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
#include "processor.hpp"
|
||||
#include "state.hpp"
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
|
||||
km_core_status
|
||||
km_core_state_debug_set(
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#include "ldml/ldml_processor.hpp"
|
||||
#include "mock/mock_processor.hpp"
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
|
||||
#include <keyman/keyman_core_api.h>
|
||||
#include "jsonpp.hpp"
|
||||
|
|
@ -18,7 +19,7 @@
|
|||
#include "processor.hpp"
|
||||
#include "state.hpp"
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
|
||||
// Forward declarations
|
||||
class context;
|
||||
|
|
@ -69,7 +70,7 @@ km_core_context *km_core_state_context(km_core_state *state)
|
|||
return static_cast<km_core_context *>(&state->context());
|
||||
}
|
||||
|
||||
km_core_status kbp_state_get_intermediate_context(
|
||||
km_core_status km_core_state_get_intermediate_context(
|
||||
km_core_state *state,
|
||||
km_core_context_item ** context_items
|
||||
) {
|
||||
|
|
@ -259,3 +260,99 @@ void km_core_state_imx_deregister_callback(km_core_state *state)
|
|||
}
|
||||
state->imx_deregister_callback();
|
||||
}
|
||||
|
||||
bool is_context_valid(km_core_cp const * context, km_core_cp const * cached_context) {
|
||||
km_core_cp const* context_p = context;
|
||||
while(*context_p) {
|
||||
context_p++;
|
||||
}
|
||||
|
||||
km_core_cp const* cached_context_p = cached_context;
|
||||
while(*cached_context_p) {
|
||||
cached_context_p++;
|
||||
}
|
||||
|
||||
// we need to compare from the end of the cached context
|
||||
for(; context_p >= context && cached_context_p >= cached_context; context_p--, cached_context_p--) {
|
||||
if(*context_p != *cached_context_p) {
|
||||
// The cached context doesn't match the application context, so it is
|
||||
// invalid
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(cached_context_p > cached_context) {
|
||||
// if the cached context is longer than the application context, then we also
|
||||
// assume that it is invalid
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's acceptable for the application context to be longer than the cached
|
||||
// context, so if we match the whole cached context, we can safely return true
|
||||
return true;
|
||||
}
|
||||
|
||||
km_core_context_status km_core_state_context_set_if_needed(
|
||||
km_core_state *state,
|
||||
km_core_cp const *application_context
|
||||
) {
|
||||
assert(state != nullptr);
|
||||
assert(application_context != nullptr);
|
||||
if(state == nullptr || application_context == nullptr) {
|
||||
return KM_CORE_CONTEXT_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
size_t buf_size;
|
||||
km_core_context_item* context_items = nullptr;
|
||||
|
||||
auto context = km_core_state_context(state);
|
||||
if(km_core_context_get(context, &context_items) != KM_CORE_STATUS_OK) {
|
||||
return KM_CORE_CONTEXT_STATUS_ERROR;
|
||||
}
|
||||
|
||||
if(km_core_context_items_to_utf16(context_items, nullptr, &buf_size) != KM_CORE_STATUS_OK) {
|
||||
km_core_context_items_dispose(context_items);
|
||||
return KM_CORE_CONTEXT_STATUS_ERROR;
|
||||
}
|
||||
|
||||
std::unique_ptr<km_core_cp[]> cached_context(new km_core_cp[buf_size]);
|
||||
|
||||
km_core_status status = km_core_context_items_to_utf16(context_items, cached_context.get(), &buf_size);
|
||||
km_core_context_items_dispose(context_items);
|
||||
|
||||
if(status != KM_CORE_STATUS_OK) {
|
||||
return KM_CORE_CONTEXT_STATUS_ERROR;
|
||||
}
|
||||
|
||||
bool is_valid = is_context_valid(application_context, cached_context.get());
|
||||
|
||||
if(is_valid) {
|
||||
// We keep the context as is
|
||||
return KM_CORE_CONTEXT_STATUS_UNCHANGED;
|
||||
}
|
||||
|
||||
km_core_context_item* new_context_items = nullptr;
|
||||
|
||||
// We replace the cached context with the current application context
|
||||
status = km_core_context_items_from_utf16(application_context, &new_context_items);
|
||||
if (status != KM_CORE_STATUS_OK) {
|
||||
km_core_context_clear(context);
|
||||
return KM_CORE_CONTEXT_STATUS_CLEARED;
|
||||
}
|
||||
|
||||
km_core_context_set(context, new_context_items);
|
||||
km_core_context_items_dispose(new_context_items);
|
||||
return KM_CORE_CONTEXT_STATUS_UPDATED;
|
||||
}
|
||||
|
||||
|
||||
km_core_status km_core_state_context_clear(
|
||||
km_core_state *state
|
||||
) {
|
||||
assert(state != nullptr);
|
||||
if(state == nullptr) {
|
||||
return KM_CORE_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
km_core_context_clear(km_core_state_context(state));
|
||||
return KM_CORE_STATUS_OK;
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
#include <kmx/kmx_processevent.h>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
void KMX_Actions::ResetQueue()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#include "kmx_base.h"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
#define MAXACTIONQUEUE 1024
|
||||
|
|
@ -59,6 +59,6 @@ public:
|
|||
};
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#endif
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
typedef struct tagSTORE
|
||||
|
|
@ -95,5 +95,5 @@ typedef struct tagKMSTATE
|
|||
enum ProcessStringReturn { psrPostMessages, psrCheckMatches };
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include <keyman/keyman_core_api_consts.h>
|
||||
#include <kmx/kmx_processevent.h>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#include "kmx_processevent.h"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
const struct char_to_vkey s_char_to_vkey[] = {
|
||||
|
|
@ -105,5 +105,5 @@ const struct char_to_vkey s_char_to_vkey[] = {
|
|||
};
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
#include <kmx/kmx_processevent.h>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
/* KMX_Context */
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#define MAXCONTEXT 64
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
class KMX_Context
|
||||
|
|
@ -118,5 +118,5 @@ public:
|
|||
};
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "kmx_processevent.h"
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
/* This array is lifted from preservedkeymap.cpp */
|
||||
|
|
@ -116,7 +116,7 @@ const struct
|
|||
};
|
||||
|
||||
|
||||
KMX_BOOL km::kbp::kmx::MapUSCharToVK(KMX_WORD ch, PKMX_WORD puKey, PKMX_DWORD puShiftFlags) {
|
||||
KMX_BOOL km::core::kmx::MapUSCharToVK(KMX_WORD ch, PKMX_WORD puKey, PKMX_DWORD puShiftFlags) {
|
||||
assert(puKey != NULL);
|
||||
assert(puShiftFlags != NULL);
|
||||
if (ch >= 0x20 && ch < 0x7F) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#include "kmx_base.h"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
#define VK_SPACE 0x20
|
||||
|
|
@ -32,5 +32,5 @@ namespace kmx {
|
|||
KMX_BOOL MapUSCharToVK(KMX_WORD ch, PKMX_WORD puKey, PKMX_DWORD puShiftFlags);
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include <kmx/kmx_processevent.h>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
void KMX_DebugItems::push_item(
|
||||
|
|
@ -93,4 +93,4 @@ void KMX_DebugItems::fill_store_offsets(km_core_state_debug_kmx_info *info, PKMX
|
|||
}
|
||||
|
||||
info->store_offsets[n] = 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#include <debug.hpp>
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
class KMX_DebugItems
|
||||
|
|
@ -143,5 +143,5 @@ KMX_DebugItems::push_rule_exit(
|
|||
}
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace km
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include <state.hpp>
|
||||
#include <option.hpp>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
namespace {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include "kmx_base.h"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
class KMX_Environment {
|
||||
|
|
@ -30,5 +30,5 @@ public:
|
|||
};
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#include <assert.h>
|
||||
#include "kmx_file_validator.hpp"
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
|
@ -20,7 +20,7 @@ KMX_BOOL KMX_ProcessEvent::Load(km_core_path_name KeyboardName)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
const int km::kbp::kmx::CODE__SIZE[] = {
|
||||
const int km::core::kmx::CODE__SIZE[] = {
|
||||
-1, // undefined 0x00
|
||||
1, // CODE_ANY 0x01
|
||||
2, // CODE_INDEX 0x02
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
#include <kmx_file.h>
|
||||
|
||||
#ifdef KMN_KBP
|
||||
#ifdef KM_CORE_LIBRARY
|
||||
// TODO: move this to a common namespace keyman::common::kmx_file or similar in the future
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
#endif
|
||||
|
||||
|
|
@ -27,8 +27,8 @@ public:
|
|||
};
|
||||
|
||||
|
||||
#ifdef KMN_KBP
|
||||
#ifdef KM_CORE_LIBRARY
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
#include "kmx_processevent.h"
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
#define MAX_RSHIFT 24
|
||||
|
|
@ -22,7 +22,7 @@ static KMX_DWORD legalRuleStates[MAX_RSHIFT] = {
|
|||
};
|
||||
|
||||
// Simulate RALT -> LCtrl+RAlt -> columns marked with *
|
||||
//
|
||||
//
|
||||
// Truth table: matching key states
|
||||
// 1 = yes match always
|
||||
// 2 = RAlt mapped as LCtrl+RAlt by keyboard driver
|
||||
|
|
@ -109,14 +109,14 @@ KMX_BOOL KMX_ProcessEvent::IsEquivalentShift(KMX_UINT rshift, KMX_UINT kshift) {
|
|||
|
||||
for(i = 0; i < MAX_RSHIFT; i++) {
|
||||
if(rshift == legalRuleStates[i]) {
|
||||
rshift = i;
|
||||
rshift = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i == MAX_RSHIFT) return FALSE;
|
||||
|
||||
for(i = 0; i < MAX_KSHIFT; i++) {
|
||||
if(kshift == legalKeyStates[i]) {
|
||||
if(kshift == legalKeyStates[i]) {
|
||||
kshift = i;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#include "kmx_processevent.h"
|
||||
#include <option.hpp>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
int KMX_Options::_GetIndex(std::u16string const &key) const {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#include "kmx_debugger.h"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
|
||||
class abstract_processor;
|
||||
|
||||
|
|
@ -68,5 +68,5 @@ STORE const * KMX_Options::end() const {
|
|||
}
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@
|
|||
#include <kmx/kmx_xstring.h>
|
||||
|
||||
#include "kmx_processevent.h" // for debug functions
|
||||
#include "ldml/keyboardprocessor_ldml.h"
|
||||
#include "ldml/keyman_core_ldml.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include "kmx_plus.h"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
/**
|
||||
|
|
@ -381,7 +381,7 @@ COMP_KMXPLUS_ELEM_ELEMENT::loadAsStringList(KMX_DWORD length, const COMP_KMXPLUS
|
|||
if (o.type() == LDML_ELEM_FLAGS_TYPE_STR) {
|
||||
// fetch the string
|
||||
const auto str16 = strs.get(o.element);
|
||||
str = km::kbp::kmx::u16string_to_u32string(str16);
|
||||
str = km::core::kmx::u16string_to_u32string(str16);
|
||||
} else {
|
||||
// single char
|
||||
str = std::u32string(1, (km_core_usv)o.element);
|
||||
|
|
@ -1289,5 +1289,5 @@ const COMP_KMXPLUS_VARS_ITEM *COMP_KMXPLUS_VARS::findByStringId(KMX_DWORD strId)
|
|||
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@
|
|||
#include <km_types.h>
|
||||
#include <kmx/kmx_base.h>
|
||||
#include <kmx_file.h>
|
||||
#include <ldml/keyboardprocessor_ldml.h>
|
||||
#include <ldml/keyman_core_ldml.h>
|
||||
#include <list>
|
||||
#include <deque>
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
/**
|
||||
|
|
@ -111,7 +111,7 @@ struct COMP_KMXPLUS_ELEM_ELEMENT {
|
|||
* @param length number of elements, including this one
|
||||
* @return the string elements as a string array
|
||||
*/
|
||||
std::deque<std::u32string> loadAsStringList(KMX_DWORD length, const km::kbp::kmx::COMP_KMXPLUS_STRS &strs) const;
|
||||
std::deque<std::u32string> loadAsStringList(KMX_DWORD length, const km::core::kmx::COMP_KMXPLUS_STRS &strs) const;
|
||||
|
||||
/** @return element type */
|
||||
KMX_DWORD type() const;
|
||||
|
|
@ -782,5 +782,5 @@ class kmx_plus {
|
|||
#endif
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@
|
|||
#include "state.hpp"
|
||||
#include <keyman/keyman_core_api_consts.h>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
/* Globals */
|
||||
|
||||
KMX_BOOL km::kbp::kmx::g_debug_ToConsole = FALSE;
|
||||
KMX_BOOL km::kbp::kmx::g_debug_KeymanLog = TRUE;
|
||||
KMX_BOOL km::kbp::kmx::g_silent = FALSE;
|
||||
KMX_BOOL km::core::kmx::g_debug_ToConsole = FALSE;
|
||||
KMX_BOOL km::core::kmx::g_debug_KeymanLog = TRUE;
|
||||
KMX_BOOL km::core::kmx::g_silent = FALSE;
|
||||
|
||||
/*
|
||||
* KMX_ProcessEvent
|
||||
|
|
@ -84,7 +84,7 @@ KMX_BOOL KMX_ProcessEvent::ProcessEvent(
|
|||
) {
|
||||
LPKEYBOARD kbd = m_keyboard.Keyboard;
|
||||
|
||||
m_kbp_state = state;
|
||||
m_core_state = state;
|
||||
|
||||
// If debugging is enabled, then ...
|
||||
DeleteInternalDebugItems();
|
||||
|
|
@ -106,7 +106,7 @@ KMX_BOOL KMX_ProcessEvent::ProcessEvent(
|
|||
|
||||
if (kbd->StartGroup[BEGIN_UNICODE] == (KMX_DWORD) -1) {
|
||||
DebugLog("Non-Unicode keyboards are not supported.");
|
||||
m_kbp_state = nullptr;
|
||||
m_core_state = nullptr;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ KMX_BOOL KMX_ProcessEvent::ProcessEvent(
|
|||
}
|
||||
|
||||
if (!isKeyDown) {
|
||||
m_kbp_state = nullptr;
|
||||
m_core_state = nullptr;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ KMX_BOOL KMX_ProcessEvent::ProcessEvent(
|
|||
DeleteInternalDebugItems();
|
||||
}
|
||||
|
||||
m_kbp_state = nullptr;
|
||||
m_core_state = nullptr;
|
||||
|
||||
return !fOutputKeystroke;
|
||||
}
|
||||
|
|
@ -488,7 +488,7 @@ int KMX_ProcessEvent::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR end
|
|||
|
||||
case CODE_CALL:
|
||||
p++;
|
||||
m_kbp_state->imx_callback(*p-1);
|
||||
m_core_state->imx_callback(*p-1);
|
||||
FoundUse = TRUE;
|
||||
break;
|
||||
case CODE_USE: // use another group
|
||||
|
|
@ -519,7 +519,7 @@ int KMX_ProcessEvent::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR end
|
|||
case CODE_RESETOPT:
|
||||
p++;
|
||||
n1 = *p - 1;
|
||||
GetOptions()->Reset(m_kbp_state->processor(), n1);
|
||||
GetOptions()->Reset(m_core_state->processor(), n1);
|
||||
break;
|
||||
case CODE_SAVEOPT:
|
||||
p++;
|
||||
|
|
@ -792,8 +792,8 @@ PKMX_WCHAR KMX_ProcessEvent::GetSystemStore(LPKEYBOARD kb, KMX_DWORD SystemID)
|
|||
|
||||
void KMX_ProcessEvent::CreateInternalDebugItems() {
|
||||
assert(m_debug_items == nullptr);
|
||||
assert(m_kbp_state != nullptr);
|
||||
m_debug_items = new KMX_DebugItems(&m_kbp_state->debug_items());
|
||||
assert(m_core_state != nullptr);
|
||||
m_debug_items = new KMX_DebugItems(&m_core_state->debug_items());
|
||||
m_options.SetInternalDebugItems(m_debug_items);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef KMN_KBP
|
||||
#define KMN_KBP
|
||||
#ifndef KM_CORE_LIBRARY
|
||||
#define KM_CORE_LIBRARY
|
||||
#endif
|
||||
#ifndef USE_CHAR16_T
|
||||
#define USE_CHAR16_T
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
/***************************************************************************/
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
/* Utility */
|
||||
|
|
@ -40,7 +40,7 @@ private:
|
|||
PKMX_WCHAR m_miniContext;
|
||||
int m_miniContextIfLen; // number of if() statements excluded from start of m_miniContext
|
||||
KMSTATE m_state;
|
||||
km_core_state *m_kbp_state;
|
||||
km_core_state *m_core_state;
|
||||
|
||||
|
||||
kmx::KMX_Actions m_actions;
|
||||
|
|
@ -127,5 +127,5 @@ struct char_to_vkey {
|
|||
extern const struct char_to_vkey s_char_to_vkey[];
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#include "kmx/kmx_processor.hpp"
|
||||
#include <map>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
// TODO consolodate with appint.cpp and put in public library.
|
||||
|
|
@ -38,7 +38,7 @@ km_core_status kmx_processor::validate() const {
|
|||
return _valid ? KM_CORE_STATUS_OK : KM_CORE_STATUS_INVALID_KEYBOARD;
|
||||
}
|
||||
|
||||
kmx_processor::kmx_processor(kbp::path p) {
|
||||
kmx_processor::kmx_processor(core::path p) {
|
||||
p.replace_extension(".kmx");
|
||||
_valid = bool(_kmx.Load(p.c_str()));
|
||||
|
||||
|
|
@ -283,8 +283,8 @@ kmx_processor::process_event(
|
|||
switch (c->type) {
|
||||
case KM_CORE_CT_CHAR:
|
||||
{
|
||||
km::kbp::kmx::char16_single buf;
|
||||
const int len = km::kbp::kmx::Utf32CharToUtf16(c->character, buf);
|
||||
km::core::kmx::char16_single buf;
|
||||
const int len = km::core::kmx::Utf32CharToUtf16(c->character, buf);
|
||||
ctxt.append(buf.ch, len);
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
#include "processor.hpp"
|
||||
|
||||
namespace km {
|
||||
namespace kbp
|
||||
namespace core
|
||||
{
|
||||
class kmx_processor : public abstract_processor
|
||||
{
|
||||
|
|
@ -82,5 +82,5 @@ namespace kbp
|
|||
|
||||
};
|
||||
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@
|
|||
#include "kmx_processevent.h"
|
||||
#include "utfcodec.hpp"
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
using namespace kmx;
|
||||
|
||||
const km_core_cp *km::kbp::kmx::u16chr(const km_core_cp *p, km_core_cp ch) {
|
||||
const km_core_cp *km::core::kmx::u16chr(const km_core_cp *p, km_core_cp ch) {
|
||||
while (*p) {
|
||||
if (*p == ch) return p;
|
||||
p++;
|
||||
|
|
@ -20,7 +20,7 @@ const km_core_cp *km::kbp::kmx::u16chr(const km_core_cp *p, km_core_cp ch) {
|
|||
return ch == 0 ? p : NULL;
|
||||
}
|
||||
|
||||
const km_core_cp *km::kbp::kmx::u16cpy(km_core_cp *dst, const km_core_cp *src) {
|
||||
const km_core_cp *km::core::kmx::u16cpy(km_core_cp *dst, const km_core_cp *src) {
|
||||
km_core_cp *o = dst;
|
||||
while (*src) {
|
||||
*dst++ = *src++;
|
||||
|
|
@ -29,7 +29,7 @@ const km_core_cp *km::kbp::kmx::u16cpy(km_core_cp *dst, const km_core_cp *src) {
|
|||
return o;
|
||||
}
|
||||
|
||||
const km_core_cp *km::kbp::kmx::u16ncpy(km_core_cp *dst, const km_core_cp *src, size_t max) {
|
||||
const km_core_cp *km::core::kmx::u16ncpy(km_core_cp *dst, const km_core_cp *src, size_t max) {
|
||||
km_core_cp *o = dst;
|
||||
while (*src && max > 0) {
|
||||
*dst++ = *src++;
|
||||
|
|
@ -42,7 +42,7 @@ const km_core_cp *km::kbp::kmx::u16ncpy(km_core_cp *dst, const km_core_cp *src,
|
|||
return o;
|
||||
}
|
||||
|
||||
size_t km::kbp::kmx::u16len(const km_core_cp *p) {
|
||||
size_t km::core::kmx::u16len(const km_core_cp *p) {
|
||||
int i = 0;
|
||||
while (*p) {
|
||||
p++;
|
||||
|
|
@ -51,7 +51,7 @@ size_t km::kbp::kmx::u16len(const km_core_cp *p) {
|
|||
return i;
|
||||
}
|
||||
|
||||
int km::kbp::kmx::u16cmp(const km_core_cp *p, const km_core_cp *q) {
|
||||
int km::core::kmx::u16cmp(const km_core_cp *p, const km_core_cp *q) {
|
||||
while (*p && *q) {
|
||||
if (*p != *q) return *p - *q;
|
||||
p++;
|
||||
|
|
@ -60,7 +60,7 @@ int km::kbp::kmx::u16cmp(const km_core_cp *p, const km_core_cp *q) {
|
|||
return *p - *q;
|
||||
}
|
||||
|
||||
int km::kbp::kmx::u16icmp(const km_core_cp *p, const km_core_cp *q) {
|
||||
int km::core::kmx::u16icmp(const km_core_cp *p, const km_core_cp *q) {
|
||||
while (*p && *q) {
|
||||
if (toupper(*p) != toupper(*q)) return *p - *q;
|
||||
p++;
|
||||
|
|
@ -69,7 +69,7 @@ int km::kbp::kmx::u16icmp(const km_core_cp *p, const km_core_cp *q) {
|
|||
return *p - *q;
|
||||
}
|
||||
|
||||
int km::kbp::kmx::u16ncmp(const km_core_cp *p, const km_core_cp *q, size_t count) {
|
||||
int km::core::kmx::u16ncmp(const km_core_cp *p, const km_core_cp *q, size_t count) {
|
||||
while (*p && *q && count) {
|
||||
if (*p != *q) return *p - *q;
|
||||
p++;
|
||||
|
|
@ -81,7 +81,7 @@ int km::kbp::kmx::u16ncmp(const km_core_cp *p, const km_core_cp *q, size_t count
|
|||
return 0;
|
||||
}
|
||||
|
||||
km_core_cp *km::kbp::kmx::u16tok(km_core_cp *p, km_core_cp ch, km_core_cp **ctx) {
|
||||
km_core_cp *km::core::kmx::u16tok(km_core_cp *p, km_core_cp ch, km_core_cp **ctx) {
|
||||
if (!p) {
|
||||
p = *ctx;
|
||||
if (!p) return NULL;
|
||||
|
|
@ -103,7 +103,7 @@ km_core_cp *km::kbp::kmx::u16tok(km_core_cp *p, km_core_cp ch, km_core_cp **ctx)
|
|||
return p;
|
||||
}
|
||||
|
||||
km_core_cp *km::kbp::kmx::u16dup(km_core_cp *src) {
|
||||
km_core_cp *km::core::kmx::u16dup(km_core_cp *src) {
|
||||
km_core_cp *dup = new km_core_cp[u16len(src) + 1];
|
||||
memcpy(dup, src, (u16len(src) + 1) * sizeof(km_core_cp));
|
||||
return dup;
|
||||
|
|
@ -120,7 +120,7 @@ km_core_cp *km::kbp::kmx::u16dup(km_core_cp *src) {
|
|||
*
|
||||
* xstrlen calculates the length of a string, ignoring some special chars.
|
||||
*/
|
||||
PKMX_WCHAR km::kbp::kmx::incxstr(PKMX_WCHAR p) {
|
||||
PKMX_WCHAR km::core::kmx::incxstr(PKMX_WCHAR p) {
|
||||
|
||||
if (*p == 0)
|
||||
return p;
|
||||
|
|
@ -154,7 +154,7 @@ PKMX_WCHAR km::kbp::kmx::incxstr(PKMX_WCHAR p) {
|
|||
return p;
|
||||
}
|
||||
|
||||
PKMX_WCHAR km::kbp::kmx::decxstr(PKMX_WCHAR p, PKMX_WCHAR pStart)
|
||||
PKMX_WCHAR km::core::kmx::decxstr(PKMX_WCHAR p, PKMX_WCHAR pStart)
|
||||
{
|
||||
PKMX_WCHAR q;
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ PKMX_WCHAR km::kbp::kmx::decxstr(PKMX_WCHAR p, PKMX_WCHAR pStart)
|
|||
return p;
|
||||
}
|
||||
|
||||
int km::kbp::kmx::xstrlen_ignoreifopt(PKMX_WCHAR p)
|
||||
int km::core::kmx::xstrlen_ignoreifopt(PKMX_WCHAR p)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; *p; i++, p=incxstr(p))
|
||||
|
|
@ -207,21 +207,21 @@ int km::kbp::kmx::xstrlen_ignoreifopt(PKMX_WCHAR p)
|
|||
return i;
|
||||
}
|
||||
|
||||
int km::kbp::kmx::xstrlen(PKMX_WCHAR p)
|
||||
int km::core::kmx::xstrlen(PKMX_WCHAR p)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; *p; i++, p=incxstr(p));
|
||||
return i;
|
||||
}
|
||||
|
||||
int km::kbp::kmx::xstrpos(PKMX_WCHAR p1, PKMX_WCHAR p)
|
||||
int km::core::kmx::xstrpos(PKMX_WCHAR p1, PKMX_WCHAR p)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; p < p1; p = incxstr(p), i++);
|
||||
return i;
|
||||
}
|
||||
|
||||
PKMX_WCHAR km::kbp::kmx::xstrchr(PKMX_WCHAR buf, PKMX_WCHAR chr)
|
||||
PKMX_WCHAR km::core::kmx::xstrchr(PKMX_WCHAR buf, PKMX_WCHAR chr)
|
||||
{
|
||||
for(PKMX_WCHAR q = incxstr(buf); *buf; buf = q, q = incxstr(buf))
|
||||
if(!u16ncmp(buf, chr, (intptr_t)(q-buf)))
|
||||
|
|
@ -229,14 +229,14 @@ PKMX_WCHAR km::kbp::kmx::xstrchr(PKMX_WCHAR buf, PKMX_WCHAR chr)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int km::kbp::kmx::xchrcmp(PKMX_WCHAR ch1, PKMX_WCHAR ch2)
|
||||
int km::core::kmx::xchrcmp(PKMX_WCHAR ch1, PKMX_WCHAR ch2)
|
||||
{
|
||||
PKMX_WCHAR nch1 = incxstr(ch1);
|
||||
if(nch1 == ch1) return *ch2 - *ch1; /* comparing *ch2 to nul */
|
||||
return u16ncmp(ch1, ch2, (intptr_t)(nch1-ch1));
|
||||
}
|
||||
|
||||
PKMX_WCHAR km::kbp::kmx::strtowstr(PKMX_CHAR in)
|
||||
PKMX_WCHAR km::core::kmx::strtowstr(PKMX_CHAR in)
|
||||
{
|
||||
PKMX_WCHAR result;
|
||||
|
||||
|
|
@ -247,7 +247,7 @@ PKMX_WCHAR km::kbp::kmx::strtowstr(PKMX_CHAR in)
|
|||
return result;
|
||||
}
|
||||
|
||||
PKMX_CHAR km::kbp::kmx::wstrtostr(PKMX_WCHAR in)
|
||||
PKMX_CHAR km::core::kmx::wstrtostr(PKMX_WCHAR in)
|
||||
{
|
||||
PKMX_CHAR result;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#include "kmx_base.h"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace kmx {
|
||||
|
||||
const char16_t Uni_LEAD_SURROGATE_START = 0xD800;
|
||||
|
|
@ -25,12 +25,12 @@ const km_core_usv Uni_MAX_CODEPOINT = 0x10FFFF;
|
|||
* @brief True if a lead surrogate
|
||||
* \def Uni_IsSurrogate1
|
||||
*/
|
||||
#define Uni_IsSurrogate1(ch) ((ch) >= km::kbp::kmx::Uni_LEAD_SURROGATE_START && (ch) <= km::kbp::kmx::Uni_LEAD_SURROGATE_END)
|
||||
#define Uni_IsSurrogate1(ch) ((ch) >= km::core::kmx::Uni_LEAD_SURROGATE_START && (ch) <= km::core::kmx::Uni_LEAD_SURROGATE_END)
|
||||
/**
|
||||
* @brief True if a trail surrogate
|
||||
* \def Uni_IsSurrogate2
|
||||
*/
|
||||
#define Uni_IsSurrogate2(ch) ((ch) >= km::kbp::kmx::Uni_TRAIL_SURROGATE_START && (ch) <= km::kbp::kmx::Uni_TRAIL_SURROGATE_END)
|
||||
#define Uni_IsSurrogate2(ch) ((ch) >= km::core::kmx::Uni_TRAIL_SURROGATE_START && (ch) <= km::core::kmx::Uni_TRAIL_SURROGATE_END)
|
||||
|
||||
/**
|
||||
* @brief True if any surrogate
|
||||
|
|
@ -42,7 +42,7 @@ const km_core_usv Uni_MAX_CODEPOINT = 0x10FFFF;
|
|||
* @brief Returns true if BMP (Plane 0)
|
||||
* \def Uni_IsBMP
|
||||
*/
|
||||
#define Uni_IsBMP(ch) ((ch) <= km::kbp::kmx::Uni_BMP_END)
|
||||
#define Uni_IsBMP(ch) ((ch) <= km::core::kmx::Uni_BMP_END)
|
||||
|
||||
/**
|
||||
* @brief Convert two UTF-16 surrogates into one UTF-32 codepoint
|
||||
|
|
@ -50,7 +50,7 @@ const km_core_usv Uni_MAX_CODEPOINT = 0x10FFFF;
|
|||
* @param cl trail surrogate - Uni_IsSurrogate2(cl) must == true
|
||||
* \def Uni_SurrogateToUTF
|
||||
*/
|
||||
#define Uni_SurrogateToUTF32(ch, cl) (((ch) - km::kbp::kmx::Uni_LEAD_SURROGATE_START) * 0x400 + ((cl) - km::kbp::kmx::Uni_TRAIL_SURROGATE_START) + km::kbp::kmx::Uni_SMP_START)
|
||||
#define Uni_SurrogateToUTF32(ch, cl) (((ch) - km::core::kmx::Uni_LEAD_SURROGATE_START) * 0x400 + ((cl) - km::core::kmx::Uni_TRAIL_SURROGATE_START) + km::core::kmx::Uni_SMP_START)
|
||||
|
||||
/**
|
||||
* @brief Convert UTF-32 BMP to UTF-16 BMP
|
||||
|
|
@ -59,8 +59,8 @@ const km_core_usv Uni_MAX_CODEPOINT = 0x10FFFF;
|
|||
*/
|
||||
#define Uni_UTF32BMPToUTF16(ch) ((ch) & Uni_FFFF_NONCHARACTER)
|
||||
|
||||
#define Uni_UTF32ToSurrogate1(ch) (char16_t)(((ch) - km::kbp::kmx::Uni_SMP_START) / 0x400 + km::kbp::kmx::Uni_LEAD_SURROGATE_START)
|
||||
#define Uni_UTF32ToSurrogate2(ch) (char16_t)(((ch) - km::kbp::kmx::Uni_SMP_START) % 0x400 + km::kbp::kmx::Uni_TRAIL_SURROGATE_START)
|
||||
#define Uni_UTF32ToSurrogate1(ch) (char16_t)(((ch) - km::core::kmx::Uni_SMP_START) / 0x400 + km::core::kmx::Uni_LEAD_SURROGATE_START)
|
||||
#define Uni_UTF32ToSurrogate2(ch) (char16_t)(((ch) - km::core::kmx::Uni_SMP_START) % 0x400 + km::core::kmx::Uni_TRAIL_SURROGATE_START)
|
||||
|
||||
/**
|
||||
* @returns true if the character is a noncharacter
|
||||
|
|
@ -224,5 +224,5 @@ inline bool Uni_IsValid(km_core_usv start, km_core_usv end) {
|
|||
|
||||
|
||||
} // namespace kmx
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma
|
|||
|
||||
## Constants and Scaffolding
|
||||
|
||||
- *Edit/Commit*: `core/include/ldml/keyboardprocessor_ldml.ts`
|
||||
- *Edit/Commit*: `core/include/ldml/keyman_core_ldml.ts`
|
||||
- update `SectionIdent` and keep in order: `'layr' |`
|
||||
- update `Constants.section` (near the end of the file) and keep in order: `'layr': 'layr',`
|
||||
- add a comment block in order `layr section`
|
||||
|
|
@ -18,7 +18,7 @@ working on ‘layr’, using ‘disp’ as a model from https://github.com/keyma
|
|||
- add parameters for each flag/bitfield
|
||||
- Check indentation, check for copypasta errs!
|
||||
- Run: `./core/tools/ldml-const-builder/build.sh clean build run`
|
||||
- Verify/Commit: `core/include/ldml/keyboardprocessor_ldml.h`
|
||||
- Verify/Commit: `core/include/ldml/keyman_core_ldml.h`
|
||||
|
||||
## XML changes
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@
|
|||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include "ldml/ldml_processor.hpp"
|
||||
#include "state.hpp"
|
||||
#include "kmx_file.h"
|
||||
#include "kmx/kmx_plus.h"
|
||||
#include "kmx/kmx_xstring.h"
|
||||
#include "ldml/keyboardprocessor_ldml.h"
|
||||
#include "ldml/keyman_core_ldml.h"
|
||||
#include "kmx/kmx_file_validator.hpp"
|
||||
#include "debuglog.h"
|
||||
#include <assert.h>
|
||||
|
|
@ -27,10 +28,10 @@ namespace {
|
|||
};
|
||||
}
|
||||
|
||||
// using km::kbp::kmx::ShouldDebug; // for DebugLog
|
||||
// using km::core::kmx::ShouldDebug; // for DebugLog
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
|
||||
|
||||
ldml_processor::ldml_processor(path const & kb_path, const std::vector<uint8_t> &data)
|
||||
|
|
@ -88,7 +89,7 @@ ldml_processor::ldml_processor(path const & kb_path, const std::vector<uint8_t>
|
|||
|
||||
// load transforms
|
||||
if (kplus.tran != nullptr && kplus.tran->groupCount > 0) {
|
||||
transforms.reset(km::kbp::ldml::transforms::load(kplus, kplus.tran, kplus.tranHelper));
|
||||
transforms.reset(km::core::ldml::transforms::load(kplus, kplus.tran, kplus.tranHelper));
|
||||
if (!transforms) {
|
||||
DebugLog("Failed to load tran transforms");
|
||||
return; // failed to load
|
||||
|
|
@ -97,7 +98,7 @@ ldml_processor::ldml_processor(path const & kb_path, const std::vector<uint8_t>
|
|||
|
||||
// load bksp transforms
|
||||
if (kplus.bksp != nullptr && kplus.bksp->groupCount > 0) {
|
||||
bksp_transforms.reset(km::kbp::ldml::transforms::load(kplus, kplus.bksp, kplus.bkspHelper));
|
||||
bksp_transforms.reset(km::core::ldml::transforms::load(kplus, kplus.bksp, kplus.bkspHelper));
|
||||
if (!bksp_transforms) {
|
||||
DebugLog("Failed to load bksp transforms");
|
||||
return; // failed to load
|
||||
|
|
@ -236,88 +237,16 @@ ldml_processor::process_event(
|
|||
// all other VKs
|
||||
{
|
||||
// Look up the key
|
||||
const std::u16string str = keys.lookup(vk, modifier_state);
|
||||
const std::u16string key_str = keys.lookup(vk, modifier_state);
|
||||
|
||||
if (str.empty()) {
|
||||
if (key_str.empty()) {
|
||||
// no key was found, so pass the keystroke on to the Engine
|
||||
state->actions().push_invalidate_context();
|
||||
state->actions().push_emit_keystroke();
|
||||
break; // ----- commit and exit
|
||||
}
|
||||
|
||||
// found a string - push it into the context and actions
|
||||
// we convert it here instead of using the emit_text() overload
|
||||
// so that we don't have to reconvert it inside the transform code.
|
||||
const std::u32string str32 = kmx::u16string_to_u32string(str);
|
||||
|
||||
if (!transforms) {
|
||||
// No transforms: just emit the string.
|
||||
emit_text(state, str32);
|
||||
} else {
|
||||
// Process transforms here
|
||||
/**
|
||||
* a copy of the current/changed context, for transform use.
|
||||
*
|
||||
*/
|
||||
std::u32string ctxtstr;
|
||||
(void)context_to_string(state, ctxtstr);
|
||||
// add the newly added key output to ctxtstr
|
||||
ctxtstr.append(str32);
|
||||
|
||||
/** the output buffer for transforms */
|
||||
std::u32string outputString;
|
||||
|
||||
// apply the transform, get how much matched (at the end)
|
||||
const size_t matchedContext = transforms->apply(ctxtstr, outputString);
|
||||
|
||||
if (matchedContext == 0) {
|
||||
// No match, just emit the original string
|
||||
emit_text(state, str32);
|
||||
} else {
|
||||
// We have a match.
|
||||
|
||||
ctxtstr.resize(ctxtstr.length() - str32.length());
|
||||
/** how many chars of the context we need to clear */
|
||||
auto charsToDelete = matchedContext - str32.length(); /* we don't need to clear the output of the current key */
|
||||
|
||||
/** how many context items need to be removed */
|
||||
size_t contextRemoved = 0;
|
||||
for (auto c = state->context().rbegin(); charsToDelete > 0 && c != state->context().rend(); c++, contextRemoved++) {
|
||||
/** last char of context */
|
||||
km_core_usv lastCtx = ctxtstr.back();
|
||||
uint8_t type = c->type;
|
||||
assert(type == KM_CORE_BT_CHAR || type == KM_CORE_BT_MARKER);
|
||||
if (type == KM_CORE_BT_CHAR) {
|
||||
// single char, drop it
|
||||
charsToDelete--;
|
||||
assert(c->character == lastCtx);
|
||||
ctxtstr.pop_back();
|
||||
state->actions().push_backspace(KM_CORE_BT_CHAR, lastCtx); // Cause prior char to be removed
|
||||
} else if (type == KM_CORE_BT_MARKER) {
|
||||
// it's a marker, 'worth' 3 uchars
|
||||
assert(charsToDelete >= 3);
|
||||
assert(lastCtx == c->marker); // end of list
|
||||
charsToDelete -= 3;
|
||||
// pop off the three-part sentinel string
|
||||
ctxtstr.pop_back();
|
||||
ctxtstr.pop_back();
|
||||
ctxtstr.pop_back();
|
||||
// push a special backspace to delete the marker
|
||||
state->actions().push_backspace(KM_CORE_BT_MARKER, c->marker);
|
||||
}
|
||||
}
|
||||
// now, pop the right number of context items
|
||||
for (size_t i = 0; i < contextRemoved; i++) {
|
||||
// we don't pop during the above loop because the iterator gets confused
|
||||
state->context().pop_back();
|
||||
}
|
||||
// Now, add in the updated text. This will convert UC_SENTINEL, etc back to marker actions.
|
||||
emit_text(state, outputString);
|
||||
// If we needed it further. we could update ctxtstr here:
|
||||
// ctxtstr.append(outputString);
|
||||
// ... but it is no longer needed at this point.
|
||||
} // end of transform match
|
||||
} // end of processing transforms
|
||||
process_key_string(state, key_str);
|
||||
} // end of processing a 'normal' vk
|
||||
} // end of switch
|
||||
// end of normal processing: commit and exit
|
||||
|
|
@ -330,6 +259,103 @@ ldml_processor::process_event(
|
|||
return KM_CORE_STATUS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ldml_processor::process_key_string(km_core_state *state, const std::u16string &key_str) const {
|
||||
// We know that key_str is not empty per the caller.
|
||||
assert(!key_str.empty());
|
||||
|
||||
// we convert the keys str to UTF-32 here instead of using the emit_text() overload
|
||||
// so that we don't have to reconvert it inside the transform code.
|
||||
std::u32string key_str32 = kmx::u16string_to_u32string(key_str);
|
||||
assert(ldml::normalize_nfd(key_str32)); // TODO-LDML: else fail?
|
||||
|
||||
// extract context string, in NFC
|
||||
std::u32string old_ctxtstr_nfc;
|
||||
(void)context_to_string(state, old_ctxtstr_nfc, false);
|
||||
assert(ldml::normalize_nfc(old_ctxtstr_nfc)); // TODO-LDML: else fail?
|
||||
|
||||
// context string in NFD
|
||||
std::u32string ctxtstr;
|
||||
(void)context_to_string(state, ctxtstr, true); // with markers
|
||||
// add the newly added key output to ctxtstr
|
||||
ctxtstr.append(key_str32);
|
||||
assert(ldml::normalize_nfd(ctxtstr)); // TODO-LDML: else fail?
|
||||
|
||||
/** transform output string */
|
||||
std::u32string outputString;
|
||||
/** how many chars of the ctxtstr to replace */
|
||||
size_t matchedContext = 0; // zero if no transforms
|
||||
|
||||
// begin modifications to the string
|
||||
|
||||
if(transforms) {
|
||||
matchedContext = transforms->apply(ctxtstr, outputString);
|
||||
} else {
|
||||
// no transforms, no output
|
||||
}
|
||||
|
||||
// drop last 'matchedContext':
|
||||
ctxtstr.resize(ctxtstr.length() - matchedContext);
|
||||
ctxtstr.append(outputString); // TODO-LDML: should be able to do a normalization-safe append here.
|
||||
assert(ldml::normalize_nfd(ctxtstr)); // TODO-LDML: else fail?
|
||||
|
||||
// Ok. We've done all the happy manipulations.
|
||||
|
||||
/** NFC and no markers */
|
||||
std::u32string ctxtstr_cleanedup = ctxtstr;
|
||||
// TODO-LDML: remove markers!
|
||||
assert(ldml::normalize_nfc(ctxtstr_cleanedup)); // TODO-LDML: else fail?
|
||||
|
||||
// find common prefix
|
||||
auto ctxt_prefix = mismatch(old_ctxtstr_nfc.begin(), old_ctxtstr_nfc.end(), ctxtstr_cleanedup.begin(), ctxtstr_cleanedup.end());
|
||||
/** the part of the old str that changed */
|
||||
std::u32string old_ctxtstr_changed(ctxt_prefix.first,old_ctxtstr_nfc.end());
|
||||
std::u32string new_ctxtstr_changed(ctxt_prefix.second,ctxtstr_cleanedup.end());
|
||||
|
||||
// drop the old suffix. Note: this mutates old_ctxtstr_changed.
|
||||
remove_text(state, old_ctxtstr_changed, old_ctxtstr_changed.length());
|
||||
assert(old_ctxtstr_changed.length() == 0);
|
||||
emit_text(state, new_ctxtstr_changed);
|
||||
}
|
||||
|
||||
void
|
||||
ldml_processor::remove_text(km_core_state *state, std::u32string &str, size_t length) {
|
||||
/** how many context items need to be removed */
|
||||
size_t contextRemoved = 0;
|
||||
for (auto c = state->context().rbegin(); length > 0 && c != state->context().rend(); c++, contextRemoved++) {
|
||||
/** last char of context */
|
||||
km_core_usv lastCtx = str.back();
|
||||
uint8_t type = c->type;
|
||||
assert(type == KM_CORE_BT_CHAR || type == KM_CORE_BT_MARKER);
|
||||
if (type == KM_CORE_BT_CHAR) {
|
||||
// single char, drop it
|
||||
length--;
|
||||
assert(c->character == lastCtx);
|
||||
str.pop_back();
|
||||
state->actions().push_backspace(KM_CORE_BT_CHAR, c->character); // Cause prior char to be removed
|
||||
} else if (type == KM_CORE_BT_MARKER) {
|
||||
// it's a marker, 'worth' 3 uchars
|
||||
assert(length >= 3);
|
||||
assert(lastCtx == c->marker); // end of list
|
||||
length -= 3;
|
||||
// pop off the three-part sentinel string (in reverse order of course)
|
||||
assert(str.back() == c->marker); // marker #
|
||||
str.pop_back();
|
||||
assert(str.back() == LDML_MARKER_CODE);
|
||||
str.pop_back();
|
||||
assert(str.back() == LDML_UC_SENTINEL);
|
||||
str.pop_back();
|
||||
// push a special backspace to delete the marker
|
||||
state->actions().push_backspace(KM_CORE_BT_MARKER, c->marker);
|
||||
}
|
||||
}
|
||||
// now, pop the right number of context items
|
||||
for (size_t i = 0; i < contextRemoved; i++) {
|
||||
// we don't pop during the above loop because the iterator gets confused
|
||||
state->context().pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
km_core_attr const & ldml_processor::attributes() const {
|
||||
return engine_attrs;
|
||||
}
|
||||
|
|
@ -389,24 +415,26 @@ ldml_processor::emit_text(km_core_state *state, km_core_usv ch) {
|
|||
|
||||
void
|
||||
ldml_processor::emit_marker(km_core_state *state, KMX_DWORD marker_no) {
|
||||
assert(km::kbp::kmx::is_valid_marker(marker_no));
|
||||
assert(km::core::kmx::is_valid_marker(marker_no));
|
||||
state->actions().push_marker(marker_no);
|
||||
state->context().push_marker(marker_no);
|
||||
}
|
||||
|
||||
size_t
|
||||
ldml_processor::context_to_string(km_core_state *state, std::u32string &str) {
|
||||
ldml_processor::context_to_string(km_core_state *state, std::u32string &str, bool include_markers) {
|
||||
str.clear();
|
||||
auto &cp = state->context();
|
||||
size_t ctxlen = 0; // TODO-LDML: is this needed?
|
||||
size_t ctxlen = 0; // TODO-LDML: not used by callers?
|
||||
uint8_t last_type = KM_CORE_BT_UNKNOWN;
|
||||
for (auto c = cp.rbegin(); c != cp.rend(); c++, ctxlen++) {
|
||||
last_type = c->type;
|
||||
if (last_type == KM_CORE_BT_CHAR) {
|
||||
str.insert(0, 1, c->character);
|
||||
} else if (last_type == KM_CORE_BT_MARKER) {
|
||||
assert(km::kbp::kmx::is_valid_marker(c->marker));
|
||||
prepend_marker(str, c->marker);
|
||||
assert(km::core::kmx::is_valid_marker(c->marker));
|
||||
if (include_markers) {
|
||||
prepend_marker(str, c->marker);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
@ -414,6 +442,5 @@ ldml_processor::context_to_string(km_core_state *state, std::u32string &str) {
|
|||
return ctxlen; // consumed the entire context buffer.
|
||||
}
|
||||
|
||||
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include "ldml_transforms.hpp"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
|
||||
#define KM_CORE_LMDL_PROCESSOR_VERSION u"1.0"
|
||||
|
||||
|
|
@ -93,6 +93,15 @@ namespace kbp {
|
|||
static void emit_text(km_core_state *state, km_core_usv ch);
|
||||
/** emit a marker */
|
||||
static void emit_marker(km_core_state *state, KMX_DWORD marker);
|
||||
/**
|
||||
* Delete text from the state.
|
||||
* @param str string with text to remove, from the end
|
||||
* @param length number of chars from the end of str to drop
|
||||
*/
|
||||
static void remove_text(km_core_state *state, std::u32string &str, size_t length);
|
||||
|
||||
/** process a typed key */
|
||||
void process_key_string(km_core_state *state, const std::u16string &key_str) const;
|
||||
|
||||
/**
|
||||
* add the string+marker portion of the context to the beginning of str.
|
||||
|
|
@ -100,7 +109,7 @@ namespace kbp {
|
|||
* Convert markers into the UC_SENTINEL format.
|
||||
* @return the number of context items consumed
|
||||
*/
|
||||
static size_t context_to_string(km_core_state *state, std::u32string &str);
|
||||
static size_t context_to_string(km_core_state *state, std::u32string &str, bool include_markers = true);
|
||||
|
||||
/** prepend the marker string in UC_SENTINEL format to the str */
|
||||
inline static void prepend_marker(std::u32string &str, KMX_DWORD marker);
|
||||
|
|
@ -112,5 +121,5 @@ namespace kbp {
|
|||
str.insert(0, triple, 3);
|
||||
}
|
||||
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@
|
|||
#include "kmx/kmx_xstring.h"
|
||||
|
||||
#ifndef assert
|
||||
#define assert(x) // TODO-LDML
|
||||
#define assert(x) ((void)0)
|
||||
#endif
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace ldml {
|
||||
|
||||
/**
|
||||
|
|
@ -403,25 +403,29 @@ transform_entry::transform_entry(const transform_entry &other)
|
|||
|
||||
transform_entry::transform_entry(const std::u32string &from, const std::u32string &to)
|
||||
: fFrom(from), fTo(to), fFromPattern(nullptr), fMapFromStrId(), fMapToStrId(), fMapFromList(), fMapToList() {
|
||||
assert(!fFrom.empty()); // TODO-LDML: should not happen?
|
||||
assert(!fFrom.empty());
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
// TODO-LDML: How do we return errors from here?
|
||||
transform_entry::transform_entry(
|
||||
const std::u32string &from,
|
||||
const std::u32string &to,
|
||||
KMX_DWORD mapFrom,
|
||||
KMX_DWORD mapTo,
|
||||
const kmx::kmx_plus &kplus)
|
||||
const kmx::kmx_plus &kplus,
|
||||
bool &valid)
|
||||
: fFrom(from), fTo(to), fFromPattern(nullptr), fMapFromStrId(mapFrom), fMapToStrId(mapTo) {
|
||||
if (!valid)
|
||||
return; // exit early
|
||||
assert(!fFrom.empty()); // TODO-LDML: should not happen?
|
||||
assert((fMapFromStrId == 0) == (fMapToStrId == 0)); // we have both or we have neither.
|
||||
assert(kplus.strs != nullptr);
|
||||
assert(kplus.vars != nullptr);
|
||||
assert(kplus.elem != nullptr);
|
||||
init();
|
||||
if(!init()) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
// setup mapFrom
|
||||
if (fMapFromStrId != 0) {
|
||||
|
|
@ -456,18 +460,23 @@ transform_entry::transform_entry(
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
transform_entry::init() {
|
||||
if (!fFrom.empty()) {
|
||||
// TODO-LDML: if we have mapFrom, may need to do other processing.
|
||||
const std::u16string patstr = km::kbp::kmx::u32string_to_u16string(fFrom);
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
/* const */ icu::UnicodeString patustr = icu::UnicodeString(patstr.data(), (int32_t)patstr.length());
|
||||
// add '$' to match to end
|
||||
patustr.append(u'$');
|
||||
fFromPattern.reset(icu::RegexPattern::compile(patustr, 0, status));
|
||||
assert(U_SUCCESS(status)); // TODO-LDML: may be best to propagate status up ^^
|
||||
if (fFrom.empty()) {
|
||||
return false;
|
||||
}
|
||||
// TODO-LDML: if we have mapFrom, may need to do other processing.
|
||||
const std::u16string patstr = km::core::kmx::u32string_to_u16string(fFrom);
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
/* const */ icu::UnicodeString patustr_raw = icu::UnicodeString(patstr.data(), (int32_t)patstr.length());
|
||||
// add '$' to match to end
|
||||
patustr_raw.append(u'$');
|
||||
icu::UnicodeString patustr;
|
||||
const icu::Normalizer2 *nfd = icu::Normalizer2::getNFDInstance(status);
|
||||
// NFD normalize on pattern creation
|
||||
nfd->normalize(patustr_raw, patustr, status);
|
||||
fFromPattern.reset(icu::RegexPattern::compile(patustr, 0, status));
|
||||
return (UASSERT_SUCCESS(status));
|
||||
}
|
||||
|
||||
size_t
|
||||
|
|
@ -476,11 +485,11 @@ transform_entry::apply(const std::u32string &input, std::u32string &output) cons
|
|||
// TODO-LDML: Really? can't go from u32 to UnicodeString?
|
||||
// TODO-LDML: Also, we could cache the u16 string at the transformGroup level or higher.
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
const std::u16string matchstr = km::kbp::kmx::u32string_to_u16string(input);
|
||||
const std::u16string matchstr = km::core::kmx::u32string_to_u16string(input);
|
||||
icu::UnicodeString matchustr = icu::UnicodeString(matchstr.data(), (int32_t)matchstr.length());
|
||||
// TODO-LDML: create a new Matcher every time. These could be cached and reset.
|
||||
std::unique_ptr<icu::RegexMatcher> matcher(fFromPattern->matcher(matchustr, status));
|
||||
assert(U_SUCCESS(status));
|
||||
UASSERT_SUCCESS(status);
|
||||
|
||||
if (!matcher->find(status)) { // i.e. matches somewhere, in this case at end of str
|
||||
return 0; // no match
|
||||
|
|
@ -490,7 +499,7 @@ transform_entry::apply(const std::u32string &input, std::u32string &output) cons
|
|||
// TODO-LDML: if we had an underlying UText this would be simpler.
|
||||
int32_t matchStart = matcher->start(status);
|
||||
int32_t matchEnd = matcher->end(status);
|
||||
assert(U_SUCCESS(status));
|
||||
UASSERT_SUCCESS(status);
|
||||
// extract..
|
||||
const icu::UnicodeString substr = matchustr.tempSubStringBetween(matchStart, matchEnd);
|
||||
// preflight to UTF-32 to get length
|
||||
|
|
@ -509,7 +518,7 @@ transform_entry::apply(const std::u32string &input, std::u32string &output) cons
|
|||
// Normal case: not a map.
|
||||
// This replace will apply $1, $2 etc.
|
||||
// Convert the fTo into u16 TODO-LDML (we could cache this?)
|
||||
const std::u16string rstr = km::kbp::kmx::u32string_to_u16string(fTo);
|
||||
const std::u16string rstr = km::core::kmx::u32string_to_u16string(fTo);
|
||||
rustr = icu::UnicodeString(rstr.data(), (int32_t)rstr.length());
|
||||
} else {
|
||||
// Set map case: mapping from/to
|
||||
|
|
@ -517,7 +526,7 @@ transform_entry::apply(const std::u32string &input, std::u32string &output) cons
|
|||
// we actually need the group(1) string here.
|
||||
// this is only the content in parenthesis ()
|
||||
icu::UnicodeString group1 = matcher->group(1, status);
|
||||
assert(U_SUCCESS(status)); // TODO-LDML: could be a malformed from pattern
|
||||
UASSERT_SUCCESS(status); // TODO-LDML: could be a malformed from pattern
|
||||
// now, how long is group1 in UTF-32, hmm?
|
||||
UErrorCode preflightStatus = U_ZERO_ERROR; // throwaway status
|
||||
auto group1Len = group1.toUTF32(nullptr, 0, preflightStatus);
|
||||
|
|
@ -525,7 +534,7 @@ transform_entry::apply(const std::u32string &input, std::u32string &output) cons
|
|||
assert(s != nullptr); // TODO-LDML: OOM
|
||||
// convert
|
||||
substr.toUTF32((UChar32 *)s, group1Len + 1, status);
|
||||
assert(U_SUCCESS(status));
|
||||
UASSERT_SUCCESS(status);
|
||||
std::u32string match32(s, group1Len); // taken from just group1
|
||||
// clean up buffer
|
||||
delete [] s;
|
||||
|
|
@ -539,18 +548,27 @@ transform_entry::apply(const std::u32string &input, std::u32string &output) cons
|
|||
|
||||
// 2. get the target string, convert to utf-16
|
||||
// we use the same matchIndex that was just found
|
||||
const std::u16string rstr = km::kbp::kmx::u32string_to_u16string(fMapToList.at(matchIndex));
|
||||
const std::u16string rstr = km::core::kmx::u32string_to_u16string(fMapToList.at(matchIndex));
|
||||
|
||||
// 3. update the UnicodeString for replacement
|
||||
rustr = icu::UnicodeString(rstr.data(), (int32_t)rstr.length());
|
||||
// and we return to the regular code flow.
|
||||
}
|
||||
const icu::Normalizer2 *nfd = icu::Normalizer2::getNFDInstance(status);
|
||||
icu::UnicodeString rustr2;
|
||||
nfd->normalize(rustr, rustr2, status);
|
||||
UASSERT_SUCCESS(status);
|
||||
// here we replace the match output.
|
||||
icu::UnicodeString entireOutput = matcher->replaceFirst(rustr, status);
|
||||
assert(U_SUCCESS(status)); // TODO-LDML: could fail here due to bad input (syntax err)
|
||||
icu::UnicodeString entireOutput = matcher->replaceFirst(rustr2, status);
|
||||
UASSERT_SUCCESS(status); // TODO-LDML: could fail here due to bad input (syntax err)
|
||||
|
||||
// entireOutput includes all of 'input', but modified. Need to substring it.
|
||||
icu::UnicodeString outu = entireOutput.tempSubString(matchStart);
|
||||
icu::UnicodeString outu_raw = entireOutput.tempSubString(matchStart);
|
||||
|
||||
// normalize the replaced string
|
||||
icu::UnicodeString outu;
|
||||
nfd->normalize(outu_raw, outu, status);
|
||||
UASSERT_SUCCESS(status);
|
||||
|
||||
// Special case if there's no output, save some allocs
|
||||
if (outu.length() == 0) {
|
||||
|
|
@ -565,7 +583,7 @@ transform_entry::apply(const std::u32string &input, std::u32string &output) cons
|
|||
assert(s != nullptr);
|
||||
// convert
|
||||
outu.toUTF32((UChar32 *)s, out32len + 1, status);
|
||||
assert(U_SUCCESS(status));
|
||||
UASSERT_SUCCESS(status);
|
||||
output.assign(s, out32len);
|
||||
// now, build a u32string
|
||||
std::u32string out32(s, out32len);
|
||||
|
|
@ -752,37 +770,36 @@ transforms::apply(std::u32string &str) {
|
|||
transforms *
|
||||
transforms::load(
|
||||
const kmx::kmx_plus &kplus,
|
||||
const kbp::kmx::COMP_KMXPLUS_TRAN *tran,
|
||||
const kbp::kmx::COMP_KMXPLUS_TRAN_Helper &tranHelper) {
|
||||
const core::kmx::COMP_KMXPLUS_TRAN *tran,
|
||||
const core::kmx::COMP_KMXPLUS_TRAN_Helper &tranHelper) {
|
||||
bool valid = true;
|
||||
if (tran == nullptr) {
|
||||
DebugLog("for tran: tran is null");
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
if (!tranHelper.valid()) {
|
||||
valid = false;
|
||||
} else if (!tranHelper.valid()) {
|
||||
DebugLog("for tran: tranHelper is invalid");
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
if (nullptr == kplus.elem) {
|
||||
valid = false;
|
||||
} else if (nullptr == kplus.elem) {
|
||||
DebugLog("for tran: kplus.elem == nullptr");
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
if (nullptr == kplus.strs) {
|
||||
valid = false;
|
||||
} else if (nullptr == kplus.strs) {
|
||||
DebugLog("for tran: kplus.strs == nullptr"); // need a string table to get strings
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
if (nullptr == kplus.vars) {
|
||||
valid = false;
|
||||
} else if (nullptr == kplus.vars) {
|
||||
DebugLog("for tran: kplus.vars == nullptr"); // need a vars table to get maps
|
||||
assert(false);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
assert(valid);
|
||||
if (!valid) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// with that out of the way, let's set it up
|
||||
|
||||
transforms *transforms = new ldml::transforms();
|
||||
std::unique_ptr<transforms> transforms;
|
||||
|
||||
transforms.reset(new ldml::transforms());
|
||||
|
||||
for (KMX_DWORD groupNumber = 0; groupNumber < tran->groupCount; groupNumber++) {
|
||||
const kmx::COMP_KMXPLUS_TRAN_GROUP *group = tranHelper.getGroup(groupNumber);
|
||||
|
|
@ -798,7 +815,15 @@ transforms::load(
|
|||
const std::u32string toStr = kmx::u16string_to_u32string(kplus.strs->get(element->to));
|
||||
KMX_DWORD mapFrom = element->mapFrom; // copy, because of alignment
|
||||
KMX_DWORD mapTo = element->mapTo; // copy, because of alignment
|
||||
newGroup.emplace_back(fromStr, toStr, mapFrom, mapTo, kplus); // creating a transform_entry
|
||||
assert(!fromStr.empty());
|
||||
if (fromStr.empty()) {
|
||||
valid = false;
|
||||
}
|
||||
newGroup.emplace_back(fromStr, toStr, mapFrom, mapTo, kplus, valid); // creating a transform_entry
|
||||
assert(valid);
|
||||
if(!valid) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
transforms->addGroup(newGroup);
|
||||
} else if (group->type == LDML_TRAN_GROUP_TYPE_REORDER) {
|
||||
|
|
@ -828,9 +853,63 @@ transforms::load(
|
|||
return nullptr;
|
||||
}
|
||||
}
|
||||
return transforms;
|
||||
assert(valid);
|
||||
if (!valid) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return transforms.release();
|
||||
}
|
||||
}
|
||||
|
||||
// string manipulation
|
||||
|
||||
bool normalize_nfd(std::u32string &str) {
|
||||
std::u16string rstr = km::core::kmx::u32string_to_u16string(str);
|
||||
if(!normalize_nfd(rstr)) {
|
||||
return false;
|
||||
} else {
|
||||
str = km::core::kmx::u16string_to_u32string(rstr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** internal function to normalize with a specified mode */
|
||||
static bool normalize(const icu::Normalizer2 *n, std::u16string &str, UErrorCode &status) {
|
||||
UASSERT_SUCCESS(status);
|
||||
assert(n != nullptr);
|
||||
icu::UnicodeString dest;
|
||||
icu::UnicodeString src = icu::UnicodeString(str.data(), (int32_t)str.length());
|
||||
n->normalize(src, dest, status);
|
||||
if (UASSERT_SUCCESS(status)) {
|
||||
str.assign(dest.getBuffer(), dest.length());
|
||||
}
|
||||
return U_SUCCESS(status);
|
||||
}
|
||||
|
||||
bool normalize_nfd(std::u16string &str) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
const icu::Normalizer2 *nfd = icu::Normalizer2::getNFDInstance(status);
|
||||
UASSERT_SUCCESS(status);
|
||||
return normalize(nfd, str, status);
|
||||
}
|
||||
|
||||
bool normalize_nfc(std::u32string &str) {
|
||||
std::u16string rstr = km::core::kmx::u32string_to_u16string(str);
|
||||
if(!normalize_nfc(rstr)) {
|
||||
return false;
|
||||
} else {
|
||||
str = km::core::kmx::u16string_to_u32string(rstr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool normalize_nfc(std::u16string &str) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
const icu::Normalizer2 *nfc = icu::Normalizer2::getNFCInstance(status);
|
||||
UASSERT_SUCCESS(status);
|
||||
return normalize(nfc, str, status);
|
||||
}
|
||||
|
||||
} // namespace ldml
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include "debuglog.h"
|
||||
|
||||
#if !defined(HAVE_ICU4C)
|
||||
#error icu4c is required for this code
|
||||
|
|
@ -25,12 +26,25 @@
|
|||
#include "unicode/unistr.h"
|
||||
#include "unicode/regex.h"
|
||||
#include "unicode/utext.h"
|
||||
#include "unicode/normalizer2.h"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace ldml {
|
||||
|
||||
using km::kbp::kmx::SimpleUSet;
|
||||
/** @returns true on success */
|
||||
inline bool uassert_success(const char *file, int line, const char *function, UErrorCode status) {
|
||||
if (U_FAILURE(status)) {
|
||||
DebugLog2(file, line, function, "U_FAILURE(%s)", u_errorName(status));
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#define UASSERT_SUCCESS(status) assert(U_SUCCESS(status)), uassert_success(__FILE__, __LINE__, __FUNCTION__, status)
|
||||
|
||||
using km::core::kmx::SimpleUSet;
|
||||
|
||||
/**
|
||||
* Type of a group
|
||||
|
|
@ -94,7 +108,8 @@ public:
|
|||
const std::u32string &to,
|
||||
KMX_DWORD mapFrom,
|
||||
KMX_DWORD mapTo,
|
||||
const kmx::kmx_plus &kplus);
|
||||
const kmx::kmx_plus &kplus,
|
||||
bool &valid);
|
||||
|
||||
/**
|
||||
* If matching, apply the match to the output string
|
||||
|
|
@ -113,8 +128,8 @@ private:
|
|||
const KMX_DWORD fMapToStrId;
|
||||
std::deque<std::u32string> fMapFromList;
|
||||
std::deque<std::u32string> fMapToList;
|
||||
/** Internal function to setup pattern string */
|
||||
void init();
|
||||
/** Internal function to setup pattern string @returns true on success */
|
||||
bool init();
|
||||
/** @returns the index of the item in the fMapFromList list, or -1 */
|
||||
int32_t findIndexFrom(const std::u32string &match) const;
|
||||
public:
|
||||
|
|
@ -266,10 +281,21 @@ public:
|
|||
/** load from a kmx_plus data section, either tran or bksp */
|
||||
static transforms *
|
||||
load(const kmx::kmx_plus &kplus,
|
||||
const kbp::kmx::COMP_KMXPLUS_TRAN *tran,
|
||||
const kbp::kmx::COMP_KMXPLUS_TRAN_Helper &tranHelper);
|
||||
const core::kmx::COMP_KMXPLUS_TRAN *tran,
|
||||
const core::kmx::COMP_KMXPLUS_TRAN_Helper &tranHelper);
|
||||
};
|
||||
|
||||
// string routines
|
||||
|
||||
/** Normalize a u32string inplace to NFD. @return false on failure */
|
||||
bool normalize_nfd(std::u32string &str);
|
||||
/** Normalize a u16string inplace to NFD. @return false on failure */
|
||||
bool normalize_nfd(std::u16string &str);
|
||||
/** Normalize a u32string inplace to NFC. @return false on failure */
|
||||
bool normalize_nfc(std::u32string &str);
|
||||
/** Normalize a u16string inplace to NFC. @return false on failure */
|
||||
bool normalize_nfc(std::u16string &str);
|
||||
|
||||
} // namespace ldml
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#include "kmx_file.h"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace ldml {
|
||||
|
||||
vkeys::vkeys() : vkey_to_string() {
|
||||
|
|
@ -78,5 +78,5 @@ vkeys::lookup(const vkey_id& id) const {
|
|||
}
|
||||
|
||||
} // namespace ldml
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
#include <keyman/keyman_core_api.h>
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
namespace core {
|
||||
namespace ldml {
|
||||
|
||||
/**
|
||||
|
|
@ -55,5 +55,5 @@ private:
|
|||
};
|
||||
|
||||
} // namespace ldml
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Copyright: © 2018-2022 SIL International.
|
||||
# Description: Cross platform build script to compile libkmnkbp.
|
||||
# Description: Cross platform build script to compile libkeymancore.
|
||||
# Create Date: 2 Oct 2018
|
||||
# Authors: Tim Eves (TSE)
|
||||
#
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
version_res = []
|
||||
|
||||
# TODO: shouldn't this be defined only for dylib and not static lib?
|
||||
defns += ['-DKMN_KBP_EXPORTING']
|
||||
defns += ['-DKM_CORE_LIBRARY_EXPORTING']
|
||||
|
||||
if cpp_compiler.get_id() == 'msvc'
|
||||
defns += ['-DUNICODE']
|
||||
|
|
@ -18,7 +18,7 @@ endif
|
|||
|
||||
if cpp_compiler.get_id() == 'emscripten'
|
||||
# TODO: why do we need this defn here?
|
||||
defns += ['-DKMN_KBP']
|
||||
defns += ['-DKM_CORE_LIBRARY']
|
||||
endif
|
||||
|
||||
# ICU4C is used for repertoire tests and core implementation
|
||||
|
|
@ -42,10 +42,12 @@ endif
|
|||
|
||||
|
||||
kmx_files = files(
|
||||
'action.cpp',
|
||||
'option.cpp',
|
||||
'keyboard.cpp',
|
||||
'state.cpp',
|
||||
'debuglog.cpp',
|
||||
'km_core_action_api.cpp',
|
||||
'km_core_context_api.cpp',
|
||||
'km_core_keyboard_api.cpp',
|
||||
'km_core_options_api.cpp',
|
||||
|
|
@ -74,6 +76,7 @@ kmx_files = files(
|
|||
)
|
||||
|
||||
api_files = files(
|
||||
'km_core_action_api.cpp',
|
||||
'km_core_context_api.cpp',
|
||||
'km_core_keyboard_api.cpp',
|
||||
'km_core_options_api.cpp',
|
||||
|
|
@ -83,6 +86,7 @@ api_files = files(
|
|||
)
|
||||
|
||||
core_files = files(
|
||||
'action.cpp',
|
||||
'option.cpp',
|
||||
'keyboard.cpp',
|
||||
'state.cpp',
|
||||
|
|
@ -94,7 +98,7 @@ mock_files = files(
|
|||
'mock/mock_processor.cpp',
|
||||
)
|
||||
|
||||
lib = library('kmnkbp0',
|
||||
lib = library('keymancore',
|
||||
api_files,
|
||||
core_files,
|
||||
kmx_files,
|
||||
|
|
@ -111,11 +115,11 @@ lib = library('kmnkbp0',
|
|||
|
||||
headerdirs = [ '.', 'keyman' ] # subdirectories of ${prefix}/include to add to header path
|
||||
|
||||
kmnkbp = declare_dependency(link_with: lib, include_directories: inc, dependencies: [icu_uc, icu_i18n])
|
||||
keymancore = declare_dependency(link_with: lib, include_directories: inc, dependencies: [icu_uc, icu_i18n])
|
||||
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(
|
||||
name: 'keyman_kmn_processor',
|
||||
name: 'keyman_core',
|
||||
version: meson.project_version(),
|
||||
description: 'Keyman processor for KMN keyboards.',
|
||||
subdirs: headerdirs,
|
||||
|
|
|
|||
|
|
@ -70,9 +70,9 @@ namespace
|
|||
}
|
||||
|
||||
namespace km {
|
||||
namespace kbp
|
||||
namespace core
|
||||
{
|
||||
mock_processor::mock_processor(kbp::path const & path)
|
||||
mock_processor::mock_processor(core::path const & path)
|
||||
: abstract_processor(
|
||||
keyboard_attributes(path.stem(), u"3.145", path.parent(), {
|
||||
option{KM_CORE_OPT_KEYBOARD, u"__test_point", u"not tiggered"},
|
||||
|
|
@ -228,5 +228,5 @@ namespace km {
|
|||
km_core_status mock_processor::validate() const { return KM_CORE_STATUS_OK; }
|
||||
|
||||
km_core_status null_processor::validate() const { return KM_CORE_STATUS_INVALID_ARGUMENT; }
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -16,14 +16,14 @@
|
|||
#include "option.hpp"
|
||||
|
||||
namespace km {
|
||||
namespace kbp
|
||||
namespace core
|
||||
{
|
||||
class mock_processor : public abstract_processor
|
||||
{
|
||||
std::unordered_map<std::u16string, std::u16string> _options;
|
||||
|
||||
public:
|
||||
mock_processor(km::kbp::path const &);
|
||||
mock_processor(km::core::path const &);
|
||||
// ~mock_processor() override;
|
||||
|
||||
km_core_status
|
||||
|
|
@ -76,5 +76,5 @@ namespace kbp
|
|||
|
||||
km_core_status validate() const override;
|
||||
};
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#include "processor.hpp"
|
||||
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -44,7 +44,7 @@ option::option(km_core_option_scope s, char16_t const *k, char16_t const *v)
|
|||
}
|
||||
|
||||
// TODO: Relocate this and fix it
|
||||
json & km::kbp::operator << (json &j, abstract_processor const &)
|
||||
json & km::core::operator << (json &j, abstract_processor const &)
|
||||
{
|
||||
j << json::object;
|
||||
// auto n = 0;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
class json;
|
||||
|
||||
namespace km {
|
||||
namespace kbp
|
||||
namespace core
|
||||
{
|
||||
struct option : public km_core_option_item
|
||||
{
|
||||
|
|
@ -89,5 +89,5 @@ namespace kbp
|
|||
|
||||
|
||||
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
// Forward declarations
|
||||
|
||||
namespace km {
|
||||
namespace kbp
|
||||
namespace core
|
||||
{
|
||||
class path
|
||||
{
|
||||
|
|
@ -27,14 +27,14 @@ namespace kbp
|
|||
using char_type = std::remove_const_t<
|
||||
std::remove_pointer_t<km_core_path_name>>;
|
||||
using string_type = std::basic_string<char_type>;
|
||||
static constexpr char_type const parent_separator = _KM_KBP_PATH_SEPARATOR,
|
||||
suffix_separator = _KM_KBP_EXT_SEPARATOR;
|
||||
static constexpr char_type const parent_separator = _KM_CORE_PATH_SEPARATOR,
|
||||
suffix_separator = _KM_CORE_EXT_SEPARATOR;
|
||||
private:
|
||||
string_type _path;
|
||||
|
||||
void normalise() {
|
||||
#if '/' != _KM_KBP_PATH_SEPARATOR
|
||||
std::replace(_path.begin(), _path.end(), char_type('/'), _KM_KBP_PATH_SEPARATOR);
|
||||
#if '/' != _KM_CORE_PATH_SEPARATOR
|
||||
std::replace(_path.begin(), _path.end(), char_type('/'), _KM_CORE_PATH_SEPARATOR);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -157,5 +157,5 @@ namespace kbp
|
|||
return os;
|
||||
}
|
||||
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
#include "keyboard.hpp"
|
||||
|
||||
namespace km {
|
||||
namespace kbp
|
||||
namespace core
|
||||
{
|
||||
class abstract_processor
|
||||
{
|
||||
|
|
@ -75,7 +75,7 @@ namespace kbp
|
|||
* updateing the state as required.
|
||||
*
|
||||
* @param state An opaque pointer to a state object
|
||||
* @return km_core_status `KM_CORE_STATUS_OK`: On success. Else KB_KBP_ error code
|
||||
* @return km_core_status `KM_CORE_STATUS_OK`: On success. Else KB_CORE_ error code
|
||||
*/
|
||||
virtual km_core_status
|
||||
process_queued_actions(
|
||||
|
|
@ -127,7 +127,7 @@ namespace kbp
|
|||
|
||||
json & operator << (json &j, abstract_processor const &opts);
|
||||
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
||||
struct km_core_keyboard : public km::kbp::abstract_processor {};
|
||||
struct km_core_keyboard : public km::core::abstract_processor {};
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#include "processor.hpp"
|
||||
#include <keyman/keyman_core_api_consts.h>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace km::core;
|
||||
|
||||
void actions::push_persist(option const &opt) {
|
||||
assert(empty() || back().type != KM_CORE_IT_END);
|
||||
|
|
@ -35,7 +35,7 @@ void actions::push_capslock(bool turnOn) {
|
|||
}
|
||||
|
||||
|
||||
state::state(km::kbp::abstract_processor & ap, km_core_option_item const *env)
|
||||
state::state(km::core::abstract_processor & ap, km_core_option_item const *env)
|
||||
: _processor(ap)
|
||||
{
|
||||
for (; env && env->key != nullptr; env++) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#include "debug.hpp"
|
||||
|
||||
namespace km {
|
||||
namespace kbp
|
||||
namespace core
|
||||
{
|
||||
//Forward declarations
|
||||
class abstract_processor;
|
||||
|
|
@ -37,7 +37,7 @@ public:
|
|||
actions(Args&&... args);
|
||||
|
||||
void push_character(km_core_usv usv);
|
||||
void push_marker(uintptr_t marker);
|
||||
void push_marker(uint32_t marker);
|
||||
void push_alert();
|
||||
void push_backspace(km_core_backspace_type expected_type, uintptr_t expected_value = 0);
|
||||
void push_persist(option const &);
|
||||
|
|
@ -68,7 +68,7 @@ void actions::push_character(km_core_usv usv) {
|
|||
|
||||
|
||||
inline
|
||||
void actions::push_marker(uintptr_t marker) {
|
||||
void actions::push_marker(uint32_t marker) {
|
||||
assert(empty() || (!empty() && back().type != KM_CORE_IT_END));
|
||||
emplace_back(km_core_action_item {KM_CORE_IT_MARKER, {0,}, {marker}});
|
||||
}
|
||||
|
|
@ -122,30 +122,30 @@ void actions::clear() {
|
|||
class state
|
||||
{
|
||||
protected:
|
||||
kbp::context _ctxt;
|
||||
kbp::abstract_processor & _processor;
|
||||
kbp::actions _actions;
|
||||
kbp::debug_items _debug_items;
|
||||
core::context _ctxt;
|
||||
core::abstract_processor & _processor;
|
||||
core::actions _actions;
|
||||
core::debug_items _debug_items;
|
||||
km_core_keyboard_imx_platform _imx_callback;
|
||||
void *_imx_object;
|
||||
|
||||
public:
|
||||
state(kbp::abstract_processor & kb, km_core_option_item const *env);
|
||||
state(core::abstract_processor & kb, km_core_option_item const *env);
|
||||
|
||||
state(state const &) = default;
|
||||
state(state const &&) = delete;
|
||||
|
||||
kbp::context & context() noexcept { return _ctxt; }
|
||||
kbp::context const & context() const noexcept { return _ctxt; }
|
||||
core::context & context() noexcept { return _ctxt; }
|
||||
core::context const & context() const noexcept { return _ctxt; }
|
||||
|
||||
kbp::abstract_processor const & processor() const noexcept { return _processor; }
|
||||
kbp::abstract_processor & processor() noexcept { return _processor; }
|
||||
core::abstract_processor const & processor() const noexcept { return _processor; }
|
||||
core::abstract_processor & processor() noexcept { return _processor; }
|
||||
|
||||
kbp::actions & actions() noexcept { return _actions; }
|
||||
kbp::actions const & actions() const noexcept { return _actions; }
|
||||
core::actions & actions() noexcept { return _actions; }
|
||||
core::actions const & actions() const noexcept { return _actions; }
|
||||
|
||||
kbp::debug_items & debug_items() noexcept { return _debug_items; }
|
||||
kbp::debug_items const & debug_items() const noexcept { return _debug_items; }
|
||||
core::debug_items & debug_items() noexcept { return _debug_items; }
|
||||
core::debug_items const & debug_items() const noexcept { return _debug_items; }
|
||||
|
||||
void imx_register_callback(km_core_keyboard_imx_platform imx_callback, void *callback_object);
|
||||
|
||||
|
|
@ -154,12 +154,12 @@ public:
|
|||
void imx_callback(uint32_t imx_id);
|
||||
};
|
||||
|
||||
} // namespace kbp
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
||||
struct km_core_state : public km::kbp::state
|
||||
struct km_core_state : public km::core::state
|
||||
{
|
||||
template<typename... Args>
|
||||
km_core_state(Args&&... args) : km::kbp::state(std::forward<Args>(args)...)
|
||||
km_core_state(Args&&... args) : km::core::state(std::forward<Args>(args)...)
|
||||
{}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
VALUE "InternalName", "Keyman Core"
|
||||
VALUE "LegalCopyright", "© SIL International"
|
||||
VALUE "LegalTrademarks", ""
|
||||
VALUE "OriginalFilename", "KMNKBP0-0.DLL"
|
||||
VALUE "OriginalFilename", "KEYMANCORE1.DLL"
|
||||
VALUE "ProductName", "Keyman Core"
|
||||
VALUE "ProductVersion", KM_CORE_VERSION_STRING
|
||||
VALUE "Comments", ""
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ uconfig.set('U_ENABLE_DYLOAD', 0) # no DLL
|
|||
uconfig.set('U_CHECK_DYLOAD', 0) # no DLL
|
||||
uconfig.set('UCONFIG_NO_FILE_IO', 1)
|
||||
uconfig.set('UCONFIG_NO_LEGACY_CONVERSION', 1) # turn off file based codepage conversion
|
||||
uconfig.set('UCONFIG_NO_NORMALIZATION', 1) # TODO-LDML: may want this
|
||||
uconfig.set('UCONFIG_NO_NORMALIZATION', 0)
|
||||
uconfig.set('UCONFIG_NO_BREAK_ITERATION', 1) # TODO-LDML: may want this
|
||||
uconfig.set('UCONFIG_NO_IDNA', 1)
|
||||
uconfig.set('UCONFIG_NO_COLLATION', 1)
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ KmxTestSource::is_token(const std::string token, std::string &line) {
|
|||
|
||||
int
|
||||
KmxTestSource::load_source(
|
||||
const km::kbp::path &path,
|
||||
const km::core::path &path,
|
||||
std::string &keys,
|
||||
std::u16string &expected,
|
||||
std::u16string &context,
|
||||
|
|
@ -210,15 +210,15 @@ key_event
|
|||
KmxTestSource::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_CORE_MODIFIER_SHIFT : 0)};
|
||||
km::core::kmx::s_char_to_vkey[(int)ch - 32].vk,
|
||||
(uint16_t)(km::core::kmx::s_char_to_vkey[(int)ch - 32].shifted ? KM_CORE_MODIFIER_SHIFT : 0)};
|
||||
}
|
||||
|
||||
uint16_t
|
||||
KmxTestSource::get_modifier(std::string const m) {
|
||||
for (int i = 0; km::kbp::kmx::s_modifier_names[i].name; i++) {
|
||||
if (m == km::kbp::kmx::s_modifier_names[i].name) {
|
||||
return km::kbp::kmx::s_modifier_names[i].modifier;
|
||||
for (int i = 0; km::core::kmx::s_modifier_names[i].name; i++) {
|
||||
if (m == km::core::kmx::s_modifier_names[i].name) {
|
||||
return km::core::kmx::s_modifier_names[i].modifier;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -227,7 +227,7 @@ KmxTestSource::get_modifier(std::string const m) {
|
|||
km_core_virtual_key
|
||||
KmxTestSource::get_vk(std::string const &vk) {
|
||||
for (int i = 1; i < 256; i++) {
|
||||
if (vk == km::kbp::kmx::s_key_names[i]) {
|
||||
if (vk == km::core::kmx::s_key_names[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public:
|
|||
KmxTestSource();
|
||||
|
||||
int load_source(
|
||||
const km::kbp::path &path,
|
||||
const km::core::path &path,
|
||||
std::string &keys,
|
||||
std::u16string &expected,
|
||||
std::u16string &context,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ coretest_files = files(
|
|||
'kmx_test_source.cpp',
|
||||
)
|
||||
|
||||
kmx_test_source_lib = static_library('kmnkbp-tests',
|
||||
kmx_test_source_lib = static_library('keymancore-tests',
|
||||
coretest_files,
|
||||
cpp_args: defns + warns + flags,
|
||||
include_directories: [inc, libsrc],
|
||||
|
|
|
|||
445
core/tests/unit/kmnkbd/action_api.cpp
Normal file
445
core/tests/unit/kmnkbd/action_api.cpp
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
/*
|
||||
Copyright: © 2018 SIL International.
|
||||
Description: Tests for the context API family of functions.
|
||||
Create Date: 23 Oct 2023
|
||||
Authors: Marc Durdin
|
||||
History: 23 Oct 2023 - MCD - Initial implementation.
|
||||
*/
|
||||
#include <string>
|
||||
#include <keyman/keyman_core_api.h>
|
||||
|
||||
#include "path.hpp"
|
||||
#include "action.hpp"
|
||||
|
||||
#include <test_assert.h>
|
||||
#include "../emscripten_filesystem.h"
|
||||
|
||||
const km_core_action_item alert_action_item();
|
||||
const km_core_action_item bksp_action_item(uint8_t type, uintptr_t value);
|
||||
const km_core_action_item caps_action_item(uint8_t capsLock);
|
||||
const km_core_action_item char_action_item(km_core_usv chr);
|
||||
const km_core_action_item emit_keystroke_action_item();
|
||||
const km_core_action_item persist_opt_action_item(km_core_option_item const *option);
|
||||
const km_core_action_item end_action_item();
|
||||
const km_core_action_item invalidate_context_action_item();
|
||||
const km_core_action_item marker_action_item(uint32_t marker);
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void test_two_backspaces() {
|
||||
const km_core_action_item action_items[] = {
|
||||
char_action_item('D'),
|
||||
bksp_action_item(KM_CORE_BT_CHAR, 'D'),
|
||||
bksp_action_item(KM_CORE_BT_CHAR, 'E'),
|
||||
end_action_item()
|
||||
};
|
||||
|
||||
km_core_actions *actions = km::core::action_item_list_to_actions_object(action_items);
|
||||
|
||||
assert(actions->code_points_to_delete == 1);
|
||||
assert(std::u32string(actions->output) == U"");
|
||||
assert(actions->persist_options != nullptr);
|
||||
assert(actions->persist_options[0].key == nullptr);
|
||||
assert(actions->persist_options[0].value == nullptr);
|
||||
assert(actions->persist_options[0].scope == KM_CORE_OPT_UNKNOWN);
|
||||
|
||||
assert(actions->do_alert == false);
|
||||
assert(actions->emit_keystroke == false);
|
||||
assert(actions->new_caps_lock_state == -1);
|
||||
|
||||
try_status(km_core_actions_dispose(actions));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void test_marker_text_interleaved() {
|
||||
const km_core_action_item action_items[] = {
|
||||
char_action_item('A'),
|
||||
marker_action_item(1),
|
||||
char_action_item('B'),
|
||||
marker_action_item(2),
|
||||
char_action_item('C'),
|
||||
bksp_action_item(KM_CORE_BT_CHAR, 'C'),
|
||||
bksp_action_item(KM_CORE_BT_MARKER, 2),
|
||||
char_action_item('D'),
|
||||
end_action_item()
|
||||
};
|
||||
|
||||
km_core_actions *actions = km::core::action_item_list_to_actions_object(action_items);
|
||||
|
||||
assert(actions->code_points_to_delete == 0);
|
||||
assert(std::u32string(actions->output) == U"ABD");
|
||||
assert(actions->persist_options != nullptr);
|
||||
assert(actions->persist_options[0].key == nullptr);
|
||||
assert(actions->persist_options[0].value == nullptr);
|
||||
assert(actions->persist_options[0].scope == KM_CORE_OPT_UNKNOWN);
|
||||
assert(actions->do_alert == false);
|
||||
assert(actions->emit_keystroke == false);
|
||||
assert(actions->new_caps_lock_state == -1);
|
||||
|
||||
try_status(km_core_actions_dispose(actions));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void test_alert() {
|
||||
const km_core_action_item action_items[] = {
|
||||
alert_action_item(),
|
||||
end_action_item()
|
||||
};
|
||||
|
||||
km_core_actions *actions = km::core::action_item_list_to_actions_object(action_items);
|
||||
|
||||
assert(actions->code_points_to_delete == 0);
|
||||
assert(std::u32string(actions->output) == U"");
|
||||
assert(actions->persist_options != nullptr);
|
||||
assert(actions->persist_options[0].key == nullptr);
|
||||
assert(actions->persist_options[0].value == nullptr);
|
||||
assert(actions->persist_options[0].scope == KM_CORE_OPT_UNKNOWN);
|
||||
assert(actions->do_alert == KM_CORE_TRUE);
|
||||
assert(actions->emit_keystroke == KM_CORE_FALSE);
|
||||
assert(actions->new_caps_lock_state == KM_CORE_CAPS_UNCHANGED);
|
||||
|
||||
try_status(km_core_actions_dispose(actions));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void test_emit_keystroke() {
|
||||
const km_core_action_item action_items[] = {
|
||||
emit_keystroke_action_item(),
|
||||
end_action_item()
|
||||
};
|
||||
|
||||
km_core_actions *actions = km::core::action_item_list_to_actions_object(action_items);
|
||||
|
||||
assert(actions->code_points_to_delete == 0);
|
||||
assert(std::u32string(actions->output) == U"");
|
||||
assert(actions->persist_options != nullptr);
|
||||
assert(actions->persist_options[0].key == nullptr);
|
||||
assert(actions->persist_options[0].value == nullptr);
|
||||
assert(actions->persist_options[0].scope == KM_CORE_OPT_UNKNOWN);
|
||||
assert(actions->do_alert == KM_CORE_FALSE);
|
||||
assert(actions->emit_keystroke == KM_CORE_TRUE);
|
||||
assert(actions->new_caps_lock_state == KM_CORE_CAPS_UNCHANGED);
|
||||
|
||||
try_status(km_core_actions_dispose(actions));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void test_invalidate_context() {
|
||||
// note, this generates a no-op
|
||||
const km_core_action_item action_items[] = {
|
||||
invalidate_context_action_item(),
|
||||
end_action_item()
|
||||
};
|
||||
|
||||
km_core_actions *actions = km::core::action_item_list_to_actions_object(action_items);
|
||||
|
||||
assert(actions->code_points_to_delete == 0);
|
||||
assert(std::u32string(actions->output) == U"");
|
||||
assert(actions->persist_options != nullptr);
|
||||
assert(actions->persist_options[0].key == nullptr);
|
||||
assert(actions->persist_options[0].value == nullptr);
|
||||
assert(actions->persist_options[0].scope == KM_CORE_OPT_UNKNOWN);
|
||||
assert(actions->do_alert == KM_CORE_FALSE);
|
||||
assert(actions->emit_keystroke == KM_CORE_FALSE);
|
||||
assert(actions->new_caps_lock_state == KM_CORE_CAPS_UNCHANGED);
|
||||
|
||||
try_status(km_core_actions_dispose(actions));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void test_persist_opt() {
|
||||
const km_core_option_item option = {
|
||||
u"key",
|
||||
u"value",
|
||||
KM_CORE_OPT_KEYBOARD
|
||||
};
|
||||
|
||||
const km_core_action_item action_items[] = {
|
||||
persist_opt_action_item(&option),
|
||||
end_action_item()
|
||||
};
|
||||
|
||||
km_core_actions *actions = km::core::action_item_list_to_actions_object(action_items);
|
||||
|
||||
assert(actions->code_points_to_delete == 0);
|
||||
assert(std::u32string(actions->output) == U"");
|
||||
assert(actions->persist_options != nullptr);
|
||||
assert(std::u16string(actions->persist_options[0].key) == u"key");
|
||||
assert(std::u16string(actions->persist_options[0].value) == u"value");
|
||||
assert(actions->persist_options[0].scope == KM_CORE_OPT_KEYBOARD);
|
||||
|
||||
// verify that data is copied
|
||||
assert(actions->persist_options[0].key != option.key);
|
||||
assert(actions->persist_options[0].value != option.value);
|
||||
|
||||
// verify that we have a KM_CORE_OPTIONS_END term
|
||||
assert(actions->persist_options[1].key == nullptr);
|
||||
assert(actions->persist_options[1].value == nullptr);
|
||||
assert(actions->persist_options[1].scope == KM_CORE_OPT_UNKNOWN);
|
||||
|
||||
assert(actions->do_alert == KM_CORE_FALSE);
|
||||
assert(actions->emit_keystroke == KM_CORE_FALSE);
|
||||
assert(actions->new_caps_lock_state == KM_CORE_CAPS_UNCHANGED);
|
||||
|
||||
try_status(km_core_actions_dispose(actions));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Context tests
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
km_core_option_item test_env_opts[] =
|
||||
{
|
||||
KM_CORE_OPTIONS_END
|
||||
};
|
||||
|
||||
km_core_keyboard * test_kb = nullptr;
|
||||
km_core_state * test_state = nullptr;
|
||||
km_core_context_item * citems = nullptr;
|
||||
std::string arg_path;
|
||||
|
||||
void teardown() {
|
||||
if(citems) {
|
||||
km_core_context_items_dispose(citems);
|
||||
citems = nullptr;
|
||||
}
|
||||
if(test_state) {
|
||||
km_core_state_dispose(test_state);
|
||||
test_state = nullptr;
|
||||
}
|
||||
if(test_kb) {
|
||||
km_core_keyboard_dispose(test_kb);
|
||||
test_kb = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void setup(const char *keyboard, const km_core_cp* context) {
|
||||
teardown();
|
||||
|
||||
km::core::path path = km::core::path::join(arg_path, keyboard);
|
||||
try_status(km_core_keyboard_load(path.native().c_str(), &test_kb));
|
||||
try_status(km_core_state_create(test_kb, test_env_opts, &test_state));
|
||||
try_status(km_core_context_items_from_utf16(context, &citems));
|
||||
try_status(km_core_context_set(km_core_state_context(test_state), citems));
|
||||
}
|
||||
|
||||
bool is_identical_context(km_core_cp const *cached_context) {
|
||||
size_t buf_size;
|
||||
try_status(km_core_context_get(km_core_state_context(test_state), &citems));
|
||||
try_status(km_core_context_items_to_utf16(citems, nullptr, &buf_size));
|
||||
km_core_cp* new_cached_context = new km_core_cp[buf_size];
|
||||
try_status(km_core_context_items_to_utf16(citems, new_cached_context, &buf_size));
|
||||
bool result = std::u16string(cached_context) == new_cached_context;
|
||||
delete[] new_cached_context;
|
||||
return result;
|
||||
}
|
||||
|
||||
void test_context_set_if_needed_identical_context() {
|
||||
km_core_cp const *application_context = u"This is a test";
|
||||
km_core_cp const *cached_context = u"This is a test";
|
||||
setup("k_000___null_keyboard.kmx", cached_context);
|
||||
assert(km_core_state_context_set_if_needed(test_state, application_context) == KM_CORE_CONTEXT_STATUS_UNCHANGED);
|
||||
assert(is_identical_context(cached_context));
|
||||
teardown();
|
||||
}
|
||||
|
||||
void test_context_set_if_needed_different_context() {
|
||||
km_core_cp const *application_context = u"This is a test";
|
||||
km_core_cp const *cached_context = u"This isn't a test";
|
||||
setup("k_000___null_keyboard.kmx", cached_context);
|
||||
assert(km_core_state_context_set_if_needed(test_state, application_context) == KM_CORE_CONTEXT_STATUS_UPDATED);
|
||||
assert(!is_identical_context(cached_context));
|
||||
assert(is_identical_context(application_context));
|
||||
teardown();
|
||||
}
|
||||
|
||||
void test_context_set_if_needed_app_context_is_longer() {
|
||||
km_core_cp const *application_context = u"Longer This is a test";
|
||||
km_core_cp const *cached_context = u"This is a test";
|
||||
setup("k_000___null_keyboard.kmx", cached_context);
|
||||
assert(km_core_state_context_set_if_needed(test_state, application_context) == KM_CORE_CONTEXT_STATUS_UNCHANGED);
|
||||
// Should be true -- longer, but what exists is identical to cached
|
||||
assert(is_identical_context(cached_context));
|
||||
teardown();
|
||||
}
|
||||
|
||||
void test_context_set_if_needed_app_context_is_shorter() {
|
||||
km_core_cp const *application_context = u"is a test";
|
||||
km_core_cp const *cached_context = u"This is a test";
|
||||
setup("k_000___null_keyboard.kmx", cached_context);
|
||||
assert(km_core_state_context_set_if_needed(test_state, application_context) == KM_CORE_CONTEXT_STATUS_UPDATED);
|
||||
// Should be false -- app ctxt is shorter, so doesn't matter that what we have
|
||||
// matches
|
||||
assert(!is_identical_context(cached_context));
|
||||
assert(is_identical_context(application_context));
|
||||
teardown();
|
||||
}
|
||||
|
||||
void test_context_set_if_needed_cached_context_has_markers() {
|
||||
km_core_cp const *application_context = u"123";
|
||||
km_core_cp const *cached_context = u"123";
|
||||
setup("k_000___null_keyboard.kmx", cached_context);
|
||||
|
||||
km_core_context_item const citems[] = {
|
||||
{ KM_CORE_CT_MARKER, {0}, { 5 } },
|
||||
{ KM_CORE_CT_CHAR, {0}, { '1' } },
|
||||
{ KM_CORE_CT_MARKER, {0}, { 1 } },
|
||||
{ KM_CORE_CT_CHAR, {0}, { '2' } },
|
||||
{ KM_CORE_CT_MARKER, {0}, { 2 } },
|
||||
{ KM_CORE_CT_CHAR, {0}, { '3' } },
|
||||
{ KM_CORE_CT_MARKER, {0}, { 3 } },
|
||||
{ KM_CORE_CT_MARKER, {0}, { 4 } },
|
||||
KM_CORE_CONTEXT_ITEM_END
|
||||
};
|
||||
|
||||
try_status(km_core_context_set(km_core_state_context(test_state), citems));
|
||||
assert(km_core_state_context_set_if_needed(test_state, application_context) == KM_CORE_CONTEXT_STATUS_UNCHANGED);
|
||||
|
||||
km_core_context_item* citems_new;
|
||||
|
||||
try_status(km_core_context_get(km_core_state_context(test_state), &citems_new));
|
||||
|
||||
for(int i = 0; citems[i].type || citems_new[i].type; i++) {
|
||||
assert(citems_new[i].type == citems[i].type);
|
||||
if(citems[i].type == KM_CORE_CT_CHAR) {
|
||||
assert(citems_new[i].character == citems[i].character);
|
||||
} else {
|
||||
assert(citems_new[i].marker == citems[i].marker);
|
||||
}
|
||||
}
|
||||
|
||||
teardown();
|
||||
}
|
||||
|
||||
void test_context_set_if_needed() {
|
||||
test_context_set_if_needed_identical_context();
|
||||
test_context_set_if_needed_different_context();
|
||||
test_context_set_if_needed_app_context_is_longer();
|
||||
test_context_set_if_needed_app_context_is_shorter();
|
||||
test_context_set_if_needed_cached_context_has_markers();
|
||||
}
|
||||
|
||||
void test_context_clear() {
|
||||
km_core_cp const *cached_context = u"This is a test";
|
||||
setup("k_000___null_keyboard.kmx", cached_context);
|
||||
try_status(km_core_state_context_clear(test_state));
|
||||
assert(!is_identical_context(cached_context));
|
||||
assert(is_identical_context(u""));
|
||||
teardown();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Launcher
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
constexpr const auto help_str = "\
|
||||
action_api [--color] <SOURCE_PATH>\n\
|
||||
\n\
|
||||
--color Force color output\n\
|
||||
SOURCE_PATH Path where debug_api.cpp is found; kmx files are\n\
|
||||
located relative to this path.\n";
|
||||
|
||||
int error_args() {
|
||||
std::cerr << "debug_api: Invalid arguments." << std::endl;
|
||||
std::cout << help_str;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv []) {
|
||||
|
||||
if(argc < 2) {
|
||||
return error_args();
|
||||
}
|
||||
|
||||
auto arg_color = std::string(argv[1]) == "--color";
|
||||
if(arg_color && argc < 3) {
|
||||
return error_args();
|
||||
}
|
||||
console_color::enabled = console_color::isaterminal() || arg_color;
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
arg_path = get_wasm_file_path(argv[arg_color ? 2 : 1]);
|
||||
#else
|
||||
arg_path = argv[arg_color ? 2 : 1];
|
||||
#endif
|
||||
|
||||
// actions
|
||||
test_two_backspaces();
|
||||
test_marker_text_interleaved();
|
||||
test_alert();
|
||||
test_emit_keystroke();
|
||||
test_invalidate_context();
|
||||
|
||||
// context -- todo move to another file
|
||||
test_context_set_if_needed();
|
||||
test_context_clear();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Helper functions
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
const km_core_action_item alert_action_item() {
|
||||
km_core_action_item res = {0};
|
||||
res.type = KM_CORE_IT_ALERT;
|
||||
return res;
|
||||
}
|
||||
|
||||
const km_core_action_item bksp_action_item(uint8_t type, uintptr_t value) {
|
||||
km_core_action_item res = {0};
|
||||
res.type = KM_CORE_IT_BACK;
|
||||
res.backspace.expected_type = type;
|
||||
res.backspace.expected_value = value;
|
||||
return res;
|
||||
}
|
||||
|
||||
const km_core_action_item caps_action_item(uint8_t capsLock) {
|
||||
km_core_action_item res = {0};
|
||||
res.type = KM_CORE_IT_CAPSLOCK;
|
||||
res.capsLock = capsLock;
|
||||
return res;
|
||||
}
|
||||
|
||||
const km_core_action_item char_action_item(km_core_usv chr) {
|
||||
km_core_action_item res = {0};
|
||||
res.type = KM_CORE_IT_CHAR;
|
||||
res.character = chr;
|
||||
return res;
|
||||
}
|
||||
|
||||
const km_core_action_item emit_keystroke_action_item() {
|
||||
km_core_action_item res = {0};
|
||||
res.type = KM_CORE_IT_EMIT_KEYSTROKE;
|
||||
return res;
|
||||
}
|
||||
|
||||
const km_core_action_item persist_opt_action_item(km_core_option_item const *option) {
|
||||
km_core_action_item res = {0};
|
||||
res.type = KM_CORE_IT_PERSIST_OPT;
|
||||
res.option = option;
|
||||
return res;
|
||||
}
|
||||
|
||||
const km_core_action_item end_action_item() {
|
||||
km_core_action_item res = {0};
|
||||
res.type = KM_CORE_IT_END;
|
||||
return res;
|
||||
}
|
||||
|
||||
const km_core_action_item invalidate_context_action_item() {
|
||||
km_core_action_item res = {0};
|
||||
res.type = KM_CORE_IT_INVALIDATE_CONTEXT;
|
||||
return res;
|
||||
}
|
||||
|
||||
const km_core_action_item marker_action_item(uint32_t marker) {
|
||||
km_core_action_item res = {0};
|
||||
res.type = KM_CORE_IT_MARKER;
|
||||
res.character = marker;
|
||||
return res;
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
#include <test_assert.h>
|
||||
#include "../emscripten_filesystem.h"
|
||||
|
||||
using namespace km::kbp::kmx;
|
||||
using namespace km::core::kmx;
|
||||
|
||||
km_core_option_item test_env_opts[] =
|
||||
{
|
||||
|
|
@ -51,7 +51,7 @@ void teardown() {
|
|||
void setup(const char *keyboard) {
|
||||
teardown();
|
||||
|
||||
km::kbp::path path = km::kbp::path::join(arg_path, keyboard);
|
||||
km::core::path path = km::core::path::join(arg_path, keyboard);
|
||||
|
||||
try_status(km_core_keyboard_load(path.native().c_str(), &test_kb));
|
||||
try_status(km_core_state_create(test_kb, test_env_opts, &test_state));
|
||||
|
|
@ -402,8 +402,11 @@ void test_save_option() {
|
|||
km_core_state_debug_item{KM_CORE_DEBUG_END, 0, {}, {u"", nullptr, nullptr, {}, 1}},
|
||||
}));
|
||||
|
||||
km_core_action_item action = {KM_CORE_IT_PERSIST_OPT, {0,}, };
|
||||
action.option = &opt;
|
||||
|
||||
assert(action_items(test_state, {
|
||||
{KM_CORE_IT_PERSIST_OPT, {0,}, {uintptr_t(&opt)}},
|
||||
action,
|
||||
{KM_CORE_IT_END}
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include <test_assert.h>
|
||||
|
||||
using namespace km::kbp::kmx;
|
||||
using namespace km::core::kmx;
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
km::kbp::path const test_kb_path = "/a/dummy/keyboard.mock";
|
||||
km::core::path const test_kb_path = "/a/dummy/keyboard.mock";
|
||||
}
|
||||
|
||||
#define try_status(expr) \
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue