mirror of
https://github.com/qmk/qmk_firmware.git
synced 2024-12-05 01:15:19 +00:00
Chordal Hold: restrict what chords settle as hold
This commit is contained in:
parent
248a09d545
commit
fa857db172
@ -49,6 +49,47 @@ __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *re
|
|||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
# ifdef CHORDAL_HOLD
|
||||||
|
__attribute__((weak)) bool get_chordal_hold(
|
||||||
|
uint16_t tap_hold_keycode, keyrecord_t* tap_hold_record,
|
||||||
|
uint16_t other_keycode, keyrecord_t* other_record) {
|
||||||
|
return get_chordal_hold_default(tap_hold_record, other_record);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_chordal_hold_default(
|
||||||
|
keyrecord_t* tap_hold_record, keyrecord_t* other_record) {
|
||||||
|
uint8_t tap_hold_hand = chordal_hold_handedness_user(tap_hold_record->event.key);
|
||||||
|
if (tap_hold_hand == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint8_t other_hand = chordal_hold_handedness_user(other_record->event.key);
|
||||||
|
return other_hand == 0 || tap_hold_hand != other_hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) uint8_t chordal_hold_handedness_kb(keypos_t key) {
|
||||||
|
# if defined(SPLIT_KEYBOARD) || ((MATRIX_ROWS) > (MATRIX_COLS))
|
||||||
|
#pragma message "Inferred handedness rows"
|
||||||
|
// If the keyboard is split or if MATRIX_ROWS > MATRIX_COLS, assume that the
|
||||||
|
// first half of the rows are left and the latter half are right.
|
||||||
|
return (key.row < (MATRIX_ROWS) / 2) ? /*left*/ 1 : /*right*/ 2;
|
||||||
|
# else
|
||||||
|
#pragma message "Inferred handedness cols"
|
||||||
|
// Otherwise, assume the first half of the cols are left, others are right.
|
||||||
|
return (key.col < (MATRIX_COLS) / 2) ? /*left*/ 1 : /*right*/ 2;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) uint8_t chordal_hold_handedness_user(keypos_t key) {
|
||||||
|
# if defined(CHORDAL_HOLD_LAYOUT)
|
||||||
|
# pragma message "Using chordal_hold_layout"
|
||||||
|
// If given, read handedness from `chordal_hold_layout` array.
|
||||||
|
return pgm_read_byte(&chordal_hold_layout[key.row][key.col]);
|
||||||
|
# else
|
||||||
|
return chordal_hold_handedness_kb(key);
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
# endif // CHORDAL_HOLD
|
||||||
|
|
||||||
# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
|
# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
|
||||||
__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) {
|
__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) {
|
||||||
return false;
|
return false;
|
||||||
@ -188,7 +229,7 @@ bool process_tapping(keyrecord_t *keyp) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
# if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
|
# if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(CHORDAL_HOLD) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
|
||||||
TAP_DEFINE_KEYCODE;
|
TAP_DEFINE_KEYCODE;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
@ -271,6 +312,22 @@ bool process_tapping(keyrecord_t *keyp) {
|
|||||||
// set interrupted flag when other key pressed during tapping
|
// set interrupted flag when other key pressed during tapping
|
||||||
if (event.pressed) {
|
if (event.pressed) {
|
||||||
tapping_key.tap.interrupted = true;
|
tapping_key.tap.interrupted = true;
|
||||||
|
|
||||||
|
# if defined(CHORDAL_HOLD)
|
||||||
|
if (!is_tap_record(keyp) &&
|
||||||
|
!get_chordal_hold(tapping_keycode, &tapping_key,
|
||||||
|
get_record_keycode(keyp, true), keyp)) {
|
||||||
|
// Settle the tapping key as *tapped*, since it is
|
||||||
|
// not considered a held chord with keyp.
|
||||||
|
ac_dprintf("Tapping: End. Tap in non-chord\n");
|
||||||
|
tapping_key.tap.count = 1;
|
||||||
|
// In process_action(), HOLD_ON_OTHER_KEY_PRESS will
|
||||||
|
// revert interrupted events to holds, so this needs
|
||||||
|
// to be set false.
|
||||||
|
tapping_key.tap.interrupted = false;
|
||||||
|
process_record(&tapping_key);
|
||||||
|
} else
|
||||||
|
# endif
|
||||||
if (TAP_GET_HOLD_ON_OTHER_KEY_PRESS
|
if (TAP_GET_HOLD_ON_OTHER_KEY_PRESS
|
||||||
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
|
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
|
||||||
// Auto Shift cannot evaluate this early
|
// Auto Shift cannot evaluate this early
|
||||||
@ -278,6 +335,8 @@ bool process_tapping(keyrecord_t *keyp) {
|
|||||||
&& !(MAYBE_RETRO_SHIFTING(event, keyp) && get_auto_shifted_key(get_record_keycode(keyp, false), keyp))
|
&& !(MAYBE_RETRO_SHIFTING(event, keyp) && get_auto_shifted_key(get_record_keycode(keyp, false), keyp))
|
||||||
# endif
|
# endif
|
||||||
) {
|
) {
|
||||||
|
// Settle the tapping key as *held*, since
|
||||||
|
// HOLD_ON_OTHER_KEY_PRESS is enabled for this key.
|
||||||
ac_dprintf("Tapping: End. No tap. Interfered by pressed key\n");
|
ac_dprintf("Tapping: End. No tap. Interfered by pressed key\n");
|
||||||
process_record(&tapping_key);
|
process_record(&tapping_key);
|
||||||
tapping_key = (keyrecord_t){0};
|
tapping_key = (keyrecord_t){0};
|
||||||
|
@ -46,6 +46,82 @@ bool get_permissive_hold(uint16_t keycode, keyrecord_t *record);
|
|||||||
bool get_retro_tapping(uint16_t keycode, keyrecord_t *record);
|
bool get_retro_tapping(uint16_t keycode, keyrecord_t *record);
|
||||||
bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record);
|
bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record);
|
||||||
|
|
||||||
|
#ifdef CHORDAL_HOLD
|
||||||
|
/**
|
||||||
|
* Callback to say when a key chord before the tapping term is considered held.
|
||||||
|
*
|
||||||
|
* In keymap.c, define the callback
|
||||||
|
*
|
||||||
|
* bool get_chordal_hold(uint16_t tap_hold_keycode,
|
||||||
|
* keyrecord_t* tap_hold_record,
|
||||||
|
* uint16_t other_keycode,
|
||||||
|
* keyrecord_t* other_record) {
|
||||||
|
* // Conditions...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* This callback is called when:
|
||||||
|
*
|
||||||
|
* 1. `tap_hold_keycode` is pressed.
|
||||||
|
* 2. `other_keycode` is pressed while `tap_hold_keycode` is still held,
|
||||||
|
* provided `other_keycode` is *not* also a tap-hold key and it is pressed
|
||||||
|
* before the tapping term.
|
||||||
|
*
|
||||||
|
* Returning true indicates that the tap-hold key should be considered held, or
|
||||||
|
* false to consider it tapped.
|
||||||
|
*
|
||||||
|
* @param tap_hold_keycode Keycode of the tap-hold key.
|
||||||
|
* @param tap_hold_record Record from the tap-hold press event.
|
||||||
|
* @param other_keycode Keycode of the other key.
|
||||||
|
* @param other_record Record from the other key's press event.
|
||||||
|
* @return True if the tap-hold key is considered held; false if tapped.
|
||||||
|
*/
|
||||||
|
bool get_chordal_hold(
|
||||||
|
uint16_t tap_hold_keycode, keyrecord_t* tap_hold_record,
|
||||||
|
uint16_t other_keycode, keyrecord_t* other_record);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default "opposite hands rule" for whether a key chord should settle as held.
|
||||||
|
*
|
||||||
|
* This function returns true when the tap-hold key and other key are on
|
||||||
|
* "opposite hands." In detail, handedness of the two keys are compared. If
|
||||||
|
* handedness values differ, or if either handedness is zero, the function
|
||||||
|
* returns true, indicating a hold. Otherwise, it returns false, indicating that
|
||||||
|
* the tap-hold key should settle as tapped.
|
||||||
|
*
|
||||||
|
* "Handedness" is determined as follows, in order of decending precedence:
|
||||||
|
* 1. `chordal_hold_handedness_user()`, if defined.
|
||||||
|
* 2. `chordal_hold_layout`, if CHORDAL_HOLD_LAYOUT is defined.
|
||||||
|
* 3. `chordal_hold_handedness_kb()`, if defined.
|
||||||
|
* 4. fallback assumption based on keyboard matrix dimensions.
|
||||||
|
*
|
||||||
|
* @param tap_hold_record Record of the active tap-hold key press.
|
||||||
|
* @param other_record Record of the other, interrupting key press.
|
||||||
|
* @return True if the tap-hold key is considered held; false if tapped.
|
||||||
|
*/
|
||||||
|
bool get_chordal_hold_default(
|
||||||
|
keyrecord_t* tap_hold_record, keyrecord_t* other_record);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keyboard-level callback to determine handedness of a key.
|
||||||
|
*
|
||||||
|
* This function should return:
|
||||||
|
* 1 for keys pressed by the left hand,
|
||||||
|
* 2 for keys on the right hand,
|
||||||
|
* 0 for keys exempt from the "opposite hands rule." This could be used
|
||||||
|
* perhaps on thumb keys or keys that might be pressed by either hand.
|
||||||
|
*
|
||||||
|
* @param key A key matrix position.
|
||||||
|
* @return Handedness value.
|
||||||
|
*/
|
||||||
|
uint8_t chordal_hold_handedness_kb(keypos_t key);
|
||||||
|
/** User callback to determine handedness of a key. */
|
||||||
|
uint8_t chordal_hold_handedness_user(keypos_t key);
|
||||||
|
|
||||||
|
# ifdef CHORDAL_HOLD_LAYOUT
|
||||||
|
extern const uint8_t chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM;
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DYNAMIC_TAPPING_TERM_ENABLE
|
#ifdef DYNAMIC_TAPPING_TERM_ENABLE
|
||||||
extern uint16_t g_tapping_term;
|
extern uint16_t g_tapping_term;
|
||||||
#endif
|
#endif
|
||||||
|
21
tests/tap_hold_configurations/chordal_hold/config.h
Normal file
21
tests/tap_hold_configurations/chordal_hold/config.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "test_common.h"
|
||||||
|
#define CHORDAL_HOLD
|
||||||
|
|
18
tests/tap_hold_configurations/chordal_hold/test.mk
Normal file
18
tests/tap_hold_configurations/chordal_hold/test.mk
Normal file
@ -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/>.
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------------
|
||||||
|
# Keep this file, even if it is empty, as a marker that this folder contains tests
|
||||||
|
# --------------------------------------------------------------------------------
|
167
tests/tap_hold_configurations/chordal_hold/test_tap_hold.cpp
Normal file
167
tests/tap_hold_configurations/chordal_hold/test_tap_hold.cpp
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 ChordalHold : public TestFixture {};
|
||||||
|
|
||||||
|
TEST_F(ChordalHold, chord_with_mod_tap_settled_as_hold) {
|
||||||
|
TestDriver driver;
|
||||||
|
InSequence s;
|
||||||
|
// Mod-tap key on the left hand.
|
||||||
|
auto mod_tap_hold_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_hold_key, regular_key});
|
||||||
|
|
||||||
|
/* Press mod-tap-hold key. */
|
||||||
|
EXPECT_NO_REPORT(driver);
|
||||||
|
mod_tap_hold_key.press();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Press regular key. */
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_A));
|
||||||
|
regular_key.press();
|
||||||
|
idle_for(TAPPING_TERM);
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release regular key. */
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
|
||||||
|
regular_key.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release mod-tap-hold key. */
|
||||||
|
EXPECT_EMPTY_REPORT(driver);
|
||||||
|
mod_tap_hold_key.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ChordalHold, 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_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
|
||||||
|
auto regular_key = KeymapKey(0, 2, 0, KC_A);
|
||||||
|
|
||||||
|
set_keymap({mod_tap_hold_key, regular_key});
|
||||||
|
|
||||||
|
/* Press mod-tap-hold key. */
|
||||||
|
EXPECT_NO_REPORT(driver);
|
||||||
|
mod_tap_hold_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();
|
||||||
|
idle_for(TAPPING_TERM);
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release mod-tap-hold key. */
|
||||||
|
EXPECT_EMPTY_REPORT(driver);
|
||||||
|
mod_tap_hold_key.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ChordalHold, 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(ChordalHold, 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(ChordalHold, chordal_hold_ignores_multiple_mod_taps) {
|
||||||
|
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-hold key. */
|
||||||
|
EXPECT_NO_REPORT(driver);
|
||||||
|
mod_tap_key1.press();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Press second mod-tap key. */
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_RIGHT_SHIFT));
|
||||||
|
mod_tap_key2.press();
|
||||||
|
idle_for(TAPPING_TERM + 1);
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release keys. */
|
||||||
|
EXPECT_REPORT(driver, (KC_RIGHT_SHIFT));
|
||||||
|
EXPECT_EMPTY_REPORT(driver);
|
||||||
|
mod_tap_key1.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
mod_tap_key2.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
|||||||
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "test_common.h"
|
||||||
|
#define CHORDAL_HOLD
|
||||||
|
#define HOLD_ON_OTHER_KEY_PRESS
|
||||||
|
|
@ -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/>.
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------------
|
||||||
|
# Keep this file, even if it is empty, as a marker that this folder contains tests
|
||||||
|
# --------------------------------------------------------------------------------
|
@ -0,0 +1,167 @@
|
|||||||
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 ChordalHoldAndHoldOnOtherKeypress : public TestFixture {};
|
||||||
|
|
||||||
|
TEST_F(ChordalHoldAndHoldOnOtherKeypress, chord_with_mod_tap_settled_as_hold) {
|
||||||
|
TestDriver driver;
|
||||||
|
InSequence s;
|
||||||
|
// Mod-tap key on the left hand.
|
||||||
|
auto mod_tap_hold_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_hold_key, regular_key});
|
||||||
|
|
||||||
|
/* Press mod-tap-hold key. */
|
||||||
|
EXPECT_NO_REPORT(driver);
|
||||||
|
mod_tap_hold_key.press();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Press regular key. */
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_A));
|
||||||
|
regular_key.press();
|
||||||
|
idle_for(TAPPING_TERM);
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release regular key. */
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
|
||||||
|
regular_key.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release mod-tap-hold key. */
|
||||||
|
EXPECT_EMPTY_REPORT(driver);
|
||||||
|
mod_tap_hold_key.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ChordalHoldAndHoldOnOtherKeypress, 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_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
|
||||||
|
auto regular_key = KeymapKey(0, 2, 0, KC_A);
|
||||||
|
|
||||||
|
set_keymap({mod_tap_hold_key, regular_key});
|
||||||
|
|
||||||
|
/* Press mod-tap-hold key. */
|
||||||
|
EXPECT_NO_REPORT(driver);
|
||||||
|
mod_tap_hold_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();
|
||||||
|
idle_for(TAPPING_TERM);
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release mod-tap-hold key. */
|
||||||
|
EXPECT_EMPTY_REPORT(driver);
|
||||||
|
mod_tap_hold_key.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ChordalHoldAndHoldOnOtherKeypress, 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(ChordalHoldAndHoldOnOtherKeypress, 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(ChordalHoldAndHoldOnOtherKeypress, chordal_hold_ignores_multiple_mod_taps) {
|
||||||
|
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-hold key. */
|
||||||
|
EXPECT_NO_REPORT(driver);
|
||||||
|
mod_tap_key1.press();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Press second mod-tap key. */
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_RIGHT_SHIFT));
|
||||||
|
mod_tap_key2.press();
|
||||||
|
idle_for(TAPPING_TERM + 1);
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release keys. */
|
||||||
|
EXPECT_REPORT(driver, (KC_RIGHT_SHIFT));
|
||||||
|
EXPECT_EMPTY_REPORT(driver);
|
||||||
|
mod_tap_key1.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
mod_tap_key2.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
|||||||
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "test_common.h"
|
||||||
|
#define CHORDAL_HOLD
|
||||||
|
#define PERMISSIVE_HOLD
|
||||||
|
|
@ -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/>.
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------------
|
||||||
|
# Keep this file, even if it is empty, as a marker that this folder contains tests
|
||||||
|
# --------------------------------------------------------------------------------
|
@ -0,0 +1,167 @@
|
|||||||
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 ChordalHoldAndPermissiveHold : public TestFixture {};
|
||||||
|
|
||||||
|
TEST_F(ChordalHoldAndPermissiveHold, chord_with_mod_tap_settled_as_hold) {
|
||||||
|
TestDriver driver;
|
||||||
|
InSequence s;
|
||||||
|
// Mod-tap key on the left hand.
|
||||||
|
auto mod_tap_hold_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_hold_key, regular_key});
|
||||||
|
|
||||||
|
/* Press mod-tap-hold key. */
|
||||||
|
EXPECT_NO_REPORT(driver);
|
||||||
|
mod_tap_hold_key.press();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Press regular key. */
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_A));
|
||||||
|
regular_key.press();
|
||||||
|
idle_for(TAPPING_TERM);
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release regular key. */
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
|
||||||
|
regular_key.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release mod-tap-hold key. */
|
||||||
|
EXPECT_EMPTY_REPORT(driver);
|
||||||
|
mod_tap_hold_key.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ChordalHoldAndPermissiveHold, 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_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
|
||||||
|
auto regular_key = KeymapKey(0, 2, 0, KC_A);
|
||||||
|
|
||||||
|
set_keymap({mod_tap_hold_key, regular_key});
|
||||||
|
|
||||||
|
/* Press mod-tap-hold key. */
|
||||||
|
EXPECT_NO_REPORT(driver);
|
||||||
|
mod_tap_hold_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();
|
||||||
|
idle_for(TAPPING_TERM);
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release mod-tap-hold key. */
|
||||||
|
EXPECT_EMPTY_REPORT(driver);
|
||||||
|
mod_tap_hold_key.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ChordalHoldAndPermissiveHold, 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(ChordalHoldAndPermissiveHold, 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(ChordalHoldAndPermissiveHold, chordal_hold_ignores_multiple_mod_taps) {
|
||||||
|
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-hold key. */
|
||||||
|
EXPECT_NO_REPORT(driver);
|
||||||
|
mod_tap_key1.press();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Press second mod-tap key. */
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
|
||||||
|
EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_RIGHT_SHIFT));
|
||||||
|
mod_tap_key2.press();
|
||||||
|
idle_for(TAPPING_TERM + 1);
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
|
||||||
|
/* Release keys. */
|
||||||
|
EXPECT_REPORT(driver, (KC_RIGHT_SHIFT));
|
||||||
|
EXPECT_EMPTY_REPORT(driver);
|
||||||
|
mod_tap_key1.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
mod_tap_key2.release();
|
||||||
|
run_one_scan_loop();
|
||||||
|
VERIFY_AND_CLEAR(driver);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user