diff --git a/docs/build/macos.md b/docs/build/macos.md
index f5ff040964..aa1eea34bf 100644
--- a/docs/build/macos.md
+++ b/docs/build/macos.md
@@ -1,6 +1,8 @@
# Setup your Keyman build environment on macOS
-This document describes prerequisites and tools required for building various Keyman projects on macOS. Each project will also have additional notes linked below.
+This document describes prerequisites and tools required for building various
+Keyman projects on macOS. Each project will also have additional notes linked
+below.
## Target Projects
@@ -10,6 +12,7 @@ On macOS, you can build the following projects:
* Keyman for iOS ([additional details](../../ios/README.md))
* Keyman for macOS ([additional details](../../mac/README.md))
* KeymanWeb ([additional details](../../web/README.md))
+* Keyman Developer Command Line Tools (kmc)
The following libraries can also be built:
@@ -20,19 +23,24 @@ The following projects **cannot** be built on macOS:
* Keyman for Linux
* Keyman for Windows
-* Keyman Developer
+* Keyman Developer (IDE component)
## System Requirements
* Minimum macOS version: macOS Catalina 10.15 or Big Sur 11.0
-**Note:** to make a fully M1-compatible release build of Keyman for macOS (for the setup Applescript), Big Sur 11.0 is required, as osacompile on earlier versions does not build for arm64 (M1). The build will still work on earlier versions, but the installer won't be able to run on M1 Macs that do not have Rosetta 2 installed.
+**Note:** to make a fully M1-compatible release build of Keyman for macOS (for
+the setup Applescript), Big Sur 11.0 is required, as osacompile on earlier
+versions does not build for arm64 (M1). The build will still work on earlier
+versions, but the installer won't be able to run on M1 Macs that do not have
+Rosetta 2 installed.
## Prerequisites
Many dependencies are only required for specific projects.
-* XCode (iOS, macOS) 12.4 or later
+* XCode (iOS, macOS) 12.4 or later is needed only for Keyman for macOS and Keyman
+ for iOS.
* Install from App Store
* Accept the Xcode license: `sudo xcodebuild -license accept`
@@ -48,73 +56,83 @@ These dependencies are also listed below if you'd prefer to install manually.
## Shared Dependencies
-* Shared: HomeBrew, Bash 5.0+, jq, Python 2.7, Python 3, Meson, Ninja, coreutils, Pandoc
+* HomeBrew, Bash 5.0+, jq, Python 2.7, Python 3, Meson, Ninja, coreutils, Pandoc
+
+```shell
+/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
+brew install bash jq python3 meson ninja coreutils pandoc pyenv
+# Python 2.7 required for DeviceKit (among others?) at present
+pyenv install 2.7.18
+pyenv global 2.7.18
+echo 'eval "$(pyenv init --path)"' >> ~/.bash_profile
+```
+
+On macOS, you will need to adjust your PATH so that coreutils’ `realpath` takes
+precedence over the BSD one:
+
+```shell
+# Credit: brew info coreutils
+PATH="$HOMEBREW_PREFIX/opt/coreutils/libexec/gnubin:$PATH"
+```
+
+## KeymanWeb Dependencies
+
+* node.js 18+, emscripten, openjdk 8
+
+```shell
+brew install node emscripten openjdk@8
+```
+
+Note: if you install emscripten with brew on macOS, only emscripten binaries are
+added to the path via symlinks. This makes it reasonably safe to have emscripten
+on the path, unlike on other platforms where emscripten also ends up adding its
+versions of node, python, and other binaries to the path.
+
+## Keyman for iOS Dependencies
+
+* XCode, swiftlint, carthage
+
+```shell
+brew install swiftlint carthage
+```
+
+## Keyman for Mac Dependencies
+
+* XCode, carthage, cocoapods
+
+```shell
+brew install carthage cocoapods
+```
+
+## Keyman for Android Dependencies
+
+* openjdk 11, Android SDK, Android Studio, Ant, Gradle, Maven
+
+```shell
+brew install openjdk@11 android-sdk android-studio ant gradle maven
+# update path
+source ../resources/devbox/macos/keyman.macos.env.sh
+# optionally install sdk images
+sdkmanager "system-images;android-30;google_apis;armeabi-v7a"
+sdkmanager --update
+sdkmanager --licenses
+```
+
+* Note: Run Android Studio once after installation to install additional
+components such as emulator images and SDK updates.
+
+## Keyman Developer Command Line (kmc)
+
+* node.js, emscripten
+
+```shell
+brew install node emscripten
+```
+
+## Optional Tools
+
+* sentry-cli: Uploading symbols for Sentry-based error reporting
```shell
- /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- brew install bash jq python3 meson ninja coreutils pandoc pyenv
- # Python 2.7 required for DeviceKit (among others?) at present
- pyenv install 2.7.18
- pyenv global 2.7.18
- echo 'eval "$(pyenv init --path)"' >> ~/.bash_profile
- ```
-
- On macOS, you will need to adjust your PATH so that coreutils’ `realpath` takes precedence over the BSD one:
-
- ```shell
- # Credit: brew info coreutils
- PATH="$HOMEBREW_PREFIX/opt/coreutils/libexec/gnubin:$PATH"
- ```
-
-* Web: node.js 18+, emscripten, openjdk 8
-
- ```shell
- brew install node emscripten openjdk@8
- ```
-
- Note: if you install emscripten with brew on macOS, only emscripten binaries
- are added to the path via symlinks. This makes it reasonably safe to have
- emscripten on the path, unlike on other platforms where emscripten also ends
- up adding its versions of node, python, and other binaries to the path.
-
-* iOS: swiftlint, carthage
-
- ```shell
- brew install swiftlint carthage
- ```
-
-* macOS: carthage, cocoapods
-
- ```shell
- brew install carthage cocoapods
- ```
-
-* Android: openjdk 8, Android SDK, Android Studio, Ant, Gradle, Maven
-
- ```shell
- brew install openjdk@8 android-sdk android-studio ant gradle maven
- # update path
- source ../resources/devbox/macos/keyman.macos.env.sh
- # optionally install sdk images
- sdkmanager "system-images;android-30;google_apis;armeabi-v7a"
- sdkmanager --update
- sdkmanager --licenses
- ```
-
-* kmc (optional): node
- - Required to build keyboards using kmc
-
- ```bash
- brew tap homebrew/cask-versions
- brew install --cask --no-quarantine wine-stable
- ```
-
-* sentry-cli (optional)
- - Uploading symbols for Sentry-based error reporting
-
- ```
brew install getsentry/tools/sentry-cli
```
-
-* Run Android Studio once after installation to install additional components
- such as emulator images and SDK updates.
diff --git a/docs/linux/README.md b/docs/linux/README.md
index d848e215ed..fe29b0d623 100644
--- a/docs/linux/README.md
+++ b/docs/linux/README.md
@@ -87,6 +87,39 @@ scripts/run-tests
The `run-tests` script accepts different arguments which can be seen with
`scripts/run-tests --help`.
+### Code Coverage Report
+
+#### Prerequisites
+
+Code coverage reports require some additional tools: lcov, gcovr,
+libdatetime-perl, and coverage.
+
+You can install these with:
+
+```bash
+sudo apt update
+sudo apt install -y lcov libdatetime-perl gcovr
+pip3 install coverage
+```
+
+**Note:** You want lcov > 1.16, so you might have to download and install
+a newer version e.g. from
.
+
+#### Creating and displaying code coverage reports
+
+All three projects (ibus-keyman, keyman-config, and keyman-system-service)
+can produce code coverage reports.
+
+Run `./build.sh --debug --coverage --report test` to create the report.
+
+**Note:** You might have to run `./build.sh clean` first.
+
+There's also an index page (`linux/CodecoverageReports.html`) that links to all
+three reports. You can run
+`linux/build.sh --debug --coverage --report --open --no-integration test`
+to create the coverage reports for all three Linux projects and open the
+index page in the browser.
+
## Running Keyman for Linux
### Setting up Ibus
diff --git a/docs/settings/linux/tasks.json b/docs/settings/linux/tasks.json
index b4a376b508..0d026c5afc 100644
--- a/docs/settings/linux/tasks.json
+++ b/docs/settings/linux/tasks.json
@@ -108,7 +108,7 @@
"CPPFLAGS": "-DG_MESSAGES_DEBUG -I${workspaceFolder}/core/build/arch/debug/include/ -I${workspaceFolder}/core/include/ -I/home/eberhard/Develop/keyman/ibus/ibus/src -I${workspaceFolder}/common/include",
"CFLAGS": "-g -O0",
"CXXFLAGS": "-g -O0",
- "KEYMAN_PROC_LIBS": "-L${workspaceFolder}/core/build/arch/debug/src -lkmnkbp0",
+ "KEYMAN_PROC_LIBS": "-L${workspaceFolder}/core/build/arch/debug/src -lkeymancore",
"KEYMAN_PROC_CFLAGS": "-I${workspaceFolder}/core/build/arch/debug/include -I${workspaceFolder}/core/include -I${workspaceFolder}/common/include",
"PKG_CONFIG_PATH": "${workspaceFolder}/core/build/arch/debug/meson-private"
},
@@ -195,6 +195,23 @@
"group": "test",
"detail": "unit test keyman-config"
},
+ {
+ "type": "shell",
+ "label": "keyman-config: report",
+ "command": "${workspaceFolder}/linux/keyman-config/build.sh",
+ "args": [
+ "test",
+ "--report",
+ "--debug",
+ "--coverage",
+ "--no-integration"
+ ],
+ "options": {
+ "cwd": "${workspaceFolder}/linux/keyman-config",
+ },
+ "group": "test",
+ "detail": "run tests and create unit test coverage report"
+ },
{
"type": "shell",
"label": "keyman-system-service: clean",
@@ -265,6 +282,7 @@
"command": "${workspaceFolder}/linux/keyman-system-service/build.sh",
"args": [
"--debug",
+ "--coverage",
"configure"
],
"options": {
@@ -282,7 +300,7 @@
"command": "${workspaceFolder}/linux/keyman-system-service/build.sh",
"args": [
"--debug",
- "test:arch"
+ "test"
],
"problemMatcher": [
"$gcc"
@@ -290,5 +308,22 @@
"group": "test",
"detail": "run tests for keyman-system-service"
},
+ {
+ "type": "shell",
+ "label": "keyman-system-service: report",
+ "command": "${workspaceFolder}/linux/keyman-system-service/build.sh",
+ "args": [
+ "test",
+ "--report",
+ "--debug",
+ "--coverage",
+ "--no-integration",
+ ],
+ "problemMatcher": [
+ "$gcc"
+ ],
+ "group": "test",
+ "detail": "run tests and create unit test coverage report"
+ },
]
}
diff --git a/linux/CodeCoverageReports.html b/linux/CodeCoverageReports.html
new file mode 100644
index 0000000000..d52d27d056
--- /dev/null
+++ b/linux/CodeCoverageReports.html
@@ -0,0 +1,37 @@
+
+
+ Keyman for Linux Code Coverage
+
+
+
+
+
+
Keyman for Linux Code Coverage Reports
+
+
+
+
+
+
+
+
+
+
+
diff --git a/linux/README.md b/linux/README.md
index e4b50cdc90..3dfb340477 100644
--- a/linux/README.md
+++ b/linux/README.md
@@ -1,5 +1,3 @@
# Keyman for Linux
See [/docs/linux/README.md](../docs/linux/README.md) for documentation.
-
-.
\ No newline at end of file
diff --git a/linux/build.sh b/linux/build.sh
index 3b3648dd14..9a4959c5d0 100755
--- a/linux/build.sh
+++ b/linux/build.sh
@@ -23,8 +23,20 @@ builder_describe \
"test" \
"install install artifacts" \
"uninstall uninstall artifacts" \
- "--no-integration+ don't run integration tests"
+ "--no-integration+ don't run integration tests" \
+ "--coverage+ capture test coverage" \
+ "--report+ create coverage report" \
+ "--open open the coverage reports in the browser"
builder_parse "$@"
builder_run_child_actions clean configure build test install uninstall
+
+test_action() {
+ if builder_has_option --open; then
+ builder_echo "Opening coverage reports in browser..."
+ xdg-open "file://${THIS_SCRIPT_PATH}/CodeCoverageReports.html"
+ fi
+}
+
+builder_run_action test test_action
diff --git a/linux/debian/control b/linux/debian/control
index 624f89fc0a..9b5e217aba 100644
--- a/linux/debian/control
+++ b/linux/debian/control
@@ -107,12 +107,14 @@ Description: Keyman for Linux configuration
This package contains the Python module to install, uninstall and view
information about Keyman keyboard packages.
-Package: libkmnkbp-dev
+Package: libkeymancore-dev
Architecture: amd64 arm64 armel armhf i386 mipsel mips64el ppc64el riscv64
Section: libdevel
Depends:
- libkmnkbp0-0 (= ${binary:Version}),
+ libkeymancore (= ${binary:Version}),
${misc:Depends},
+Conflicts: libkmnkbp-dev
+Replaces: libkmnkbp-dev
Multi-Arch: same
Description: Development files for Keyman keyboard processing library
Originally created in 1993 to type Lao on Windows, Keyman is now a free and
@@ -126,12 +128,12 @@ Description: Development files for Keyman keyboard processing library
Keyboard layouts are distributed through an open catalog to all major desktop
and mobile platforms.
.
- The Keyman keyboardprocessor library processes input from input method engines
+ The Keyman core library processes input from input method engines
and applies rules from compiled Keyman keyboard files.
.
This package contains development headers and libraries.
-Package: libkmnkbp0-0
+Package: libkeymancore
Architecture: amd64 arm64 armel armhf i386 mipsel mips64el ppc64el riscv64
Section: libs
Pre-Depends:
@@ -141,6 +143,8 @@ Depends:
${shlibs:Depends},
Suggests:
keyman,
+Conflicts: libkmnkbp0-0
+Replaces: libkmnkbp0-0
Multi-Arch: same
Description: Keyman keyboard processing library
Originally created in 1993 to type Lao on Windows, Keyman is now a free and
@@ -154,14 +158,14 @@ Description: Keyman keyboard processing library
Keyboard layouts are distributed through an open catalog to all major desktop
and mobile platforms.
.
- The Keyman keyboardprocessor library processes input from input method engines
+ The Keyman core library processes input from input method engines
and applies rules from compiled Keyman keyboard files.
Package: ibus-keyman
Architecture: amd64 arm64 armel armhf i386 mipsel mips64el ppc64el riscv64
Depends:
ibus (>= 1.3.7),
- libkmnkbp0-0 (= ${binary:Version}),
+ libkeymancore (= ${binary:Version}),
keyman-system-service (= ${binary:Version}),
${misc:Depends},
${shlibs:Depends},
diff --git a/linux/debian/libkmnkbp-dev.install b/linux/debian/libkeymancore-dev.install
similarity index 100%
rename from linux/debian/libkmnkbp-dev.install
rename to linux/debian/libkeymancore-dev.install
diff --git a/linux/debian/libkmnkbp0-0.install b/linux/debian/libkeymancore.install
similarity index 100%
rename from linux/debian/libkmnkbp0-0.install
rename to linux/debian/libkeymancore.install
diff --git a/linux/debian/libkeymancore.symbols b/linux/debian/libkeymancore.symbols
new file mode 100644
index 0000000000..9482ded8ae
--- /dev/null
+++ b/linux/debian/libkeymancore.symbols
@@ -0,0 +1,48 @@
+libkeymancore.so.1 libkeymancore #MINVER#
+* Build-Depends-Package: libkeymancore-dev
+
+ km_core_actions_dispose@Base 17.0.197
+ km_core_context_append@Base 17.0.195
+ km_core_context_clear@Base 17.0.195
+ km_core_context_get@Base 17.0.195
+ km_core_context_item_list_size@Base 17.0.195
+ km_core_context_items_dispose@Base 17.0.195
+ km_core_context_items_from_utf16@Base 17.0.195
+ km_core_context_items_from_utf8@Base 17.0.195
+ km_core_context_items_to_utf16@Base 17.0.195
+ km_core_context_items_to_utf32@Base 17.0.197
+ km_core_context_items_to_utf8@Base 17.0.195
+ km_core_context_length@Base 17.0.195
+ km_core_context_set@Base 17.0.195
+ km_core_context_shrink@Base 17.0.195
+ km_core_event@Base 17.0.195
+ km_core_get_engine_attrs@Base 17.0.195
+ km_core_keyboard_dispose@Base 17.0.195
+ km_core_keyboard_get_attrs@Base 17.0.195
+ km_core_keyboard_get_imx_list@Base 17.0.195
+ km_core_keyboard_get_key_list@Base 17.0.195
+ km_core_keyboard_imx_list_dispose@Base 17.0.195
+ km_core_keyboard_key_list_dispose@Base 17.0.195
+ km_core_keyboard_load@Base 17.0.195
+ km_core_options_list_size@Base 17.0.195
+ km_core_process_event@Base 17.0.195
+ km_core_process_queued_actions@Base 17.0.195
+ km_core_state_action_items@Base 17.0.195
+ km_core_state_clone@Base 17.0.195
+ km_core_state_context@Base 17.0.195
+ km_core_state_context_clear@Base 17.0.197
+ km_core_state_context_set_if_needed@Base 17.0.197
+ km_core_state_create@Base 17.0.195
+ km_core_state_debug_get@Base 17.0.195
+ km_core_state_debug_items@Base 17.0.195
+ km_core_state_debug_set@Base 17.0.195
+ km_core_state_dispose@Base 17.0.195
+ km_core_state_get_actions@Base 17.0.197
+ km_core_state_get_intermediate_context@Base 17.0.195
+ km_core_state_imx_deregister_callback@Base 17.0.195
+ km_core_state_imx_register_callback@Base 17.0.195
+ km_core_state_option_lookup@Base 17.0.195
+ km_core_state_options_to_json@Base 17.0.195
+ km_core_state_options_update@Base 17.0.195
+ km_core_state_queue_action_items@Base 17.0.195
+ km_core_state_to_json@Base 17.0.195
diff --git a/linux/debian/libkmnkbp0-0.symbols b/linux/debian/libkmnkbp0-0.symbols
deleted file mode 100644
index cbcbb6f4ce..0000000000
--- a/linux/debian/libkmnkbp0-0.symbols
+++ /dev/null
@@ -1,43 +0,0 @@
-libkmnkbp0.so.0 libkmnkbp0-0 #MINVER#
-* Build-Depends-Package: libkmnkbp-dev
-
- kbp_state_get_intermediate_context@Base 15.0
- km_core_context_append@Base 15.0
- km_core_context_clear@Base 15.0
- km_core_context_get@Base 15.0
- km_core_context_item_list_size@Base 15.0
- km_core_context_items_dispose@Base 15.0
- km_core_context_items_from_utf16@Base 15.0
- km_core_context_items_from_utf8@Base 15.0
- km_core_context_items_to_utf16@Base 15.0
- km_core_context_items_to_utf8@Base 15.0
- km_core_context_length@Base 15.0
- km_core_context_set@Base 15.0
- km_core_context_shrink@Base 15.0
- km_core_event@Base 16.0.105
- km_core_get_engine_attrs@Base 15.0
- km_core_keyboard_dispose@Base 15.0
- km_core_keyboard_get_attrs@Base 15.0
- km_core_keyboard_get_imx_list@Base 15.0
- km_core_keyboard_get_key_list@Base 15.0
- km_core_keyboard_imx_list_dispose@Base 15.0
- km_core_keyboard_key_list_dispose@Base 15.0
- km_core_keyboard_load@Base 15.0
- km_core_options_list_size@Base 15.0
- km_core_process_event@Base 15.0
- km_core_process_queued_actions@Base 15.0
- km_core_state_action_items@Base 15.0
- km_core_state_clone@Base 15.0
- km_core_state_context@Base 15.0
- km_core_state_create@Base 15.0
- km_core_state_debug_get@Base 15.0
- km_core_state_debug_items@Base 15.0
- km_core_state_debug_set@Base 15.0
- km_core_state_dispose@Base 15.0
- km_core_state_imx_deregister_callback@Base 15.0
- km_core_state_imx_register_callback@Base 15.0
- km_core_state_option_lookup@Base 15.0
- km_core_state_options_to_json@Base 15.0
- km_core_state_options_update@Base 15.0
- km_core_state_queue_action_items@Base 15.0
- km_core_state_to_json@Base 15.0
diff --git a/linux/debian/rules b/linux/debian/rules
index 1abfbf004a..6b2e8c75c1 100755
--- a/linux/debian/rules
+++ b/linux/debian/rules
@@ -24,7 +24,7 @@ include /usr/share/dpkg/pkg-info.mk
dh $@ --with-python3
override_dh_auto_configure:
- # keymankeyboardprocessor - we need to configure and build core before
+ # keyman core - we need to configure and build core before
# we can configure ibus-keyman!
core/build.sh --no-tests configure:arch build:arch -- \
--wrap-mode=nodownload --prefix=/usr --sysconfdir=/etc \
diff --git a/linux/debian/tests/control b/linux/debian/tests/control
index c1164c9c8e..ca633c6571 100644
--- a/linux/debian/tests/control
+++ b/linux/debian/tests/control
@@ -1,5 +1,6 @@
Tests: test-build
Depends: @,
build-essential,
+ libicu-dev,
pkg-config,
Architecture: amd64 arm64 armel armhf i386 mips64el mipsel ppc64el riscv64
diff --git a/linux/debian/tests/test-build b/linux/debian/tests/test-build
index e9a989e36f..cdd20dc68e 100644
--- a/linux/debian/tests/test-build
+++ b/linux/debian/tests/test-build
@@ -1,5 +1,5 @@
#!/bin/bash
-# autopkgtest check: Build a program against libkmnkbp-dev to verify that the
+# autopkgtest check: Build a program against libkeymancore-dev to verify that the
# headers and pkg-config file are included and installed correctly.
# cf https://github.com/keymanapp/keyman/issues/7490
@@ -16,7 +16,7 @@ km_core_context* c;
EOF
# shellcheck disable=SC2046
-gcc -c keymantest.c $(pkg-config --cflags --libs keyman_kmn_processor)
+gcc -c keymantest.c $(pkg-config --cflags --libs keyman_core)
echo "build 1: OK"
# Test pkg-config file - include without path
@@ -26,5 +26,5 @@ km_core_context* c;
EOF
# shellcheck disable=SC2046
-gcc -c keymantest.c $(pkg-config --cflags --libs keyman_kmn_processor)
+gcc -c keymantest.c $(pkg-config --cflags --libs keyman_core)
echo "build 2: OK"
diff --git a/linux/ibus-keyman/build.sh b/linux/ibus-keyman/build.sh
index c2c45deec1..5e13ef12e0 100755
--- a/linux/ibus-keyman/build.sh
+++ b/linux/ibus-keyman/build.sh
@@ -49,46 +49,31 @@ else
MESON_COVERAGE=
fi
-if builder_start_action clean; then
- rm -rf "$THIS_SCRIPT_PATH/../build/"
- builder_finish_action success clean
-fi
+configure_action() {
+ # shellcheck disable=SC2086,SC2154
+ meson setup ${MESON_COVERAGE} --werror --buildtype $MESON_TARGET "${builder_extra_params[@]}" "$MESON_PATH"
+}
-if builder_start_action configure; then
- cd "$THIS_SCRIPT_PATH"
- # shellcheck disable=SC2086
- meson setup "$MESON_PATH" --werror --buildtype $MESON_TARGET ${MESON_COVERAGE} "${builder_extra_params[@]}"
- builder_finish_action success configure
-fi
-
-if builder_start_action build; then
- cd "$THIS_SCRIPT_PATH/$MESON_PATH"
- ninja
- builder_finish_action success build
-fi
-
-if builder_start_action test; then
- cd "$THIS_SCRIPT_PATH/$MESON_PATH"
+test_action() {
if builder_has_option --no-integration; then
+ # shellcheck disable=SC2086,SC2154
meson test --print-errorlogs $builder_verbose setup-src-test keymanutil-tests print-kmpdetails-test print-kmp-test bcp47-util-tests teardown-src-test
else
+ # shellcheck disable=SC2086
meson test --print-errorlogs $builder_verbose
fi
if builder_has_option --coverage; then
# Note: requires lcov > 1.16 to properly work (see https://github.com/mesonbuild/meson/issues/6747)
ninja coverage-html
fi
- builder_finish_action success test
-fi
+}
-if builder_start_action install; then
- cd "$THIS_SCRIPT_PATH/$MESON_PATH"
- ninja install
- builder_finish_action success install
-fi
+builder_run_action clean rm -rf "$THIS_SCRIPT_PATH/../build/"
+builder_run_action configure configure_action
-if builder_start_action uninstall; then
- cd "$THIS_SCRIPT_PATH/$MESON_PATH"
- ninja uninstall
- builder_finish_action success uninstall
-fi
+[ -d "$MESON_PATH" ] && cd "$MESON_PATH"
+
+builder_run_action build ninja
+builder_run_action test test_action
+builder_run_action install ninja install
+builder_run_action uninstall ninja uninstall
diff --git a/linux/ibus-keyman/meson.build b/linux/ibus-keyman/meson.build
index 2e402b868e..45539b5ab7 100644
--- a/linux/ibus-keyman/meson.build
+++ b/linux/ibus-keyman/meson.build
@@ -18,8 +18,8 @@ icu = dependency('icu-i18n')
core_dir = meson.current_source_dir() / '../../core'
common_dir = meson.current_source_dir() / '../../common'
-kmnkbp_lib = cc.find_library(
- 'kmnkbp0', # meson will prefix 'lib'
+keymancore_lib = cc.find_library(
+ 'keymancore', # meson will prefix 'lib'
dirs: [ core_dir / 'build/arch' / get_option('buildtype') / 'src' ]
)
diff --git a/linux/ibus-keyman/src/keymanutil.c b/linux/ibus-keyman/src/keymanutil.c
index 4f797aa07b..d8b6b48d60 100644
--- a/linux/ibus-keyman/src/keymanutil.c
+++ b/linux/ibus-keyman/src/keymanutil.c
@@ -65,6 +65,21 @@
#define N_(text) text
+GHashTable *custom_keyboards = NULL;
+
+void free_cust_kbd(gpointer data) {
+ if (data == NULL)
+ return;
+
+ cust_kbd *kbd_data = (cust_kbd *)data;
+
+ g_free(kbd_data->kb_id_with_lang);
+ g_free(kbd_data->lang->id);
+ g_free(kbd_data->lang->name);
+ g_free(kbd_data->lang);
+ g_free(kbd_data);
+}
+
// change to keyman_get_kmpdirs_fromdir
// returns list of directories with kmp.json
GList * keyman_get_kmpdirs_fromdir(GList *kmpdir_list, const gchar * path)
@@ -151,18 +166,17 @@ get_engine_for_language(
kmp_info *info,
keyboard_details *kbd_details,
gchar *kmp_dir,
- gchar *lang_id,
- gchar *lang_name) {
+ kmp_language *lang) {
IBusEngineDesc* engine_desc = NULL;
- if (!lang_id || !strlen(lang_id))
+ if (!lang || !lang->id || !strlen(lang->id))
return engine_desc;
int capacity = 255;
g_autofree gchar *name_with_lang = NULL;
g_autofree gchar *minimized_tag = g_new0(gchar, capacity);
- int result = bcp47_minimize(lang_id, minimized_tag, capacity);
+ int result = bcp47_minimize(lang->id, minimized_tag, capacity);
if (result < 0) {
- g_strlcpy(minimized_tag, lang_id, capacity);
+ g_strlcpy(minimized_tag, lang->id, capacity);
}
g_autofree gchar *lang_code = g_new0(gchar, capacity);
@@ -172,11 +186,11 @@ get_engine_for_language(
// If ibus doesn't know about the language then append the
// language name to the keyboard name
- if (lang_name != NULL) {
+ if (lang->name != NULL) {
g_autofree gchar *ibus_lang = ibus_get_untranslated_language_name(lang_code);
g_debug("%s: untranslated ibus language for %s: %s", __FUNCTION__, minimized_tag, ibus_lang);
if (g_strcmp0(ibus_lang, "Other") == 0) {
- name_with_lang = g_strjoin(" - ", keyboard->name, lang_name, NULL);
+ name_with_lang = g_strjoin(" - ", keyboard->name, lang->name, NULL);
}
}
@@ -198,13 +212,12 @@ get_engine_for_language(
return engine_desc;
}
-// Add a keyboard (ibus engine) to the list of engines
-void
-keyman_add_keyboard(gpointer data, gpointer user_data) {
- kmp_keyboard *keyboard = (kmp_keyboard *)data;
- add_keyboard_data *kb_data = (add_keyboard_data *)user_data;
-
- for (GList *e = kb_data->engines_list; e != NULL; e = e->next) {
+gboolean
+keyman_list_contains_keyboard(
+ GList *engines_list,
+ kmp_keyboard *keyboard
+) {
+ for (GList *e = engines_list; e != NULL; e = e->next) {
IBusEngineDesc *engine_desc = (IBusEngineDesc *)e->data;
const gchar *version = ibus_engine_desc_get_version(engine_desc);
const gchar *engine_name = ibus_engine_desc_get_name(engine_desc);
@@ -215,40 +228,90 @@ keyman_add_keyboard(gpointer data, gpointer user_data) {
// TODO: fix version comparison (#9593)
if (g_strcmp0(kmx_file, keyboard->kmx_file) == 0 && g_strcmp0(version, keyboard->version) >= 0) {
g_debug("keyboard %s already exists at version %s which is newer or same as %s", kmx_file, version, keyboard->version);
- return;
+ return TRUE;
}
}
+ return FALSE;
+}
+
+GList *
+keyman_add_custom_keyboards(
+ kmp_keyboard *keyboard,
+ add_keyboard_data *kb_data,
+ keyboard_details * kbd_details,
+ gchar * kmx_path
+) {
+ GList * engines_list = kb_data->engines_list;
+ GPtrArray *language_keyboards = g_hash_table_lookup(custom_keyboards, kmx_path);
+ if (language_keyboards == NULL)
+ return engines_list;
+
+ for (int i = 0; i < language_keyboards->len; i++) {
+ cust_kbd *data = (cust_kbd *)g_ptr_array_index(language_keyboards, i);
+ IBusEngineDesc *engine_desc = get_engine_for_language(keyboard, kb_data->info, kbd_details, kb_data->kmp_dir, data->lang);
+ if (engine_desc) {
+ engines_list = g_list_append(engines_list, engine_desc);
+ }
+ }
+ return engines_list;
+}
+
+GList *
+keyman_add_keyboards_for_language_if_given(
+ kmp_keyboard *keyboard,
+ add_keyboard_data *kb_data,
+ keyboard_details * kbd_details,
+ gchar * kmx_path
+) {
+ GList * engines_list = kb_data->engines_list;
+ if (keyboard->languages != NULL) {
+ for (GList *l = keyboard->languages; l != NULL; l = l->next) {
+ kmp_language *language = (kmp_language *)l->data;
+ IBusEngineDesc *engine_desc =
+ get_engine_for_language(keyboard, kb_data->info, kbd_details, kb_data->kmp_dir, language);
+ if (engine_desc) {
+ engines_list = g_list_append(engines_list, engine_desc);
+ }
+ }
+ } else {
+ g_message("adding engine %s", kmx_path);
+ engines_list = g_list_append(
+ engines_list,
+ ibus_keyman_engine_desc_new(
+ kmx_path, // kmx full path
+ keyboard->name, // longname
+ kbd_details->description, // description
+ kb_data->info->copyright, // copyright if available
+ NULL, // language, most are ignored by ibus except major languages
+ kbd_details->license, // license
+ kb_data->info->author_desc, // author name only, not email
+ keyman_get_icon_file(kmx_path), // icon full path
+ "us", // layout defaulting to us (en-US)
+ keyboard->version));
+ }
+ return engines_list;
+}
+
+// Add a keyboard (ibus engine) to the list of engines
+void
+keyman_add_keyboard(
+ gpointer data,
+ gpointer user_data
+) {
+ kmp_keyboard *keyboard = (kmp_keyboard *)data;
+ add_keyboard_data *kb_data = (add_keyboard_data *)user_data;
+
+ if (keyman_list_contains_keyboard(kb_data->engines_list, keyboard)) {
+ return;
+ }
g_autofree gchar *json_file = g_strjoin(".", keyboard->id, "json", NULL);
g_autoptr(keyboard_details) kbd_details = g_new0(keyboard_details, 1);
get_keyboard_details(kb_data->kmp_dir, json_file, kbd_details);
g_autofree gchar *abs_kmx = g_strjoin("/", kb_data->kmp_dir, keyboard->kmx_file, NULL);
- if (keyboard->languages != NULL) {
- for (GList *l = keyboard->languages; l != NULL; l = l->next) {
- kmp_language *language = (kmp_language *)l->data;
- IBusEngineDesc *engine_desc =
- get_engine_for_language(keyboard, kb_data->info, kbd_details, kb_data->kmp_dir, language->id, language->name);
- if (engine_desc) {
- kb_data->engines_list = g_list_append(kb_data->engines_list, engine_desc);
- }
- }
- } else {
- g_message("adding engine %s", abs_kmx);
- kb_data->engines_list = g_list_append(
- kb_data->engines_list,
- ibus_keyman_engine_desc_new(
- abs_kmx, // kmx full path
- keyboard->name, // longname
- kbd_details->description, // description
- kb_data->info->copyright, // copyright if available
- NULL, // language, most are ignored by ibus except major languages
- kbd_details->license, // license
- kb_data->info->author_desc, // author name only, not email
- keyman_get_icon_file(abs_kmx), // icon full path
- "us", // layout defaulting to us (en-US)
- keyboard->version));
- }
+ kb_data->engines_list = keyman_add_keyboards_for_language_if_given(keyboard, kb_data, kbd_details, abs_kmx);
+ kb_data->engines_list = keyman_add_custom_keyboards(keyboard, kb_data, kbd_details, abs_kmx);
}
// Add keyboards found in {kmp_dir}/kmp.json to engines_list
@@ -258,6 +321,7 @@ keyman_add_keyboards_from_dir(gpointer data, gpointer user_data) {
GList ** engines_list = (GList **)user_data;
g_autoptr(kmp_details) details = g_new0(kmp_details, 1);
+
if (get_kmp_details(kmp_dir, details) == JSON_OK) {
add_keyboard_data kb_data;
kb_data.engines_list = *engines_list;
@@ -270,13 +334,15 @@ keyman_add_keyboards_from_dir(gpointer data, gpointer user_data) {
}
GList *
-ibus_keyman_list_engines (void)
+ibus_keyman_list_engines()
{
GList *engines = NULL;
GList *kmpdir_list;
gchar *xdgenv;
g_autofree gchar *local_keyboard_path;
+ custom_keyboards = keyman_get_custom_keyboard_dictionary();
+
g_debug("adding from /usr/share/keyman");
kmpdir_list = keyman_get_kmpdirs_fromdir(NULL, "/usr/share/keyman");
g_debug("adding from /usr/local/share/keyman");
@@ -298,7 +364,10 @@ ibus_keyman_list_engines (void)
}
void
-add_engine(gpointer data, gpointer user_data) {
+add_engine(
+ gpointer data,
+ gpointer user_data
+) {
IBusEngineDesc *desc = IBUS_ENGINE_DESC(data);
IBusComponent *component = IBUS_COMPONENT(user_data);
ibus_component_add_engine(component, g_object_ref(desc));
@@ -324,7 +393,6 @@ ibus_keyman_get_component (void)
return component;
}
-
// Obtain Keyboard Options list from DConf
// DConf options are in a list of strings like ['option_key1=value1', 'option_key2=value2']
//
@@ -460,6 +528,119 @@ keyman_put_options_todconf(gchar *package_id,
// kvp got assigned to options[x] and so gets freed when options are freed
}
+static GPtrArray *
+_ptr_array_new_from_array(
+ gpointer *data,
+ gsize len,
+ gboolean null_terminated) {
+ GPtrArray *array;
+
+ g_assert(data != NULL || len == 0);
+ g_assert(len <= G_MAXUINT);
+
+ array = g_ptr_array_new_full(len, NULL);
+
+ for (gsize i = 0; i < len; i++)
+ array->pdata[i] = g_strdup(data[i]);
+
+ if (null_terminated && array->pdata != NULL)
+ array->pdata[len++] = NULL;
+
+ array->len = len;
+
+ return array;
+}
+
+// `g_ptr_array_new_from_null_terminated_array` is only available in GLib 2.76, but we're still
+// stuck to 2.64 (Ubuntu 20.04 Focal) and 2.72 (Ubuntu 22.04 Jammy). Therefore we
+// copy the implementation here (slightly simplified). Once we're past 2.76 we can use the GLib method
+// directly.
+GPtrArray *
+_g_ptr_array_new_from_null_terminated_array(
+ gpointer *data,
+ GCopyFunc copy_func,
+ gpointer copy_func_user_data,
+ GDestroyNotify element_free_func) {
+ gsize len = 0;
+
+ if (data != NULL) {
+ for (gsize i = 0; data[i] != NULL; ++i)
+ len += 1;
+ }
+
+ g_assert(data != NULL || len == 0);
+ g_return_val_if_fail(len <= G_MAXUINT, NULL);
+
+ return _ptr_array_new_from_array(data, len, TRUE);
+}
+
+GPtrArray *
+_keyman_cleanup_custom_keyboards(gchar **keyboards) {
+ GPtrArray *ptr_array = _g_ptr_array_new_from_null_terminated_array((gpointer*)keyboards, NULL, NULL, NULL);
+
+ for (int i = 0; i < ptr_array->len; i++) {
+ if (ptr_array->pdata[i] == NULL) {
+ continue;
+ }
+ g_auto(GStrv) keyboard_tokens = g_strsplit(ptr_array->pdata[i], ":", 2);
+ if (keyboard_tokens == NULL
+ || keyboard_tokens[0] == NULL || strlen(keyboard_tokens[0]) == 0
+ || keyboard_tokens[1] == NULL || strlen(keyboard_tokens[1]) == 0
+ ) {
+ g_ptr_array_remove_index(ptr_array, i--);
+ }
+ }
+ return ptr_array;
+}
+
+gchar **
+keyman_get_custom_keyboards() {
+ g_autoptr(GSettings) settings = g_settings_new(KEYMAN_DCONF_ENGINE_NAME);
+ g_auto(GStrv) result = g_settings_get_strv(settings, KEYMAN_DCONF_KEYBOARDS_KEY);
+ if (result && result[0] == NULL) {
+ return NULL;
+ }
+ g_autoptr(GPtrArray) cleaned_result = _keyman_cleanup_custom_keyboards(result);
+ return (gchar**)g_ptr_array_steal(cleaned_result, NULL);
+}
+
+void
+keyman_set_custom_keyboards(gchar ** keyboards) {
+ g_autoptr(GSettings) settings = g_settings_new(KEYMAN_DCONF_ENGINE_NAME);
+ g_autoptr(GPtrArray) cleaned_keyboards = _keyman_cleanup_custom_keyboards(keyboards);
+ g_settings_set_strv(settings, KEYMAN_DCONF_KEYBOARDS_KEY, (const gchar *const *)cleaned_keyboards->pdata);
+}
+
+GHashTable *
+keyman_get_custom_keyboard_dictionary() {
+ g_auto(GStrv) custom_keyboards = keyman_get_custom_keyboards();
+ if (!custom_keyboards)
+ return NULL;
+
+ GHashTable *hash_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
+
+ for (int i = 0; custom_keyboards[i]; i++) {
+ g_auto(GStrv) keyboard_tokens = g_strsplit(custom_keyboards[i], ":", 2);
+
+ if (keyboard_tokens != NULL && keyboard_tokens[0] != NULL && keyboard_tokens[1] != NULL) {
+ GPtrArray *language_keyboards = g_hash_table_lookup(hash_table, keyboard_tokens[1]);
+ if (!language_keyboards) {
+ language_keyboards = g_ptr_array_new_full(1, free_cust_kbd);
+ g_hash_table_insert(hash_table, g_strdup(keyboard_tokens[1]), language_keyboards);
+ }
+
+ cust_kbd *data = g_new0(cust_kbd, 1);
+ data->kb_id_with_lang = g_strdup(custom_keyboards[i]);
+ data->lang = g_new0(kmp_language, 1);
+ data->lang->id = g_strdup(keyboard_tokens[0]);
+ g_ptr_array_add(language_keyboards, data);
+ } else {
+ g_debug("%s: Invalid keyboard: '%s'", __FUNCTION__, custom_keyboards[i]);
+ }
+ }
+
+ return hash_table;
+}
#ifdef DEBUG
#include
diff --git a/linux/ibus-keyman/src/keymanutil.h b/linux/ibus-keyman/src/keymanutil.h
index de1aef49fa..8a3f4506fa 100644
--- a/linux/ibus-keyman/src/keymanutil.h
+++ b/linux/ibus-keyman/src/keymanutil.h
@@ -68,6 +68,9 @@
#define KEYMAN_DCONF_OPTIONS_PATH "/desktop/ibus/keyman/options/"
#define KEYMAN_DCONF_OPTIONS_KEY "options"
+#define KEYMAN_DCONF_ENGINE_NAME "com.keyman.engine"
+#define KEYMAN_DCONF_ENGINE_PATH "/com/keyman/engine/"
+#define KEYMAN_DCONF_KEYBOARDS_KEY "additional-keyboards"
G_BEGIN_DECLS
diff --git a/linux/ibus-keyman/src/keymanutil_internal.h b/linux/ibus-keyman/src/keymanutil_internal.h
index 614bf73712..5b9495519d 100644
--- a/linux/ibus-keyman/src/keymanutil_internal.h
+++ b/linux/ibus-keyman/src/keymanutil_internal.h
@@ -13,6 +13,11 @@ typedef struct {
gchar *kmp_dir;
} add_keyboard_data;
+typedef struct {
+ kmp_language *lang;
+ gchar *kb_id_with_lang;
+} cust_kbd;
+
IBusEngineDesc *ibus_keyman_engine_desc_new(
gchar *file_name,
gchar *name,
@@ -30,9 +35,11 @@ IBusEngineDesc *get_engine_for_language(
kmp_info *info,
keyboard_details *kbd_details,
gchar *kmp_dir,
- gchar *lang_id,
- gchar *lang_name);
+ kmp_language *lang);
+gchar** keyman_get_custom_keyboards();
+void keyman_set_custom_keyboards(gchar ** keyboards);
+GHashTable * keyman_get_custom_keyboard_dictionary();
void keyman_add_keyboard(gpointer data, gpointer user_data);
void keyman_add_keyboards_from_dir(gpointer data, gpointer user_data);
diff --git a/linux/ibus-keyman/src/kmpdetails.c b/linux/ibus-keyman/src/kmpdetails.c
index 4748fc08bd..ba6e26faea 100644
--- a/linux/ibus-keyman/src/kmpdetails.c
+++ b/linux/ibus-keyman/src/kmpdetails.c
@@ -229,6 +229,7 @@ void kmp_files_foreach (JsonArray *array,
}
}
+// Get kmp_details from `.json`
kmp_json_status get_keyboard_details(const char *kmp_dir, const char *id, keyboard_details *keyboard)
{
g_autoptr(JsonParser) parser;
@@ -267,6 +268,7 @@ kmp_json_status get_keyboard_details(const char *kmp_dir, const char *id, keyboa
return JSON_OK;
}
+// Get kmp_details from `kmp.json` file
kmp_json_status get_kmp_details(const char *kmp_dir, kmp_details *details)
{
g_autoptr(JsonParser) parser;
diff --git a/linux/ibus-keyman/src/meson.build b/linux/ibus-keyman/src/meson.build
index f7fe8ada1d..2b436caef6 100644
--- a/linux/ibus-keyman/src/meson.build
+++ b/linux/ibus-keyman/src/meson.build
@@ -17,7 +17,7 @@ include_dirs = [
include_directories(meson.current_build_dir() / '..'),
]
-deps = [gtk, ibus, icu, json_glib, kmnkbp_lib, systemd]
+deps = [gtk, ibus, icu, json_glib, keymancore_lib, systemd]
prefix = get_option('prefix')
cfg = configuration_data()
diff --git a/linux/ibus-keyman/src/test/keymanutil_tests.c b/linux/ibus-keyman/src/test/keymanutil_tests.c
index 2be4c38bf2..9a06ff4cf9 100644
--- a/linux/ibus-keyman/src/test/keymanutil_tests.c
+++ b/linux/ibus-keyman/src/test/keymanutil_tests.c
@@ -30,13 +30,32 @@ _get_tst_options_key(gchar* testname) {
return result;
}
+void
+_delete_tst_kbds_key() {
+ g_autoptr(GSettings) settings = g_settings_new(KEYMAN_DCONF_ENGINE_NAME);
+ g_settings_reset(settings, KEYMAN_DCONF_KEYBOARDS_KEY);
+}
+
+void
+_set_tst_kbds_key(gchar** keyboards) {
+ g_autoptr(GSettings) settings = g_settings_new(KEYMAN_DCONF_ENGINE_NAME);
+ g_settings_set_strv(settings, KEYMAN_DCONF_KEYBOARDS_KEY, (const gchar* const*)keyboards);
+}
+
+gchar**
+_get_tst_kbds_key() {
+ g_autoptr(GSettings) settings = g_settings_new(KEYMAN_DCONF_ENGINE_NAME);
+ return g_settings_get_strv(settings, KEYMAN_DCONF_KEYBOARDS_KEY);
+}
+
kmp_keyboard*
-_get_tst_kmp_keyboard(gchar* version, gchar** languages) {
+_get_tst_kmp_keyboard(gchar* version, gchar** languages, gchar* id) {
+ g_assert(id != NULL);
kmp_keyboard* keyboard = g_new0(kmp_keyboard, 1);
keyboard->name = g_strdup("Testing");
- keyboard->id = g_strdup("tst");
+ keyboard->id = g_strdup(id);
keyboard->version = g_strdup(version);
- keyboard->kmx_file = g_strdup("tst.kmx");
+ keyboard->kmx_file = g_strdup_printf("%s.kmx", id);
keyboard->kvk_file = g_strdup("");
keyboard->languages = NULL;
for (gchar* lang = *languages++; lang; lang = *languages++) {
@@ -88,6 +107,22 @@ _free_tst_kb_data(add_keyboard_data* kb_data) {
G_DEFINE_AUTOPTR_CLEANUP_FUNC(add_keyboard_data, _free_tst_kb_data)
+// Newer glib versions have g_assert_cmpstrv which would allow to do
+// g_assert_cmpstrv(result, keyboards);
+// but unfortunately Ubuntu 20.04 Focal doesn't have that, so we roll
+// our own
+void
+_kmn_assert_cmpstrv(gchar** result, gchar** expected) {
+ g_assert_nonnull(result);
+ g_assert_nonnull(expected);
+ int i = 0;
+ for (; result[i] && expected[i]; i++) {
+ g_assert_cmpstr(result[i], ==, expected[i]);
+ }
+ g_assert_null(result[i]);
+ g_assert_null(expected[i]);
+}
+
//----------------------------------------------------------------------------------------------
void
test_keyman_put_options_todconf__invalid() {
@@ -177,6 +212,247 @@ test_keyman_put_options_todconf__existing_key() {
_delete_tst_options_key(testname);
}
+//----------------------------------------------------------------------------------------------
+void
+test_keyman_get_custom_keyboard_dictionary__values() {
+ // Initialize
+ gchar* keyboards[] = {"fr:/tmp/test/test.kmx", "en:/tmp/foo/foo.kmx", "fr:/tmp/foo/foo.kmx", NULL};
+ _set_tst_kbds_key(keyboards);
+
+ // Execute
+ g_autoptr(GHashTable) result = keyman_get_custom_keyboard_dictionary();
+
+ // Verify
+ GPtrArray* value = g_hash_table_lookup(result, "/tmp/test/test.kmx");
+ g_assert_cmpint(value->len, ==, 1);
+ cust_kbd* pdata = value->pdata[0];
+ g_assert_cmpstr(pdata->lang->id, ==, "fr");
+ g_assert_cmpstr(pdata->kb_id_with_lang, ==, "fr:/tmp/test/test.kmx");
+
+ value = g_hash_table_lookup(result, "/tmp/foo/foo.kmx");
+ g_assert_cmpint(value->len, ==, 2);
+ pdata = value->pdata[0];
+ g_assert_cmpstr(pdata->lang->id, ==, "en");
+ g_assert_cmpstr(pdata->kb_id_with_lang, ==, "en:/tmp/foo/foo.kmx");
+ pdata = value->pdata[1];
+ g_assert_cmpstr(pdata->lang->id, ==, "fr");
+ g_assert_cmpstr(pdata->kb_id_with_lang, ==, "fr:/tmp/foo/foo.kmx");
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+void
+test_keyman_get_custom_keyboard_dictionary__invalid() {
+ // Initialize
+ gchar* keyboards[] = {"/tmp/test/test.kmx", NULL};
+ _set_tst_kbds_key(keyboards);
+
+ // Execute
+ g_autoptr(GHashTable) result = keyman_get_custom_keyboard_dictionary();
+
+ // Verify
+ g_assert_nonnull(result);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+void
+test_keyman_get_custom_keyboard_dictionary__empty() {
+ // Initialize
+ gchar* keyboards[] = {NULL};
+ _set_tst_kbds_key(keyboards);
+
+ // Execute
+ g_autoptr(GHashTable) result = keyman_get_custom_keyboard_dictionary();
+
+ // Verify
+ g_assert_null(result);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+void
+test_keyman_get_custom_keyboard_dictionary__null() {
+ // Initialize
+ _delete_tst_kbds_key();
+
+ // Execute
+ g_autoptr(GHashTable) result = keyman_get_custom_keyboard_dictionary();
+
+ // Verify
+ g_assert_null(result);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+//----------------------------------------------------------------------------------------------
+void
+test_keyman_set_custom_keyboards__new_key() {
+ // Initialize
+ _delete_tst_kbds_key();
+ gchar* keyboards[] = {"fr:/tmp/test/test.kmx", NULL};
+
+ // Execute
+ keyman_set_custom_keyboards(keyboards);
+
+ // Verify
+ g_auto(GStrv) result = _get_tst_kbds_key();
+ g_assert_nonnull(result);
+ _kmn_assert_cmpstrv(result, keyboards);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+void
+test_keyman_set_custom_keyboards__overwrite_key() {
+ // Initialize
+ gchar* initialKbds[] = {"fr:/tmp/test/test.kmx", NULL};
+ _set_tst_kbds_key(initialKbds);
+
+ gchar* keyboards[] = {"fr:/tmp/test/test.kmx", "en:/tmp/foo/foo.kmx", NULL};
+
+ // Execute
+ keyman_set_custom_keyboards(keyboards);
+
+ // Verify
+ g_auto(GStrv) result = _get_tst_kbds_key();
+ g_assert_nonnull(result);
+ _kmn_assert_cmpstrv(result, keyboards);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+void
+test_keyman_set_custom_keyboards__delete_key_NULL() {
+ // Initialize
+ gchar* initialKbds[] = {"fr:/tmp/test/test.kmx", NULL};
+ _set_tst_kbds_key(initialKbds);
+
+ // Execute
+ keyman_set_custom_keyboards(NULL);
+
+ // Verify
+ g_auto(GStrv) result = _get_tst_kbds_key();
+ gchar* expected[] = {NULL};
+ g_assert_nonnull(result);
+ _kmn_assert_cmpstrv(result, expected);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+void
+test_keyman_set_custom_keyboards__delete_key_empty_array() {
+ // Initialize
+ gchar* initialKbds[] = {"fr:/tmp/test/test.kmx", NULL};
+ _set_tst_kbds_key(initialKbds);
+
+ gchar* keyboards[] = {NULL};
+
+ // Execute
+ keyman_set_custom_keyboards(keyboards);
+
+ // Verify
+ g_auto(GStrv) result = _get_tst_kbds_key();
+ g_assert_nonnull(result);
+ _kmn_assert_cmpstrv(result, keyboards);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+void
+test_keyman_set_custom_keyboards__invalid_values() {
+ // Initialize
+ _delete_tst_kbds_key();
+ gchar* keyboards[] = {"invalid", "fr:/tmp/test/test.kmx", "", ":/tmp/bla.kmx", "fr:", NULL};
+
+ // Execute
+ keyman_set_custom_keyboards(keyboards);
+
+ // Verify
+ g_auto(GStrv) result = _get_tst_kbds_key();
+ g_assert_nonnull(result);
+ gchar* expected[] = {"fr:/tmp/test/test.kmx", NULL};
+ _kmn_assert_cmpstrv(result, expected);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+//----------------------------------------------------------------------------------------------
+void
+test_keyman_get_custom_keyboards__value() {
+ // Initialize
+ gchar* keyboards[] = {"fr:/tmp/test/test.kmx", NULL};
+ _set_tst_kbds_key(keyboards);
+
+ // Execute
+ g_auto(GStrv) result = keyman_get_custom_keyboards();
+
+ // Verify
+ g_assert_nonnull(result);
+ _kmn_assert_cmpstrv(result, keyboards);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+void
+test_keyman_get_custom_keyboards__invalid_values() {
+ // Initialize
+ gchar* keyboards[] = {"invalid", "fr:/tmp/test/test.kmx", "", NULL};
+ _set_tst_kbds_key(keyboards);
+
+ // Execute
+ g_auto(GStrv) result = keyman_get_custom_keyboards();
+
+ // Verify
+ gchar* expected[] = {"fr:/tmp/test/test.kmx", NULL};
+ g_assert_nonnull(result);
+ _kmn_assert_cmpstrv(result, expected);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+void
+test_keyman_get_custom_keyboards__no_key() {
+ // Initialize
+ _delete_tst_kbds_key();
+
+ // Execute
+ g_auto(GStrv) result = keyman_get_custom_keyboards();
+
+ // Verify
+ g_assert_null(result);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
+void
+test_keyman_get_custom_keyboards__empty() {
+ // Initialize
+ gchar* keyboards[] = {NULL};
+ _set_tst_kbds_key(keyboards);
+
+ // Execute
+ g_auto(GStrv) result = keyman_get_custom_keyboards();
+
+ // Verify
+ g_assert_null(result);
+
+ // Cleanup
+ _delete_tst_kbds_key();
+}
+
//----------------------------------------------------------------------------------------------
void
test_ibus_keyman_engine_desc_new__all_set() {
@@ -254,12 +530,13 @@ void
test_get_engine_for_language__null_language() {
// Initialize
gchar* languages[] = {"en:English", NULL};
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com");
g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT");
+ kmp_language lang = {NULL, NULL};
// Execute
- g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", NULL, NULL);
+ g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", &lang);
// Verify
g_assert_null(desc);
@@ -269,12 +546,13 @@ void
test_get_engine_for_language__empty_language() {
// Initialize
gchar* languages[] = {"en:English", NULL};
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com");
g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT");
+ kmp_language lang = {"", ""};
// Execute
- g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "", "");
+ g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", &lang);
// Verify
g_assert_null(desc);
@@ -284,12 +562,13 @@ void
test_get_engine_for_language__one_language() {
// Initialize
gchar* languages[] = {"en:English", NULL};
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com");
g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT");
+ kmp_language lang = {"English", "en"};
// Execute
- g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "en", "English");
+ g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", &lang);
// Verify
g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx");
@@ -301,12 +580,13 @@ void
test_get_engine_for_language__one_unknown_language() {
// Initialize
gchar* languages[] = {"foo:Foo", NULL};
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com");
g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT");
+ kmp_language lang = {"Foo", "foo"};
// Execute
- g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "foo", "Foo");
+ g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", &lang);
// Verify
g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "foo:/tmp/tst.kmx");
@@ -318,12 +598,13 @@ void
test_get_engine_for_language__different_languages() {
// Initialize
gchar* languages[] = {"en:English", NULL};
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com");
g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT");
+ kmp_language lang = {"Foo", "foo"};
// Execute
- g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "foo", "Foo");
+ g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", &lang);
// Verify
g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "foo:/tmp/tst.kmx");
@@ -335,12 +616,13 @@ void
test_get_engine_for_language__no_kbd_language() {
// Initialize
gchar* languages[] = {NULL};
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
g_autoptr(kmp_info) info = _get_tst_kmp_info("Copyright by me", "My Author", "myauthor@example.com");
g_autoptr(keyboard_details) details = _get_tst_keyboard_details("my description", "MIT");
+ kmp_language lang = {"English", "en"};
// Execute
- g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", "en", "English");
+ g_autoptr(IBusEngineDesc) desc = get_engine_for_language(keyboard, info, details, "/tmp", &lang);
// Verify
g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx");
@@ -348,12 +630,15 @@ test_get_engine_for_language__no_kbd_language() {
g_assert_cmpstr(ibus_engine_desc_get_language(desc), ==, "en");
}
+extern GHashTable* custom_keyboards;
+
//----------------------------------------------------------------------------------------------
void
test_keyman_add_keyboard__no_language() {
// Initialize
- gchar* languages[] = {NULL};
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages);
+ gchar* languages[] = {NULL};
+ custom_keyboards = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data();
// Execute
@@ -369,8 +654,9 @@ test_keyman_add_keyboard__no_language() {
void
test_keyman_add_keyboard__one_language() {
// Initialize
- gchar* languages[] = {"en:English", NULL};
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages);
+ gchar* languages[] = {"en:English", NULL};
+ custom_keyboards = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data();
// Execute
@@ -386,8 +672,9 @@ test_keyman_add_keyboard__one_language() {
void
test_keyman_add_keyboard__two_languages() {
// Initialize
- gchar* languages[] = {"en:English", "fr:French", NULL};
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages);
+ gchar* languages[] = {"en:English", "fr:French", NULL};
+ custom_keyboards = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data();
// Execute
@@ -404,11 +691,82 @@ test_keyman_add_keyboard__two_languages() {
g_assert_null(kb_data->engines_list->next->next);
}
+void
+test_keyman_add_keyboard__one_language_plus_custom() {
+ // Initialize
+ gchar* languages[] = {"en:English", NULL};
+ custom_keyboards = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
+ g_hash_table_insert(custom_keyboards, g_strdup("/tmp/tst.kmx"), g_ptr_array_new_full(1, g_free));
+ GPtrArray* language_keyboards = g_hash_table_lookup(custom_keyboards, "/tmp/tst.kmx");
+ cust_kbd* data = g_new0(cust_kbd, 1);
+ data->kb_id_with_lang = g_strdup("ldb:/tmp/tst.kmx");
+ data->lang = g_new0(kmp_language, 1);
+ data->lang->id = g_strdup("ldb");
+ g_ptr_array_add(language_keyboards, data);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
+ g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data();
+
+ // Execute
+ keyman_add_keyboard(keyboard, kb_data);
+
+ // Verify
+ g_assert_nonnull(kb_data->engines_list->data);
+ IBusEngineDesc* desc = (IBusEngineDesc*)kb_data->engines_list->data;
+ g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx");
+ g_assert_nonnull(kb_data->engines_list->next);
+ g_assert_nonnull(kb_data->engines_list->next->data);
+ desc = (IBusEngineDesc*)kb_data->engines_list->next->data;
+ g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "ldb:/tmp/tst.kmx");
+ g_assert_null(kb_data->engines_list->next->next);
+}
+
+void
+test_keyman_add_keyboard__one_language_plus_two_custom() {
+ // Initialize
+ gchar* languages[] = {"en:English", NULL};
+ custom_keyboards = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
+ g_hash_table_insert(custom_keyboards, g_strdup("/tmp/tst.kmx"), g_ptr_array_new_full(1, g_free));
+ GPtrArray* language_keyboards = g_hash_table_lookup(custom_keyboards, "/tmp/tst.kmx");
+ cust_kbd* data = g_new0(cust_kbd, 1);
+ data->kb_id_with_lang = g_strdup("ldb:/tmp/tst.kmx");
+ data->lang = g_new0(kmp_language, 1);
+ data->lang->id = g_strdup("ldb");
+ g_ptr_array_add(language_keyboards, data);
+ data = g_new0(cust_kbd, 1);
+ data->kb_id_with_lang = g_strdup("lda:/tmp/tst.kmx");
+ data->lang = g_new0(kmp_language, 1);
+ data->lang->id = g_strdup("lda");
+ g_ptr_array_add(language_keyboards, data);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
+ g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data();
+
+ // Execute
+ keyman_add_keyboard(keyboard, kb_data);
+
+ // Verify
+ GList* list = kb_data->engines_list;
+ g_assert_nonnull(list);
+ IBusEngineDesc* desc = (IBusEngineDesc*)list->data;
+ g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "en:/tmp/tst.kmx");
+ g_assert_nonnull(list->next);
+ list = list->next;
+ g_assert_nonnull(list->data);
+ desc = (IBusEngineDesc*)list->data;
+ g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "ldb:/tmp/tst.kmx");
+ g_assert_nonnull(list->next);
+ list = list->next;
+ g_assert_nonnull(list->data);
+ desc = (IBusEngineDesc*)list->data;
+ g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, "lda:/tmp/tst.kmx");
+ g_assert_null(list->next);
+}
+
void
test_keyman_add_keyboard__prev_engine_adding_same_version() {
// Initialize
- gchar* languages[] = {"en:English", NULL};
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages);
+ gchar* languages[] = {"en:English", NULL};
+ custom_keyboards = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.0", languages, "tst");
g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data();
IBusEngineDesc* desc = ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.0");
kb_data->engines_list = g_list_append(kb_data->engines_list, desc);
@@ -426,8 +784,9 @@ test_keyman_add_keyboard__prev_engine_adding_same_version() {
void
test_keyman_add_keyboard__prev_engine_adding_newer_version() {
// Initialize
- gchar* languages[] = {"en:English", "fr:French", NULL}; // New version adds French
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.1", languages);
+ gchar* languages[] = {"en:English", "fr:French", NULL}; // New version adds French
+ custom_keyboards = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.1", languages, "tst");
g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data();
IBusEngineDesc* desc =
ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.0");
@@ -457,8 +816,9 @@ test_keyman_add_keyboard__prev_engine_adding_newer_version_9593() {
// the list.
// Initialize
- gchar* languages[] = {"en:English", "fr:French", NULL}; // New version adds French
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.10", languages);
+ gchar* languages[] = {"en:English", "fr:French", NULL}; // New version adds French
+ custom_keyboards = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("1.10", languages, "tst");
g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data();
IBusEngineDesc* desc =
ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.9");
@@ -485,8 +845,9 @@ test_keyman_add_keyboard__prev_engine_adding_newer_version_9593() {
void
test_keyman_add_keyboard__prev_engine_adding_older_version() {
// Initialize
- gchar* languages[] = {"en:English", "fr:French", NULL}; // Old version has additional French
- g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("0.9", languages);
+ gchar* languages[] = {"en:English", "fr:French", NULL}; // Old version has additional French
+ custom_keyboards = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
+ g_autoptr(kmp_keyboard) keyboard = _get_tst_kmp_keyboard("0.9", languages, "tst");
g_autoptr(add_keyboard_data) kb_data = _get_tst_keyboard_data();
IBusEngineDesc* desc =
ibus_keyman_engine_desc_new("en:/usr/share/keyman/tst.kmx", "Testing", NULL, NULL, "en", NULL, NULL, "", "us", "1.0");
@@ -621,6 +982,46 @@ test_keyman_add_keyboards_from_dir__prev_keyboards() {
g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name);
}
+void
+test_keyman_add_keyboards_from_dir__one_language_plus_two_different_custom() {
+ // Initialize
+ g_autoptr(GList) keyboards = NULL;
+ g_autoptr(gchar) kmp_dir = g_dir_make_tmp(NULL, NULL);
+ copy_test_data(kmp_dir, "kmp1.json");
+
+ custom_keyboards = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_ptr_array_unref);
+ gchar* kmx_path = g_strdup_printf("%s/test1.kmx", kmp_dir);
+ g_hash_table_insert(custom_keyboards, g_strdup(kmx_path), g_ptr_array_new_full(1, g_free));
+ GPtrArray* language_keyboards = g_hash_table_lookup(custom_keyboards, kmx_path);
+ cust_kbd* data = g_new0(cust_kbd, 1);
+ data->kb_id_with_lang = g_strdup_printf("ldb:%s", kmx_path);
+ data->lang = g_new0(kmp_language, 1);
+ data->lang->id = g_strdup("ldb");
+ g_ptr_array_add(language_keyboards, data);
+
+ // This custom keyboard will be ignored since we don't have this keyboard installed
+ g_hash_table_insert(custom_keyboards, g_strdup("/tmp/foo.kmx"), g_ptr_array_new_full(1, g_free));
+ language_keyboards = g_hash_table_lookup(custom_keyboards, "/tmp/foo.kmx");
+ data = g_new0(cust_kbd, 1);
+ data->kb_id_with_lang = g_strdup("lda:/tmp/foo.kmx");
+ data->lang = g_new0(kmp_language, 1);
+ data->lang->id = g_strdup("foo");
+ g_ptr_array_add(language_keyboards, data);
+
+ // Execute
+ keyman_add_keyboards_from_dir(kmp_dir, &keyboards);
+
+ // Verify
+ g_assert_cmpint(g_list_length(keyboards), ==, 2);
+
+ IBusEngineDesc* desc = IBUS_ENGINE_DESC(keyboards->data);
+ g_autofree gchar* expected_name = g_strdup_printf("bza:%s/test1.kmx", kmp_dir);
+ g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name);
+ desc = IBUS_ENGINE_DESC(keyboards->next->data);
+ expected_name = g_strdup_printf("ldb:%s/test1.kmx", kmp_dir);
+ g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name);
+}
+
//----------------------------------------------------------------------------------------------
void
print_usage() {
@@ -655,6 +1056,24 @@ int main(int argc, char* argv[]) {
g_test_add_func("/keymanutil/keyman_put_options_todconf/other_keys", test_keyman_put_options_todconf__other_keys);
g_test_add_func("/keymanutil/keyman_put_options_todconf/existing_key", test_keyman_put_options_todconf__existing_key);
+ g_test_add_func("/keymanutil/keyman_get_custom_keyboard_dictionary/values", test_keyman_get_custom_keyboard_dictionary__values);
+ g_test_add_func("/keymanutil/keyman_get_custom_keyboard_dictionary/invalid", test_keyman_get_custom_keyboard_dictionary__invalid);
+ g_test_add_func("/keymanutil/keyman_get_custom_keyboard_dictionary/empty", test_keyman_get_custom_keyboard_dictionary__empty);
+ g_test_add_func("/keymanutil/keyman_get_custom_keyboard_dictionary/null", test_keyman_get_custom_keyboard_dictionary__null);
+
+ g_test_add_func("/keymanutil/keyman_set_custom_keyboards/new_key", test_keyman_set_custom_keyboards__new_key);
+ g_test_add_func("/keymanutil/keyman_set_custom_keyboards/overwrite_key", test_keyman_set_custom_keyboards__overwrite_key);
+ g_test_add_func("/keymanutil/keyman_set_custom_keyboards/delete_key_NULL", test_keyman_set_custom_keyboards__delete_key_NULL);
+ g_test_add_func(
+ "/keymanutil/keyman_set_custom_keyboards/delete_key_empty_array",
+ test_keyman_set_custom_keyboards__delete_key_empty_array);
+ g_test_add_func("/keymanutil/keyman_set_custom_keyboards/invalid_values", test_keyman_set_custom_keyboards__invalid_values);
+
+ g_test_add_func("/keymanutil/keyman_get_custom_keyboards/value", test_keyman_get_custom_keyboards__value);
+ g_test_add_func("/keymanutil/keyman_get_custom_keyboards/invalid_values", test_keyman_get_custom_keyboards__invalid_values);
+ g_test_add_func("/keymanutil/keyman_get_custom_keyboards/no_key", test_keyman_get_custom_keyboards__no_key);
+ g_test_add_func("/keymanutil/keyman_get_custom_keyboards/empty", test_keyman_get_custom_keyboards__empty);
+
g_test_add_func("/keymanutil/ibus_keyman_engine_desc_new/all_set", test_ibus_keyman_engine_desc_new__all_set);
g_test_add_func("/keymanutil/ibus_keyman_engine_desc_new/only_description", test_ibus_keyman_engine_desc_new__only_description);
g_test_add_func("/keymanutil/ibus_keyman_engine_desc_new/only_copyright", test_ibus_keyman_engine_desc_new__only_copyright);
@@ -672,6 +1091,8 @@ int main(int argc, char* argv[]) {
g_test_add_func("/keymanutil/keyman_add_keyboard/no_language", test_keyman_add_keyboard__no_language);
g_test_add_func("/keymanutil/keyman_add_keyboard/one_language", test_keyman_add_keyboard__one_language);
g_test_add_func("/keymanutil/keyman_add_keyboard/two_languages", test_keyman_add_keyboard__two_languages);
+ g_test_add_func("/keymanutil/keyman_add_keyboard/one_language_plus_custom", test_keyman_add_keyboard__one_language_plus_custom);
+ g_test_add_func("/keymanutil/keyman_add_keyboard/one_language_plus_two_custom", test_keyman_add_keyboard__one_language_plus_two_custom);
g_test_add_func(
"/keymanutil/keyman_add_keyboard/prev_engine_adding_same_version",
test_keyman_add_keyboard__prev_engine_adding_same_version);
@@ -690,6 +1111,9 @@ int main(int argc, char* argv[]) {
g_test_add_func("/keymanutil/keyman_add_keyboards_from_dir/one_dir", test_keyman_add_keyboards_from_dir__one_dir);
g_test_add_func("/keymanutil/keyman_add_keyboards_from_dir/two_langs", test_keyman_add_keyboards_from_dir__two_langs);
g_test_add_func("/keymanutil/keyman_add_keyboards_from_dir/prev_keyboards", test_keyman_add_keyboards_from_dir__prev_keyboards);
+ g_test_add_func(
+ "/keymanutil/keyman_add_keyboards_from_dir/one_language_plus_two_different_custom",
+ test_keyman_add_keyboards_from_dir__one_language_plus_two_different_custom);
// Run tests
int retVal = g_test_run();
diff --git a/linux/ibus-keyman/src/test/meson.build b/linux/ibus-keyman/src/test/meson.build
index 02631e27f7..333f05c212 100644
--- a/linux/ibus-keyman/src/test/meson.build
+++ b/linux/ibus-keyman/src/test/meson.build
@@ -3,7 +3,7 @@ keymanutil_sources = [
util_files,
]
-keymanutil_deps = [gtk, ibus, icu, json_glib, kmnkbp_lib]
+keymanutil_deps = [gtk, ibus, icu, json_glib, keymancore_lib]
test_env = [
'G_TEST_SRCDIR=' + meson.current_source_dir(),
diff --git a/linux/ibus-keyman/tests/meson.build b/linux/ibus-keyman/tests/meson.build
index 1637fbc9bc..73ce8d84d0 100644
--- a/linux/ibus-keyman/tests/meson.build
+++ b/linux/ibus-keyman/tests/meson.build
@@ -5,12 +5,12 @@ test_files = [
'../src/KeymanSystemServiceClient.cpp',
]
-kmnkbp_tests_lib = cc.find_library(
- 'kmnkbp-tests', # meson will prefix 'lib'
+keymancore_tests_lib = cc.find_library(
+ 'keymancore-tests', # meson will prefix 'lib'
dirs: [ core_dir / 'build/arch' / get_option('buildtype') / 'tests/kmx_test_source' ]
)
-test_deps = [gtk, ibus, icu, json_glib, kmnkbp_lib, kmnkbp_tests_lib, systemd]
+test_deps = [gtk, ibus, icu, json_glib, keymancore_lib, keymancore_tests_lib, systemd]
dbus_deps = [gtk, systemd]
test_env = [
diff --git a/linux/ibus-keyman/tests/testfixture.cpp b/linux/ibus-keyman/tests/testfixture.cpp
index cfc1530a75..8ea476d916 100644
--- a/linux/ibus-keyman/tests/testfixture.cpp
+++ b/linux/ibus-keyman/tests/testfixture.cpp
@@ -243,8 +243,8 @@ get_context_keys(std::u16string context) {
G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, "We can only deal with ASCII characters in the context", utf8, "", "");
}
result.push_back(
- {km::kbp::kmx::s_char_to_vkey[(int)utf8[0] - 32].vk,
- (uint16_t)(km::kbp::kmx::s_char_to_vkey[(int)utf8[0] - 32].shifted ? KM_CORE_MODIFIER_SHIFT : 0)});
+ {km::core::kmx::s_char_to_vkey[(int)utf8[0] - 32].vk,
+ (uint16_t)(km::core::kmx::s_char_to_vkey[(int)utf8[0] - 32].shifted ? KM_CORE_MODIFIER_SHIFT : 0)});
}
return result;
}
diff --git a/linux/keyman-config/build.sh b/linux/keyman-config/build.sh
index 5a01ead0da..9c0588f796 100755
--- a/linux/keyman-config/build.sh
+++ b/linux/keyman-config/build.sh
@@ -16,7 +16,9 @@ builder_describe \
"test" \
"install install artifacts" \
"uninstall uninstall artifacts" \
- "--no-integration don't run integration tests"
+ "--no-integration don't run integration tests" \
+ "--report create coverage report" \
+ "--coverage capture test coverage"
builder_parse "$@"
@@ -44,7 +46,7 @@ execute_with_temp_schema() {
TEMP_DATA_DIR=$(mktemp -d)
SCHEMA_DIR="${TEMP_DATA_DIR}/glib-2.0/schemas"
export XDG_DATA_DIRS="${TEMP_DATA_DIR}":${XDG_DATA_DIRS-}
- export GSETTINGS_SCHEMA_DIR="${SCHEMA_DIR}"
+ export GSETTINGS_SCHEMA_DIR="${SCHEMA_DIR}:/usr/share/glib-2.0/schemas/:${GSETTINGS_SCHEMA_DIR-}"
mkdir -p "${SCHEMA_DIR}"
cp resources/com.keyman.gschema.xml "${SCHEMA_DIR}"/
glib-compile-schemas "${SCHEMA_DIR}"
@@ -87,7 +89,19 @@ build_action() {
}
test_action() {
- execute_with_temp_schema ./run-tests.sh
+ local options
+
+ if builder_has_option --coverage; then
+ options="--coverage"
+ else
+ options=""
+ fi
+ execute_with_temp_schema ./run-tests.sh "${options}"
+
+ if builder_has_option --report; then
+ builder_echo "Creating coverage report"
+ python3 -m coverage html --directory="$THIS_SCRIPT_PATH/build/coveragereport/" --data-file=build/.coverage
+ fi
}
install_action() {
diff --git a/linux/keyman-config/keyman_config/custom_keyboards.py b/linux/keyman-config/keyman_config/custom_keyboards.py
new file mode 100644
index 0000000000..2fdfa14de0
--- /dev/null
+++ b/linux/keyman-config/keyman_config/custom_keyboards.py
@@ -0,0 +1,52 @@
+#!/usr/bin/python3
+
+from keyman_config.gsettings import GSettings
+
+
+GSETTINGS_ENGINE_BASE = 'com.keyman.engine'
+GSETTINGS_ADDITIONAL_KEYBOARDS_KEY = 'additional-keyboards'
+
+class CustomKeyboards():
+ def __init__(self):
+ self.gsettings = GSettings(GSETTINGS_ENGINE_BASE)
+
+ def add(self, keyboard):
+ if not keyboard:
+ return
+
+ custom_keyboards = self.gsettings.get(GSETTINGS_ADDITIONAL_KEYBOARDS_KEY)
+ if isinstance(custom_keyboards, type([])):
+ duplicate = False
+ i = 0
+ while i < len(custom_keyboards):
+ kb = custom_keyboards[i]
+ if kb == keyboard:
+ duplicate = True
+ elif not len(kb.split(':')) > 1:
+ custom_keyboards.remove(kb)
+ i -= 1
+ i += 1
+ if duplicate:
+ return
+ custom_keyboards.append(keyboard)
+ else:
+ custom_keyboards = [keyboard]
+ self.gsettings.set('additional-keyboards', custom_keyboards, 'as')
+
+ def remove(self, keyboard_path):
+ if not keyboard_path:
+ return
+
+ custom_keyboards = self.gsettings.get(GSETTINGS_ADDITIONAL_KEYBOARDS_KEY)
+ if isinstance(custom_keyboards, type([])):
+ i = 0
+ while i < len(custom_keyboards):
+ kb = custom_keyboards[i]
+ if kb.endswith(keyboard_path) or not len(kb.split(':')) > 1:
+ custom_keyboards.remove(kb)
+ else:
+ i += 1
+ else:
+ custom_keyboards = []
+
+ self.gsettings.set('additional-keyboards', custom_keyboards, 'as')
diff --git a/linux/keyman-config/keyman_config/gsettings.py b/linux/keyman-config/keyman_config/gsettings.py
index cdba58e446..e0b0726b6f 100644
--- a/linux/keyman-config/keyman_config/gsettings.py
+++ b/linux/keyman-config/keyman_config/gsettings.py
@@ -93,3 +93,4 @@ class GSettings():
else:
variant = self._convert_array_to_variant(value, type)
self.schema.set_value(key, variant)
+ self.schema.sync()
diff --git a/linux/keyman-config/keyman_config/install_kmp.py b/linux/keyman-config/keyman_config/install_kmp.py
index 95d1e294e0..f9a0db5d0f 100755
--- a/linux/keyman-config/keyman_config/install_kmp.py
+++ b/linux/keyman-config/keyman_config/install_kmp.py
@@ -11,6 +11,7 @@ from shutil import rmtree
from keyman_config import _, __version__, secure_lookup
from keyman_config.canonical_language_code_utils import CanonicalLanguageCodeUtils
from keyman_config.convertico import checkandsaveico, extractico
+from keyman_config.custom_keyboards import CustomKeyboards
from keyman_config.dbus_util import get_keyman_config_service
from keyman_config.fcitx_util import is_fcitx_running, restart_fcitx
from keyman_config.get_kmp import (InstallLocation, get_keyboard_data,
@@ -19,6 +20,7 @@ from keyman_config.get_kmp import (InstallLocation, get_keyboard_data,
from keyman_config.gnome_keyboards_util import (GnomeKeyboardsUtil,
get_ibus_keyboard_id,
is_gnome_shell)
+from keyman_config.gsettings import GSettings
from keyman_config.ibus_util import get_ibus_bus, install_to_ibus, restart_ibus
from keyman_config.kmpmetadata import KMFileTypes, get_metadata
from keyman_config.kvk2ldml import convert_kvk_to_ldml, output_ldml
@@ -114,39 +116,32 @@ class InstallKmp():
extract_kmp(inputfile, self.packageDir)
- if is_fcitx_running():
- restart_fcitx()
- else:
- restart_ibus()
-
info, system, options, keyboards, files = get_metadata(self.packageDir)
self._check_version(inputfile, system)
- if keyboards:
- # sourcery skip: extract-method
- logging.info("Installing %s", secure_lookup(info, 'name', 'description'))
- process_keyboard_data(self.packageID, self.packageDir)
- for kb in keyboards:
- if kb['id'] != self.packageID:
- process_keyboard_data(kb['id'], self.packageDir)
-
- if files is None:
- return self.install_keyboards(keyboards, self.packageDir, language)
-
- self._install_files(keyboards, files)
-
- return self.install_keyboards(keyboards, self.packageDir, language)
- else:
+ if not keyboards:
logging.error("install_kmp.py: error: No kmp.json or kmp.inf found in %s", inputfile)
logging.info("Contents of %s:", inputfile)
for o in os.listdir(self.packageDir):
logging.info(o)
rmtree(self.packageDir)
message = _("No kmp.json or kmp.inf found in {packageFile}").format(
- packageFile=inputfile)
+ packageFile=inputfile)
raise InstallError(InstallStatus.Abort, message)
+ # sourcery skip: extract-method
+ logging.info("Installing %s", secure_lookup(info, 'name', 'description'))
+ process_keyboard_data(self.packageID, self.packageDir)
+ for kb in keyboards:
+ if kb['id'] != self.packageID:
+ process_keyboard_data(kb['id'], self.packageDir)
+
+ if files is not None:
+ self._install_files(keyboards, files)
+
+ return self._install_keyboards(keyboards, self.packageDir, language)
+
def _check_version(self, inputfile, system):
if not system:
return
@@ -245,11 +240,29 @@ class InstallKmp():
return tag
return None
- def install_keyboards(self, keyboards, packageDir, language=None):
+ def _add_custom_keyboard(self, keyboard, package_dir, requested_language):
+ if not requested_language:
+ return None
+ language = CanonicalLanguageCodeUtils.findBestTag(requested_language, False, False)
+ new_keyboard = get_ibus_keyboard_id(keyboard, package_dir, language)
+ CustomKeyboards().add(new_keyboard)
+ return language
+
+ def _install_keyboards(self, keyboards, packageDir, requested_language=None):
+ language = requested_language
+ # TODO: add other keyboards as well (#9757)
firstKeyboard = keyboards[0]
- if secure_lookup(firstKeyboard, 'languages') and len(firstKeyboard['languages']) > 0:
+ if secure_lookup(firstKeyboard, 'languages') and firstKeyboard['languages']:
language = self._normalize_language(firstKeyboard['languages'], language)
+ if not language:
+ language = self._add_custom_keyboard(firstKeyboard, packageDir, requested_language)
+
+ if is_fcitx_running():
+ restart_fcitx()
+ else:
+ restart_ibus()
+
if is_fcitx_running():
return self._install_keyboards_to_fcitx()
elif is_gnome_shell():
diff --git a/linux/keyman-config/keyman_config/sentry_handling.py b/linux/keyman-config/keyman_config/sentry_handling.py
index 2013468589..7585f88e43 100644
--- a/linux/keyman-config/keyman_config/sentry_handling.py
+++ b/linux/keyman-config/keyman_config/sentry_handling.py
@@ -143,6 +143,11 @@ class SentryErrorHandling:
scope.set_tag('os.version', os_release['VERSION'])
except OSError as e:
logging.debug(f'System does not have os_release file: {e.strerror}')
+ except AttributeError:
+ logging.debug('System does not have platform.freedesktop_os_release() method')
+ except:
+ logging.debug(
+ 'Got exception trying to access platform.freedesktop_os_release() method or os_release information')
logging.info("Initialized Sentry error reporting")
def _raven_initialize(self):
diff --git a/linux/keyman-config/keyman_config/uninstall_kmp.py b/linux/keyman-config/keyman_config/uninstall_kmp.py
index 967a3ba7dc..58a529a259 100755
--- a/linux/keyman-config/keyman_config/uninstall_kmp.py
+++ b/linux/keyman-config/keyman_config/uninstall_kmp.py
@@ -6,6 +6,7 @@ import os
from shutil import rmtree
from keyman_config import _
+from keyman_config.custom_keyboards import CustomKeyboards
from keyman_config.dbus_util import get_keyman_config_service
from keyman_config.fcitx_util import is_fcitx_running
from keyman_config.get_kmp import (InstallLocation, get_keyboard_dir,
@@ -13,6 +14,7 @@ from keyman_config.get_kmp import (InstallLocation, get_keyboard_dir,
from keyman_config.gnome_keyboards_util import (GnomeKeyboardsUtil,
get_ibus_keyboard_id,
is_gnome_shell)
+from keyman_config.gsettings import GSettings
from keyman_config.ibus_util import IbusUtil, get_ibus_bus, restart_ibus
from keyman_config.kmpmetadata import get_metadata
@@ -42,7 +44,6 @@ def _uninstall_dir(what, dir):
else:
logging.info('No %s directory %s', what, dir)
-
def _uninstall_kmp_common(location, packageID, removeLanguages):
kbdir = get_keyboard_dir(location, packageID)
kbdocdir = get_keyman_doc_dir(location, packageID)
@@ -59,8 +60,16 @@ def _uninstall_kmp_common(location, packageID, removeLanguages):
_uninstall_keyboards_from_gnome(keyboards, kbdir)
else:
_uninstall_keyboards_from_ibus(keyboards, kbdir)
+
+ logging.debug('Removing custom keyboards:')
+ custom_keyboards = CustomKeyboards()
+ for kb in keyboards:
+ kb_path = os.path.join(kbdir, kb["id"] + ".kmx")
+ logging.debug(f' removing {kb["id"]} ({kb_path})')
+ custom_keyboards.remove(kb_path)
else:
logging.info(f'Could not uninstall {where} keyboard "{packageID}" from list of keyboards')
+
else:
logging.info(f'Replacing {where} keyboard: "{packageID}"')
diff --git a/linux/keyman-config/resources/com.keyman.gschema.xml b/linux/keyman-config/resources/com.keyman.gschema.xml
index df50060d8c..c286687f40 100644
--- a/linux/keyman-config/resources/com.keyman.gschema.xml
+++ b/linux/keyman-config/resources/com.keyman.gschema.xml
@@ -16,4 +16,13 @@
List of strings
+
+
+
+ []
+ User-added additional language/keyboards
+ This setting contains installed keyboards for bcp47 codes
+ that are not listed in the keyboard metadata.
+
+
diff --git a/linux/keyman-config/run-tests.sh b/linux/keyman-config/run-tests.sh
index db62cda374..de99c55e8f 100755
--- a/linux/keyman-config/run-tests.sh
+++ b/linux/keyman-config/run-tests.sh
@@ -8,13 +8,21 @@ if [ -f /usr/libexec/ibus-memconf ]; then
export GSETTINGS_BACKEND=keyfile
fi
-if [ -n "$TEAMCITY_VERSION" ]; then
- if ! pip3 list --format=columns | grep -q teamcity-messages; then
- pip3 install teamcity-messages
- fi
- python3 -m teamcity.unittestpy discover -s tests -p test_*.py
-else
- python3 -m unittest discover -v -s tests -p test_*.py
+if [ "$1" == "--coverage" ]; then
+ coverage="-m coverage run --source=. --data-file=build/.coverage"
fi
+if [ -n "$TEAMCITY_VERSION" ]; then
+ if ! pip3 list --format=columns | grep -q teamcity-messages; then
+ pip3 install teamcity-messages
+ fi
+ test_module=teamcity.unittestpy
+else
+ test_module=unittest
+ extra_opts=-v
+fi
+
+# shellcheck disable=SC2086
+python3 ${coverage:-} -m ${test_module:-} discover ${extra_opts:-} -s tests -p test_*.py
+
rm -rf "$XDG_CONFIG_HOME"
diff --git a/linux/keyman-config/tests/test_custom_keyboards.py b/linux/keyman-config/tests/test_custom_keyboards.py
new file mode 100644
index 0000000000..6b41475cf5
--- /dev/null
+++ b/linux/keyman-config/tests/test_custom_keyboards.py
@@ -0,0 +1,194 @@
+#!/usr/bin/python3
+import unittest
+from unittest.mock import patch
+from keyman_config import __version__
+from keyman_config.custom_keyboards import CustomKeyboards
+
+class AddCustomKeyboardTests(unittest.TestCase):
+ def setUp(self):
+ patcher1 = patch('keyman_config.custom_keyboards.GSettings')
+ self.mockGSettingsClass = patcher1.start()
+ self.addCleanup(patcher1.stop)
+
+ def test_Add_NoParam(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = None
+ # Execute
+ CustomKeyboards().add(None)
+ # Verify
+ mockGSettingsInstance.set.assert_not_called()
+
+ def test_Add_Unset(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = None
+ keyboard = 'mul:fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().add(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with('additional-keyboards', ['mul:fooDir/foo1.kmx'], 'as')
+
+ def test_Add_Invalid(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = 'foo'
+ keyboard = 'mul:fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().add(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with('additional-keyboards', ['mul:fooDir/foo1.kmx'], 'as')
+
+ def test_Add_InvalidArray(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = ['garbage', '', 'und:barDir/bar.kmx']
+ keyboard = 'mul:fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().add(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with(
+ 'additional-keyboards', ['und:barDir/bar.kmx', 'mul:fooDir/foo1.kmx'], 'as')
+
+ def test_Add_EmptyArray(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = []
+ keyboard = 'mul:fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().add(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with('additional-keyboards', ['mul:fooDir/foo1.kmx'], 'as')
+
+ def test_Add_EmptyString(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = ['', 'und:barDir/bar.kmx']
+ keyboard = 'mul:fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().add(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with(
+ 'additional-keyboards', ['und:barDir/bar.kmx', 'mul:fooDir/foo1.kmx'], 'as')
+
+ def test_Add_PrevVal(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = ['und:barDir/bar.kmx']
+ keyboard = 'mul:fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().add(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with(
+ 'additional-keyboards', ['und:barDir/bar.kmx', 'mul:fooDir/foo1.kmx'], 'as')
+
+ def test_Add_Duplicate(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = ['und:barDir/bar.kmx', 'mul:fooDir/foo1.kmx']
+ keyboard = 'mul:fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().add(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_not_called() # nothing changed, so no need to call
+
+
+class RemoveCustomKeyboardTests(unittest.TestCase):
+ def setUp(self):
+ patcher1 = patch('keyman_config.custom_keyboards.GSettings')
+ self.mockGSettingsClass = patcher1.start()
+ self.addCleanup(patcher1.stop)
+
+ def test_Remove_NoParam(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = None
+ # Execute
+ CustomKeyboards().remove(None)
+ # Verify
+ mockGSettingsInstance.set.assert_not_called()
+
+ def test_Remove_Unset(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = None
+ keyboard = 'fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().remove(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with('additional-keyboards', [], 'as')
+
+ def test_Remove_Invalid(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = 'foo'
+ keyboard = 'fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().remove(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with('additional-keyboards', [], 'as')
+
+ def test_Remove_InvalidArray(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = ['garbage', '', 'und:barDir/bar.kmx']
+ keyboard = 'fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().remove(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with(
+ 'additional-keyboards', ['und:barDir/bar.kmx'], 'as')
+
+ def test_Remove_EmptyArray(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = []
+ keyboard = 'fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().remove(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with('additional-keyboards', [], 'as')
+
+ def test_Remove_EmptyString(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = ['', 'und:barDir/bar.kmx']
+ keyboard = 'fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().remove(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with(
+ 'additional-keyboards', ['und:barDir/bar.kmx'], 'as')
+
+ def test_Remove_MultipleValues(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = ['und:barDir/bar.kmx', 'mul:fooDir/foo1.kmx']
+ keyboard = 'fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().remove(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with(
+ 'additional-keyboards', ['und:barDir/bar.kmx'], 'as')
+
+ def test_Remove_MultipleValuesWithSameKeyboard(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = ['und:barDir/bar.kmx', 'mul:fooDir/foo1.kmx', 'und:fooDir/foo1.kmx']
+ keyboard = 'fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().remove(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with(
+ 'additional-keyboards', ['und:barDir/bar.kmx'], 'as')
+
+ def test_Remove_SingleValue(self):
+ # Setup
+ mockGSettingsInstance = self.mockGSettingsClass.return_value
+ mockGSettingsInstance.get.return_value = ['mul:fooDir/foo1.kmx']
+ keyboard = 'fooDir/foo1.kmx'
+ # Execute
+ CustomKeyboards().remove(keyboard)
+ # Verify
+ mockGSettingsInstance.set.assert_called_once_with(
+ 'additional-keyboards', [], 'as')
diff --git a/linux/keyman-config/tests/test_ibus_util.py b/linux/keyman-config/tests/test_ibus_util.py
index eb51515f3f..29918994e3 100644
--- a/linux/keyman-config/tests/test_ibus_util.py
+++ b/linux/keyman-config/tests/test_ibus_util.py
@@ -6,9 +6,10 @@ from keyman_config.ibus_util import get_ibus_bus, install_to_ibus, uninstall_fro
@patch('keyman_config.ibus_util.IBus.Bus')
+@patch('time.sleep', return_value=None)
class IbusUtilTests(unittest.TestCase):
- def test_getIbusBus_NotConnected_ReturnsNone(self, MockIbusBusClass):
+ def test_getIbusBus_NotConnected_ReturnsNone(self, patched_time_sleep, MockIbusBusClass):
# Setup
mock_ibusBusInstance = MockIbusBusClass.return_value
mock_ibusBusInstance.is_connected.return_value = False
@@ -18,7 +19,7 @@ class IbusUtilTests(unittest.TestCase):
self.assertTrue(MockIbusBusClass.called, "IBus.Bus called")
self.assertTrue(mock_ibusBusInstance.destroy.called)
- def test_getIbusBus_GlobalEngineNotEnabled_ReturnsNone(self, MockIbusBusClass):
+ def test_getIbusBus_GlobalEngineNotEnabled_ReturnsNone(self, patched_time_sleep, MockIbusBusClass):
# Setup
mock_ibusBusInstance = MockIbusBusClass.return_value
mock_ibusBusInstance.is_connected.return_value = True
@@ -30,7 +31,7 @@ class IbusUtilTests(unittest.TestCase):
self.assertTrue(MockIbusBusClass.called, "IBus.Bus called")
self.assertTrue(mock_ibusBusInstance.destroy.called)
- def test_getIbusBus_ReturnsBus(self, MockIbusBusClass):
+ def test_getIbusBus_ReturnsBus(self, patched_time_sleep, MockIbusBusClass):
# Setup
mock_ibusBusInstance = MockIbusBusClass.return_value
# Execute/Verify
@@ -38,66 +39,54 @@ class IbusUtilTests(unittest.TestCase):
self.assertTrue(MockIbusBusClass.called, "IBus.Bus called")
self.assertFalse(mock_ibusBusInstance.destroy.called)
- @patch('keyman_config.gsettings.GSettings.get')
+ @patch('keyman_config.gsettings.GSettings.get', return_value=['k1', 'k2', 'k3'])
@patch('keyman_config.gsettings.GSettings.set')
- def test_installToIbus_AlreadyInstalled(self, MockSettingsSet, MockSettingsGet, MockIbusBusClass):
+ def test_installToIbus_AlreadyInstalled(self, patched_settings_set, patched_settings_get, patched_time_sleep, MockIbusBusClass):
# Setup
- mock_settingsGet = MockSettingsGet
- mock_settingsSet = MockSettingsSet
- mock_settingsGet.return_value = ['k1', 'k2', 'k3']
mock_ibusBusInstance = MockIbusBusClass.return_value
# Execute
install_to_ibus(mock_ibusBusInstance, 'k3')
# Verify
- mock_settingsSet.assert_called_once_with(
+ patched_settings_set.assert_called_once_with(
"preload-engines", ['k1', 'k2', 'k3'], 'as')
mock_ibusBusInstance.preload_engines.assert_called_once_with(
['k1', 'k2', 'k3'])
- @patch('keyman_config.gsettings.GSettings.get')
+ @patch('keyman_config.gsettings.GSettings.get', return_value=['k1', 'k2', 'k3'])
@patch('keyman_config.gsettings.GSettings.set')
- def test_installToIbus_InstallsNewKb(self, MockSettingsSet, MockSettingsGet, MockIbusBusClass):
+ def test_installToIbus_InstallsNewKb(self, patched_settings_set, patched_settings_get, patched_time_sleep, MockIbusBusClass):
# Setup
- mock_settingsGet = MockSettingsGet
- mock_settingsSet = MockSettingsSet
- mock_settingsGet.return_value = ['k1', 'k2', 'k3']
mock_ibusBusInstance = MockIbusBusClass.return_value
# Execute
install_to_ibus(mock_ibusBusInstance, 'k4')
# Verify
- mock_settingsSet.assert_called_once_with(
+ patched_settings_set.assert_called_once_with(
"preload-engines", ['k1', 'k2', 'k3', 'k4'], 'as')
mock_ibusBusInstance.preload_engines.assert_called_once_with(
['k1', 'k2', 'k3', 'k4'])
- @patch('keyman_config.gsettings.GSettings.get')
+ @patch('keyman_config.gsettings.GSettings.get', return_value=['k1', 'k2', 'k3'])
@patch('keyman_config.gsettings.GSettings.set')
- def test_uninstallFromIbus_KbInstalled(self, MockSettingsSet, MockSettingsGet, MockIbusBusClass):
+ def test_uninstallFromIbus_KbInstalled(self, patched_settings_set, patched_settings_get, patched_time_sleep, MockIbusBusClass):
# Setup
- mock_settingsGet = MockSettingsGet
- mock_settingsSet = MockSettingsSet
- mock_settingsGet.return_value = ['k1', 'k2', 'k3']
mock_ibusBusInstance = MockIbusBusClass.return_value
# Execute
uninstall_from_ibus(mock_ibusBusInstance, 'k2')
# Verify
- mock_settingsSet.assert_called_once_with(
+ patched_settings_set.assert_called_once_with(
"preload-engines", ['k1', 'k3'], 'as')
mock_ibusBusInstance.preload_engines.assert_called_once_with(
['k1', 'k3'])
- @patch('keyman_config.gsettings.GSettings.get')
+ @patch('keyman_config.gsettings.GSettings.get', return_value=['k1', 'k2', 'k3'])
@patch('keyman_config.gsettings.GSettings.set')
- def test_uninstallFromIbus_KbNotInstalled(self, MockSettingsSet, MockSettingsGet, MockIbusBusClass):
+ def test_uninstallFromIbus_KbNotInstalled(self, patched_settings_set, patched_settings_get, patched_time_sleep, MockIbusBusClass):
# Setup
- mock_settingsGet = MockSettingsGet
- mock_settingsSet = MockSettingsSet
- mock_settingsGet.return_value = ['k1', 'k2', 'k3']
mock_ibusBusInstance = MockIbusBusClass.return_value
# Execute
uninstall_from_ibus(mock_ibusBusInstance, 'k4')
# Verify
- mock_settingsSet.assert_called_once_with(
+ patched_settings_set.assert_called_once_with(
"preload-engines", ['k1', 'k2', 'k3'], 'as')
mock_ibusBusInstance.preload_engines.assert_called_once_with(
['k1', 'k2', 'k3'])
diff --git a/linux/keyman-config/tests/test_install_kmp.py b/linux/keyman-config/tests/test_install_kmp.py
index 3320c532b5..32b87f53a5 100644
--- a/linux/keyman-config/tests/test_install_kmp.py
+++ b/linux/keyman-config/tests/test_install_kmp.py
@@ -8,7 +8,7 @@ from keyman_config.get_kmp import InstallLocation
from keyman_config.install_kmp import InstallError, InstallKmp
-class InstallKmpTests(unittest.TestCase):
+class InstallKmpBase(unittest.TestCase):
def setUp(self):
patcher1 = patch('keyman_config.install_kmp.install_to_ibus')
@@ -40,7 +40,12 @@ class InstallKmpTests(unittest.TestCase):
self.mockIsFcitxRunning = patcher9.start()
self.addCleanup(patcher9.stop)
self.mockIsFcitxRunning.return_value = False
+ patcher10 = patch('keyman_config.install_kmp.CustomKeyboards')
+ self.mockCustomKeyboardsClass = patcher10.start()
+ self.addCleanup(patcher10.stop)
+
+class InstallKeyboardsToIbusTests(InstallKmpBase):
def test_InstallKeyboardsToIbus_NoIbus(self):
# Setup
self.mockGetIbusBus.return_value = None
@@ -122,6 +127,8 @@ class InstallKmpTests(unittest.TestCase):
self.mockRestartIbus.assert_called_once()
bus.destroy.assert_called_once()
+
+class InstallKeyboardsToGnomeTests(InstallKmpBase):
def test_InstallKeyboardsToGnome_SingleKbNoLanguages(self):
# Setup
mockGnomeKeyboardsUtilInstance = self.mockGnomeKeyboardsUtilClass.return_value
@@ -194,6 +201,8 @@ class InstallKmpTests(unittest.TestCase):
[('xkb', 'en'), ('ibus', 'de:fooDir/foo1.kmx')])
self.mockRestartIbus.assert_not_called()
+
+class NormalizeLanguageTests(InstallKmpBase):
def test_normalizeLanguage(self):
# Setup
languages = [
@@ -233,6 +242,8 @@ class InstallKmpTests(unittest.TestCase):
# Verify
self.assertEqual(result, '')
+
+class InstallKmpTests(InstallKmpBase):
def _createEmptyKmp(self, workdir):
kmpfile = os.path.join(workdir, 'foo.kmp')
with open(kmpfile, 'w') as file:
@@ -298,8 +309,12 @@ class InstallKmpTests(unittest.TestCase):
kmpfile = self._createEmptyKmp(workdir.name)
self._createKmpJson(packagedir.name, testcase['fileVersion'])
- # Execute
- InstallKmp()._install_kmp(kmpfile, 'km', InstallLocation.User)
+ with patch('keyman_config.install_kmp.process_keyboard_data') as mock:
+ method = mock.return_value
+ method.return_value = None
+
+ # Execute
+ InstallKmp()._install_kmp(kmpfile, 'km', InstallLocation.User)
# Verify
self.mockInstallToIbus.assert_called_once()
@@ -309,5 +324,58 @@ class InstallKmpTests(unittest.TestCase):
packagedir.cleanup()
-if __name__ == '__main__':
- unittest.main()
+class InstallKeyboardsTests(InstallKmpBase):
+ def test_InstallKeyboards_NoLang(self):
+ # Setup
+ mockCustomKeyboardsInstance = self.mockCustomKeyboardsClass.return_value
+ mockCustomKeyboardsInstance.get.return_value = None
+ keyboards = [{'id': 'foo1', 'languages': [{'id': 'en'}, {'id': 'fr'}]}]
+ # Execute
+ InstallKmp()._install_keyboards(keyboards, 'fooDir', None)
+ # Verify
+ self.mockInstallToIbus.assert_called_once_with(ANY, 'en:fooDir/foo1.kmx')
+ mockCustomKeyboardsInstance.add.assert_not_called()
+
+ def test_InstallKeyboards_LangInKeyboard(self):
+ # Setup
+ keyboards = [{'id': 'foo1', 'languages': [{'id': 'en'}, {'id': 'fr'}]}]
+ mockCustomKeyboardsInstance = self.mockCustomKeyboardsClass.return_value
+ mockCustomKeyboardsInstance.get.return_value = None
+ # Execute
+ InstallKmp()._install_keyboards(keyboards, 'fooDir', 'fr')
+ # Verify
+ self.mockInstallToIbus.assert_called_once_with(ANY, 'fr:fooDir/foo1.kmx')
+ mockCustomKeyboardsInstance.add.assert_not_called()
+
+ def test_InstallKeyboards_CustomLang(self):
+ # Setup
+ mockCustomKeyboardsInstance = self.mockCustomKeyboardsClass.return_value
+ mockCustomKeyboardsInstance.get.return_value = None
+ keyboards = [{'id': 'foo1', 'languages': [{'id': 'en'}, {'id': 'fr'}]}]
+ # Execute
+ InstallKmp()._install_keyboards(keyboards, 'fooDir', 'mul')
+ # Verify
+ self.mockInstallToIbus.assert_called_once_with(ANY, 'mul:fooDir/foo1.kmx')
+ mockCustomKeyboardsInstance.add.assert_called_once_with('mul:fooDir/foo1.kmx')
+
+
+class AddCustomKeyboardTests(InstallKmpBase):
+ def test_AddCustomKeyboard_NoLang(self):
+ # Setup
+ mockCustomKeyboardsInstance = self.mockCustomKeyboardsClass.return_value
+ keyboard = {'id': 'foo1'}
+ # Execute
+ language = InstallKmp()._add_custom_keyboard(keyboard, 'fooDir', None)
+ # Verify
+ self.assertEqual(language, None)
+ mockCustomKeyboardsInstance.add.assert_not_called()
+
+ def test_AddCustomKeyboard_Add(self):
+ # Setup
+ mockCustomKeyboardsInstance = self.mockCustomKeyboardsClass.return_value
+ keyboard = {'id': 'foo1'}
+ # Execute
+ language = InstallKmp()._add_custom_keyboard(keyboard, 'fooDir', 'mul')
+ # Verify
+ self.assertEqual(language, 'mul')
+ mockCustomKeyboardsInstance.add.assert_called_once_with('mul:fooDir/foo1.kmx')
diff --git a/linux/keyman-config/tests/test_uninstall_kmp.py b/linux/keyman-config/tests/test_uninstall_kmp.py
index 109b519768..76321d8993 100644
--- a/linux/keyman-config/tests/test_uninstall_kmp.py
+++ b/linux/keyman-config/tests/test_uninstall_kmp.py
@@ -1,8 +1,11 @@
#!/usr/bin/python3
+import os
+import tempfile
import unittest
from unittest.mock import patch
+from keyman_config.get_kmp import InstallLocation
-from keyman_config.uninstall_kmp import _uninstall_keyboards_from_gnome, _uninstall_keyboards_from_ibus
+from keyman_config.uninstall_kmp import _uninstall_keyboards_from_gnome, _uninstall_keyboards_from_ibus, _uninstall_kmp_common
class UninstallKmpGnomeTests(unittest.TestCase):
@@ -230,3 +233,88 @@ class UninstallKmpIbusTests(unittest.TestCase):
mockIbusUtilInstance.write_preload_engines.assert_called_once_with(
None,
['xkb:us::eng', 'km:fooDir/foo2.kmx'])
+
+
+class UninstallKmpCommonTests(unittest.TestCase):
+ def setUp(self):
+ patcher1 = patch('keyman_config.uninstall_kmp.get_keyboard_dir')
+ self.mockGetKeyboardDir = patcher1.start()
+ self.addCleanup(patcher1.stop)
+ patcher2 = patch('keyman_config.uninstall_kmp.get_keyman_doc_dir')
+ self.mockGetKeymanDocDir = patcher2.start()
+ self.addCleanup(patcher2.stop)
+ patcher3 = patch('keyman_config.uninstall_kmp.get_keyman_font_dir')
+ self.mockGetKeymanFontDir = patcher3.start()
+ self.addCleanup(patcher3.stop)
+ patcher4 = patch('keyman_config.uninstall_kmp.is_gnome_shell')
+ self.mockIsGnomeShell = patcher4.start()
+ self.addCleanup(patcher4.stop)
+ self.mockIsGnomeShell.return_value = False
+ patcher5 = patch('keyman_config.uninstall_kmp.is_fcitx_running')
+ self.mockIsFcitxRunning = patcher5.start()
+ self.addCleanup(patcher5.stop)
+ self.mockIsFcitxRunning.return_value = False
+ patcher6 = patch('keyman_config.uninstall_kmp._uninstall_keyboards_from_ibus')
+ self.mockUninstallKeyboardsFromIbus = patcher6.start()
+ self.addCleanup(patcher6.stop)
+ patcher7 = patch('keyman_config.uninstall_kmp._uninstall_dir')
+ self.mockUninstallDir = patcher7.start()
+ self.addCleanup(patcher7.stop)
+ patcher8 = patch('keyman_config.uninstall_kmp.get_metadata')
+ self.mockGetMetaData = patcher8.start()
+ self.addCleanup(patcher8.stop)
+ patcher9 = patch('keyman_config.uninstall_kmp.CustomKeyboards')
+ self.mockCustomKeyboardsClass = patcher9.start()
+ self.addCleanup(patcher9.stop)
+
+ def test_UninstallKmpCommon_NoRemoveLanguage(self):
+ # Setup
+ mockCustomKeyboardsInstance = self.mockCustomKeyboardsClass.return_value
+ self.mockGetKeyboardDir.return_value = '/tmp/foo'
+ keyboards = [{'id': 'foo1', 'languages': [{'id': 'en'}]}]
+ self.mockGetMetaData.return_value = (None, None, None, keyboards, None)
+ # Execute
+ _uninstall_kmp_common(InstallLocation.User, 'foo1', False)
+ # Verify
+ self.mockUninstallKeyboardsFromIbus.assert_not_called()
+ self.mockUninstallDir.assert_called()
+ mockCustomKeyboardsInstance.remove.assert_not_called()
+
+ def test_UninstallKmpCommon_RemoveLanguage(self):
+ # Setup
+ mockCustomKeyboardsInstance = self.mockCustomKeyboardsClass.return_value
+ self.mockGetKeyboardDir.return_value = '/tmp/foo'
+ keyboards = [{'id': 'foo1', 'languages': [{'id': 'en'}]}]
+ self.mockGetMetaData.return_value = (None, None, None, keyboards, None)
+ # Execute
+ _uninstall_kmp_common(InstallLocation.User, 'foo1', True)
+ # Verify
+ self.mockUninstallKeyboardsFromIbus.assert_called_once_with(keyboards, '/tmp/foo')
+ self.mockUninstallDir.assert_called()
+ mockCustomKeyboardsInstance.remove.assert_called_once_with('/tmp/foo/foo1.kmx')
+
+ def test_UninstallKmpCommon_RemoveLanguage_NoKeyboards(self):
+ # Setup
+ mockCustomKeyboardsInstance = self.mockCustomKeyboardsClass.return_value
+ self.mockGetKeyboardDir.return_value = '/tmp/foo'
+ keyboards = []
+ self.mockGetMetaData.return_value = (None, None, None, keyboards, None)
+ # Execute
+ _uninstall_kmp_common(InstallLocation.User, 'foo1', True)
+ # Verify
+ self.mockUninstallKeyboardsFromIbus.assert_not_called()
+ self.mockUninstallDir.assert_called()
+ mockCustomKeyboardsInstance.remove.assert_not_called()
+
+ def test_UninstallKmpCommon_RemoveLanguage_MultipleKeyboards(self):
+ # Setup
+ mockCustomKeyboardsInstance = self.mockCustomKeyboardsClass.return_value
+ self.mockGetKeyboardDir.return_value = '/tmp/foo'
+ keyboards = [{'id': 'foo1', 'languages': [{'id': 'en'}]}, {'id': 'bar2', 'languages': [{'id': 'fr'}]}]
+ self.mockGetMetaData.return_value = (None, None, None, keyboards, None)
+ # Execute
+ _uninstall_kmp_common(InstallLocation.User, 'foo1', True)
+ # Verify
+ self.mockUninstallKeyboardsFromIbus.assert_called_once_with(keyboards, '/tmp/foo')
+ self.mockUninstallDir.assert_called()
+ self.assertEquals(mockCustomKeyboardsInstance.remove.call_count, 2)
diff --git a/linux/keyman-system-service/build.sh b/linux/keyman-system-service/build.sh
index 9bca26421c..d9cb9785ee 100755
--- a/linux/keyman-system-service/build.sh
+++ b/linux/keyman-system-service/build.sh
@@ -17,7 +17,9 @@ builder_describe \
"test" \
"install install artifacts" \
"uninstall uninstall artifacts" \
- "--no-integration don't run integration tests"
+ "--no-integration don't run integration tests" \
+ "--report create coverage report" \
+ "--coverage capture test coverage"
builder_parse "$@"
@@ -33,35 +35,55 @@ else
fi
MESON_PATH="build/$(uname -m)/$MESON_TARGET"
+clean_action() {
+ rm -rf "$THIS_SCRIPT_PATH/build/"
+}
+
+check_missing_coverage_configuration() {
+ if builder_has_option --coverage && [ -d "$MESON_PATH" ]; then
+ # It's possible that we got configured because we're a dependency of ibus-keyman
+ # in which case the `--coverage` option wasn't passed along.
+ cd "$MESON_PATH"
+ if ! ninja -t targets | grep -q coverage-html ; then
+ cd "$THIS_SCRIPT_PATH"
+ clean_action
+ fi
+ cd "$THIS_SCRIPT_PATH"
+ fi
+}
+
+configure_action() {
+ # shellcheck disable=SC2086,SC2154
+ meson setup ${MESON_COVERAGE} --werror --buildtype $MESON_TARGET "${builder_extra_params[@]}" "$MESON_PATH"
+}
+
+test_action() {
+ # shellcheck disable=SC2086,SC2154
+ meson test --print-errorlogs $builder_verbose
+ if builder_has_option --coverage; then
+ # Note: requires lcov > 1.16 to properly work (see https://github.com/mesonbuild/meson/issues/6747)
+ ninja coverage-html
+ fi
+}
+
+check_missing_coverage_configuration
+
builder_describe_outputs \
configure "${MESON_PATH}/build.ninja" \
build "${MESON_PATH}/src/keyman-system-service"
-builder_run_action clean rm -rf "$THIS_SCRIPT_PATH/build/"
-
-# shellcheck disable=SC2086
-builder_run_action configure meson setup "$MESON_PATH" --werror --buildtype $MESON_TARGET "${builder_extra_params[@]}"
-
-if builder_start_action build; then
- cd "$MESON_PATH"
- ninja
- builder_finish_action success build
+if builder_has_option --coverage; then
+ MESON_COVERAGE=-Db_coverage=true
+else
+ MESON_COVERAGE=
fi
-if builder_start_action test; then
- cd "$MESON_PATH"
- meson test --print-errorlogs $builder_verbose
- builder_finish_action success test
-fi
+builder_run_action clean clean_action
+builder_run_action configure configure_action
-if builder_start_action install; then
- cd "$MESON_PATH"
- ninja install
- builder_finish_action success install
-fi
+[ -d "$MESON_PATH" ] && cd "$MESON_PATH"
-if builder_start_action uninstall; then
- cd "$MESON_PATH"
- ninja uninstall
- builder_finish_action success uninstall
-fi
+builder_run_action build ninja
+builder_run_action test test_action
+builder_run_action install ninja install
+builder_run_action uninstall ninja uninstall
diff --git a/linux/scripts/deb-packaging.sh b/linux/scripts/deb-packaging.sh
index 62fd389199..3a669ab9af 100755
--- a/linux/scripts/deb-packaging.sh
+++ b/linux/scripts/deb-packaging.sh
@@ -57,14 +57,17 @@ fi
if builder_start_action verify; then
tar xf "${SRC_PKG}"
- if [ ! -f debian/libkmnkbp0-0.symbols ]; then
- echo ":warning: Missing libkmnkbp0-0.symbols file" >&2
+ if [ ! -f debian/libkeymancore.symbols ]; then
+ echo ":warning: Missing libkeymancore.symbols file" >&2
else
+ PKG_NAME=libkeymancore
+ LIB_NAME=libkeymancore
+
tmpDir=$(mktemp -d)
dpkg -x "${BIN_PKG}" "$tmpDir"
cd debian
- dpkg-gensymbols -v"${PKG_VERSION}" -plibkmnkbp0-0 -e"${tmpDir}"/usr/lib/x86_64-linux-gnu/libkmnkbp0.so* -Olibkmnkbp0-0.symbols -c4
- echo ":heavy_check_mark: libkmnkbp0-0 API didn't change" >&2
+ dpkg-gensymbols -v"${PKG_VERSION}" -p${PKG_NAME} -e"${tmpDir}"/usr/lib/x86_64-linux-gnu/${LIB_NAME}.so* -O${PKG_NAME}.symbols -c4
+ echo ":heavy_check_mark: ${LIB_NAME} API didn't change" >&2
fi
builder_finish_action success verify
exit 0
diff --git a/linux/scripts/launchpad.sh b/linux/scripts/launchpad.sh
index 5aa2ad0bb6..7437272c03 100755
--- a/linux/scripts/launchpad.sh
+++ b/linux/scripts/launchpad.sh
@@ -33,8 +33,7 @@ else
fi
echo "ppa: ${ppa}"
-# distributions="${DIST:-focal jammy lunar mantic}"
-distributions="${DIST:-focal jammy lunar}"
+distributions="${DIST:-focal jammy lunar mantic}"
packageversion="${PACKAGEVERSION:-1~sil1}"
BASEDIR=$(pwd)
diff --git a/mac/Keyman4MacIM/Keyman4MacIM/Base.lproj/MainMenu.xib b/mac/Keyman4MacIM/Keyman4MacIM/Base.lproj/MainMenu.xib
index 4062663c00..0696c81eba 100644
--- a/mac/Keyman4MacIM/Keyman4MacIM/Base.lproj/MainMenu.xib
+++ b/mac/Keyman4MacIM/Keyman4MacIM/Base.lproj/MainMenu.xib
@@ -1,8 +1,8 @@
-
+
-
+
@@ -19,19 +19,16 @@
diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMConfiguration/KMConfigurationWindowController.m b/mac/Keyman4MacIM/Keyman4MacIM/KMConfiguration/KMConfigurationWindowController.m
index 14ee3c9c35..70937eecbf 100644
--- a/mac/Keyman4MacIM/Keyman4MacIM/KMConfiguration/KMConfigurationWindowController.m
+++ b/mac/Keyman4MacIM/Keyman4MacIM/KMConfiguration/KMConfigurationWindowController.m
@@ -364,6 +364,8 @@
[self saveActiveKeyboards];
}
else if (checkBox.state == NSOffState) {
+ if ([self.AppDelegate debugMode])
+ NSLog(@"Disabling active keyboard: %@", kmxFilePath);
[self.activeKeyboards removeObject:kmxFilePath];
[self saveActiveKeyboards];
}
diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputController.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputController.m
index 1607e915e0..47d2bfceb5 100644
--- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputController.m
+++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputController.m
@@ -183,65 +183,40 @@ NSMutableArray *servers;
return self.AppDelegate.kmx;
}
-
- (void)menuAction:(id)sender {
NSMenuItem *mItem = [sender objectForKey:kIMKCommandMenuItemName];
NSInteger itag = mItem.tag;
if ([self.AppDelegate debugMode])
NSLog(@"Keyman menu clicked - tag: %lu", itag);
- if (itag == 2) {
- // Using `showConfigurationWindow` instead of `showPreferences:` because `showPreferences:` is missing in
- // High Sierra (10.13.1 - 10.13.3). See: https://bugreport.apple.com/web/?problemID=35422518
- // rrb: where Apple's API is broken (10.13.1-10.13.3) call our workaround, otherwise, call showPreferences
- u_int16_t systemVersion = [KMOSVersion SystemVersion];
- if ([KMOSVersion Version_10_13_1] <= systemVersion && systemVersion <= [KMOSVersion Version_10_13_3]) // between 10.13.1 and 10.13.3 inclusive
- {
- NSLog(@"Input Menu: calling workaround instead of showPreferences (sys ver %x)", systemVersion);
- [self.AppDelegate showConfigurationWindow]; // call our workaround
- }
- else
- {
- NSLog(@"Input Menu: calling Apple's showPreferences (sys ver %x)", systemVersion);
- [self showPreferences:sender]; // call Apple API
- }
+ if (itag == CONFIG_MENUITEM_TAG) {
+ [self showConfigurationWindow:sender];
}
- else if (itag == 3) {
+ else if (itag == OSK_MENUITEM_TAG) {
[self.AppDelegate showOSK];
}
- else if (itag == 4) {
+ else if (itag == ABOUT_MENUITEM_TAG) {
[self.AppDelegate showAboutWindow];
}
- else if (itag >= 1000) {
- NSMenuItem *keyboards = [self.AppDelegate.menu itemWithTag:1];
- for (NSMenuItem *item in keyboards.submenu.itemArray) {
- if (item.tag == itag)
- [item setState:NSOnState];
- else
- [item setState:NSOffState];
- }
-
- NSString *path = [self.AppDelegate.activeKeyboards objectAtIndex:itag%1000];
- KMXFile *kmx = [[KMXFile alloc] initWithFilePath:path];
- [self.AppDelegate setKmx:kmx];
- KVKFile *kvk = nil;
- NSDictionary *kmxInfo = [KMXFile keyboardInfoFromKmxFile:path];
- NSString *kvkFilename = [kmxInfo objectForKey:kKMVisualKeyboardKey];
- if (kvkFilename != nil) {
- NSString *kvkFilePath = [self.AppDelegate kvkFilePathFromFilename:kvkFilename];
- if (kvkFilePath != nil)
- kvk = [[KVKFile alloc] initWithFilePath:kvkFilePath];
- }
- [self.AppDelegate setKvk:kvk];
- NSString *keyboardName = [kmxInfo objectForKey:kKMKeyboardNameKey];
- if ([self.AppDelegate debugMode])
- NSLog(@"Selected keyboard from menu: %@", keyboardName);
- [self.AppDelegate setKeyboardName:keyboardName];
- [self.AppDelegate setKeyboardIcon:[kmxInfo objectForKey:kKMKeyboardIconKey]];
- [self.AppDelegate setContextBuffer:nil];
- [self.AppDelegate setSelectedKeyboard:path];
- [self.AppDelegate loadSavedStores];
- if (kvk != nil && self.AppDelegate.alwaysShowOSK)
- [self.AppDelegate showOSK];
+ else if (itag >= KEYMAN_FIRST_KEYBOARD_MENUITEM_TAG) {
+ [self.AppDelegate selectKeyboardFromMenu:itag];
}
}
+
+- (void)showConfigurationWindow:(id)sender {
+ // Using `showConfigurationWindow` instead of `showPreferences:` because `showPreferences:` is missing in
+ // High Sierra (10.13.1 - 10.13.3). See: https://bugreport.apple.com/web/?problemID=35422518
+ // rrb: where Apple's API is broken (10.13.1-10.13.3) call our workaround, otherwise, call showPreferences
+ u_int16_t systemVersion = [KMOSVersion SystemVersion];
+ if ([KMOSVersion Version_10_13_1] <= systemVersion && systemVersion <= [KMOSVersion Version_10_13_3]) // between 10.13.1 and 10.13.3 inclusive
+ {
+ NSLog(@"Input Menu: calling workaround instead of showPreferences (sys ver %x)", systemVersion);
+ [self.AppDelegate showConfigurationWindow]; // call our workaround
+ }
+ else
+ {
+ NSLog(@"Input Menu: calling Apple's showPreferences (sys ver %x)", systemVersion);
+ [self showPreferences:sender]; // call Apple API
+ }
+}
+
@end
diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.h b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.h
index 5214faf821..e4e53e5972 100644
--- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.h
+++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.h
@@ -38,6 +38,21 @@ typedef struct {
NSString *apiKeymanCom;
} KeymanVersionInfo;
+// tags for default menu items, displayed whether keyboards are active or not
+static const int DIVIDER_MENUITEM_TAG = -4;
+static const int CONFIG_MENUITEM_TAG = -3;
+static const int OSK_MENUITEM_TAG = -2;
+static const int ABOUT_MENUITEM_TAG = -1;
+
+// the number of menu items that do not represent active keyboards
+static const int DEFAULT_KEYMAN_MENU_ITEM_COUNT = 4;
+
+// the tag for the first keyboard dynamically added to the menu
+static const int KEYMAN_FIRST_KEYBOARD_MENUITEM_TAG = 0;
+
+// the menu index for the first keyboard dynamically added to the menu
+static const int KEYMAN_FIRST_KEYBOARD_MENUITEM_INDEX = 0;
+
@interface KMInputMethodAppDelegate : NSObject
#define USE_ALERT_SHOW_HELP_TO_FORCE_EASTER_EGG_CRASH_FROM_ENGINE 1
#ifdef USE_ALERT_SHOW_HELP_TO_FORCE_EASTER_EGG_CRASH_FROM_ENGINE
@@ -55,6 +70,7 @@ typedef struct {
@property (nonatomic, strong) NSMutableArray *kmxFileList;
@property (nonatomic, strong) NSString *selectedKeyboard;
@property (nonatomic, strong) NSMutableArray *activeKeyboards;
+@property (assign) int numberOfKeyboardMenuItems;
@property (nonatomic, strong) NSMutableString *contextBuffer;
@property (nonatomic, assign) NSEventModifierFlags currentModifierFlags;
@property (nonatomic, assign) CFMachPortRef lowLevelEventTap;
@@ -88,6 +104,7 @@ typedef struct {
- (void)showAboutWindow;
- (void)showOSK;
- (void)showConfigurationWindow;
+- (void)selectKeyboardFromMenu:(NSInteger)tag;
- (void)sleepFollowingDeactivationOfServer:(id)lastServer;
- (void)wakeUpWith:(id)newServer;
- (void)handleKeyEvent:(NSEvent *)event;
diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m
index 44df2ef6fa..b04236ed5b 100644
--- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m
+++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m
@@ -700,14 +700,14 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
[userData setObject:_activeKeyboards forKey:kKMActiveKeyboardsKey];
[userData synchronize];
[self resetActiveKeyboards];
- [self setKeyboardsSubMenu];
+ [self updateKeyboardMenuItems];
}
- (void)clearActiveKeyboards {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:nil forKey:kKMActiveKeyboardsKey];
[userData synchronize];
- [self setKeyboardsSubMenu];
+ [self updateKeyboardMenuItems];
}
- (void)resetActiveKeyboards {
@@ -760,90 +760,174 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
}
- (void)awakeFromNib {
- [self setKeyboardsSubMenu];
-
- NSMenuItem *config = [self.menu itemWithTag:2];
- if (config)
- [config setAction:@selector(menuAction:)];
-
- NSMenuItem *osk = [self.menu itemWithTag:3];
- if (osk)
- [osk setAction:@selector(menuAction:)];
-
- NSMenuItem *about = [self.menu itemWithTag:4];
- if (about)
- [about setAction:@selector(menuAction:)];
+ [self setDefaultKeymanMenuItems];
+ [self updateKeyboardMenuItems];
}
-- (void)setKeyboardsSubMenu {
- NSMenuItem *keyboards = [self.menu itemWithTag:1];
- if (keyboards) {
- KVKFile *kvk = nil;
- BOOL didSetKeyboard = NO;
- NSInteger itag = 1000;
- [keyboards.submenu removeAllItems];
- for (NSString *path in self.activeKeyboards) {
- NSDictionary *infoDict = [KMXFile keyboardInfoFromKmxFile:path];
- if (!infoDict)
- continue;
- //NSString *str = [NSString stringWithFormat:@"%@ (%@)", [infoDict objectForKey:kKMKeyboardNameKey], [infoDict objectForKey:kKMKeyboardVersionKey]];
- NSString *str = [infoDict objectForKey:kKMKeyboardNameKey];
- NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:str action:@selector(menuAction:) keyEquivalent:@""];
- [item setTag:itag++];
- if ([path isEqualToString:self.selectedKeyboard]) {
- [item setState:NSOnState];
- KMXFile *kmx = [[KMXFile alloc] initWithFilePath:path];
- [self setKmx:kmx];
- NSDictionary *kmxInfo = [KMXFile keyboardInfoFromKmxFile:path];
- NSString *kvkFilename = [kmxInfo objectForKey:kKMVisualKeyboardKey];
- if (kvkFilename != nil) {
- NSString *kvkFilePath = [self kvkFilePathFromFilename:kvkFilename];
- if (kvkFilePath != nil)
- kvk = [[KVKFile alloc] initWithFilePath:kvkFilePath];
- }
- [self setKvk:kvk];
- [self setKeyboardName:[kmxInfo objectForKey:kKMKeyboardNameKey]];
- [self setKeyboardIcon:[kmxInfo objectForKey:kKMKeyboardIconKey]];
- [self loadSavedStores];
+- (void)setDefaultKeymanMenuItems {
+ NSMenuItem *config = [self.menu itemWithTag:CONFIG_MENUITEM_TAG];
+ if (config) {
+ [config setAction:@selector(menuAction:)];
+ }
+
+ NSMenuItem *osk = [self.menu itemWithTag:OSK_MENUITEM_TAG];
+ if (osk) {
+ [osk setAction:@selector(menuAction:)];
+ }
+
+ NSMenuItem *about = [self.menu itemWithTag:ABOUT_MENUITEM_TAG];
+ if (about) {
+ [about setAction:@selector(menuAction:)];
+ }
+}
- didSetKeyboard = YES;
- }
- else
- [item setState:NSOffState];
+- (void)updateKeyboardMenuItems {
+ self.numberOfKeyboardMenuItems = [self calculateNumberOfKeyboardMenuItems];
+ [self removeDynamicKeyboardMenuItems];
+ [self addDynamicKeyboardMenuItems];
+}
- [keyboards.submenu addItem:item];
+- (int)calculateNumberOfKeyboardMenuItems {
+ if (self.activeKeyboards.count == 0) {
+ // if there are no active keyboards, then we will insert one placeholder menu item 'No Active Keyboards'
+ return 1;
+ } else {
+ return (int) self.activeKeyboards.count;
+ }
+}
+
+- (void)removeDynamicKeyboardMenuItems {
+ int numberToRemove = (int) self.menu.numberOfItems - DEFAULT_KEYMAN_MENU_ITEM_COUNT;
+
+ if (self.debugMode) {
+ NSLog(@"*** removeDynamicKeyboardMenuItems, self.menu.numberOfItems = %ld, number of items to remove = %d", (long)self.menu.numberOfItems, numberToRemove);
+ }
+
+ if (numberToRemove > 0) {
+ for (int i = 0; i < numberToRemove; i++) {
+ [self.menu removeItemAtIndex:KEYMAN_FIRST_KEYBOARD_MENUITEM_INDEX];
+ }
+ }
+}
+
+- (void)addDynamicKeyboardMenuItems {
+ BOOL didSetSelectedKeyboard = NO;
+ NSInteger itag = KEYMAN_FIRST_KEYBOARD_MENUITEM_TAG;
+ NSString *keyboardMenuName = @"";
+ int menuItemIndex = KEYMAN_FIRST_KEYBOARD_MENUITEM_INDEX;
+
+ if (self.debugMode) {
+ NSLog(@"*** populateKeyboardMenuItems, number of active keyboards=%lu", self.activeKeyboards.count);
+ }
+
+ // loop through the active keyboards list and add them to the menu
+ for (NSString *path in self.activeKeyboards) {
+ NSDictionary *infoDict = [KMXFile keyboardInfoFromKmxFile:path];
+ if (!infoDict) {
+ continue;
}
-
- if (keyboards.submenu.numberOfItems == 0) {
- NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"(None)" action:NULL keyEquivalent:@""];
- [keyboards.submenu addItem:item];
- [self setKmx:nil];
- [self setKvk:nil];
- [self setKeyboardName:nil];
- [self setKeyboardIcon:nil];
- [self setContextBuffer:nil];
- [self setSelectedKeyboard:nil];
+ keyboardMenuName = [infoDict objectForKey:kKMKeyboardNameKey];
+ NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:keyboardMenuName action:@selector(menuAction:) keyEquivalent:@""];
+ [item setTag:itag++];
+
+ // if this is the selected keyboard, then configure it as selected
+ if ([path isEqualToString:self.selectedKeyboard]) {
+ [self setSelectedKeyboard:path inMenuItem:item];
+ didSetSelectedKeyboard = YES;
}
- else if (!didSetKeyboard) {
- [keyboards.submenu itemAtIndex:0].state = NSOnState;
- NSString *path = [self.activeKeyboards objectAtIndex:0];
- KMXFile *kmx = [[KMXFile alloc] initWithFilePath:path];
- [self setKmx:kmx];
- NSDictionary *kmxInfo = [KMXFile keyboardInfoFromKmxFile:path];
- NSString *kvkFilename = [kmxInfo objectForKey:kKMVisualKeyboardKey];
- if (kvkFilename != nil) {
- NSString *kvkFilePath = [self kvkFilePathFromFilename:kvkFilename];
- if (kvkFilePath != nil)
- kvk = [[KVKFile alloc] initWithFilePath:kvkFilePath];
- }
- [self setKvk:kvk];
- [self setKeyboardName:[kmxInfo objectForKey:kKMKeyboardNameKey]];
- [self setKeyboardIcon:[kmxInfo objectForKey:kKMKeyboardIconKey]];
- [self setContextBuffer:nil];
- [self loadSavedStores];
- [self setSelectedKeyboard:path];
+ else {
+ [item setState:NSOffState];
+ }
+
+ [self.menu insertItem:item atIndex:menuItemIndex++];
+ }
+
+ if (self.activeKeyboards.count == 0) {
+ [self addKeyboardPlaceholderMenuItem];
+ } else if (!didSetSelectedKeyboard) {
+ [self setDefaultSelectedKeyboard];
+ }
+}
+
+- (void) setSelectedKeyboard:(NSString*)keyboardName inMenuItem:(NSMenuItem*) menuItem {
+ KVKFile *kvk = nil;
+
+ [menuItem setState:NSOnState];
+ KMXFile *kmx = [[KMXFile alloc] initWithFilePath:keyboardName];
+ [self setKmx:kmx];
+ NSDictionary *kmxInfo = [KMXFile keyboardInfoFromKmxFile:keyboardName];
+ NSString *kvkFilename = [kmxInfo objectForKey:kKMVisualKeyboardKey];
+ if (kvkFilename != nil) {
+ NSString *kvkFilePath = [self kvkFilePathFromFilename:kvkFilename];
+ if (kvkFilePath != nil) {
+ kvk = [[KVKFile alloc] initWithFilePath:kvkFilePath];
}
}
+ [self setKvk:kvk];
+ [self setKeyboardName:[kmxInfo objectForKey:kKMKeyboardNameKey]];
+ [self setKeyboardIcon:[kmxInfo objectForKey:kKMKeyboardIconKey]];
+ [self loadSavedStores];
+}
+
+// defaults to the whatever keyboard happens to be first in the list
+- (void) setDefaultSelectedKeyboard {
+ NSMenuItem* menuItem = [self.menu itemAtIndex:KEYMAN_FIRST_KEYBOARD_MENUITEM_INDEX];
+ NSString *keyboardName = [self.activeKeyboards objectAtIndex:0];
+ [self setSelectedKeyboard:keyboardName inMenuItem:menuItem];
+ [self setSelectedKeyboard:keyboardName];
+ [self setContextBuffer:nil];
+}
+
+- (void) addKeyboardPlaceholderMenuItem {
+ NSString* placeholder = NSLocalizedString(@"no-keyboard-configured-menu-placeholder", nil);
+ NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:placeholder action:NULL keyEquivalent:@""];
+ [self.menu insertItem:item atIndex:KEYMAN_FIRST_KEYBOARD_MENUITEM_INDEX];
+ [self setKmx:nil];
+ [self setKvk:nil];
+ [self setKeyboardName:nil];
+ [self setKeyboardIcon:nil];
+ [self setContextBuffer:nil];
+ [self setSelectedKeyboard:nil];
+}
+
+- (void)selectKeyboardFromMenu:(NSInteger)tag {
+ NSMenuItem *menuItem = [self.menu itemWithTag:tag];
+ NSString *title = menuItem.title;
+ NSLog(@"Input Menu, selected Keyboards menu, itag: %lu, title: %@", tag, title);
+ for (NSMenuItem *item in self.menu.itemArray) {
+ // set the state of the keyboard items in the Keyman menu based on the new selection
+ if (item.tag >= KEYMAN_FIRST_KEYBOARD_MENUITEM_TAG) {
+ if (item.tag == tag) {
+ [item setState:NSOnState];
+ }
+ else {
+ [item setState:NSOffState];
+ }
+ }
+ }
+
+ NSString *path = [self.activeKeyboards objectAtIndex:tag-KEYMAN_FIRST_KEYBOARD_MENUITEM_TAG];
+ KMXFile *kmx = [[KMXFile alloc] initWithFilePath:path];
+ [self setKmx:kmx];
+ KVKFile *kvk = nil;
+ NSDictionary *kmxInfo = [KMXFile keyboardInfoFromKmxFile:path];
+ NSString *kvkFilename = [kmxInfo objectForKey:kKMVisualKeyboardKey];
+ if (kvkFilename != nil) {
+ NSString *kvkFilePath = [self kvkFilePathFromFilename:kvkFilename];
+ if (kvkFilePath != nil)
+ kvk = [[KVKFile alloc] initWithFilePath:kvkFilePath];
+ }
+ [self setKvk:kvk];
+ NSString *keyboardName = [kmxInfo objectForKey:kKMKeyboardNameKey];
+ if ([self debugMode])
+ NSLog(@"Selected keyboard from menu: %@", keyboardName);
+ [self setKeyboardName:keyboardName];
+ [self setKeyboardIcon:[kmxInfo objectForKey:kKMKeyboardIconKey]];
+ [self setContextBuffer:nil];
+ [self setSelectedKeyboard:path];
+ [self loadSavedStores];
+ if (kvk != nil && self.alwaysShowOSK)
+ [self showOSK];
}
- (NSArray *)KMXFiles {
diff --git a/mac/Keyman4MacIM/Keyman4MacIM/en.lproj/Localizable.strings b/mac/Keyman4MacIM/Keyman4MacIM/en.lproj/Localizable.strings
index f14a66b0b4..6b815a7b17 100644
--- a/mac/Keyman4MacIM/Keyman4MacIM/en.lproj/Localizable.strings
+++ b/mac/Keyman4MacIM/Keyman4MacIM/en.lproj/Localizable.strings
@@ -71,4 +71,6 @@
/* message displayed to alert user to need grant accessibility permission */
"privacy-alert-text" = "To function properly, Keyman requires accessibility features:\n\nGrant access in System Preferences, Security & Privacy.\nRestart your system.";
+/* Text of menu item in Input Menu when no Keyboards are configured -- include parentheses */
+"no-keyboard-configured-menu-placeholder" = "(No Keyboard Configured)";
diff --git a/oem/firstvoices/ios/build.sh b/oem/firstvoices/ios/build.sh
index 061d7b760e..03693589db 100755
--- a/oem/firstvoices/ios/build.sh
+++ b/oem/firstvoices/ios/build.sh
@@ -113,8 +113,8 @@ function do_build() {
-exportArchive \
-archivePath "$ARCHIVE_PATH" \
-exportOptionsPlist exportAppStore.plist \
- -exportPath "$BUILD_PATH/${CONFIG}-iphoneos"
- -allowProvisioningUpdates \
+ -exportPath "$BUILD_PATH/${CONFIG}-iphoneos" \
+ -allowProvisioningUpdates \
VERSION=$VERSION \
VERSION_WITH_TAG=$VERSION_WITH_TAG
else
diff --git a/web/build.sh b/web/build.sh
index e01c95dc5e..51df8a346d 100755
--- a/web/build.sh
+++ b/web/build.sh
@@ -97,10 +97,11 @@ builder_run_child_actions build:app/ui
# Needs both app/browser and app/ui.
builder_run_child_actions build:samples
-builder_run_child_actions build:test-pages
-
builder_run_child_actions build:tools
+# Some test pages refer to KMW tools.
+builder_run_child_actions build:test-pages
+
builder_run_child_actions test
if builder_start_action test; then
diff --git a/web/src/app/browser/src/contextManager.ts b/web/src/app/browser/src/contextManager.ts
index 492c627146..42b79109ef 100644
--- a/web/src/app/browser/src/contextManager.ts
+++ b/web/src/app/browser/src/contextManager.ts
@@ -184,7 +184,17 @@ export default class ContextManager extends ContextManagerBase, sendEvents: boolean) {
diff --git a/web/src/app/browser/src/keymanEngine.ts b/web/src/app/browser/src/keymanEngine.ts
index a49d9cb8f4..45f031d021 100644
--- a/web/src/app/browser/src/keymanEngine.ts
+++ b/web/src/app/browser/src/keymanEngine.ts
@@ -175,6 +175,19 @@ export default class KeymanEngine extends KeymanEngineBase {
+ if(!this.contextManager.activeKeyboard?.keyboard) {
+ // Autoselects this.keyboardRequisitioner.cache.defaultStub, which will be
+ // set to an actual keyboard on mobile devices.
+ this.setActiveKeyboard('', '');
+ }
+ });
+
this.contextManager.initialize(); // will seek to attach to the page, which requires document.body
// Capture the saved-keyboard string now, before we load any keyboards/stubs
@@ -213,6 +226,8 @@ export default class KeymanEngine extends KeymanEngineBase void {
diff --git a/web/src/tools/build.sh b/web/src/tools/build.sh
index 4ac7860bd5..55375f05c2 100755
--- a/web/src/tools/build.sh
+++ b/web/src/tools/build.sh
@@ -31,42 +31,12 @@ builder_describe "Builds the Keyman Engine for Web's development & unit-testing
builder_parse "$@"
-### CLEAN ACTIONS
+builder_run_child_actions clean
-if builder_start_action clean:bulk_rendering; then
- testing/bulk_rendering/build.sh clean
-
- builder_finish_action success clean:bulk_rendering
+# Some of the web/src/test section uses this script as a dependency. We can skip
+# the configure sections in such a case.
+if ! builder_is_dep_build && ! builder_is_child_build; then
+ builder_run_child_actions configure
fi
-if builder_start_action clean:recorder; then
- testing/recorder/build.sh clean
-
- builder_finish_action success clean:recorder
-fi
-
-if builder_start_action clean:sourcemap-root; then
- building/sourcemap-root/build.sh clean
-
- builder_finish_action success clean:sourcemap-root
-fi
-
-### BUILD ACTIONS
-
-if builder_start_action build:bulk_rendering; then
- testing/bulk_rendering/build.sh
-
- builder_finish_action success build:bulk_rendering
-fi
-
-if builder_start_action build:recorder; then
- testing/recorder/build.sh
-
- builder_finish_action success build:recorder
-fi
-
-if builder_start_action build:sourcemap-root; then
- building/sourcemap-root/build.sh
-
- builder_finish_action success build:sourcemap-root
-fi
\ No newline at end of file
+builder_run_child_actions build
\ No newline at end of file
diff --git a/windows/src/desktop/help/advanced/index.md b/windows/src/desktop/help/advanced/index.md
index 46d35ac664..371f85521d 100644
--- a/windows/src/desktop/help/advanced/index.md
+++ b/windows/src/desktop/help/advanced/index.md
@@ -4,4 +4,3 @@ title: Advanced Help
* [proxy_config](proxy_config)
* [locale_edit](locale_edit)
-* [text_services_framework](text_services_framework)
diff --git a/windows/src/desktop/help/advanced/text_services_framework.md b/windows/src/desktop/help/advanced/text_services_framework.md
deleted file mode 100644
index e7a977a062..0000000000
--- a/windows/src/desktop/help/advanced/text_services_framework.md
+++ /dev/null
@@ -1,28 +0,0 @@
----
-title: Text Services Framework
----
-
-## What is the Text Services Framework?
-
-The Text Services Framework, or TSF, is a set of components for modern
-applications that support advanced input and text processing. It
-supports features such as keyboard drivers, handwriting recognition,
-speech recognition, as well as spell checking and other text processing
-functions.
-
-With Keyman Desktop 9 and later versions, all keyboards are registered
-through the Windows interfaces, and the key advantage is that Keyman now
-automatically detects applications that have support for TSF and
-upgrades the experience for those applications.
-
-For those applications that support TSF, the most important advantage is
-that Keyman can read the current 'context' from the application. The
-'context' is the characters on the screen around the insertion point.
-Earlier versions of Keyman remembered the context while inputting text,
-but as soon as an arrow key was pressed, or the mouse clicked, Keyman
-would forget the context. This means that existing text can be edited in
-a much more intuitive manner when using TSF.
-
-## What applications support TSF?
-
-TSF is supported by a wide range of software and Windows components.
diff --git a/windows/src/desktop/help/basic/make-taskbar-icon-visible.md b/windows/src/desktop/help/basic/make-taskbar-icon-visible.md
index 62b90be5d4..eebd699e0a 100644
--- a/windows/src/desktop/help/basic/make-taskbar-icon-visible.md
+++ b/windows/src/desktop/help/basic/make-taskbar-icon-visible.md
@@ -37,3 +37,16 @@ Here's how to make the change with Windows Settings:
5. The Keyman icon will now always appear in the Windows
Taskbar near the clock, if Keyman is on.
6. Continue on to [Step 5](../start/tutorial#step-5-) of this guide.
+
+- On Windows 11
+
+ 1. Right-click on the Windows Taskbar.
+
+ 
+ 2. Select 'Taskbar settings'.
+ 3. Under 'Other system tray icons', expand the list,
+ click the toggle button beside 'Keyman Engine x86'to turn it on.
+ 
+ 4. The Keyman icon will now always appear in the Windows
+ Taskbar near the clock, if Keyman is on.
+ 5. Continue on to [Step 5](../start/tutorial#step-5-) of this guide.
\ No newline at end of file
diff --git a/windows/src/desktop/help/basic/uninstall.md b/windows/src/desktop/help/basic/uninstall.md
index 7e7cb2274c..6d98645613 100644
--- a/windows/src/desktop/help/basic/uninstall.md
+++ b/windows/src/desktop/help/basic/uninstall.md
@@ -15,3 +15,18 @@ title: Software Task - Uninstall Keyman
5. Click Uninstall.
6. Follow the prompts to complete the uninstall.
+
+## Uninstall Keyman from Windows 11
+
+1. Exit Keyman.
+
+2. Open Windows Start Menu and type "Add or Remove Programs", and open that app.
+
+3. In the Apps > Installed apps search box, type "Keyman".
+
+4. Click the three horizontal dots to the right of Keyman in the list.
+
+
+5. Click Uninstall.
+
+6. Follow the prompts to complete the uninstall.
diff --git a/windows/src/desktop/help/desktop_images/win11-taskbar1.png b/windows/src/desktop/help/desktop_images/win11-taskbar1.png
new file mode 100644
index 0000000000..ec72256a71
Binary files /dev/null and b/windows/src/desktop/help/desktop_images/win11-taskbar1.png differ
diff --git a/windows/src/desktop/help/desktop_images/win11-taskbar2.png b/windows/src/desktop/help/desktop_images/win11-taskbar2.png
new file mode 100644
index 0000000000..a349d65aa9
Binary files /dev/null and b/windows/src/desktop/help/desktop_images/win11-taskbar2.png differ
diff --git a/windows/src/desktop/help/desktop_images/win11-uninstall.png b/windows/src/desktop/help/desktop_images/win11-uninstall.png
new file mode 100644
index 0000000000..a0611a8b39
Binary files /dev/null and b/windows/src/desktop/help/desktop_images/win11-uninstall.png differ
diff --git a/windows/src/desktop/help/desktop_images/win11_km_install_anywhere_1.png b/windows/src/desktop/help/desktop_images/win11_km_install_anywhere_1.png
new file mode 100644
index 0000000000..f991126de0
Binary files /dev/null and b/windows/src/desktop/help/desktop_images/win11_km_install_anywhere_1.png differ
diff --git a/windows/src/desktop/help/index.md b/windows/src/desktop/help/index.md
index 6dd83bca6f..f5d5ddf378 100644
--- a/windows/src/desktop/help/index.md
+++ b/windows/src/desktop/help/index.md
@@ -33,7 +33,6 @@ frequently asked questions, tutorials, and videos.
### [Advanced Topics](advanced/)
* [Character Map](basic/toolbox/character-map)
* [Hotkeys](start/hotkey_set)
-* [Text Services Framework Addin](advanced/text_services_framework)
* [Use Word macros to switch keyboard and font](https://help.keyman.com/kb/93)
* [Application Programming Interface (API)](https://help.keyman.com/developer/engine/desktop/current-version/api/)
* [Keyman for Windows version history](https://help.keyman.com/products/windows/version-history)
diff --git a/windows/src/desktop/help/start/download-and-install-keyman.md b/windows/src/desktop/help/start/download-and-install-keyman.md
index 27902001c7..00a8538508 100644
--- a/windows/src/desktop/help/start/download-and-install-keyman.md
+++ b/windows/src/desktop/help/start/download-and-install-keyman.md
@@ -17,7 +17,7 @@ After you download Keyman, to install the program:
2. Double-click the file to begin installation.
-3. You may be warned to use a "Microsoft-verfied" App from the Microsoft Store. This is because Keyman is not available in the Microsoft Store. If "**Install Anyway**" is an option, select that. Otherwise follow the instructions found here [How To - Allow Windows 10 to Install Apps From Anywhere](../troubleshooting/install-app-from-anywhere)
+3. You may be warned to use a "Microsoft-verfied" App from the Microsoft Store. This is because Keyman is not available in the Microsoft Store. If "**Install Anyway**" is an option, select that. Otherwise follow the instructions found here [How To - Allow Windows to Install Apps From Anywhere](../troubleshooting/install-app-from-anywhere)

4. You will be asked to allow the installer to access your system.
diff --git a/windows/src/desktop/help/troubleshooting/index.md b/windows/src/desktop/help/troubleshooting/index.md
index 54416ad9ba..cafa510647 100644
--- a/windows/src/desktop/help/troubleshooting/index.md
+++ b/windows/src/desktop/help/troubleshooting/index.md
@@ -10,4 +10,4 @@ title: Troubleshooting
* [How To - Resolve Security Software Conflicts with keyman32.dll](securitysoftware)
* [How To - Use the Keyman Setup Bootstrapper](bootstrapper)
-* [How To - Allow Windows 10 to Install Apps From Anywhere](install-app-from-anywhere)
+* [How To - Allow Windows to Install Apps From Anywhere](install-app-from-anywhere)
diff --git a/windows/src/desktop/help/troubleshooting/install-app-from-anywhere.md b/windows/src/desktop/help/troubleshooting/install-app-from-anywhere.md
index 160d7cf023..23c4213b74 100644
--- a/windows/src/desktop/help/troubleshooting/install-app-from-anywhere.md
+++ b/windows/src/desktop/help/troubleshooting/install-app-from-anywhere.md
@@ -1,5 +1,5 @@
---
-title: How To - Allow Windows 10 to Install Apps From Anywhere
+title: How To - Allow Windows to Install Apps From Anywhere
---
## Allow Windows 10 to Install Apps From Anywhere
@@ -20,6 +20,22 @@ If "**Change my app recommendation settings**" is shown, select that and go to s
5. Now continue installing by double-clicking on the Keyman file identified in Step 1 of [How To - Download and Install a Keyman](../start/download-and-install-keyman).
+## Allow Windows 11 to Install Apps From Anywhere
+
+1. You may be warned to use a "Microsoft-verified" App from the Microsoft Store. This is because Keyman is not available in the Microsoft Store.
+If "**Change my app recommendation settings**" is shown, select that and go to step 4.
+
+
+
+2. If that is not available, open the Windows 11 start menu (Windows logo) and select **Settings**.
+
+3. Click on the **Apps** in the **Windows Settings** menu
+
+4. Click on **Advanced app settings** from the option **choose where to get apps**, click on the dropdown menu below and select "Anywhere, but warn me before installing an app that's not from the Microsoft Store" or one of the other options that allows you to install from *Anywhere*.
+
+
+5. Now continue installing by double-clicking on the Keyman file identified in Step 1 of [How To - Download and Install a Keyman](../start/download-and-install-keyman).
+
## Related Topics
- [How To - Download and Install a Keyman](../start/download-and-install-keyman)
diff --git a/windows/src/engine/keyman32/calldll.cpp b/windows/src/engine/keyman32/calldll.cpp
index 0c1fce80b8..d71b73145d 100644
--- a/windows/src/engine/keyman32/calldll.cpp
+++ b/windows/src/engine/keyman32/calldll.cpp
@@ -165,7 +165,7 @@ LogContext(km_core_state *lpCoreKeyboardState, uint8_t context_type) {
log_str_title = int_context;
break;
case CONTEXT_INT:
- error_status = (km_core_status_codes)kbp_state_get_intermediate_context(lpCoreKeyboardState, &citems);
+ error_status = (km_core_status_codes)km_core_state_get_intermediate_context(lpCoreKeyboardState, &citems);
log_str_title = core_context;
break;
default:
@@ -318,7 +318,7 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMSetOutput(PWSTR buf, DWORD backlen
// correctly. To do this need to check the context as we process the
// backspaces.
km_core_context_item *citems = nullptr;
- if (KM_CORE_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) {
+ if (KM_CORE_STATUS_OK != km_core_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) {
delete[] actionItems;
return FALSE;
}
@@ -428,7 +428,7 @@ extern "C" BOOL _declspec(dllexport) WINAPI KMGetContext(PWSTR buf, DWORD len)
}
km_core_context_item *citems = nullptr;
- if (KM_CORE_STATUS_OK != kbp_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) {
+ if (KM_CORE_STATUS_OK != km_core_state_get_intermediate_context(_td->lpActiveKeyboard->lpCoreKeyboardState, &citems)) {
return FALSE;
}
diff --git a/windows/src/engine/keyman32/keyboardoptions.cpp b/windows/src/engine/keyman32/keyboardoptions.cpp
index cc83828625..33758e3d7b 100644
--- a/windows/src/engine/keyman32/keyboardoptions.cpp
+++ b/windows/src/engine/keyman32/keyboardoptions.cpp
@@ -22,14 +22,14 @@
BOOL IntLoadKeyboardOptionsRegistrytoCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_core_state* const state);
void IntSaveKeyboardOptionCoretoRegistry(LPCSTR REGKey, LPINTKEYBOARDINFO kp, LPCWSTR key, LPWSTR value);
-static km_core_cp* CloneKMKBPCP(const km_core_cp* cp) {
+static km_core_cp* CloneKeymanCoreCP(const km_core_cp* cp) {
LPCWSTR buf = reinterpret_cast(cp);
km_core_cp* clone = new km_core_cp[wcslen(buf) + 1];
wcscpy_s(reinterpret_cast(clone), wcslen(buf) + 1, buf);
return clone;
}
-static km_core_cp* CloneKMKBPCPFromWSTR(LPWSTR buf) {
+static km_core_cp* CloneKeymanCoreCPFromWSTR(LPWSTR buf) {
km_core_cp* clone = new km_core_cp[wcslen(buf) + 1];
wcscpy_s(reinterpret_cast(clone), wcslen(buf) + 1, buf);
return clone;
@@ -90,9 +90,9 @@ BOOL IntLoadKeyboardOptionsRegistrytoCore(LPCSTR key, LPINTKEYBOARDINFO kp, km_c
LPCWSTR coreKey = reinterpret_cast(kpc->key);
WCHAR val[256];
if (hasData && r.ReadString(coreKey, val, sizeof(val) / sizeof(val[0])) && val[0]) {
- keyboardOpts[n].value = CloneKMKBPCPFromWSTR(val);
+ keyboardOpts[n].value = CloneKeymanCoreCPFromWSTR(val);
} else {
- keyboardOpts[n].value = CloneKMKBPCP(kpc->value);
+ keyboardOpts[n].value = CloneKeymanCoreCP(kpc->value);
}
n++;
}
diff --git a/windows/src/engine/keyman32/keyman-engine.vcxproj b/windows/src/engine/keyman32/keyman-engine.vcxproj
index d2e78ef7e4..391b86cbbc 100644
--- a/windows/src/engine/keyman32/keyman-engine.vcxproj
+++ b/windows/src/engine/keyman32/keyman-engine.vcxproj
@@ -98,7 +98,7 @@
/verbose:lib /section:.SHARDATA,rws %(AdditionalOptions)
- kmnkbp0.lib;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
+ keymancore.lib;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
./Keyman32.dll
true
keyman32.def
@@ -160,7 +160,7 @@
/verbose:lib /section:.SHARDATA,rws %(AdditionalOptions)
- kmnkbp0.lib;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
+ keymancore.lib;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
./Keyman32.dll
true
keyman32.def
diff --git a/windows/src/engine/keyman32/keyman32.vcxproj b/windows/src/engine/keyman32/keyman32.vcxproj
index 8688133787..70b8402ad7 100644
--- a/windows/src/engine/keyman32/keyman32.vcxproj
+++ b/windows/src/engine/keyman32/keyman32.vcxproj
@@ -96,7 +96,7 @@
/verbose:lib /section:.SHARDATA,rws %(AdditionalOptions)
- libicuuc.a;libicuin.a;libkmnkbp0.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
+ libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
true
keyman32.def
true
@@ -152,7 +152,7 @@
/verbose:lib /section:.SHARDATA,rws %(AdditionalOptions)
- libicuuc.a;libicuin.a;libkmnkbp0.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
+ libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;rpcrt4.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;%(AdditionalDependencies)
true
keyman32.def
true
@@ -387,4 +387,4 @@
-
\ No newline at end of file
+
diff --git a/windows/src/engine/keyman32/keymanengine.h b/windows/src/engine/keyman32/keymanengine.h
index 65fc9ec1e5..c563210a76 100644
--- a/windows/src/engine/keyman32/keymanengine.h
+++ b/windows/src/engine/keyman32/keymanengine.h
@@ -19,8 +19,8 @@
#endif
// For keyman_core_api_bits.h
-#ifndef KMN_KBP_STATIC
-#define KMN_KBP_STATIC
+#ifndef KM_CORE_LIBRARY_STATIC
+#define KM_CORE_LIBRARY_STATIC
#endif
// For keyman_core_api_bits.h
#ifndef _WIN32
diff --git a/windows/src/engine/keyman32/tests/keyman-engine-tests/keyman-engine-tests.vcxproj b/windows/src/engine/keyman32/tests/keyman-engine-tests/keyman-engine-tests.vcxproj
index 94ec1574c2..5b9116ea2d 100644
--- a/windows/src/engine/keyman32/tests/keyman-engine-tests/keyman-engine-tests.vcxproj
+++ b/windows/src/engine/keyman32/tests/keyman-engine-tests/keyman-engine-tests.vcxproj
@@ -98,7 +98,7 @@
true
Console
- version.lib;psapi.lib;imm32.lib;libkmnkbp0.a;%(AdditionalDependencies)
+ version.lib;psapi.lib;imm32.lib;libkeymancore.a;%(AdditionalDependencies)
false
@@ -157,4 +157,4 @@
-
\ No newline at end of file
+
diff --git a/windows/src/engine/keyman32/tests/keyman-engine-tests/kmprocessactionstests.cpp b/windows/src/engine/keyman32/tests/keyman-engine-tests/kmprocessactionstests.cpp
index 7af218cd77..362bc0497f 100644
--- a/windows/src/engine/keyman32/tests/keyman-engine-tests/kmprocessactionstests.cpp
+++ b/windows/src/engine/keyman32/tests/keyman-engine-tests/kmprocessactionstests.cpp
@@ -49,7 +49,7 @@ TEST_F(KMPROCESSACTIONS, processMarkertest) {
WCHAR callbuf[MAXCONTEXT];
AITIP testApp;
WCHAR expectedContext[] = {UC_SENTINEL, CODE_DEADKEY, 2, 0};
- uintptr_t marker = 2;
+ uint32_t marker = 2;
km_core_action_item itemAddMarker = {KM_CORE_IT_MARKER, {0,}, {marker}};
processMarker(&testApp, &itemAddMarker);
@@ -67,7 +67,7 @@ TEST_F(KMPROCESSACTIONS, processBackDeadkeytest) {
km_core_action_item itemAddChar = {KM_CORE_IT_CHAR, {0,}, {'A'}};
processUnicodeChar(&testApp, &itemAddChar);
- uintptr_t marker = 2;
+ uint32_t marker = 2;
km_core_action_item itemAddMarker = {KM_CORE_IT_MARKER, {0,}, {marker}};
processMarker(&testApp, &itemAddMarker);
km_core_action_item itemBackSpace = {KM_CORE_IT_BACK};
diff --git a/windows/src/engine/keyman64/keyman64.vcxproj b/windows/src/engine/keyman64/keyman64.vcxproj
index 89b7ffa2e1..f80c93089d 100644
--- a/windows/src/engine/keyman64/keyman64.vcxproj
+++ b/windows/src/engine/keyman64/keyman64.vcxproj
@@ -96,7 +96,7 @@
/verbose:lib /section:.SHARDATA,rws %(AdditionalOptions)
- libicuuc.a;libicuin.a;libkmnkbp0.a;psapi.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;libcmt.lib;%(AdditionalDependencies)
+ libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;libcmt.lib;%(AdditionalDependencies)
true
C:\Program Files\Microsoft SDKs\Windows\v7.0\Lib\x64;$(VCInstallDir)lib\amd64;$(VCInstallDir)lib;%(AdditionalLibraryDirectories)
false
@@ -152,7 +152,7 @@
/verbose:lib /section:.SHARDATA,rws %(AdditionalOptions)
- libicuuc.a;libicuin.a;libkmnkbp0.a;psapi.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;libcmt.lib;%(AdditionalDependencies)
+ libicuuc.a;libicuin.a;libkeymancore.a;psapi.lib;version.lib;setupapi.lib;iphlpapi.lib;imm32.lib;crypt32.lib;wintrust.lib;imagehlp.lib;ws2_32.lib;libcmt.lib;%(AdditionalDependencies)
true
C:\Program Files\Microsoft SDKs\Windows\v7.0\Lib\x64;$(VCInstallDir)lib\amd64;$(VCInstallDir)lib;%(AdditionalLibraryDirectories)
false
@@ -327,4 +327,4 @@
-
\ No newline at end of file
+