feat(q5_max): Add speculative hold for caps_mod key

This commit is contained in:
2026-04-22 22:36:16 -04:00
parent 363b43cafd
commit 73de490e53
3 changed files with 43 additions and 51 deletions
@@ -17,7 +17,16 @@
// COMM+DOT+SLSH fallback combo fires regardless of the active layer. // COMM+DOT+SLSH fallback combo fires regardless of the active layer.
#define COMBO_ONLY_FROM_LAYER 0 #define COMBO_ONLY_FROM_LAYER 0
// Pressing the Shift key continues Caps Word and inverts the shift state
#define CAPS_WORD_INVERT_ON_SHIFT
// Default tapping term for mod-tap, layer-tap, and tap-dance keys. // Default tapping term for mod-tap, layer-tap, and tap-dance keys.
#define TAPPING_TERM 200 #define TAPPING_TERM 200
// Allow per-key overrides via get_tapping_term() in keymap.c. // Allow per-key overrides via get_tapping_term() in keymap.c.
#define TAPPING_TERM_PER_KEY #define TAPPING_TERM_PER_KEY
// Use right CTRL key to neutralize modifier taps when cancelled.
#define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_RIGHT_CTRL
// Neutralize left ALT and left GUI (Default value)
#define MODS_TO_NEUTRALIZE {MOD_BIT(KC_LEFT_ALT), MOD_BIT(KC_LEFT_GUI)}
@@ -41,10 +41,11 @@ enum custom_keycodes {
LCK_FN4, // Lock/unlock FN4 LCK_FN4, // Lock/unlock FN4
LCK_CTL, // Lock/unlock KEEB_CTL LCK_CTL, // Lock/unlock KEEB_CTL
LCK_BASE, // Clear all locks and return to BASE LCK_BASE, // Clear all locks and return to BASE
CAPS_MOD, // Tap=ESC, hold=Ctrl, Shift=CapsLock, Alt=CapsWord, GUI=Autocorrect
BSP_DEL, // Tap=Backspace, Shift+Tap=Delete BSP_DEL, // Tap=Backspace, Shift+Tap=Delete
}; };
#define CAPS_MOD MT(MOD_LCTL, KC_ESC)
// Declare layers early so the HID functions below can reference KEEB_CTL. // Declare layers early so the HID functions below can reference KEEB_CTL.
enum layers { enum layers {
BASE, BASE,
@@ -192,10 +193,7 @@ bool kc_raw_hid_rx_kb(uint8_t *data, uint8_t length) {
resp[HID_OFF_SRC] = HID_DEV_Q5MAX; resp[HID_OFF_SRC] = HID_DEV_Q5MAX;
resp[HID_OFF_FLAGS] = HID_FLAG_RESPONSE; resp[HID_OFF_FLAGS] = HID_FLAG_RESPONSE;
#ifdef LK_WIRELESS_ENABLE #ifdef LK_WIRELESS_ENABLE
resp[HID_PAYLOAD(HID_BATT_OFF_LEVEL)] = resp[HID_PAYLOAD(HID_BATT_OFF_LEVEL)] = (get_transport() & TRANSPORT_WIRELESS) ? battery_get_percentage() : HID_BATT_UNAVAILABLE;
(get_transport() & TRANSPORT_WIRELESS)
? battery_get_percentage()
: HID_BATT_UNAVAILABLE;
#else #else
resp[HID_PAYLOAD(HID_BATT_OFF_LEVEL)] = HID_BATT_UNAVAILABLE; resp[HID_PAYLOAD(HID_BATT_OFF_LEVEL)] = HID_BATT_UNAVAILABLE;
#endif #endif
@@ -211,9 +209,7 @@ bool kc_raw_hid_rx_kb(uint8_t *data, uint8_t length) {
} }
// CAPS_MOD state: tap=ESC, hold=Ctrl, Shift+tap=CapsLock, Alt+tap=CapsWord, GUI+tap=Autocorrect // CAPS_MOD state: tap=ESC, hold=Ctrl, Shift+tap=CapsLock, Alt+tap=CapsWord, GUI+tap=Autocorrect
static bool caps_mod_held = false; // (Refactored to use MT(MOD_LCTL, KC_ESC) with custom tap logic)
static bool caps_mod_ctrl_registered = false;
static uint16_t caps_mod_timer = 0;
// clang-format off // clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
@@ -350,38 +346,28 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) { switch (keycode) {
case CAPS_MOD: case CAPS_MOD:
if (record->event.pressed) { // Custom tap logic: only intercept if it's a TAP AND a modifier is held.
caps_mod_held = true; // If it's a pure hold (Ctrl) or a pure tap (Esc), return true to let
caps_mod_timer = timer_read(); // the MT() core handle it.
// If a real modifier is held, send a dummy key so the OS sees if (record->tap.count > 0 && record->event.pressed) {
// modifier+key rather than a bare modifier hold/tap. Without
// this, the OS never receives any keycode while the modifier is
// down and treats the eventual modifier release as a tap (e.g.
// GUI opening the app menu). KC_F24 is harmless and universally
// ignored by applications.
if (get_mods() & (MOD_MASK_GUI | MOD_MASK_ALT | MOD_MASK_SHIFT)) {
register_code(KC_F24);
unregister_code(KC_F24);
}
} else {
if (caps_mod_ctrl_registered) {
unregister_code(KC_LCTL);
caps_mod_ctrl_registered = false;
} else {
uint8_t mods = get_mods(); uint8_t mods = get_mods();
if (mods & (MOD_MASK_GUI | MOD_MASK_ALT)) {
// Neutralize the modifier hold so releasing GUI/Alt doesn't
// trigger an OS "tap" action (like opening the Start menu).
tap_code(DUMMY_MOD_NEUTRALIZER_KEYCODE);
if (mods & MOD_MASK_GUI) { if (mods & MOD_MASK_GUI) {
autocorrect_toggle(); autocorrect_toggle();
} else if (mods & MOD_MASK_ALT) {
caps_word_toggle();
} else if (mods & MOD_MASK_SHIFT) {
tap_code(KC_CAPS); // Shift still held → host sees Shift+CapsLock (toggles on most OSes)
} else { } else {
tap_code(KC_ESC); caps_word_toggle();
}
return false; // suppress default Esc tap
} else if (mods & MOD_MASK_SHIFT) {
tap_code(KC_CAPS);
return false; // suppress default Esc tap
} }
} }
caps_mod_held = false; // cleared in both hold and tap paths return true; // let core handle Esc tap or Ctrl hold
}
return false;
case LCK_FN1: case LCK_FN1:
case LCK_FN2: case LCK_FN2:
@@ -466,10 +452,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
} }
void matrix_scan_user(void) { void matrix_scan_user(void) {
if (caps_mod_held && !caps_mod_ctrl_registered && timer_elapsed(caps_mod_timer) > TAPPING_TERM) {
caps_mod_ctrl_registered = true;
register_code(KC_LCTL);
}
if (alt_tab_active && timer_elapsed(alt_tab_timer) > ALT_TAB_TIMEOUT) { if (alt_tab_active && timer_elapsed(alt_tab_timer) > ALT_TAB_TIMEOUT) {
unregister_code(KC_LALT); unregister_code(KC_LALT);
alt_tab_active = false; alt_tab_active = false;
+2 -1
View File
@@ -18,7 +18,8 @@
"nkro" : true, "nkro" : true,
"rgb_matrix": true, "rgb_matrix": true,
"raw" : true, "raw" : true,
"send_string": true "send_string": true,
"speculative_hold": true
}, },
"matrix_pins": { "matrix_pins": {
"cols": ["C6", "C7", "C8", "A14", "A15", "C10", "C11", "C13", "C14", "C15", "C0", "C1", "C2", "C3", "A0", "A1", "A2", "A3", "B10"], "cols": ["C6", "C7", "C8", "A14", "A15", "C10", "C11", "C13", "C14", "C15", "C0", "C1", "C2", "C3", "A0", "A1", "A2", "A3", "B10"],