Merge dab6f26a64
into 69f7bfb409
This commit is contained in:
commit
12b0dcc010
14 changed files with 242 additions and 51 deletions
|
@ -97,5 +97,6 @@ target_sources(app PRIVATE src/workqueue.c)
|
||||||
target_sources(app PRIVATE src/main.c)
|
target_sources(app PRIVATE src/main.c)
|
||||||
|
|
||||||
add_subdirectory(src/display/)
|
add_subdirectory(src/display/)
|
||||||
|
add_subdirectory(src/shell/)
|
||||||
|
|
||||||
zephyr_cc_option(-Wfatal-errors)
|
zephyr_cc_option(-Wfatal-errors)
|
||||||
|
|
|
@ -406,6 +406,8 @@ endmenu
|
||||||
|
|
||||||
menu "Advanced"
|
menu "Advanced"
|
||||||
|
|
||||||
|
rsource "src/shell/Kconfig"
|
||||||
|
|
||||||
menu "Initialization Priorities"
|
menu "Initialization Priorities"
|
||||||
|
|
||||||
if USB_DEVICE_STACK
|
if USB_DEVICE_STACK
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
/ {
|
/ {
|
||||||
chosen {
|
chosen {
|
||||||
zmk,kscan = &kscan;
|
zmk,kscan = &kscan;
|
||||||
|
zephyr,shell-uart = &uart0;
|
||||||
};
|
};
|
||||||
|
|
||||||
kscan: kscan {
|
kscan: kscan {
|
||||||
|
@ -14,5 +15,10 @@
|
||||||
rows = <2>;
|
rows = <2>;
|
||||||
columns = <2>;
|
columns = <2>;
|
||||||
exit-after;
|
exit-after;
|
||||||
|
events = <>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
&uart0 {
|
||||||
|
status = "okay";
|
||||||
|
};
|
16
app/boards/native_posix_shell_test_extra.conf
Normal file
16
app/boards/native_posix_shell_test_extra.conf
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
CONFIG_SERIAL=y
|
||||||
|
CONFIG_UART_NATIVE_POSIX=y
|
||||||
|
#CONFIG_NATIVE_UART_0_ON_OWN_PTY=y
|
||||||
|
CONFIG_NATIVE_UART_0_ON_STDINOUT=y
|
||||||
|
|
||||||
|
CONFIG_SHELL=y
|
||||||
|
CONFIG_SHELL_BACKEND_SERIAL=y
|
||||||
|
CONFIG_ZMK_SHELL_KEY_POSITIONS=y
|
||||||
|
CONFIG_SHELL_BACKEND_SERIAL_RX_RING_BUFFER_SIZE=1024
|
||||||
|
|
||||||
|
CONFIG_SHELL_PROMPT_UART="zmk> "
|
||||||
|
|
||||||
|
#CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN=n
|
||||||
|
#CONFIG_UART_INTERRUPT_DRIVEN=n
|
||||||
|
#CONFIG_UART_LINE_CTRL=n
|
||||||
|
#CONFIG_SHELL_BACKEND_SERIAL_CHECK_DTR=n
|
|
@ -55,7 +55,7 @@ static int kscan_mock_configure(const struct device *dev, kscan_callback_t callb
|
||||||
uint32_t ev = cfg->events[data->event_index]; \
|
uint32_t ev = cfg->events[data->event_index]; \
|
||||||
LOG_DBG("delaying next keypress: %d", ZMK_MOCK_MSEC(ev)); \
|
LOG_DBG("delaying next keypress: %d", ZMK_MOCK_MSEC(ev)); \
|
||||||
k_work_schedule(&data->work, K_MSEC(ZMK_MOCK_MSEC(ev))); \
|
k_work_schedule(&data->work, K_MSEC(ZMK_MOCK_MSEC(ev))); \
|
||||||
} else if (cfg->exit_after) { \
|
} else if (cfg->exit_after && DT_INST_PROP_LEN(n, events) > 0) { \
|
||||||
LOG_DBG("Exiting"); \
|
LOG_DBG("Exiting"); \
|
||||||
exit(0); \
|
exit(0); \
|
||||||
} \
|
} \
|
||||||
|
|
133
app/run-test.sh
133
app/run-test.sh
|
@ -1,47 +1,114 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
# Copyright (c) 2020 The ZMK Contributors
|
# Copyright (c) 2020 The ZMK Contributors
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
if [ -z "$1" ]; then
|
function main() {
|
||||||
echo "Usage: ./run-test.sh <path to testcase>"
|
if [ -z "$1" ] || [ "$1" = "all" ]; then
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
path="$1"
|
|
||||||
if [ $path = "all" ]; then
|
|
||||||
path="tests"
|
path="tests"
|
||||||
fi
|
else
|
||||||
|
path="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
testcases=$(find $path -name native_posix_64.keymap -exec dirname \{\} \;)
|
>&2 echo "Running all tests in ./$path"
|
||||||
num_cases=$(echo "$testcases" | wc -l)
|
|
||||||
if [ $num_cases -gt 1 ] || [ "$testcases" != "$path" ]; then
|
testcases=$(find $path -name native_posix_64.keymap -exec dirname \{\} \;)
|
||||||
echo "" > ./build/tests/pass-fail.log
|
|
||||||
echo "$testcases" | xargs -L 1 -P ${J:-4} ./run-test.sh
|
:>./build/tests/pass-fail.log
|
||||||
|
echo "$testcases" | xargs -I {} -P ${J:-4} bash -c 'run_test "$@"' _ {}
|
||||||
err=$?
|
err=$?
|
||||||
|
>&2 echo
|
||||||
|
>&2 echo "=== Test results ==="
|
||||||
sort -k2 ./build/tests/pass-fail.log
|
sort -k2 ./build/tests/pass-fail.log
|
||||||
exit $err
|
exit $err
|
||||||
fi
|
}
|
||||||
|
|
||||||
testcase="$path"
|
function run_test() {
|
||||||
echo "Running $testcase:"
|
path="$1"
|
||||||
|
|
||||||
west build -d build/$testcase -b native_posix_64 -- -DZMK_CONFIG="$(pwd)/$testcase" > /dev/null 2>&1
|
build_dir="build/$path"
|
||||||
if [ $? -gt 0 ]; then
|
build_log="$build_dir/build.log"
|
||||||
echo "FAILED: $testcase did not build" | tee -a ./build/tests/pass-fail.log
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
./build/$testcase/zephyr/zmk.exe | sed -e "s/.*> //" | tee build/$testcase/keycode_events_full.log | sed -n -f $testcase/events.patterns > build/$testcase/keycode_events.log
|
# Look for a file named "inputs.txt" in some subdirectory of the test.
|
||||||
diff -auZ $testcase/keycode_events.snapshot build/$testcase/keycode_events.log
|
#
|
||||||
if [ $? -gt 0 ]; then
|
# If none exists, assume it uses the old approach of hardcoding events in the kscan-mock driver.
|
||||||
if [ -f $testcase/pending ]; then
|
inputs=$(find $path -mindepth 1 -name inputs.txt -exec dirname \{\} \;)
|
||||||
echo "PENDING: $testcase" | tee -a ./build/tests/pass-fail.log
|
|
||||||
exit 0
|
extra_config=""
|
||||||
|
if [ -n "$inputs" ]; then
|
||||||
|
# replace with -DEXTRA_CONFIG_FILE for Zephyr 3.4
|
||||||
|
extra_config+="-DOVERLAY_CONFIG=boards/native_posix_shell_test_extra.conf"
|
||||||
fi
|
fi
|
||||||
echo "FAILED: $testcase" | tee -a ./build/tests/pass-fail.log
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "PASS: $testcase" | tee -a ./build/tests/pass-fail.log
|
mkdir -p $build_dir
|
||||||
exit 0
|
>&2 echo "Building $path:"
|
||||||
|
west build -d $build_dir -b native_posix_64 -- -DZMK_CONFIG="$(pwd)/$path" $extra_config >"$build_log" 2>&1
|
||||||
|
if [ $? -gt 0 ]; then
|
||||||
|
echo "FAILED: $path did not build"
|
||||||
|
>&2 printf "\t%s\n" "for details see ./$build_log"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
function check_test_results() {
|
||||||
|
testdir="$1"
|
||||||
|
outdir="$2"
|
||||||
|
tee $outdir/keycode_events_full.log | \
|
||||||
|
sed -e "s/.*> //" | \
|
||||||
|
sed -e "s/\x1B\[[0-9;]*[a-zA-Z]//g" | \
|
||||||
|
sed -n -f $testdir/events.patterns > $outdir/keycode_events.log
|
||||||
|
|
||||||
|
>&2 diff -auZ $testdir/keycode_events.snapshot $outdir/keycode_events.log
|
||||||
|
|
||||||
|
if [ $? -gt 0 ]; then
|
||||||
|
if [ -f $testdir/pending ]; then
|
||||||
|
echo "PENDING: $testdir"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "FAILED: $testdir"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
echo "PASS: $testdir"
|
||||||
|
return 0
|
||||||
|
fi | tee -a ./build/tests/pass-fail.log
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "$inputs" ]; then
|
||||||
|
>&2 echo "Running $path using kscan-mock:"
|
||||||
|
./build/$path/zephyr/zmk.exe | check_test_results $path build/$path
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Filters comments and blank lines from the inputs.txt file, adds a fixed
|
||||||
|
# `wait` after each command, and appends an `exit`.
|
||||||
|
#
|
||||||
|
# FIXME(ecstaticmore): make the `wait` configurable.
|
||||||
|
function augment_commands() {
|
||||||
|
grep -v -e '^\s*#' -e '^\s*$' "$1/inputs.txt" | \
|
||||||
|
awk '{ print; print "wait 10" }'
|
||||||
|
echo "exit"
|
||||||
|
}
|
||||||
|
|
||||||
|
# This convoluted invocation is needed to work around some quirks in
|
||||||
|
# Zephyr's serial shell driver:
|
||||||
|
#
|
||||||
|
# - When stdin is closed while CONFIG_NATIVE_UART_0_ON_STDINOUT is enabled,
|
||||||
|
# reading from the UART driver returns the same byte over and over
|
||||||
|
# (instead of blocking).
|
||||||
|
# - The shell driver reads from the UART driver greedily until it blocks
|
||||||
|
# (instead of stopping on newlines or when the ring buffer is full).
|
||||||
|
#
|
||||||
|
# This means that piping directly to the Zephyr executable causes it to
|
||||||
|
# enter an infinite loop. To work around this, we sleep inside a process
|
||||||
|
# substitution to keep the pipe open until the Zephyr executable exits.
|
||||||
|
ret=0
|
||||||
|
for input in $inputs; do
|
||||||
|
>&2 echo "Running $path/native_posix_64.keymap with $input/inputs.txt:"
|
||||||
|
mkdir -p build/$input
|
||||||
|
./build/$path/zephyr/zmk.exe < <(augment_commands $input; sleep 999) \
|
||||||
|
| check_test_results $input build/$input || ret=1
|
||||||
|
done
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
|
export -f run_test
|
||||||
|
main "$@"
|
1
app/src/shell/CMakeLists.txt
Normal file
1
app/src/shell/CMakeLists.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
target_sources_ifdef(CONFIG_ZMK_SHELL_KEY_POSITIONS app PRIVATE key_positions.c)
|
8
app/src/shell/Kconfig
Normal file
8
app/src/shell/Kconfig
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
menu "Shell"
|
||||||
|
|
||||||
|
config ZMK_SHELL_KEY_POSITIONS
|
||||||
|
bool "Key Position commands"
|
||||||
|
default y if !ZMK_SPLIT || ZMK_SPLIT_ROLE_CENTRAL
|
||||||
|
depends on SHELL
|
||||||
|
|
||||||
|
endmenu
|
100
app/src/shell/key_positions.c
Normal file
100
app/src/shell/key_positions.c
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/shell/shell.h>
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
#include <zmk/events/position_state_changed.h>
|
||||||
|
|
||||||
|
#define HELP_NONE "[key_position]"
|
||||||
|
#define TAP_EVENT_SPACING K_MSEC(50)
|
||||||
|
|
||||||
|
static int parse_positive_int(char *s) {
|
||||||
|
char *endptr;
|
||||||
|
unsigned long value = strtoul(s, &endptr, 10);
|
||||||
|
|
||||||
|
if (endptr == s)
|
||||||
|
return -EINVAL;
|
||||||
|
if (value > INT_MAX)
|
||||||
|
return -ERANGE;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_and_raise(const struct shell *shell, char *pos_str, bool pressed) {
|
||||||
|
long position = parse_positive_int(pos_str);
|
||||||
|
|
||||||
|
if (position < 0) {
|
||||||
|
shell_error(shell, "Enter an integer key position");
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZMK_EVENT_RAISE(new_zmk_position_state_changed(
|
||||||
|
(struct zmk_position_state_changed){.source = ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL,
|
||||||
|
.state = pressed,
|
||||||
|
.position = position,
|
||||||
|
.timestamp = k_uptime_get()}));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_key_tap(const struct shell *shell, size_t argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_and_raise(shell, argv[1], true);
|
||||||
|
|
||||||
|
k_sleep(TAP_EVENT_SPACING);
|
||||||
|
|
||||||
|
parse_and_raise(shell, argv[1], false);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cmd_key_press(const struct shell *shell, size_t argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_and_raise(shell, argv[1], true);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cmd_key_release(const struct shell *shell, size_t argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_and_raise(shell, argv[1], false);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cmd_sleep(const struct shell *shell, size_t argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sleep_ms = parse_positive_int(argv[1]);
|
||||||
|
|
||||||
|
if (sleep_ms < 0) {
|
||||||
|
shell_error(shell, "Enter a positive number of milliseconds");
|
||||||
|
return sleep_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sleep(K_MSEC(sleep_ms));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cmd_exit(const struct shell *shell, size_t argc, char **argv) { exit(0); };
|
||||||
|
|
||||||
|
SHELL_STATIC_SUBCMD_SET_CREATE(sub_key, SHELL_CMD(tap, NULL, HELP_NONE, cmd_key_tap),
|
||||||
|
SHELL_CMD(press, NULL, HELP_NONE, cmd_key_press),
|
||||||
|
SHELL_CMD(release, NULL, HELP_NONE, cmd_key_release),
|
||||||
|
SHELL_SUBCMD_SET_END /* Array terminated. */
|
||||||
|
);
|
||||||
|
|
||||||
|
SHELL_CMD_REGISTER(key, &sub_key, "Key commands", NULL);
|
||||||
|
SHELL_CMD_REGISTER(sleep, NULL, "Sleep (milliseconds)", cmd_sleep);
|
||||||
|
SHELL_CMD_REGISTER(exit, NULL, "Exit", cmd_exit);
|
5
app/tests/toggle-layer/early-key-release/inputs.txt
Normal file
5
app/tests/toggle-layer/early-key-release/inputs.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
key press 0
|
||||||
|
key press 1
|
||||||
|
key release 0
|
||||||
|
key release 1
|
||||||
|
key tap 0
|
|
@ -1,9 +0,0 @@
|
||||||
#include <dt-bindings/zmk/keys.h>
|
|
||||||
#include <behaviors.dtsi>
|
|
||||||
#include <dt-bindings/zmk/kscan_mock.h>
|
|
||||||
#include "../behavior_keymap.dtsi"
|
|
||||||
|
|
||||||
&kscan {
|
|
||||||
events = <ZMK_MOCK_PRESS(0,0,10) ZMK_MOCK_PRESS(0,1,10) ZMK_MOCK_RELEASE(0,0,10) ZMK_MOCK_RELEASE(0,1,10)
|
|
||||||
ZMK_MOCK_PRESS(0,0,10) ZMK_MOCK_RELEASE(0,0,10)>;
|
|
||||||
};
|
|
2
app/tests/toggle-layer/normal/inputs.txt
Normal file
2
app/tests/toggle-layer/normal/inputs.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
key tap 1
|
||||||
|
key tap 0
|
|
@ -1,8 +0,0 @@
|
||||||
#include <dt-bindings/zmk/keys.h>
|
|
||||||
#include <behaviors.dtsi>
|
|
||||||
#include <dt-bindings/zmk/kscan_mock.h>
|
|
||||||
#include "../behavior_keymap.dtsi"
|
|
||||||
|
|
||||||
&kscan {
|
|
||||||
events = <ZMK_MOCK_PRESS(0,1,10) ZMK_MOCK_RELEASE(0,1,10) ZMK_MOCK_PRESS(0,0,10) ZMK_MOCK_RELEASE(0,0,10)>;
|
|
||||||
};
|
|
Loading…
Add table
Reference in a new issue