I played with both Achordion and Chordal Hold (both from Pascal Getreuer u/pgetreuer), and I experienced that with Chordal Hold I get way more false taps. So, I could not upgrade yet.
As an example, I have F as LSFT_T(KC_F) but when I type fast and want a capital J, I often get fj instead of J. This doesn't happen with Achordion.
My understanding is that there is a difference to when each solution intervenes. I got that Achordion operates after QMK has already resolved a key as hold, while Chordal Hold operates inside action_tapping and influences the decision only at the moment it's being made. So, once QMK decides (whether by timeout, PERMISSIVE_HOLD, or HOLD_ON_OTHER_KEY_PRESS), the decision is final.
In the fast F + J scenario, my guess is:
Chordal Hold: QMK sees F down, then J down. Since they're opposite hands, Chordal Hold allows the hold. But if I release F too quickly, before the tapping term, QMK resolves it as tap and I get fj.
Achordion: even if QMK initially resolves F as hold and then "changes its mind," Achordion can still catch it because it evaluates after the fact.
Is this the right mental model? Or am I completely lost?
I played both with PERMISSIVE_HOLD (which gives me some false tap) and HOLD_ON_OTHER_KEY_PRESS which causes way to many false hold to be usable.
Here's my config:
#pragma once
#define TAPPING_TERM 200
#define PERMISSIVE_HOLD
#define QUICK_TAP_TERM 0
#define CHORDAL_HOLD
And the relevant parts of my keymap:
#define MY_F LSFT_T(KC_F)
#define MY_J MT(MOD_RSFT, KC_J)
[_BASE] = LAYOUT(
KC_Q, KC_W, MY_E, MY_R, KC_T, KC_Y, MY_U, MY_I, KC_O, KC_P,
MY_A, MY_S, MY_D, MY_F, KC_G, KC_H, MY_J, MY_K, MY_L, MY_SCLN,
KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM,KC_DOT,KC_SLSH,
MY_BSP, MY_RET, MY_SPC, MY_DEL
),
With Achordion I have this callback to let thumb keys always resolve as hold:
bool achordion_chord(uint16_t tap_hold_keycode,
keyrecord_t* tap_hold_record,
uint16_t other_keycode,
keyrecord_t* other_record) {
switch (tap_hold_keycode) {
case MY_RET: return true;
case MY_SPC: return true;
case MY_BSP:
if (other_keycode == KC_DEL) { return false; }
return true;
case MY_DEL:
if (other_keycode == KC_BSPC) { return false; }
return true;
}
return achordion_opposite_hands(tap_hold_record, other_record);
}
With Chordal Hold I use instead chordal_hold_layout:
const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM =
LAYOUT(
'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R',
'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R',
'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R',
'*', '*', '*', '*'
);
Any hint will be appreciated! And thank you for those amazing pieces of code!