From f5eb9a8e928ea50847a95f456d81584a88be305e Mon Sep 17 00:00:00 2001 From: Pascal Getreuer <50221757+getreuer@users.noreply.github.com> Date: Sun, 29 Jun 2025 14:32:04 -0700 Subject: [PATCH] Add Repeat Key and Alt Repeat Key to vial-qmk. (#906) * Repeat Key for vial-qmk. * Add new keycodes to vial_ensure_keycode.h. --- builddefs/build_vial.mk | 1 + quantum/dynamic_keymap.c | 46 ++++++-- quantum/dynamic_keymap.h | 4 + quantum/nvm/eeprom/nvm_dynamic_keymap.c | 33 +++++- quantum/nvm/nvm_dynamic_keymap.h | 5 + quantum/vial.c | 147 ++++++++++++++++++++++++ quantum/vial.h | 39 +++++++ quantum/vial_ensure_keycode.h | 3 +- 8 files changed, 265 insertions(+), 13 deletions(-) diff --git a/builddefs/build_vial.mk b/builddefs/build_vial.mk index 456bf351a6..04a4c9c72d 100644 --- a/builddefs/build_vial.mk +++ b/builddefs/build_vial.mk @@ -10,6 +10,7 @@ CAPS_WORD_ENABLE ?= yes COMBO_ENABLE ?= yes KEY_OVERRIDE_ENABLE ?= yes LAYER_LOCK_ENABLE ?= yes +REPEAT_KEY_ENABLE ?= yes SRC += $(QUANTUM_DIR)/vial.c OPT_DEFS += -DVIAL_ENABLE -DNO_DEBUG -DSERIAL_NUMBER=\"vial:f64c2b3c\" -DCAPS_WORD_INVERT_ON_SHIFT diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index 8898e6eb41..bbeeb44246 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -103,6 +103,16 @@ int dynamic_keymap_set_key_override(uint8_t index, const vial_key_override_entry } #endif +#ifdef VIAL_ALT_REPEAT_KEY_ENABLE +int dynamic_keymap_get_alt_repeat_key(uint8_t index, vial_alt_repeat_key_entry_t *entry) { + return nvm_dynamic_keymap_get_alt_repeat_key(index, entry); +} + +int dynamic_keymap_set_alt_repeat_key(uint8_t index, const vial_alt_repeat_key_entry_t *entry) { + return nvm_dynamic_keymap_set_alt_repeat_key(index, entry); +} +#endif + void dynamic_keymap_reset(void) { #ifdef VIAL_ENABLE /* temporarily unlock the keyboard so we can set hardcoded QK_BOOT keycode */ @@ -133,24 +143,38 @@ void dynamic_keymap_reset(void) { #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); + { + 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_COMBO_ENABLE - vial_combo_entry_t combo = { 0 }; - for (size_t i = 0; i < VIAL_COMBO_ENTRIES; ++i) - dynamic_keymap_set_combo(i, &combo); + { + vial_combo_entry_t combo = { 0 }; + for (size_t i = 0; i < VIAL_COMBO_ENTRIES; ++i) + dynamic_keymap_set_combo(i, &combo); + } #endif #ifdef VIAL_KEY_OVERRIDE_ENABLE - vial_key_override_entry_t ko = { 0 }; - ko.layers = ~0; - ko.options = vial_ko_option_activation_negative_mod_up | vial_ko_option_activation_required_mod_down | vial_ko_option_activation_trigger_down; - for (size_t i = 0; i < VIAL_KEY_OVERRIDE_ENTRIES; ++i) - dynamic_keymap_set_key_override(i, &ko); + { + vial_key_override_entry_t ko = { 0 }; + ko.layers = ~0; + ko.options = vial_ko_option_activation_negative_mod_up | vial_ko_option_activation_required_mod_down | vial_ko_option_activation_trigger_down; + for (size_t i = 0; i < VIAL_KEY_OVERRIDE_ENTRIES; ++i) + dynamic_keymap_set_key_override(i, &ko); + } +#endif + +#ifdef VIAL_ALT_REPEAT_KEY_ENABLE + { + vial_alt_repeat_key_entry_t arep = { 0 }; + for (size_t i = 0; i < VIAL_ALT_REPEAT_KEY_ENTRIES; ++i) + dynamic_keymap_set_alt_repeat_key(i, &arep); + } #endif #ifdef VIAL_ENABLE diff --git a/quantum/dynamic_keymap.h b/quantum/dynamic_keymap.h index 0460b92553..e1e401690b 100644 --- a/quantum/dynamic_keymap.h +++ b/quantum/dynamic_keymap.h @@ -53,6 +53,10 @@ int dynamic_keymap_set_combo(uint8_t index, const vial_combo_entry_t *entry); int dynamic_keymap_get_key_override(uint8_t index, vial_key_override_entry_t *entry); int dynamic_keymap_set_key_override(uint8_t index, const vial_key_override_entry_t *entry); #endif +#ifdef VIAL_ALT_REPEAT_KEY_ENABLE +int dynamic_keymap_get_alt_repeat_key(uint8_t index, vial_alt_repeat_key_entry_t *entry); +int dynamic_keymap_set_alt_repeat_key(uint8_t index, const vial_alt_repeat_key_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/nvm/eeprom/nvm_dynamic_keymap.c b/quantum/nvm/eeprom/nvm_dynamic_keymap.c index b185356621..8fa925e5fa 100644 --- a/quantum/nvm/eeprom/nvm_dynamic_keymap.c +++ b/quantum/nvm/eeprom/nvm_dynamic_keymap.c @@ -81,9 +81,18 @@ STATIC_ASSERT(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR <= 65535, "DYNAMIC_KEYMAP_EEPROM_MA #define VIAL_KEY_OVERRIDE_SIZE 0 #endif +// Alt Repeat Key +#define VIAL_ALT_REPEAT_KEY_EEPROM_ADDR (VIAL_KEY_OVERRIDE_EEPROM_ADDR + VIAL_KEY_OVERRIDE_SIZE) + +#ifdef VIAL_ALT_REPEAT_KEY_ENABLE +#define VIAL_ALT_REPEAT_KEY_SIZE (sizeof(vial_alt_repeat_key_entry_t) * VIAL_ALT_REPEAT_KEY_ENTRIES) +#else +#define VIAL_ALT_REPEAT_KEY_SIZE 0 +#endif + // Dynamic macro #ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR -# define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (VIAL_KEY_OVERRIDE_EEPROM_ADDR + VIAL_KEY_OVERRIDE_SIZE) +# define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (VIAL_ALT_REPEAT_KEY_EEPROM_ADDR + VIAL_ALT_REPEAT_KEY_SIZE) #endif // Sanity check that dynamic keymaps fit in available EEPROM @@ -355,3 +364,25 @@ int nvm_dynamic_keymap_set_key_override(uint8_t index, const vial_key_override_e return 0; } #endif + +#ifdef VIAL_ALT_REPEAT_KEY_ENABLE +int nvm_dynamic_keymap_get_alt_repeat_key(uint8_t index, vial_alt_repeat_key_entry_t *entry) { + if (index >= VIAL_ALT_REPEAT_KEY_ENTRIES) + return -1; + + void *address = (void*)(VIAL_ALT_REPEAT_KEY_EEPROM_ADDR + index * sizeof(vial_alt_repeat_key_entry_t)); + eeprom_read_block(entry, address, sizeof(vial_alt_repeat_key_entry_t)); + + return 0; +} + +int nvm_dynamic_keymap_set_alt_repeat_key(uint8_t index, const vial_alt_repeat_key_entry_t *entry) { + if (index >= VIAL_ALT_REPEAT_KEY_ENTRIES) + return -1; + + void *address = (void*)(VIAL_ALT_REPEAT_KEY_EEPROM_ADDR + index * sizeof(vial_alt_repeat_key_entry_t)); + eeprom_write_block(entry, address, sizeof(vial_alt_repeat_key_entry_t)); + + return 0; +} +#endif diff --git a/quantum/nvm/nvm_dynamic_keymap.h b/quantum/nvm/nvm_dynamic_keymap.h index 75ce2efcd4..079ee38ce9 100644 --- a/quantum/nvm/nvm_dynamic_keymap.h +++ b/quantum/nvm/nvm_dynamic_keymap.h @@ -45,3 +45,8 @@ int nvm_dynamic_keymap_set_combo(uint8_t index, const vial_combo_entry_t *entry) int nvm_dynamic_keymap_get_key_override(uint8_t index, vial_key_override_entry_t *entry); int nvm_dynamic_keymap_set_key_override(uint8_t index, const vial_key_override_entry_t *entry); #endif + +#ifdef VIAL_ALT_REPEAT_KEY_ENABLE +int nvm_dynamic_keymap_get_alt_repeat_key(uint8_t index, vial_alt_repeat_key_entry_t *entry); +int nvm_dynamic_keymap_set_alt_repeat_key(uint8_t index, const vial_alt_repeat_key_entry_t *entry); +#endif diff --git a/quantum/vial.c b/quantum/vial.c index c1ae54777e..42eb4c5e2a 100644 --- a/quantum/vial.c +++ b/quantum/vial.c @@ -58,6 +58,10 @@ static void reload_combo(void); static void reload_key_override(void); #endif +#ifdef VIAL_ALT_REPEAT_KEY_ENABLE +static void reload_alt_repeat_key(void); +#endif + void vial_init(void) { #ifdef VIAL_TAP_DANCE_ENABLE reload_tap_dance(); @@ -68,6 +72,9 @@ void vial_init(void) { #ifdef VIAL_KEY_OVERRIDE_ENABLE reload_key_override(); #endif +#ifdef VIAL_ALT_REPEAT_KEY_ENABLE + reload_alt_repeat_key(); +#endif } __attribute__((unused)) static uint16_t vial_keycode_firewall(uint16_t in) { @@ -224,6 +231,7 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) { msg[0] = VIAL_TAP_DANCE_ENTRIES; msg[1] = VIAL_COMBO_ENTRIES; msg[2] = VIAL_KEY_OVERRIDE_ENTRIES; + msg[3] = VIAL_ALT_REPEAT_KEY_ENTRIES; // The last byte of msg indicates optionally supported features. msg[length - 1] = (0 @@ -292,6 +300,25 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) { reload_key_override(); break; } +#endif +#ifdef VIAL_ALT_REPEAT_KEY_ENABLE + case dynamic_vial_alt_repeat_key_get: { + uint8_t idx = msg[3]; + vial_alt_repeat_key_entry_t entry = { 0 }; + msg[0] = dynamic_keymap_get_alt_repeat_key(idx, &entry); + memcpy(&msg[1], &entry, sizeof(entry)); + break; + } + case dynamic_vial_alt_repeat_key_set: { + uint8_t idx = msg[3]; + vial_alt_repeat_key_entry_t entry; + memcpy(&entry, &msg[4], sizeof(entry)); + entry.keycode = vial_keycode_firewall(entry.keycode); + entry.alt_keycode = vial_keycode_firewall(entry.alt_keycode); + msg[0] = dynamic_keymap_set_alt_repeat_key(idx, &entry); + reload_alt_repeat_key(); + break; + } #endif } @@ -628,3 +655,123 @@ const key_override_t* key_override_get(uint16_t key_override_idx) { return &vial_key_overrides[key_override_idx]; } #endif + +#ifdef VIAL_ALT_REPEAT_KEY_ENABLE +typedef struct { + uint16_t keycode; + uint16_t alt_keycode; + uint8_t required_mods; + uint8_t alt_required_mods; + uint8_t allowed_mods; + uint8_t options; +} alt_repeat_key_t; + +static alt_repeat_key_t vial_alt_repeat_key[VIAL_ALT_REPEAT_KEY_ENTRIES] = { 0 }; + +static uint8_t unpack_mods5(uint8_t mods5) { + return (mods5 & 0x10) != 0 ? (mods5 << 4) : mods5; +} + +static uint16_t alt_repeat_key_normalize_keycode(uint16_t keycode, uint8_t *mods) { + switch (keycode) { + case QK_MODS ... QK_MODS_MAX: // Unpack modifier + basic key. + *mods |= unpack_mods5(QK_MODS_GET_MODS(keycode)); + keycode = QK_MODS_GET_BASIC_KEYCODE(keycode); + break; + case QK_MOD_TAP ... QK_MOD_TAP_MAX: + keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode); + break; + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: + keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode); + break; + } + return keycode; +} + +static int vial_get_alt_repeat_key(uint8_t index, alt_repeat_key_t *out) { + vial_alt_repeat_key_entry_t entry; + int ret; + if ((ret = dynamic_keymap_get_alt_repeat_key(index, &entry)) != 0) { + return ret; + } + + memset(out, 0, sizeof(*out)); + out->keycode = alt_repeat_key_normalize_keycode(entry.keycode, &out->required_mods); + out->alt_keycode = alt_repeat_key_normalize_keycode(entry.alt_keycode, &out->alt_required_mods); + out->allowed_mods = entry.allowed_mods; + out->options = entry.options; + + return 0; +} + +static void reload_alt_repeat_key(void) { + for (size_t i = 0; i < VIAL_ALT_REPEAT_KEY_ENTRIES; ++i) { + vial_get_alt_repeat_key(i, &vial_alt_repeat_key[i]); + } +} + +uint16_t alt_repeat_key_count(void) { + return VIAL_ALT_REPEAT_KEY_ENTRIES; +} + +static bool alt_repeat_key_mods_match(uint8_t mods, uint8_t required_mods, uint8_t allowed_mods, uint8_t options) { + allowed_mods |= required_mods; // Required mods, if any, are allowed. + + // If ignoring mod handedness, bitwise-or low (lhs) 4 bits with upper (rhs) 4 bits. + if ((options & vial_arep_option_ignore_mod_handedness)) { + mods = (mods & 0xf) | (mods >> 4); + required_mods = (required_mods & 0xf) | (required_mods >> 4); + allowed_mods = (allowed_mods & 0xf) | (allowed_mods >> 4); + } + + // Check that all required mods are set and all disallowed mods are unset. + return (mods & required_mods) == required_mods && (mods & ~allowed_mods) == 0; +} + +uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { + uint16_t alt_keycode = KC_TRNS; + int8_t best_fit = -1; + + keycode = alt_repeat_key_normalize_keycode(keycode, &mods); + + for (size_t i = 0; i < VIAL_ALT_REPEAT_KEY_ENTRIES; ++i) { + const alt_repeat_key_t* entry = &vial_alt_repeat_key[i]; + const uint8_t options = entry->options; + if (!(options & vial_arep_enabled)) { // Skip disabled entries. + continue; + } + + // Search for an entry with matching keycode and mods. If there is more + // than one match, the entry with the most mods wins. + if (entry->keycode == keycode && + alt_repeat_key_mods_match(mods, entry->required_mods, entry->allowed_mods, options)) { + const int8_t fit = bitpop(entry->required_mods); + if (fit > best_fit) { + alt_keycode = (entry->alt_required_mods << 8) | entry->alt_keycode; + best_fit = fit; + } + } + + // If the entry is bidirectional, check for match with the alt keycode. + if (entry->alt_keycode == keycode && + (options & vial_arep_option_bidirectional) != 0 && + alt_repeat_key_mods_match(mods, entry->alt_required_mods, entry->allowed_mods, options)) { + const int8_t fit = bitpop(entry->alt_required_mods); + if (fit > best_fit) { + alt_keycode = (entry->required_mods << 8) | entry->keycode; + best_fit = fit; + } + } + + // If this entry is the default alt key and allowed mods are satisfied, + // use it if no there is no other match. + if ((options & vial_arep_option_default_to_this_alt_key) != 0 && + best_fit == -1 && alt_keycode == KC_TRNS && + alt_repeat_key_mods_match(mods, 0, entry->allowed_mods, options)) { + alt_keycode = (entry->alt_required_mods << 8) | entry->alt_keycode; + } + } + + return alt_keycode; +} +#endif diff --git a/quantum/vial.h b/quantum/vial.h index 0b68732ee0..9c66275d4b 100644 --- a/quantum/vial.h +++ b/quantum/vial.h @@ -58,6 +58,8 @@ enum { dynamic_vial_combo_set = 0x04, dynamic_vial_key_override_get = 0x05, dynamic_vial_key_override_set = 0x06, + dynamic_vial_alt_repeat_key_get = 0x07, + dynamic_vial_alt_repeat_key_set = 0x08, }; #define VIAL_MACRO_EXT_TAP 5 @@ -181,3 +183,40 @@ enum { #undef VIAL_KEY_OVERRIDE_ENTRIES #define VIAL_KEY_OVERRIDE_ENTRIES 0 #endif + + +#if defined(REPEAT_KEY_ENABLE) && !defined(NO_ALT_REPEAT_KEY) +#define VIAL_ALT_REPEAT_KEY_ENABLE + +#ifndef VIAL_ALT_REPEAT_KEY_ENTRIES + #if TOTAL_EEPROM_BYTE_COUNT > 4000 + #define VIAL_ALT_REPEAT_KEY_ENTRIES 32 + #elif TOTAL_EEPROM_BYTE_COUNT > 2000 + #define VIAL_ALT_REPEAT_KEY_ENTRIES 16 + #elif TOTAL_EEPROM_BYTE_COUNT > 1000 + #define VIAL_ALT_REPEAT_KEY_ENTRIES 8 + #else + #define VIAL_ALT_REPEAT_KEY_ENTRIES 4 + #endif +#endif + +/* alt repeat key struct as it is stored in eeprom and transferred to vial-gui */ +typedef struct { + uint16_t keycode; + uint16_t alt_keycode; + uint8_t allowed_mods; + uint8_t options; +} vial_alt_repeat_key_entry_t; +_Static_assert(sizeof(vial_alt_repeat_key_entry_t) == 6, "Unexpected size of the vial_alt_repeat_key_entry_t structure"); + +enum { + vial_arep_option_default_to_this_alt_key = (1 << 0), + vial_arep_option_bidirectional = (1 << 1), + vial_arep_option_ignore_mod_handedness = (1 << 2), + vial_arep_enabled = (1 << 3), +}; + +#else +#undef VIAL_ALT_REPEAT_KEY_ENTRIES +#define VIAL_ALT_REPEAT_KEY_ENTRIES 0 +#endif diff --git a/quantum/vial_ensure_keycode.h b/quantum/vial_ensure_keycode.h index 0f31036e4e..9edd478748 100644 --- a/quantum/vial_ensure_keycode.h +++ b/quantum/vial_ensure_keycode.h @@ -815,7 +815,8 @@ _Static_assert(QK_CAPS_WORD_TOGGLE == 0x7C73, ""); _Static_assert(FN_MO13 == 0x7C77, ""); _Static_assert(FN_MO23 == 0x7C78, ""); - +_Static_assert(QK_REPEAT_KEY == 0x7C79, ""); +_Static_assert(QK_ALT_REPEAT_KEY == 0x7C7A, ""); _Static_assert(QK_LAYER_LOCK == 0x7C7B, ""); /* Ensure that we have 64 USERxx keycodes */