Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix layer switching from tap dances by redoing the keymap lookup #17935

Merged
merged 2 commits into from
Oct 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions quantum/process_keycode/process_tap_dance.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ static inline void process_tap_dance_action_on_dance_finished(qk_tap_dance_actio
}
}

void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) {
bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) {
qk_tap_dance_action_t *action;

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

if (!active_td || keycode == active_td) return;
if (!active_td || keycode == active_td) return false;

action = &tap_dance_actions[TD_INDEX(active_td)];
action->state.interrupted = true;
Expand All @@ -130,6 +130,12 @@ void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) {
// Tap dance actions can leave some weak mods active (e.g., if the tap dance is mapped to a keycode with
// modifiers), but these weak mods should not affect the keypress which interrupted the tap dance.
clear_weak_mods();

// Signal that a tap dance has been finished due to being interrupted,
// therefore the keymap lookup for the currently processed event needs to
// be repeated with the current layer state that might have been updated by
// the finished tap dance.
return true;
}

bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {
Expand Down
2 changes: 1 addition & 1 deletion quantum/process_keycode/process_tap_dance.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ void reset_tap_dance(qk_tap_dance_state_t *state);

/* To be used internally */

void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record);
bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record);
bool process_tap_dance(uint16_t keycode, keyrecord_t *record);
void tap_dance_task(void);

Expand Down
6 changes: 5 additions & 1 deletion quantum/quantum.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,11 @@ bool process_record_quantum(keyrecord_t *record) {
#endif

#ifdef TAP_DANCE_ENABLE
preprocess_tap_dance(keycode, record);
if (preprocess_tap_dance(keycode, record)) {
// The tap dance might have updated the layer state, therefore the
// result of the keycode lookup might change.
keycode = get_record_keycode(record, true);
}
#endif

if (!(
Expand Down
6 changes: 6 additions & 0 deletions tests/tap_dance/tap_dance_layers/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright 2022 Sergey Vlasov (@sigprof)
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "test_common.h"
97 changes: 97 additions & 0 deletions tests/tap_dance/tap_dance_layers/tap_dance_defs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright 2022 Sergey Vlasov (@sigprof)
// SPDX-License-Identifier: GPL-2.0-or-later

#include "quantum.h"
#include "tap_dance_defs.h"

// Implement custom keycodes which are used to check that the layer switching
// behaves properly.
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case FAST_AB:
case SLOW_AB:
if (record->event.pressed) {
tap_code(KC_A);
} else {
tap_code(KC_B);
}
return keycode == SLOW_AB;
case FAST_CD:
case SLOW_CD:
if (record->event.pressed) {
tap_code(KC_C);
} else {
tap_code(KC_D);
}
return keycode == SLOW_CD;
}
return true;
}

// Implement a custom tap dance with the following behavior:
// - single tap: KC_APP
// - single hold: MO(1)
// - double tap/hold: KC_RCTL
// (The single tap and hold actions are mostly equivalent to LT(1, KC_APP).)

enum lt_app_state {
LTA_NONE,
LTA_SINGLE_TAP,
LTA_SINGLE_HOLD,
LTA_DOUBLE_HOLD,
};

static enum lt_app_state saved_lt_app_state;

static enum lt_app_state get_lt_app_state(qk_tap_dance_state_t *state) {
if (state->count == 1) {
if (!state->pressed) {
return LTA_SINGLE_TAP;
} else {
return LTA_SINGLE_HOLD;
}
} else if (state->count == 2) {
return LTA_DOUBLE_HOLD;
} else {
return LTA_NONE;
}
}

static void lt_app_finished(qk_tap_dance_state_t *state, void *user_data) {
saved_lt_app_state = get_lt_app_state(state);
switch (saved_lt_app_state) {
case LTA_NONE:
break;
case LTA_SINGLE_TAP:
register_code(KC_APP);
break;
case LTA_SINGLE_HOLD:
layer_on(1);
break;
case LTA_DOUBLE_HOLD:
register_code(KC_RCTL);
break;
}
}

static void lt_app_reset(qk_tap_dance_state_t *state, void *user_data) {
switch (saved_lt_app_state) {
case LTA_NONE:
break;
case LTA_SINGLE_TAP:
unregister_code(KC_APP);
break;
case LTA_SINGLE_HOLD:
layer_off(1);
break;
case LTA_DOUBLE_HOLD:
unregister_code(KC_RCTL);
break;
}
}

qk_tap_dance_action_t tap_dance_actions[] = {
[TD_L_MOVE] = ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1),
[TD_L_TOGG] = ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1),
[TD_LT_APP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, lt_app_finished, lt_app_reset),
};
29 changes: 29 additions & 0 deletions tests/tap_dance/tap_dance_layers/tap_dance_defs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2022 Sergey Vlasov (@sigprof)
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

enum custom_keycodes {
// (FAST|SLOW)_xy = tap KC_x on press, tap KC_y on release. For FAST_xy
// process_record_user() returns false to stop processing early; for
// SLOW_xy process_record_user() returns true, therefore all other key
// handlers are invoked.
FAST_AB = SAFE_RANGE,
FAST_CD,
SLOW_AB,
SLOW_CD,
};

enum tap_dance_ids {
TD_L_MOVE, // ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1)
TD_L_TOGG, // ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1)
TD_LT_APP, // similar to LT(1, KC_APP) with KC_RCTL on tap+hold or double tap
};

#ifdef __cplusplus
}
#endif
10 changes: 10 additions & 0 deletions tests/tap_dance/tap_dance_layers/test.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright 2022 Sergey Vlasov (@sigprof)
# SPDX-License-Identifier: GPL-2.0-or-later

# --------------------------------------------------------------------------------
# Keep this file, even if it is empty, as a marker that this folder contains tests
# --------------------------------------------------------------------------------

TAP_DANCE_ENABLE = yes

SRC += tap_dance_defs.c
Loading