[Core] Flow Tap tap-hold option to disable HRMs during fast typing (#25125)
aka Global Quick Tap, Require Prior Idle
This commit is contained in:
		
							parent
							
								
									a7bf8e64a5
								
							
						
					
					
						commit
						8d8dcb089e
					
				
					 11 changed files with 648 additions and 36 deletions
				
			
		|  | @ -4,6 +4,7 @@ | |||
| #include "action.h" | ||||
| #include "action_layer.h" | ||||
| #include "action_tapping.h" | ||||
| #include "action_util.h" | ||||
| #include "keycode.h" | ||||
| #include "timer.h" | ||||
| 
 | ||||
|  | @ -49,9 +50,7 @@ __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *re | |||
| } | ||||
| #    endif | ||||
| 
 | ||||
| #    if defined(CHORDAL_HOLD) | ||||
| extern const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM; | ||||
| 
 | ||||
| #    if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM) | ||||
| #        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] = {}; | ||||
|  | @ -66,6 +65,14 @@ static void registered_taps_del_index(uint8_t i); | |||
| /** Logs the registered_taps array for debugging. */ | ||||
| static void debug_registered_taps(void); | ||||
| 
 | ||||
| static bool is_mt_or_lt(uint16_t keycode) { | ||||
|     return IS_QK_MOD_TAP(keycode) || IS_QK_LAYER_TAP(keycode); | ||||
| } | ||||
| #    endif // defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
 | ||||
| 
 | ||||
| #    if defined(CHORDAL_HOLD) | ||||
| extern const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM; | ||||
| 
 | ||||
| /** \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 | ||||
|  | @ -82,10 +89,6 @@ 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
 | ||||
| 
 | ||||
| #    ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY | ||||
|  | @ -98,6 +101,13 @@ __attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyreco | |||
| #        include "process_auto_shift.h" | ||||
| #    endif | ||||
| 
 | ||||
| #    if defined(FLOW_TAP_TERM) | ||||
| static uint32_t last_input   = 0; | ||||
| static uint16_t prev_keycode = KC_NO; | ||||
| 
 | ||||
| uint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t *record, uint16_t prev_keycode); | ||||
| #    endif // defined(FLOW_TAP_TERM)
 | ||||
| 
 | ||||
| static keyrecord_t tapping_key                         = {}; | ||||
| static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {}; | ||||
| static uint8_t     waiting_buffer_head                 = 0; | ||||
|  | @ -147,6 +157,19 @@ void action_tapping_process(keyrecord_t record) { | |||
|         } | ||||
|     } | ||||
|     if (IS_EVENT(record.event)) { | ||||
| #    if defined(FLOW_TAP_TERM) | ||||
|         const uint16_t keycode = get_record_keycode(&record, false); | ||||
|         // Track the previous key press.
 | ||||
|         if (record.event.pressed) { | ||||
|             prev_keycode = keycode; | ||||
|         } | ||||
|         // If there is no unsettled tap-hold key, update last input time. Ignore
 | ||||
|         // mod keys in this update to allow for chording multiple mods for
 | ||||
|         // hotkeys like "Ctrl+Shift+arrow".
 | ||||
|         if (IS_NOEVENT(tapping_key.event) && !IS_MODIFIER_KEYCODE(keycode)) { | ||||
|             last_input = timer_read32(); | ||||
|         } | ||||
| #    endif // defined(FLOW_TAP_TERM)
 | ||||
|         ac_dprintf("\n"); | ||||
|     } | ||||
| } | ||||
|  | @ -205,7 +228,7 @@ void action_tapping_process(keyrecord_t record) { | |||
| bool process_tapping(keyrecord_t *keyp) { | ||||
|     const keyevent_t event = keyp->event; | ||||
| 
 | ||||
| #    if defined(CHORDAL_HOLD) | ||||
| #    if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM) | ||||
|     if (!event.pressed) { | ||||
|         const int8_t i = registered_tap_find(event.key); | ||||
|         if (i != -1) { | ||||
|  | @ -217,7 +240,7 @@ bool process_tapping(keyrecord_t *keyp) { | |||
|             debug_registered_taps(); | ||||
|         } | ||||
|     } | ||||
| #    endif // CHORDAL_HOLD
 | ||||
| #    endif // defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
 | ||||
| 
 | ||||
|     // state machine is in the "reset" state, no tapping key is to be
 | ||||
|     // processed
 | ||||
|  | @ -227,6 +250,27 @@ bool process_tapping(keyrecord_t *keyp) { | |||
|         } else if (event.pressed && is_tap_record(keyp)) { | ||||
|             // the currently pressed key is a tapping key, therefore transition
 | ||||
|             // into the "pressed" tapping key state
 | ||||
| 
 | ||||
| #    if defined(FLOW_TAP_TERM) | ||||
|             const uint16_t keycode = get_record_keycode(keyp, false); | ||||
|             if (is_mt_or_lt(keycode)) { | ||||
|                 const uint32_t idle_time = timer_elapsed32(last_input); | ||||
|                 uint16_t       term      = get_flow_tap_term(keycode, keyp, prev_keycode); | ||||
|                 if (term > 500) { | ||||
|                     term = 500; | ||||
|                 } | ||||
|                 if (idle_time < 500 && idle_time < term) { | ||||
|                     debug_event(keyp->event); | ||||
|                     ac_dprintf(" within flow tap term (%u < %u) considered a tap\n", (int16_t)idle_time, term); | ||||
|                     keyp->tap.count = 1; | ||||
|                     registered_taps_add(keyp->event.key); | ||||
|                     debug_registered_taps(); | ||||
|                     process_record(keyp); | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
| #    endif // defined(FLOW_TAP_TERM)
 | ||||
| 
 | ||||
|             ac_dprintf("Tapping: Start(Press tap key).\n"); | ||||
|             tapping_key = *keyp; | ||||
|             process_record_tap_hint(&tapping_key); | ||||
|  | @ -655,28 +699,7 @@ void waiting_buffer_scan_tap(void) { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #    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]); | ||||
| } | ||||
| 
 | ||||
| #    if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM) | ||||
| static void registered_taps_add(keypos_t key) { | ||||
|     if (num_registered_taps >= REGISTERED_TAPS_SIZE) { | ||||
|         ac_dprintf("TAPS OVERFLOW: CLEAR ALL STATES\n"); | ||||
|  | @ -714,6 +737,30 @@ static void debug_registered_taps(void) { | |||
|     ac_dprintf("}\n"); | ||||
| } | ||||
| 
 | ||||
| #    endif // defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
 | ||||
| 
 | ||||
| #    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 uint8_t waiting_buffer_find_chordal_hold_tap(void) { | ||||
|     keyrecord_t *prev         = &tapping_key; | ||||
|     uint16_t     prev_keycode = get_record_keycode(&tapping_key, false); | ||||
|  | @ -761,6 +808,35 @@ static void waiting_buffer_process_regular(void) { | |||
| } | ||||
| #    endif // CHORDAL_HOLD
 | ||||
| 
 | ||||
| #    ifdef FLOW_TAP_TERM | ||||
| // By default, enable Flow Tap for the keys in the main alphas area and Space.
 | ||||
| // This should work reasonably even if the layout is remapped on the host to an
 | ||||
| // alt layout or international layout (e.g. Dvorak or AZERTY), where these same
 | ||||
| // key positions are mostly used for typing letters.
 | ||||
| __attribute__((weak)) bool is_flow_tap_key(uint16_t keycode) { | ||||
|     if ((get_mods() & (MOD_MASK_CG | MOD_BIT_LALT)) != 0) { | ||||
|         return false; // Disable Flow Tap on hotkeys.
 | ||||
|     } | ||||
|     switch (get_tap_keycode(keycode)) { | ||||
|         case KC_SPC: | ||||
|         case KC_A ... KC_Z: | ||||
|         case KC_DOT: | ||||
|         case KC_COMM: | ||||
|         case KC_SCLN: | ||||
|         case KC_SLSH: | ||||
|             return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| __attribute__((weak)) uint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t *record, uint16_t prev_keycode) { | ||||
|     if (is_flow_tap_key(keycode) && is_flow_tap_key(prev_keycode)) { | ||||
|         return FLOW_TAP_TERM; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| #    endif // FLOW_TAP_TERM
 | ||||
| 
 | ||||
| /** \brief Logs tapping key if ACTION_DEBUG is enabled. */ | ||||
| static void debug_tapping_key(void) { | ||||
|     ac_dprintf("TAPPING_KEY="); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Pascal Getreuer
						Pascal Getreuer