keychron_qmk_firmware/users/drashna/readme_tap_dance.md
Drashna Jaelre d41961c9ed [Keymap] Drashna's Feature madness (#6128)
* Fix my Tap Dance issues after I broke them

* Cleanup and organization of userspace documentation

As well as some additional cleanup of functions due to review of documentation.

* Enable Tapdance on Glow and remove more animations

* Revert to Eager PR debouncing

* Add better check for startup animation

* Move where RGB Matrix defines are listed

* Limit RGB Matrix max val

* Update keyboard for Iris Rev 3 conflicts

* Enable encoder support on planck ez

* Remove is_master check from corne\'s OLED code

* Overhaul OLED screens for my Corne

* One last removal

* Show RGB valu On both sides

* Updates for OLED display info

* Fix compile issues for rgb config

* Disabled Space Cadet for all drashna keymaps

* Fix OLED Screen configs

* Minor OLED Tweaks

* Revert some Iris changes

* Fix song include

* Handle MAKE macro for the Corne boards better

* Add super hacky-hack for eeconfig initialization

* Add audio support for Fractal since Elite Cs support it

* Add defines for keycode steps

* Add White layout

* Update Corne RGB info

* Add fun effects to layer indication for RGB Matrix enabled boards

* Use proper define for product name detection

* Update formatting

* Use custom timeout mechanism for OLED timeout

* Fix up OLED screen HSV code for new HSV structure

* Better handle turning off RGB Matrix when sleeping

* Disable MultiSplash Animation

* Change Iris back to using serial

* Why was RGB disabled?!?!?!

* Limit val in rgb_matrix_layer_helper function

* Remove EECONFIG setting for RGB matrix
2019-07-22 20:22:33 -07:00

5.4 KiB

Diablo Tap Dances

My Tap Dance file includes the tap dance declarations, and everything needed for them.

This is used for making Diablo 3 much easier to plan, especially at high rift levels.

This works by using Tap Dances. The taps don't actually "do anything". Instead, it sets up the interval for how often to send specific keypresses. As you can tell, this makes automating things very easy.

For critics that think this is cheating, just search "diablo 3 num lock auto cast". This is just a simpler method, that doesn't require a numpad.

Custom Tap Dance Type

The real fun here is that the tap dances use a custom defined Tap Dance type:

#define ACTION_TAP_DANCE_DIABLO(index, keycode) {  \
    .fn = { NULL, (void *)diablo_tapdance_master, NULL }, \
    .user_data = (void *)&((diable_keys_t) { index, keycode }),  \
  }

This lets me set an index and keycode for the tap dance. This isn't the cool part yet, but this allows for the really cool stuff.

The Index is needed because I don't know how to handle it otherwise.

The Actual Dances

These are the custom defined dances that I'm using. It sets up everything for later, using the above custom dance type.

//Tap Dance Definitions, sets the index and the keycode.
qk_tap_dance_action_t tap_dance_actions[] = {
    // tap once to disable, and more to enable timed micros
    [TD_D3_1] = ACTION_TAP_DANCE_DIABLO(0, KC_1),
    [TD_D3_2] = ACTION_TAP_DANCE_DIABLO(1, KC_2),
    [TD_D3_3] = ACTION_TAP_DANCE_DIABLO(2, KC_3),
    [TD_D3_4] = ACTION_TAP_DANCE_DIABLO(3, KC_4),
};

Custom Data Structures

First, to get this all working, there are a couple of things that need to be set up. In a header file (or you could put it into the keymap), you need to create a couple of custom structures:

typedef struct {
    uint16_t timer;
    uint8_t key_interval;
    uint8_t keycode;
} diablo_timer_t;

typedef struct {
  uint8_t index;
  uint8_t keycode;
} diable_keys_t;

The first structure is for tracking each key that is being used. The second is to pass data from the Tap Dance action array to the actual function that we will need.

Custom Arrays

To facilitate things, you will need a couple of arrays in your c file.

//define diablo macro timer variables
diablo_timer_t diablo_timer[4];

// Set the default intervals.  Always start with 0 so that it will disable on first hit.
// Otherwise, you will need to hit a bunch of times, or hit the "clear" command
uint8_t diablo_times[] = { 0, 1, 3, 5, 10, 30 };

The first one (diablo_timer) is what keeps track of the timer used for the keys, the interval that it uses, and the actual keycode. This makes managing it a lot easier.

The second array is a list of predefined intervals, in seconds. You can add more here, or remove entries. It doesn't matter how long the array is, as this is computed automatically.

The Magic - Part 1: Master function

The first part of the magic here is the diablo_tapdance_master function. The Tap Dance feature calls this function, directly, and passes some data to the function. Namely, it passes the array of the index and the keycode (diablo_keys_t from above). This sets the keycode and the interval for the specific index of diabolo_timer based on the number of taps. If you hit it more than the number of items in the array, then it zeroes out the interval, disabling it.

// Cycle through the times for the macro, starting at 0, for disabled.
void diablo_tapdance_master(qk_tap_dance_state_t *state, void *user_data) {
    diable_keys_t *diablo_keys = (diable_keys_t *)user_data;
    // Sets the keycode based on the index
    diablo_timer[diablo_keys->index].keycode = diablo_keys->keycode;

    // if the tapdance is hit more than the number of elemints in the array, reset
    if (state->count >= (sizeof(diablo_times) / sizeof(uint8_t) ) ) {
        diablo_timer[diablo_keys->index].key_interval = 0;
        reset_tap_dance(state);
    }  else { // else set the interval (tapdance count starts at 1, array starts at 0, so offset by one)
        diablo_timer[diablo_keys->index].key_interval = diablo_times[state->count - 1];
    }
}

The Magic - Part 2: The Coup de Grace

The real core here is the run_diablo_macro_check() function. You need to call this from matrix_scan_user, as this handles the timer check.

Specifically, it runs a check for each index of the timer. It checks to see if it's enabled, and if enough time has passed. If enough time has passed, it resets the timer, and will tap the keycode that you set for that index, but only if the Diablo layer is enabled.

// Checks each of the 4 timers/keys to see if enough time has elapsed
void run_diablo_macro_check(void) {
    for (uint8_t index = 0; index < NUM_OF_DIABLO_KEYS; index++) {
        // if key_interval is 0, it's disabled, so only run if it's set.  If it's set, check the timer.
        if ( diablo_timer[index].key_interval && timer_elapsed( diablo_timer[index].timer ) > ( diablo_timer[index].key_interval * 1000 ) ) {
            // reset the timer, since enough time has passed
            diablo_timer[index].timer = timer_read();
            // send keycode ONLY if we're on the diablo layer.
            if (IS_LAYER_ON(_DIABLO)) {
                tap_code(diablo_timer[index].keycode);
            }
        }
    }
}