From 5f4aa29c0d33fc7b6ded42c41b05f3d700a50c27 Mon Sep 17 00:00:00 2001 From: Ilya Zhuravlev Date: Sat, 3 Jul 2021 13:30:43 -0400 Subject: [PATCH] vial/tap-dance: initial implementation --- common_features.mk | 1 + quantum/dynamic_keymap.c | 43 +++++++- quantum/dynamic_keymap.h | 4 + quantum/vial.c | 203 +++++++++++++++++++++++++++++++++++++ quantum/vial.h | 30 ++++++ tmk_core/common/keyboard.c | 6 ++ 6 files changed, 284 insertions(+), 3 deletions(-) diff --git a/common_features.mk b/common_features.mk index d3f07c6f61..7f406ed9e5 100644 --- a/common_features.mk +++ b/common_features.mk @@ -453,6 +453,7 @@ ifeq ($(strip $(VIA_ENABLE)), yes) endif ifeq ($(strip $(VIAL_ENABLE)), yes) + TAP_DANCE_ENABLE ?= yes SRC += $(QUANTUM_DIR)/vial.c EXTRAINCDIRS += $(KEYMAP_OUTPUT) OPT_DEFS += -DVIAL_ENABLE -DNO_DEBUG diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index bb4d91bf38..71d3ad7b37 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -78,7 +78,7 @@ static pin_t encoders_pad_a[] = ENCODERS_PAD_A; #define VIAL_QMK_SETTINGS_EEPROM_ADDR (VIAL_ENCODERS_EEPROM_ADDR + VIAL_ENCODERS_SIZE) -// QMK settings area is just past encoders, or dynamic keymaps if encoders aren't enabled +// QMK settings area is just past encoders #ifdef QMK_SETTINGS #include "qmk_settings.h" #define VIAL_QMK_SETTINGS_SIZE (sizeof(qmk_settings_t)) @@ -86,9 +86,17 @@ static pin_t encoders_pad_a[] = ENCODERS_PAD_A; #define VIAL_QMK_SETTINGS_SIZE 0 #endif -// Dynamic macro starts after encoders, or dynamic keymaps if encoders aren't enabled +#define VIAL_TAP_DANCE_EEPROM_ADDR (VIAL_QMK_SETTINGS_EEPROM_ADDR + VIAL_QMK_SETTINGS_SIZE) + +#ifdef VIAL_TAP_DANCE_ENABLE +#define VIAL_TAP_DANCE_SIZE (sizeof(vial_tap_dance_entry_t) * VIAL_TAP_DANCE_ENTRIES) +#else +#define VIAL_TAP_DANCE_SIZE 0 +#endif + +// Dynamic macro starts after tap-dance #ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR -# define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (VIAL_QMK_SETTINGS_EEPROM_ADDR + VIAL_QMK_SETTINGS_SIZE) +# define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (VIAL_TAP_DANCE_EEPROM_ADDR + VIAL_TAP_DANCE_SIZE) #endif // Sanity check that dynamic keymaps fit in available EEPROM @@ -185,6 +193,28 @@ void dynamic_keymap_set_qmk_settings(uint16_t offset, uint8_t value) { } #endif +#ifdef VIAL_TAP_DANCE_ENABLE +int dynamic_keymap_get_tap_dance(uint8_t index, vial_tap_dance_entry_t *entry) { + if (index >= VIAL_TAP_DANCE_ENTRIES) + return -1; + + void *address = (void*)(VIAL_TAP_DANCE_EEPROM_ADDR + index * sizeof(vial_tap_dance_entry_t)); + eeprom_read_block(entry, address, sizeof(vial_tap_dance_entry_t)); + + return 0; +} + +int dynamic_keymap_set_tap_dance(uint8_t index, const vial_tap_dance_entry_t *entry) { + if (index >= VIAL_TAP_DANCE_ENTRIES) + return -1; + + void *address = (void*)(VIAL_TAP_DANCE_EEPROM_ADDR + index * sizeof(vial_tap_dance_entry_t)); + eeprom_write_block(entry, address, sizeof(vial_tap_dance_entry_t)); + + return 0; +} +#endif + #if defined(VIAL_ENCODERS_ENABLE) && defined(VIAL_ENCODER_DEFAULT) static const uint16_t PROGMEM vial_encoder_default[] = VIAL_ENCODER_DEFAULT; _Static_assert(sizeof(vial_encoder_default)/sizeof(*vial_encoder_default) == 2 * DYNAMIC_KEYMAP_LAYER_COUNT * NUMBER_OF_ENCODERS, @@ -225,6 +255,13 @@ void dynamic_keymap_reset(void) { qmk_settings_reset(); #endif +#ifdef VIAL_TAP_DANCE_ENABLE + vial_tap_dance_entry_t td = { KC_NO, KC_NO, KC_NO, KC_NO, TAPPING_TERM }; + for (size_t i = 0; i < VIAL_TAP_DANCE_ENTRIES; ++i) { + dynamic_keymap_set_tap_dance(i, &td); + } +#endif + #ifdef VIAL_ENABLE /* re-lock the keyboard */ vial_unlocked = vial_unlocked_prev; diff --git a/quantum/dynamic_keymap.h b/quantum/dynamic_keymap.h index e7bf8b462d..7fd092caed 100644 --- a/quantum/dynamic_keymap.h +++ b/quantum/dynamic_keymap.h @@ -34,6 +34,10 @@ void dynamic_keymap_set_encoder(uint8_t layer, uint8_t idx, uint8_t dir, uint16_ uint8_t dynamic_keymap_get_qmk_settings(uint16_t offset); void dynamic_keymap_set_qmk_settings(uint16_t offset, uint8_t value); #endif +#ifdef VIAL_TAP_DANCE_ENABLE +int dynamic_keymap_get_tap_dance(uint8_t index, vial_tap_dance_entry_t *entry); +int dynamic_keymap_set_tap_dance(uint8_t index, const vial_tap_dance_entry_t *entry); +#endif void dynamic_keymap_reset(void); // These get/set the keycodes as stored in the EEPROM buffer // Data is big-endian 16-bit values (the keycodes) diff --git a/quantum/vial.c b/quantum/vial.c index 1edbe94e77..9d0f0411b8 100644 --- a/quantum/vial.c +++ b/quantum/vial.c @@ -54,6 +54,16 @@ _Static_assert(sizeof(vial_unlock_combo_rows) == sizeof(vial_unlock_combo_cols), #include "qmk_settings.h" #endif +#ifdef VIAL_TAP_DANCE_ENABLE +static void reload_tap_dance(void); +#endif + +void vial_init(void) { +#ifdef VIAL_TAP_DANCE_ENABLE + reload_tap_dance(); +#endif +} + void vial_handle_cmd(uint8_t *msg, uint8_t length) { /* All packets must be fixed 32 bytes */ if (length != VIAL_RAW_EPSIZE) @@ -185,6 +195,32 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) { qmk_settings_reset(); break; } + case vial_dynamic_entry_op: { + switch (msg[2]) { + case dynamic_vial_get_number_of_entries: { + memset(msg, 0, length); + msg[0] = VIAL_TAP_DANCE_ENTRIES; + break; + } + case dynamic_vial_tap_dance_get: { + uint8_t idx = msg[3]; + vial_tap_dance_entry_t td = { 0 }; + msg[0] = dynamic_keymap_get_tap_dance(idx, &td); + memcpy(&msg[1], &td, sizeof(td)); + break; + } + case dynamic_vial_tap_dance_set: { + uint8_t idx = msg[3]; + vial_tap_dance_entry_t td; + memcpy(&td, &msg[4], sizeof(td)); + msg[0] = dynamic_keymap_set_tap_dance(idx, &td); + reload_tap_dance(); + break; + } + } + + break; + } } } @@ -242,3 +278,170 @@ bool vial_encoder_update(uint8_t index, bool clockwise) { return true; } #endif + +#ifdef VIAL_TAP_DANCE_ENABLE +#include "process_tap_dance.h" + +/* based on ZSA configurator generated code */ + +enum { + SINGLE_TAP = 1, + SINGLE_HOLD, + DOUBLE_TAP, + DOUBLE_HOLD, + DOUBLE_SINGLE_TAP, + MORE_TAPS +}; + +static uint8_t dance_state[VIAL_TAP_DANCE_ENTRIES]; +static vial_tap_dance_entry_t td_entry; + +static uint8_t dance_step(qk_tap_dance_state_t *state) { + if (state->count == 1) { + if (state->interrupted || !state->pressed) return SINGLE_TAP; + else return SINGLE_HOLD; + } else if (state->count == 2) { + if (state->interrupted) return DOUBLE_SINGLE_TAP; + else if (state->pressed) return DOUBLE_HOLD; + else return DOUBLE_TAP; + } + return MORE_TAPS; +} + +static void on_dance(qk_tap_dance_state_t *state, void *user_data) { + uint8_t index = (uintptr_t)user_data; + if (dynamic_keymap_get_tap_dance(index, &td_entry) != 0) + return; + uint16_t kc = td_entry.on_tap; + if (kc) { + if (state->count == 3) { + tap_code16(kc); + tap_code16(kc); + tap_code16(kc); + } else if (state->count > 3) { + tap_code16(kc); + } + } +} + +static void on_dance_finished(qk_tap_dance_state_t *state, void *user_data) { + uint8_t index = (uintptr_t)user_data; + if (dynamic_keymap_get_tap_dance(index, &td_entry) != 0) + return; + dance_state[index] = dance_step(state); + switch (dance_state[index]) { + case SINGLE_TAP: { + if (td_entry.on_tap) + register_code16(td_entry.on_tap); + break; + } + case SINGLE_HOLD: { + if (td_entry.on_hold) + register_code16(td_entry.on_hold); + else if (td_entry.on_tap) + register_code16(td_entry.on_tap); + break; + } + case DOUBLE_TAP: { + if (td_entry.on_double_tap) { + register_code16(td_entry.on_double_tap); + } else if (td_entry.on_tap) { + register_code16(td_entry.on_tap); + register_code16(td_entry.on_tap); + } + break; + } + case DOUBLE_HOLD: { + if (td_entry.on_tap_hold) { + register_code16(td_entry.on_tap_hold); + } else { + if (td_entry.on_tap) { + tap_code16(td_entry.on_tap); + if (td_entry.on_hold) + register_code16(td_entry.on_hold); + else + register_code16(td_entry.on_tap); + } else if (td_entry.on_hold) { + register_code16(td_entry.on_hold); + } + } + break; + } + case DOUBLE_SINGLE_TAP: { + if (td_entry.on_tap) { + tap_code16(td_entry.on_tap); + register_code16(td_entry.on_tap); + } + break; + } + } +} + +static void on_dance_reset(qk_tap_dance_state_t *state, void *user_data) { + uint8_t index = (uintptr_t)user_data; + if (dynamic_keymap_get_tap_dance(index, &td_entry) != 0) + return; + wait_ms(10); + switch (dance_state[index]) { + case SINGLE_TAP: { + if (td_entry.on_tap) + unregister_code16(td_entry.on_tap); + break; + } + case SINGLE_HOLD: { + if (td_entry.on_hold) + unregister_code16(td_entry.on_hold); + else if (td_entry.on_tap) + unregister_code16(td_entry.on_tap); + break; + } + case DOUBLE_TAP: { + if (td_entry.on_double_tap) { + unregister_code16(td_entry.on_double_tap); + } else if (td_entry.on_tap) { + unregister_code16(td_entry.on_tap); + unregister_code16(td_entry.on_tap); + } + break; + } + case DOUBLE_HOLD: { + if (td_entry.on_tap_hold) { + unregister_code16(td_entry.on_tap_hold); + } else { + if (td_entry.on_tap) { + if (td_entry.on_hold) + unregister_code16(td_entry.on_hold); + else + unregister_code16(td_entry.on_tap); + } else if (td_entry.on_hold) { + unregister_code16(td_entry.on_hold); + } + } + break; + } + case DOUBLE_SINGLE_TAP: { + if (td_entry.on_tap) { + unregister_code16(td_entry.on_tap); + } + break; + } + } + dance_state[index] = 0; +} + +qk_tap_dance_action_t tap_dance_actions[VIAL_TAP_DANCE_ENTRIES]; + +/* Load timings from eeprom into custom_tapping_term */ +static void reload_tap_dance(void) { + for (size_t i = 0; i < VIAL_TAP_DANCE_ENTRIES; ++i) { + vial_tap_dance_entry_t td; + tap_dance_actions[i].fn.on_each_tap = on_dance; + tap_dance_actions[i].fn.on_dance_finished = on_dance_finished; + tap_dance_actions[i].fn.on_reset = on_dance_reset; + tap_dance_actions[i].user_data = (void*)i; + if (dynamic_keymap_get_tap_dance(i, &td) == 0) { + tap_dance_actions[i].custom_tapping_term = td.custom_tapping_term; + } + } +} +#endif diff --git a/quantum/vial.h b/quantum/vial.h index 6b4c73d87f..e5c24f1c0f 100644 --- a/quantum/vial.h +++ b/quantum/vial.h @@ -21,6 +21,7 @@ #define VIAL_PROTOCOL_VERSION ((uint32_t)0x00000004) +void vial_init(void); void vial_handle_cmd(uint8_t *data, uint8_t length); #ifdef VIAL_ENCODERS_ENABLE @@ -44,7 +45,36 @@ enum { vial_qmk_settings_get = 0x0A, vial_qmk_settings_set = 0x0B, vial_qmk_settings_reset = 0x0C, + vial_dynamic_entry_op = 0x0D, /* operate on tapdance, combos, etc */ +}; + +enum { + dynamic_vial_get_number_of_entries = 0x00, + dynamic_vial_tap_dance_get = 0x01, + dynamic_vial_tap_dance_set = 0x02, }; /* Fake encoder position in keyboard matrix, can't use 255 as that is immediately rejected by IS_NOEVENT */ #define VIAL_ENCODER_MATRIX_MAGIC 254 + +#ifdef TAP_DANCE_ENABLE + +#define VIAL_TAP_DANCE_ENABLE + +#ifndef VIAL_TAP_DANCE_ENTRIES +#define VIAL_TAP_DANCE_ENTRIES 16 +#endif + +typedef struct { + uint16_t on_tap; + uint16_t on_hold; + uint16_t on_double_tap; + uint16_t on_tap_hold; + uint16_t custom_tapping_term; +} vial_tap_dance_entry_t; +_Static_assert(sizeof(vial_tap_dance_entry_t) == 10, "Unexpected size of the vial_tap_dance_entry_t structure"); + +#else +#undef VIAL_TAP_DANCE_ENTRIES +#define VIAL_TAP_DANCE_ENTRIES 0 +#endif diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index ad3bd6ad55..2cb0678628 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -103,6 +103,9 @@ along with this program. If not, see . #ifdef QMK_SETTINGS # include "qmk_settings.h" #endif +#ifdef VIAL_ENABLE +# include "vial.h" +#endif static uint32_t last_input_modification_time = 0; uint32_t last_input_activity_time(void) { return last_input_modification_time; } @@ -246,6 +249,9 @@ void keyboard_setup(void) { #ifdef EEPROM_DRIVER eeprom_driver_init(); #endif +#ifdef VIAL_ENABLE + vial_init(); +#endif #ifdef QMK_SETTINGS qmk_settings_init(); #endif