Skip to content

Commit

Permalink
Implement Toggle-Hold Mod keys as tap dances
Browse files Browse the repository at this point in the history
These keys act like the accessibility feature Sticky Keys, in that they allow the user to press the keys to lock or unlock the modifier held, but they also allow the user to simply press the key until it is depressed if holding it for longer than the `TAPPING_TERM` or if another key is pressed along with the Toggle-Hold Mod key.
  • Loading branch information
Nebuleon committed Feb 4, 2024
1 parent 6823e93 commit 24e8179
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 8 deletions.
12 changes: 12 additions & 0 deletions keyboards/zsa/moonlander/keymaps/nebuleon/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,16 @@
#define IL_TOGG LED_LEVEL /* stands for Indicator Led TOGGle */
#define LC_TOGG TOGGLE_LAYER_COLOR /* stands for Layer Color TOGGle */

/* \brief Converts a modifier bitfield for use with MT, i.e. where the side
* of all modifiers, left or right, is represented with one bit, to
* a modifier bitfield where each modifier on each side gets its own
* bit, for use with get_mods() and related functions.
* \param mods the 5-bit modifiers to convert
* \return the modifiers in 'mods', converted into an 8-bit bitfield
*/
uint8_t mt_to_mod_bits(uint8_t mods);

extern const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS];

extern uint8_t toggle_hold_held_mods;
extern uint8_t toggle_hold_locked_mods;
12 changes: 6 additions & 6 deletions keyboards/zsa/moonlander/keymaps/nebuleon/keymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
_______, TD_SCLC, TD_CMLA, TD_DTLP, TD_P_LB, TD_Y_UN, KC_UP, KC_LEFT, TD_F_EQ, TD_G_RB, TD_C_RP, TD_R_RA, TD_X_RC, TT_Web,
KC_BSPC, TD_A_DL, TD_O_DQ, TD_E_MI, TD_U_PC, TD_I_AM, KC_DOWN, KC_RGHT, TD_D_PI, TD_H_AT, TD_T_PL, TD_N_SQ, TD_S_TI, TT_Phrs,
KC_DEL, TD_CLLD, TD_Q_EX, TD_J_SL, TD_K_BS, TD_L_NB, TD_B_DG, TD_M_HA, TD_W_AS, TD_V_QU, TD_Z_RD, _______,
_______, KC_LGUI, SC_LAAP, TT_Move, TT_Nums, TT_FnMs, KC_LGUI, TT_Syms, TT_DvAc, KC_RALT, _______, TT_RGBC,
SC_LSSP, SC_LCEN, SC_TAWT, SC_LAAP, KC_LCTL, KC_LSFT
_______, KC_LGUI, SC_LAAP, TT_Move, TT_Nums, TT_FnMs, TD_LGUI, TT_Syms, TT_DvAc, KC_RALT, _______, TT_RGBC,
SC_LSSP, SC_LCEN, SC_TAWT, TD_LALT, TD_LCTL, TD_LSFT
),
[L_Control] = LAYOUT_moonlander(
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
Expand Down Expand Up @@ -85,16 +85,16 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
_______, FR_SCLN, FR_7, FR_8, FR_9, FR_COMM, _______, _______, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, _______,
_______, FR_COLN, FR_4, FR_5, FR_6, FR_DOT, _______, _______, FR_EQL, FR_PLUS, FR_MINS, FR_ASTR, FR_SLSH, _______,
_______, FR_0, FR_1, FR_2, FR_3, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, _______,
_______, _______, _______, _______, _leave_, _______, KC_RGUI, _______, _______, _______, _______, _______,
_______, _______, _______, SC_LAAP, KC_RCTL, KC_RSFT
_______, _______, _______, _______, _leave_, _______, TD_LGUI, _______, _______, _______, _______, _______,
_______, _______, _______, TD_LALT, TD_LCTL, TD_LSFT
),
[L_FnMouse] = LAYOUT_moonlander(
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, KC_F9, KC_F10, KC_F11, KC_F12, XXXXXXX, _______, _______, XXXXXXX, KC_BTN1, KC_MS_U, KC_BTN2, KC_WH_U, _______,
_______, KC_F5, KC_F6, KC_F7, KC_F8, XXXXXXX, _______, _______, XXXXXXX, KC_MS_L, KC_MS_D, KC_MS_R, KC_WH_D, _______,
_______, KC_F1, KC_F2, KC_F3, KC_F4, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, _______,
_______, _______, _______, _______, _______, _leave_, KC_LGUI, _______, _______, _______, _______, _______,
_______, _______, _______, SC_LAAP, KC_LCTL, KC_LSFT
_______, _______, _______, _______, _______, _leave_, TD_LGUI, _______, _______, _______, _______, _______,
_______, _______, _______, TD_LALT, TD_LCTL, TD_LSFT
),
[L_Web] = LAYOUT_moonlander(
_______, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, _______, _______, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, _______,
Expand Down
37 changes: 37 additions & 0 deletions keyboards/zsa/moonlander/keymaps/nebuleon/rgb.c
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,43 @@ void set_layer_color(void) {
}
break;
#endif
/* Tap Dance keys implementing Toggle-Hold Mods. */
case TD_LSFT:
case TD_LCTL:
case TD_LALT:
case TD_LGUI: {
uint8_t key_toggle_hold_mods;
switch (keycode) {
case TD_LSFT:
key_toggle_hold_mods = MOD_BIT(KC_LSFT);
break;
case TD_LCTL:
key_toggle_hold_mods = MOD_BIT(KC_LCTL);
break;
case TD_LALT:
key_toggle_hold_mods = MOD_BIT(KC_LALT);
break;
case TD_LGUI:
key_toggle_hold_mods = MOD_BIT(KC_LGUI);
break;
}
/* a) Keep the brightness of the glow if all of the
* modifiers it toggle-holds are currently locked
* and make it blink. */
if ((toggle_hold_locked_mods & key_toggle_hold_mods) == key_toggle_hold_mods) {
hsv.v = BLINK_BRIGHTNESS(hsv.v);
}
/* b) Keep the brightness of the glow if all of the
* modifiers it toggle-holds are currently held. */
else if ((toggle_hold_held_mods & key_toggle_hold_mods) == key_toggle_hold_mods) {
}
/* c) Reduce the brightness of the glow if any of the
* modifiers it toggle-holds isn't currently active. */
else {
hsv.v = REDUCE_BRIGHTNESS(hsv.v);
}
break;
}
default:
break;
}
Expand Down
73 changes: 71 additions & 2 deletions keyboards/zsa/moonlander/keymaps/nebuleon/tap_dances.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ typedef struct {
uint16_t kc_hold; /* Keycode to be sent on a held key */
} tap_dance_tap_hold_t;

static_assert(GENERATE_TAP_DANCES_TAP_HOLD(LENGTH_TAP_HOLD) <= UINT8_MAX + 1, "Number of tap dances must fit within a byte");

static tap dance_state[GENERATE_TAP_DANCES_TAP_HOLD(LENGTH_TAP_HOLD)];

void tapdance_lowlatency_press(tap_dance_state_t *state, void *user_data) {
Expand Down Expand Up @@ -112,11 +110,82 @@ void clear_lock_reset(tap_dance_state_t *state, void *user_data) {
}
}

/* - - - TAP-TOGGLE MODS IMPLEMENTATION - - - */

typedef struct {
uint8_t mods; /* Modifiers to be toggled by this dance */
} tap_dance_toggle_hold_mod_t;

uint8_t toggle_hold_held_mods;
uint8_t toggle_hold_locked_mods;

void toggle_hold_mod_press(tap_dance_state_t *state, void *user_data) {
tap_dance_toggle_hold_mod_t *dance_data = (tap_dance_toggle_hold_mod_t *) user_data;
uint8_t mods = mt_to_mod_bits(pgm_read_byte(&dance_data->mods));

bool are_locked = (toggle_hold_locked_mods & mods) == mods;

toggle_hold_held_mods |= mods;
if (!are_locked) {
register_mods(mods);
}
}

void toggle_hold_mod_release(tap_dance_state_t *state, void *user_data) {
tap_dance_toggle_hold_mod_t *dance_data = (tap_dance_toggle_hold_mod_t *) user_data;
uint8_t mods = mt_to_mod_bits(pgm_read_byte(&dance_data->mods));

bool are_locked = (toggle_hold_locked_mods & mods) == mods;

toggle_hold_held_mods &= ~mods;

/* As of when a tap dance starts, the weak mods are preserved but unapplied.
* Apply them here, otherwise we will send a report with modifiers released.
*/
add_weak_mods(state->weak_mods);
#ifndef NO_ACTION_ONESHOT
add_mods(state->oneshot_mods);
#endif

if (state->interrupted || state->finished) {
/* If the toggle-mod key was either pressed along with another key or
* held for longer than the tapping term, do not toggle locking, but
* instead release the modifiers at the host if they are not locked. */
if (!are_locked) {
unregister_mods(mods);
}
} else {
/* If the toggle-mod key was tapped, toggle locking. */
if (are_locked) {
toggle_hold_locked_mods &= ~mods;
unregister_mods(mods);
} else {
toggle_hold_locked_mods |= mods;
}
}
}

#define DATA_TOGGLE_HOLD_MOD(dance_name, mods) { (mods) },

#define ACTION_TOGGLE_HOLD_MOD(dance_name, mods) \
[DN##dance_name] = { .fn = { toggle_hold_mod_press, NULL, NULL, toggle_hold_mod_release }, .user_data = (void *) &toggle_hold_mod_data[DN##dance_name - (GENERATE_TAP_DANCES_TAP_HOLD(LENGTH_TAP_HOLD))], },

#define LENGTH_TOGGLE_HOLD_MOD(dance_name, mods) + 1

const tap_dance_toggle_hold_mod_t PROGMEM toggle_hold_mod_data[] = {
GENERATE_TAP_DANCES_TOGGLE_HOLD_MOD(DATA_TOGGLE_HOLD_MOD)
};

/* - - - END TAP-TOGGLE MODS IMPLEMENTATION - - - */

static_assert(GENERATE_TAP_DANCES_TAP_HOLD(LENGTH_TAP_HOLD) + GENERATE_TAP_DANCES_TOGGLE_HOLD_MOD(LENGTH_TOGGLE_HOLD_MOD) + 1 /* the Lock dance */ <= UINT8_MAX + 1, "Number of tap dances must fit within a byte");

const tap_dance_tap_hold_t PROGMEM tap_hold_data[] = {
GENERATE_TAP_DANCES_TAP_HOLD(DATA_TAP_HOLD)
};

tap_dance_action_t tap_dance_actions[] = {
GENERATE_TAP_DANCES_TAP_HOLD(ACTION_TAP_HOLD)
GENERATE_TAP_DANCES_TOGGLE_HOLD_MOD(ACTION_TOGGLE_HOLD_MOD)
[DN_LOCK] = { .fn = { clear_lock_press, NULL, clear_lock_reset } },
};
11 changes: 11 additions & 0 deletions keyboards/zsa/moonlander/keymaps/nebuleon/tap_dances.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,29 @@
/* On L_Web */ \
GENERATE(_RFSH, KC_F5, LCTL(KC_F5)) \

#define GENERATE_TAP_DANCES_TOGGLE_HOLD_MOD(GENERATE) \
/* On L_DvorakLX, right thumb cluster */ \
GENERATE(_LCTL, MOD_LCTL) \
GENERATE(_LSFT, MOD_LSFT) \
GENERATE(_LALT, MOD_LALT) \
GENERATE(_LGUI, MOD_LGUI) \

#define DN_ENUM_TAP_DOUBLE_TAP_CUSTOM(dance_name) DN##dance_name,
#define DN_ENUM_TAP_HOLD(dance_name, kc_tap, kc_hold) DN##dance_name,
#define DN_ENUM_TOGGLE_HOLD_MOD(dance_name, mods) DN##dance_name,

enum tap_dances_dn {
GENERATE_TAP_DANCES_TAP_HOLD(DN_ENUM_TAP_HOLD)
GENERATE_TAP_DANCES_TOGGLE_HOLD_MOD(DN_ENUM_TOGGLE_HOLD_MOD)
GENERATE_TAP_DANCES_TAP_DOUBLE_TAP_CUSTOM(DN_ENUM_TAP_DOUBLE_TAP_CUSTOM)
};

#define TD_ENUM_TAP_DOUBLE_TAP_CUSTOM(dance_name) TD##dance_name = TD(DN##dance_name),
#define TD_ENUM_TAP_HOLD(dance_name, kc_tap, kc_hold) TD##dance_name = TD(DN##dance_name),
#define TD_ENUM_TOGGLE_HOLD_MOD(dance_name, mods) TD##dance_name = TD(DN##dance_name),

enum tap_dances_td {
GENERATE_TAP_DANCES_TAP_HOLD(TD_ENUM_TAP_HOLD)
GENERATE_TAP_DANCES_TOGGLE_HOLD_MOD(TD_ENUM_TOGGLE_HOLD_MOD)
GENERATE_TAP_DANCES_TAP_DOUBLE_TAP_CUSTOM(TD_ENUM_TAP_DOUBLE_TAP_CUSTOM)
};

0 comments on commit 24e8179

Please sign in to comment.