From 050fa9062f6a62ee2adefde17e6634edd79d92fc Mon Sep 17 00:00:00 2001 From: Jouke Witteveen Date: Thu, 16 Jun 2022 08:05:27 +0200 Subject: tap-dance: Rename tests so that tap_dance is used consistently (#17396) --- tests/tap_dance/config.h | 19 +++ tests/tap_dance/examples.c | 199 ++++++++++++++++++++++++ tests/tap_dance/examples.h | 33 ++++ tests/tap_dance/test.mk | 22 +++ tests/tap_dance/test_examples.cpp | 319 ++++++++++++++++++++++++++++++++++++++ tests/tapdance/config.h | 19 --- tests/tapdance/examples.c | 199 ------------------------ tests/tapdance/examples.h | 33 ---- tests/tapdance/test.mk | 22 --- tests/tapdance/test_examples.cpp | 319 -------------------------------------- 10 files changed, 592 insertions(+), 592 deletions(-) create mode 100644 tests/tap_dance/config.h create mode 100644 tests/tap_dance/examples.c create mode 100644 tests/tap_dance/examples.h create mode 100644 tests/tap_dance/test.mk create mode 100644 tests/tap_dance/test_examples.cpp delete mode 100644 tests/tapdance/config.h delete mode 100644 tests/tapdance/examples.c delete mode 100644 tests/tapdance/examples.h delete mode 100644 tests/tapdance/test.mk delete mode 100644 tests/tapdance/test_examples.cpp (limited to 'tests') diff --git a/tests/tap_dance/config.h b/tests/tap_dance/config.h new file mode 100644 index 0000000000..6aada3efd3 --- /dev/null +++ b/tests/tap_dance/config.h @@ -0,0 +1,19 @@ +/* Copyright 2022 Jouke Witteveen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "test_common.h" diff --git a/tests/tap_dance/examples.c b/tests/tap_dance/examples.c new file mode 100644 index 0000000000..4a5be41b08 --- /dev/null +++ b/tests/tap_dance/examples.c @@ -0,0 +1,199 @@ +/* Copyright 2022 Jouke Witteveen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "quantum.h" +#include "examples.h" + +// Example code from the tap dance documentation, adapted for testing + +// clang-format off + +// Example 1 + +void dance_egg(qk_tap_dance_state_t *state, void *user_data) { + if (state->count >= 100) { + // SEND_STRING("Safety dance!"); + tap_code(KC_C); + reset_tap_dance(state); + } +} + + +// Example 2 + +void dance_flsh_each(qk_tap_dance_state_t *state, void *user_data) { + switch (state->count) { + case 1: + register_code(KC_3); + break; + case 2: + register_code(KC_2); + break; + case 3: + register_code(KC_1); + break; + case 4: + unregister_code(KC_3); + // wait_ms(50); + unregister_code(KC_2); + // wait_ms(50); + unregister_code(KC_1); + } +} + +void dance_flsh_finished(qk_tap_dance_state_t *state, void *user_data) { + if (state->count >= 4) { + // reset_keyboard(); + tap_code(KC_R); + } +} + +void dance_flsh_reset(qk_tap_dance_state_t *state, void *user_data) { + unregister_code(KC_1); + // wait_ms(50); + unregister_code(KC_2); + // wait_ms(50); + unregister_code(KC_3); +} + + +// Example 3 + +typedef struct { + uint16_t tap; + uint16_t hold; + uint16_t held; +} tap_dance_tap_hold_t; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + qk_tap_dance_action_t *action; + + switch (keycode) { + case TD(CT_CLN): + action = &tap_dance_actions[TD_INDEX(keycode)]; + if (!record->event.pressed && action->state.count && !action->state.finished) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; + tap_code16(tap_hold->tap); + } + } + return true; +} + +void tap_dance_tap_hold_finished(qk_tap_dance_state_t *state, void *user_data) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + + if (state->pressed) { + if (state->count == 1 +#ifndef PERMISSIVE_HOLD + && !state->interrupted +#endif + ) { + register_code16(tap_hold->hold); + tap_hold->held = tap_hold->hold; + } else { + register_code16(tap_hold->tap); + tap_hold->held = tap_hold->tap; + } + } +} + +void tap_dance_tap_hold_reset(qk_tap_dance_state_t *state, void *user_data) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + + if (tap_hold->held) { + unregister_code16(tap_hold->held); + tap_hold->held = 0; + } +} + +#define ACTION_TAP_DANCE_TAP_HOLD(tap, hold) \ + { .fn = {NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, .user_data = (void *)&((tap_dance_tap_hold_t){tap, hold, 0}), } + + +// Example 4 + +typedef enum { + TD_NONE, + TD_UNKNOWN, + TD_SINGLE_TAP, + TD_SINGLE_HOLD, + TD_DOUBLE_TAP, + TD_DOUBLE_HOLD, + TD_DOUBLE_SINGLE_TAP, + TD_TRIPLE_TAP, + TD_TRIPLE_HOLD +} td_state_t; + +typedef struct { + bool is_press_action; + td_state_t state; +} td_tap_t; + +td_state_t cur_dance(qk_tap_dance_state_t *state) { + if (state->count == 1) { + if (state->interrupted || !state->pressed) return TD_SINGLE_TAP; + else return TD_SINGLE_HOLD; + } else if (state->count == 2) { + if (state->interrupted) return TD_DOUBLE_SINGLE_TAP; + else if (state->pressed) return TD_DOUBLE_HOLD; + else return TD_DOUBLE_TAP; + } + + if (state->count == 3) { + if (state->interrupted || !state->pressed) return TD_TRIPLE_TAP; + else return TD_TRIPLE_HOLD; + } else return TD_UNKNOWN; +} + +static td_tap_t xtap_state = { + .is_press_action = true, + .state = TD_NONE +}; + +void x_finished(qk_tap_dance_state_t *state, void *user_data) { + xtap_state.state = cur_dance(state); + switch (xtap_state.state) { + case TD_SINGLE_TAP: register_code(KC_X); break; + case TD_SINGLE_HOLD: register_code(KC_LCTL); break; + case TD_DOUBLE_TAP: register_code(KC_ESC); break; + case TD_DOUBLE_HOLD: register_code(KC_LALT); break; + case TD_DOUBLE_SINGLE_TAP: tap_code(KC_X); register_code(KC_X); + default: break; // Not present in documentation + } +} + +void x_reset(qk_tap_dance_state_t *state, void *user_data) { + switch (xtap_state.state) { + case TD_SINGLE_TAP: unregister_code(KC_X); break; + case TD_SINGLE_HOLD: unregister_code(KC_LCTL); break; + case TD_DOUBLE_TAP: unregister_code(KC_ESC); break; + case TD_DOUBLE_HOLD: unregister_code(KC_LALT); + case TD_DOUBLE_SINGLE_TAP: unregister_code(KC_X); + default: break; // Not present in documentation + } + xtap_state.state = TD_NONE; +} + + +qk_tap_dance_action_t tap_dance_actions[] = { + [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS), + [CT_EGG] = ACTION_TAP_DANCE_FN(dance_egg), + [CT_FLSH] = ACTION_TAP_DANCE_FN_ADVANCED(dance_flsh_each, dance_flsh_finished, dance_flsh_reset), + [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(KC_COLN, KC_SCLN), + [X_CTL] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished, x_reset) +}; + +// clang-format on diff --git a/tests/tap_dance/examples.h b/tests/tap_dance/examples.h new file mode 100644 index 0000000000..2622af6b2f --- /dev/null +++ b/tests/tap_dance/examples.h @@ -0,0 +1,33 @@ +/* Copyright 2022 Jouke Witteveen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + TD_ESC_CAPS, + CT_EGG, + CT_FLSH, + CT_CLN, + X_CTL, +}; + +#ifdef __cplusplus +} +#endif diff --git a/tests/tap_dance/test.mk b/tests/tap_dance/test.mk new file mode 100644 index 0000000000..041d9b4dc9 --- /dev/null +++ b/tests/tap_dance/test.mk @@ -0,0 +1,22 @@ +# Copyright 2022 Jouke Witteveen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- + +TAP_DANCE_ENABLE = yes + +SRC += examples.c diff --git a/tests/tap_dance/test_examples.cpp b/tests/tap_dance/test_examples.cpp new file mode 100644 index 0000000000..e67e6cb907 --- /dev/null +++ b/tests/tap_dance/test_examples.cpp @@ -0,0 +1,319 @@ +/* Copyright 2022 Jouke Witteveen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_keymap_key.hpp" +#include "examples.h" + +using testing::_; +using testing::InSequence; + +class TapDance : public TestFixture {}; + +TEST_F(TapDance, DoubleTap) { + TestDriver driver; + InSequence s; + auto key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)}; + + set_keymap({key_esc_caps}); + + /* The tap dance key does nothing on the first press */ + key_esc_caps.press(); + run_one_scan_loop(); + key_esc_caps.release(); + EXPECT_NO_REPORT(driver); + + /* We get the key press and the release on timeout */ + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_ESC)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Double tap gets us the second key */ + tap_key(key_esc_caps); + EXPECT_NO_REPORT(driver); + key_esc_caps.press(); + EXPECT_REPORT(driver, (KC_CAPS)); + run_one_scan_loop(); + key_esc_caps.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, DoubleTapWithMod) { + TestDriver driver; + InSequence s; + auto key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)}; + auto key_shift = KeymapKey{0, 2, 0, KC_LSFT}; + + set_keymap({key_esc_caps, key_shift}); + + /* The tap dance key does nothing on the first press */ + key_shift.press(); + EXPECT_REPORT(driver, (KC_LSFT)); + run_one_scan_loop(); + key_esc_caps.press(); + run_one_scan_loop(); + + key_esc_caps.release(); + key_shift.release(); + EXPECT_EMPTY_REPORT(driver); + + /* We get the key press and the release */ + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_REPORT(driver, (KC_LSFT, KC_ESC)); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Double tap gets us the second key */ + key_shift.press(); + EXPECT_REPORT(driver, (KC_LSFT)); + run_one_scan_loop(); + tap_key(key_esc_caps); + EXPECT_NO_REPORT(driver); + key_shift.release(); + key_esc_caps.press(); + EXPECT_REPORT(driver, (KC_LSFT, KC_CAPS)); + run_one_scan_loop(); + key_esc_caps.release(); + EXPECT_REPORT(driver, (KC_LSFT)); + run_one_scan_loop(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, DoubleTapInterrupted) { + TestDriver driver; + InSequence s; + auto key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)}; + auto regular_key = KeymapKey(0, 2, 0, KC_A); + + set_keymap({key_esc_caps, regular_key}); + + /* Interrupted double tap */ + tap_key(key_esc_caps); + regular_key.press(); + /* Immediate tap of the first key */ + EXPECT_REPORT(driver, (KC_ESC)); + EXPECT_EMPTY_REPORT(driver); + /* Followed by the interrupting key */ + EXPECT_REPORT(driver, (KC_A)); + run_one_scan_loop(); + regular_key.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Second tap after being interrupted acts as a single tap */ + key_esc_caps.press(); + run_one_scan_loop(); + key_esc_caps.release(); + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_ESC)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, DanceFn) { + TestDriver driver; + InSequence s; + auto key_egg = KeymapKey(0, 1, 0, TD(CT_EGG)); + + set_keymap({key_egg}); + + /* 99 taps do nothing */ + for (int i = 0; i < 99; i++) { + run_one_scan_loop(); + key_egg.press(); + run_one_scan_loop(); + key_egg.release(); + } + idle_for(TAPPING_TERM); + EXPECT_NO_REPORT(driver); + run_one_scan_loop(); + + /* 100 taps trigger the action */ + for (int i = 0; i < 100; i++) { + run_one_scan_loop(); + key_egg.press(); + run_one_scan_loop(); + key_egg.release(); + } + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_C)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* 250 taps act the same as 100 taps */ + /* Taps are counted in an uint8_t, so the count overflows after 255 taps */ + for (int i = 0; i < 250; i++) { + run_one_scan_loop(); + key_egg.press(); + run_one_scan_loop(); + key_egg.release(); + } + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_C)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, DanceFnAdvanced) { + TestDriver driver; + InSequence s; + auto key_flsh = KeymapKey(0, 1, 0, TD(CT_FLSH)); + + set_keymap({key_flsh}); + + /* Three taps don't trigger a reset */ + EXPECT_REPORT(driver, (KC_3)); + EXPECT_REPORT(driver, (KC_3, KC_2)); + EXPECT_REPORT(driver, (KC_3, KC_2, KC_1)); + for (int i = 0; i < 3; i++) { + run_one_scan_loop(); + key_flsh.press(); + run_one_scan_loop(); + key_flsh.release(); + } + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_3, KC_2)); + EXPECT_REPORT(driver, (KC_3)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Four taps trigger a reset */ + EXPECT_REPORT(driver, (KC_3)); + EXPECT_REPORT(driver, (KC_3, KC_2)); + EXPECT_REPORT(driver, (KC_3, KC_2, KC_1)); + EXPECT_REPORT(driver, (KC_2, KC_1)); + EXPECT_REPORT(driver, (KC_1)); + EXPECT_EMPTY_REPORT(driver); + for (int i = 0; i < 4; i++) { + run_one_scan_loop(); + key_flsh.press(); + run_one_scan_loop(); + key_flsh.release(); + } + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_R)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, TapHold) { + TestDriver driver; + InSequence s; + auto key_cln = KeymapKey{0, 1, 0, TD(CT_CLN)}; + + set_keymap({key_cln}); + + /* Short taps fire on release */ + key_cln.press(); + run_one_scan_loop(); + key_cln.release(); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_REPORT(driver, (KC_LSFT, KC_SCLN)); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Holds immediate following a tap apply to the tap key */ + key_cln.press(); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_REPORT(driver, (KC_LSFT, KC_SCLN)); + idle_for(TAPPING_TERM * 2); + key_cln.release(); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Holds trigger the hold key */ + key_cln.press(); + idle_for(TAPPING_TERM); + run_one_scan_loop(); + EXPECT_REPORT(driver, (KC_SCLN)); + run_one_scan_loop(); + key_cln.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, QuadFunction) { + TestDriver driver; + InSequence s; + auto key_quad = KeymapKey{0, 1, 0, TD(X_CTL)}; + auto regular_key = KeymapKey(0, 2, 0, KC_A); + + set_keymap({key_quad, regular_key}); + + /* Single tap */ + key_quad.press(); + run_one_scan_loop(); + key_quad.release(); + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_X)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Single hold */ + key_quad.press(); + run_one_scan_loop(); + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_LCTL)); + run_one_scan_loop(); + key_quad.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Double tap */ + tap_key(key_quad); + key_quad.press(); + run_one_scan_loop(); + key_quad.release(); + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_ESC)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Double tap and hold */ + tap_key(key_quad); + key_quad.press(); + run_one_scan_loop(); + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_LALT)); + run_one_scan_loop(); + key_quad.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Double single tap */ + tap_key(key_quad); + tap_key(key_quad); + regular_key.press(); + EXPECT_REPORT(driver, (KC_X)); + EXPECT_EMPTY_REPORT(driver); + EXPECT_REPORT(driver, (KC_X)); + EXPECT_EMPTY_REPORT(driver); + EXPECT_REPORT(driver, (KC_A)); + run_one_scan_loop(); + regular_key.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} diff --git a/tests/tapdance/config.h b/tests/tapdance/config.h deleted file mode 100644 index 6aada3efd3..0000000000 --- a/tests/tapdance/config.h +++ /dev/null @@ -1,19 +0,0 @@ -/* Copyright 2022 Jouke Witteveen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "test_common.h" diff --git a/tests/tapdance/examples.c b/tests/tapdance/examples.c deleted file mode 100644 index 4a5be41b08..0000000000 --- a/tests/tapdance/examples.c +++ /dev/null @@ -1,199 +0,0 @@ -/* Copyright 2022 Jouke Witteveen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "quantum.h" -#include "examples.h" - -// Example code from the tap dance documentation, adapted for testing - -// clang-format off - -// Example 1 - -void dance_egg(qk_tap_dance_state_t *state, void *user_data) { - if (state->count >= 100) { - // SEND_STRING("Safety dance!"); - tap_code(KC_C); - reset_tap_dance(state); - } -} - - -// Example 2 - -void dance_flsh_each(qk_tap_dance_state_t *state, void *user_data) { - switch (state->count) { - case 1: - register_code(KC_3); - break; - case 2: - register_code(KC_2); - break; - case 3: - register_code(KC_1); - break; - case 4: - unregister_code(KC_3); - // wait_ms(50); - unregister_code(KC_2); - // wait_ms(50); - unregister_code(KC_1); - } -} - -void dance_flsh_finished(qk_tap_dance_state_t *state, void *user_data) { - if (state->count >= 4) { - // reset_keyboard(); - tap_code(KC_R); - } -} - -void dance_flsh_reset(qk_tap_dance_state_t *state, void *user_data) { - unregister_code(KC_1); - // wait_ms(50); - unregister_code(KC_2); - // wait_ms(50); - unregister_code(KC_3); -} - - -// Example 3 - -typedef struct { - uint16_t tap; - uint16_t hold; - uint16_t held; -} tap_dance_tap_hold_t; - -bool process_record_user(uint16_t keycode, keyrecord_t *record) { - qk_tap_dance_action_t *action; - - switch (keycode) { - case TD(CT_CLN): - action = &tap_dance_actions[TD_INDEX(keycode)]; - if (!record->event.pressed && action->state.count && !action->state.finished) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; - tap_code16(tap_hold->tap); - } - } - return true; -} - -void tap_dance_tap_hold_finished(qk_tap_dance_state_t *state, void *user_data) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; - - if (state->pressed) { - if (state->count == 1 -#ifndef PERMISSIVE_HOLD - && !state->interrupted -#endif - ) { - register_code16(tap_hold->hold); - tap_hold->held = tap_hold->hold; - } else { - register_code16(tap_hold->tap); - tap_hold->held = tap_hold->tap; - } - } -} - -void tap_dance_tap_hold_reset(qk_tap_dance_state_t *state, void *user_data) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; - - if (tap_hold->held) { - unregister_code16(tap_hold->held); - tap_hold->held = 0; - } -} - -#define ACTION_TAP_DANCE_TAP_HOLD(tap, hold) \ - { .fn = {NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, .user_data = (void *)&((tap_dance_tap_hold_t){tap, hold, 0}), } - - -// Example 4 - -typedef enum { - TD_NONE, - TD_UNKNOWN, - TD_SINGLE_TAP, - TD_SINGLE_HOLD, - TD_DOUBLE_TAP, - TD_DOUBLE_HOLD, - TD_DOUBLE_SINGLE_TAP, - TD_TRIPLE_TAP, - TD_TRIPLE_HOLD -} td_state_t; - -typedef struct { - bool is_press_action; - td_state_t state; -} td_tap_t; - -td_state_t cur_dance(qk_tap_dance_state_t *state) { - if (state->count == 1) { - if (state->interrupted || !state->pressed) return TD_SINGLE_TAP; - else return TD_SINGLE_HOLD; - } else if (state->count == 2) { - if (state->interrupted) return TD_DOUBLE_SINGLE_TAP; - else if (state->pressed) return TD_DOUBLE_HOLD; - else return TD_DOUBLE_TAP; - } - - if (state->count == 3) { - if (state->interrupted || !state->pressed) return TD_TRIPLE_TAP; - else return TD_TRIPLE_HOLD; - } else return TD_UNKNOWN; -} - -static td_tap_t xtap_state = { - .is_press_action = true, - .state = TD_NONE -}; - -void x_finished(qk_tap_dance_state_t *state, void *user_data) { - xtap_state.state = cur_dance(state); - switch (xtap_state.state) { - case TD_SINGLE_TAP: register_code(KC_X); break; - case TD_SINGLE_HOLD: register_code(KC_LCTL); break; - case TD_DOUBLE_TAP: register_code(KC_ESC); break; - case TD_DOUBLE_HOLD: register_code(KC_LALT); break; - case TD_DOUBLE_SINGLE_TAP: tap_code(KC_X); register_code(KC_X); - default: break; // Not present in documentation - } -} - -void x_reset(qk_tap_dance_state_t *state, void *user_data) { - switch (xtap_state.state) { - case TD_SINGLE_TAP: unregister_code(KC_X); break; - case TD_SINGLE_HOLD: unregister_code(KC_LCTL); break; - case TD_DOUBLE_TAP: unregister_code(KC_ESC); break; - case TD_DOUBLE_HOLD: unregister_code(KC_LALT); - case TD_DOUBLE_SINGLE_TAP: unregister_code(KC_X); - default: break; // Not present in documentation - } - xtap_state.state = TD_NONE; -} - - -qk_tap_dance_action_t tap_dance_actions[] = { - [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS), - [CT_EGG] = ACTION_TAP_DANCE_FN(dance_egg), - [CT_FLSH] = ACTION_TAP_DANCE_FN_ADVANCED(dance_flsh_each, dance_flsh_finished, dance_flsh_reset), - [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(KC_COLN, KC_SCLN), - [X_CTL] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished, x_reset) -}; - -// clang-format on diff --git a/tests/tapdance/examples.h b/tests/tapdance/examples.h deleted file mode 100644 index 2622af6b2f..0000000000 --- a/tests/tapdance/examples.h +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright 2022 Jouke Witteveen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -enum { - TD_ESC_CAPS, - CT_EGG, - CT_FLSH, - CT_CLN, - X_CTL, -}; - -#ifdef __cplusplus -} -#endif diff --git a/tests/tapdance/test.mk b/tests/tapdance/test.mk deleted file mode 100644 index 041d9b4dc9..0000000000 --- a/tests/tapdance/test.mk +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2022 Jouke Witteveen -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# -------------------------------------------------------------------------------- -# Keep this file, even if it is empty, as a marker that this folder contains tests -# -------------------------------------------------------------------------------- - -TAP_DANCE_ENABLE = yes - -SRC += examples.c diff --git a/tests/tapdance/test_examples.cpp b/tests/tapdance/test_examples.cpp deleted file mode 100644 index e67e6cb907..0000000000 --- a/tests/tapdance/test_examples.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/* Copyright 2022 Jouke Witteveen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "keyboard_report_util.hpp" -#include "keycode.h" -#include "test_common.hpp" -#include "action_tapping.h" -#include "test_keymap_key.hpp" -#include "examples.h" - -using testing::_; -using testing::InSequence; - -class TapDance : public TestFixture {}; - -TEST_F(TapDance, DoubleTap) { - TestDriver driver; - InSequence s; - auto key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)}; - - set_keymap({key_esc_caps}); - - /* The tap dance key does nothing on the first press */ - key_esc_caps.press(); - run_one_scan_loop(); - key_esc_caps.release(); - EXPECT_NO_REPORT(driver); - - /* We get the key press and the release on timeout */ - idle_for(TAPPING_TERM); - EXPECT_REPORT(driver, (KC_ESC)); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); - - /* Double tap gets us the second key */ - tap_key(key_esc_caps); - EXPECT_NO_REPORT(driver); - key_esc_caps.press(); - EXPECT_REPORT(driver, (KC_CAPS)); - run_one_scan_loop(); - key_esc_caps.release(); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); -} - -TEST_F(TapDance, DoubleTapWithMod) { - TestDriver driver; - InSequence s; - auto key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)}; - auto key_shift = KeymapKey{0, 2, 0, KC_LSFT}; - - set_keymap({key_esc_caps, key_shift}); - - /* The tap dance key does nothing on the first press */ - key_shift.press(); - EXPECT_REPORT(driver, (KC_LSFT)); - run_one_scan_loop(); - key_esc_caps.press(); - run_one_scan_loop(); - - key_esc_caps.release(); - key_shift.release(); - EXPECT_EMPTY_REPORT(driver); - - /* We get the key press and the release */ - idle_for(TAPPING_TERM); - EXPECT_REPORT(driver, (KC_LSFT)); - EXPECT_REPORT(driver, (KC_LSFT, KC_ESC)); - EXPECT_REPORT(driver, (KC_LSFT)); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); - - /* Double tap gets us the second key */ - key_shift.press(); - EXPECT_REPORT(driver, (KC_LSFT)); - run_one_scan_loop(); - tap_key(key_esc_caps); - EXPECT_NO_REPORT(driver); - key_shift.release(); - key_esc_caps.press(); - EXPECT_REPORT(driver, (KC_LSFT, KC_CAPS)); - run_one_scan_loop(); - key_esc_caps.release(); - EXPECT_REPORT(driver, (KC_LSFT)); - run_one_scan_loop(); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); -} - -TEST_F(TapDance, DoubleTapInterrupted) { - TestDriver driver; - InSequence s; - auto key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)}; - auto regular_key = KeymapKey(0, 2, 0, KC_A); - - set_keymap({key_esc_caps, regular_key}); - - /* Interrupted double tap */ - tap_key(key_esc_caps); - regular_key.press(); - /* Immediate tap of the first key */ - EXPECT_REPORT(driver, (KC_ESC)); - EXPECT_EMPTY_REPORT(driver); - /* Followed by the interrupting key */ - EXPECT_REPORT(driver, (KC_A)); - run_one_scan_loop(); - regular_key.release(); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); - - /* Second tap after being interrupted acts as a single tap */ - key_esc_caps.press(); - run_one_scan_loop(); - key_esc_caps.release(); - idle_for(TAPPING_TERM); - EXPECT_REPORT(driver, (KC_ESC)); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); -} - -TEST_F(TapDance, DanceFn) { - TestDriver driver; - InSequence s; - auto key_egg = KeymapKey(0, 1, 0, TD(CT_EGG)); - - set_keymap({key_egg}); - - /* 99 taps do nothing */ - for (int i = 0; i < 99; i++) { - run_one_scan_loop(); - key_egg.press(); - run_one_scan_loop(); - key_egg.release(); - } - idle_for(TAPPING_TERM); - EXPECT_NO_REPORT(driver); - run_one_scan_loop(); - - /* 100 taps trigger the action */ - for (int i = 0; i < 100; i++) { - run_one_scan_loop(); - key_egg.press(); - run_one_scan_loop(); - key_egg.release(); - } - idle_for(TAPPING_TERM); - EXPECT_REPORT(driver, (KC_C)); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); - - /* 250 taps act the same as 100 taps */ - /* Taps are counted in an uint8_t, so the count overflows after 255 taps */ - for (int i = 0; i < 250; i++) { - run_one_scan_loop(); - key_egg.press(); - run_one_scan_loop(); - key_egg.release(); - } - idle_for(TAPPING_TERM); - EXPECT_REPORT(driver, (KC_C)); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); -} - -TEST_F(TapDance, DanceFnAdvanced) { - TestDriver driver; - InSequence s; - auto key_flsh = KeymapKey(0, 1, 0, TD(CT_FLSH)); - - set_keymap({key_flsh}); - - /* Three taps don't trigger a reset */ - EXPECT_REPORT(driver, (KC_3)); - EXPECT_REPORT(driver, (KC_3, KC_2)); - EXPECT_REPORT(driver, (KC_3, KC_2, KC_1)); - for (int i = 0; i < 3; i++) { - run_one_scan_loop(); - key_flsh.press(); - run_one_scan_loop(); - key_flsh.release(); - } - idle_for(TAPPING_TERM); - EXPECT_REPORT(driver, (KC_3, KC_2)); - EXPECT_REPORT(driver, (KC_3)); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); - - /* Four taps trigger a reset */ - EXPECT_REPORT(driver, (KC_3)); - EXPECT_REPORT(driver, (KC_3, KC_2)); - EXPECT_REPORT(driver, (KC_3, KC_2, KC_1)); - EXPECT_REPORT(driver, (KC_2, KC_1)); - EXPECT_REPORT(driver, (KC_1)); - EXPECT_EMPTY_REPORT(driver); - for (int i = 0; i < 4; i++) { - run_one_scan_loop(); - key_flsh.press(); - run_one_scan_loop(); - key_flsh.release(); - } - idle_for(TAPPING_TERM); - EXPECT_REPORT(driver, (KC_R)); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); -} - -TEST_F(TapDance, TapHold) { - TestDriver driver; - InSequence s; - auto key_cln = KeymapKey{0, 1, 0, TD(CT_CLN)}; - - set_keymap({key_cln}); - - /* Short taps fire on release */ - key_cln.press(); - run_one_scan_loop(); - key_cln.release(); - EXPECT_REPORT(driver, (KC_LSFT)); - EXPECT_REPORT(driver, (KC_LSFT, KC_SCLN)); - EXPECT_REPORT(driver, (KC_LSFT)); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); - - /* Holds immediate following a tap apply to the tap key */ - key_cln.press(); - EXPECT_REPORT(driver, (KC_LSFT)); - EXPECT_REPORT(driver, (KC_LSFT, KC_SCLN)); - idle_for(TAPPING_TERM * 2); - key_cln.release(); - EXPECT_REPORT(driver, (KC_LSFT)); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); - - /* Holds trigger the hold key */ - key_cln.press(); - idle_for(TAPPING_TERM); - run_one_scan_loop(); - EXPECT_REPORT(driver, (KC_SCLN)); - run_one_scan_loop(); - key_cln.release(); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); -} - -TEST_F(TapDance, QuadFunction) { - TestDriver driver; - InSequence s; - auto key_quad = KeymapKey{0, 1, 0, TD(X_CTL)}; - auto regular_key = KeymapKey(0, 2, 0, KC_A); - - set_keymap({key_quad, regular_key}); - - /* Single tap */ - key_quad.press(); - run_one_scan_loop(); - key_quad.release(); - idle_for(TAPPING_TERM); - EXPECT_REPORT(driver, (KC_X)); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); - - /* Single hold */ - key_quad.press(); - run_one_scan_loop(); - idle_for(TAPPING_TERM); - EXPECT_REPORT(driver, (KC_LCTL)); - run_one_scan_loop(); - key_quad.release(); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); - - /* Double tap */ - tap_key(key_quad); - key_quad.press(); - run_one_scan_loop(); - key_quad.release(); - idle_for(TAPPING_TERM); - EXPECT_REPORT(driver, (KC_ESC)); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); - - /* Double tap and hold */ - tap_key(key_quad); - key_quad.press(); - run_one_scan_loop(); - idle_for(TAPPING_TERM); - EXPECT_REPORT(driver, (KC_LALT)); - run_one_scan_loop(); - key_quad.release(); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); - - /* Double single tap */ - tap_key(key_quad); - tap_key(key_quad); - regular_key.press(); - EXPECT_REPORT(driver, (KC_X)); - EXPECT_EMPTY_REPORT(driver); - EXPECT_REPORT(driver, (KC_X)); - EXPECT_EMPTY_REPORT(driver); - EXPECT_REPORT(driver, (KC_A)); - run_one_scan_loop(); - regular_key.release(); - EXPECT_EMPTY_REPORT(driver); - run_one_scan_loop(); -} -- cgit v1.2.3