diff --git a/.clang-format b/.clang-format
index 81ca2c82a3..50df9fee12 100644
--- a/.clang-format
+++ b/.clang-format
@@ -134,4 +134,3 @@ TabWidth: 8
UseCRLF: false
UseTab: Never
...
-
diff --git a/.gitignore b/.gitignore
index 8534d172a9..772784fdb8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -183,4 +183,3 @@ lcov.info
/keyman*.buildinfo
/keyman*.changes
/keyman*.tar.?z
-
diff --git a/HISTORY.md b/HISTORY.md
index f45e1e85cd..f911703834 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,5 +1,24 @@
# Keyman Version History
+## 18.0.90 alpha 2024-08-15
+
+* refactor(web): move parts of `keyboard-processor` → `js-processor` (#12111)
+* fix(web): allow `lm-worker` to build on Linux (#12181)
+* refactor(web): move remaining parts of `keyboard-processor` → `keyboard` (#12131)
+* docs: update .kmx documentation around bitmaps, modifier state (#12183)
+* refactor(web): rename `package-cache` → `keyboard-storage` (#12135)
+
+## 18.0.89 alpha 2024-08-14
+
+* feat(web): test skipped prediction round handling (#12169)
+* fix(web): support live configuration of longpress delay (#12175)
+* feat(web): add osk.gestureParams for better gesture-config persistence (#12176)
+* refactor(core): move utfcodec to common (#12171)
+* refactor: move kmx_u16 to common and rename to km_u16 (#12177)
+* refactor(developer): use npm or local source for server addons instead of github references (#12090)
+* fix(developer): fix crash with Windows Clipboard by ignoring zero scan code in debugger (#12166)
+* chore(developer): update SIL logo (#12168)
+
## 18.0.88 alpha 2024-08-13
* docs: add .kmx specification (#12163)
diff --git a/VERSION.md b/VERSION.md
index 06ebe83646..8f233fffc0 100644
--- a/VERSION.md
+++ b/VERSION.md
@@ -1 +1 @@
-18.0.89
\ No newline at end of file
+18.0.91
\ No newline at end of file
diff --git a/common/predictive-text/.build-builder b/common/predictive-text/.build-builder
deleted file mode 100644
index 4e15741a62..0000000000
--- a/common/predictive-text/.build-builder
+++ /dev/null
@@ -1 +0,0 @@
-The presence of this file tells CI to use the new builder_ style parameters for build.sh and unit_tests/test.sh.
\ No newline at end of file
diff --git a/common/web/keyboard-processor/build.sh b/common/web/keyboard-processor/build.sh
deleted file mode 100755
index 3921063bb9..0000000000
--- a/common/web/keyboard-processor/build.sh
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/usr/bin/env bash
-#
-# Compile KeymanWeb's 'keyboard-processor' module, one of the components of Web's 'core' module.
-#
-## START STANDARD BUILD SCRIPT INCLUDE
-# adjust relative paths as necessary
-THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
-. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
-## END STANDARD BUILD SCRIPT INCLUDE
-
-. "${KEYMAN_ROOT}/resources/shellHelperFunctions.sh"
-
-BUNDLE_CMD="node ${KEYMAN_ROOT}/common/web/es-bundling/build/common-bundle.mjs"
-
-################################ Main script ################################
-
-builder_describe \
- "Compiles the web-oriented utility function module." \
- "@/web/src/tools/testing/recorder-core test" \
- "@/common/web/keyman-version" \
- "@/common/web/es-bundling" \
- "@/common/web/types" \
- "@/common/web/utils" \
- configure \
- clean \
- build \
- test \
- "--ci For use with action $(builder_term test) - emits CI-friendly test reports"
-
-builder_describe_outputs \
- configure /node_modules \
- build /common/web/keyboard-processor/build/lib/index.mjs
-
-builder_parse "$@"
-
-function do_configure() {
- verify_npm_setup
-
- # Configure Web browser-engine testing environments. As is, this should only
- # make changes when we update the dependency, even on our CI build agents.
- playwright install
-}
-
-function do_build() {
- tsc --build "${THIS_SCRIPT_PATH}/tsconfig.all.json"
-
- # Base product - the main keyboard processor
- builder_echo "Bundle base product - the main keyboard processor"
- ${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/index.js" \
- --out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/lib/index.mjs" \
- --format esm
-
- # The DOM-oriented keyboard loader
- builder_echo "Bundle the DOM-oriented keyboard loader"
- ${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/keyboards/loaders/dom-keyboard-loader.js" \
- --out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/lib/dom-keyboard-loader.mjs" \
- --format esm
-
- # The Node-oriented keyboard loader
- builder_echo "Bundle the Node-oriented keyboard loader"
- ${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/obj/keyboards/loaders/node-keyboard-loader.js" \
- --out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/lib/node-keyboard-loader.mjs" \
- --format esm \
- --platform node
-
- # Tests
- builder_echo "Bundle tests"
- ${BUNDLE_CMD} "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/cases/domKeyboardLoader.spec.js" \
- --out "${KEYMAN_ROOT}/common/web/keyboard-processor/build/tests/dom/domKeyboardLoader.spec.mjs" \
- --format esm
-
- # Declaration bundling.
- builder_echo "Declaration bundling"
- tsc --emitDeclarationOnly --outFile ./build/lib/index.d.ts
- tsc --emitDeclarationOnly --outFile ./build/lib/dom-keyboard-loader.d.ts -p src/keyboards/loaders/tsconfig.dom.json
- tsc --emitDeclarationOnly --outFile ./build/lib/node-keyboard-loader.d.ts -p src/keyboards/loaders/tsconfig.node.json
-}
-
-function do_test() {
- local MOCHA_FLAGS=
- local WTR_CONFIG=
- if builder_has_option --ci; then
- echo "Replacing user-friendly test reports with CI-friendly versions."
- MOCHA_FLAGS="$MOCHA_FLAGS --reporter mocha-teamcity-reporter"
- WTR_CONFIG=.CI
- fi
-
- c8 mocha --recursive $MOCHA_FLAGS ./tests/node/
- web-test-runner --config tests/dom/web-test-runner${WTR_CONFIG}.config.mjs
-}
-
-builder_run_action configure do_configure
-builder_run_action clean rm -rf ./build
-builder_run_action build do_build
-builder_run_action test do_test
diff --git a/common/web/keyboard-processor/package.json b/common/web/keyboard-processor/package.json
deleted file mode 100644
index 3f0a595897..0000000000
--- a/common/web/keyboard-processor/package.json
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "name": "@keymanapp/keyboard-processor",
- "description": "Core module for Keyman keyboard support in KeymanWeb.",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/keymanapp/keyman.git"
- },
- "keywords": [
- "input",
- "languages",
- "keyboards"
- ],
- "author": "SIL International",
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/keymanapp/keyman/issues"
- },
- "homepage": "https://github.com/keymanapp/keyman#readme",
- "devDependencies": {
- "@keymanapp/resources-gosh": "*",
- "c8": "^7.12.0",
- "mocha": "^10.0.0",
- "mocha-teamcity-reporter": "^4.0.0",
- "typescript": "^5.4.5"
- },
- "scripts": {
- "build": "gosh build.sh",
- "clean": "gosh build.sh clean",
- "test": "gosh build.sh test"
- },
- "dependencies": {
- "@keymanapp/common-types": "*",
- "@keymanapp/models-types": "*",
- "@keymanapp/keyman-version": "*",
- "@keymanapp/web-utils": "*"
- },
- "type": "module",
- "main": "./build/obj/index.js",
- "types": "./build/obj/index.d.ts",
- "exports": {
- ".": {
- "es6-bundling": "./src/index.ts",
- "default": "./build/obj/index.js"
- },
- "./node-keyboard-loader": {
- "es6-bundling": "./src/keyboards/loaders/node-keyboard-loader.ts",
- "types": "./build/obj/keyboards/loaders/node-keyboard-loader.d.ts",
- "import": "./build/obj/keyboards/loaders/node-keyboard-loader.js"
- },
- "./dom-keyboard-loader": {
- "es6-bundling": "./src/keyboards/loaders/dom-keyboard-loader.ts",
- "types": "./build/obj/keyboards/loaders/dom-keyboard-loader.d.ts",
- "import": "./build/obj/keyboards/loaders/dom-keyboard-loader.js"
- },
- "./lib": {
- "types": "./build/lib/index.d.ts",
- "import": "./build/lib/index.mjs"
- },
- "./lib/node-keyboard-loader": {
- "types": "./build/lib/node-keyboard-loader.d.ts",
- "import": "./build/lib/node-keyboard-loader.mjs"
- },
- "./lib/dom-keyboard-loader": {
- "types": "./build/lib/dom-keyboard-loader.d.ts",
- "import": "./build/lib/dom-keyboard-loader.mjs"
- },
- "./obj/*.js": "./build/obj/*.js"
- }
-}
diff --git a/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.dom.json b/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.dom.json
deleted file mode 100644
index 7ba2abe026..0000000000
--- a/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.dom.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "extends": "../../../../tsconfig.kmw-main-base.json",
- "compilerOptions": {
- "baseUrl": "../../../",
- "outDir": "../../../build/obj/keyboards/loaders/",
- "tsBuildInfoFile": "../../../build/obj/keyboards/loaders/tsconfig.dom.tsbuildinfo",
- "rootDir": "."
- },
- "references": [
- { "path": "../../../tsconfig.json" }
- ],
- "include": ["dom-keyboard-loader.ts", "domKeyboardLoader.ts"],
-}
diff --git a/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.node.json b/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.node.json
deleted file mode 100644
index 0be3f169ad..0000000000
--- a/common/web/keyboard-processor/src/keyboards/loaders/tsconfig.node.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "extends": "../../../../tsconfig.kmw-main-base.json",
- "compilerOptions": {
- "types": [ "node" ],
- "baseUrl": "../../../",
- "outDir": "../../../build/obj/keyboards/loaders/",
- "tsBuildInfoFile": "../../../build/obj/keyboards/loaders/tsconfig.node.tsbuildinfo",
- "rootDir": "."
- },
- "references": [
- { "path": "../../../tsconfig.json" }
- ],
- "include": ["node-keyboard-loader.ts", "nodeKeyboardLoader.ts"],
-}
diff --git a/common/web/keyboard-processor/tests/dom/readme.md b/common/web/keyboard-processor/tests/dom/readme.md
deleted file mode 100644
index 153b7f4a1e..0000000000
--- a/common/web/keyboard-processor/tests/dom/readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Automated tests in this subfolder and its children are designed to facilitate simple, browser-independent
-unit tests that are DOM-reliant.
-
-Tests for anything that may reasonably vary depending upon the browser used to run the code should go under
-the "integrated" folder instead.
\ No newline at end of file
diff --git a/common/web/keyboard-processor/tests/dom/web-test-runner.CI.config.mjs b/common/web/keyboard-processor/tests/dom/web-test-runner.CI.config.mjs
deleted file mode 100644
index d18a8a9cb0..0000000000
--- a/common/web/keyboard-processor/tests/dom/web-test-runner.CI.config.mjs
+++ /dev/null
@@ -1,13 +0,0 @@
-// @ts-check
-import BASE_CONFIG from './web-test-runner.config.mjs';
-import teamcityReporter from '@keymanapp/common-test-resources/test-runner-TC-reporter.mjs';
-import { sessionStabilityReporter } from '@keymanapp/common-test-resources/test-runner-stability-reporter.mjs';
-
-/** @type {import('@web/test-runner').TestRunnerConfig} */
-export default {
- ...BASE_CONFIG,
- reporters: [
- teamcityReporter(), /* custom-written, for CI-friendly reports */
- sessionStabilityReporter({ciMode: true})
- ]
-}
\ No newline at end of file
diff --git a/common/web/keyboard-processor/tests/dom/web-test-runner.config.mjs b/common/web/keyboard-processor/tests/dom/web-test-runner.config.mjs
deleted file mode 100644
index 76651182ac..0000000000
--- a/common/web/keyboard-processor/tests/dom/web-test-runner.config.mjs
+++ /dev/null
@@ -1,62 +0,0 @@
-// @ts-check
-import { devices, playwrightLauncher } from '@web/test-runner-playwright';
-import { esbuildPlugin } from '@web/dev-server-esbuild';
-import { defaultReporter, summaryReporter } from '@web/test-runner';
-import { LauncherWrapper, sessionStabilityReporter } from '@keymanapp/common-test-resources/test-runner-stability-reporter.mjs';
-import { importMapsPlugin } from '@web/dev-server-import-maps';
-import { dirname, resolve } from 'path';
-import { fileURLToPath } from 'url';
-
-const dir = dirname(fileURLToPath(import.meta.url));
-const KEYMAN_ROOT = resolve(dir, '../../../../..');
-
-/** @type {import('@web/test-runner').TestRunnerConfig} */
-export default {
- // debug: true,
- browsers: [
- new LauncherWrapper(playwrightLauncher({ product: 'chromium' })),
- new LauncherWrapper(playwrightLauncher({ product: 'firefox' })),
- new LauncherWrapper(playwrightLauncher({ product: 'webkit', concurrency: 1 })),
- ],
- concurrency: 10,
- nodeResolve: true,
- files: [
- 'build/tests/dom/**/*.spec.mjs'
- ],
- middleware: [
- // Rewrites short-hand paths for test resources, making them fully relative to the repo root.
- function rewriteResourcePath(context, next) {
- if(context.url.startsWith('/resources/')) {
- context.url = '/common/test' + context.url;
- }
-
- return next();
- }
- ],
- plugins: [
- esbuildPlugin({ts: true, target: 'auto'}),
- importMapsPlugin({
- inject: {
- importMap: {
- // Redirects `eventemitter3` imports to the bundled ESM library. The standard import is an
- // ESM wrapper around the CommonJS implementation, and WTR fails when it hits the CommonJS.
- imports: {
- 'eventemitter3': '/node_modules/eventemitter3/dist/eventemitter3.esm.js'
- }
- }
- }
- })
- ],
- reporters: [
- summaryReporter({}), /* local-dev mocha-style */
- sessionStabilityReporter({}),
- defaultReporter({})
- ],
- /*
- Un-comment the next two lines for easy interactive debugging; it'll launch the
- test page in your preferred browser.
- */
- // open: true,
- // manual: true,
- rootDir: KEYMAN_ROOT
-}
diff --git a/common/web/keyboard-processor/tests/tsconfig.json b/common/web/keyboard-processor/tests/tsconfig.json
deleted file mode 100644
index 0e978545b8..0000000000
--- a/common/web/keyboard-processor/tests/tsconfig.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "baseUrl": "../",
- "outDir": "../build/tests/",
- "tsBuildInfoFile": "../build/tests/tsconfig.tsbuildinfo",
- "rootDir": "./"
- },
- "include": [ "./dom/**/*.ts"],
- "exclude": []
-}
diff --git a/common/web/keyboard-processor/tsconfig.json b/common/web/keyboard-processor/tsconfig.json
deleted file mode 100644
index 728935b74f..0000000000
--- a/common/web/keyboard-processor/tsconfig.json
+++ /dev/null
@@ -1,18 +0,0 @@
-// Is used by the keyboard-loader submodules.
-{
- "extends": "../tsconfig.kmw-main-base.json",
- "compilerOptions": {
- "baseUrl": "./",
- "outDir": "build/obj/",
- "tsBuildInfoFile": "build/obj/tsconfig.tsbuildinfo",
- "rootDir": "./src/"
- },
- "references": [
- { "path": "../types" },
- { "path": "../../models/types" },
- { "path": "../keyman-version/" },
- { "path": "../utils/" }
- ],
- "include": [ "./src/**/*.ts"],
- "exclude": ["./src/keyboards/loaders/**/*.ts"]
-}
diff --git a/common/web/lm-worker/build.sh b/common/web/lm-worker/build.sh
index 22ffa9208c..0343d151c8 100755
--- a/common/web/lm-worker/build.sh
+++ b/common/web/lm-worker/build.sh
@@ -98,7 +98,7 @@ function do_test() {
if builder_has_option --ci; then
MOCHA_FLAGS="$MOCHA_FLAGS --reporter mocha-teamcity-reporter"
- WTR_CONFIG=.ci
+ WTR_CONFIG=.CI
fi
if builder_has_option --debug; then
diff --git a/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts b/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts
index 757316a70a..786d9213e2 100644
--- a/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts
+++ b/common/web/types/src/keyman-touch-layout/keyman-touch-layout-file.ts
@@ -58,7 +58,7 @@ export const PRIVATE_USE_IDS = [
] as const;
/* A map of key field names with values matching the `typeof` the corresponding property
- * exists in common/web/keyboard-processor, keyboards/activeLayout.ts.
+ * exists in /web/src/engine/keyboard/src/keyboards/activeLayout.ts.
*
* Make sure that when one is updated, the other also is. TS types are compile-time only,
* so the run-time-accessible mapping in activeLayout.ts cannot be auto-generated by TS. */
diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS
index 7902ce7a68..75530e6bde 100644
--- a/docs/CODEOWNERS
+++ b/docs/CODEOWNERS
@@ -3,30 +3,30 @@
#
# @darcywong00 @mcdurdin @ermshiperete @rc-swag @SabineSIL @sgschantz
-/android/ @darcywong00 @sgschantz
+/android/ @darcywong00 @sgschantz
-/common/ @mcdurdin @rc-swag
-/core/ @mcdurdin @rc-swag
-/common/lexical-model-types/ @jahorton @mcdurdin
-/common/models/ @jahorton @mcdurdin
-/common/predictive-text/ @jahorton @mcdurdin
-/common/schemas/ @mcdurdin @jahorton
-/common/test/ @mcdurdin @ermshiperete
-/common/web/ @jahorton @mcdurdin
+/common/ @mcdurdin @rc-swag
+/core/ @mcdurdin @rc-swag
+/common/lexical-model-types/ @jahorton @mcdurdin
+/common/models/ @jahorton @mcdurdin
+/common/schemas/ @mcdurdin @jahorton
+/common/test/ @mcdurdin @ermshiperete
+/common/web/ @jahorton @mcdurdin
-/developer/ @mcdurdin @darcywong00
-/docs/ @mcdurdin @jahorton
-/ios/ @sgschantz @jahorton
-/linux/ @ermshiperete @darcywong00
-/mac/ @sgschantz @SabineSIL
+/developer/ @mcdurdin @darcywong00
+/docs/ @mcdurdin @jahorton
+/ios/ @sgschantz @jahorton
+/linux/ @ermshiperete @darcywong00
+/mac/ @sgschantz @SabineSIL
-/oem/firstvoices/android/ @darcywong00 @sgschantz
-/oem/firstvoices/common/ @mcdurdin @rc-swag
-/oem/firstvoices/ios/ @sgschantz @jahorton
-/oem/firstvoices/windows/ @rc-swag @ermshiperete
-/resources/ @mcdurdin @jahorton
+/oem/firstvoices/android/ @darcywong00 @sgschantz
+/oem/firstvoices/common/ @mcdurdin @rc-swag
+/oem/firstvoices/ios/ @sgschantz @jahorton
+/oem/firstvoices/windows/ @rc-swag @ermshiperete
+/resources/ @mcdurdin @jahorton
# Web is currently shared between Eberhard and Joshua:
-/web/ @ermshiperete @jahorton
+/web/ @ermshiperete @jahorton
+/web/src/engine/predictive-text/ @jahorton @mcdurdin
-/windows/ @rc-swag @ermshiperete
+/windows/ @rc-swag @ermshiperete
diff --git a/docs/file-formats/kmx-file-format.md b/docs/file-formats/kmx-file-format.md
index 6de14df56f..67288bc123 100644
--- a/docs/file-formats/kmx-file-format.md
+++ b/docs/file-formats/kmx-file-format.md
@@ -44,12 +44,14 @@ The very first section is a header that describes the remainder of the file.
|44 | 32 | `StartGroup_Unicode` | index of starting Unicode `COMP_GROUP`, `0xFFFFFFFF` means unused |
|48 | 32 | `dwFlags` | global flags for the keyboard, see description below |
|52 | 32 | `dwHotKey` | default hotkey for keyboard, from [`&Hotkey`], see description below |
-|56 | 32 | `dpBitmapOffset` | offset of keyboard icon, `0x0` if not present |
+|56 | 32 | `dpBitmapOffset` | offset of keyboard icon |
|60 | 32 | `dwBitmapSize` | size in bytes of keyboard icon, `0x0` if not present |
This structure is present at the start of every .kmx file. The `KXTS` identifier
can be used as a 'magic' to determine whether a binary file is a .kmx file.
+Note that `dpBitmapOffset` should be ignored if `dwBitmapSize` is `0x0` -- it may be a non-zero value.
+
### `dwFileVersion`: Minimum version of Keyman required to read the file
The `dwFileVersion` property should be checked before attempting to read any
@@ -220,7 +222,7 @@ file and is never written to the binary format.
| ∆ | Bits | Name | Description |
|----|------|--------------|------------------------------------------------------------------------------------------------|
-| 0 | 16 | `Key` | Keyman virtual key for the rule, 0 if unused |
+| 0 | 16 | `Key` | [Keyman virtual key], virtual character key, or character, for the rule, 0 if unused |
| 2 | 16 | (unused) | padding, reserved |
| 4 | 32 | `Line` | Line number for the rule, 0 if not compiled with debug information |
| 8 | 32 | `ShiftFlags` | Modifier flags for the key, see description below |
@@ -232,6 +234,58 @@ compiler expands `any()` statements found in the key part of a rule into
multiple rules -- so the key part of the rule is always only ever matching a
single key combination.
+`ShiftFlags` contains a set of modifier and state key flags, and some additional
+flags that determine how to interpret `Key`.
+
+### Modifier and state key flags
+
+The modifier and state key flags are:
+
+| Bit mask | Name | Description |
+|--------------|------------------|-----------------------------------------------------------|
+| `0x00000001` | `LCTRLFLAG` | Left Control key |
+| `0x00000002` | `RCTRLFLAG` | Right Control key |
+| `0x00000004` | `LALTFLAG` | Left Alt key (Option on macOS) |
+| `0x00000008` | `RALTFLAG` | Right Alt key |
+| `0x00000010` | `K_SHIFTFLAG` | Either Shift key |
+| `0x00000020` | `K_CTRLFLAG` | Either Ctrl key |
+| `0x00000040` | `K_ALTFLAG` | Either Alt key |
+| `0x00000080` | `K_METAFLAG` | Either Meta key. Reserved, not for use in .kmx |
+| `0x00000100` | `CAPITALFLAG` | Caps lock on |
+| `0x00000200` | `NOTCAPITALFLAG` | Caps lock NOT on |
+| `0x00000400` | `NUMLOCKFLAG` | Num lock on |
+| `0x00000800` | `NOTNUMLOCKFLAG` | Num lock NOT on |
+| `0x00001000` | `SCROLLFLAG` | Scroll lock on |
+| `0x00002000` | `NOTSCROLLFLAG` | Scroll lock NOT on |
+
+* The chiral (left/right) modifier flags should not be used together with the
+ non-chiral flags.
+* If neither `CAPITALFLAG` nor `NOTCAPITALFLAG` are set, then the state of the
+ Caps Lock key is ignored, and the same principle applies for Num lock and
+ Scroll lock.
+* The Meta key may be the Windows key on Windows, or the
+ Command key on macOS. This flag is reserved for internal use, and
+ is not valid in a .kmx file.
+
+### Additional flags
+
+The meaning of `Key` is determined by the flags `ISVIRTUALKEY` and
+`VIRTUALCHARKEY` in `ShiftFlags` (mask `0x0000C000`):
+
+* `0`: `Key` is a character on a key cap (US English unless the keyboard is a
+ mnemonic layout). All other shift flags are ignord
+* `ISVIRTUALKEY`: `Key` is a US English virtual key
+* `VIRTUALCHARKEY`: `Key` is a character on a key cap, combined with the shift
+ flags (US English unless the keyboard is a mnemonic layout)
+
+| Bit mask | Name | Description |
+|--------------|------------------|-----------------------------------------------|
+| `0x00004000` | `ISVIRTUALKEY` | `Key` is a virtual key |
+| `0x00008000` | `VIRTUALCHARKEY` | Keyman 6.0+: `Key` is a Virtual character key |
+
+Note that the shift flags for key rules do not use the same values as hotkeys
+for historical reasons.
+
## Debug information for keyboards
When debug information is present, it will be found in four places in compiled
diff --git a/linux/mcompile/Readme.md b/linux/mcompile/Readme.md
new file mode 100644
index 0000000000..f45bdbe1fb
--- /dev/null
+++ b/linux/mcompile/Readme.md
@@ -0,0 +1,3 @@
+This is a proposal to rewrite mcompile for Linux. For this we need to query the base keyboard data from the Linux platform, then rewriting the keyboard .kmx using the same approach as is done in mcompile for Windows, but working from the data from the x11 keyboard on Linux.
+
+Ideally, we'd rewrite mcompile to be cross-platform (Windows, Linux, macOS), so that the keyboard interrogation would be separated from the .kmx rewriting, at least to some degree. Nevertheless it would probably be easiest to start from a standalone implementation.
diff --git a/linux/mcompile/keymap/.gitignore b/linux/mcompile/keymap/.gitignore
new file mode 100644
index 0000000000..fcea0ffb39
--- /dev/null
+++ b/linux/mcompile/keymap/.gitignore
@@ -0,0 +1,2 @@
+resources/
+build/
diff --git a/linux/mcompile/keymap/README.md b/linux/mcompile/keymap/README.md
new file mode 100644
index 0000000000..d12a0820e0
--- /dev/null
+++ b/linux/mcompile/keymap/README.md
@@ -0,0 +1,7 @@
+This is a proposal to rewrite mcompile for Linux. For this we need to query the base keyboard data from the Linux platform, then rewriting the keyboard .kmx using the same approach as is done in mcompile for Windows, but working from the data from the x11 keyboard on Linux.
+
+Ideally, we'd rewrite mcompile to be cross-platform (Windows, Linux, macOS), so that the keyboard interrogation would be separated from the .kmx rewriting, at least to some degree. Nevertheless it would probably be easiest to start from a standalone implementation.
+Sample program that reads US basic keyboard and compares to key value group
+
+
+# Keymap
diff --git a/linux/mcompile/keymap/build.sh b/linux/mcompile/keymap/build.sh
new file mode 100755
index 0000000000..7c66e4b0b4
--- /dev/null
+++ b/linux/mcompile/keymap/build.sh
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+
+## START STANDARD BUILD SCRIPT INCLUDE
+# adjust relative paths as necessary
+THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
+. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
+## END STANDARD BUILD SCRIPT INCLUDE
+
+#. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
+
+################################ Main script ################################
+
+builder_describe \
+ "Mnemonic layout recompiler for Linux" \
+ "@/common/include" \
+ "clean" \
+ "configure" \
+ "build" \
+ "test"
+
+builder_parse "$@"
+
+builder_describe_outputs \
+ configure build/build.ninja \
+ build build/mcompile
+
+TARGET_PATH="$THIS_SCRIPT_PATH/build"
+
+do_clean() {
+ rm -rf "$THIS_SCRIPT_PATH/resources"
+ rm -rf "$TARGET_PATH"
+}
+
+do_configure() {
+ # Import our standard compiler defines; this is copied from
+ # /resources/build/meson/standard.meson.build by build.sh, because meson doesn't
+ # allow us to reference a file outside its root
+ mkdir -p "$THIS_SCRIPT_PATH/resources"
+ cp "$KEYMAN_ROOT/resources/build/meson/standard.meson.build" "$THIS_SCRIPT_PATH/resources/meson.build"
+
+ pushd "$THIS_SCRIPT_PATH" > /dev/null
+ # Additional arguments are used by Linux build, e.g. -Dprefix=${INSTALLDIR}
+ meson setup build --buildtype $BUILDER_CONFIGURATION "${builder_extra_params[@]}"
+ popd > /dev/null
+
+}
+
+do_build() {
+ pushd "$TARGET_PATH" > /dev/null
+ ninja
+ popd > /dev/null
+}
+
+do_test() {
+ pushd "$TARGET_PATH" > /dev/null
+ meson test "${builder_extra_params[@]}"
+ popd > /dev/null
+}
+
+builder_run_action clean do_clean
+builder_run_action configure do_configure
+builder_run_action build do_build
+builder_run_action test do_test
diff --git a/linux/mcompile/keymap/deadkey.cpp b/linux/mcompile/keymap/deadkey.cpp
new file mode 100644
index 0000000000..325082026b
--- /dev/null
+++ b/linux/mcompile/keymap/deadkey.cpp
@@ -0,0 +1,331 @@
+/*
+ * Keyman is copyright (C) 2004 - 2024 SIL International. MIT License.
+ *
+ * Mnemonic layout support for Linux
+ */
+
+#include "keymap.h"
+#include "deadkey.h"
+
+/**
+ * @brief create a Vector of DeadKey containing all combinations of deadkey + character for ALL possible Linux keyboards
+ * @return vector of Deadkey* that holds all combinations of deadkey + character
+*/
+std::vector create_deadkeys_by_basechar() {
+ std::vector alDead;
+ vec_dword_2D dk_ComposeTable;
+
+ create_DKTable(dk_ComposeTable);
+
+ for (int i = 0; i < (int)dk_ComposeTable.size() - 1; i++) {
+ DeadKey* dk2 = new DeadKey(dk_ComposeTable[i][0]);
+ for (int j = i; j < (int)dk_ComposeTable.size(); j++) {
+ if ((dk_ComposeTable[i][0] == dk_ComposeTable[j][0]) && (IsKeymanUsedChar(dk_ComposeTable[j][1])))
+ dk2->KMX_AddDeadKeyRow(dk_ComposeTable[j][1], dk_ComposeTable[j][2]);
+ }
+ alDead.push_back(dk2);
+ }
+ return alDead;
+}
+
+/**
+ * @brief filter entries for the currently used Linux Keyboard out of a vector of all existing deadKey combinations
+ * @param dk the deadkey for which all combinations will be found
+ * @param[in,out] dkVec combinations of deadkey + character for the currently used Linux Keyboard
+ * @param r_All_Vec all existing combinations of deadkey + character for ALL possible Linux keyboards
+*/
+void refine_alDead(KMX_WCHAR dk, std::vector& dkVec, std::vector& r_All_Vec) {
+ if (dk == 0)
+ return;
+
+ for (int j = 0; j < (int)r_All_Vec.size(); j++) {
+ if (dk == r_All_Vec[j]->KMX_GetDeadCharacter()) {
+ if (!found_dk_inVector(dk, dkVec)) {
+ dkVec.push_back(r_All_Vec[j]);
+ }
+ return;
+ }
+ }
+}
+
+/**
+ * @brief check whether a deadkey already exists in the deadkey vector
+ * @param dk the deadkey to be found
+ * @param dkVec vector containing combinations of deadkey + character
+ * @return true if deadkey alredy exists;
+ * false if not
+*/
+bool found_dk_inVector(KMX_WCHAR dk, std::vector& dkVec) {
+ for (int i = 0; i < dkVec.size(); i++) {
+ if (dk == dkVec[i]->KMX_GetDeadCharacter())
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief find all deadkey combinations for a certain deadkey in a vector of all deadkey combinations
+ * @param r_dk_ComposeTable vector containing all possible deadkey combinations
+ * @param dk deadkey of interest
+ * @param[in,out] dk_SingleTable vector containing all dk-character combinations for a specific deadkey dk
+ * @return true if successful;
+ * false if not
+*/
+bool query_dk_combinations_for_specific_dk(vec_dword_2D& r_dk_ComposeTable, KMX_DWORD dk, vec_dword_2D& dk_SingleTable) {
+ vec_dword_1D row;
+
+ for (int i = 0; i < (int)r_dk_ComposeTable.size(); i++) {
+ if (r_dk_ComposeTable[i][0] == dk && IsKeymanUsedChar(r_dk_ComposeTable[i][1])) {
+ row.push_back(r_dk_ComposeTable[i][0]);
+ row.push_back(r_dk_ComposeTable[i][1]);
+ row.push_back(r_dk_ComposeTable[i][2]);
+ dk_SingleTable.push_back(row);
+ row.clear();
+ }
+ }
+
+ if (dk_SingleTable.size() > 0)
+ return true;
+ else
+ return false;
+}
+
+/**
+ * @brief convert a character to the upper-case equivalent and find the corresponding shiftstate
+ * of the entered keyval: a(97) -> A(65) + Base A(65) -> A(65) + Shift
+ * @param kval keyval that might be changed
+ * @param[in,out] shift the shiftstate of the entered keyval
+ * @param keymap a pointer to the currently used (underlying) keyboard layout
+ * @return the upper case equivalent of the keyval
+*/
+KMX_DWORD KMX_change_keyname_to_capital(KMX_DWORD kVal, KMX_DWORD& shift, GdkKeymap* keymap) {
+ guint keyval = (guint)kVal;
+ GdkKeymapKey* keys;
+ gint n_keys;
+
+ KMX_DWORD capitalKeyval = (KMX_DWORD)gdk_keyval_to_upper(kVal);
+ if (keyval != 0) {
+ gdk_keymap_get_entries_for_keyval(keymap, keyval, &keys, &n_keys);
+ for (int i = 0; i < n_keys; i++) {
+ if (keys[i].group == 0) {
+ shift = keys[i].level;
+ return capitalKeyval;
+ }
+ }
+ }
+ return capitalKeyval;
+}
+
+/**
+ * @brief append a 1D-vector containing name, base character and unicode_value to a 2D-Vector
+ * holding all possible combinations of deadkey + character for all Linux keyboards
+ * @param[in,out] dk_ComposeTable 2D-Vector holding all possible combinations of deadkey + character
+ * @param diacritic_name the name of a diacritic
+ * @param base_char base character
+ * @param unicode_value Unicode-value of the combined character
+*/
+void add_deadkey_combination(vec_dword_2D& dk_ComposeTable, std::string diacritic_name, std::string base_char, KMX_DWORD unicode_value) {
+ vec_dword_1D line;
+ line.push_back(convertNamesTo_DWORD_Value(diacritic_name));
+ line.push_back(convertNamesTo_DWORD_Value(base_char));
+ line.push_back(unicode_value);
+ dk_ComposeTable.push_back(line);
+}
+
+/**
+ * @brief create a 2D-Vector containing all possible combinations of deadkey + character for all Linux keyboards
+ * the values are taken from from: https://help.ubuntu.com/community/GtkDeadKeyTable#Accents
+ * dk_ComposeTable[i][0] : diacritic_name (e.g. dead_circumflex)
+ * dk_ComposeTable[i][1] : base_char (e.g. a)
+ * dk_ComposeTable[i][2] : unicode_value-Value (e.g. 0x00E2)
+ * @param[in,out] dk_ComposeTable
+*/
+void create_DKTable(vec_dword_2D& dk_ComposeTable) {
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "a", 0x00E2); // small A with circumflex
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "A", 0x00C2); // capital A with circumflex
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "e", 0x00EA); // small E with circumflex
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "E", 0x00CA); // capital E with circumflex
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "i", 0x00EE); // small I with circumflex
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "I", 0x00CE); // capital I with circumflex
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "o", 0x00F4); // small O with circumflex
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "O", 0x00D4); // capital O with circumflex
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "u", 0x00FB); // small U with circumflex
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "U", 0x00DB); // capital U with circumflex
+
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "a", 0x00E1); // small A with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "A", 0x00C1); // capital A with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "c", 0x0107); // small C with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "C", 0x0106); // capital C with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "e", 0x00E9); // small E with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "E", 0x00C9); // capital E with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "i", 0x00ED); // small I with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "I", 0x00CD); // capital I with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "l", 0x013A); // small L with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "L", 0x0139); // capital L with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "n", 0x0144); // small N with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "N", 0x0143); // capital N with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "o", 0x00F3); // small O with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "O", 0x00D3); // capital O with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "r", 0x0155); // small R with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "R", 0x0154); // capital R with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "s", 0x015B); // small S with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "S", 0x015A); // capital S with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "u", 0x00FA); // small U with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "U", 0x00DA); // capital U with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "y", 0x00FD); // small Y with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "Y", 0x00DD); // capital Y with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "z", 0x017A); // small Z with acute
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "Z", 0x0179); // capital Z with acute
+
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "a", 0x00E0); // small A with grave
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "A", 0x00C0); // capital A with grave
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "e", 0x00E8); // small E with grave
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "E", 0x00C8); // capital E with grave
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "i", 0x00EC); // small I with grave
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "I", 0x00CC); // capital I with grave
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "o", 0x00F2); // small O with grave
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "O", 0x00D2); // capital O with grave
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "u", 0x00F9); // small U with grave
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "U", 0x00D9); // capital U with grave
+
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "a", 0x00E3); // small A with tilde
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "A", 0x00C3); // capital A with tilde
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "i", 0x0129); // small I with tilde
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "I", 0x0128); // capital I with tilde
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "n", 0x00F1); // small N with tilde
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "N", 0x00D1); // capital N with tilde
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "o", 0x00F5); // small O with tilde
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "O", 0x00D5); // capital O with tilde
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "u", 0x0169); // small U with tilde
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "U", 0x0168); // capital U with tilde
+
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "a", 0x0101); // small A with macron
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "A", 0x0100); // capital A with macron
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "e", 0x0113); // small E with macron
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "E", 0x0112); // capital E with macron
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "i", 0x012B); // small I with macron
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "I", 0x012A); // capital I with macron
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "o", 0x014D); // small O with macron
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "O", 0x014C); // capital O with macron
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "u", 0x016B); // small U with macron
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "U", 0x016A); // capital U with macron
+
+ add_deadkey_combination(dk_ComposeTable, "dead_breve", "a", 0x0103); // small A with breve
+ add_deadkey_combination(dk_ComposeTable, "dead_breve", "A", 0x0102); // capital A with breve
+ add_deadkey_combination(dk_ComposeTable, "dead_breve", "g", 0x011F); // small G with breve
+ add_deadkey_combination(dk_ComposeTable, "dead_breve", "G", 0x011E); // capital G with breve
+
+ add_deadkey_combination(dk_ComposeTable, "dead_abovedot", "e", 0x0117); // small E with dot above
+ add_deadkey_combination(dk_ComposeTable, "dead_abovedot", "E", 0x0116); // capital E with dot above
+ add_deadkey_combination(dk_ComposeTable, "dead_abovedot", "i", 0x0131); // small DOTLESS_I
+ add_deadkey_combination(dk_ComposeTable, "dead_abovedot", "I", 0x0130); // capital I with dot above
+ add_deadkey_combination(dk_ComposeTable, "dead_abovedot", "z", 0x017C); // small Z with dot above
+ add_deadkey_combination(dk_ComposeTable, "dead_abovedot", "Z", 0x017B); // capital Z with dot above
+
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "a", 0x00E4); // small A with diaeresis
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "A", 0x00C4); // capital A with diaeresis
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "e", 0x00EB); // small E with diaeresis
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "E", 0x00CB); // capital E with diaeresis
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "i", 0x00EF); // small I with diaeresis
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "I", 0x00CF); // capital I with diaeresis
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "o", 0x00F6); // small O with diaeresis
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "O", 0x00D6); // capital O with diaeresis
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "u", 0x00FC); // small U with diaeresis
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "U", 0x00DC); // capital U with diaeresis
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "y", 0x00FF); // small Y with diaeresis
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "Y", 0x0178); // capital Y with diaeresis
+
+ add_deadkey_combination(dk_ComposeTable, "dead_abovering", "a", 0x00E5); // small A with ring above
+ add_deadkey_combination(dk_ComposeTable, "dead_abovering", "A", 0x00C5); // capital A with ring above
+ add_deadkey_combination(dk_ComposeTable, "dead_abovering", "u", 0x016F); // small U with ring above
+ add_deadkey_combination(dk_ComposeTable, "dead_abovering", "U", 0x016E); // capital U with ring above
+
+ add_deadkey_combination(dk_ComposeTable, "dead_doubleacute", "o", 0x0151); // small O with double acute
+ add_deadkey_combination(dk_ComposeTable, "dead_doubleacute", "O", 0x0150); // capital O with double acute
+ add_deadkey_combination(dk_ComposeTable, "dead_doubleacute", "u", 0x0171); // small U with double acute
+ add_deadkey_combination(dk_ComposeTable, "dead_doubleacute", "U", 0x0170); // capital U with double acute
+
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "c", 0x010D); // small C with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "C", 0x010C); // capital C with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "d", 0x010F); // small D with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "D", 0x010E); // capital D with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "e", 0x011B); // small E with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "E", 0x011A); // capital E with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "l", 0x013E); // small L with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "L", 0x013D); // capital L with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "n", 0x0148); // small N with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "N", 0x0147); // capital N with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "r", 0x0159); // small R with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "R", 0x0158); // capital R with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "s", 0x0161); // small S with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "S", 0x0160); // capital S with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "t", 0x0165); // small T with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "T", 0x0164); // capital T with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "z", 0x017E); // small Z with caron
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "Z", 0x017D); // capital Z with caron
+
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "c", 0x00E7); // small C with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "C", 0x00C7); // capital C with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "g", 0x0123); // small G with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "G", 0x0122); // capital G with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "k", 0x0137); // small K with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "K", 0x0136); // capital K with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "l", 0x013C); // small L with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "L", 0x013B); // capital L with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "n", 0x0146); // small N with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "N", 0x0145); // capital N with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "r", 0x0157); // small R with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "R", 0x0156); // capital R with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "s", 0x015F); // small S with cedilla
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "S", 0x015E); // capital S with cedilla
+
+ add_deadkey_combination(dk_ComposeTable, "dead_ogonek", "a", 0x0105); // small A with ogonek
+ add_deadkey_combination(dk_ComposeTable, "dead_ogonek", "A", 0x0104); // capital A with ogonek
+ add_deadkey_combination(dk_ComposeTable, "dead_ogonek", "e", 0x0119); // small E with ogonek
+ add_deadkey_combination(dk_ComposeTable, "dead_ogonek", "E", 0x0118); // capital E with ogonek
+ add_deadkey_combination(dk_ComposeTable, "dead_ogonek", "i", 0x012F); // small I with ogonek
+ add_deadkey_combination(dk_ComposeTable, "dead_ogonek", "I", 0x012E); // capital I with ogonek
+ add_deadkey_combination(dk_ComposeTable, "dead_ogonek", "u", 0x0173); // small U with ogonek
+ add_deadkey_combination(dk_ComposeTable, "dead_ogonek", "U", 0x0172); // capital U with ogonek
+
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "space", 0x005E); // CIRCUMFLEX_ACCENT
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "space", 0x0027); // APOSTROPHE
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "space", 0x0060); // GRAVE_ACCENT
+ add_deadkey_combination(dk_ComposeTable, "dead_breve", "space", 0x02D8); // BREVE
+ add_deadkey_combination(dk_ComposeTable, "dead_abovedot", "space", 0x02D9); // DOT_ABOVE
+ add_deadkey_combination(dk_ComposeTable, "dead_abovering", "space", 0x02DA); // RING_ABOVE
+ add_deadkey_combination(dk_ComposeTable, "dead_doubleacute", "space", 0x02DD); // DOUBLE_ACUTE_ACCENT
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "space", 0x02C7); // CARON
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "space", 0x00B8); // CEDILLA
+ add_deadkey_combination(dk_ComposeTable, "dead_ogonek", "space", 0x02DB); // OGONEK
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "space", 0x007E); // TILDE
+
+ add_deadkey_combination(dk_ComposeTable, "dead_breve", "dead_breve", 0x02D8); // BREVE
+ add_deadkey_combination(dk_ComposeTable, "dead_abovedot", "abovedot", 0x02D9); // DOT_ABOVE
+ add_deadkey_combination(dk_ComposeTable, "dead_abovedot", "dead_abovedot", 0x02D9); // DOT_ABOVE
+ add_deadkey_combination(dk_ComposeTable, "dead_abovering", "dead_abovering", 0x02DA); // RING_ABOVE
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "apostrophe", 0x00B4); // ACUTE_ACCENT
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "acute", 0x00B4); // ACUTE_ACCENT
+ add_deadkey_combination(dk_ComposeTable, "dead_acute", "dead_acute", 0x00B4); // ACUTE_ACCENT
+ add_deadkey_combination(dk_ComposeTable, "dead_doubleacute", "dead_doubleacute", 0x02DD); // DOUBLE_ACUTE_ACCENT
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "caron", 0x02C7); // CARON
+ add_deadkey_combination(dk_ComposeTable, "dead_caron", "dead_caron", 0x02C7); // CARON
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "comma", 0x00B8); // CEDILLA
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "cedilla", 0x00B8); // CEDILLA
+ add_deadkey_combination(dk_ComposeTable, "dead_cedilla", "dead_cedilla", 0x00B8); // CEDILLA
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "minus", 0x00AF); // MACRON
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "asciicircum", 0x005E); // CIRCUMFLEX_ACCENT
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "underscore", 0x00AF); // MACRON
+ add_deadkey_combination(dk_ComposeTable, "dead_circumflex", "dead_circumflex", 0x005E); // CIRCUMFLEX_ACCENT
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "quotedbl", 0x00A8); // DIAERESIS
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "diaeresis", 0x00A8); // DIAERESIS
+ add_deadkey_combination(dk_ComposeTable, "dead_diaeresis", "dead_diaeresis", 0x00A8); // DIAERESIS
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "grave", 0x0060); // GRAVE_ACCENT
+ add_deadkey_combination(dk_ComposeTable, "dead_grave", "dead_grave", 0x0060); // GRAVE_ACCENT
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "macron", 0x00AF); // MACRON
+ add_deadkey_combination(dk_ComposeTable, "dead_macron", "dead_macron", 0x00AF); // MACRON
+ add_deadkey_combination(dk_ComposeTable, "dead_ogonek", "ogonek", 0x02DB); // OGONEK
+ add_deadkey_combination(dk_ComposeTable, "dead_ogonek", "dead_ogonek", 0x02DB); // OGONEK
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "asciitilde", 0x007E); // TILDE
+ add_deadkey_combination(dk_ComposeTable, "dead_tilde", "dead_tilde", 0x007E); // TILDE
+}
diff --git a/linux/mcompile/keymap/deadkey.h b/linux/mcompile/keymap/deadkey.h
new file mode 100644
index 0000000000..5c52768ecf
--- /dev/null
+++ b/linux/mcompile/keymap/deadkey.h
@@ -0,0 +1,30 @@
+
+#pragma once
+#ifndef DEADKEY_H
+#define DEADKEY_H
+
+#include "mc_import_rules.h"
+#include