Skip to content

Commit ca0c128

Browse files
authored
Fix layer switching from tap dances by redoing the keymap lookup (#17935)
1 parent 0e6f191 commit ca0c128

File tree

8 files changed

+874
-5
lines changed

8 files changed

+874
-5
lines changed

quantum/process_keycode/process_tap_dance.c

+9-3
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,12 @@ static inline void process_tap_dance_action_on_dance_finished(qk_tap_dance_actio
115115
}
116116
}
117117

118-
void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) {
118+
bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) {
119119
qk_tap_dance_action_t *action;
120120

121-
if (!record->event.pressed) return;
121+
if (!record->event.pressed) return false;
122122

123-
if (!active_td || keycode == active_td) return;
123+
if (!active_td || keycode == active_td) return false;
124124

125125
action = &tap_dance_actions[TD_INDEX(active_td)];
126126
action->state.interrupted = true;
@@ -130,6 +130,12 @@ void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) {
130130
// Tap dance actions can leave some weak mods active (e.g., if the tap dance is mapped to a keycode with
131131
// modifiers), but these weak mods should not affect the keypress which interrupted the tap dance.
132132
clear_weak_mods();
133+
134+
// Signal that a tap dance has been finished due to being interrupted,
135+
// therefore the keymap lookup for the currently processed event needs to
136+
// be repeated with the current layer state that might have been updated by
137+
// the finished tap dance.
138+
return true;
133139
}
134140

135141
bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {

quantum/process_keycode/process_tap_dance.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ void reset_tap_dance(qk_tap_dance_state_t *state);
8181

8282
/* To be used internally */
8383

84-
void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record);
84+
bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record);
8585
bool process_tap_dance(uint16_t keycode, keyrecord_t *record);
8686
void tap_dance_task(void);
8787

quantum/quantum.c

+5-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,11 @@ bool process_record_quantum(keyrecord_t *record) {
251251
#endif
252252

253253
#ifdef TAP_DANCE_ENABLE
254-
preprocess_tap_dance(keycode, record);
254+
if (preprocess_tap_dance(keycode, record)) {
255+
// The tap dance might have updated the layer state, therefore the
256+
// result of the keycode lookup might change.
257+
keycode = get_record_keycode(record, true);
258+
}
255259
#endif
256260

257261
if (!(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright 2022 Sergey Vlasov (@sigprof)
2+
// SPDX-License-Identifier: GPL-2.0-or-later
3+
4+
#pragma once
5+
6+
#include "test_common.h"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright 2022 Sergey Vlasov (@sigprof)
2+
// SPDX-License-Identifier: GPL-2.0-or-later
3+
4+
#include "quantum.h"
5+
#include "tap_dance_defs.h"
6+
7+
// Implement custom keycodes which are used to check that the layer switching
8+
// behaves properly.
9+
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
10+
switch (keycode) {
11+
case FAST_AB:
12+
case SLOW_AB:
13+
if (record->event.pressed) {
14+
tap_code(KC_A);
15+
} else {
16+
tap_code(KC_B);
17+
}
18+
return keycode == SLOW_AB;
19+
case FAST_CD:
20+
case SLOW_CD:
21+
if (record->event.pressed) {
22+
tap_code(KC_C);
23+
} else {
24+
tap_code(KC_D);
25+
}
26+
return keycode == SLOW_CD;
27+
}
28+
return true;
29+
}
30+
31+
// Implement a custom tap dance with the following behavior:
32+
// - single tap: KC_APP
33+
// - single hold: MO(1)
34+
// - double tap/hold: KC_RCTL
35+
// (The single tap and hold actions are mostly equivalent to LT(1, KC_APP).)
36+
37+
enum lt_app_state {
38+
LTA_NONE,
39+
LTA_SINGLE_TAP,
40+
LTA_SINGLE_HOLD,
41+
LTA_DOUBLE_HOLD,
42+
};
43+
44+
static enum lt_app_state saved_lt_app_state;
45+
46+
static enum lt_app_state get_lt_app_state(qk_tap_dance_state_t *state) {
47+
if (state->count == 1) {
48+
if (!state->pressed) {
49+
return LTA_SINGLE_TAP;
50+
} else {
51+
return LTA_SINGLE_HOLD;
52+
}
53+
} else if (state->count == 2) {
54+
return LTA_DOUBLE_HOLD;
55+
} else {
56+
return LTA_NONE;
57+
}
58+
}
59+
60+
static void lt_app_finished(qk_tap_dance_state_t *state, void *user_data) {
61+
saved_lt_app_state = get_lt_app_state(state);
62+
switch (saved_lt_app_state) {
63+
case LTA_NONE:
64+
break;
65+
case LTA_SINGLE_TAP:
66+
register_code(KC_APP);
67+
break;
68+
case LTA_SINGLE_HOLD:
69+
layer_on(1);
70+
break;
71+
case LTA_DOUBLE_HOLD:
72+
register_code(KC_RCTL);
73+
break;
74+
}
75+
}
76+
77+
static void lt_app_reset(qk_tap_dance_state_t *state, void *user_data) {
78+
switch (saved_lt_app_state) {
79+
case LTA_NONE:
80+
break;
81+
case LTA_SINGLE_TAP:
82+
unregister_code(KC_APP);
83+
break;
84+
case LTA_SINGLE_HOLD:
85+
layer_off(1);
86+
break;
87+
case LTA_DOUBLE_HOLD:
88+
unregister_code(KC_RCTL);
89+
break;
90+
}
91+
}
92+
93+
qk_tap_dance_action_t tap_dance_actions[] = {
94+
[TD_L_MOVE] = ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1),
95+
[TD_L_TOGG] = ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1),
96+
[TD_LT_APP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, lt_app_finished, lt_app_reset),
97+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2022 Sergey Vlasov (@sigprof)
2+
// SPDX-License-Identifier: GPL-2.0-or-later
3+
4+
#pragma once
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
enum custom_keycodes {
11+
// (FAST|SLOW)_xy = tap KC_x on press, tap KC_y on release. For FAST_xy
12+
// process_record_user() returns false to stop processing early; for
13+
// SLOW_xy process_record_user() returns true, therefore all other key
14+
// handlers are invoked.
15+
FAST_AB = SAFE_RANGE,
16+
FAST_CD,
17+
SLOW_AB,
18+
SLOW_CD,
19+
};
20+
21+
enum tap_dance_ids {
22+
TD_L_MOVE, // ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1)
23+
TD_L_TOGG, // ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1)
24+
TD_LT_APP, // similar to LT(1, KC_APP) with KC_RCTL on tap+hold or double tap
25+
};
26+
27+
#ifdef __cplusplus
28+
}
29+
#endif
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright 2022 Sergey Vlasov (@sigprof)
2+
# SPDX-License-Identifier: GPL-2.0-or-later
3+
4+
# --------------------------------------------------------------------------------
5+
# Keep this file, even if it is empty, as a marker that this folder contains tests
6+
# --------------------------------------------------------------------------------
7+
8+
TAP_DANCE_ENABLE = yes
9+
10+
SRC += tap_dance_defs.c

0 commit comments

Comments
 (0)