Skip to content

Commit

Permalink
Fix layer switching from tap dances by redoing the keymap lookup (qmk…
Browse files Browse the repository at this point in the history
  • Loading branch information
sigprof authored Oct 3, 2022
1 parent 0e6f191 commit ca0c128
Show file tree
Hide file tree
Showing 8 changed files with 874 additions and 5 deletions.
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

0 comments on commit ca0c128

Please sign in to comment.