diff options
Diffstat (limited to 'tests')
31 files changed, 2351 insertions, 977 deletions
| diff --git a/tests/basic/test_keycode_util.cpp b/tests/basic/test_keycode_util.cpp deleted file mode 100644 index 693334676e..0000000000 --- a/tests/basic/test_keycode_util.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2022 Stefan Kerkmann -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "test_common.hpp" - -class KeycodeToIdentifierSuite : public ::testing::TestWithParam<std::pair<std::uint16_t, std::string>> {}; - -TEST_P(KeycodeToIdentifierSuite, ConversionTests) { -    ASSERT_EQ(get_keycode_identifier_or_default(GetParam().first), GetParam().second); -} - -INSTANTIATE_TEST_CASE_P(ConversionTestsP, KeycodeToIdentifierSuite, -                        // clang-format off -::testing::Values( -    // Goto layer -    std::make_pair(TO(0), "TO(0)"), -    std::make_pair(TO(0x1F), "TO(31)"), -    // Momentary switch layer -    std::make_pair(MO(0), "MO(0)"), -    std::make_pair(MO(0x1F), "MO(31)"), -    // Set default layer -    std::make_pair(DF(0), "DF(0)"), -    std::make_pair(DF(0x1F), "DF(31)"), -    // Toggle layer -    std::make_pair(TG(0), "TG(0)"), -    std::make_pair(TG(0x1F), "TG(31)"), -    // One-shot layer -    std::make_pair(OSL(0), "OSL(0)"), -    std::make_pair(OSL(0x1F), "OSL(31)"), -    // One-shot mod -    std::make_pair(OSM(MOD_LSFT), "OSM(MOD_LSFT)"), -    std::make_pair(OSM(MOD_LSFT | MOD_LCTL), "OSM(MOD_LCTL | MOD_LSFT)"), -    // Layer Mod -    std::make_pair(LM(0, MOD_LSFT), "LM(0, MOD_LSFT)"), -    std::make_pair(LM(0xF, MOD_LSFT), "LM(15, MOD_LSFT)"), -    std::make_pair(LM(0xF, MOD_LSFT | MOD_LCTL), "LM(15, MOD_LCTL | MOD_LSFT)"), -    // Layer tap toggle -    std::make_pair(TT(0), "TT(0)"), -    std::make_pair(TT(0x1F), "TT(31)"), -    // Layer tap -    std::make_pair(LT(0, KC_A), "LT(0, KC_A)"), -    std::make_pair(LT(0xF, KC_SPACE), "LT(15, KC_SPACE)"), -    std::make_pair(LT(1, KC_SPC), "LT(1, KC_SPACE)"), -    // Mod tap -    std::make_pair(MT(MOD_LCTL, KC_A), "MT(MOD_LCTL, KC_A)"), -    std::make_pair(MT(MOD_LCTL | MOD_LSFT, KC_A), "MT(MOD_LCTL | MOD_LSFT, KC_A)"), -    std::make_pair(ALT_T(KC_TAB), "MT(MOD_LALT, KC_TAB)"), -    // Mods -    std::make_pair(LCTL(KC_A), "QK_MODS(KC_A, QK_LCTL)"), -    std::make_pair(HYPR(KC_SPACE), "QK_MODS(KC_SPACE, QK_LCTL | QK_LSFT | QK_LALT | QK_LGUI)") -)); -// clang-format on diff --git a/tests/basic/test_one_shot_keys.cpp b/tests/basic/test_one_shot_keys.cpp index 92db52f811..4784c86694 100644 --- a/tests/basic/test_one_shot_keys.cpp +++ b/tests/basic/test_one_shot_keys.cpp @@ -686,4 +686,4 @@ TEST_F(OneShot, OSLWithLongModTapKeyAndRegularKey) {      regular_key.release();      run_one_scan_loop();      VERIFY_AND_CLEAR(driver); -}
\ No newline at end of file +} diff --git a/tests/caps_word/test.mk b/tests/caps_word/test.mk index 2509b01858..6d5664aa05 100644 --- a/tests/caps_word/test.mk +++ b/tests/caps_word/test.mk @@ -15,5 +15,7 @@  CAPS_WORD_ENABLE = yes  COMMAND_ENABLE = no +LAYER_LOCK_ENABLE = yes  SPACE_CADET_ENABLE = yes +TRI_LAYER_ENABLE = yes diff --git a/tests/caps_word/test_caps_word.cpp b/tests/caps_word/test_caps_word.cpp index 28d86e9324..4b58790915 100644 --- a/tests/caps_word/test_caps_word.cpp +++ b/tests/caps_word/test_caps_word.cpp @@ -156,21 +156,22 @@ TEST_F(CapsWord, IdleTimeout) {      // Turn on Caps Word and tap "A".      caps_word_on();      tap_key(key_a); -      VERIFY_AND_CLEAR(driver); +    EXPECT_EMPTY_REPORT(driver);      idle_for(CAPS_WORD_IDLE_TIMEOUT);      run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver);      // Caps Word should be off and mods should be clear.      EXPECT_EQ(is_caps_word_on(), false);      EXPECT_EQ(get_mods() | get_weak_mods(), 0); -    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());      // Expect unshifted "A".      EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver);      tap_key(key_a); - +    run_one_scan_loop();      VERIFY_AND_CLEAR(driver);  } @@ -244,6 +245,7 @@ TEST_F(CapsWord, ShiftsAltGrSymbols) {      // clang-format off      EXPECT_CALL(driver, send_keyboard_mock(AnyOf(                  KeyboardReport(), +                KeyboardReport(KC_LSFT),                  KeyboardReport(KC_RALT),                  KeyboardReport(KC_LSFT, KC_RALT))))          .Times(AnyNumber()); @@ -259,6 +261,9 @@ TEST_F(CapsWord, ShiftsAltGrSymbols) {      tap_key(key_a);      run_one_scan_loop();      key_altgr.release(); +    run_one_scan_loop(); + +    idle_for(CAPS_WORD_IDLE_TIMEOUT);      VERIFY_AND_CLEAR(driver);  } @@ -274,6 +279,7 @@ TEST_F(CapsWord, ShiftsModTapAltGrSymbols) {      // clang-format off      EXPECT_CALL(driver, send_keyboard_mock(AnyOf(                  KeyboardReport(), +                KeyboardReport(KC_LSFT),                  KeyboardReport(KC_RALT),                  KeyboardReport(KC_LSFT, KC_RALT))))          .Times(AnyNumber()); @@ -289,8 +295,11 @@ TEST_F(CapsWord, ShiftsModTapAltGrSymbols) {      tap_key(key_a);      run_one_scan_loop();      key_altgr_t.release(); - +    run_one_scan_loop();      EXPECT_TRUE(is_caps_word_on()); + +    idle_for(CAPS_WORD_IDLE_TIMEOUT); +      VERIFY_AND_CLEAR(driver);  } @@ -535,7 +544,11 @@ TEST_P(CapsWordDoubleTapShift, Activation) {      // machine at this point. This due to imperfect test isolation which can't      // reset the caps word double shift timer on test case setup.      idle_for(CAPS_WORD_IDLE_TIMEOUT); + +    EXPECT_REPORT(driver, (KC_ESC)); +    EXPECT_EMPTY_REPORT(driver);      tap_key(esc); +    VERIFY_AND_CLEAR(driver);  }  // Double tap doesn't count if another key is pressed between the taps. @@ -589,6 +602,7 @@ TEST_P(CapsWordDoubleTapShift, SlowTaps) {      EXPECT_EQ(is_caps_word_on(), false); // Caps Word is still off.      clear_oneshot_mods(); +    send_keyboard_report();      VERIFY_AND_CLEAR(driver);  } @@ -626,7 +640,7 @@ TEST_F(CapsWord, IgnoresOSLHold) {      run_one_scan_loop();      tap_key(key_b);      key_osl.release(); -    run_one_scan_loop(); +    idle_for(CAPS_WORD_IDLE_TIMEOUT + 1);      VERIFY_AND_CLEAR(driver);  } @@ -645,15 +659,39 @@ TEST_F(CapsWord, IgnoresOSLTap) {                  KeyboardReport(),                  KeyboardReport(KC_LSFT))))          .Times(AnyNumber()); +    // clang-format on      EXPECT_REPORT(driver, (KC_LSFT, KC_B));      caps_word_on();      tap_key(key_osl);      tap_key(key_b); -    run_one_scan_loop(); +    idle_for(CAPS_WORD_IDLE_TIMEOUT); + +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(CapsWord, IgnoresLayerLockKey) { +    TestDriver driver; +    KeymapKey  key_llock(0, 1, 0, QK_LAYER_LOCK); +    KeymapKey  key_b(0, 0, 0, KC_B); +    set_keymap({key_llock, key_b}); + +    // Allow any number of reports with no keys or only modifiers. +    // clang-format off +    EXPECT_CALL(driver, send_keyboard_mock(AnyOf( +                KeyboardReport(), +                KeyboardReport(KC_LSFT)))) +        .Times(AnyNumber()); +    // clang-format on + +    EXPECT_REPORT(driver, (KC_LSFT, KC_B)); +    caps_word_on(); + +    tap_key(key_llock); +    tap_key(key_b); +    idle_for(CAPS_WORD_IDLE_TIMEOUT);      VERIFY_AND_CLEAR(driver);  } -// clang-format on  } // namespace diff --git a/tests/combo/combo_repress/test_combo.cpp b/tests/combo/combo_repress/test_combo.cpp index 1488d5c1bd..b1a2d36cb3 100644 --- a/tests/combo/combo_repress/test_combo.cpp +++ b/tests/combo/combo_repress/test_combo.cpp @@ -2,9 +2,9 @@  // SPDX-License-Identifier: GPL-2.0-or-later  #include "keyboard_report_util.hpp" -#include "quantum.h"  #include "keycode.h"  #include "test_common.h" +#include "test_common.hpp"  #include "test_driver.hpp"  #include "test_fixture.hpp"  #include "test_keymap_key.hpp" diff --git a/tests/combo/test_combo.cpp b/tests/combo/test_combo.cpp index ac852f9d16..d78ec55990 100644 --- a/tests/combo/test_combo.cpp +++ b/tests/combo/test_combo.cpp @@ -3,7 +3,6 @@  // SPDX-License-Identifier: GPL-2.0-or-later  #include "keyboard_report_util.hpp" -#include "quantum.h"  #include "keycode.h"  #include "test_common.h"  #include "test_driver.hpp" diff --git a/tests/keycode_string/config.h b/tests/keycode_string/config.h new file mode 100644 index 0000000000..7fc76d7c2e --- /dev/null +++ b/tests/keycode_string/config.h @@ -0,0 +1,19 @@ +/* Copyright 2017 Fred Sundvik + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "test_common.h" diff --git a/tests/keycode_string/test.mk b/tests/keycode_string/test.mk new file mode 100644 index 0000000000..aa255a1b6b --- /dev/null +++ b/tests/keycode_string/test.mk @@ -0,0 +1,22 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +#     https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +EXTRAKEY_ENABLE = yes +KEYCODE_STRING_ENABLE = yes +KEY_LOCK_ENABLE = yes +MAGIC_ENABLE = yes +MOUSEKEY_ENABLE = yes +PROGRAMMABLE_BUTTON_ENABLE = yes +SECURE_ENABLE = yes +SWAP_HANDS_ENABLE = yes diff --git a/tests/keycode_string/test_keycode_string.cpp b/tests/keycode_string/test_keycode_string.cpp new file mode 100644 index 0000000000..e1dec70e7a --- /dev/null +++ b/tests/keycode_string/test_keycode_string.cpp @@ -0,0 +1,153 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <iostream> + +#include "test_common.hpp" + +enum { +    MYMACRO1 = SAFE_RANGE, +    MYMACRO2, +}; + +// clang-format off +extern "C" { + +KEYCODE_STRING_NAMES_KB( +    KEYCODE_STRING_NAME(MYMACRO1), +); + +KEYCODE_STRING_NAMES_USER( +    KEYCODE_STRING_NAME(MYMACRO2), +    KEYCODE_STRING_NAME(KC_EXLM), +); + +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +  {{9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, +  {{9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, +  {{9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, +  {{9, 3}, {8, 3}, {7, 3}, {6, 3}, {5, 3}, {4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}}, +}; + +} // extern "C" +// clang-format on + +class KeycodeStringTest : public TestFixture {}; + +TEST_F(KeycodeStringTest, get_keycode_string) { +    struct TestParams { +        uint16_t    keycode; +        std::string expected; +    }; +    for (const auto [keycode, expected] : std::vector<TestParams>({ +             {KC_TRNS, "KC_TRNS"}, +             {KC_ESC, "KC_ESC"}, +             {KC_A, "KC_A"}, +             {KC_Z, "KC_Z"}, +             {KC_0, "KC_0"}, +             {KC_9, "KC_9"}, +             {KC_KP_0, "KC_KP_0"}, +             {KC_KP_9, "KC_KP_9"}, +             {KC_LBRC, "KC_LBRC"}, +             {KC_NUHS, "KC_NUHS"}, +             {KC_NUBS, "KC_NUBS"}, +             {KC_CAPS, "KC_CAPS"}, +             {DB_TOGG, "DB_TOGG"}, +             {KC_LCTL, "KC_LCTL"}, +             {KC_LSFT, "KC_LSFT"}, +             {KC_RALT, "KC_RALT"}, +             {KC_RGUI, "KC_RGUI"}, +             {KC_UP, "KC_UP"}, +             {KC_HYPR, "KC_HYPR"}, +             {KC_MEH, "KC_MEH"}, +             // F1-F24 keycodes. +             {KC_F1, "KC_F1"}, +             {KC_F12, "KC_F12"}, +             {KC_F13, "KC_F13"}, +             {KC_F24, "KC_F24"}, +             // Macro keycodes. +             {MC_0, "MC_0"}, +             {MC_31, "MC_31"}, +             // Keyboard range keycodes. +             {QK_KB_0, "QK_KB_0"}, +             {QK_KB_31, "QK_KB_31"}, +             // User range keycodes. +             {QK_USER_2, "QK_USER_2"}, +             {QK_USER_31, "QK_USER_31"}, +             // Modified keycodes. +             {KC_COLN, "S(KC_SCLN)"}, +             {C(KC_PGUP), "C(KC_PGUP)"}, +             {RALT(KC_BSPC), "RALT(KC_BSPC)"}, +             // One-shot mods. +             {OSM(MOD_LSFT), "OSM(MOD_LSFT)"}, +             {OSM(MOD_RGUI), "OSM(MOD_RGUI)"}, +             {OSM(MOD_RCTL | MOD_RGUI), "OSM(0x19)"}, +             // Layer switch keycodes. +             {DF(2), "DF(2)"}, +             {PDF(12), "PDF(12)"}, +             {MO(3), "MO(3)"}, +             {TO(0), "TO(0)"}, +             {TT(1), "TT(1)"}, +             {TG(3), "TG(3)"}, +             {OSL(3), "OSL(3)"}, +             {LM(3, MOD_RALT), "LM(3,MOD_RALT)"}, +             {LT(15, KC_QUOT), "LT(15,KC_QUOT)"}, +             // Tap dance keycodes. +             {TD(0), "TD(0)"}, +             {TD(31), "TD(31)"}, +             // Mod-tap keycodes. +             {LSFT_T(KC_ENT), "LSFT_T(KC_ENT)"}, +             {RCTL_T(KC_RGHT), "RCTL_T(KC_RGHT)"}, +             {HYPR_T(KC_GRV), "HYPR_T(KC_GRV)"}, +             {MEH_T(KC_EQL), "MEH_T(KC_EQL)"}, +             {RSA_T(KC_LBRC), "MT(0x16,KC_LBRC)"}, +             // Extrakey keycodes. +             {KC_WBAK, "KC_WBAK"}, +             {KC_WFWD, "KC_WFWD"}, +             {KC_WREF, "KC_WREF"}, +             {KC_VOLU, "KC_VOLU"}, +             {KC_VOLD, "KC_VOLD"}, +             // Mouse Key keycodes. +             {MS_LEFT, "MS_LEFT"}, +             {MS_RGHT, "MS_RGHT"}, +             {MS_UP, "MS_UP"}, +             {MS_WHLU, "MS_WHLU"}, +             {MS_WHLD, "MS_WHLD"}, +             {MS_BTN1, "MS_BTN1"}, +             {MS_BTN8, "MS_BTN8"}, +             // Swap Hands keycodes. +             {SH_MON, "SH_MON"}, +             {SH_TOGG, "SH_TOGG"}, +             {SH_T(KC_PSCR), "SH_T(KC_PSCR)"}, +             // Secure keycodes. +             {SE_LOCK, "SE_LOCK"}, +             {SE_UNLK, "SE_UNLK"}, +             {SE_TOGG, "SE_TOGG"}, +             {SE_REQ, "SE_REQ"}, +             // Programmable button keycodes. +             {PB_1, "PB_1"}, +             {PB_32, "PB_32"}, +             // Magic button keycodes. +             {QK_MAGIC + 7, "QK_MAGIC+7"}, +             // Quantum keycodes. +             {QK_LOCK, "QK_LOCK"}, +             {QK_QUANTUM + 7, "QK_QUANTUM+7"}, +             // Custom keycode names. +             {MYMACRO1, "MYMACRO1"}, +             {MYMACRO2, "MYMACRO2"}, +             {KC_EXLM, "KC_EXLM"}, +         })) { +        EXPECT_EQ(get_keycode_string(keycode), expected) << "where keycode = 0x" << std::hex << keycode; +    } +} diff --git a/tests/no_tapping/no_action_tapping/test.mk b/tests/no_tapping/no_action_tapping/test.mk index 29690d1adf..6ec384609c 100644 --- a/tests/no_tapping/no_action_tapping/test.mk +++ b/tests/no_tapping/no_action_tapping/test.mk @@ -15,4 +15,4 @@  # --------------------------------------------------------------------------------  # Keep this file, even if it is empty, as a marker that this folder contains tests -# --------------------------------------------------------------------------------
\ No newline at end of file +# -------------------------------------------------------------------------------- diff --git a/tests/no_tapping/no_mod_tap_mods/test.mk b/tests/no_tapping/no_mod_tap_mods/test.mk index 29690d1adf..6ec384609c 100644 --- a/tests/no_tapping/no_mod_tap_mods/test.mk +++ b/tests/no_tapping/no_mod_tap_mods/test.mk @@ -15,4 +15,4 @@  # --------------------------------------------------------------------------------  # Keep this file, even if it is empty, as a marker that this folder contains tests -# --------------------------------------------------------------------------------
\ No newline at end of file +# -------------------------------------------------------------------------------- diff --git a/tests/repeat_key/alt_repeat_key/test_alt_repeat_key.cpp b/tests/repeat_key/alt_repeat_key/test_alt_repeat_key.cpp index ae525acb45..e2eed3dbb6 100644 --- a/tests/repeat_key/alt_repeat_key/test_alt_repeat_key.cpp +++ b/tests/repeat_key/alt_repeat_key/test_alt_repeat_key.cpp @@ -210,7 +210,7 @@ TEST_F(AltRepeatKey, GetAltRepeatKeyKeycode) {            {MO(1), 0, KC_NO},               // clang-format on           })) { -        SCOPED_TRACE(std::string("Input keycode: ") + get_keycode_identifier_or_default(params.keycode)); +        SCOPED_TRACE(std::string("Input keycode: ") + get_keycode_string(params.keycode));          set_last_keycode(params.keycode);          set_last_mods(params.mods); diff --git a/tests/repeat_key/test.mk b/tests/repeat_key/test.mk index aec8ff3bfb..186207ffc2 100644 --- a/tests/repeat_key/test.mk +++ b/tests/repeat_key/test.mk @@ -16,3 +16,4 @@  REPEAT_KEY_ENABLE = yes  AUTO_SHIFT_ENABLE = yes +LAYER_LOCK_ENABLE = yes diff --git a/tests/repeat_key/test_repeat_key.cpp b/tests/repeat_key/test_repeat_key.cpp index eee44fc104..ed5d618761 100644 --- a/tests/repeat_key/test_repeat_key.cpp +++ b/tests/repeat_key/test_repeat_key.cpp @@ -751,4 +751,37 @@ TEST_F(RepeatKey, RepeatKeyInvoke) {      testing::Mock::VerifyAndClearExpectations(&driver);  } +// Check that mods and Layer Lock are not remembered. +TEST_F(RepeatKey, IgnoredKeys) { +    TestDriver driver; +    KeymapKey  regular_key(0, 0, 0, KC_A); +    KeymapKey  key_repeat(0, 1, 0, QK_REP); +    KeymapKey  key_lsft(0, 2, 0, KC_LSFT); +    KeymapKey  key_lctl(0, 3, 0, KC_LCTL); +    KeymapKey  key_llck(0, 4, 0, QK_LAYER_LOCK); +    set_keymap({regular_key, key_repeat, key_lsft, key_lctl, key_llck}); + +    // Allow any number of empty reports. +    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); +    { +        InSequence seq; +        EXPECT_REPORT(driver, (KC_A)); +        EXPECT_REPORT(driver, (KC_LSFT)); +        EXPECT_REPORT(driver, (KC_LCTL)); +        EXPECT_REPORT(driver, (KC_A)); +        EXPECT_REPORT(driver, (KC_A)); +    } + +    tap_key(regular_key); // Taps the KC_A key. + +    // Tap Shift, Ctrl, and Layer Lock keys, which should not be remembered. +    tap_keys(key_lsft, key_lctl, key_llck); +    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_A); + +    // Tapping the Repeat Key should still reproduce KC_A. +    tap_keys(key_repeat, key_repeat); + +    testing::Mock::VerifyAndClearExpectations(&driver); +} +  } // namespace diff --git a/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/config.h b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/config.h new file mode 100644 index 0000000000..9267b94bec --- /dev/null +++ b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/config.h @@ -0,0 +1,23 @@ +/* Copyright 2022 Vladislav Kucheriavykh + * Copyright 2024-2025 Google LLC + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "test_common.h" +#define CHORDAL_HOLD +#define PERMISSIVE_HOLD +#define FLOW_TAP_TERM 150 diff --git a/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test.mk b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test.mk new file mode 100644 index 0000000000..2b049cea3b --- /dev/null +++ b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test.mk @@ -0,0 +1,17 @@ +# Copyright 2022 Vladislav Kucheriavykh +# Copyright 2024 Google LLC +# +# 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 <http://www.gnu.org/licenses/>. + +INTROSPECTION_KEYMAP_C = test_keymap.c diff --git a/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_keymap.c b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_keymap.c new file mode 100644 index 0000000000..8a6a2c59b0 --- /dev/null +++ b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_keymap.c @@ -0,0 +1,22 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "quantum.h" + +const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = { +    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'}, +    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'}, +    {'*', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'}, +    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'}, +}; diff --git a/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_one_shot_keys.cpp b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_one_shot_keys.cpp new file mode 100644 index 0000000000..e48cba73e2 --- /dev/null +++ b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_one_shot_keys.cpp @@ -0,0 +1,174 @@ +/* Copyright 2021 Stefan Kerkmann + * Copyright 2024 Google LLC + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "action_util.h" +#include "keyboard_report_util.hpp" +#include "test_common.hpp" + +using testing::_; +using testing::InSequence; + +class OneShot : public TestFixture {}; +class OneShotParametrizedTestFixture : public ::testing::WithParamInterface<std::pair<KeymapKey, KeymapKey>>, public OneShot {}; + +TEST_P(OneShotParametrizedTestFixture, OSMWithAdditionalKeypress) { +    TestDriver driver; +    KeymapKey  osm_key     = GetParam().first; +    KeymapKey  regular_key = GetParam().second; + +    set_keymap({osm_key, regular_key}); + +    // Press and release OSM. +    EXPECT_NO_REPORT(driver); +    tap_key(osm_key); +    VERIFY_AND_CLEAR(driver); + +    // Press regular key. +    EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code)); +    regular_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release regular key. +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_P(OneShotParametrizedTestFixture, OSMAsRegularModifierWithAdditionalKeypress) { +    TestDriver driver; +    KeymapKey  osm_key     = GetParam().first; +    KeymapKey  regular_key = GetParam().second; + +    set_keymap({osm_key, regular_key}); + +    // Press OSM. +    EXPECT_NO_REPORT(driver); +    osm_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press regular key. +    EXPECT_NO_REPORT(driver); +    regular_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release regular key. +    EXPECT_REPORT(driver, (osm_key.report_code)).Times(2); +    EXPECT_REPORT(driver, (regular_key.report_code, osm_key.report_code)); +    regular_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release OSM. +    EXPECT_EMPTY_REPORT(driver); +    osm_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +// clang-format off + +INSTANTIATE_TEST_CASE_P( +    OneShotModifierTests, +    OneShotParametrizedTestFixture, +    ::testing::Values( +        // First is osm key, second is regular key. +        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT}, KeymapKey{0, 1, 1, KC_A}), +        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LCTL), KC_LCTL}, KeymapKey{0, 1, 1, KC_A}), +        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LALT), KC_LALT}, KeymapKey{0, 1, 1, KC_A}), +        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LGUI), KC_LGUI}, KeymapKey{0, 1, 1, KC_A}), +        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RCTL), KC_RCTL}, KeymapKey{0, 1, 1, KC_A}), +        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RSFT), KC_RSFT}, KeymapKey{0, 1, 1, KC_A}), +        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RALT), KC_RALT}, KeymapKey{0, 1, 1, KC_A}), +        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RGUI), KC_RGUI}, KeymapKey{0, 1, 1, KC_A}) +        )); +// clang-format on + +TEST_F(OneShot, OSLWithAdditionalKeypress) { +    TestDriver driver; +    InSequence s; +    KeymapKey  osl_key      = KeymapKey{0, 0, 0, OSL(1)}; +    KeymapKey  osl_key1     = KeymapKey{1, 0, 0, KC_X}; +    KeymapKey  regular_key0 = KeymapKey{0, 1, 0, KC_Y}; +    KeymapKey  regular_key1 = KeymapKey{1, 1, 0, KC_A}; + +    set_keymap({osl_key, osl_key1, regular_key0, regular_key1}); + +    // Press OSL key. +    EXPECT_NO_REPORT(driver); +    osl_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release OSL key. +    EXPECT_NO_REPORT(driver); +    osl_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press regular key. +    EXPECT_REPORT(driver, (regular_key1.report_code)); +    EXPECT_EMPTY_REPORT(driver); +    regular_key1.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release regular key. +    EXPECT_NO_REPORT(driver); +    regular_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(OneShot, OSLWithOsmAndAdditionalKeypress) { +    TestDriver driver; +    InSequence s; +    KeymapKey  osl_key     = KeymapKey{0, 0, 0, OSL(1)}; +    KeymapKey  osm_key     = KeymapKey{1, 1, 0, OSM(MOD_LSFT), KC_LSFT}; +    KeymapKey  regular_key = KeymapKey{1, 1, 1, KC_A}; +    KeymapKey  blank_key   = KeymapKey{1, 0, 0, KC_NO}; + +    set_keymap({osl_key, osm_key, regular_key, blank_key}); + +    // Press OSL key. +    EXPECT_NO_REPORT(driver); +    osl_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release OSL key. +    EXPECT_NO_REPORT(driver); +    osl_key.release(); +    run_one_scan_loop(); +    EXPECT_TRUE(layer_state_is(1)); +    VERIFY_AND_CLEAR(driver); + +    // Press and release OSM. +    EXPECT_NO_REPORT(driver); +    tap_key(osm_key); +    EXPECT_TRUE(layer_state_is(1)); +    VERIFY_AND_CLEAR(driver); + +    // Tap regular key. +    EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); +} diff --git a/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_tap_hold.cpp b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_tap_hold.cpp new file mode 100644 index 0000000000..05acb6f416 --- /dev/null +++ b/tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_tap_hold.cpp @@ -0,0 +1,911 @@ +// Copyright 2024-2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using testing::_; +using testing::InSequence; + +class ChordalHoldPermissiveHoldFlowTap : public TestFixture {}; + +TEST_F(ChordalHoldPermissiveHoldFlowTap, chordal_hold_handedness) { +    EXPECT_EQ(chordal_hold_handedness({.col = 0, .row = 0}), 'L'); +    EXPECT_EQ(chordal_hold_handedness({.col = MATRIX_COLS - 1, .row = 0}), 'R'); +    EXPECT_EQ(chordal_hold_handedness({.col = 0, .row = 2}), '*'); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, get_chordal_hold_default) { +    auto make_record = [](uint8_t row, uint8_t col, keyevent_type_t type = KEY_EVENT) { +        return keyrecord_t{ +            .event = +                { +                    .key     = {.col = col, .row = row}, +                    .type    = type, +                    .pressed = true, +                }, +        }; +    }; +    // Create two records on the left hand. +    keyrecord_t record_l0 = make_record(0, 0); +    keyrecord_t record_l1 = make_record(1, 0); +    // Create a record on the right hand. +    keyrecord_t record_r = make_record(0, MATRIX_COLS - 1); + +    // Function should return true when records are on opposite hands. +    EXPECT_TRUE(get_chordal_hold_default(&record_l0, &record_r)); +    EXPECT_TRUE(get_chordal_hold_default(&record_r, &record_l0)); +    // ... and false when on the same hand. +    EXPECT_FALSE(get_chordal_hold_default(&record_l0, &record_l1)); +    EXPECT_FALSE(get_chordal_hold_default(&record_l1, &record_l0)); +    // But (2, 0) has handedness '*', for which true is returned for chords +    // with either hand. +    keyrecord_t record_l2 = make_record(2, 0); +    EXPECT_TRUE(get_chordal_hold_default(&record_l2, &record_l0)); +    EXPECT_TRUE(get_chordal_hold_default(&record_l2, &record_r)); + +    // Create a record resulting from a combo. +    keyrecord_t record_combo = make_record(0, 0, COMBO_EVENT); +    // Function returns true in all cases. +    EXPECT_TRUE(get_chordal_hold_default(&record_l0, &record_combo)); +    EXPECT_TRUE(get_chordal_hold_default(&record_r, &record_combo)); +    EXPECT_TRUE(get_chordal_hold_default(&record_combo, &record_l0)); +    EXPECT_TRUE(get_chordal_hold_default(&record_combo, &record_r)); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, chord_nested_press_settled_as_hold) { +    TestDriver driver; +    InSequence s; +    // Mod-tap key on the left hand. +    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); +    // Regular key on the right hand. +    auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A); + +    set_keymap({mod_tap_key, regular_key}); + +    // Press mod-tap key. +    EXPECT_NO_REPORT(driver); +    mod_tap_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Tap regular key. +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_A)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap key. +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, chord_rolled_press_settled_as_tap) { +    TestDriver driver; +    InSequence s; +    // Mod-tap key on the left hand. +    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); +    // Regular key on the right hand. +    auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A); + +    set_keymap({mod_tap_key, regular_key}); + +    // Press mod-tap key and regular key. +    EXPECT_NO_REPORT(driver); +    mod_tap_key.press(); +    run_one_scan_loop(); +    regular_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap key. +    EXPECT_REPORT(driver, (KC_P)); +    EXPECT_REPORT(driver, (KC_P, KC_A)); +    EXPECT_REPORT(driver, (KC_A)); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release regular key. +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, non_chord_with_mod_tap_settled_as_tap) { +    TestDriver driver; +    InSequence s; +    // Mod-tap key and regular key both on the left hand. +    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); +    auto regular_key = KeymapKey(0, 2, 0, KC_A); + +    set_keymap({mod_tap_key, regular_key}); + +    // Press mod-tap-hold key. +    EXPECT_NO_REPORT(driver); +    mod_tap_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press regular key. +    EXPECT_REPORT(driver, (KC_P)); +    EXPECT_REPORT(driver, (KC_P, KC_A)); +    regular_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release regular key. +    EXPECT_REPORT(driver, (KC_P)); +    regular_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap-hold key. +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, tap_mod_tap_key) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + +    set_keymap({mod_tap_key}); + +    EXPECT_NO_REPORT(driver); +    mod_tap_key.press(); +    idle_for(TAPPING_TERM - 1); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_P)); +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, hold_mod_tap_key) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); + +    set_keymap({mod_tap_key}); + +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    mod_tap_key.press(); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_same_hand_hold_til_timeout) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, MATRIX_COLS - 2, 0, RCTL_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B)); + +    set_keymap({mod_tap_key1, mod_tap_key2}); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Continue holding til the tapping term. +    EXPECT_REPORT(driver, (KC_RIGHT_CTRL)); +    EXPECT_REPORT(driver, (KC_RIGHT_CTRL, KC_RIGHT_SHIFT)); +    idle_for(TAPPING_TERM); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap keys. +    EXPECT_REPORT(driver, (KC_RIGHT_SHIFT)); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_nested_press_opposite_hands) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B)); + +    set_keymap({mod_tap_key1, mod_tap_key2}); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap keys. +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_nested_press_same_hand) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, 2, 0, RSFT_T(KC_B)); + +    set_keymap({mod_tap_key1, mod_tap_key2}); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap keys. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    EXPECT_REPORT(driver, (KC_A)); +    mod_tap_key2.release(); +    run_one_scan_loop(); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_same_hand_streak_roll) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B)); +    auto       mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C)); + +    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3}); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release keys 1, 2, 3. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_C)); +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    mod_tap_key3.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_same_hand_streak_orders) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B)); +    auto       mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C)); + +    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3}); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release keys 3, 2, 1. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    mod_tap_key3.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_A)); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release keys 3, 1, 2. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    mod_tap_key3.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_B)); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release keys 2, 3, 1. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_A, KC_C)); +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    mod_tap_key3.release(); +    run_one_scan_loop(); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_NO_REPORT(driver); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_opposite_hands_roll) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B)); +    auto       mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C)); + +    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3}); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release keys 1, 2, 3. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_C)); +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    mod_tap_key3.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_two_left_one_right) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B)); +    auto       mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C)); + +    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3}); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release key 3. +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL, KC_C)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL)); +    mod_tap_key3.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release key 2, then key 1. +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release key 3. +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL, KC_C)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL)); +    mod_tap_key3.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release key 1, then key 2. +    EXPECT_REPORT(driver, (KC_LEFT_CTRL)); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_one_held_two_tapped) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 2, 0, CTL_T(KC_B)); +    auto       mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C)); + +    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3}); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release keys 3, 2, 1. +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B)); +    mod_tap_key3.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release keys 3, 1, 2. +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B)); +    mod_tap_key3.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_B)); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_one_regular_key) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 2, 0, CTL_T(KC_B)); +    auto       regular_key  = KeymapKey(0, MATRIX_COLS - 1, 0, KC_C); + +    set_keymap({mod_tap_key1, mod_tap_key2, regular_key}); + +    // Press keys. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    regular_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release keys. +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_C)); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    regular_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    regular_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release keys. +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B)); +    regular_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_B)); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, tap_regular_key_while_layer_tap_key_is_held) { +    TestDriver driver; +    InSequence s; +    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P)); +    auto       regular_key        = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A); +    auto       no_key             = KeymapKey(1, 1, 0, XXXXXXX); +    auto       layer_key          = KeymapKey(1, MATRIX_COLS - 1, 0, KC_B); + +    set_keymap({layer_tap_hold_key, regular_key, no_key, layer_key}); + +    // Press layer-tap-hold key. +    EXPECT_NO_REPORT(driver); +    layer_tap_hold_key.press(); +    run_one_scan_loop(); +    // Press regular key. +    regular_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release regular key. +    EXPECT_REPORT(driver, (KC_B)); +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    EXPECT_EQ(layer_state, 2); +    VERIFY_AND_CLEAR(driver); + +    // Release layer-tap-hold key. +    EXPECT_NO_REPORT(driver); +    layer_tap_hold_key.release(); +    run_one_scan_loop(); +    EXPECT_EQ(layer_state, 0); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, nested_tap_of_layer_0_layer_tap_keys) { +    TestDriver driver; +    InSequence s; +    // The keys are layer-taps on layer 2 but regular keys on layer 1. +    auto first_layer_tap_key  = KeymapKey(0, 1, 0, LT(1, KC_A)); +    auto second_layer_tap_key = KeymapKey(0, MATRIX_COLS - 1, 0, LT(1, KC_P)); +    auto first_key_on_layer   = KeymapKey(1, 1, 0, KC_B); +    auto second_key_on_layer  = KeymapKey(1, MATRIX_COLS - 1, 0, KC_Q); + +    set_keymap({first_layer_tap_key, second_layer_tap_key, first_key_on_layer, second_key_on_layer}); + +    // Press first layer-tap key. +    EXPECT_NO_REPORT(driver); +    first_layer_tap_key.press(); +    run_one_scan_loop(); +    // Press second layer-tap key. +    second_layer_tap_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release second layer-tap key. +    EXPECT_REPORT(driver, (KC_Q)); +    EXPECT_EMPTY_REPORT(driver); +    second_layer_tap_key.release(); +    run_one_scan_loop(); +    EXPECT_EQ(layer_state, 2); +    VERIFY_AND_CLEAR(driver); + +    // Release first layer-tap key. +    EXPECT_NO_REPORT(driver); +    first_layer_tap_key.release(); +    run_one_scan_loop(); +    EXPECT_EQ(layer_state, 0); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, lt_mt_one_regular_key) { +    TestDriver driver; +    InSequence s; +    auto       lt_key      = KeymapKey(0, 1, 0, LT(1, KC_A)); +    auto       mt_key0     = KeymapKey(0, 2, 0, SFT_T(KC_B)); +    auto       mt_key1     = KeymapKey(1, 2, 0, CTL_T(KC_C)); +    auto       regular_key = KeymapKey(1, MATRIX_COLS - 1, 0, KC_X); +    auto       no_key0     = KeymapKey(0, MATRIX_COLS - 1, 0, XXXXXXX); +    auto       no_key1     = KeymapKey(1, 1, 0, XXXXXXX); + +    set_keymap({lt_key, mt_key0, mt_key1, regular_key, no_key0, no_key1}); + +    // Press LT, MT, and regular key. +    EXPECT_NO_REPORT(driver); +    lt_key.press(); +    run_one_scan_loop(); +    mt_key1.press(); +    run_one_scan_loop(); +    regular_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release the regular key. +    EXPECT_REPORT(driver, (KC_LCTL)); +    EXPECT_REPORT(driver, (KC_LCTL, KC_X)); +    EXPECT_REPORT(driver, (KC_LCTL)); +    regular_key.release(); +    run_one_scan_loop(); +    EXPECT_EQ(get_mods(), MOD_BIT_LCTRL); +    EXPECT_EQ(layer_state, 2); +    VERIFY_AND_CLEAR(driver); + +    // Release MT key. +    EXPECT_EMPTY_REPORT(driver); +    mt_key1.release(); +    run_one_scan_loop(); +    EXPECT_EQ(get_mods(), 0); +    VERIFY_AND_CLEAR(driver); + +    // Release LT key. +    EXPECT_NO_REPORT(driver); +    lt_key.release(); +    run_one_scan_loop(); +    EXPECT_EQ(layer_state, 0); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, nested_tap_of_layer_tap_keys) { +    TestDriver driver; +    InSequence s; +    // The keys are layer-taps on all layers. +    auto first_key_layer_0  = KeymapKey(0, 1, 0, LT(1, KC_A)); +    auto second_key_layer_0 = KeymapKey(0, MATRIX_COLS - 1, 0, LT(1, KC_P)); +    auto first_key_layer_1  = KeymapKey(1, 1, 0, LT(2, KC_B)); +    auto second_key_layer_1 = KeymapKey(1, MATRIX_COLS - 1, 0, LT(2, KC_Q)); +    auto first_key_layer_2  = KeymapKey(2, 1, 0, KC_TRNS); +    auto second_key_layer_2 = KeymapKey(2, MATRIX_COLS - 1, 0, KC_TRNS); + +    set_keymap({first_key_layer_0, second_key_layer_0, first_key_layer_1, second_key_layer_1, first_key_layer_2, second_key_layer_2}); + +    // Press first layer-tap key. +    EXPECT_NO_REPORT(driver); +    first_key_layer_0.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press second layer-tap key. +    EXPECT_NO_REPORT(driver); +    second_key_layer_0.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release second layer-tap key. +    EXPECT_REPORT(driver, (KC_Q)); +    EXPECT_EMPTY_REPORT(driver); +    second_key_layer_0.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release first layer-tap key. +    EXPECT_NO_REPORT(driver); +    first_key_layer_0.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, roll_layer_tap_key_with_regular_key) { +    TestDriver driver; +    InSequence s; + +    auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P)); +    auto regular_key        = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A); +    auto layer_key          = KeymapKey(1, MATRIX_COLS - 1, 0, KC_B); + +    set_keymap({layer_tap_hold_key, regular_key, layer_key}); + +    // Press layer-tap-hold key. +    EXPECT_NO_REPORT(driver); +    layer_tap_hold_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press regular key. +    EXPECT_NO_REPORT(driver); +    regular_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release layer-tap-hold key. +    EXPECT_REPORT(driver, (KC_P)); +    EXPECT_REPORT(driver, (KC_P, KC_A)); +    EXPECT_REPORT(driver, (KC_A)); +    layer_tap_hold_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release regular key. +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_tap_keys_stuttered_press) { +    TestDriver driver; +    InSequence s; + +    auto mod_tap_key1 = KeymapKey(0, 1, 0, LSFT_T(KC_A)); +    auto mod_tap_key2 = KeymapKey(0, 2, 0, LCTL_T(KC_B)); + +    set_keymap({mod_tap_key1, mod_tap_key2}); + +    // Hold first mod-tap key until the tapping term. +    EXPECT_REPORT(driver, (KC_LSFT)); +    mod_tap_key1.press(); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Press the second mod-tap key, then quickly release and press the first. +    EXPECT_NO_REPORT(driver); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_LSFT, KC_B)); +    EXPECT_REPORT(driver, (KC_B)); +    EXPECT_REPORT(driver, (KC_B, KC_A)); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    EXPECT_EQ(get_mods(), 0); // Verify that Shift was released. +    VERIFY_AND_CLEAR(driver); + +    // Release both keys. +    EXPECT_REPORT(driver, (KC_A)); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} diff --git a/tests/tap_hold_configurations/flow_tap/config.h b/tests/tap_hold_configurations/flow_tap/config.h new file mode 100644 index 0000000000..d6f385d8d4 --- /dev/null +++ b/tests/tap_hold_configurations/flow_tap/config.h @@ -0,0 +1,23 @@ +/* Copyright 2022 Vladislav Kucheriavykh + * Copyright 2025 Google LLC + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "test_common.h" + +#define FLOW_TAP_TERM 150 +#define PERMISSIVE_HOLD diff --git a/tests/tap_hold_configurations/flow_tap/test.mk b/tests/tap_hold_configurations/flow_tap/test.mk new file mode 100644 index 0000000000..81ba8da66d --- /dev/null +++ b/tests/tap_hold_configurations/flow_tap/test.mk @@ -0,0 +1,18 @@ +# Copyright 2022 Vladislav Kucheriavykh +# +# 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 <http://www.gnu.org/licenses/>. + +COMBO_ENABLE = yes + +INTROSPECTION_KEYMAP_C = test_keymap.c diff --git a/tests/tap_hold_configurations/flow_tap/test_keymap.c b/tests/tap_hold_configurations/flow_tap/test_keymap.c new file mode 100644 index 0000000000..4dfe5e4cb6 --- /dev/null +++ b/tests/tap_hold_configurations/flow_tap/test_keymap.c @@ -0,0 +1,23 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "quantum.h" + +uint16_t const mt_lt_combo[] = {SFT_T(KC_X), LT(1, KC_Y), COMBO_END}; + +// clang-format off +combo_t key_combos[] = { +    COMBO(mt_lt_combo, KC_Z), +}; +// clang-format on diff --git a/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp b/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp new file mode 100644 index 0000000000..d419a3b313 --- /dev/null +++ b/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp @@ -0,0 +1,844 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_fixture.hpp" +#include "test_keymap_key.hpp" + +using testing::_; +using testing::AnyNumber; +using testing::InSequence; + +class FlowTapTest : public TestFixture {}; + +// Test an input of quick distinct taps. All should be settled as tapped. +TEST_F(FlowTapTest, distinct_taps) { +    TestDriver driver; +    InSequence s; +    auto       regular_key  = KeymapKey(0, 0, 0, KC_A); +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B)); +    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C)); +    auto       mod_tap_key3 = KeymapKey(0, 3, 0, ALT_T(KC_D)); + +    set_keymap({regular_key, mod_tap_key1, mod_tap_key2, mod_tap_key3}); + +    // Tap regular key. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key, FLOW_TAP_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Tap mod-tap 1. +    EXPECT_REPORT(driver, (KC_B)); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Tap mod-tap 2. +    EXPECT_REPORT(driver, (KC_C)); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Tap mod-tap 3. +    EXPECT_REPORT(driver, (KC_D)); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    mod_tap_key3.release(); +    idle_for(FLOW_TAP_TERM + 1); // Pause between taps. +    VERIFY_AND_CLEAR(driver); + +    // Tap mod-tap 1. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_B)); +    EXPECT_EMPTY_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Tap mod-tap 2. +    EXPECT_REPORT(driver, (KC_C)); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    idle_for(TAPPING_TERM + 1); +    mod_tap_key2.release(); +    idle_for(FLOW_TAP_TERM + 1); +    VERIFY_AND_CLEAR(driver); +} + +// By default, Flow Tap is disabled when mods other than Shift and AltGr are on. +TEST_F(FlowTapTest, hotkey_taps) { +    TestDriver driver; +    InSequence s; +    auto       ctrl_key    = KeymapKey(0, 0, 0, KC_LCTL); +    auto       shft_key    = KeymapKey(0, 1, 0, KC_LSFT); +    auto       alt_key     = KeymapKey(0, 2, 0, KC_LALT); +    auto       gui_key     = KeymapKey(0, 3, 0, KC_LGUI); +    auto       regular_key = KeymapKey(0, 4, 0, KC_A); +    auto       mod_tap_key = KeymapKey(0, 5, 0, RCTL_T(KC_B)); + +    set_keymap({ctrl_key, shft_key, alt_key, gui_key, regular_key, mod_tap_key}); + +    for (KeymapKey* mod_key : {&ctrl_key, &alt_key, &gui_key}) { +        // Hold mod key. +        EXPECT_REPORT(driver, (mod_key->code)); +        mod_key->press(); +        run_one_scan_loop(); + +        // Tap regular key. +        EXPECT_REPORT(driver, (mod_key->code, KC_A)); +        regular_key.press(); +        run_one_scan_loop(); +        VERIFY_AND_CLEAR(driver); + +        EXPECT_REPORT(driver, (mod_key->code)); +        regular_key.release(); +        run_one_scan_loop(); +        VERIFY_AND_CLEAR(driver); + +        // Press mod-tap, where Flow Tap is disabled due to the held mod. +        EXPECT_REPORT(driver, (mod_key->code, KC_RCTL)); +        mod_tap_key.press(); +        idle_for(TAPPING_TERM + 1); +        VERIFY_AND_CLEAR(driver); + +        // Release mod-tap. +        EXPECT_REPORT(driver, (mod_key->code)); +        mod_tap_key.release(); +        run_one_scan_loop(); + +        // Release mod key. +        EXPECT_EMPTY_REPORT(driver); +        mod_key->release(); +        run_one_scan_loop(); +        VERIFY_AND_CLEAR(driver); +    } + +    // Hold Shift key. +    EXPECT_REPORT(driver, (KC_LSFT)); +    shft_key.press(); +    run_one_scan_loop(); + +    // Tap regular key. +    EXPECT_REPORT(driver, (KC_LSFT, KC_A)); +    regular_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_LSFT)); +    regular_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press mod-tap, where Flow Tap applies to settle as tapped. +    EXPECT_REPORT(driver, (KC_LSFT, KC_B)); +    mod_tap_key.press(); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap. +    EXPECT_REPORT(driver, (KC_LSFT)); +    mod_tap_key.release(); +    run_one_scan_loop(); + +    // Release Shift key. +    EXPECT_EMPTY_REPORT(driver); +    shft_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +// Test input with two mod-taps in a rolled press quickly after a regular key. +TEST_F(FlowTapTest, rolled_press) { +    TestDriver driver; +    InSequence s; +    auto       regular_key  = KeymapKey(0, 0, 0, KC_A); +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B)); +    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C)); + +    set_keymap({regular_key, mod_tap_key1, mod_tap_key2}); + +    // Tap regular key. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); + +    // Press mod-tap key 1 quickly after regular key. The mod-tap should settle +    // immediately as tapped, sending `KC_B`. +    EXPECT_REPORT(driver, (KC_B)); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Press mod-tap key 2 quickly. +    EXPECT_REPORT(driver, (KC_B, KC_C)); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Hold for longer than the tapping term. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap keys. +    EXPECT_REPORT(driver, (KC_C)); +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, long_flow_tap_settled_as_held) { +    TestDriver driver; +    InSequence s; +    auto       regular_key = KeymapKey(0, 0, 0, KC_A); +    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B)); + +    set_keymap({regular_key, mod_tap_key}); + +    // Tap regular key. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_NO_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Press mod-tap key. +    EXPECT_NO_REPORT(driver); +    mod_tap_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Hold for the tapping term. +    EXPECT_REPORT(driver, (KC_LSFT)); +    idle_for(TAPPING_TERM); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap key. +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, holding_multiple_mod_taps) { +    TestDriver driver; +    InSequence s; +    auto       regular_key  = KeymapKey(0, 0, 0, KC_A); +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B)); +    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C)); + +    set_keymap({regular_key, mod_tap_key1, mod_tap_key2}); + +    // Tap regular key. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_NO_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    idle_for(TAPPING_TERM - 5); // Hold almost until tapping term. +    VERIFY_AND_CLEAR(driver); + +    // Press regular key. +    EXPECT_REPORT(driver, (KC_LSFT)); +    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); +    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL, KC_A)); +    regular_key.press(); +    idle_for(10); +    VERIFY_AND_CLEAR(driver); + +    // Release keys. +    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); +    EXPECT_REPORT(driver, (KC_LCTL)); +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, holding_mod_tap_with_regular_mod) { +    TestDriver driver; +    InSequence s; +    auto       regular_key = KeymapKey(0, 0, 0, KC_A); +    auto       mod_key     = KeymapKey(0, 1, 0, KC_LSFT); +    auto       mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_C)); + +    set_keymap({regular_key, mod_key, mod_tap_key}); + +    // Tap regular key. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_NO_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Press mod and mod-tap keys. +    EXPECT_REPORT(driver, (KC_LSFT)); +    mod_key.press(); +    run_one_scan_loop(); +    mod_tap_key.press(); +    idle_for(TAPPING_TERM - 5); // Hold almost until tapping term. +    VERIFY_AND_CLEAR(driver); + +    // Press regular key. +    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); +    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL, KC_A)); +    regular_key.press(); +    idle_for(10); +    VERIFY_AND_CLEAR(driver); + +    // Release keys. +    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); +    EXPECT_REPORT(driver, (KC_LCTL)); +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    mod_key.release(); +    run_one_scan_loop(); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, layer_tap_key) { +    TestDriver driver; +    InSequence s; +    auto       regular_key   = KeymapKey(0, 0, 0, KC_A); +    auto       layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_B)); +    auto       regular_key2  = KeymapKey(1, 0, 0, KC_C); + +    set_keymap({regular_key, layer_tap_key, regular_key2}); + +    // Tap regular key. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); + +    // Press layer-tap key, quickly after the regular key. +    EXPECT_REPORT(driver, (KC_B)); +    layer_tap_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Release layer-tap key. +    EXPECT_EMPTY_REPORT(driver); +    layer_tap_key.release(); +    run_one_scan_loop(); + +    // Tap regular key. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_NO_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Press layer-tap key, slowly after the regular key. +    EXPECT_NO_REPORT(driver); +    layer_tap_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM + 1); +    EXPECT_EQ(layer_state, 1 << 1); +    VERIFY_AND_CLEAR(driver); + +    // Tap regular key2. +    EXPECT_REPORT(driver, (KC_C)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); + +    // Release layer-tap key. +    EXPECT_NO_REPORT(driver); +    layer_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, layer_tap_ignored_with_disabled_key) { +    TestDriver driver; +    InSequence s; +    auto       no_key        = KeymapKey(0, 0, 0, KC_NO); +    auto       regular_key   = KeymapKey(1, 0, 0, KC_ESC); +    auto       layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_A)); +    auto       mod_tap_key   = KeymapKey(0, 2, 0, CTL_T(KC_B)); + +    set_keymap({no_key, regular_key, layer_tap_key, mod_tap_key}); + +    EXPECT_REPORT(driver, (KC_ESC)); +    EXPECT_EMPTY_REPORT(driver); +    layer_tap_key.press(); +    idle_for(TAPPING_TERM + 1); +    tap_key(regular_key); +    layer_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_LCTL)); +    mod_tap_key.press(); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, layer_tap_ignored_with_disabled_key_complex) { +    TestDriver driver; +    InSequence s; +    auto       regular_key1  = KeymapKey(0, 0, 0, KC_Q); +    auto       layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_SPC)); +    auto       mod_tap_key1  = KeymapKey(0, 2, 0, CTL_T(KC_T)); +    // Place RALT_T(KC_I), where Flow Tap is enabled, in the same position on +    // layer 0 as KC_RGHT, where Flow Tap is disabled. This tests that Flow Tap +    // tracks the keycode from the correct layer. +    auto mod_tap_key2 = KeymapKey(0, 3, 0, RALT_T(KC_I)); +    auto regular_key2 = KeymapKey(1, 3, 0, KC_RGHT); + +    set_keymap({regular_key1, layer_tap_key, mod_tap_key1, mod_tap_key2, regular_key2}); + +    // Tap regular key 1. +    EXPECT_REPORT(driver, (KC_Q)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key1); +    idle_for(FLOW_TAP_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Hold layer-tap key. +    EXPECT_NO_REPORT(driver); +    layer_tap_key.press(); +    run_one_scan_loop(); +    // idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Tap regular key 2. +    EXPECT_REPORT(driver, (KC_RGHT)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key2); +    VERIFY_AND_CLEAR(driver); + +    // Release layer-tap key. +    EXPECT_NO_REPORT(driver); +    layer_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Quickly hold mod-tap key 1. +    EXPECT_NO_REPORT(driver); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_LCTL)); +    EXPECT_REPORT(driver, (KC_LCTL, KC_Q)); +    EXPECT_REPORT(driver, (KC_LCTL)); +    tap_key(regular_key1); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, layer_tap_ignored_with_enabled_key) { +    TestDriver driver; +    InSequence s; +    auto       no_key        = KeymapKey(0, 0, 0, KC_NO); +    auto       regular_key   = KeymapKey(1, 0, 0, KC_C); +    auto       layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_A)); +    auto       mod_tap_key   = KeymapKey(0, 2, 0, CTL_T(KC_B)); + +    set_keymap({no_key, regular_key, layer_tap_key, mod_tap_key}); + +    EXPECT_REPORT(driver, (KC_C)); +    EXPECT_EMPTY_REPORT(driver); +    layer_tap_key.press(); +    idle_for(TAPPING_TERM + 1); +    tap_key(regular_key); +    layer_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_B)); +    mod_tap_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_EMPTY_REPORT(driver); +    idle_for(TAPPING_TERM + 1); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, combo_key) { +    TestDriver driver; +    InSequence s; +    auto       regular_key   = KeymapKey(0, 0, 0, KC_A); +    auto       mod_tap_key   = KeymapKey(0, 1, 0, SFT_T(KC_X)); +    auto       layer_tap_key = KeymapKey(0, 2, 0, LT(1, KC_Y)); + +    set_keymap({regular_key, mod_tap_key, layer_tap_key}); + +    // Tap regular key. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); + +    // Press combo keys quickly after regular key. +    EXPECT_REPORT(driver, (KC_Z)); +    EXPECT_EMPTY_REPORT(driver); +    tap_combo({mod_tap_key, layer_tap_key}); +    VERIFY_AND_CLEAR(driver); + +    // Press mod-tap key quickly. +    EXPECT_REPORT(driver, (KC_X)); +    mod_tap_key.press(); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap key. +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, oneshot_mod_key) { +    TestDriver driver; +    InSequence s; +    auto       regular_key = KeymapKey(0, 0, 0, KC_A); +    auto       osm_key     = KeymapKey(0, 1, 0, OSM(MOD_LSFT)); + +    set_keymap({regular_key, osm_key}); + +    // Tap regular key. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); + +    // Tap OSM, tap regular key. +    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); +    EXPECT_REPORT(driver, (KC_LSFT, KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(osm_key); +    tap_key(regular_key); +    VERIFY_AND_CLEAR(driver); + +    // Nested press of OSM and regular keys. +    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); +    EXPECT_REPORT(driver, (KC_LSFT, KC_A)); +    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); +    EXPECT_EMPTY_REPORT(driver); +    osm_key.press(); +    run_one_scan_loop(); +    tap_key(regular_key); +    osm_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, quick_tap) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_A)); + +    set_keymap({mod_tap_key}); + +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_EMPTY_REPORT(driver); +    tap_key(mod_tap_key); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_A)); +    mod_tap_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap key. +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, rolling_mt_mt) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B)); + +    set_keymap({mod_tap_key1, mod_tap_key2}); + +    EXPECT_NO_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    EXPECT_REPORT(driver, (KC_B)); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Hold for longer than the tapping term. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap keys. +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, rolling_lt_mt_regular) { +    TestDriver driver; +    InSequence s; +    auto       layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); +    auto       mod_tap_key   = KeymapKey(0, 1, 0, CTL_T(KC_B)); +    auto       regular_key   = KeymapKey(0, 2, 0, KC_C); + +    set_keymap({layer_tap_key, mod_tap_key, regular_key}); + +    EXPECT_NO_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    layer_tap_key.press(); +    run_one_scan_loop(); +    mod_tap_key.press(); +    run_one_scan_loop(); +    regular_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_B, KC_C)); +    layer_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Hold for longer than the tapping term. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap keys. +    EXPECT_REPORT(driver, (KC_C)); +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key.release(); +    run_one_scan_loop(); +    regular_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, rolling_lt_regular_mt) { +    TestDriver driver; +    InSequence s; +    auto       layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); +    auto       regular_key   = KeymapKey(0, 1, 0, KC_B); +    auto       mod_tap_key   = KeymapKey(0, 2, 0, CTL_T(KC_C)); + +    set_keymap({layer_tap_key, regular_key, mod_tap_key}); + +    EXPECT_NO_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    layer_tap_key.press(); +    run_one_scan_loop(); +    regular_key.press(); +    run_one_scan_loop(); +    mod_tap_key.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_B, KC_C)); +    layer_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Hold for longer than the tapping term. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Release mod-tap keys. +    EXPECT_REPORT(driver, (KC_C)); +    EXPECT_EMPTY_REPORT(driver); +    regular_key.release(); +    run_one_scan_loop(); +    mod_tap_key.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, rolling_mt_mt_mt) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, 0, 0, CTL_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, 1, 0, GUI_T(KC_B)); +    auto       mod_tap_key3 = KeymapKey(0, 2, 0, ALT_T(KC_C)); + +    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3}); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    run_one_scan_loop(); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release first mod-tap key. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_B, KC_C)); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Hold for longer than the tapping term. +    EXPECT_NO_REPORT(driver); +    idle_for(TAPPING_TERM + 1); +    VERIFY_AND_CLEAR(driver); + +    // Release other mod-tap keys. +    EXPECT_REPORT(driver, (KC_C)); +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    mod_tap_key3.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} + +TEST_F(FlowTapTest, roll_release_132) { +    TestDriver driver; +    InSequence s; +    auto       mod_tap_key1 = KeymapKey(0, 0, 0, CTL_T(KC_A)); +    auto       mod_tap_key2 = KeymapKey(0, 1, 0, GUI_T(KC_B)); +    auto       mod_tap_key3 = KeymapKey(0, 2, 0, ALT_T(KC_C)); + +    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3}); + +    // Press mod-tap keys. +    EXPECT_NO_REPORT(driver); +    idle_for(FLOW_TAP_TERM + 1); +    mod_tap_key1.press(); +    run_one_scan_loop(); +    mod_tap_key2.press(); +    idle_for(FLOW_TAP_TERM + 1); +    mod_tap_key3.press(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release first mod-tap key. +    EXPECT_REPORT(driver, (KC_A)); +    EXPECT_REPORT(driver, (KC_A, KC_B)); +    EXPECT_REPORT(driver, (KC_B)); +    mod_tap_key1.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); + +    // Release other mod-tap keys. +    EXPECT_REPORT(driver, (KC_B, KC_C)); +    EXPECT_REPORT(driver, (KC_B)); +    EXPECT_EMPTY_REPORT(driver); +    mod_tap_key3.release(); +    run_one_scan_loop(); +    mod_tap_key2.release(); +    run_one_scan_loop(); +    VERIFY_AND_CLEAR(driver); +} diff --git a/tests/test_common/build.mk b/tests/test_common/build.mk index d7423bc78a..385da7adf9 100644 --- a/tests/test_common/build.mk +++ b/tests/test_common/build.mk @@ -14,3 +14,4 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  CUSTOM_MATRIX=yes +KEYCODE_STRING_ENABLE = yes diff --git a/tests/test_common/keyboard_report_util.cpp b/tests/test_common/keyboard_report_util.cpp index 18e0574277..5e40323669 100644 --- a/tests/test_common/keyboard_report_util.cpp +++ b/tests/test_common/keyboard_report_util.cpp @@ -19,6 +19,10 @@  #include <vector>  #include <algorithm> +extern "C" { +#include "keycode_string.h" +} +  using namespace testing;  extern std::map<uint16_t, std::string> KEYCODE_ID_TABLE; @@ -72,7 +76,7 @@ std::ostream& operator<<(std::ostream& os, const report_keyboard_t& report) {      os << "(";      for (auto key = keys.cbegin(); key != keys.cend();) { -        os << KEYCODE_ID_TABLE.at(*key); +        os << get_keycode_string(*key);          key++;          if (key != keys.cend()) {              os << ", "; @@ -82,7 +86,7 @@ std::ostream& operator<<(std::ostream& os, const report_keyboard_t& report) {      os << ") [";      for (auto mod = mods.cbegin(); mod != mods.cend();) { -        os << KEYCODE_ID_TABLE.at(*mod); +        os << get_keycode_string(*mod);          mod++;          if (mod != mods.cend()) {              os << ", "; diff --git a/tests/test_common/keycode_table.cpp b/tests/test_common/keycode_table.cpp deleted file mode 100644 index 26d0919619..0000000000 --- a/tests/test_common/keycode_table.cpp +++ /dev/null @@ -1,767 +0,0 @@ -// Copyright 2025 QMK -// SPDX-License-Identifier: GPL-2.0-or-later - -/******************************************************************************* -  88888888888 888      d8b                .d888 d8b 888               d8b -      888     888      Y8P               d88P"  Y8P 888               Y8P -      888     888                        888        888 -      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b -      888     888 "88b 888 88K           888    888 888 d8P  Y8b      888 88K -      888     888  888 888 "Y8888b.      888    888 888 88888888      888 "Y8888b. -      888     888  888 888      X88      888    888 888 Y8b.          888      X88 -      888     888  888 888  88888P'      888    888 888  "Y8888       888  88888P' -                                                        888                 888 -                                                        888                 888 -                                                        888                 888 -     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888 -    d88P"88b d8P  Y8b 888 "88b d8P  Y8b 888P"      "88b 888   d8P  Y8b d88" 888 -    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888 -    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888 -     "Y88888  "Y8888  888  888  "Y8888  888    "Y888888  "Y888 "Y8888   "Y88888 -         888 -    Y8b d88P -     "Y88P" -*******************************************************************************/ - -// clang-format off -extern "C" { -#include <keycode.h> -} -#include <map> -#include <string> -#include <cstdint> - -std::map<uint16_t, std::string> KEYCODE_ID_TABLE = { -    {KC_NO, "KC_NO"}, -    {KC_TRANSPARENT, "KC_TRANSPARENT"}, -    {KC_A, "KC_A"}, -    {KC_B, "KC_B"}, -    {KC_C, "KC_C"}, -    {KC_D, "KC_D"}, -    {KC_E, "KC_E"}, -    {KC_F, "KC_F"}, -    {KC_G, "KC_G"}, -    {KC_H, "KC_H"}, -    {KC_I, "KC_I"}, -    {KC_J, "KC_J"}, -    {KC_K, "KC_K"}, -    {KC_L, "KC_L"}, -    {KC_M, "KC_M"}, -    {KC_N, "KC_N"}, -    {KC_O, "KC_O"}, -    {KC_P, "KC_P"}, -    {KC_Q, "KC_Q"}, -    {KC_R, "KC_R"}, -    {KC_S, "KC_S"}, -    {KC_T, "KC_T"}, -    {KC_U, "KC_U"}, -    {KC_V, "KC_V"}, -    {KC_W, "KC_W"}, -    {KC_X, "KC_X"}, -    {KC_Y, "KC_Y"}, -    {KC_Z, "KC_Z"}, -    {KC_1, "KC_1"}, -    {KC_2, "KC_2"}, -    {KC_3, "KC_3"}, -    {KC_4, "KC_4"}, -    {KC_5, "KC_5"}, -    {KC_6, "KC_6"}, -    {KC_7, "KC_7"}, -    {KC_8, "KC_8"}, -    {KC_9, "KC_9"}, -    {KC_0, "KC_0"}, -    {KC_ENTER, "KC_ENTER"}, -    {KC_ESCAPE, "KC_ESCAPE"}, -    {KC_BACKSPACE, "KC_BACKSPACE"}, -    {KC_TAB, "KC_TAB"}, -    {KC_SPACE, "KC_SPACE"}, -    {KC_MINUS, "KC_MINUS"}, -    {KC_EQUAL, "KC_EQUAL"}, -    {KC_LEFT_BRACKET, "KC_LEFT_BRACKET"}, -    {KC_RIGHT_BRACKET, "KC_RIGHT_BRACKET"}, -    {KC_BACKSLASH, "KC_BACKSLASH"}, -    {KC_NONUS_HASH, "KC_NONUS_HASH"}, -    {KC_SEMICOLON, "KC_SEMICOLON"}, -    {KC_QUOTE, "KC_QUOTE"}, -    {KC_GRAVE, "KC_GRAVE"}, -    {KC_COMMA, "KC_COMMA"}, -    {KC_DOT, "KC_DOT"}, -    {KC_SLASH, "KC_SLASH"}, -    {KC_CAPS_LOCK, "KC_CAPS_LOCK"}, -    {KC_F1, "KC_F1"}, -    {KC_F2, "KC_F2"}, -    {KC_F3, "KC_F3"}, -    {KC_F4, "KC_F4"}, -    {KC_F5, "KC_F5"}, -    {KC_F6, "KC_F6"}, -    {KC_F7, "KC_F7"}, -    {KC_F8, "KC_F8"}, -    {KC_F9, "KC_F9"}, -    {KC_F10, "KC_F10"}, -    {KC_F11, "KC_F11"}, -    {KC_F12, "KC_F12"}, -    {KC_PRINT_SCREEN, "KC_PRINT_SCREEN"}, -    {KC_SCROLL_LOCK, "KC_SCROLL_LOCK"}, -    {KC_PAUSE, "KC_PAUSE"}, -    {KC_INSERT, "KC_INSERT"}, -    {KC_HOME, "KC_HOME"}, -    {KC_PAGE_UP, "KC_PAGE_UP"}, -    {KC_DELETE, "KC_DELETE"}, -    {KC_END, "KC_END"}, -    {KC_PAGE_DOWN, "KC_PAGE_DOWN"}, -    {KC_RIGHT, "KC_RIGHT"}, -    {KC_LEFT, "KC_LEFT"}, -    {KC_DOWN, "KC_DOWN"}, -    {KC_UP, "KC_UP"}, -    {KC_NUM_LOCK, "KC_NUM_LOCK"}, -    {KC_KP_SLASH, "KC_KP_SLASH"}, -    {KC_KP_ASTERISK, "KC_KP_ASTERISK"}, -    {KC_KP_MINUS, "KC_KP_MINUS"}, -    {KC_KP_PLUS, "KC_KP_PLUS"}, -    {KC_KP_ENTER, "KC_KP_ENTER"}, -    {KC_KP_1, "KC_KP_1"}, -    {KC_KP_2, "KC_KP_2"}, -    {KC_KP_3, "KC_KP_3"}, -    {KC_KP_4, "KC_KP_4"}, -    {KC_KP_5, "KC_KP_5"}, -    {KC_KP_6, "KC_KP_6"}, -    {KC_KP_7, "KC_KP_7"}, -    {KC_KP_8, "KC_KP_8"}, -    {KC_KP_9, "KC_KP_9"}, -    {KC_KP_0, "KC_KP_0"}, -    {KC_KP_DOT, "KC_KP_DOT"}, -    {KC_NONUS_BACKSLASH, "KC_NONUS_BACKSLASH"}, -    {KC_APPLICATION, "KC_APPLICATION"}, -    {KC_KB_POWER, "KC_KB_POWER"}, -    {KC_KP_EQUAL, "KC_KP_EQUAL"}, -    {KC_F13, "KC_F13"}, -    {KC_F14, "KC_F14"}, -    {KC_F15, "KC_F15"}, -    {KC_F16, "KC_F16"}, -    {KC_F17, "KC_F17"}, -    {KC_F18, "KC_F18"}, -    {KC_F19, "KC_F19"}, -    {KC_F20, "KC_F20"}, -    {KC_F21, "KC_F21"}, -    {KC_F22, "KC_F22"}, -    {KC_F23, "KC_F23"}, -    {KC_F24, "KC_F24"}, -    {KC_EXECUTE, "KC_EXECUTE"}, -    {KC_HELP, "KC_HELP"}, -    {KC_MENU, "KC_MENU"}, -    {KC_SELECT, "KC_SELECT"}, -    {KC_STOP, "KC_STOP"}, -    {KC_AGAIN, "KC_AGAIN"}, -    {KC_UNDO, "KC_UNDO"}, -    {KC_CUT, "KC_CUT"}, -    {KC_COPY, "KC_COPY"}, -    {KC_PASTE, "KC_PASTE"}, -    {KC_FIND, "KC_FIND"}, -    {KC_KB_MUTE, "KC_KB_MUTE"}, -    {KC_KB_VOLUME_UP, "KC_KB_VOLUME_UP"}, -    {KC_KB_VOLUME_DOWN, "KC_KB_VOLUME_DOWN"}, -    {KC_LOCKING_CAPS_LOCK, "KC_LOCKING_CAPS_LOCK"}, -    {KC_LOCKING_NUM_LOCK, "KC_LOCKING_NUM_LOCK"}, -    {KC_LOCKING_SCROLL_LOCK, "KC_LOCKING_SCROLL_LOCK"}, -    {KC_KP_COMMA, "KC_KP_COMMA"}, -    {KC_KP_EQUAL_AS400, "KC_KP_EQUAL_AS400"}, -    {KC_INTERNATIONAL_1, "KC_INTERNATIONAL_1"}, -    {KC_INTERNATIONAL_2, "KC_INTERNATIONAL_2"}, -    {KC_INTERNATIONAL_3, "KC_INTERNATIONAL_3"}, -    {KC_INTERNATIONAL_4, "KC_INTERNATIONAL_4"}, -    {KC_INTERNATIONAL_5, "KC_INTERNATIONAL_5"}, -    {KC_INTERNATIONAL_6, "KC_INTERNATIONAL_6"}, -    {KC_INTERNATIONAL_7, "KC_INTERNATIONAL_7"}, -    {KC_INTERNATIONAL_8, "KC_INTERNATIONAL_8"}, -    {KC_INTERNATIONAL_9, "KC_INTERNATIONAL_9"}, -    {KC_LANGUAGE_1, "KC_LANGUAGE_1"}, -    {KC_LANGUAGE_2, "KC_LANGUAGE_2"}, -    {KC_LANGUAGE_3, "KC_LANGUAGE_3"}, -    {KC_LANGUAGE_4, "KC_LANGUAGE_4"}, -    {KC_LANGUAGE_5, "KC_LANGUAGE_5"}, -    {KC_LANGUAGE_6, "KC_LANGUAGE_6"}, -    {KC_LANGUAGE_7, "KC_LANGUAGE_7"}, -    {KC_LANGUAGE_8, "KC_LANGUAGE_8"}, -    {KC_LANGUAGE_9, "KC_LANGUAGE_9"}, -    {KC_ALTERNATE_ERASE, "KC_ALTERNATE_ERASE"}, -    {KC_SYSTEM_REQUEST, "KC_SYSTEM_REQUEST"}, -    {KC_CANCEL, "KC_CANCEL"}, -    {KC_CLEAR, "KC_CLEAR"}, -    {KC_PRIOR, "KC_PRIOR"}, -    {KC_RETURN, "KC_RETURN"}, -    {KC_SEPARATOR, "KC_SEPARATOR"}, -    {KC_OUT, "KC_OUT"}, -    {KC_OPER, "KC_OPER"}, -    {KC_CLEAR_AGAIN, "KC_CLEAR_AGAIN"}, -    {KC_CRSEL, "KC_CRSEL"}, -    {KC_EXSEL, "KC_EXSEL"}, -    {KC_SYSTEM_POWER, "KC_SYSTEM_POWER"}, -    {KC_SYSTEM_SLEEP, "KC_SYSTEM_SLEEP"}, -    {KC_SYSTEM_WAKE, "KC_SYSTEM_WAKE"}, -    {KC_AUDIO_MUTE, "KC_AUDIO_MUTE"}, -    {KC_AUDIO_VOL_UP, "KC_AUDIO_VOL_UP"}, -    {KC_AUDIO_VOL_DOWN, "KC_AUDIO_VOL_DOWN"}, -    {KC_MEDIA_NEXT_TRACK, "KC_MEDIA_NEXT_TRACK"}, -    {KC_MEDIA_PREV_TRACK, "KC_MEDIA_PREV_TRACK"}, -    {KC_MEDIA_STOP, "KC_MEDIA_STOP"}, -    {KC_MEDIA_PLAY_PAUSE, "KC_MEDIA_PLAY_PAUSE"}, -    {KC_MEDIA_SELECT, "KC_MEDIA_SELECT"}, -    {KC_MEDIA_EJECT, "KC_MEDIA_EJECT"}, -    {KC_MAIL, "KC_MAIL"}, -    {KC_CALCULATOR, "KC_CALCULATOR"}, -    {KC_MY_COMPUTER, "KC_MY_COMPUTER"}, -    {KC_WWW_SEARCH, "KC_WWW_SEARCH"}, -    {KC_WWW_HOME, "KC_WWW_HOME"}, -    {KC_WWW_BACK, "KC_WWW_BACK"}, -    {KC_WWW_FORWARD, "KC_WWW_FORWARD"}, -    {KC_WWW_STOP, "KC_WWW_STOP"}, -    {KC_WWW_REFRESH, "KC_WWW_REFRESH"}, -    {KC_WWW_FAVORITES, "KC_WWW_FAVORITES"}, -    {KC_MEDIA_FAST_FORWARD, "KC_MEDIA_FAST_FORWARD"}, -    {KC_MEDIA_REWIND, "KC_MEDIA_REWIND"}, -    {KC_BRIGHTNESS_UP, "KC_BRIGHTNESS_UP"}, -    {KC_BRIGHTNESS_DOWN, "KC_BRIGHTNESS_DOWN"}, -    {KC_CONTROL_PANEL, "KC_CONTROL_PANEL"}, -    {KC_ASSISTANT, "KC_ASSISTANT"}, -    {KC_MISSION_CONTROL, "KC_MISSION_CONTROL"}, -    {KC_LAUNCHPAD, "KC_LAUNCHPAD"}, -    {QK_MOUSE_CURSOR_UP, "QK_MOUSE_CURSOR_UP"}, -    {QK_MOUSE_CURSOR_DOWN, "QK_MOUSE_CURSOR_DOWN"}, -    {QK_MOUSE_CURSOR_LEFT, "QK_MOUSE_CURSOR_LEFT"}, -    {QK_MOUSE_CURSOR_RIGHT, "QK_MOUSE_CURSOR_RIGHT"}, -    {QK_MOUSE_BUTTON_1, "QK_MOUSE_BUTTON_1"}, -    {QK_MOUSE_BUTTON_2, "QK_MOUSE_BUTTON_2"}, -    {QK_MOUSE_BUTTON_3, "QK_MOUSE_BUTTON_3"}, -    {QK_MOUSE_BUTTON_4, "QK_MOUSE_BUTTON_4"}, -    {QK_MOUSE_BUTTON_5, "QK_MOUSE_BUTTON_5"}, -    {QK_MOUSE_BUTTON_6, "QK_MOUSE_BUTTON_6"}, -    {QK_MOUSE_BUTTON_7, "QK_MOUSE_BUTTON_7"}, -    {QK_MOUSE_BUTTON_8, "QK_MOUSE_BUTTON_8"}, -    {QK_MOUSE_WHEEL_UP, "QK_MOUSE_WHEEL_UP"}, -    {QK_MOUSE_WHEEL_DOWN, "QK_MOUSE_WHEEL_DOWN"}, -    {QK_MOUSE_WHEEL_LEFT, "QK_MOUSE_WHEEL_LEFT"}, -    {QK_MOUSE_WHEEL_RIGHT, "QK_MOUSE_WHEEL_RIGHT"}, -    {QK_MOUSE_ACCELERATION_0, "QK_MOUSE_ACCELERATION_0"}, -    {QK_MOUSE_ACCELERATION_1, "QK_MOUSE_ACCELERATION_1"}, -    {QK_MOUSE_ACCELERATION_2, "QK_MOUSE_ACCELERATION_2"}, -    {KC_LEFT_CTRL, "KC_LEFT_CTRL"}, -    {KC_LEFT_SHIFT, "KC_LEFT_SHIFT"}, -    {KC_LEFT_ALT, "KC_LEFT_ALT"}, -    {KC_LEFT_GUI, "KC_LEFT_GUI"}, -    {KC_RIGHT_CTRL, "KC_RIGHT_CTRL"}, -    {KC_RIGHT_SHIFT, "KC_RIGHT_SHIFT"}, -    {KC_RIGHT_ALT, "KC_RIGHT_ALT"}, -    {KC_RIGHT_GUI, "KC_RIGHT_GUI"}, -    {QK_SWAP_HANDS_TOGGLE, "QK_SWAP_HANDS_TOGGLE"}, -    {QK_SWAP_HANDS_TAP_TOGGLE, "QK_SWAP_HANDS_TAP_TOGGLE"}, -    {QK_SWAP_HANDS_MOMENTARY_ON, "QK_SWAP_HANDS_MOMENTARY_ON"}, -    {QK_SWAP_HANDS_MOMENTARY_OFF, "QK_SWAP_HANDS_MOMENTARY_OFF"}, -    {QK_SWAP_HANDS_OFF, "QK_SWAP_HANDS_OFF"}, -    {QK_SWAP_HANDS_ON, "QK_SWAP_HANDS_ON"}, -    {QK_SWAP_HANDS_ONE_SHOT, "QK_SWAP_HANDS_ONE_SHOT"}, -    {QK_MAGIC_SWAP_CONTROL_CAPS_LOCK, "QK_MAGIC_SWAP_CONTROL_CAPS_LOCK"}, -    {QK_MAGIC_UNSWAP_CONTROL_CAPS_LOCK, "QK_MAGIC_UNSWAP_CONTROL_CAPS_LOCK"}, -    {QK_MAGIC_TOGGLE_CONTROL_CAPS_LOCK, "QK_MAGIC_TOGGLE_CONTROL_CAPS_LOCK"}, -    {QK_MAGIC_CAPS_LOCK_AS_CONTROL_OFF, "QK_MAGIC_CAPS_LOCK_AS_CONTROL_OFF"}, -    {QK_MAGIC_CAPS_LOCK_AS_CONTROL_ON, "QK_MAGIC_CAPS_LOCK_AS_CONTROL_ON"}, -    {QK_MAGIC_SWAP_LALT_LGUI, "QK_MAGIC_SWAP_LALT_LGUI"}, -    {QK_MAGIC_UNSWAP_LALT_LGUI, "QK_MAGIC_UNSWAP_LALT_LGUI"}, -    {QK_MAGIC_SWAP_RALT_RGUI, "QK_MAGIC_SWAP_RALT_RGUI"}, -    {QK_MAGIC_UNSWAP_RALT_RGUI, "QK_MAGIC_UNSWAP_RALT_RGUI"}, -    {QK_MAGIC_GUI_ON, "QK_MAGIC_GUI_ON"}, -    {QK_MAGIC_GUI_OFF, "QK_MAGIC_GUI_OFF"}, -    {QK_MAGIC_TOGGLE_GUI, "QK_MAGIC_TOGGLE_GUI"}, -    {QK_MAGIC_SWAP_GRAVE_ESC, "QK_MAGIC_SWAP_GRAVE_ESC"}, -    {QK_MAGIC_UNSWAP_GRAVE_ESC, "QK_MAGIC_UNSWAP_GRAVE_ESC"}, -    {QK_MAGIC_SWAP_BACKSLASH_BACKSPACE, "QK_MAGIC_SWAP_BACKSLASH_BACKSPACE"}, -    {QK_MAGIC_UNSWAP_BACKSLASH_BACKSPACE, "QK_MAGIC_UNSWAP_BACKSLASH_BACKSPACE"}, -    {QK_MAGIC_TOGGLE_BACKSLASH_BACKSPACE, "QK_MAGIC_TOGGLE_BACKSLASH_BACKSPACE"}, -    {QK_MAGIC_NKRO_ON, "QK_MAGIC_NKRO_ON"}, -    {QK_MAGIC_NKRO_OFF, "QK_MAGIC_NKRO_OFF"}, -    {QK_MAGIC_TOGGLE_NKRO, "QK_MAGIC_TOGGLE_NKRO"}, -    {QK_MAGIC_SWAP_ALT_GUI, "QK_MAGIC_SWAP_ALT_GUI"}, -    {QK_MAGIC_UNSWAP_ALT_GUI, "QK_MAGIC_UNSWAP_ALT_GUI"}, -    {QK_MAGIC_TOGGLE_ALT_GUI, "QK_MAGIC_TOGGLE_ALT_GUI"}, -    {QK_MAGIC_SWAP_LCTL_LGUI, "QK_MAGIC_SWAP_LCTL_LGUI"}, -    {QK_MAGIC_UNSWAP_LCTL_LGUI, "QK_MAGIC_UNSWAP_LCTL_LGUI"}, -    {QK_MAGIC_SWAP_RCTL_RGUI, "QK_MAGIC_SWAP_RCTL_RGUI"}, -    {QK_MAGIC_UNSWAP_RCTL_RGUI, "QK_MAGIC_UNSWAP_RCTL_RGUI"}, -    {QK_MAGIC_SWAP_CTL_GUI, "QK_MAGIC_SWAP_CTL_GUI"}, -    {QK_MAGIC_UNSWAP_CTL_GUI, "QK_MAGIC_UNSWAP_CTL_GUI"}, -    {QK_MAGIC_TOGGLE_CTL_GUI, "QK_MAGIC_TOGGLE_CTL_GUI"}, -    {QK_MAGIC_EE_HANDS_LEFT, "QK_MAGIC_EE_HANDS_LEFT"}, -    {QK_MAGIC_EE_HANDS_RIGHT, "QK_MAGIC_EE_HANDS_RIGHT"}, -    {QK_MAGIC_SWAP_ESCAPE_CAPS_LOCK, "QK_MAGIC_SWAP_ESCAPE_CAPS_LOCK"}, -    {QK_MAGIC_UNSWAP_ESCAPE_CAPS_LOCK, "QK_MAGIC_UNSWAP_ESCAPE_CAPS_LOCK"}, -    {QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK, "QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK"}, -    {QK_MIDI_ON, "QK_MIDI_ON"}, -    {QK_MIDI_OFF, "QK_MIDI_OFF"}, -    {QK_MIDI_TOGGLE, "QK_MIDI_TOGGLE"}, -    {QK_MIDI_NOTE_C_0, "QK_MIDI_NOTE_C_0"}, -    {QK_MIDI_NOTE_C_SHARP_0, "QK_MIDI_NOTE_C_SHARP_0"}, -    {QK_MIDI_NOTE_D_0, "QK_MIDI_NOTE_D_0"}, -    {QK_MIDI_NOTE_D_SHARP_0, "QK_MIDI_NOTE_D_SHARP_0"}, -    {QK_MIDI_NOTE_E_0, "QK_MIDI_NOTE_E_0"}, -    {QK_MIDI_NOTE_F_0, "QK_MIDI_NOTE_F_0"}, -    {QK_MIDI_NOTE_F_SHARP_0, "QK_MIDI_NOTE_F_SHARP_0"}, -    {QK_MIDI_NOTE_G_0, "QK_MIDI_NOTE_G_0"}, -    {QK_MIDI_NOTE_G_SHARP_0, "QK_MIDI_NOTE_G_SHARP_0"}, -    {QK_MIDI_NOTE_A_0, "QK_MIDI_NOTE_A_0"}, -    {QK_MIDI_NOTE_A_SHARP_0, "QK_MIDI_NOTE_A_SHARP_0"}, -    {QK_MIDI_NOTE_B_0, "QK_MIDI_NOTE_B_0"}, -    {QK_MIDI_NOTE_C_1, "QK_MIDI_NOTE_C_1"}, -    {QK_MIDI_NOTE_C_SHARP_1, "QK_MIDI_NOTE_C_SHARP_1"}, -    {QK_MIDI_NOTE_D_1, "QK_MIDI_NOTE_D_1"}, -    {QK_MIDI_NOTE_D_SHARP_1, "QK_MIDI_NOTE_D_SHARP_1"}, -    {QK_MIDI_NOTE_E_1, "QK_MIDI_NOTE_E_1"}, -    {QK_MIDI_NOTE_F_1, "QK_MIDI_NOTE_F_1"}, -    {QK_MIDI_NOTE_F_SHARP_1, "QK_MIDI_NOTE_F_SHARP_1"}, -    {QK_MIDI_NOTE_G_1, "QK_MIDI_NOTE_G_1"}, -    {QK_MIDI_NOTE_G_SHARP_1, "QK_MIDI_NOTE_G_SHARP_1"}, -    {QK_MIDI_NOTE_A_1, "QK_MIDI_NOTE_A_1"}, -    {QK_MIDI_NOTE_A_SHARP_1, "QK_MIDI_NOTE_A_SHARP_1"}, -    {QK_MIDI_NOTE_B_1, "QK_MIDI_NOTE_B_1"}, -    {QK_MIDI_NOTE_C_2, "QK_MIDI_NOTE_C_2"}, -    {QK_MIDI_NOTE_C_SHARP_2, "QK_MIDI_NOTE_C_SHARP_2"}, -    {QK_MIDI_NOTE_D_2, "QK_MIDI_NOTE_D_2"}, -    {QK_MIDI_NOTE_D_SHARP_2, "QK_MIDI_NOTE_D_SHARP_2"}, -    {QK_MIDI_NOTE_E_2, "QK_MIDI_NOTE_E_2"}, -    {QK_MIDI_NOTE_F_2, "QK_MIDI_NOTE_F_2"}, -    {QK_MIDI_NOTE_F_SHARP_2, "QK_MIDI_NOTE_F_SHARP_2"}, -    {QK_MIDI_NOTE_G_2, "QK_MIDI_NOTE_G_2"}, -    {QK_MIDI_NOTE_G_SHARP_2, "QK_MIDI_NOTE_G_SHARP_2"}, -    {QK_MIDI_NOTE_A_2, "QK_MIDI_NOTE_A_2"}, -    {QK_MIDI_NOTE_A_SHARP_2, "QK_MIDI_NOTE_A_SHARP_2"}, -    {QK_MIDI_NOTE_B_2, "QK_MIDI_NOTE_B_2"}, -    {QK_MIDI_NOTE_C_3, "QK_MIDI_NOTE_C_3"}, -    {QK_MIDI_NOTE_C_SHARP_3, "QK_MIDI_NOTE_C_SHARP_3"}, -    {QK_MIDI_NOTE_D_3, "QK_MIDI_NOTE_D_3"}, -    {QK_MIDI_NOTE_D_SHARP_3, "QK_MIDI_NOTE_D_SHARP_3"}, -    {QK_MIDI_NOTE_E_3, "QK_MIDI_NOTE_E_3"}, -    {QK_MIDI_NOTE_F_3, "QK_MIDI_NOTE_F_3"}, -    {QK_MIDI_NOTE_F_SHARP_3, "QK_MIDI_NOTE_F_SHARP_3"}, -    {QK_MIDI_NOTE_G_3, "QK_MIDI_NOTE_G_3"}, -    {QK_MIDI_NOTE_G_SHARP_3, "QK_MIDI_NOTE_G_SHARP_3"}, -    {QK_MIDI_NOTE_A_3, "QK_MIDI_NOTE_A_3"}, -    {QK_MIDI_NOTE_A_SHARP_3, "QK_MIDI_NOTE_A_SHARP_3"}, -    {QK_MIDI_NOTE_B_3, "QK_MIDI_NOTE_B_3"}, -    {QK_MIDI_NOTE_C_4, "QK_MIDI_NOTE_C_4"}, -    {QK_MIDI_NOTE_C_SHARP_4, "QK_MIDI_NOTE_C_SHARP_4"}, -    {QK_MIDI_NOTE_D_4, "QK_MIDI_NOTE_D_4"}, -    {QK_MIDI_NOTE_D_SHARP_4, "QK_MIDI_NOTE_D_SHARP_4"}, -    {QK_MIDI_NOTE_E_4, "QK_MIDI_NOTE_E_4"}, -    {QK_MIDI_NOTE_F_4, "QK_MIDI_NOTE_F_4"}, -    {QK_MIDI_NOTE_F_SHARP_4, "QK_MIDI_NOTE_F_SHARP_4"}, -    {QK_MIDI_NOTE_G_4, "QK_MIDI_NOTE_G_4"}, -    {QK_MIDI_NOTE_G_SHARP_4, "QK_MIDI_NOTE_G_SHARP_4"}, -    {QK_MIDI_NOTE_A_4, "QK_MIDI_NOTE_A_4"}, -    {QK_MIDI_NOTE_A_SHARP_4, "QK_MIDI_NOTE_A_SHARP_4"}, -    {QK_MIDI_NOTE_B_4, "QK_MIDI_NOTE_B_4"}, -    {QK_MIDI_NOTE_C_5, "QK_MIDI_NOTE_C_5"}, -    {QK_MIDI_NOTE_C_SHARP_5, "QK_MIDI_NOTE_C_SHARP_5"}, -    {QK_MIDI_NOTE_D_5, "QK_MIDI_NOTE_D_5"}, -    {QK_MIDI_NOTE_D_SHARP_5, "QK_MIDI_NOTE_D_SHARP_5"}, -    {QK_MIDI_NOTE_E_5, "QK_MIDI_NOTE_E_5"}, -    {QK_MIDI_NOTE_F_5, "QK_MIDI_NOTE_F_5"}, -    {QK_MIDI_NOTE_F_SHARP_5, "QK_MIDI_NOTE_F_SHARP_5"}, -    {QK_MIDI_NOTE_G_5, "QK_MIDI_NOTE_G_5"}, -    {QK_MIDI_NOTE_G_SHARP_5, "QK_MIDI_NOTE_G_SHARP_5"}, -    {QK_MIDI_NOTE_A_5, "QK_MIDI_NOTE_A_5"}, -    {QK_MIDI_NOTE_A_SHARP_5, "QK_MIDI_NOTE_A_SHARP_5"}, -    {QK_MIDI_NOTE_B_5, "QK_MIDI_NOTE_B_5"}, -    {QK_MIDI_OCTAVE_N2, "QK_MIDI_OCTAVE_N2"}, -    {QK_MIDI_OCTAVE_N1, "QK_MIDI_OCTAVE_N1"}, -    {QK_MIDI_OCTAVE_0, "QK_MIDI_OCTAVE_0"}, -    {QK_MIDI_OCTAVE_1, "QK_MIDI_OCTAVE_1"}, -    {QK_MIDI_OCTAVE_2, "QK_MIDI_OCTAVE_2"}, -    {QK_MIDI_OCTAVE_3, "QK_MIDI_OCTAVE_3"}, -    {QK_MIDI_OCTAVE_4, "QK_MIDI_OCTAVE_4"}, -    {QK_MIDI_OCTAVE_5, "QK_MIDI_OCTAVE_5"}, -    {QK_MIDI_OCTAVE_6, "QK_MIDI_OCTAVE_6"}, -    {QK_MIDI_OCTAVE_7, "QK_MIDI_OCTAVE_7"}, -    {QK_MIDI_OCTAVE_DOWN, "QK_MIDI_OCTAVE_DOWN"}, -    {QK_MIDI_OCTAVE_UP, "QK_MIDI_OCTAVE_UP"}, -    {QK_MIDI_TRANSPOSE_N6, "QK_MIDI_TRANSPOSE_N6"}, -    {QK_MIDI_TRANSPOSE_N5, "QK_MIDI_TRANSPOSE_N5"}, -    {QK_MIDI_TRANSPOSE_N4, "QK_MIDI_TRANSPOSE_N4"}, -    {QK_MIDI_TRANSPOSE_N3, "QK_MIDI_TRANSPOSE_N3"}, -    {QK_MIDI_TRANSPOSE_N2, "QK_MIDI_TRANSPOSE_N2"}, -    {QK_MIDI_TRANSPOSE_N1, "QK_MIDI_TRANSPOSE_N1"}, -    {QK_MIDI_TRANSPOSE_0, "QK_MIDI_TRANSPOSE_0"}, -    {QK_MIDI_TRANSPOSE_1, "QK_MIDI_TRANSPOSE_1"}, -    {QK_MIDI_TRANSPOSE_2, "QK_MIDI_TRANSPOSE_2"}, -    {QK_MIDI_TRANSPOSE_3, "QK_MIDI_TRANSPOSE_3"}, -    {QK_MIDI_TRANSPOSE_4, "QK_MIDI_TRANSPOSE_4"}, -    {QK_MIDI_TRANSPOSE_5, "QK_MIDI_TRANSPOSE_5"}, -    {QK_MIDI_TRANSPOSE_6, "QK_MIDI_TRANSPOSE_6"}, -    {QK_MIDI_TRANSPOSE_DOWN, "QK_MIDI_TRANSPOSE_DOWN"}, -    {QK_MIDI_TRANSPOSE_UP, "QK_MIDI_TRANSPOSE_UP"}, -    {QK_MIDI_VELOCITY_0, "QK_MIDI_VELOCITY_0"}, -    {QK_MIDI_VELOCITY_1, "QK_MIDI_VELOCITY_1"}, -    {QK_MIDI_VELOCITY_2, "QK_MIDI_VELOCITY_2"}, -    {QK_MIDI_VELOCITY_3, "QK_MIDI_VELOCITY_3"}, -    {QK_MIDI_VELOCITY_4, "QK_MIDI_VELOCITY_4"}, -    {QK_MIDI_VELOCITY_5, "QK_MIDI_VELOCITY_5"}, -    {QK_MIDI_VELOCITY_6, "QK_MIDI_VELOCITY_6"}, -    {QK_MIDI_VELOCITY_7, "QK_MIDI_VELOCITY_7"}, -    {QK_MIDI_VELOCITY_8, "QK_MIDI_VELOCITY_8"}, -    {QK_MIDI_VELOCITY_9, "QK_MIDI_VELOCITY_9"}, -    {QK_MIDI_VELOCITY_10, "QK_MIDI_VELOCITY_10"}, -    {QK_MIDI_VELOCITY_DOWN, "QK_MIDI_VELOCITY_DOWN"}, -    {QK_MIDI_VELOCITY_UP, "QK_MIDI_VELOCITY_UP"}, -    {QK_MIDI_CHANNEL_1, "QK_MIDI_CHANNEL_1"}, -    {QK_MIDI_CHANNEL_2, "QK_MIDI_CHANNEL_2"}, -    {QK_MIDI_CHANNEL_3, "QK_MIDI_CHANNEL_3"}, -    {QK_MIDI_CHANNEL_4, "QK_MIDI_CHANNEL_4"}, -    {QK_MIDI_CHANNEL_5, "QK_MIDI_CHANNEL_5"}, -    {QK_MIDI_CHANNEL_6, "QK_MIDI_CHANNEL_6"}, -    {QK_MIDI_CHANNEL_7, "QK_MIDI_CHANNEL_7"}, -    {QK_MIDI_CHANNEL_8, "QK_MIDI_CHANNEL_8"}, -    {QK_MIDI_CHANNEL_9, "QK_MIDI_CHANNEL_9"}, -    {QK_MIDI_CHANNEL_10, "QK_MIDI_CHANNEL_10"}, -    {QK_MIDI_CHANNEL_11, "QK_MIDI_CHANNEL_11"}, -    {QK_MIDI_CHANNEL_12, "QK_MIDI_CHANNEL_12"}, -    {QK_MIDI_CHANNEL_13, "QK_MIDI_CHANNEL_13"}, -    {QK_MIDI_CHANNEL_14, "QK_MIDI_CHANNEL_14"}, -    {QK_MIDI_CHANNEL_15, "QK_MIDI_CHANNEL_15"}, -    {QK_MIDI_CHANNEL_16, "QK_MIDI_CHANNEL_16"}, -    {QK_MIDI_CHANNEL_DOWN, "QK_MIDI_CHANNEL_DOWN"}, -    {QK_MIDI_CHANNEL_UP, "QK_MIDI_CHANNEL_UP"}, -    {QK_MIDI_ALL_NOTES_OFF, "QK_MIDI_ALL_NOTES_OFF"}, -    {QK_MIDI_SUSTAIN, "QK_MIDI_SUSTAIN"}, -    {QK_MIDI_PORTAMENTO, "QK_MIDI_PORTAMENTO"}, -    {QK_MIDI_SOSTENUTO, "QK_MIDI_SOSTENUTO"}, -    {QK_MIDI_SOFT, "QK_MIDI_SOFT"}, -    {QK_MIDI_LEGATO, "QK_MIDI_LEGATO"}, -    {QK_MIDI_MODULATION, "QK_MIDI_MODULATION"}, -    {QK_MIDI_MODULATION_SPEED_DOWN, "QK_MIDI_MODULATION_SPEED_DOWN"}, -    {QK_MIDI_MODULATION_SPEED_UP, "QK_MIDI_MODULATION_SPEED_UP"}, -    {QK_MIDI_PITCH_BEND_DOWN, "QK_MIDI_PITCH_BEND_DOWN"}, -    {QK_MIDI_PITCH_BEND_UP, "QK_MIDI_PITCH_BEND_UP"}, -    {QK_SEQUENCER_ON, "QK_SEQUENCER_ON"}, -    {QK_SEQUENCER_OFF, "QK_SEQUENCER_OFF"}, -    {QK_SEQUENCER_TOGGLE, "QK_SEQUENCER_TOGGLE"}, -    {QK_SEQUENCER_TEMPO_DOWN, "QK_SEQUENCER_TEMPO_DOWN"}, -    {QK_SEQUENCER_TEMPO_UP, "QK_SEQUENCER_TEMPO_UP"}, -    {QK_SEQUENCER_RESOLUTION_DOWN, "QK_SEQUENCER_RESOLUTION_DOWN"}, -    {QK_SEQUENCER_RESOLUTION_UP, "QK_SEQUENCER_RESOLUTION_UP"}, -    {QK_SEQUENCER_STEPS_ALL, "QK_SEQUENCER_STEPS_ALL"}, -    {QK_SEQUENCER_STEPS_CLEAR, "QK_SEQUENCER_STEPS_CLEAR"}, -    {QK_JOYSTICK_BUTTON_0, "QK_JOYSTICK_BUTTON_0"}, -    {QK_JOYSTICK_BUTTON_1, "QK_JOYSTICK_BUTTON_1"}, -    {QK_JOYSTICK_BUTTON_2, "QK_JOYSTICK_BUTTON_2"}, -    {QK_JOYSTICK_BUTTON_3, "QK_JOYSTICK_BUTTON_3"}, -    {QK_JOYSTICK_BUTTON_4, "QK_JOYSTICK_BUTTON_4"}, -    {QK_JOYSTICK_BUTTON_5, "QK_JOYSTICK_BUTTON_5"}, -    {QK_JOYSTICK_BUTTON_6, "QK_JOYSTICK_BUTTON_6"}, -    {QK_JOYSTICK_BUTTON_7, "QK_JOYSTICK_BUTTON_7"}, -    {QK_JOYSTICK_BUTTON_8, "QK_JOYSTICK_BUTTON_8"}, -    {QK_JOYSTICK_BUTTON_9, "QK_JOYSTICK_BUTTON_9"}, -    {QK_JOYSTICK_BUTTON_10, "QK_JOYSTICK_BUTTON_10"}, -    {QK_JOYSTICK_BUTTON_11, "QK_JOYSTICK_BUTTON_11"}, -    {QK_JOYSTICK_BUTTON_12, "QK_JOYSTICK_BUTTON_12"}, -    {QK_JOYSTICK_BUTTON_13, "QK_JOYSTICK_BUTTON_13"}, -    {QK_JOYSTICK_BUTTON_14, "QK_JOYSTICK_BUTTON_14"}, -    {QK_JOYSTICK_BUTTON_15, "QK_JOYSTICK_BUTTON_15"}, -    {QK_JOYSTICK_BUTTON_16, "QK_JOYSTICK_BUTTON_16"}, -    {QK_JOYSTICK_BUTTON_17, "QK_JOYSTICK_BUTTON_17"}, -    {QK_JOYSTICK_BUTTON_18, "QK_JOYSTICK_BUTTON_18"}, -    {QK_JOYSTICK_BUTTON_19, "QK_JOYSTICK_BUTTON_19"}, -    {QK_JOYSTICK_BUTTON_20, "QK_JOYSTICK_BUTTON_20"}, -    {QK_JOYSTICK_BUTTON_21, "QK_JOYSTICK_BUTTON_21"}, -    {QK_JOYSTICK_BUTTON_22, "QK_JOYSTICK_BUTTON_22"}, -    {QK_JOYSTICK_BUTTON_23, "QK_JOYSTICK_BUTTON_23"}, -    {QK_JOYSTICK_BUTTON_24, "QK_JOYSTICK_BUTTON_24"}, -    {QK_JOYSTICK_BUTTON_25, "QK_JOYSTICK_BUTTON_25"}, -    {QK_JOYSTICK_BUTTON_26, "QK_JOYSTICK_BUTTON_26"}, -    {QK_JOYSTICK_BUTTON_27, "QK_JOYSTICK_BUTTON_27"}, -    {QK_JOYSTICK_BUTTON_28, "QK_JOYSTICK_BUTTON_28"}, -    {QK_JOYSTICK_BUTTON_29, "QK_JOYSTICK_BUTTON_29"}, -    {QK_JOYSTICK_BUTTON_30, "QK_JOYSTICK_BUTTON_30"}, -    {QK_JOYSTICK_BUTTON_31, "QK_JOYSTICK_BUTTON_31"}, -    {QK_PROGRAMMABLE_BUTTON_1, "QK_PROGRAMMABLE_BUTTON_1"}, -    {QK_PROGRAMMABLE_BUTTON_2, "QK_PROGRAMMABLE_BUTTON_2"}, -    {QK_PROGRAMMABLE_BUTTON_3, "QK_PROGRAMMABLE_BUTTON_3"}, -    {QK_PROGRAMMABLE_BUTTON_4, "QK_PROGRAMMABLE_BUTTON_4"}, -    {QK_PROGRAMMABLE_BUTTON_5, "QK_PROGRAMMABLE_BUTTON_5"}, -    {QK_PROGRAMMABLE_BUTTON_6, "QK_PROGRAMMABLE_BUTTON_6"}, -    {QK_PROGRAMMABLE_BUTTON_7, "QK_PROGRAMMABLE_BUTTON_7"}, -    {QK_PROGRAMMABLE_BUTTON_8, "QK_PROGRAMMABLE_BUTTON_8"}, -    {QK_PROGRAMMABLE_BUTTON_9, "QK_PROGRAMMABLE_BUTTON_9"}, -    {QK_PROGRAMMABLE_BUTTON_10, "QK_PROGRAMMABLE_BUTTON_10"}, -    {QK_PROGRAMMABLE_BUTTON_11, "QK_PROGRAMMABLE_BUTTON_11"}, -    {QK_PROGRAMMABLE_BUTTON_12, "QK_PROGRAMMABLE_BUTTON_12"}, -    {QK_PROGRAMMABLE_BUTTON_13, "QK_PROGRAMMABLE_BUTTON_13"}, -    {QK_PROGRAMMABLE_BUTTON_14, "QK_PROGRAMMABLE_BUTTON_14"}, -    {QK_PROGRAMMABLE_BUTTON_15, "QK_PROGRAMMABLE_BUTTON_15"}, -    {QK_PROGRAMMABLE_BUTTON_16, "QK_PROGRAMMABLE_BUTTON_16"}, -    {QK_PROGRAMMABLE_BUTTON_17, "QK_PROGRAMMABLE_BUTTON_17"}, -    {QK_PROGRAMMABLE_BUTTON_18, "QK_PROGRAMMABLE_BUTTON_18"}, -    {QK_PROGRAMMABLE_BUTTON_19, "QK_PROGRAMMABLE_BUTTON_19"}, -    {QK_PROGRAMMABLE_BUTTON_20, "QK_PROGRAMMABLE_BUTTON_20"}, -    {QK_PROGRAMMABLE_BUTTON_21, "QK_PROGRAMMABLE_BUTTON_21"}, -    {QK_PROGRAMMABLE_BUTTON_22, "QK_PROGRAMMABLE_BUTTON_22"}, -    {QK_PROGRAMMABLE_BUTTON_23, "QK_PROGRAMMABLE_BUTTON_23"}, -    {QK_PROGRAMMABLE_BUTTON_24, "QK_PROGRAMMABLE_BUTTON_24"}, -    {QK_PROGRAMMABLE_BUTTON_25, "QK_PROGRAMMABLE_BUTTON_25"}, -    {QK_PROGRAMMABLE_BUTTON_26, "QK_PROGRAMMABLE_BUTTON_26"}, -    {QK_PROGRAMMABLE_BUTTON_27, "QK_PROGRAMMABLE_BUTTON_27"}, -    {QK_PROGRAMMABLE_BUTTON_28, "QK_PROGRAMMABLE_BUTTON_28"}, -    {QK_PROGRAMMABLE_BUTTON_29, "QK_PROGRAMMABLE_BUTTON_29"}, -    {QK_PROGRAMMABLE_BUTTON_30, "QK_PROGRAMMABLE_BUTTON_30"}, -    {QK_PROGRAMMABLE_BUTTON_31, "QK_PROGRAMMABLE_BUTTON_31"}, -    {QK_PROGRAMMABLE_BUTTON_32, "QK_PROGRAMMABLE_BUTTON_32"}, -    {QK_AUDIO_ON, "QK_AUDIO_ON"}, -    {QK_AUDIO_OFF, "QK_AUDIO_OFF"}, -    {QK_AUDIO_TOGGLE, "QK_AUDIO_TOGGLE"}, -    {QK_AUDIO_CLICKY_TOGGLE, "QK_AUDIO_CLICKY_TOGGLE"}, -    {QK_AUDIO_CLICKY_ON, "QK_AUDIO_CLICKY_ON"}, -    {QK_AUDIO_CLICKY_OFF, "QK_AUDIO_CLICKY_OFF"}, -    {QK_AUDIO_CLICKY_UP, "QK_AUDIO_CLICKY_UP"}, -    {QK_AUDIO_CLICKY_DOWN, "QK_AUDIO_CLICKY_DOWN"}, -    {QK_AUDIO_CLICKY_RESET, "QK_AUDIO_CLICKY_RESET"}, -    {QK_MUSIC_ON, "QK_MUSIC_ON"}, -    {QK_MUSIC_OFF, "QK_MUSIC_OFF"}, -    {QK_MUSIC_TOGGLE, "QK_MUSIC_TOGGLE"}, -    {QK_MUSIC_MODE_NEXT, "QK_MUSIC_MODE_NEXT"}, -    {QK_AUDIO_VOICE_NEXT, "QK_AUDIO_VOICE_NEXT"}, -    {QK_AUDIO_VOICE_PREVIOUS, "QK_AUDIO_VOICE_PREVIOUS"}, -    {QK_STENO_BOLT, "QK_STENO_BOLT"}, -    {QK_STENO_GEMINI, "QK_STENO_GEMINI"}, -    {QK_STENO_COMB, "QK_STENO_COMB"}, -    {QK_STENO_COMB_MAX, "QK_STENO_COMB_MAX"}, -    {QK_MACRO_0, "QK_MACRO_0"}, -    {QK_MACRO_1, "QK_MACRO_1"}, -    {QK_MACRO_2, "QK_MACRO_2"}, -    {QK_MACRO_3, "QK_MACRO_3"}, -    {QK_MACRO_4, "QK_MACRO_4"}, -    {QK_MACRO_5, "QK_MACRO_5"}, -    {QK_MACRO_6, "QK_MACRO_6"}, -    {QK_MACRO_7, "QK_MACRO_7"}, -    {QK_MACRO_8, "QK_MACRO_8"}, -    {QK_MACRO_9, "QK_MACRO_9"}, -    {QK_MACRO_10, "QK_MACRO_10"}, -    {QK_MACRO_11, "QK_MACRO_11"}, -    {QK_MACRO_12, "QK_MACRO_12"}, -    {QK_MACRO_13, "QK_MACRO_13"}, -    {QK_MACRO_14, "QK_MACRO_14"}, -    {QK_MACRO_15, "QK_MACRO_15"}, -    {QK_MACRO_16, "QK_MACRO_16"}, -    {QK_MACRO_17, "QK_MACRO_17"}, -    {QK_MACRO_18, "QK_MACRO_18"}, -    {QK_MACRO_19, "QK_MACRO_19"}, -    {QK_MACRO_20, "QK_MACRO_20"}, -    {QK_MACRO_21, "QK_MACRO_21"}, -    {QK_MACRO_22, "QK_MACRO_22"}, -    {QK_MACRO_23, "QK_MACRO_23"}, -    {QK_MACRO_24, "QK_MACRO_24"}, -    {QK_MACRO_25, "QK_MACRO_25"}, -    {QK_MACRO_26, "QK_MACRO_26"}, -    {QK_MACRO_27, "QK_MACRO_27"}, -    {QK_MACRO_28, "QK_MACRO_28"}, -    {QK_MACRO_29, "QK_MACRO_29"}, -    {QK_MACRO_30, "QK_MACRO_30"}, -    {QK_MACRO_31, "QK_MACRO_31"}, -    {QK_OUTPUT_AUTO, "QK_OUTPUT_AUTO"}, -    {QK_OUTPUT_NEXT, "QK_OUTPUT_NEXT"}, -    {QK_OUTPUT_PREV, "QK_OUTPUT_PREV"}, -    {QK_OUTPUT_NONE, "QK_OUTPUT_NONE"}, -    {QK_OUTPUT_USB, "QK_OUTPUT_USB"}, -    {QK_OUTPUT_2P4GHZ, "QK_OUTPUT_2P4GHZ"}, -    {QK_OUTPUT_BLUETOOTH, "QK_OUTPUT_BLUETOOTH"}, -    {QK_BLUETOOTH_PROFILE_NEXT, "QK_BLUETOOTH_PROFILE_NEXT"}, -    {QK_BLUETOOTH_PROFILE_PREV, "QK_BLUETOOTH_PROFILE_PREV"}, -    {QK_BLUETOOTH_UNPAIR, "QK_BLUETOOTH_UNPAIR"}, -    {QK_BLUETOOTH_PROFILE1, "QK_BLUETOOTH_PROFILE1"}, -    {QK_BLUETOOTH_PROFILE2, "QK_BLUETOOTH_PROFILE2"}, -    {QK_BLUETOOTH_PROFILE3, "QK_BLUETOOTH_PROFILE3"}, -    {QK_BLUETOOTH_PROFILE4, "QK_BLUETOOTH_PROFILE4"}, -    {QK_BLUETOOTH_PROFILE5, "QK_BLUETOOTH_PROFILE5"}, -    {QK_BACKLIGHT_ON, "QK_BACKLIGHT_ON"}, -    {QK_BACKLIGHT_OFF, "QK_BACKLIGHT_OFF"}, -    {QK_BACKLIGHT_TOGGLE, "QK_BACKLIGHT_TOGGLE"}, -    {QK_BACKLIGHT_DOWN, "QK_BACKLIGHT_DOWN"}, -    {QK_BACKLIGHT_UP, "QK_BACKLIGHT_UP"}, -    {QK_BACKLIGHT_STEP, "QK_BACKLIGHT_STEP"}, -    {QK_BACKLIGHT_TOGGLE_BREATHING, "QK_BACKLIGHT_TOGGLE_BREATHING"}, -    {QK_LED_MATRIX_ON, "QK_LED_MATRIX_ON"}, -    {QK_LED_MATRIX_OFF, "QK_LED_MATRIX_OFF"}, -    {QK_LED_MATRIX_TOGGLE, "QK_LED_MATRIX_TOGGLE"}, -    {QK_LED_MATRIX_MODE_NEXT, "QK_LED_MATRIX_MODE_NEXT"}, -    {QK_LED_MATRIX_MODE_PREVIOUS, "QK_LED_MATRIX_MODE_PREVIOUS"}, -    {QK_LED_MATRIX_BRIGHTNESS_UP, "QK_LED_MATRIX_BRIGHTNESS_UP"}, -    {QK_LED_MATRIX_BRIGHTNESS_DOWN, "QK_LED_MATRIX_BRIGHTNESS_DOWN"}, -    {QK_LED_MATRIX_SPEED_UP, "QK_LED_MATRIX_SPEED_UP"}, -    {QK_LED_MATRIX_SPEED_DOWN, "QK_LED_MATRIX_SPEED_DOWN"}, -    {QK_UNDERGLOW_TOGGLE, "QK_UNDERGLOW_TOGGLE"}, -    {QK_UNDERGLOW_MODE_NEXT, "QK_UNDERGLOW_MODE_NEXT"}, -    {QK_UNDERGLOW_MODE_PREVIOUS, "QK_UNDERGLOW_MODE_PREVIOUS"}, -    {QK_UNDERGLOW_HUE_UP, "QK_UNDERGLOW_HUE_UP"}, -    {QK_UNDERGLOW_HUE_DOWN, "QK_UNDERGLOW_HUE_DOWN"}, -    {QK_UNDERGLOW_SATURATION_UP, "QK_UNDERGLOW_SATURATION_UP"}, -    {QK_UNDERGLOW_SATURATION_DOWN, "QK_UNDERGLOW_SATURATION_DOWN"}, -    {QK_UNDERGLOW_VALUE_UP, "QK_UNDERGLOW_VALUE_UP"}, -    {QK_UNDERGLOW_VALUE_DOWN, "QK_UNDERGLOW_VALUE_DOWN"}, -    {QK_UNDERGLOW_SPEED_UP, "QK_UNDERGLOW_SPEED_UP"}, -    {QK_UNDERGLOW_SPEED_DOWN, "QK_UNDERGLOW_SPEED_DOWN"}, -    {RGB_MODE_PLAIN, "RGB_MODE_PLAIN"}, -    {RGB_MODE_BREATHE, "RGB_MODE_BREATHE"}, -    {RGB_MODE_RAINBOW, "RGB_MODE_RAINBOW"}, -    {RGB_MODE_SWIRL, "RGB_MODE_SWIRL"}, -    {RGB_MODE_SNAKE, "RGB_MODE_SNAKE"}, -    {RGB_MODE_KNIGHT, "RGB_MODE_KNIGHT"}, -    {RGB_MODE_XMAS, "RGB_MODE_XMAS"}, -    {RGB_MODE_GRADIENT, "RGB_MODE_GRADIENT"}, -    {RGB_MODE_RGBTEST, "RGB_MODE_RGBTEST"}, -    {RGB_MODE_TWINKLE, "RGB_MODE_TWINKLE"}, -    {QK_RGB_MATRIX_ON, "QK_RGB_MATRIX_ON"}, -    {QK_RGB_MATRIX_OFF, "QK_RGB_MATRIX_OFF"}, -    {QK_RGB_MATRIX_TOGGLE, "QK_RGB_MATRIX_TOGGLE"}, -    {QK_RGB_MATRIX_MODE_NEXT, "QK_RGB_MATRIX_MODE_NEXT"}, -    {QK_RGB_MATRIX_MODE_PREVIOUS, "QK_RGB_MATRIX_MODE_PREVIOUS"}, -    {QK_RGB_MATRIX_HUE_UP, "QK_RGB_MATRIX_HUE_UP"}, -    {QK_RGB_MATRIX_HUE_DOWN, "QK_RGB_MATRIX_HUE_DOWN"}, -    {QK_RGB_MATRIX_SATURATION_UP, "QK_RGB_MATRIX_SATURATION_UP"}, -    {QK_RGB_MATRIX_SATURATION_DOWN, "QK_RGB_MATRIX_SATURATION_DOWN"}, -    {QK_RGB_MATRIX_VALUE_UP, "QK_RGB_MATRIX_VALUE_UP"}, -    {QK_RGB_MATRIX_VALUE_DOWN, "QK_RGB_MATRIX_VALUE_DOWN"}, -    {QK_RGB_MATRIX_SPEED_UP, "QK_RGB_MATRIX_SPEED_UP"}, -    {QK_RGB_MATRIX_SPEED_DOWN, "QK_RGB_MATRIX_SPEED_DOWN"}, -    {QK_BOOTLOADER, "QK_BOOTLOADER"}, -    {QK_REBOOT, "QK_REBOOT"}, -    {QK_DEBUG_TOGGLE, "QK_DEBUG_TOGGLE"}, -    {QK_CLEAR_EEPROM, "QK_CLEAR_EEPROM"}, -    {QK_MAKE, "QK_MAKE"}, -    {QK_AUTO_SHIFT_DOWN, "QK_AUTO_SHIFT_DOWN"}, -    {QK_AUTO_SHIFT_UP, "QK_AUTO_SHIFT_UP"}, -    {QK_AUTO_SHIFT_REPORT, "QK_AUTO_SHIFT_REPORT"}, -    {QK_AUTO_SHIFT_ON, "QK_AUTO_SHIFT_ON"}, -    {QK_AUTO_SHIFT_OFF, "QK_AUTO_SHIFT_OFF"}, -    {QK_AUTO_SHIFT_TOGGLE, "QK_AUTO_SHIFT_TOGGLE"}, -    {QK_GRAVE_ESCAPE, "QK_GRAVE_ESCAPE"}, -    {QK_VELOCIKEY_TOGGLE, "QK_VELOCIKEY_TOGGLE"}, -    {QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN, "QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN"}, -    {QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE, "QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE"}, -    {QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN, "QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN"}, -    {QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE, "QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE"}, -    {QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN, "QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN"}, -    {QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE, "QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE"}, -    {QK_SPACE_CADET_RIGHT_SHIFT_ENTER, "QK_SPACE_CADET_RIGHT_SHIFT_ENTER"}, -    {QK_UNICODE_MODE_NEXT, "QK_UNICODE_MODE_NEXT"}, -    {QK_UNICODE_MODE_PREVIOUS, "QK_UNICODE_MODE_PREVIOUS"}, -    {QK_UNICODE_MODE_MACOS, "QK_UNICODE_MODE_MACOS"}, -    {QK_UNICODE_MODE_LINUX, "QK_UNICODE_MODE_LINUX"}, -    {QK_UNICODE_MODE_WINDOWS, "QK_UNICODE_MODE_WINDOWS"}, -    {QK_UNICODE_MODE_BSD, "QK_UNICODE_MODE_BSD"}, -    {QK_UNICODE_MODE_WINCOMPOSE, "QK_UNICODE_MODE_WINCOMPOSE"}, -    {QK_UNICODE_MODE_EMACS, "QK_UNICODE_MODE_EMACS"}, -    {QK_HAPTIC_ON, "QK_HAPTIC_ON"}, -    {QK_HAPTIC_OFF, "QK_HAPTIC_OFF"}, -    {QK_HAPTIC_TOGGLE, "QK_HAPTIC_TOGGLE"}, -    {QK_HAPTIC_RESET, "QK_HAPTIC_RESET"}, -    {QK_HAPTIC_FEEDBACK_TOGGLE, "QK_HAPTIC_FEEDBACK_TOGGLE"}, -    {QK_HAPTIC_BUZZ_TOGGLE, "QK_HAPTIC_BUZZ_TOGGLE"}, -    {QK_HAPTIC_MODE_NEXT, "QK_HAPTIC_MODE_NEXT"}, -    {QK_HAPTIC_MODE_PREVIOUS, "QK_HAPTIC_MODE_PREVIOUS"}, -    {QK_HAPTIC_CONTINUOUS_TOGGLE, "QK_HAPTIC_CONTINUOUS_TOGGLE"}, -    {QK_HAPTIC_CONTINUOUS_UP, "QK_HAPTIC_CONTINUOUS_UP"}, -    {QK_HAPTIC_CONTINUOUS_DOWN, "QK_HAPTIC_CONTINUOUS_DOWN"}, -    {QK_HAPTIC_DWELL_UP, "QK_HAPTIC_DWELL_UP"}, -    {QK_HAPTIC_DWELL_DOWN, "QK_HAPTIC_DWELL_DOWN"}, -    {QK_COMBO_ON, "QK_COMBO_ON"}, -    {QK_COMBO_OFF, "QK_COMBO_OFF"}, -    {QK_COMBO_TOGGLE, "QK_COMBO_TOGGLE"}, -    {QK_DYNAMIC_MACRO_RECORD_START_1, "QK_DYNAMIC_MACRO_RECORD_START_1"}, -    {QK_DYNAMIC_MACRO_RECORD_START_2, "QK_DYNAMIC_MACRO_RECORD_START_2"}, -    {QK_DYNAMIC_MACRO_RECORD_STOP, "QK_DYNAMIC_MACRO_RECORD_STOP"}, -    {QK_DYNAMIC_MACRO_PLAY_1, "QK_DYNAMIC_MACRO_PLAY_1"}, -    {QK_DYNAMIC_MACRO_PLAY_2, "QK_DYNAMIC_MACRO_PLAY_2"}, -    {QK_LEADER, "QK_LEADER"}, -    {QK_LOCK, "QK_LOCK"}, -    {QK_ONE_SHOT_ON, "QK_ONE_SHOT_ON"}, -    {QK_ONE_SHOT_OFF, "QK_ONE_SHOT_OFF"}, -    {QK_ONE_SHOT_TOGGLE, "QK_ONE_SHOT_TOGGLE"}, -    {QK_KEY_OVERRIDE_TOGGLE, "QK_KEY_OVERRIDE_TOGGLE"}, -    {QK_KEY_OVERRIDE_ON, "QK_KEY_OVERRIDE_ON"}, -    {QK_KEY_OVERRIDE_OFF, "QK_KEY_OVERRIDE_OFF"}, -    {QK_SECURE_LOCK, "QK_SECURE_LOCK"}, -    {QK_SECURE_UNLOCK, "QK_SECURE_UNLOCK"}, -    {QK_SECURE_TOGGLE, "QK_SECURE_TOGGLE"}, -    {QK_SECURE_REQUEST, "QK_SECURE_REQUEST"}, -    {QK_DYNAMIC_TAPPING_TERM_PRINT, "QK_DYNAMIC_TAPPING_TERM_PRINT"}, -    {QK_DYNAMIC_TAPPING_TERM_UP, "QK_DYNAMIC_TAPPING_TERM_UP"}, -    {QK_DYNAMIC_TAPPING_TERM_DOWN, "QK_DYNAMIC_TAPPING_TERM_DOWN"}, -    {QK_CAPS_WORD_TOGGLE, "QK_CAPS_WORD_TOGGLE"}, -    {QK_AUTOCORRECT_ON, "QK_AUTOCORRECT_ON"}, -    {QK_AUTOCORRECT_OFF, "QK_AUTOCORRECT_OFF"}, -    {QK_AUTOCORRECT_TOGGLE, "QK_AUTOCORRECT_TOGGLE"}, -    {QK_TRI_LAYER_LOWER, "QK_TRI_LAYER_LOWER"}, -    {QK_TRI_LAYER_UPPER, "QK_TRI_LAYER_UPPER"}, -    {QK_REPEAT_KEY, "QK_REPEAT_KEY"}, -    {QK_ALT_REPEAT_KEY, "QK_ALT_REPEAT_KEY"}, -    {QK_LAYER_LOCK, "QK_LAYER_LOCK"}, -    {QK_KB_0, "QK_KB_0"}, -    {QK_KB_1, "QK_KB_1"}, -    {QK_KB_2, "QK_KB_2"}, -    {QK_KB_3, "QK_KB_3"}, -    {QK_KB_4, "QK_KB_4"}, -    {QK_KB_5, "QK_KB_5"}, -    {QK_KB_6, "QK_KB_6"}, -    {QK_KB_7, "QK_KB_7"}, -    {QK_KB_8, "QK_KB_8"}, -    {QK_KB_9, "QK_KB_9"}, -    {QK_KB_10, "QK_KB_10"}, -    {QK_KB_11, "QK_KB_11"}, -    {QK_KB_12, "QK_KB_12"}, -    {QK_KB_13, "QK_KB_13"}, -    {QK_KB_14, "QK_KB_14"}, -    {QK_KB_15, "QK_KB_15"}, -    {QK_KB_16, "QK_KB_16"}, -    {QK_KB_17, "QK_KB_17"}, -    {QK_KB_18, "QK_KB_18"}, -    {QK_KB_19, "QK_KB_19"}, -    {QK_KB_20, "QK_KB_20"}, -    {QK_KB_21, "QK_KB_21"}, -    {QK_KB_22, "QK_KB_22"}, -    {QK_KB_23, "QK_KB_23"}, -    {QK_KB_24, "QK_KB_24"}, -    {QK_KB_25, "QK_KB_25"}, -    {QK_KB_26, "QK_KB_26"}, -    {QK_KB_27, "QK_KB_27"}, -    {QK_KB_28, "QK_KB_28"}, -    {QK_KB_29, "QK_KB_29"}, -    {QK_KB_30, "QK_KB_30"}, -    {QK_KB_31, "QK_KB_31"}, -    {QK_USER_0, "QK_USER_0"}, -    {QK_USER_1, "QK_USER_1"}, -    {QK_USER_2, "QK_USER_2"}, -    {QK_USER_3, "QK_USER_3"}, -    {QK_USER_4, "QK_USER_4"}, -    {QK_USER_5, "QK_USER_5"}, -    {QK_USER_6, "QK_USER_6"}, -    {QK_USER_7, "QK_USER_7"}, -    {QK_USER_8, "QK_USER_8"}, -    {QK_USER_9, "QK_USER_9"}, -    {QK_USER_10, "QK_USER_10"}, -    {QK_USER_11, "QK_USER_11"}, -    {QK_USER_12, "QK_USER_12"}, -    {QK_USER_13, "QK_USER_13"}, -    {QK_USER_14, "QK_USER_14"}, -    {QK_USER_15, "QK_USER_15"}, -    {QK_USER_16, "QK_USER_16"}, -    {QK_USER_17, "QK_USER_17"}, -    {QK_USER_18, "QK_USER_18"}, -    {QK_USER_19, "QK_USER_19"}, -    {QK_USER_20, "QK_USER_20"}, -    {QK_USER_21, "QK_USER_21"}, -    {QK_USER_22, "QK_USER_22"}, -    {QK_USER_23, "QK_USER_23"}, -    {QK_USER_24, "QK_USER_24"}, -    {QK_USER_25, "QK_USER_25"}, -    {QK_USER_26, "QK_USER_26"}, -    {QK_USER_27, "QK_USER_27"}, -    {QK_USER_28, "QK_USER_28"}, -    {QK_USER_29, "QK_USER_29"}, -    {QK_USER_30, "QK_USER_30"}, -    {QK_USER_31, "QK_USER_31"}, -}; diff --git a/tests/test_common/keycode_util.cpp b/tests/test_common/keycode_util.cpp deleted file mode 100644 index 539cab819a..0000000000 --- a/tests/test_common/keycode_util.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "keycode_util.hpp" -#include <cstdint> -extern "C" { -#include "action_code.h" -#include "keycode.h" -#include "quantum_keycodes.h" -#include "util.h" -} -#include <string> -#include <iomanip> -#include <map> - -extern std::map<uint16_t, std::string> KEYCODE_ID_TABLE; - -std::string get_mods(uint8_t mods) { -    std::stringstream s; -    if ((mods & MOD_RCTL) == MOD_RCTL) { -        s << XSTR(MOD_RCTL) << " | "; -    } else if ((mods & MOD_LCTL) == MOD_LCTL) { -        s << XSTR(MOD_LCTL) << " | "; -    } - -    if ((mods & MOD_RSFT) == MOD_RSFT) { -        s << XSTR(MOD_RSFT) << " | "; -    } else if ((mods & MOD_LSFT) == MOD_LSFT) { -        s << XSTR(MOD_LSFT) << " | "; -    } - -    if ((mods & MOD_RALT) == MOD_RALT) { -        s << XSTR(MOD_RALT) << " | "; -    } else if ((mods & MOD_LALT) == MOD_LALT) { -        s << XSTR(MOD_LALT) << " | "; -    } - -    if ((mods & MOD_RGUI) == MOD_RGUI) { -        s << XSTR(MOD_RGUI) << " | "; -    } else if ((mods & MOD_LGUI) == MOD_LGUI) { -        s << XSTR(MOD_LGUI) << " | "; -    } - -    auto _mods = s.str(); - -    if (_mods.size()) { -        _mods.resize(_mods.size() - 3); -    } - -    return std::string(_mods); -} - -std::string get_qk_mods(uint16_t keycode) { -    std::stringstream s; -    if ((keycode & QK_RCTL) == QK_RCTL) { -        s << XSTR(QK_RCTL) << " | "; -    } else if ((keycode & QK_LCTL) == QK_LCTL) { -        s << XSTR(QK_LCTL) << " | "; -    } - -    if ((keycode & QK_RSFT) == QK_RSFT) { -        s << XSTR(QK_RSFT) << " | "; -    } else if ((keycode & QK_LSFT) == QK_LSFT) { -        s << XSTR(QK_LSFT) << " | "; -    } - -    if ((keycode & QK_RALT) == QK_RALT) { -        s << XSTR(QK_RALT) << " | "; -    } else if ((keycode & QK_LALT) == QK_LALT) { -        s << XSTR(QK_LALT) << " | "; -    } - -    if ((keycode & QK_RGUI) == QK_RGUI) { -        s << XSTR(QK_RGUI) << " | "; -    } else if ((keycode & QK_LGUI) == QK_LGUI) { -        s << XSTR(QK_LGUI) << " | "; -    } - -    auto _mods = s.str(); - -    if (_mods.size()) { -        _mods.resize(_mods.size() - 3); -    } - -    return std::string(_mods); -} - -std::string generate_identifier(uint16_t kc) { -    std::stringstream s; -    if (IS_QK_MOD_TAP(kc)) { -        s << "MT(" << get_mods(QK_MOD_TAP_GET_MODS(kc)) << ", " << KEYCODE_ID_TABLE.at(kc & 0xFF) << ")"; -    } else if (IS_QK_LAYER_TAP(kc)) { -        s << "LT(" << +QK_LAYER_TAP_GET_LAYER(kc) << ", " << KEYCODE_ID_TABLE.at(kc & 0xFF) << ")"; -    } else if (IS_QK_TO(kc)) { -        s << "TO(" << +QK_TO_GET_LAYER(kc) << ")"; -    } else if (IS_QK_MOMENTARY(kc)) { -        s << "MO(" << +QK_MOMENTARY_GET_LAYER(kc) << ")"; -    } else if (IS_QK_DEF_LAYER(kc)) { -        s << "DF(" << +QK_DEF_LAYER_GET_LAYER(kc) << ")"; -    } else if (IS_QK_PERSISTENT_DEF_LAYER(kc)) { -        s << "PDF(" << +QK_PERSISTENT_DEF_LAYER_GET_LAYER(kc) << ")"; -    } else if (IS_QK_TOGGLE_LAYER(kc)) { -        s << "TG(" << +QK_TOGGLE_LAYER_GET_LAYER(kc) << ")"; -    } else if (IS_QK_LAYER_TAP_TOGGLE(kc)) { -        s << "TT(" << +QK_LAYER_TAP_TOGGLE_GET_LAYER(kc) << ")"; -    } else if (IS_QK_ONE_SHOT_LAYER(kc)) { -        s << "OSL(" << +QK_ONE_SHOT_LAYER_GET_LAYER(kc) << ")"; -    } else if (IS_QK_LAYER_MOD(kc)) { -        s << "LM(" << +QK_LAYER_MOD_GET_LAYER(kc) << ", " << get_mods(QK_LAYER_MOD_GET_MODS(kc)) << ")"; -    } else if (IS_QK_ONE_SHOT_MOD(kc)) { -        s << "OSM(" << get_mods(QK_ONE_SHOT_MOD_GET_MODS(kc)) << ")"; -    } else if (IS_QK_MODS(kc)) { -        s << "QK_MODS(" << KEYCODE_ID_TABLE.at(QK_MODS_GET_BASIC_KEYCODE(kc)) << ", " << get_qk_mods(kc) << ")"; -    } else if (IS_QK_TAP_DANCE(kc)) { -        s << "TD(" << +(kc & 0xFF) << ")"; -    } else { -        // Fallback - we didn't found any matching keycode, generate the hex representation. -        s << "unknown keycode: 0x" << std::hex << kc << ". Add conversion to " << XSTR(generate_identifier); -    } - -    return std::string(s.str()); -} - -std::string get_keycode_identifier_or_default(uint16_t keycode) { -    auto identifier = KEYCODE_ID_TABLE.find(keycode); -    if (identifier != KEYCODE_ID_TABLE.end()) { -        return identifier->second; -    } - -    KEYCODE_ID_TABLE[keycode] = generate_identifier(keycode); - -    return KEYCODE_ID_TABLE[keycode]; -} diff --git a/tests/test_common/keycode_util.hpp b/tests/test_common/keycode_util.hpp deleted file mode 100644 index 3143ab364e..0000000000 --- a/tests/test_common/keycode_util.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include <cstdint> -#include <string> - -std::string get_keycode_identifier_or_default(uint16_t keycode); diff --git a/tests/test_common/test_driver.hpp b/tests/test_common/test_driver.hpp index fea8225953..5c36c80dfc 100644 --- a/tests/test_common/test_driver.hpp +++ b/tests/test_common/test_driver.hpp @@ -20,7 +20,9 @@  #include <stdint.h>  #include "host.h"  #include "keyboard_report_util.hpp" -#include "keycode_util.hpp" +extern "C" { +#include "keycode_string.h" +}  #include "test_logger.hpp"  class TestDriver { @@ -146,11 +148,11 @@ class TestDriver {  /** @brief Tests whether keycode `actual` is equal to `expected`. */  #define EXPECT_KEYCODE_EQ(actual, expected) EXPECT_THAT((actual), KeycodeEq((expected))) -MATCHER_P(KeycodeEq, expected_keycode, "is equal to " + testing::PrintToString(expected_keycode) + ", keycode " + get_keycode_identifier_or_default(expected_keycode)) { +MATCHER_P(KeycodeEq, expected_keycode, "is equal to " + testing::PrintToString(expected_keycode) + ", keycode " + get_keycode_string(expected_keycode)) {      if (arg == expected_keycode) {          return true;      } -    *result_listener << "keycode " << get_keycode_identifier_or_default(arg); +    *result_listener << "keycode " << get_keycode_string(arg);      return false;  } diff --git a/tests/test_common/test_fixture.cpp b/tests/test_common/test_fixture.cpp index 81806b0e6d..00084721d6 100644 --- a/tests/test_common/test_fixture.cpp +++ b/tests/test_common/test_fixture.cpp @@ -46,7 +46,7 @@ void TestFixture::SetUpTestCase() {      // The following is enough to bootstrap the values set in main      eeconfig_init_quantum(); -    eeconfig_update_debug(debug_config.raw); +    eeconfig_update_debug(&debug_config);      TestDriver driver;      keyboard_init(); diff --git a/tests/test_common/test_keymap_key.hpp b/tests/test_common/test_keymap_key.hpp index 37b4c827e4..ba2c2912e3 100644 --- a/tests/test_common/test_keymap_key.hpp +++ b/tests/test_common/test_keymap_key.hpp @@ -18,10 +18,10 @@  #include <cstddef>  #include <string> -#include "keycode_util.hpp"  extern "C" {  #include "keyboard.h"  #include "test_matrix.h" +#include "keycode_string.h"  }  #include <cassert> @@ -29,11 +29,11 @@ extern "C" {  typedef uint8_t layer_t;  struct KeymapKey { -    KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(keycode), name(get_keycode_identifier_or_default(keycode)) { +    KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(keycode), name(get_keycode_string(keycode)) {          validate();      } -    KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode, uint16_t report_code) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(report_code), name{get_keycode_identifier_or_default(keycode)} { +    KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode, uint16_t report_code) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(report_code), name{get_keycode_string(keycode)} {          validate();      } | 
