2015-04-09 16:32:04 +00:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
2022-03-09 18:43:12 +00:00
|
|
|
#include "action.h"
|
|
|
|
#include "action_layer.h"
|
|
|
|
#include "action_tapping.h"
|
|
|
|
#include "keycode.h"
|
|
|
|
#include "timer.h"
|
|
|
|
|
2015-04-09 16:32:04 +00:00
|
|
|
#ifndef NO_ACTION_TAPPING
|
|
|
|
|
2022-12-13 11:20:07 +00:00
|
|
|
# if defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
|
|
|
|
# error "IGNORE_MOD_TAP_INTERRUPT_PER_KEY has been removed; the code needs to be ported to use HOLD_ON_OTHER_KEY_PRESS_PER_KEY instead."
|
2023-04-03 05:32:47 +00:00
|
|
|
# elif defined(IGNORE_MOD_TAP_INTERRUPT)
|
|
|
|
# error "IGNORE_MOD_TAP_INTERRUPT is no longer necessary as it is now the default behavior of mod-tap keys. Please remove it from your config."
|
2022-12-13 11:20:07 +00:00
|
|
|
# endif
|
|
|
|
|
2021-11-01 19:18:33 +00:00
|
|
|
# ifndef COMBO_ENABLE
|
2023-04-03 08:33:45 +00:00
|
|
|
# define IS_TAPPING_RECORD(r) (KEYEQ(tapping_key.event.key, (r->event.key)))
|
2021-11-01 19:18:33 +00:00
|
|
|
# else
|
2023-04-03 08:33:45 +00:00
|
|
|
# define IS_TAPPING_RECORD(r) (KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode)
|
2021-11-01 19:18:33 +00:00
|
|
|
# endif
|
2022-04-16 18:24:09 +00:00
|
|
|
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < GET_TAPPING_TERM(get_record_keycode(&tapping_key, false), &tapping_key))
|
2022-12-12 15:52:22 +00:00
|
|
|
# define WITHIN_QUICK_TAP_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < GET_QUICK_TAP_TERM(get_record_keycode(&tapping_key, false), &tapping_key))
|
2015-04-09 16:32:04 +00:00
|
|
|
|
2022-04-16 18:24:09 +00:00
|
|
|
# ifdef DYNAMIC_TAPPING_TERM_ENABLE
|
2021-11-25 20:06:50 +00:00
|
|
|
uint16_t g_tapping_term = TAPPING_TERM;
|
2022-04-16 18:24:09 +00:00
|
|
|
# endif
|
2021-11-25 20:06:50 +00:00
|
|
|
|
2022-04-16 18:24:09 +00:00
|
|
|
# ifdef TAPPING_TERM_PER_KEY
|
2022-02-12 18:29:31 +00:00
|
|
|
__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) {
|
2022-04-16 18:24:09 +00:00
|
|
|
# ifdef DYNAMIC_TAPPING_TERM_ENABLE
|
2022-02-12 18:29:31 +00:00
|
|
|
return g_tapping_term;
|
2022-04-16 18:24:09 +00:00
|
|
|
# else
|
|
|
|
return TAPPING_TERM;
|
|
|
|
# endif
|
2022-02-12 18:29:31 +00:00
|
|
|
}
|
2019-08-30 18:19:03 +00:00
|
|
|
# endif
|
2015-04-09 16:32:04 +00:00
|
|
|
|
2022-12-12 15:52:22 +00:00
|
|
|
# ifdef QUICK_TAP_TERM_PER_KEY
|
|
|
|
__attribute__((weak)) uint16_t get_quick_tap_term(uint16_t keycode, keyrecord_t *record) {
|
|
|
|
return QUICK_TAP_TERM;
|
2022-02-12 18:29:31 +00:00
|
|
|
}
|
2020-01-17 20:49:23 +00:00
|
|
|
# endif
|
|
|
|
|
2020-02-25 18:25:52 +00:00
|
|
|
# ifdef PERMISSIVE_HOLD_PER_KEY
|
2022-02-12 18:29:31 +00:00
|
|
|
__attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-02-25 18:25:52 +00:00
|
|
|
# endif
|
|
|
|
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
# if defined(CHORDAL_HOLD)
|
|
|
|
extern const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM;
|
|
|
|
|
|
|
|
# define REGISTERED_TAPS_SIZE 8
|
|
|
|
// Array of tap-hold keys that have been settled as tapped but not yet released.
|
|
|
|
static keypos_t registered_taps[REGISTERED_TAPS_SIZE] = {};
|
|
|
|
static uint8_t num_registered_taps = 0;
|
|
|
|
|
|
|
|
/** Adds `key` to the registered_taps array. */
|
|
|
|
static void registered_taps_add(keypos_t key);
|
|
|
|
/** Returns the index of `key` in registered_taps, or -1 if not found. */
|
|
|
|
static int8_t registered_tap_find(keypos_t key);
|
|
|
|
/** Removes index `i` from the registered_taps array. */
|
|
|
|
static void registered_taps_del_index(uint8_t i);
|
|
|
|
/** Logs the registered_taps array for debugging. */
|
|
|
|
static void debug_registered_taps(void);
|
|
|
|
|
|
|
|
/** \brief Finds which queued events should be held according to Chordal Hold.
|
|
|
|
*
|
|
|
|
* In a situation with multiple unsettled tap-hold key presses, scan the queue
|
|
|
|
* up until the first release, non-tap-hold, or one-shot event and find the
|
|
|
|
* latest event in the queue that settles as held according to
|
|
|
|
* get_chordal_hold().
|
|
|
|
*
|
|
|
|
* \return Index of the first tap, or equivalently, one past the latest hold.
|
|
|
|
*/
|
|
|
|
static uint8_t waiting_buffer_find_chordal_hold_tap(void);
|
|
|
|
|
|
|
|
/** Processes queued events up to and including `key` as tapped. */
|
|
|
|
static void waiting_buffer_chordal_hold_taps_until(keypos_t key);
|
|
|
|
|
|
|
|
/** \brief Processes and pops buffered events until the first tap-hold event. */
|
|
|
|
static void waiting_buffer_process_regular(void);
|
|
|
|
|
|
|
|
static bool is_mt_or_lt(uint16_t keycode) {
|
|
|
|
return IS_QK_MOD_TAP(keycode) || IS_QK_LAYER_TAP(keycode);
|
|
|
|
}
|
|
|
|
# endif // CHORDAL_HOLD
|
|
|
|
|
2021-08-06 23:16:26 +00:00
|
|
|
# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
|
2022-02-12 18:29:31 +00:00
|
|
|
__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-08-06 23:16:26 +00:00
|
|
|
# endif
|
|
|
|
|
2021-11-25 12:12:14 +00:00
|
|
|
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
|
|
|
|
# include "process_auto_shift.h"
|
|
|
|
# endif
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
static keyrecord_t tapping_key = {};
|
2015-04-09 16:32:04 +00:00
|
|
|
static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
|
2019-08-30 18:19:03 +00:00
|
|
|
static uint8_t waiting_buffer_head = 0;
|
|
|
|
static uint8_t waiting_buffer_tail = 0;
|
2015-04-09 16:32:04 +00:00
|
|
|
|
|
|
|
static bool process_tapping(keyrecord_t *record);
|
|
|
|
static bool waiting_buffer_enq(keyrecord_t record);
|
|
|
|
static void waiting_buffer_clear(void);
|
|
|
|
static bool waiting_buffer_typed(keyevent_t event);
|
|
|
|
static bool waiting_buffer_has_anykey_pressed(void);
|
|
|
|
static void waiting_buffer_scan_tap(void);
|
|
|
|
static void debug_tapping_key(void);
|
|
|
|
static void debug_waiting_buffer(void);
|
|
|
|
|
2018-03-22 06:50:38 +00:00
|
|
|
/** \brief Action Tapping Process
|
|
|
|
*
|
|
|
|
* FIXME: Needs doc
|
|
|
|
*/
|
2019-08-30 18:19:03 +00:00
|
|
|
void action_tapping_process(keyrecord_t record) {
|
2015-04-09 16:32:04 +00:00
|
|
|
if (process_tapping(&record)) {
|
2022-12-17 14:06:27 +00:00
|
|
|
if (IS_EVENT(record.event)) {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("processed: ");
|
2019-08-30 18:19:03 +00:00
|
|
|
debug_record(record);
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!waiting_buffer_enq(record)) {
|
|
|
|
// clear all in case of overflow.
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("OVERFLOW: CLEAR ALL STATES\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
clear_keyboard();
|
|
|
|
waiting_buffer_clear();
|
2023-04-03 08:33:45 +00:00
|
|
|
tapping_key = (keyrecord_t){0};
|
2015-04-09 16:32:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// process waiting_buffer
|
2022-12-17 14:06:27 +00:00
|
|
|
if (IS_EVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("---- action_exec: process waiting_buffer -----\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
}
|
|
|
|
for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {
|
|
|
|
if (process_tapping(&waiting_buffer[waiting_buffer_tail])) {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("processed: waiting_buffer[%u] =", waiting_buffer_tail);
|
2019-08-30 18:19:03 +00:00
|
|
|
debug_record(waiting_buffer[waiting_buffer_tail]);
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("\n\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-12-17 14:06:27 +00:00
|
|
|
if (IS_EVENT(record.event)) {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-28 08:16:38 +00:00
|
|
|
/* Some conditionally defined helper macros to keep process_tapping more
|
|
|
|
* readable. The conditional definition of tapping_keycode and all the
|
|
|
|
* conditional uses of it are hidden inside macros named TAP_...
|
|
|
|
*/
|
2023-09-25 02:45:49 +00:00
|
|
|
# define TAP_DEFINE_KEYCODE const uint16_t tapping_keycode = get_record_keycode(&tapping_key, false)
|
2022-11-28 08:16:38 +00:00
|
|
|
|
|
|
|
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
|
|
|
|
# ifdef RETRO_TAPPING_PER_KEY
|
2023-09-25 02:45:49 +00:00
|
|
|
# define TAP_GET_RETRO_TAPPING(keyp) get_auto_shifted_key(tapping_keycode, keyp) && get_retro_tapping(tapping_keycode, &tapping_key)
|
2022-11-28 08:16:38 +00:00
|
|
|
# else
|
2023-09-25 02:45:49 +00:00
|
|
|
# define TAP_GET_RETRO_TAPPING(keyp) get_auto_shifted_key(tapping_keycode, keyp)
|
2022-11-28 08:16:38 +00:00
|
|
|
# endif
|
2023-09-25 02:45:49 +00:00
|
|
|
/* Used to extend TAPPING_TERM:
|
|
|
|
* indefinitely if RETRO_SHIFT does not have a value
|
|
|
|
* to RETRO_SHIFT if RETRO_SHIFT is set
|
|
|
|
* for possibly retro shifted keys.
|
|
|
|
*/
|
|
|
|
# define MAYBE_RETRO_SHIFTING(ev, keyp) (get_auto_shifted_key(tapping_keycode, keyp) && TAP_GET_RETRO_TAPPING(keyp) && ((RETRO_SHIFT + 0) == 0 || TIMER_DIFF_16((ev).time, tapping_key.event.time) < (RETRO_SHIFT + 0)))
|
2022-12-14 15:31:08 +00:00
|
|
|
# define TAP_IS_LT IS_QK_LAYER_TAP(tapping_keycode)
|
|
|
|
# define TAP_IS_MT IS_QK_MOD_TAP(tapping_keycode)
|
2022-11-28 08:16:38 +00:00
|
|
|
# define TAP_IS_RETRO IS_RETRO(tapping_keycode)
|
|
|
|
# else
|
2023-09-25 02:45:49 +00:00
|
|
|
# define TAP_GET_RETRO_TAPPING(keyp) false
|
|
|
|
# define MAYBE_RETRO_SHIFTING(ev, kp) false
|
2022-11-28 08:16:38 +00:00
|
|
|
# define TAP_IS_LT false
|
|
|
|
# define TAP_IS_MT false
|
|
|
|
# define TAP_IS_RETRO false
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# ifdef PERMISSIVE_HOLD_PER_KEY
|
|
|
|
# define TAP_GET_PERMISSIVE_HOLD get_permissive_hold(tapping_keycode, &tapping_key)
|
|
|
|
# elif defined(PERMISSIVE_HOLD)
|
|
|
|
# define TAP_GET_PERMISSIVE_HOLD true
|
|
|
|
# else
|
|
|
|
# define TAP_GET_PERMISSIVE_HOLD false
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
|
|
|
|
# define TAP_GET_HOLD_ON_OTHER_KEY_PRESS get_hold_on_other_key_press(tapping_keycode, &tapping_key)
|
|
|
|
# elif defined(HOLD_ON_OTHER_KEY_PRESS)
|
|
|
|
# define TAP_GET_HOLD_ON_OTHER_KEY_PRESS true
|
|
|
|
# else
|
|
|
|
# define TAP_GET_HOLD_ON_OTHER_KEY_PRESS false
|
|
|
|
# endif
|
|
|
|
|
2018-03-22 06:50:38 +00:00
|
|
|
/** \brief Tapping
|
2015-04-09 16:32:04 +00:00
|
|
|
*
|
|
|
|
* Rule: Tap key is typed(pressed and released) within TAPPING_TERM.
|
|
|
|
* (without interfering by typing other key)
|
|
|
|
*/
|
|
|
|
/* return true when key event is processed or consumed. */
|
2019-08-30 18:19:03 +00:00
|
|
|
bool process_tapping(keyrecord_t *keyp) {
|
2023-04-03 08:33:45 +00:00
|
|
|
const keyevent_t event = keyp->event;
|
|
|
|
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
# if defined(CHORDAL_HOLD)
|
|
|
|
if (!event.pressed) {
|
|
|
|
const int8_t i = registered_tap_find(event.key);
|
|
|
|
if (i != -1) {
|
|
|
|
// If a tap-hold key was previously settled as tapped, set its
|
|
|
|
// tap.count correspondingly on release.
|
|
|
|
keyp->tap.count = 1;
|
|
|
|
registered_taps_del_index(i);
|
|
|
|
ac_dprintf("Found tap release for [%d]\n", i);
|
|
|
|
debug_registered_taps();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# endif // CHORDAL_HOLD
|
|
|
|
|
2023-04-03 08:33:45 +00:00
|
|
|
// state machine is in the "reset" state, no tapping key is to be
|
|
|
|
// processed
|
2023-05-10 15:59:52 +00:00
|
|
|
if (IS_NOEVENT(tapping_key.event)) {
|
|
|
|
if (!IS_EVENT(event)) {
|
|
|
|
// early return for tick events
|
|
|
|
} else if (event.pressed && is_tap_record(keyp)) {
|
2023-04-03 08:33:45 +00:00
|
|
|
// the currently pressed key is a tapping key, therefore transition
|
|
|
|
// into the "pressed" tapping key state
|
|
|
|
ac_dprintf("Tapping: Start(Press tap key).\n");
|
|
|
|
tapping_key = *keyp;
|
|
|
|
process_record_tap_hint(&tapping_key);
|
|
|
|
waiting_buffer_scan_tap();
|
|
|
|
debug_tapping_key();
|
|
|
|
} else {
|
|
|
|
// the current key is just a regular key, pass it on for regular
|
|
|
|
// processing
|
|
|
|
process_record(keyp);
|
|
|
|
}
|
2023-05-10 15:59:52 +00:00
|
|
|
|
|
|
|
return true;
|
2023-04-03 08:33:45 +00:00
|
|
|
}
|
|
|
|
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
# if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(CHORDAL_HOLD) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
|
2022-11-28 08:16:38 +00:00
|
|
|
TAP_DEFINE_KEYCODE;
|
2023-09-25 02:45:49 +00:00
|
|
|
# endif
|
2015-04-09 16:32:04 +00:00
|
|
|
|
2023-04-03 08:33:45 +00:00
|
|
|
// process "pressed" tapping key state
|
|
|
|
if (tapping_key.event.pressed) {
|
2023-09-25 02:45:49 +00:00
|
|
|
if (WITHIN_TAPPING_TERM(event) || MAYBE_RETRO_SHIFTING(event, keyp)) {
|
2023-04-03 08:33:45 +00:00
|
|
|
if (IS_NOEVENT(event)) {
|
|
|
|
// early return for tick events
|
|
|
|
return true;
|
|
|
|
}
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
|
2015-04-09 16:32:04 +00:00
|
|
|
if (tapping_key.tap.count == 0) {
|
2021-08-05 23:44:57 +00:00
|
|
|
if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
|
2015-04-09 16:32:04 +00:00
|
|
|
// first tap!
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: First tap(0->1).\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
tapping_key.tap.count = 1;
|
|
|
|
debug_tapping_key();
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(&tapping_key);
|
2015-04-09 16:32:04 +00:00
|
|
|
|
|
|
|
// copy tapping state
|
|
|
|
keyp->tap = tapping_key.tap;
|
|
|
|
// enqueue
|
|
|
|
return false;
|
|
|
|
}
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
# if defined(CHORDAL_HOLD)
|
|
|
|
else if (is_mt_or_lt(tapping_keycode) && !event.pressed && waiting_buffer_typed(event) && !get_chordal_hold(tapping_keycode, &tapping_key, get_record_keycode(keyp, false), keyp)) {
|
|
|
|
// Key release that is not a chord with the tapping key.
|
|
|
|
// Settle the tapping key and any other pending tap-hold
|
|
|
|
// keys preceding the press of this key as tapped.
|
|
|
|
|
|
|
|
ac_dprintf("Tapping: End. Chord considered a tap\n");
|
|
|
|
tapping_key.tap.count = 1;
|
|
|
|
registered_taps_add(tapping_key.event.key);
|
|
|
|
process_record(&tapping_key);
|
|
|
|
tapping_key = (keyrecord_t){0};
|
|
|
|
|
|
|
|
waiting_buffer_chordal_hold_taps_until(event.key);
|
|
|
|
debug_registered_taps();
|
|
|
|
debug_waiting_buffer();
|
|
|
|
// enqueue
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
# endif // CHORDAL_HOLD
|
2015-04-09 16:32:04 +00:00
|
|
|
/* Process a key typed within TAPPING_TERM
|
|
|
|
* This can register the key before settlement of tapping,
|
|
|
|
* useful for long TAPPING_TERM but may prevent fast typing.
|
|
|
|
*/
|
2021-11-25 12:12:14 +00:00
|
|
|
// clang-format off
|
|
|
|
else if (
|
2023-09-25 02:45:49 +00:00
|
|
|
!event.pressed && waiting_buffer_typed(event) &&
|
2021-11-25 12:12:14 +00:00
|
|
|
(
|
2023-09-25 02:45:49 +00:00
|
|
|
TAP_GET_PERMISSIVE_HOLD ||
|
|
|
|
// Causes nested taps to not wait past TAPPING_TERM/RETRO_SHIFT
|
|
|
|
// unnecessarily and fixes them for Layer Taps.
|
|
|
|
TAP_GET_RETRO_TAPPING(keyp)
|
2021-11-25 12:12:14 +00:00
|
|
|
)
|
|
|
|
) {
|
|
|
|
// clang-format on
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: End. No tap. Interfered by typing key\n");
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(&tapping_key);
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
|
|
|
|
# if defined(CHORDAL_HOLD)
|
|
|
|
uint8_t first_tap = waiting_buffer_find_chordal_hold_tap();
|
|
|
|
ac_dprintf("first_tap = %u\n", first_tap);
|
|
|
|
if (first_tap < WAITING_BUFFER_SIZE) {
|
|
|
|
for (; waiting_buffer_tail != first_tap; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {
|
|
|
|
ac_dprintf("Processing [%u]\n", waiting_buffer_tail);
|
|
|
|
process_record(&waiting_buffer[waiting_buffer_tail]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
waiting_buffer_chordal_hold_taps_until(event.key);
|
|
|
|
debug_registered_taps();
|
|
|
|
debug_waiting_buffer();
|
|
|
|
# endif // CHORDAL_HOLD
|
|
|
|
|
2023-04-03 08:33:45 +00:00
|
|
|
tapping_key = (keyrecord_t){0};
|
2015-04-09 16:32:04 +00:00
|
|
|
debug_tapping_key();
|
|
|
|
// enqueue
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/* Process release event of a key pressed before tapping starts
|
|
|
|
* Without this unexpected repeating will occur with having fast repeating setting
|
|
|
|
* https://github.com/tmk/tmk_keyboard/issues/60
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
*
|
|
|
|
* NOTE: This workaround causes events to process out of order,
|
|
|
|
* e.g. in a rolled press of three tap-hold keys like
|
|
|
|
*
|
|
|
|
* "A down, B down, C down, A up, B up, C up"
|
|
|
|
*
|
|
|
|
* events are processed as
|
|
|
|
*
|
|
|
|
* "A down, B down, A up, B up, C down, C up"
|
|
|
|
*
|
|
|
|
* It seems incorrect to process keyp before the tapping key.
|
|
|
|
* This workaround is old, from 2013. This might no longer
|
|
|
|
* be needed for the original problem it was meant to address.
|
2015-04-09 16:32:04 +00:00
|
|
|
*/
|
2023-04-03 08:33:45 +00:00
|
|
|
else if (!event.pressed && !waiting_buffer_typed(event)) {
|
2022-11-28 08:16:38 +00:00
|
|
|
// Modifier/Layer should be retained till end of this tapping.
|
2015-04-09 16:32:04 +00:00
|
|
|
action_t action = layer_switch_get_action(event.key);
|
|
|
|
switch (action.kind.id) {
|
|
|
|
case ACT_LMODS:
|
|
|
|
case ACT_RMODS:
|
|
|
|
if (action.key.mods && !action.key.code) return false;
|
2023-02-10 21:10:14 +00:00
|
|
|
if (IS_MODIFIER_KEYCODE(action.key.code)) return false;
|
2015-04-09 16:32:04 +00:00
|
|
|
break;
|
|
|
|
case ACT_LMODS_TAP:
|
|
|
|
case ACT_RMODS_TAP:
|
|
|
|
if (action.key.mods && keyp->tap.count == 0) return false;
|
2023-02-10 21:10:14 +00:00
|
|
|
if (IS_MODIFIER_KEYCODE(action.key.code)) return false;
|
2015-04-09 16:32:04 +00:00
|
|
|
break;
|
2022-11-28 08:16:38 +00:00
|
|
|
case ACT_LAYER_TAP:
|
|
|
|
case ACT_LAYER_TAP_EXT:
|
|
|
|
switch (action.layer_tap.code) {
|
|
|
|
case 0 ...(OP_TAP_TOGGLE - 1):
|
|
|
|
case OP_ON_OFF:
|
|
|
|
case OP_OFF_ON:
|
|
|
|
case OP_SET_CLEAR:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
2015-04-09 16:32:04 +00:00
|
|
|
}
|
|
|
|
// Release of key should be process immediately.
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: release event of a key pressed before tapping\n");
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(keyp);
|
2015-04-09 16:32:04 +00:00
|
|
|
return true;
|
2019-08-30 18:19:03 +00:00
|
|
|
} else {
|
2023-09-25 02:45:49 +00:00
|
|
|
// set interrupted flag when other key pressed during tapping
|
2015-04-09 16:32:04 +00:00
|
|
|
if (event.pressed) {
|
|
|
|
tapping_key.tap.interrupted = true;
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
|
|
|
|
# if defined(CHORDAL_HOLD)
|
|
|
|
if (is_mt_or_lt(tapping_keycode) && !get_chordal_hold(tapping_keycode, &tapping_key, get_record_keycode(keyp, false), keyp)) {
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
if (!is_tap_record(keyp)) {
|
|
|
|
ac_dprintf("Tapping: End. Chord considered a tap\n");
|
|
|
|
tapping_key.tap.count = 1;
|
|
|
|
registered_taps_add(tapping_key.event.key);
|
|
|
|
debug_registered_taps();
|
|
|
|
process_record(&tapping_key);
|
|
|
|
tapping_key = (keyrecord_t){0};
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
# endif // CHORDAL_HOLD
|
|
|
|
if (TAP_GET_HOLD_ON_OTHER_KEY_PRESS
|
2023-09-25 02:45:49 +00:00
|
|
|
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
// Auto Shift cannot evaluate this early
|
|
|
|
// Retro Shift uses the hold action for all nested taps even without HOLD_ON_OTHER_KEY_PRESS, so this is fine to skip
|
|
|
|
&& !(MAYBE_RETRO_SHIFTING(event, keyp) && get_auto_shifted_key(get_record_keycode(keyp, false), keyp))
|
2023-09-25 02:45:49 +00:00
|
|
|
# endif
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
) {
|
|
|
|
// Settle the tapping key as *held*, since
|
|
|
|
// HOLD_ON_OTHER_KEY_PRESS is enabled for this key.
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: End. No tap. Interfered by pressed key\n");
|
2021-08-06 23:16:26 +00:00
|
|
|
process_record(&tapping_key);
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
|
|
|
|
# if defined(CHORDAL_HOLD)
|
|
|
|
if (waiting_buffer_tail != waiting_buffer_head && is_tap_record(&waiting_buffer[waiting_buffer_tail])) {
|
|
|
|
tapping_key = waiting_buffer[waiting_buffer_tail];
|
|
|
|
// Pop tail from the queue.
|
|
|
|
waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE;
|
|
|
|
debug_waiting_buffer();
|
|
|
|
} else
|
|
|
|
# endif // CHORDAL_HOLD
|
|
|
|
{
|
|
|
|
tapping_key = (keyrecord_t){0};
|
|
|
|
}
|
2021-08-06 23:16:26 +00:00
|
|
|
debug_tapping_key();
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
|
|
|
|
# if defined(CHORDAL_HOLD)
|
|
|
|
waiting_buffer_process_regular();
|
|
|
|
# endif // CHORDAL_HOLD
|
2021-08-06 23:16:26 +00:00
|
|
|
}
|
2015-04-09 16:32:04 +00:00
|
|
|
}
|
2016-04-17 17:54:32 +00:00
|
|
|
// enqueue
|
2015-04-09 16:32:04 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// tap_count > 0
|
|
|
|
else {
|
2021-08-05 23:44:57 +00:00
|
|
|
if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: Tap release(%u)\n", tapping_key.tap.count);
|
2015-04-09 16:32:04 +00:00
|
|
|
keyp->tap = tapping_key.tap;
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(keyp);
|
2015-04-09 16:32:04 +00:00
|
|
|
tapping_key = *keyp;
|
|
|
|
debug_tapping_key();
|
|
|
|
return true;
|
2021-08-05 23:44:57 +00:00
|
|
|
} else if (is_tap_record(keyp) && event.pressed) {
|
2015-04-09 16:32:04 +00:00
|
|
|
if (tapping_key.tap.count > 1) {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: Start new tap with releasing last tap(>1).\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
// unregister key
|
2021-11-01 19:18:33 +00:00
|
|
|
process_record(&(keyrecord_t){
|
|
|
|
.tap = tapping_key.tap,
|
|
|
|
.event.key = tapping_key.event.key,
|
|
|
|
.event.time = event.time,
|
|
|
|
.event.pressed = false,
|
2023-04-03 08:33:45 +00:00
|
|
|
.event.type = tapping_key.event.type,
|
2021-11-01 19:18:33 +00:00
|
|
|
# ifdef COMBO_ENABLE
|
|
|
|
.keycode = tapping_key.keycode,
|
|
|
|
# endif
|
|
|
|
});
|
2015-04-09 16:32:04 +00:00
|
|
|
} else {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: Start while last tap(1).\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
}
|
|
|
|
tapping_key = *keyp;
|
|
|
|
waiting_buffer_scan_tap();
|
|
|
|
debug_tapping_key();
|
|
|
|
return true;
|
2019-08-30 18:19:03 +00:00
|
|
|
} else {
|
2023-04-03 08:33:45 +00:00
|
|
|
ac_dprintf("Tapping: key event while last tap(>0).\n");
|
2023-09-25 02:45:49 +00:00
|
|
|
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
|
|
|
|
retroshift_swap_times();
|
|
|
|
# endif
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(keyp);
|
2015-04-09 16:32:04 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// after TAPPING_TERM
|
|
|
|
else {
|
|
|
|
if (tapping_key.tap.count == 0) {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: End. Timeout. Not tap(0): ");
|
2019-08-30 18:19:03 +00:00
|
|
|
debug_event(event);
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("\n");
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(&tapping_key);
|
2023-04-03 08:33:45 +00:00
|
|
|
tapping_key = (keyrecord_t){0};
|
2015-04-09 16:32:04 +00:00
|
|
|
debug_tapping_key();
|
|
|
|
return false;
|
2019-08-30 18:19:03 +00:00
|
|
|
} else {
|
2023-04-03 08:33:45 +00:00
|
|
|
if (IS_NOEVENT(event)) {
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-05 23:44:57 +00:00
|
|
|
if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: End. last timeout tap release(>0).");
|
2015-04-09 16:32:04 +00:00
|
|
|
keyp->tap = tapping_key.tap;
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(keyp);
|
2023-04-03 08:33:45 +00:00
|
|
|
tapping_key = (keyrecord_t){0};
|
2015-04-09 16:32:04 +00:00
|
|
|
return true;
|
2021-08-05 23:44:57 +00:00
|
|
|
} else if (is_tap_record(keyp) && event.pressed) {
|
2015-04-09 16:32:04 +00:00
|
|
|
if (tapping_key.tap.count > 1) {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: Start new tap with releasing last timeout tap(>1).\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
// unregister key
|
2021-11-01 19:18:33 +00:00
|
|
|
process_record(&(keyrecord_t){
|
|
|
|
.tap = tapping_key.tap,
|
|
|
|
.event.key = tapping_key.event.key,
|
|
|
|
.event.time = event.time,
|
|
|
|
.event.pressed = false,
|
2023-04-03 08:33:45 +00:00
|
|
|
.event.type = tapping_key.event.type,
|
2021-11-01 19:18:33 +00:00
|
|
|
# ifdef COMBO_ENABLE
|
|
|
|
.keycode = tapping_key.keycode,
|
|
|
|
# endif
|
|
|
|
});
|
2015-04-09 16:32:04 +00:00
|
|
|
} else {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: Start while last timeout tap(1).\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
}
|
|
|
|
tapping_key = *keyp;
|
|
|
|
waiting_buffer_scan_tap();
|
|
|
|
debug_tapping_key();
|
|
|
|
return true;
|
2019-08-30 18:19:03 +00:00
|
|
|
} else {
|
2023-04-03 08:33:45 +00:00
|
|
|
ac_dprintf("Tapping: key event while last timeout tap(>0).\n");
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(keyp);
|
2015-04-09 16:32:04 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-04-03 08:33:45 +00:00
|
|
|
}
|
|
|
|
// process "released" tapping key state
|
|
|
|
else {
|
2023-09-25 02:45:49 +00:00
|
|
|
if (WITHIN_TAPPING_TERM(event) || MAYBE_RETRO_SHIFTING(event, keyp)) {
|
2023-04-03 08:33:45 +00:00
|
|
|
if (IS_NOEVENT(event)) {
|
|
|
|
// early return for tick events
|
|
|
|
return true;
|
|
|
|
}
|
2015-04-09 16:32:04 +00:00
|
|
|
if (event.pressed) {
|
2021-08-05 23:44:57 +00:00
|
|
|
if (IS_TAPPING_RECORD(keyp)) {
|
2022-12-12 15:52:22 +00:00
|
|
|
if (WITHIN_QUICK_TAP_TERM(event) && !tapping_key.tap.interrupted && tapping_key.tap.count > 0) {
|
2015-04-09 16:32:04 +00:00
|
|
|
// sequential tap.
|
|
|
|
keyp->tap = tapping_key.tap;
|
|
|
|
if (keyp->tap.count < 15) keyp->tap.count += 1;
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: Tap press(%u)\n", keyp->tap.count);
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(keyp);
|
2015-04-09 16:32:04 +00:00
|
|
|
tapping_key = *keyp;
|
|
|
|
debug_tapping_key();
|
|
|
|
return true;
|
|
|
|
}
|
2017-02-14 05:12:54 +00:00
|
|
|
// FIX: start new tap again
|
|
|
|
tapping_key = *keyp;
|
|
|
|
return true;
|
2021-08-05 23:44:57 +00:00
|
|
|
} else if (is_tap_record(keyp)) {
|
2015-04-09 16:32:04 +00:00
|
|
|
// Sequential tap can be interfered with other tap key.
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: Start with interfering other tap.\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
tapping_key = *keyp;
|
|
|
|
waiting_buffer_scan_tap();
|
|
|
|
debug_tapping_key();
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
// should none in buffer
|
|
|
|
// FIX: interrupted when other key is pressed
|
|
|
|
tapping_key.tap.interrupted = true;
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(keyp);
|
2015-04-09 16:32:04 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
2023-04-03 08:33:45 +00:00
|
|
|
ac_dprintf("Tapping: other key just after tap.\n");
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(keyp);
|
2015-04-09 16:32:04 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
2023-04-03 08:33:45 +00:00
|
|
|
// Timeout - reset state machine.
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("Tapping: End(Timeout after releasing last tap): ");
|
2019-08-30 18:19:03 +00:00
|
|
|
debug_event(event);
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("\n");
|
2023-04-03 08:33:45 +00:00
|
|
|
tapping_key = (keyrecord_t){0};
|
2015-04-09 16:32:04 +00:00
|
|
|
debug_tapping_key();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-22 06:50:38 +00:00
|
|
|
/** \brief Waiting buffer enq
|
|
|
|
*
|
|
|
|
* FIXME: Needs docs
|
2015-04-09 16:32:04 +00:00
|
|
|
*/
|
2019-08-30 18:19:03 +00:00
|
|
|
bool waiting_buffer_enq(keyrecord_t record) {
|
2015-04-09 16:32:04 +00:00
|
|
|
if (IS_NOEVENT(record.event)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("waiting_buffer_enq: Over flow.\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
waiting_buffer[waiting_buffer_head] = record;
|
2019-08-30 18:19:03 +00:00
|
|
|
waiting_buffer_head = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE;
|
2015-04-09 16:32:04 +00:00
|
|
|
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("waiting_buffer_enq: ");
|
2019-08-30 18:19:03 +00:00
|
|
|
debug_waiting_buffer();
|
2015-04-09 16:32:04 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-22 06:50:38 +00:00
|
|
|
/** \brief Waiting buffer clear
|
|
|
|
*
|
|
|
|
* FIXME: Needs docs
|
|
|
|
*/
|
2019-08-30 18:19:03 +00:00
|
|
|
void waiting_buffer_clear(void) {
|
2015-04-09 16:32:04 +00:00
|
|
|
waiting_buffer_head = 0;
|
|
|
|
waiting_buffer_tail = 0;
|
|
|
|
}
|
|
|
|
|
2018-03-22 06:50:38 +00:00
|
|
|
/** \brief Waiting buffer typed
|
|
|
|
*
|
|
|
|
* FIXME: Needs docs
|
|
|
|
*/
|
2019-08-30 18:19:03 +00:00
|
|
|
bool waiting_buffer_typed(keyevent_t event) {
|
2015-04-09 16:32:04 +00:00
|
|
|
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
2019-08-30 18:19:03 +00:00
|
|
|
if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) {
|
2015-04-09 16:32:04 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-03-22 06:50:38 +00:00
|
|
|
/** \brief Waiting buffer has anykey pressed
|
|
|
|
*
|
|
|
|
* FIXME: Needs docs
|
|
|
|
*/
|
2019-08-30 18:19:03 +00:00
|
|
|
__attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) {
|
2015-04-09 16:32:04 +00:00
|
|
|
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
|
|
|
if (waiting_buffer[i].event.pressed) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-03-22 06:50:38 +00:00
|
|
|
/** \brief Scan buffer for tapping
|
|
|
|
*
|
|
|
|
* FIXME: Needs docs
|
|
|
|
*/
|
2019-08-30 18:19:03 +00:00
|
|
|
void waiting_buffer_scan_tap(void) {
|
2023-04-03 08:33:45 +00:00
|
|
|
// early return if:
|
|
|
|
// - tapping already is settled
|
|
|
|
// - invalid state: tapping_key released && tap.count == 0
|
|
|
|
if ((tapping_key.tap.count > 0) || !tapping_key.event.pressed) {
|
|
|
|
return;
|
|
|
|
}
|
2015-04-09 16:32:04 +00:00
|
|
|
|
2023-09-25 02:45:49 +00:00
|
|
|
# if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
|
|
|
|
TAP_DEFINE_KEYCODE;
|
|
|
|
# endif
|
2015-04-09 16:32:04 +00:00
|
|
|
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
2023-04-03 08:33:45 +00:00
|
|
|
keyrecord_t *candidate = &waiting_buffer[i];
|
2023-09-25 02:45:49 +00:00
|
|
|
// clang-format off
|
|
|
|
if (IS_EVENT(candidate->event) && KEYEQ(candidate->event.key, tapping_key.event.key) && !candidate->event.pressed && (
|
|
|
|
WITHIN_TAPPING_TERM(waiting_buffer[i].event) || MAYBE_RETRO_SHIFTING(waiting_buffer[i].event, &tapping_key)
|
|
|
|
)) {
|
|
|
|
// clang-format on
|
2023-04-03 08:33:45 +00:00
|
|
|
tapping_key.tap.count = 1;
|
|
|
|
candidate->tap.count = 1;
|
2016-05-15 04:47:25 +00:00
|
|
|
process_record(&tapping_key);
|
2015-04-09 16:32:04 +00:00
|
|
|
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("waiting_buffer_scan_tap: found at [%u]\n", i);
|
2015-04-09 16:32:04 +00:00
|
|
|
debug_waiting_buffer();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
# 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) {
|
|
|
|
if (tap_hold_record->event.type != KEY_EVENT || other_record->event.type != KEY_EVENT) {
|
|
|
|
return true; // Return true on combos or other non-key events.
|
|
|
|
}
|
|
|
|
|
|
|
|
char tap_hold_hand = chordal_hold_handedness(tap_hold_record->event.key);
|
|
|
|
if (tap_hold_hand == '*') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
char other_hand = chordal_hold_handedness(other_record->event.key);
|
|
|
|
return other_hand == '*' || tap_hold_hand != other_hand;
|
|
|
|
}
|
|
|
|
|
|
|
|
__attribute__((weak)) char chordal_hold_handedness(keypos_t key) {
|
|
|
|
return (char)pgm_read_byte(&chordal_hold_layout[key.row][key.col]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void registered_taps_add(keypos_t key) {
|
|
|
|
if (num_registered_taps >= REGISTERED_TAPS_SIZE) {
|
|
|
|
ac_dprintf("TAPS OVERFLOW: CLEAR ALL STATES\n");
|
|
|
|
clear_keyboard();
|
|
|
|
num_registered_taps = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
registered_taps[num_registered_taps] = key;
|
|
|
|
++num_registered_taps;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int8_t registered_tap_find(keypos_t key) {
|
|
|
|
for (int8_t i = 0; i < num_registered_taps; ++i) {
|
|
|
|
if (KEYEQ(registered_taps[i], key)) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void registered_taps_del_index(uint8_t i) {
|
|
|
|
if (i < num_registered_taps) {
|
|
|
|
--num_registered_taps;
|
|
|
|
if (i < num_registered_taps) {
|
|
|
|
registered_taps[i] = registered_taps[num_registered_taps];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void debug_registered_taps(void) {
|
|
|
|
ac_dprintf("registered_taps = { ");
|
|
|
|
for (int8_t i = 0; i < num_registered_taps; ++i) {
|
|
|
|
ac_dprintf("%02X%02X ", registered_taps[i].row, registered_taps[i].col);
|
|
|
|
}
|
|
|
|
ac_dprintf("}\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t waiting_buffer_find_chordal_hold_tap(void) {
|
|
|
|
keyrecord_t *prev = &tapping_key;
|
|
|
|
uint16_t prev_keycode = get_record_keycode(&tapping_key, false);
|
|
|
|
uint8_t first_tap = WAITING_BUFFER_SIZE;
|
|
|
|
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
|
|
|
keyrecord_t * cur = &waiting_buffer[i];
|
|
|
|
const uint16_t cur_keycode = get_record_keycode(cur, false);
|
|
|
|
if (!cur->event.pressed || !is_mt_or_lt(prev_keycode)) {
|
|
|
|
break;
|
|
|
|
} else if (get_chordal_hold(prev_keycode, prev, cur_keycode, cur)) {
|
|
|
|
first_tap = i; // Track one index past the latest hold.
|
|
|
|
}
|
|
|
|
prev = cur;
|
|
|
|
prev_keycode = cur_keycode;
|
|
|
|
}
|
|
|
|
return first_tap;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void waiting_buffer_chordal_hold_taps_until(keypos_t key) {
|
|
|
|
while (waiting_buffer_tail != waiting_buffer_head) {
|
|
|
|
keyrecord_t *record = &waiting_buffer[waiting_buffer_tail];
|
|
|
|
ac_dprintf("waiting_buffer_chordal_hold_taps_until: processing [%u]\n", waiting_buffer_tail);
|
2025-01-29 19:17:54 +00:00
|
|
|
if (record->event.pressed && is_tap_record(record)) {
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
record->tap.count = 1;
|
|
|
|
registered_taps_add(record->event.key);
|
|
|
|
}
|
|
|
|
process_record(record);
|
|
|
|
waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE;
|
|
|
|
|
|
|
|
if (KEYEQ(key, record->event.key) && record->event.pressed) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void waiting_buffer_process_regular(void) {
|
|
|
|
for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {
|
|
|
|
if (is_tap_record(&waiting_buffer[waiting_buffer_tail])) {
|
|
|
|
break; // Stop once a tap-hold key event is reached.
|
|
|
|
}
|
|
|
|
ac_dprintf("waiting_buffer_process_regular: processing [%u]\n", waiting_buffer_tail);
|
|
|
|
process_record(&waiting_buffer[waiting_buffer_tail]);
|
|
|
|
}
|
|
|
|
debug_waiting_buffer();
|
|
|
|
}
|
|
|
|
# endif // CHORDAL_HOLD
|
|
|
|
|
|
|
|
/** \brief Logs tapping key if ACTION_DEBUG is enabled. */
|
2019-08-30 18:19:03 +00:00
|
|
|
static void debug_tapping_key(void) {
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("TAPPING_KEY=");
|
2019-08-30 18:19:03 +00:00
|
|
|
debug_record(tapping_key);
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
}
|
|
|
|
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
/** \brief Logs waiting buffer if ACTION_DEBUG is enabled. */
|
2019-08-30 18:19:03 +00:00
|
|
|
static void debug_waiting_buffer(void) {
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
ac_dprintf("{");
|
2015-04-09 16:32:04 +00:00
|
|
|
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 11:32:23 +00:00
|
|
|
ac_dprintf(" [%u]=", i);
|
2019-08-30 18:19:03 +00:00
|
|
|
debug_record(waiting_buffer[i]);
|
2015-04-09 16:32:04 +00:00
|
|
|
}
|
2022-12-15 22:38:25 +00:00
|
|
|
ac_dprintf("}\n");
|
2015-04-09 16:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|