keyboardqmk-firmwaretap-dance

Is there a method that can be called during qmk tap dance that handles advanced key codes?


I'm programming my new keyboard using qmk firmware. When writing a tap dance function that makes a key do different things when tapped, held, and double tapped, I need to call a method to send the keystrokes.

They examples I've found say to call register_code16(keycode); and then unregister_code16(keycode);. This works great for basic keycodes such as letters and numbers; even when modifier keys are included. However it doesn't work for advanced keycodes such as QK_CAPS_WORD_TOGGLE to toggle caps word, or for keycodes for switching layers.

As a workaround, I'm handling those keycodes myself along with some custom keycodes that I've created just for my layout:

// handle key down for tap dances
void eztd_reg(uint16_t keycode){
    if(!keycode) return;
    switch (keycode) {
        case QK_CAPS_WORD_TOGGLE:
            caps_word_toggle();
            break;
        case KC_RIGHT_SHIFT:
        case KC_LEFT_SHIFT:
            register_mods(MOD_MASK_SHIFT);
            break;
        case MO_CTRL_KEY_AND_LAYOUT:
            register_mods(MOD_MASK_CTRL);
            layer_on(LAY_QWERTY_CONTROL);
            break;
        case MO_ALT_KEY_AND_LAYOUT:
            register_mods(MOD_MASK_ALT);
            layer_on(LAY_QWERTY_ALT);
            break;
        case MO_META_KEY_AND_LAYOUT:
            register_mods(MOD_MASK_GUI);
            layer_on(LAY_QWERTY_META);
            break;
        case MO_NUMFN_LAYOUT:
        case TG_NUMFN_LAYOUT:
            layer_invert(LAY_NUM_FN);
            break;
        case MO_BASE_LAYER:
        case TG_BASE_LAYER:
            layer_invert(LAY_DVORAK);
            layer_invert(LAY_QWERTY);
            break;
        case QK_AUTO_SHIFT_TOGGLE:
            autoshift_toggle();
            break;
        default:
            register_code16(keycode);
    }
}

// handle key up for tap dances
void eztd_unreg(uint16_t keycode){
    if(!keycode) return;
    switch (keycode) {
        case QK_CAPS_WORD_TOGGLE:
            break;
        case KC_RIGHT_SHIFT:
        case KC_LEFT_SHIFT:
            unregister_mods(MOD_MASK_SHIFT);
            break;
        case MO_CTRL_KEY_AND_LAYOUT:
            unregister_mods(MOD_MASK_CTRL);
            layer_off(LAY_QWERTY_CONTROL);
            break;
        case MO_ALT_KEY_AND_LAYOUT:
            unregister_mods(MOD_MASK_ALT);
            layer_off(LAY_QWERTY_ALT);
            break;
        case MO_META_KEY_AND_LAYOUT:
            unregister_mods(MOD_MASK_GUI);
            layer_off(LAY_QWERTY_META);
            break;
        case MO_NUMFN_LAYOUT:
            layer_invert(LAY_NUM_FN);
            break;
        case TG_NUMFN_LAYOUT:
            break;
        case MO_BASE_LAYER:
            layer_invert(LAY_DVORAK);
            layer_invert(LAY_QWERTY);
            break;
        case TG_BASE_LAYER:
            break;
        case QK_AUTO_SHIFT_TOGGLE:
            break;
        default:
            unregister_code16(keycode);
    }
}

I realize I will need custom code for my custom keycodes, but shouldn't there be something I can call that can handle the predefined advanced codes?

Looking through the qmk code, I see that lots of them are handled in process_record_quantum(). However, I don't find any examples that call it from tap dance, and since it handles tap dancing itself, I suspect it would just cause tap dance to interrupt itself.


Solution

  • Just Basics

    register_code16(keycode); and unregister_code16(keycode); support:

    but do not support:

    Adding Layers

    Bare modifier key support and layer functionality can be achieved by calling process_action() instead of registering and unregistering codes. It is a little more difficult to call because its arguments are not just the keycode and whether or not it is registering or unregistering. You can create a key event to pass to it and convert the keycode to an action using action_for_keycode()

    bool pressed = true; // true for key down, false for key up
    keyrecord_t record = (keyrecord_t){.event = MAKE_KEYEVENT(0, 0, pressed)};
    action_t action = action_for_keycode(keycode);
    
    if (action.code != ACTION_NO) {
        process_action(&record, action);
        return;
    }
    

    Advanced keycodes

    There does not appear to be a good way to add support for all the advanced keycodes in one method call. Each module that powers advanced keycodes has a process_xxxxxxx(keycode, record) method. There are a couple dozen of them and they can be called with the same record that was created above. You could copy and paste this section from quantum.c,