Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions behaviors/behavior_os_layer_mod.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ static void os_layer_mod_work_handler(struct k_work *work) {
}
} else {
do_hold(olm);
/* Replay any events captured before the timer fired — they
* must be processed under the now-active layer, and won't be
* replayed by the release path (which sees OLM_HOLD). */
release_captured_events();
}
}
}
Expand Down Expand Up @@ -263,6 +267,9 @@ static int on_os_layer_mod_binding_released(struct zmk_behavior_binding *binding
* modifier, dismissing the OS app-switcher menu. */
zmk_keymap_layer_deactivate(olm->layer);
zmk_behavior_invoke_binding(&olm->mod_binding, event, false);
/* Safety: discard any captured events that were not replayed
* (e.g. timer fired while an interrupt key was still down). */
captured_events_count = 0;
}

olm->active = false;
Expand Down
60 changes: 34 additions & 26 deletions run-tests
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,44 @@ set -e

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

# Resolve the test path to an absolute path
if [ -n "$1" ]; then
case "$1" in
/*) TEST_PATH="$1" ;;
*) TEST_PATH="$SCRIPT_DIR/$1" ;;
esac
else
TEST_PATH="$SCRIPT_DIR/tests/oskey/"
fi
run_suite() {
TEST_PATH="$1"
DOCKER_TEST_PATH="/workspace${TEST_PATH#$SCRIPT_DIR}"
docker run --rm \
-v "$SCRIPT_DIR:/workspace" \
-e "ZMK_TEST_PATH=$DOCKER_TEST_PATH" \
-e ZMK_TESTS_AUTO_ACCEPT \
zmkfirmware/zmk-build-arm:3.5 \
bash -c '
git config --global --add safe.directory "*"
set -e
cd /workspace/tests
cmake -P /workspace/tests/zephyr/share/zephyr-package/cmake/zephyr_export.cmake
cmake -P /workspace/tests/zephyr/share/zephyrunittest-package/cmake/zephyr_export.cmake
ZEPHYR_TOOLCHAIN_VARIANT=host \
ZMK_EXTRA_MODULES=/workspace \
ZMK_SRC_DIR=/workspace/tests/zmk/app \
/workspace/tests/zmk/app/run-test.sh "$ZMK_TEST_PATH"
'
}

if ! command -v docker >/dev/null 2>&1; then
echo "Error: Docker is required to run tests."
echo "Install Docker Desktop: https://docs.docker.com/desktop/"
exit 1
fi

DOCKER_TEST_PATH="/workspace${TEST_PATH#$SCRIPT_DIR}"
exec docker run --rm \
-v "$SCRIPT_DIR:/workspace" \
-e "ZMK_TEST_PATH=$DOCKER_TEST_PATH" \
-e ZMK_TESTS_AUTO_ACCEPT \
zmkfirmware/zmk-build-arm:3.5 \
bash -c '
git config --global --add safe.directory "*"
set -e
cd /workspace/tests
cmake -P /workspace/tests/zephyr/share/zephyr-package/cmake/zephyr_export.cmake
cmake -P /workspace/tests/zephyr/share/zephyrunittest-package/cmake/zephyr_export.cmake
ZEPHYR_TOOLCHAIN_VARIANT=host \
ZMK_EXTRA_MODULES=/workspace \
ZMK_SRC_DIR=/workspace/tests/zmk/app \
/workspace/tests/zmk/app/run-test.sh "$ZMK_TEST_PATH"
'
SUITES="$*"
if [ -z "$SUITES" ]; then
SUITES="oskey os-layer-mod"
fi

FAIL=0
for suite in $SUITES; do
case "$suite" in
/*) TEST_PATH="$suite" ;;
*) TEST_PATH="$SCRIPT_DIR/tests/$suite" ;;
esac
run_suite "$TEST_PATH" || FAIL=1
done
exit $FAIL
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
s/.*hid_listener_keycode_//p
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pressed: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0x17 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x17 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "../behavior_keymap.dtsi"

/* Set flavor to balanced. */
&ok_ltm_test {
flavor = "balanced";
};

/* Default OS = Windows. Hold (1,1), press (0,0) as an interrupt key, then
* let the tapping-term timer expire while (0,0) is STILL held.
*
* Before the fix, the captured (0,0) key-down event was never replayed when
* the timer triggered the hold — it stayed in the buffer and leaked into the
* next interaction. After the fix, release_captured_events() is called
* immediately after do_hold() in the timer handler.
*
* Position (0,0) maps to &kp T on app_switch_layer (layer 1), so after the
* hold fires the replayed key-down should produce a T press. The subsequent
* (0,0) key-up arrives while layer 1 is still active, producing a T release.
* Releasing (1,1) then deactivates the layer and releases Left Alt. */
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,50)
ZMK_MOCK_PRESS(0,0,300)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(1,1,10)
>;
};