From c0cd1bfc6c6ee9fddf53ab31529e0a119c85b05f Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Fri, 2 Jul 2021 23:19:41 -0400
Subject: [PATCH 01/44] vial: bump protocol version

---
 quantum/vial.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/quantum/vial.h b/quantum/vial.h
index f4be5a99c3..197918765f 100644
--- a/quantum/vial.h
+++ b/quantum/vial.h
@@ -19,7 +19,7 @@
 #include <inttypes.h>
 #include <stdbool.h>
 
-#define VIAL_PROTOCOL_VERSION ((uint32_t)0x00000003)
+#define VIAL_PROTOCOL_VERSION ((uint32_t)0x00000004)
 
 void vial_handle_cmd(uint8_t *data, uint8_t length);
 

From 8dc2d0728872b13576da33af010cbc5a461051cb Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Tue, 29 Jun 2021 19:43:11 -0400
Subject: [PATCH 02/44] qmk_settings: initial prototype

---
 common_features.mk                          |  5 ++
 quantum/process_keycode/process_grave_esc.c | 17 ++---
 quantum/qmk_settings.c                      | 36 +++++++++++
 quantum/qmk_settings.h                      | 71 +++++++++++++++++++++
 quantum/vial.c                              | 19 ++++++
 quantum/vial.h                              |  3 +
 6 files changed, 143 insertions(+), 8 deletions(-)
 create mode 100644 quantum/qmk_settings.c
 create mode 100644 quantum/qmk_settings.h

diff --git a/common_features.mk b/common_features.mk
index 702e262892..6bfc91d277 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -476,6 +476,11 @@ ifeq ($(strip $(DYNAMIC_KEYMAP_ENABLE)), yes)
     SRC += $(QUANTUM_DIR)/dynamic_keymap.c
 endif
 
+ifeq ($(strip $(QMK_SETTINGS)), yes)
+    SRC += $(QUANTUM_DIR)/qmk_settings.c
+    OPT_DEFS += -DQMK_SETTINGS
+endif
+
 ifeq ($(strip $(DIP_SWITCH_ENABLE)), yes)
     OPT_DEFS += -DDIP_SWITCH_ENABLE
     SRC += $(QUANTUM_DIR)/dip_switch.c
diff --git a/quantum/process_keycode/process_grave_esc.c b/quantum/process_keycode/process_grave_esc.c
index 41c50f5cb8..05a352c80c 100644
--- a/quantum/process_keycode/process_grave_esc.c
+++ b/quantum/process_keycode/process_grave_esc.c
@@ -14,6 +14,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 #include "process_grave_esc.h"
+#include "qmk_settings.h"
 
 /* true if the last press of GRAVE_ESC was shifted (i.e. GUI or SHIFT were pressed), false otherwise.
  * Used to ensure that the correct keycode is released if the key is released.
@@ -25,35 +26,35 @@ bool process_grave_esc(uint16_t keycode, keyrecord_t *record) {
         const uint8_t mods    = get_mods();
         uint8_t       shifted = mods & MOD_MASK_SG;
 
-#ifdef GRAVE_ESC_ALT_OVERRIDE
+if (QS_grave_esc_alt_override) {
         // if ALT is pressed, ESC is always sent
         // this is handy for the cmd+opt+esc shortcut on macOS, among other things.
         if (mods & MOD_MASK_ALT) {
             shifted = 0;
         }
-#endif
+}
 
-#ifdef GRAVE_ESC_CTRL_OVERRIDE
+if (QS_grave_esc_ctrl_override) {
         // if CTRL is pressed, ESC is always sent
         // this is handy for the ctrl+shift+esc shortcut on windows, among other things.
         if (mods & MOD_MASK_CTRL) {
             shifted = 0;
         }
-#endif
+}
 
-#ifdef GRAVE_ESC_GUI_OVERRIDE
+if (QS_grave_esc_gui_override) {
         // if GUI is pressed, ESC is always sent
         if (mods & MOD_MASK_GUI) {
             shifted = 0;
         }
-#endif
+}
 
-#ifdef GRAVE_ESC_SHIFT_OVERRIDE
+if (QS_grave_esc_shift_override) {
         // if SHIFT is pressed, ESC is always sent
         if (mods & MOD_MASK_SHIFT) {
             shifted = 0;
         }
-#endif
+}
 
         if (record->event.pressed) {
             grave_esc_was_shifted = shifted;
diff --git a/quantum/qmk_settings.c b/quantum/qmk_settings.c
new file mode 100644
index 0000000000..d63a54a8c5
--- /dev/null
+++ b/quantum/qmk_settings.c
@@ -0,0 +1,36 @@
+#include "qmk_settings.h"
+
+#include <stddef.h>
+#include "progmem.h"
+#include <string.h>
+
+qmk_settings_t QS;
+
+#define DECLARE_SETTING(id, field)  { .qsid=id, .ptr=&QS.field, .sz=sizeof(QS.field) }
+
+static const qmk_settings_proto_t protos[] PROGMEM = {
+   DECLARE_SETTING(1, grave_esc_override),
+};
+
+static const qmk_settings_proto_t *find_setting(uint16_t qsid) {
+    for (size_t i = 0; i < sizeof(protos)/sizeof(*protos); ++i)
+        if (pgm_read_word(&protos[i].qsid) == qsid)
+            return &protos[i];
+    return NULL;
+}
+
+int qmk_settings_get(uint16_t qsid, void *setting, size_t maxsz) {
+    const qmk_settings_proto_t *proto = find_setting(qsid);
+    if (!proto || pgm_read_word(&proto->sz) > maxsz)
+        return -1;
+    memcpy(setting, pgm_read_ptr(&proto->ptr), pgm_read_word(&proto->sz));
+    return 0;
+}
+
+int qmk_settings_set(uint16_t qsid, const void *setting, size_t maxsz) {
+    const qmk_settings_proto_t *proto = find_setting(qsid);
+    if (!proto || pgm_read_word(&proto->sz) > maxsz)
+        return -1;
+    memcpy(pgm_read_ptr(&proto->ptr), setting, pgm_read_word(&proto->sz));
+    return 0;
+}
diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
new file mode 100644
index 0000000000..e117a3b856
--- /dev/null
+++ b/quantum/qmk_settings.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#include <inttypes.h>
+#include <stddef.h>
+
+/* take qmk config macros and set up helper variables for default settings */
+
+/* ========================================================================== */
+/* Grave escape                                                               */
+/* ========================================================================== */
+#ifdef GRAVE_ESC_ALT_OVERRIDE
+#define GRAVE_ESC_ALT_OVERRIDE_Defined 1
+#else
+#define GRAVE_ESC_ALT_OVERRIDE_Defined 0
+#endif
+
+#ifdef GRAVE_ESC_CTRL_OVERRIDE
+#define GRAVE_ESC_CTRL_OVERRIDE_Defined 1
+#else
+#define GRAVE_ESC_CTRL_OVERRIDE_Defined 0
+#endif
+
+#ifdef GRAVE_ESC_GUI_OVERRIDE
+#define GRAVE_ESC_GUI_OVERRIDE_Defined 1
+#else
+#define GRAVE_ESC_GUI_OVERRIDE_Defined 0
+#endif
+
+#ifdef GRAVE_ESC_SHIFT_OVERRIDE
+#define GRAVE_ESC_SHIFT_OVERRIDE_Defined 1
+#else
+#define GRAVE_ESC_SHIFT_OVERRIDE_Defined 0
+#endif
+
+#ifdef QMK_SETTINGS
+/* dynamic settings framework is enabled */
+
+/* actual settings - stored in RAM and backed by EEPROM */
+typedef struct {
+    uint8_t grave_esc_override;
+} qmk_settings_t;
+
+/* setting prototype - describes how to get/set settings, stored in flash */
+typedef struct {
+    uint16_t qsid;
+    uint16_t sz;
+    void *ptr;
+} qmk_settings_proto_t;
+
+int qmk_settings_get(uint16_t qsid, void *setting, size_t maxsz);
+int qmk_settings_set(uint16_t qsid, const void *setting, size_t maxsz);
+
+extern qmk_settings_t QS;
+
+/* Grave escape */
+#define QS_grave_esc_alt_override (QS.grave_esc_override & 1)
+#define QS_grave_esc_ctrl_override (QS.grave_esc_override & 2)
+#define QS_grave_esc_gui_override (QS.grave_esc_override & 4)
+#define QS_grave_esc_shift_override (QS.grave_esc_override & 8)
+
+#else
+/* dynamic settings framework is disabled => hardcode the settings and let the compiler optimize extra branches out */
+
+
+/* Grave escape */
+#define QS_grave_esc_alt_override GRAVE_ESC_ALT_OVERRIDE_Defined
+#define QS_grave_esc_ctrl_override GRAVE_ESC_CTRL_OVERRIDE_Defined
+#define QS_grave_esc_gui_override GRAVE_ESC_GUI_OVERRIDE_Defined
+#define QS_grave_esc_shift_override GRAVE_ESC_SHIFT_OVERRIDE_Defined
+
+#endif
\ No newline at end of file
diff --git a/quantum/vial.c b/quantum/vial.c
index d47fe1c549..4a23e7511d 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -50,6 +50,10 @@ _Static_assert(sizeof(vial_unlock_combo_rows) == sizeof(vial_unlock_combo_cols),
 #define VIAL_ENCODER_KEYCODE_DELAY 10
 #endif
 
+#ifdef QMK_SETTINGS
+#include "qmk_settings.h"
+#endif
+
 void vial_handle_cmd(uint8_t *msg, uint8_t length) {
     /* All packets must be fixed 32 bytes */
     if (length != VIAL_RAW_EPSIZE)
@@ -160,6 +164,21 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
 #endif
             break;
         }
+        case vial_qmk_settings_query: {
+            break;
+        }
+        case vial_qmk_settings_get: {
+            uint16_t qsid = msg[2] | (msg[3] << 8);
+            msg[0] = qmk_settings_get(qsid, &msg[1], length - 1);
+
+            break;
+        }
+        case vial_qmk_settings_set: {
+            uint16_t qsid = msg[2] | (msg[3] << 8);
+            msg[0] = qmk_settings_set(qsid, &msg[4], length - 4);
+
+            break;
+        }
     }
 }
 
diff --git a/quantum/vial.h b/quantum/vial.h
index 197918765f..d4b5f24c18 100644
--- a/quantum/vial.h
+++ b/quantum/vial.h
@@ -40,6 +40,9 @@ enum {
     vial_unlock_start = 0x06,
     vial_unlock_poll = 0x07,
     vial_lock = 0x08,
+    vial_qmk_settings_query = 0x09,
+    vial_qmk_settings_get = 0x0A,
+    vial_qmk_settings_set = 0x0B,
 };
 
 /* Fake encoder position in keyboard matrix, can't use 255 as that is immediately rejected by IS_NOEVENT  */

From b34258dbd644f13aeed00c3fa75e8d8297067f09 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Wed, 30 Jun 2021 23:42:40 -0400
Subject: [PATCH 03/44] qmk_settings: update with test settings

---
 quantum/qmk_settings.c |  7 +++++++
 quantum/qmk_settings.h | 11 ++++++++++-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/quantum/qmk_settings.c b/quantum/qmk_settings.c
index d63a54a8c5..346cd8023c 100644
--- a/quantum/qmk_settings.c
+++ b/quantum/qmk_settings.c
@@ -10,6 +10,13 @@ qmk_settings_t QS;
 
 static const qmk_settings_proto_t protos[] PROGMEM = {
    DECLARE_SETTING(1, grave_esc_override),
+   DECLARE_SETTING(2, debounce_time),
+   DECLARE_SETTING(3, auto_shift),
+   DECLARE_SETTING(4, auto_shift_timeout),
+   DECLARE_SETTING(5, osk_tap_toggle),
+   DECLARE_SETTING(6, osk_timeout),
+   DECLARE_SETTING(7, tapping_term),
+   DECLARE_SETTING(8, tap_hold),
 };
 
 static const qmk_settings_proto_t *find_setting(uint16_t qsid) {
diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index e117a3b856..86c075abc0 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -35,10 +35,19 @@
 #ifdef QMK_SETTINGS
 /* dynamic settings framework is enabled */
 
-/* actual settings - stored in RAM and backed by EEPROM */
+/* actual settings - stored in RAM and backed by EEPROM
+   these are in arbitrary order to ensure they are aligned w/o any holes, and the order can be changed at will */
 typedef struct {
     uint8_t grave_esc_override;
+    uint8_t auto_shift;
+    uint8_t osk_tap_toggle;
+    uint8_t tap_hold;
+    uint16_t debounce_time;
+    uint16_t auto_shift_timeout;
+    uint16_t osk_timeout;
+    uint16_t tapping_term;
 } qmk_settings_t;
+_Static_assert(sizeof(qmk_settings_t) == 12, "unexpected size of the qmk_settings_t structure");
 
 /* setting prototype - describes how to get/set settings, stored in flash */
 typedef struct {

From 6b0df5146fc61fb853a9a7c7a397df0ca3be24e9 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 1 Jul 2021 14:33:37 -0400
Subject: [PATCH 04/44] qmk_settings: implement setting persistence

---
 quantum/dynamic_keymap.c   | 34 ++++++++++++++++++++++++++++++-
 quantum/dynamic_keymap.h   |  4 ++++
 quantum/qmk_settings.c     | 41 +++++++++++++++++++++++++++++++++++++-
 quantum/qmk_settings.h     |  2 ++
 tmk_core/common/keyboard.c |  6 ++++++
 5 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c
index 5f2f4d5f22..bb4d91bf38 100644
--- a/quantum/dynamic_keymap.c
+++ b/quantum/dynamic_keymap.c
@@ -76,9 +76,19 @@ static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
 #define VIAL_ENCODERS_SIZE 0
 #endif
 
+#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
+#ifdef QMK_SETTINGS
+#include "qmk_settings.h"
+#define VIAL_QMK_SETTINGS_SIZE (sizeof(qmk_settings_t))
+#else
+#define VIAL_QMK_SETTINGS_SIZE 0
+#endif
+
 // Dynamic macro starts after encoders, or dynamic keymaps if encoders aren't enabled
 #ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
-#    define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (VIAL_ENCODERS_EEPROM_ADDR + VIAL_ENCODERS_SIZE)
+#    define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (VIAL_QMK_SETTINGS_EEPROM_ADDR + VIAL_QMK_SETTINGS_SIZE)
 #endif
 
 // Sanity check that dynamic keymaps fit in available EEPROM
@@ -157,6 +167,24 @@ void dynamic_keymap_set_encoder(uint8_t layer, uint8_t idx, uint8_t dir, uint16_
 }
 #endif
 
+#ifdef QMK_SETTINGS
+uint8_t dynamic_keymap_get_qmk_settings(uint16_t offset) {
+    if (offset >= VIAL_QMK_SETTINGS_SIZE)
+        return 0;
+
+    void *address = (void*)(VIAL_QMK_SETTINGS_EEPROM_ADDR + offset);
+    return eeprom_read_byte(address);
+}
+
+void dynamic_keymap_set_qmk_settings(uint16_t offset, uint8_t value) {
+    if (offset >= VIAL_QMK_SETTINGS_SIZE)
+        return;
+
+    void *address = (void*)(VIAL_QMK_SETTINGS_EEPROM_ADDR + offset);
+    eeprom_update_byte(address, value);
+}
+#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,
@@ -193,6 +221,10 @@ void dynamic_keymap_reset(void) {
 #endif
     }
 
+#ifdef QMK_SETTINGS
+    qmk_settings_reset();
+#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 a6bcdd878b..e7bf8b462d 100644
--- a/quantum/dynamic_keymap.h
+++ b/quantum/dynamic_keymap.h
@@ -30,6 +30,10 @@ void     dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column,
 uint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t idx, uint8_t dir);
 void dynamic_keymap_set_encoder(uint8_t layer, uint8_t idx, uint8_t dir, uint16_t keycode);
 #endif
+#ifdef QMK_SETTINGS
+uint8_t dynamic_keymap_get_qmk_settings(uint16_t offset);
+void dynamic_keymap_set_qmk_settings(uint16_t offset, uint8_t value);
+#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/qmk_settings.c b/quantum/qmk_settings.c
index 346cd8023c..50c4e39395 100644
--- a/quantum/qmk_settings.c
+++ b/quantum/qmk_settings.c
@@ -1,9 +1,11 @@
 #include "qmk_settings.h"
 
 #include <stddef.h>
-#include "progmem.h"
 #include <string.h>
 
+#include "progmem.h"
+#include "dynamic_keymap.h"
+
 qmk_settings_t QS;
 
 #define DECLARE_SETTING(id, field)  { .qsid=id, .ptr=&QS.field, .sz=sizeof(QS.field) }
@@ -26,6 +28,42 @@ static const qmk_settings_proto_t *find_setting(uint16_t qsid) {
     return NULL;
 }
 
+static void load_settings(void) {
+    for (size_t i = 0; i < sizeof(qmk_settings_t); ++i) {
+        uint8_t byte;
+        byte = dynamic_keymap_get_qmk_settings(i);
+        memcpy((char*)&QS + i, &byte, 1);
+    }
+}
+
+static void save_settings(void) {
+    for (size_t i = 0; i < sizeof(qmk_settings_t); ++i) {
+        uint8_t old_byte, new_byte;
+        old_byte = dynamic_keymap_get_qmk_settings(i);
+        memcpy(&new_byte, (char*)&QS + i, 1);
+        if (old_byte != new_byte)
+            dynamic_keymap_set_qmk_settings(i, new_byte);
+    }
+}
+
+void qmk_settings_init(void) {
+    load_settings();
+}
+
+void qmk_settings_reset(void) {
+    /* TODO: this should take values from various #define's */
+    QS.grave_esc_override = 0;
+    QS.debounce_time = 5;
+    QS.auto_shift = 0;
+    QS.auto_shift_timeout = 175;
+    QS.osk_tap_toggle = 0;
+    QS.osk_timeout = 5000;
+    QS.tapping_term = 200;
+    QS.tap_hold = 0;
+
+    save_settings();
+}
+
 int qmk_settings_get(uint16_t qsid, void *setting, size_t maxsz) {
     const qmk_settings_proto_t *proto = find_setting(qsid);
     if (!proto || pgm_read_word(&proto->sz) > maxsz)
@@ -39,5 +77,6 @@ int qmk_settings_set(uint16_t qsid, const void *setting, size_t maxsz) {
     if (!proto || pgm_read_word(&proto->sz) > maxsz)
         return -1;
     memcpy(pgm_read_ptr(&proto->ptr), setting, pgm_read_word(&proto->sz));
+    save_settings();
     return 0;
 }
diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index 86c075abc0..5aaed68d6b 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -56,6 +56,8 @@ typedef struct {
     void *ptr;
 } qmk_settings_proto_t;
 
+void qmk_settings_init(void);
+void qmk_settings_reset(void);
 int qmk_settings_get(uint16_t qsid, void *setting, size_t maxsz);
 int qmk_settings_set(uint16_t qsid, const void *setting, size_t maxsz);
 
diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c
index 3d6092e71c..ad3bd6ad55 100644
--- a/tmk_core/common/keyboard.c
+++ b/tmk_core/common/keyboard.c
@@ -100,6 +100,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #ifdef EEPROM_DRIVER
 #    include "eeprom_driver.h"
 #endif
+#ifdef QMK_SETTINGS
+#   include "qmk_settings.h"
+#endif
 
 static uint32_t last_input_modification_time = 0;
 uint32_t        last_input_activity_time(void) { return last_input_modification_time; }
@@ -242,6 +245,9 @@ void keyboard_setup(void) {
 #endif
 #ifdef EEPROM_DRIVER
     eeprom_driver_init();
+#endif
+#ifdef QMK_SETTINGS
+    qmk_settings_init();
 #endif
     matrix_setup();
     keyboard_pre_init_kb();

From 2affa3ac05be96b530408b2bf26d7ca53cdb133b Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 1 Jul 2021 15:00:12 -0400
Subject: [PATCH 05/44] vial: add qmk settings reset command

---
 quantum/vial.c | 4 ++++
 quantum/vial.h | 1 +
 2 files changed, 5 insertions(+)

diff --git a/quantum/vial.c b/quantum/vial.c
index 4a23e7511d..e5fa51c645 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -179,6 +179,10 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
 
             break;
         }
+        case vial_qmk_settings_reset: {
+            qmk_settings_reset();
+            break;
+        }
     }
 }
 
diff --git a/quantum/vial.h b/quantum/vial.h
index d4b5f24c18..6b4c73d87f 100644
--- a/quantum/vial.h
+++ b/quantum/vial.h
@@ -43,6 +43,7 @@ enum {
     vial_qmk_settings_query = 0x09,
     vial_qmk_settings_get = 0x0A,
     vial_qmk_settings_set = 0x0B,
+    vial_qmk_settings_reset = 0x0C,
 };
 
 /* Fake encoder position in keyboard matrix, can't use 255 as that is immediately rejected by IS_NOEVENT  */

From 2848a74f62a242955bc0dca92f22dc790b5d5df5 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 1 Jul 2021 15:10:05 -0400
Subject: [PATCH 06/44] qmk_settings: retrieve supported settings

---
 quantum/qmk_settings.c | 20 ++++++++++++++++++++
 quantum/qmk_settings.h |  1 +
 quantum/vial.c         |  2 ++
 3 files changed, 23 insertions(+)

diff --git a/quantum/qmk_settings.c b/quantum/qmk_settings.c
index 50c4e39395..1524fd048d 100644
--- a/quantum/qmk_settings.c
+++ b/quantum/qmk_settings.c
@@ -64,6 +64,26 @@ void qmk_settings_reset(void) {
     save_settings();
 }
 
+void qmk_settings_query(uint16_t qsid_gt, void *buffer, size_t sz) {
+    /* set all FFs, so caller can identify when all settings are retrieved by looking for an 0xFFFF entry */
+    memset(buffer, 0xFF, sz);
+
+    size_t buffer_offset = 0;
+    for (size_t i = 0; i < sizeof(protos)/sizeof(*protos); ++i) {
+        uint16_t qsid;
+
+        /* if output buffer has no space left, bail out */
+        if (buffer_offset + sizeof(qsid) > sz)
+            break;
+
+        qsid = pgm_read_word(&protos[i].qsid);
+        if (qsid > qsid_gt) {
+            memcpy((char*)buffer + buffer_offset, &qsid, sizeof(qsid));
+            buffer_offset += sizeof(qsid);
+        }
+    }
+}
+
 int qmk_settings_get(uint16_t qsid, void *setting, size_t maxsz) {
     const qmk_settings_proto_t *proto = find_setting(qsid);
     if (!proto || pgm_read_word(&proto->sz) > maxsz)
diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index 5aaed68d6b..5dff122793 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -58,6 +58,7 @@ typedef struct {
 
 void qmk_settings_init(void);
 void qmk_settings_reset(void);
+void qmk_settings_query(uint16_t qsid_gt, void *buffer, size_t sz);
 int qmk_settings_get(uint16_t qsid, void *setting, size_t maxsz);
 int qmk_settings_set(uint16_t qsid, const void *setting, size_t maxsz);
 
diff --git a/quantum/vial.c b/quantum/vial.c
index e5fa51c645..1edbe94e77 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -165,6 +165,8 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
             break;
         }
         case vial_qmk_settings_query: {
+            uint16_t qsid_greater_than = msg[2] | (msg[3] << 8);
+            qmk_settings_query(qsid_greater_than, msg, length);
             break;
         }
         case vial_qmk_settings_get: {

From e692dee6c7a9a1cb77b92c305ee595a7fc8500b5 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 1 Jul 2021 16:17:15 -0400
Subject: [PATCH 07/44] qmk_settings: wrap auto shift

---
 common_features.mk                           |  3 +-
 quantum/process_keycode/process_auto_shift.c | 34 +++++-----
 quantum/qmk_settings.c                       | 20 +++++-
 quantum/qmk_settings.h                       | 69 +++++++++++++++++++-
 4 files changed, 104 insertions(+), 22 deletions(-)

diff --git a/common_features.mk b/common_features.mk
index 6bfc91d277..d3f07c6f61 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -477,8 +477,9 @@ ifeq ($(strip $(DYNAMIC_KEYMAP_ENABLE)), yes)
 endif
 
 ifeq ($(strip $(QMK_SETTINGS)), yes)
+    AUTO_SHIFT_ENABLE := yes
     SRC += $(QUANTUM_DIR)/qmk_settings.c
-    OPT_DEFS += -DQMK_SETTINGS
+    OPT_DEFS += -DQMK_SETTINGS -DAUTO_SHIFT_NO_SETUP
 endif
 
 ifeq ($(strip $(DIP_SWITCH_ENABLE)), yes)
diff --git a/quantum/process_keycode/process_auto_shift.c b/quantum/process_keycode/process_auto_shift.c
index 51b0efdb47..521fc1e6e2 100644
--- a/quantum/process_keycode/process_auto_shift.c
+++ b/quantum/process_keycode/process_auto_shift.c
@@ -20,6 +20,7 @@
 #    include <stdio.h>
 
 #    include "process_auto_shift.h"
+#    include "qmk_settings.h"
 
 static uint16_t autoshift_time    = 0;
 static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT;
@@ -45,16 +46,14 @@ static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record)
         return true;
     }
 
-#    ifndef AUTO_SHIFT_MODIFIERS
+if (!QS_auto_shift_modifiers) {
     if (get_mods()) {
         return true;
     }
-#    endif
-#    ifdef AUTO_SHIFT_REPEAT
+}
+if (QS_auto_shift_repeat) {
     const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
-#        ifndef AUTO_SHIFT_NO_AUTO_REPEAT
-    if (!autoshift_flags.lastshifted) {
-#        endif
+    if (QS_auto_shift_no_auto_repeat || !autoshift_flags.lastshifted) {
         if (elapsed < TAPPING_TERM && keycode == autoshift_lastkey) {
             // Allow a tap-then-hold for keyrepeat.
             if (!autoshift_flags.lastshifted) {
@@ -66,10 +65,8 @@ static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record)
             }
             return false;
         }
-#        ifndef AUTO_SHIFT_NO_AUTO_REPEAT
     }
-#        endif
-#    endif
+}
 
     // Record the keycode so we can simulate it later.
     autoshift_lastkey           = keycode;
@@ -105,12 +102,12 @@ static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) {
             add_weak_mods(MOD_BIT(KC_LSFT));
             register_code(autoshift_lastkey);
             autoshift_flags.lastshifted = true;
-#    if defined(AUTO_SHIFT_REPEAT) && !defined(AUTO_SHIFT_NO_AUTO_REPEAT)
+if (QS_auto_shift_repeat && !QS_auto_shift_no_auto_repeat) {
             if (matrix_trigger) {
                 // Prevents release.
                 return;
             }
-#    endif
+}
         }
 
 #    if TAP_CODE_DELAY > 0
@@ -140,6 +137,8 @@ static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) {
  *  to be released.
  */
 void autoshift_matrix_scan(void) {
+    if (!QS_auto_shift_enable) return;
+
     if (autoshift_flags.in_progress) {
         const uint16_t now     = timer_read();
         const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
@@ -178,6 +177,7 @@ uint16_t get_autoshift_timeout(void) { return autoshift_timeout; }
 void set_autoshift_timeout(uint16_t timeout) { autoshift_timeout = timeout; }
 
 bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
+    if (!QS_auto_shift_enable)  return true;
     // Note that record->event.time isn't reliable, see:
     // https://github.com/qmk/qmk_firmware/pull/9826#issuecomment-733559550
     const uint16_t now = timer_read();
@@ -229,18 +229,16 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
 
 __attribute__((weak)) bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
     switch (keycode) {
-#    ifndef NO_AUTO_SHIFT_ALPHA
         case KC_A ... KC_Z:
-#    endif
-#    ifndef NO_AUTO_SHIFT_NUMERIC
+            if (!QS_auto_shift_no_auto_shift_alpha) return true;
         case KC_1 ... KC_0:
-#    endif
-#    ifndef NO_AUTO_SHIFT_SPECIAL
+            if (!QS_auto_shift_no_auto_shift_numeric) return true;
+            break;
         case KC_TAB:
         case KC_MINUS ... KC_SLASH:
         case KC_NONUS_BSLASH:
-#    endif
-            return true;
+            if (!QS_auto_shift_no_auto_shift_special) return true;
+            break;
     }
     return false;
 }
diff --git a/quantum/qmk_settings.c b/quantum/qmk_settings.c
index 1524fd048d..2c66d90c30 100644
--- a/quantum/qmk_settings.c
+++ b/quantum/qmk_settings.c
@@ -5,16 +5,22 @@
 
 #include "progmem.h"
 #include "dynamic_keymap.h"
+#include "process_auto_shift.h"
 
 qmk_settings_t QS;
 
 #define DECLARE_SETTING(id, field)  { .qsid=id, .ptr=&QS.field, .sz=sizeof(QS.field) }
+#define DECLARE_SETTING_CB(id, field, callback) { .qsid=id, .ptr=&QS.field, .sz=sizeof(QS.field), .cb=callback }
+
+static void auto_shift_timeout_apply(void) {
+    set_autoshift_timeout(QS.auto_shift_timeout);
+}
 
 static const qmk_settings_proto_t protos[] PROGMEM = {
    DECLARE_SETTING(1, grave_esc_override),
    DECLARE_SETTING(2, debounce_time),
    DECLARE_SETTING(3, auto_shift),
-   DECLARE_SETTING(4, auto_shift_timeout),
+   DECLARE_SETTING_CB(4, auto_shift_timeout, auto_shift_timeout_apply),
    DECLARE_SETTING(5, osk_tap_toggle),
    DECLARE_SETTING(6, osk_timeout),
    DECLARE_SETTING(7, tapping_term),
@@ -48,6 +54,13 @@ static void save_settings(void) {
 
 void qmk_settings_init(void) {
     load_settings();
+    /* execute all callbacks to initialize the settings */
+    for (size_t i = 0; i < sizeof(protos)/sizeof(*protos); ++i) {
+        const qmk_settings_proto_t *proto = &protos[i];
+        qmk_setting_callback_t cb = pgm_read_ptr(&proto->cb);
+        if (cb)
+            cb();
+    }
 }
 
 void qmk_settings_reset(void) {
@@ -62,6 +75,8 @@ void qmk_settings_reset(void) {
     QS.tap_hold = 0;
 
     save_settings();
+    /* to trigger all callbacks */
+    qmk_settings_init();
 }
 
 void qmk_settings_query(uint16_t qsid_gt, void *buffer, size_t sz) {
@@ -98,5 +113,8 @@ int qmk_settings_set(uint16_t qsid, const void *setting, size_t maxsz) {
         return -1;
     memcpy(pgm_read_ptr(&proto->ptr), setting, pgm_read_word(&proto->sz));
     save_settings();
+    qmk_setting_callback_t cb = pgm_read_ptr(&proto->cb);
+    if (cb)
+        cb();
     return 0;
 }
diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index 5dff122793..768fa8931b 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -32,6 +32,51 @@
 #define GRAVE_ESC_SHIFT_OVERRIDE_Defined 0
 #endif
 
+/* ========================================================================== */
+/* Auto shift                                                                 */
+/* ========================================================================== */
+#ifdef AUTO_SHIFT_ENABLE
+#define AUTO_SHIFT_ENABLE_Defined 1
+#else
+#define AUTO_SHIFT_ENABLE_Defined 0
+#endif
+
+#ifdef AUTO_SHIFT_MODIFIERS
+#define AUTO_SHIFT_MODIFIERS_Defined 1
+#else
+#define AUTO_SHIFT_MODIFIERS_Defined 0
+#endif
+
+#ifdef NO_AUTO_SHIFT_SPECIAL
+#define NO_AUTO_SHIFT_SPECIAL_Defined 1
+#else
+#define NO_AUTO_SHIFT_SPECIAL_Defined 0
+#endif
+
+#ifdef NO_AUTO_SHIFT_NUMERIC
+#define NO_AUTO_SHIFT_NUMERIC_Defined 1
+#else
+#define NO_AUTO_SHIFT_NUMERIC_Defined 0
+#endif
+
+#ifdef NO_AUTO_SHIFT_ALPHA
+#define NO_AUTO_SHIFT_ALPHA_Defined 1
+#else
+#define NO_AUTO_SHIFT_ALPHA_Defined 0
+#endif
+
+#ifdef AUTO_SHIFT_REPEAT
+#define AUTO_SHIFT_REPEAT_Defined 1
+#else
+#define AUTO_SHIFT_REPEAT_Defined 0
+#endif
+
+#ifdef AUTO_SHIFT_NO_AUTO_REPEAT
+#define AUTO_SHIFT_NO_AUTO_REPEAT_Defined 1
+#else
+#define AUTO_SHIFT_NO_AUTO_REPEAT_Defined 0
+#endif
+
 #ifdef QMK_SETTINGS
 /* dynamic settings framework is enabled */
 
@@ -49,11 +94,14 @@ typedef struct {
 } qmk_settings_t;
 _Static_assert(sizeof(qmk_settings_t) == 12, "unexpected size of the qmk_settings_t structure");
 
+typedef void (*qmk_setting_callback_t)(void);
+
 /* setting prototype - describes how to get/set settings, stored in flash */
 typedef struct {
     uint16_t qsid;
     uint16_t sz;
     void *ptr;
+    qmk_setting_callback_t cb;
 } qmk_settings_proto_t;
 
 void qmk_settings_init(void);
@@ -70,14 +118,31 @@ extern qmk_settings_t QS;
 #define QS_grave_esc_gui_override (QS.grave_esc_override & 4)
 #define QS_grave_esc_shift_override (QS.grave_esc_override & 8)
 
+/* Auto shift */
+#define QS_auto_shift_enable (QS.auto_shift & 1)
+#define QS_auto_shift_modifiers (QS.auto_shift & 2)
+#define QS_auto_shift_no_auto_shift_special (QS.auto_shift & 4)
+#define QS_auto_shift_no_auto_shift_numeric (QS.auto_shift & 8)
+#define QS_auto_shift_no_auto_shift_alpha (QS.auto_shift & 16)
+#define QS_auto_shift_repeat (QS.auto_shift & 32)
+#define QS_auto_shift_no_auto_repeat (QS.auto_shift & 64)
+
 #else
 /* dynamic settings framework is disabled => hardcode the settings and let the compiler optimize extra branches out */
 
-
 /* Grave escape */
 #define QS_grave_esc_alt_override GRAVE_ESC_ALT_OVERRIDE_Defined
 #define QS_grave_esc_ctrl_override GRAVE_ESC_CTRL_OVERRIDE_Defined
 #define QS_grave_esc_gui_override GRAVE_ESC_GUI_OVERRIDE_Defined
 #define QS_grave_esc_shift_override GRAVE_ESC_SHIFT_OVERRIDE_Defined
 
-#endif
\ No newline at end of file
+/* Auto shift */
+#define QS_auto_shift_enable AUTO_SHIFT_ENABLE_Defined
+#define QS_auto_shift_modifiers AUTO_SHIFT_MODIFIERS_Defined
+#define QS_auto_shift_no_auto_shift_special NO_AUTO_SHIFT_SPECIAL_Defined
+#define QS_auto_shift_no_auto_shift_numeric NO_AUTO_SHIFT_NUMERIC_Defined
+#define QS_auto_shift_no_auto_shift_alpha NO_AUTO_SHIFT_ALPHA_Defined
+#define QS_auto_shift_repeat AUTO_SHIFT_REPEAT_Defined
+#define QS_auto_shift_no_auto_repeat AUTO_SHIFT_NO_AUTO_REPEAT_Defined
+
+#endif

From 2f37c69ac914cc45f9465633f7ea47fa894bb2d3 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 1 Jul 2021 20:13:00 -0400
Subject: [PATCH 08/44] qmk_settings: wrap one shot keys

---
 quantum/qmk_settings.h        |  8 ++++++++
 tmk_core/common/action.c      | 25 ++++++++++++-------------
 tmk_core/common/action_util.c | 33 +++++----------------------------
 3 files changed, 25 insertions(+), 41 deletions(-)

diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index 768fa8931b..ac1521861a 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -127,6 +127,10 @@ extern qmk_settings_t QS;
 #define QS_auto_shift_repeat (QS.auto_shift & 32)
 #define QS_auto_shift_no_auto_repeat (QS.auto_shift & 64)
 
+/* One Shot Keys */
+#define QS_oneshot_tap_toggle (QS.osk_tap_toggle)
+#define QS_oneshot_timeout (QS.osk_timeout)
+
 #else
 /* dynamic settings framework is disabled => hardcode the settings and let the compiler optimize extra branches out */
 
@@ -145,4 +149,8 @@ extern qmk_settings_t QS;
 #define QS_auto_shift_repeat AUTO_SHIFT_REPEAT_Defined
 #define QS_auto_shift_no_auto_repeat AUTO_SHIFT_NO_AUTO_REPEAT_Defined
 
+/* One Shot Keys */
+#define QS_oneshot_tap_toggle ONESHOT_TAP_TOGGLE
+#define QS_oneshot_timeout ONESHOT_TIMEOUT
+
 #endif
diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c
index 9335062217..c1f3a2968a 100644
--- a/tmk_core/common/action.c
+++ b/tmk_core/common/action.c
@@ -26,6 +26,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "action_util.h"
 #include "action.h"
 #include "wait.h"
+#include "qmk_settings.h"
 
 #ifdef BACKLIGHT_ENABLE
 #    include "backlight.h"
@@ -90,7 +91,7 @@ void action_exec(keyevent_t event) {
     keyrecord_t record = {.event = event};
 
 #ifndef NO_ACTION_ONESHOT
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+if (QS_oneshot_timeout > 0) {
     if (has_oneshot_layer_timed_out()) {
         clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
     }
@@ -102,7 +103,7 @@ void action_exec(keyevent_t event) {
         clear_oneshot_swaphands();
     }
 #        endif
-#    endif
+}
 #endif
 
 #ifndef NO_ACTION_TAPPING
@@ -301,13 +302,11 @@ void process_action(keyrecord_t *record, action_t action) {
                         } else if (tap_count == 1) {
                             dprint("MODS_TAP: Oneshot: start\n");
                             set_oneshot_mods(mods | get_oneshot_mods());
-#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
-                        } else if (tap_count == ONESHOT_TAP_TOGGLE) {
+                        } else if (QS_oneshot_tap_toggle > 1 && tap_count == QS_oneshot_tap_toggle) {
                             dprint("MODS_TAP: Toggling oneshot");
                             clear_oneshot_mods();
                             set_oneshot_locked_mods(mods);
                             register_mods(mods);
-#        endif
                         } else {
                             register_mods(mods | get_oneshot_mods());
                         }
@@ -317,15 +316,15 @@ void process_action(keyrecord_t *record, action_t action) {
                             unregister_mods(mods);
                         } else if (tap_count == 1) {
                             // Retain Oneshot mods
-#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
+if (QS_oneshot_tap_toggle > 1) {
                             if (mods & get_mods()) {
                                 clear_oneshot_locked_mods();
                                 clear_oneshot_mods();
                                 unregister_mods(mods);
                             }
-                        } else if (tap_count == ONESHOT_TAP_TOGGLE) {
+}
+                        } else if (QS_oneshot_tap_toggle > 1 && tap_count == QS_oneshot_tap_toggle) {
                             // Toggle Oneshot Layer
-#        endif
                         } else {
                             clear_oneshot_mods();
                             unregister_mods(mods);
@@ -547,7 +546,7 @@ void process_action(keyrecord_t *record, action_t action) {
 #        ifndef NO_ACTION_ONESHOT
                 case OP_ONESHOT:
                     // Oneshot modifier
-#            if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
+if (QS_oneshot_tap_toggle > 1) {
                     do_release_oneshot = false;
                     if (event.pressed) {
                         del_mods(get_oneshot_locked_mods());
@@ -555,13 +554,13 @@ void process_action(keyrecord_t *record, action_t action) {
                             reset_oneshot_layer();
                             layer_off(action.layer_tap.val);
                             break;
-                        } else if (tap_count < ONESHOT_TAP_TOGGLE) {
+                        } else if (tap_count < QS_oneshot_tap_toggle) {
                             layer_on(action.layer_tap.val);
                             set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
                         }
                     } else {
                         add_mods(get_oneshot_locked_mods());
-                        if (tap_count >= ONESHOT_TAP_TOGGLE) {
+                        if (tap_count >= QS_oneshot_tap_toggle) {
                             reset_oneshot_layer();
                             clear_oneshot_locked_mods();
                             set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
@@ -569,7 +568,7 @@ void process_action(keyrecord_t *record, action_t action) {
                             clear_oneshot_layer_state(ONESHOT_PRESSED);
                         }
                     }
-#            else
+} else {
                     if (event.pressed) {
                         layer_on(action.layer_tap.val);
                         set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
@@ -579,7 +578,7 @@ void process_action(keyrecord_t *record, action_t action) {
                             clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
                         }
                     }
-#            endif
+}
                     break;
 #        endif
                 default:
diff --git a/tmk_core/common/action_util.c b/tmk_core/common/action_util.c
index a57c8bf66a..6f703c4517 100644
--- a/tmk_core/common/action_util.c
+++ b/tmk_core/common/action_util.c
@@ -21,6 +21,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "action_layer.h"
 #include "timer.h"
 #include "keycode_config.h"
+#include "qmk_settings.h"
 
 extern keymap_config_t keymap_config;
 
@@ -62,12 +63,8 @@ void clear_oneshot_locked_mods(void) {
         oneshot_locked_mods_changed_kb(oneshot_locked_mods);
     }
 }
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
 static uint16_t oneshot_time = 0;
-bool            has_oneshot_mods_timed_out(void) { return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT; }
-#    else
-bool has_oneshot_mods_timed_out(void) { return false; }
-#    endif
+bool            has_oneshot_mods_timed_out(void) { return QS_oneshot_timeout > 0 && TIMER_DIFF_16(timer_read(), oneshot_time) >= QS_oneshot_timeout; }
 #endif
 
 /* oneshot layer */
@@ -92,26 +89,22 @@ enum {
 } swap_hands_oneshot = SHO_OFF;
 #    endif
 
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
 static uint16_t oneshot_layer_time = 0;
-inline bool     has_oneshot_layer_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT && !(get_oneshot_layer_state() & ONESHOT_TOGGLED); }
+inline bool     has_oneshot_layer_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= QS_oneshot_timeout && !(get_oneshot_layer_state() & ONESHOT_TOGGLED); }
 #        ifdef SWAP_HANDS_ENABLE
 static uint16_t oneshot_swaphands_time = 0;
-inline bool     has_oneshot_swaphands_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= ONESHOT_TIMEOUT && (swap_hands_oneshot == SHO_ACTIVE); }
+inline bool     has_oneshot_swaphands_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= QS_oneshot_timeout && (swap_hands_oneshot == SHO_ACTIVE); }
 #        endif
-#    endif
 
 #    ifdef SWAP_HANDS_ENABLE
 
 void set_oneshot_swaphands(void) {
     swap_hands_oneshot = SHO_PRESSED;
     swap_hands         = true;
-#        if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
     oneshot_swaphands_time = timer_read();
     if (oneshot_layer_time != 0) {
         oneshot_layer_time = oneshot_swaphands_time;
     }
-#        endif
 }
 
 void release_oneshot_swaphands(void) {
@@ -135,9 +128,7 @@ void use_oneshot_swaphands(void) {
 void clear_oneshot_swaphands(void) {
     swap_hands_oneshot = SHO_OFF;
     swap_hands         = false;
-#        if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
     oneshot_swaphands_time = 0;
-#        endif
 }
 
 #    endif
@@ -150,9 +141,7 @@ void set_oneshot_layer(uint8_t layer, uint8_t state) {
     if (!keymap_config.oneshot_disable) {
         oneshot_layer_data = layer << 3 | state;
         layer_on(layer);
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
         oneshot_layer_time = timer_read();
-#    endif
         oneshot_layer_changed_kb(get_oneshot_layer());
     } else {
         layer_on(layer);
@@ -164,9 +153,7 @@ void set_oneshot_layer(uint8_t layer, uint8_t state) {
  */
 void reset_oneshot_layer(void) {
     oneshot_layer_data = 0;
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
     oneshot_layer_time = 0;
-#    endif
     oneshot_layer_changed_kb(get_oneshot_layer());
 }
 /** \brief Clear oneshot layer
@@ -231,12 +218,10 @@ void send_keyboard_report(void) {
     keyboard_report->mods |= macro_mods;
 #ifndef NO_ACTION_ONESHOT
     if (oneshot_mods) {
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
-        if (has_oneshot_mods_timed_out()) {
+        if (QS_oneshot_timeout > 0 && has_oneshot_mods_timed_out()) {
             dprintf("Oneshot: timeout\n");
             clear_oneshot_mods();
         }
-#    endif
         keyboard_report->mods |= oneshot_mods;
         if (has_anykey(keyboard_report)) {
             clear_oneshot_mods();
@@ -335,9 +320,7 @@ uint8_t get_oneshot_mods(void) { return oneshot_mods; }
 
 void add_oneshot_mods(uint8_t mods) {
     if ((oneshot_mods & mods) != mods) {
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
         oneshot_time = timer_read();
-#    endif
         oneshot_mods |= mods;
         oneshot_mods_changed_kb(mods);
     }
@@ -346,9 +329,7 @@ void add_oneshot_mods(uint8_t mods) {
 void del_oneshot_mods(uint8_t mods) {
     if (oneshot_mods & mods) {
         oneshot_mods &= ~mods;
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
         oneshot_time = oneshot_mods ? timer_read() : 0;
-#    endif
         oneshot_mods_changed_kb(oneshot_mods);
     }
 }
@@ -360,9 +341,7 @@ void del_oneshot_mods(uint8_t mods) {
 void set_oneshot_mods(uint8_t mods) {
     if (!keymap_config.oneshot_disable) {
         if (oneshot_mods != mods) {
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
             oneshot_time = timer_read();
-#    endif
             oneshot_mods = mods;
             oneshot_mods_changed_kb(mods);
         }
@@ -376,9 +355,7 @@ void set_oneshot_mods(uint8_t mods) {
 void clear_oneshot_mods(void) {
     if (oneshot_mods) {
         oneshot_mods = 0;
-#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
         oneshot_time = 0;
-#    endif
         oneshot_mods_changed_kb(oneshot_mods);
     }
 }

From d0747a690442204da21db511ee2cf4b891dc39e9 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 1 Jul 2021 20:43:35 -0400
Subject: [PATCH 09/44] qmk_settings: wrap mouse keys

---
 quantum/mousekey.c     | 13 +++++++------
 quantum/mousekey.h     |  2 ++
 quantum/qmk_settings.c | 31 +++++++++++++++++++++++++++++++
 quantum/qmk_settings.h | 18 +++++++++++++++++-
 4 files changed, 57 insertions(+), 7 deletions(-)

diff --git a/quantum/mousekey.c b/quantum/mousekey.c
index 7ad797042b..036e98aaf9 100644
--- a/quantum/mousekey.c
+++ b/quantum/mousekey.c
@@ -22,6 +22,7 @@
 #include "print.h"
 #include "debug.h"
 #include "mousekey.h"
+#include "qmk_settings.h"
 
 inline int8_t times_inv_sqrt2(int8_t x) {
     // 181/256 is pretty close to 1/sqrt(2)
@@ -74,17 +75,17 @@ uint8_t mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;
 static uint8_t move_unit(void) {
     uint16_t unit;
     if (mousekey_accel & (1 << 0)) {
-        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 4;
+        unit = (QS_mousekey_move_delta * mk_max_speed) / 4;
     } else if (mousekey_accel & (1 << 1)) {
-        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 2;
+        unit = (QS_mousekey_move_delta * mk_max_speed) / 2;
     } else if (mousekey_accel & (1 << 2)) {
-        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed);
+        unit = (QS_mousekey_move_delta * mk_max_speed);
     } else if (mousekey_repeat == 0) {
-        unit = MOUSEKEY_MOVE_DELTA;
+        unit = QS_mousekey_move_delta;
     } else if (mousekey_repeat >= mk_time_to_max) {
-        unit = MOUSEKEY_MOVE_DELTA * mk_max_speed;
+        unit = QS_mousekey_move_delta * mk_max_speed;
     } else {
-        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * mousekey_repeat) / mk_time_to_max;
+        unit = (QS_mousekey_move_delta * mk_max_speed * mousekey_repeat) / mk_time_to_max;
     }
     return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit));
 }
diff --git a/quantum/mousekey.h b/quantum/mousekey.h
index 70dc4bb5c5..1e56e3ca03 100644
--- a/quantum/mousekey.h
+++ b/quantum/mousekey.h
@@ -165,6 +165,8 @@ extern uint8_t mk_delay;
 extern uint8_t mk_interval;
 extern uint8_t mk_max_speed;
 extern uint8_t mk_time_to_max;
+extern uint8_t mk_wheel_delay;
+extern uint8_t mk_wheel_interval;
 extern uint8_t mk_wheel_max_speed;
 extern uint8_t mk_wheel_time_to_max;
 
diff --git a/quantum/qmk_settings.c b/quantum/qmk_settings.c
index 2c66d90c30..586fcf7759 100644
--- a/quantum/qmk_settings.c
+++ b/quantum/qmk_settings.c
@@ -6,6 +6,7 @@
 #include "progmem.h"
 #include "dynamic_keymap.h"
 #include "process_auto_shift.h"
+#include "mousekey.h"
 
 qmk_settings_t QS;
 
@@ -16,6 +17,17 @@ static void auto_shift_timeout_apply(void) {
     set_autoshift_timeout(QS.auto_shift_timeout);
 }
 
+static void mousekey_apply(void) {
+    mk_delay = QS.mousekey_delay / 10;
+    mk_interval = QS.mousekey_interval;
+    mk_max_speed = QS.mousekey_max_speed;
+    mk_time_to_max = QS.mousekey_time_to_max;
+    mk_wheel_delay = QS.mousekey_wheel_delay / 10;
+    mk_wheel_interval    = QS.mousekey_wheel_interval;
+    mk_wheel_max_speed   = QS.mousekey_wheel_max_speed;
+    mk_wheel_time_to_max = QS.mousekey_wheel_time_to_max;
+}
+
 static const qmk_settings_proto_t protos[] PROGMEM = {
    DECLARE_SETTING(1, grave_esc_override),
    DECLARE_SETTING(2, debounce_time),
@@ -25,6 +37,15 @@ static const qmk_settings_proto_t protos[] PROGMEM = {
    DECLARE_SETTING(6, osk_timeout),
    DECLARE_SETTING(7, tapping_term),
    DECLARE_SETTING(8, tap_hold),
+   DECLARE_SETTING_CB(9, mousekey_delay, mousekey_apply),
+   DECLARE_SETTING_CB(10, mousekey_interval, mousekey_apply),
+   DECLARE_SETTING_CB(11, mousekey_move_delta, mousekey_apply),
+   DECLARE_SETTING_CB(12, mousekey_max_speed, mousekey_apply),
+   DECLARE_SETTING_CB(13, mousekey_time_to_max, mousekey_apply),
+   DECLARE_SETTING_CB(14, mousekey_wheel_delay, mousekey_apply),
+   DECLARE_SETTING_CB(15, mousekey_wheel_interval, mousekey_apply),
+   DECLARE_SETTING_CB(16, mousekey_wheel_max_speed, mousekey_apply),
+   DECLARE_SETTING_CB(17, mousekey_wheel_time_to_max, mousekey_apply),
 };
 
 static const qmk_settings_proto_t *find_setting(uint16_t qsid) {
@@ -74,6 +95,16 @@ void qmk_settings_reset(void) {
     QS.tapping_term = 200;
     QS.tap_hold = 0;
 
+    QS.mousekey_delay = MOUSEKEY_DELAY;
+    QS.mousekey_interval = MOUSEKEY_INTERVAL;
+    QS.mousekey_move_delta = MOUSEKEY_MOVE_DELTA;
+    QS.mousekey_max_speed = MOUSEKEY_MAX_SPEED;
+    QS.mousekey_time_to_max = MOUSEKEY_TIME_TO_MAX;
+    QS.mousekey_wheel_delay = MOUSEKEY_WHEEL_DELAY;
+    QS.mousekey_wheel_interval = MOUSEKEY_WHEEL_INTERVAL;
+    QS.mousekey_wheel_max_speed = MOUSEKEY_WHEEL_MAX_SPEED;
+    QS.mousekey_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;
+
     save_settings();
     /* to trigger all callbacks */
     qmk_settings_init();
diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index ac1521861a..0c2105ca54 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -91,8 +91,18 @@ typedef struct {
     uint16_t auto_shift_timeout;
     uint16_t osk_timeout;
     uint16_t tapping_term;
+
+    uint16_t mousekey_delay;
+    uint16_t mousekey_interval;
+    uint16_t mousekey_move_delta;
+    uint16_t mousekey_max_speed;
+    uint16_t mousekey_time_to_max;
+    uint16_t mousekey_wheel_delay;
+    uint16_t mousekey_wheel_interval;
+    uint16_t mousekey_wheel_max_speed;
+    uint16_t mousekey_wheel_time_to_max;
 } qmk_settings_t;
-_Static_assert(sizeof(qmk_settings_t) == 12, "unexpected size of the qmk_settings_t structure");
+_Static_assert(sizeof(qmk_settings_t) == 30, "unexpected size of the qmk_settings_t structure");
 
 typedef void (*qmk_setting_callback_t)(void);
 
@@ -131,6 +141,9 @@ extern qmk_settings_t QS;
 #define QS_oneshot_tap_toggle (QS.osk_tap_toggle)
 #define QS_oneshot_timeout (QS.osk_timeout)
 
+/* Mouse keys */
+#define QS_mousekey_move_delta (QS.mousekey_move_delta)
+
 #else
 /* dynamic settings framework is disabled => hardcode the settings and let the compiler optimize extra branches out */
 
@@ -153,4 +166,7 @@ extern qmk_settings_t QS;
 #define QS_oneshot_tap_toggle ONESHOT_TAP_TOGGLE
 #define QS_oneshot_timeout ONESHOT_TIMEOUT
 
+/* Mouse keys */
+#define QS_mousekey_move_delta MOUSEKEY_MOVE_DELTA
+
 #endif

From 9ae8b1bc272c54bfcbcd675ee52615545e00f465 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 1 Jul 2021 20:47:26 -0400
Subject: [PATCH 10/44] qmk_settings: remove debounce, tap-hold

---
 quantum/qmk_settings.c |  9 +--------
 quantum/qmk_settings.h | 13 +++++--------
 2 files changed, 6 insertions(+), 16 deletions(-)

diff --git a/quantum/qmk_settings.c b/quantum/qmk_settings.c
index 586fcf7759..ea71add9cd 100644
--- a/quantum/qmk_settings.c
+++ b/quantum/qmk_settings.c
@@ -30,13 +30,10 @@ static void mousekey_apply(void) {
 
 static const qmk_settings_proto_t protos[] PROGMEM = {
    DECLARE_SETTING(1, grave_esc_override),
-   DECLARE_SETTING(2, debounce_time),
    DECLARE_SETTING(3, auto_shift),
    DECLARE_SETTING_CB(4, auto_shift_timeout, auto_shift_timeout_apply),
    DECLARE_SETTING(5, osk_tap_toggle),
    DECLARE_SETTING(6, osk_timeout),
-   DECLARE_SETTING(7, tapping_term),
-   DECLARE_SETTING(8, tap_hold),
    DECLARE_SETTING_CB(9, mousekey_delay, mousekey_apply),
    DECLARE_SETTING_CB(10, mousekey_interval, mousekey_apply),
    DECLARE_SETTING_CB(11, mousekey_move_delta, mousekey_apply),
@@ -85,15 +82,11 @@ void qmk_settings_init(void) {
 }
 
 void qmk_settings_reset(void) {
-    /* TODO: this should take values from various #define's */
     QS.grave_esc_override = 0;
-    QS.debounce_time = 5;
     QS.auto_shift = 0;
-    QS.auto_shift_timeout = 175;
+    QS.auto_shift_timeout = AUTO_SHIFT_TIMEOUT;
     QS.osk_tap_toggle = 0;
     QS.osk_timeout = 5000;
-    QS.tapping_term = 200;
-    QS.tap_hold = 0;
 
     QS.mousekey_delay = MOUSEKEY_DELAY;
     QS.mousekey_interval = MOUSEKEY_INTERVAL;
diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index 0c2105ca54..b3b0bfe0c7 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -83,15 +83,8 @@
 /* actual settings - stored in RAM and backed by EEPROM
    these are in arbitrary order to ensure they are aligned w/o any holes, and the order can be changed at will */
 typedef struct {
-    uint8_t grave_esc_override;
-    uint8_t auto_shift;
-    uint8_t osk_tap_toggle;
-    uint8_t tap_hold;
-    uint16_t debounce_time;
     uint16_t auto_shift_timeout;
     uint16_t osk_timeout;
-    uint16_t tapping_term;
-
     uint16_t mousekey_delay;
     uint16_t mousekey_interval;
     uint16_t mousekey_move_delta;
@@ -101,8 +94,12 @@ typedef struct {
     uint16_t mousekey_wheel_interval;
     uint16_t mousekey_wheel_max_speed;
     uint16_t mousekey_wheel_time_to_max;
+    uint8_t grave_esc_override;
+    uint8_t auto_shift;
+    uint8_t osk_tap_toggle;
+    uint8_t padding0;
 } qmk_settings_t;
-_Static_assert(sizeof(qmk_settings_t) == 30, "unexpected size of the qmk_settings_t structure");
+_Static_assert(sizeof(qmk_settings_t) == 26, "unexpected size of the qmk_settings_t structure");
 
 typedef void (*qmk_setting_callback_t)(void);
 

From 5f4aa29c0d33fc7b6ded42c41b05f3d700a50c27 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sat, 3 Jul 2021 13:30:43 -0400
Subject: [PATCH 11/44] 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 <http://www.gnu.org/licenses/>.
 #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

From be80d90f425c04718ed70d048d8c60528cb2c0a3 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sat, 3 Jul 2021 14:09:08 -0400
Subject: [PATCH 12/44] vial/tap-dance: adjust behavior for double tap+hold

---
 quantum/vial.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/quantum/vial.c b/quantum/vial.c
index 9d0f0411b8..3afd1fd392 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -346,7 +346,7 @@ static void on_dance_finished(qk_tap_dance_state_t *state, void *user_data) {
             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);
+                tap_code16(td_entry.on_tap);
                 register_code16(td_entry.on_tap);
             }
             break;
@@ -355,7 +355,10 @@ static void on_dance_finished(qk_tap_dance_state_t *state, void *user_data) {
             if (td_entry.on_tap_hold) {
                 register_code16(td_entry.on_tap_hold);
             } else {
-                if (td_entry.on_tap) {
+                if (td_entry.on_double_tap) {
+                    tap_code16(td_entry.on_double_tap);
+                    register_code16(td_entry.on_double_tap);
+                } else if (td_entry.on_tap) {
                     tap_code16(td_entry.on_tap);
                     if (td_entry.on_hold)
                         register_code16(td_entry.on_hold);
@@ -381,7 +384,6 @@ 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)
@@ -400,7 +402,6 @@ static void on_dance_reset(qk_tap_dance_state_t *state, void *user_data) {
                 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;
         }
@@ -408,7 +409,9 @@ static void on_dance_reset(qk_tap_dance_state_t *state, void *user_data) {
             if (td_entry.on_tap_hold) {
                 unregister_code16(td_entry.on_tap_hold);
             } else {
-                if (td_entry.on_tap) {
+                if (td_entry.on_double_tap) {
+                    unregister_code16(td_entry.on_double_tap);
+                } else if (td_entry.on_tap) {
                     if (td_entry.on_hold)
                         unregister_code16(td_entry.on_hold);
                     else

From 274d9dcf0785c7ac596fff132dc43c8531b8a2df Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sat, 3 Jul 2021 16:38:09 -0400
Subject: [PATCH 13/44] vial/tap-dance: change double hold behavior when no
 explicit entry

---
 quantum/vial.c | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/quantum/vial.c b/quantum/vial.c
index 3afd1fd392..65bfffbdd5 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -355,10 +355,7 @@ static void on_dance_finished(qk_tap_dance_state_t *state, void *user_data) {
             if (td_entry.on_tap_hold) {
                 register_code16(td_entry.on_tap_hold);
             } else {
-                if (td_entry.on_double_tap) {
-                    tap_code16(td_entry.on_double_tap);
-                    register_code16(td_entry.on_double_tap);
-                } else if (td_entry.on_tap) {
+                if (td_entry.on_tap) {
                     tap_code16(td_entry.on_tap);
                     if (td_entry.on_hold)
                         register_code16(td_entry.on_hold);
@@ -409,9 +406,7 @@ static void on_dance_reset(qk_tap_dance_state_t *state, void *user_data) {
             if (td_entry.on_tap_hold) {
                 unregister_code16(td_entry.on_tap_hold);
             } else {
-                if (td_entry.on_double_tap) {
-                    unregister_code16(td_entry.on_double_tap);
-                } else if (td_entry.on_tap) {
+                if (td_entry.on_tap) {
                     if (td_entry.on_hold)
                         unregister_code16(td_entry.on_hold);
                     else

From e0c7388e5d25c94ce038f3e473ddeb980349110d Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sat, 3 Jul 2021 19:48:01 -0400
Subject: [PATCH 14/44] vial/tap-dance: allow complex keycodes

---
 quantum/dynamic_keymap.c       |   6 +-
 quantum/vial.c                 | 116 +++++++++++++++++++--------------
 quantum/vial.h                 |   5 +-
 tmk_core/common/action_layer.c |  12 ++++
 4 files changed, 82 insertions(+), 57 deletions(-)

diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c
index 71d3ad7b37..d7658c2084 100644
--- a/quantum/dynamic_keymap.c
+++ b/quantum/dynamic_keymap.c
@@ -341,9 +341,7 @@ void dynamic_keymap_set_buffer(uint16_t offset, uint16_t size, uint8_t *data) {
     }
 }
 
-#ifdef VIAL_ENCODERS_ENABLE
 extern uint16_t g_vial_magic_keycode_override;
-#endif
 
 // This overrides the one in quantum/keymap_common.c
 uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) {
@@ -351,10 +349,8 @@ uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) {
     /* Disable any keycode processing while unlocking */
     if (vial_unlock_in_progress)
         return KC_NO;
-#endif
 
-#ifdef VIAL_ENCODERS_ENABLE
-    if (key.row == VIAL_ENCODER_MATRIX_MAGIC && key.col == VIAL_ENCODER_MATRIX_MAGIC)
+    if (key.row == VIAL_MATRIX_MAGIC && key.col == VIAL_MATRIX_MAGIC)
         return g_vial_magic_keycode_override;
 #endif
 
diff --git a/quantum/vial.c b/quantum/vial.c
index 65bfffbdd5..754832cb1c 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -224,37 +224,51 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
     }
 }
 
-#ifdef VIAL_ENCODERS_ENABLE
 uint16_t g_vial_magic_keycode_override;
 
-static void exec_keycode(uint16_t keycode) {
-#ifdef VIAL_ENCODER_SIMPLE_TAP
-    register_code16(keycode);
-#if VIAL_ENCODER_KEYCODE_DELAY > 0
-    wait_ms(VIAL_ENCODER_KEYCODE_DELAY);
-#endif
-    unregister_code16(keycode);
-#else
+static void vial_keycode_down(uint16_t keycode) {
     g_vial_magic_keycode_override = keycode;
 
-    keyrecord_t record = {.event = (keyevent_t){.key = { VIAL_ENCODER_MATRIX_MAGIC, VIAL_ENCODER_MATRIX_MAGIC }, .pressed = true, .time = (timer_read() | 1)}};
-
-    if (keycode <= QK_MODS_MAX)
+    if (keycode <= QK_MODS_MAX) {
         register_code16(keycode);
-    else
-        process_record_quantum_helper(keycode, &record);
+    } else {
+        action_exec((keyevent_t){
+            .key = (keypos_t){.row = VIAL_MATRIX_MAGIC, .col = VIAL_MATRIX_MAGIC}, .pressed = 1, .time = (timer_read() | 1) /* time should not be 0 */
+        });
+    }
+}
+
+static void vial_keycode_up(uint16_t keycode) {
+    g_vial_magic_keycode_override = keycode;
+
+    if (keycode <= QK_MODS_MAX) {
+        unregister_code16(keycode);
+    } else {
+        action_exec((keyevent_t){
+            .key = (keypos_t){.row = VIAL_MATRIX_MAGIC, .col = VIAL_MATRIX_MAGIC}, .pressed = 0, .time = (timer_read() | 1) /* time should not be 0 */
+        });
+    }
+}
+
+static void vial_keycode_tap(uint16_t keycode) {
+    vial_keycode_down(keycode);
+
+#if TAP_CODE_DELAY > 0
+    wait_ms(TAP_CODE_DELAY);
+#endif
+
+    vial_keycode_up(keycode);
+}
+
+#ifdef VIAL_ENCODERS_ENABLE
+static void exec_keycode(uint16_t keycode) {
+    vial_keycode_down(keycode);
 
 #if VIAL_ENCODER_KEYCODE_DELAY > 0
     wait_ms(VIAL_ENCODER_KEYCODE_DELAY);
 #endif
-    record.event.time = timer_read() | 1;
-    record.event.pressed = false;
 
-    if (keycode <= QK_MODS_MAX)
-        unregister_code16(keycode);
-    else
-        process_record_quantum_helper(keycode, &record);
-#endif
+    vial_keycode_up(keycode);
 }
 
 bool vial_encoder_update(uint8_t index, bool clockwise) {
@@ -315,11 +329,11 @@ static void on_dance(qk_tap_dance_state_t *state, void *user_data) {
     uint16_t kc = td_entry.on_tap;
     if (kc) {
         if (state->count == 3) {
-            tap_code16(kc);
-            tap_code16(kc);
-            tap_code16(kc);
+            vial_keycode_tap(kc);
+            vial_keycode_tap(kc);
+            vial_keycode_tap(kc);
         } else if (state->count > 3) {
-            tap_code16(kc);
+            vial_keycode_tap(kc);
         }
     }
 }
@@ -332,45 +346,45 @@ static void on_dance_finished(qk_tap_dance_state_t *state, void *user_data) {
     switch (dance_state[index]) {
         case SINGLE_TAP: {
             if (td_entry.on_tap)
-                register_code16(td_entry.on_tap);
+                vial_keycode_down(td_entry.on_tap);
             break;
         }
         case SINGLE_HOLD: {
             if (td_entry.on_hold)
-                register_code16(td_entry.on_hold);
+                vial_keycode_down(td_entry.on_hold);
             else if (td_entry.on_tap)
-                register_code16(td_entry.on_tap);
+                vial_keycode_down(td_entry.on_tap);
             break;
         }
         case DOUBLE_TAP: {
             if (td_entry.on_double_tap) {
-                register_code16(td_entry.on_double_tap);
+                vial_keycode_down(td_entry.on_double_tap);
             } else if (td_entry.on_tap) {
-                tap_code16(td_entry.on_tap);
-                register_code16(td_entry.on_tap);
+                vial_keycode_tap(td_entry.on_tap);
+                vial_keycode_down(td_entry.on_tap);
             }
             break;
         }
         case DOUBLE_HOLD: {
             if (td_entry.on_tap_hold) {
-                register_code16(td_entry.on_tap_hold);
+                vial_keycode_down(td_entry.on_tap_hold);
             } else {
                 if (td_entry.on_tap) {
-                    tap_code16(td_entry.on_tap);
+                    vial_keycode_tap(td_entry.on_tap);
                     if (td_entry.on_hold)
-                        register_code16(td_entry.on_hold);
+                        vial_keycode_down(td_entry.on_hold);
                     else
-                        register_code16(td_entry.on_tap);
+                        vial_keycode_down(td_entry.on_tap);
                 } else if (td_entry.on_hold) {
-                    register_code16(td_entry.on_hold);
+                    vial_keycode_down(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);
+                vial_keycode_tap(td_entry.on_tap);
+                vial_keycode_down(td_entry.on_tap);
             }
             break;
         }
@@ -381,50 +395,52 @@ 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;
-    switch (dance_state[index]) {
+    uint8_t st = dance_state[index];
+    state->count = 0;
+    dance_state[index] = 0;
+    switch (st) {
         case SINGLE_TAP: {
             if (td_entry.on_tap)
-                unregister_code16(td_entry.on_tap);
+                vial_keycode_up(td_entry.on_tap);
             break;
         }
         case SINGLE_HOLD: {
             if (td_entry.on_hold)
-                unregister_code16(td_entry.on_hold);
+                vial_keycode_up(td_entry.on_hold);
             else if (td_entry.on_tap)
-                unregister_code16(td_entry.on_tap);
+                vial_keycode_up(td_entry.on_tap);
             break;
         }
         case DOUBLE_TAP: {
             if (td_entry.on_double_tap) {
-                unregister_code16(td_entry.on_double_tap);
+                vial_keycode_up(td_entry.on_double_tap);
             } else if (td_entry.on_tap) {
-                unregister_code16(td_entry.on_tap);
+                vial_keycode_up(td_entry.on_tap);
             }
             break;
         }
         case DOUBLE_HOLD: {
             if (td_entry.on_tap_hold) {
-                unregister_code16(td_entry.on_tap_hold);
+                vial_keycode_up(td_entry.on_tap_hold);
             } else {
                 if (td_entry.on_tap) {
                     if (td_entry.on_hold)
-                        unregister_code16(td_entry.on_hold);
+                        vial_keycode_up(td_entry.on_hold);
                     else
-                        unregister_code16(td_entry.on_tap);
+                        vial_keycode_up(td_entry.on_tap);
                 } else if (td_entry.on_hold) {
-                    unregister_code16(td_entry.on_hold);
+                    vial_keycode_up(td_entry.on_hold);
                 }
             }
             break;
         }
         case DOUBLE_SINGLE_TAP: {
             if (td_entry.on_tap) {
-                unregister_code16(td_entry.on_tap);
+                vial_keycode_up(td_entry.on_tap);
             }
             break;
         }
     }
-    dance_state[index] = 0;
 }
 
 qk_tap_dance_action_t tap_dance_actions[VIAL_TAP_DANCE_ENTRIES];
diff --git a/quantum/vial.h b/quantum/vial.h
index e5c24f1c0f..84ae0476b8 100644
--- a/quantum/vial.h
+++ b/quantum/vial.h
@@ -54,8 +54,9 @@ enum {
     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
+/* Fake position in keyboard matrix, can't use 255 as that is immediately rejected by IS_NOEVENT
+   used to send arbitrary keycodes thru process_record_quantum_helper */
+#define VIAL_MATRIX_MAGIC 254
 
 #ifdef TAP_DANCE_ENABLE
 
diff --git a/tmk_core/common/action_layer.c b/tmk_core/common/action_layer.c
index af2d7d964b..3b2bf3096f 100644
--- a/tmk_core/common/action_layer.c
+++ b/tmk_core/common/action_layer.c
@@ -10,6 +10,10 @@
 #    include "nodebug.h"
 #endif
 
+#ifdef VIAL_ENABLE
+#include "vial.h"
+#endif
+
 /** \brief Default Layer State
  */
 layer_state_t default_layer_state = 0;
@@ -192,6 +196,10 @@ uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS]
  * Updates the cached keys when changing layers
  */
 void update_source_layers_cache(keypos_t key, uint8_t layer) {
+#ifdef VIAL_ENABLE
+    if (key.row == VIAL_MATRIX_MAGIC) return;
+#endif
+
     const uint8_t key_number  = key.col + (key.row * MATRIX_COLS);
     const uint8_t storage_row = key_number / 8;
     const uint8_t storage_bit = key_number % 8;
@@ -206,6 +214,10 @@ void update_source_layers_cache(keypos_t key, uint8_t layer) {
  * reads the cached keys stored when the layer was changed
  */
 uint8_t read_source_layers_cache(keypos_t key) {
+#ifdef VIAL_ENABLE
+    if (key.row == VIAL_MATRIX_MAGIC) return 0;
+#endif
+
     const uint8_t key_number  = key.col + (key.row * MATRIX_COLS);
     const uint8_t storage_row = key_number / 8;
     const uint8_t storage_bit = key_number % 8;

From 8ccef55b3ef657f4dd41246bff9528bdc83e15e4 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sat, 3 Jul 2021 23:12:12 -0400
Subject: [PATCH 15/44] vial/combo: prototype

---
 common_features.mk                      |  1 +
 quantum/process_keycode/process_combo.c |  4 ++++
 quantum/process_keycode/process_combo.h |  4 ++++
 quantum/vial.c                          | 15 +++++++++++++++
 quantum/vial.h                          | 16 ++++++++++++++++
 5 files changed, 40 insertions(+)

diff --git a/common_features.mk b/common_features.mk
index 7f406ed9e5..3e0c6e9983 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -454,6 +454,7 @@ endif
 
 ifeq ($(strip $(VIAL_ENABLE)), yes)
     TAP_DANCE_ENABLE ?= yes
+    COMBO_ENABLE ?= yes
     SRC += $(QUANTUM_DIR)/vial.c
     EXTRAINCDIRS += $(KEYMAP_OUTPUT)
     OPT_DEFS += -DVIAL_ENABLE -DNO_DEBUG
diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
index f38d7d47a0..d2f52edd2e 100644
--- a/quantum/process_keycode/process_combo.c
+++ b/quantum/process_keycode/process_combo.c
@@ -126,6 +126,10 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
     drop_buffer                = false;
     bool no_combo_keys_pressed = true;
 
+    if (keycode == KC_NO) {
+        return true;
+    }
+
     if (keycode == CMB_ON && record->event.pressed) {
         combo_enable();
         return true;
diff --git a/quantum/process_keycode/process_combo.h b/quantum/process_keycode/process_combo.h
index e51a2f1f4e..50a5de24c1 100644
--- a/quantum/process_keycode/process_combo.h
+++ b/quantum/process_keycode/process_combo.h
@@ -16,6 +16,10 @@
 
 #pragma once
 
+#ifdef VIAL_ENABLE
+#include "vial.h"
+#endif
+
 #include "progmem.h"
 #include "quantum.h"
 #include <stdint.h>
diff --git a/quantum/vial.c b/quantum/vial.c
index 754832cb1c..9619fbb967 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -200,6 +200,7 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
             case dynamic_vial_get_number_of_entries: {
                 memset(msg, 0, length);
                 msg[0] = VIAL_TAP_DANCE_ENTRIES;
+                msg[1] = VIAL_COMBO_ENTRIES;
                 break;
             }
             case dynamic_vial_tap_dance_get: {
@@ -459,3 +460,17 @@ static void reload_tap_dance(void) {
     }
 }
 #endif
+
+#ifdef VIAL_COMBO_ENABLE
+const uint16_t PROGMEM test_combo[] = {KC_X, KC_Z, COMBO_END};
+combo_t key_combos[COMBO_COUNT] = {COMBO_ACTION(test_combo)};
+
+void process_combo_event(uint16_t combo_index, bool pressed) {
+    uprintf("combo event %d\n", combo_index);
+    if (pressed)
+        vial_keycode_down(0x5F12);
+    else
+        vial_keycode_up(0x5F12);
+}
+
+#endif
diff --git a/quantum/vial.h b/quantum/vial.h
index 84ae0476b8..968f878ef3 100644
--- a/quantum/vial.h
+++ b/quantum/vial.h
@@ -79,3 +79,19 @@ _Static_assert(sizeof(vial_tap_dance_entry_t) == 10, "Unexpected size of the via
 #undef VIAL_TAP_DANCE_ENTRIES
 #define VIAL_TAP_DANCE_ENTRIES 0
 #endif
+
+#ifdef COMBO_ENABLE
+#define VIAL_COMBO_ENABLE
+
+#ifndef VIAL_COMBO_ENTRIES
+#define VIAL_COMBO_ENTRIES 16
+#endif
+
+/* also to catch wrong include order in e.g. process_combo.h */
+#ifdef COMBO_COUNT
+#error COMBO_COUNT redefined - define VIAL_COMBO_ENTRIES instead
+#endif
+
+#define COMBO_COUNT VIAL_COMBO_ENTRIES
+
+#endif

From 26a9cb574968fe347bad3e2f678a47dc58281cb4 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 4 Jul 2021 10:11:04 -0400
Subject: [PATCH 16/44] vial/combo: implement dynamic combos

---
 quantum/dynamic_keymap.c                | 44 +++++++++++++++++++++++--
 quantum/dynamic_keymap.h                |  4 +++
 quantum/process_keycode/process_combo.c | 19 +++++++++++
 quantum/vial.c                          | 39 +++++++++++++++++++---
 quantum/vial.h                          |  8 +++++
 5 files changed, 106 insertions(+), 8 deletions(-)

diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c
index d7658c2084..2fb37cf634 100644
--- a/quantum/dynamic_keymap.c
+++ b/quantum/dynamic_keymap.c
@@ -86,6 +86,7 @@ static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
 #define VIAL_QMK_SETTINGS_SIZE 0
 #endif
 
+// Tap-dance
 #define VIAL_TAP_DANCE_EEPROM_ADDR (VIAL_QMK_SETTINGS_EEPROM_ADDR + VIAL_QMK_SETTINGS_SIZE)
 
 #ifdef VIAL_TAP_DANCE_ENABLE
@@ -94,9 +95,18 @@ static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
 #define VIAL_TAP_DANCE_SIZE 0
 #endif
 
-// Dynamic macro starts after tap-dance
+// Combos
+#define VIAL_COMBO_EEPROM_ADDR (VIAL_TAP_DANCE_EEPROM_ADDR + VIAL_TAP_DANCE_SIZE)
+
+#ifdef VIAL_COMBO_ENABLE
+#define VIAL_COMBO_SIZE (sizeof(vial_combo_entry_t) * VIAL_COMBO_ENTRIES)
+#else
+#define VIAL_COMBO_SIZE 0
+#endif
+
+// Dynamic macro
 #ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
-#    define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (VIAL_TAP_DANCE_EEPROM_ADDR + VIAL_TAP_DANCE_SIZE)
+#    define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (VIAL_COMBO_EEPROM_ADDR + VIAL_COMBO_SIZE)
 #endif
 
 // Sanity check that dynamic keymaps fit in available EEPROM
@@ -104,7 +114,7 @@ static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
 // The keyboard should override DYNAMIC_KEYMAP_LAYER_COUNT to reduce it,
 // or DYNAMIC_KEYMAP_EEPROM_MAX_ADDR to increase it, *only if* the microcontroller has
 // more than the default.
-_Static_assert(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR - DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR >= 100, "Dynamic keymaps are configured to use more EEPROM than is available.");
+_Static_assert(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + 100, "Dynamic keymaps are configured to use more EEPROM than is available.");
 
 // Dynamic macros are stored after the keymaps and use what is available
 // up to and including DYNAMIC_KEYMAP_EEPROM_MAX_ADDR.
@@ -215,6 +225,28 @@ int dynamic_keymap_set_tap_dance(uint8_t index, const vial_tap_dance_entry_t *en
 }
 #endif
 
+#ifdef VIAL_COMBO_ENABLE
+int dynamic_keymap_get_combo(uint8_t index, vial_combo_entry_t *entry) {
+    if (index >= VIAL_COMBO_ENTRIES)
+        return -1;
+
+    void *address = (void*)(VIAL_COMBO_EEPROM_ADDR + index * sizeof(vial_combo_entry_t));
+    eeprom_read_block(entry, address, sizeof(vial_combo_entry_t));
+
+    return 0;
+}
+
+int dynamic_keymap_set_combo(uint8_t index, const vial_combo_entry_t *entry) {
+    if (index >= VIAL_COMBO_ENTRIES)
+        return -1;
+
+    void *address = (void*)(VIAL_COMBO_EEPROM_ADDR + index * sizeof(vial_combo_entry_t));
+    eeprom_write_block(entry, address, sizeof(vial_combo_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,
@@ -262,6 +294,12 @@ void dynamic_keymap_reset(void) {
     }
 #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);
+#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 7fd092caed..5114cbe46a 100644
--- a/quantum/dynamic_keymap.h
+++ b/quantum/dynamic_keymap.h
@@ -38,6 +38,10 @@ void dynamic_keymap_set_qmk_settings(uint16_t offset, uint8_t value);
 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
+#ifdef VIAL_COMBO_ENABLE
+int dynamic_keymap_get_combo(uint8_t index, vial_combo_entry_t *entry);
+int dynamic_keymap_set_combo(uint8_t index, const vial_combo_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/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
index d2f52edd2e..b844b565b4 100644
--- a/quantum/process_keycode/process_combo.c
+++ b/quantum/process_keycode/process_combo.c
@@ -17,6 +17,10 @@
 #include "print.h"
 #include "process_combo.h"
 
+#ifdef VIAL_COMBO_ENABLE
+#include "dynamic_keymap.h"
+#endif
+
 #ifndef COMBO_VARIABLE_LEN
 __attribute__((weak)) combo_t key_combos[COMBO_COUNT] = {};
 #else
@@ -84,12 +88,27 @@ static inline void dump_key_buffer(bool emit) {
 static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record) {
     uint8_t  count = 0;
     uint16_t index = -1;
+#ifdef VIAL_COMBO_ENABLE
+    uint8_t combo_idx = (uintptr_t)combo->keys;
+    vial_combo_entry_t entry;
+    if (dynamic_keymap_get_combo(combo_idx, &entry) != 0)
+        return false;
+    for (count = 0; count < sizeof(entry.input)/sizeof(*entry.input); ++count) {
+        uint16_t key = entry.input[count];
+        if (key == KC_NO) break;
+        if (key == keycode) index = count;
+    }
+    /* must have at least 2 keys in the combo */
+    if (count < 2)
+        return false;
+#else
     /* Find index of keycode and number of combo keys */
     for (const uint16_t *keys = combo->keys;; ++count) {
         uint16_t key = pgm_read_word(&keys[count]);
         if (keycode == key) index = count;
         if (COMBO_END == key) break;
     }
+#endif
 
     /* Continue processing if not a combo key */
     if (-1 == (int8_t)index) return false;
diff --git a/quantum/vial.c b/quantum/vial.c
index 9619fbb967..601008bd4f 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -58,10 +58,17 @@ _Static_assert(sizeof(vial_unlock_combo_rows) == sizeof(vial_unlock_combo_cols),
 static void reload_tap_dance(void);
 #endif
 
+#ifdef VIAL_COMBO_ENABLE
+static void init_combo(void);
+#endif
+
 void vial_init(void) {
 #ifdef VIAL_TAP_DANCE_ENABLE
     reload_tap_dance();
 #endif
+#ifdef VIAL_COMBO_ENABLE
+    init_combo();
+#endif
 }
 
 void vial_handle_cmd(uint8_t *msg, uint8_t length) {
@@ -218,6 +225,20 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
                 reload_tap_dance();
                 break;
             }
+            case dynamic_vial_combo_get: {
+                uint8_t idx = msg[3];
+                vial_combo_entry_t entry = { 0 };
+                msg[0] = dynamic_keymap_get_combo(idx, &entry);
+                memcpy(&msg[1], &entry, sizeof(entry));
+                break;
+            }
+            case dynamic_vial_combo_set: {
+                uint8_t idx = msg[3];
+                vial_combo_entry_t entry;
+                memcpy(&entry, &msg[4], sizeof(entry));
+                msg[0] = dynamic_keymap_set_combo(idx, &entry);
+                break;
+            }
             }
 
             break;
@@ -462,15 +483,23 @@ static void reload_tap_dance(void) {
 #endif
 
 #ifdef VIAL_COMBO_ENABLE
-const uint16_t PROGMEM test_combo[] = {KC_X, KC_Z, COMBO_END};
-combo_t key_combos[COMBO_COUNT] = {COMBO_ACTION(test_combo)};
+combo_t key_combos[VIAL_COMBO_ENTRIES];
+
+static void init_combo(void) {
+    for (size_t i = 0; i < VIAL_COMBO_ENTRIES; ++i) {
+        key_combos[i].keys = (void*)(uintptr_t)i;
+    }
+}
 
 void process_combo_event(uint16_t combo_index, bool pressed) {
-    uprintf("combo event %d\n", combo_index);
+    vial_combo_entry_t entry;
+    if (dynamic_keymap_get_combo(combo_index, &entry) != 0)
+        return;
+
     if (pressed)
-        vial_keycode_down(0x5F12);
+        vial_keycode_down(entry.output);
     else
-        vial_keycode_up(0x5F12);
+        vial_keycode_up(entry.output);
 }
 
 #endif
diff --git a/quantum/vial.h b/quantum/vial.h
index 968f878ef3..dc17bcd93e 100644
--- a/quantum/vial.h
+++ b/quantum/vial.h
@@ -52,6 +52,8 @@ enum {
     dynamic_vial_get_number_of_entries = 0x00,
     dynamic_vial_tap_dance_get = 0x01,
     dynamic_vial_tap_dance_set = 0x02,
+    dynamic_vial_combo_get = 0x03,
+    dynamic_vial_combo_set = 0x04,
 };
 
 /* Fake position in keyboard matrix, can't use 255 as that is immediately rejected by IS_NOEVENT
@@ -87,6 +89,12 @@ _Static_assert(sizeof(vial_tap_dance_entry_t) == 10, "Unexpected size of the via
 #define VIAL_COMBO_ENTRIES 16
 #endif
 
+typedef struct {
+    uint16_t input[4];
+    uint16_t output;
+} vial_combo_entry_t;
+_Static_assert(sizeof(vial_combo_entry_t) == 10, "Unexpected size of the vial_combo_entry_t structure");
+
 /* also to catch wrong include order in e.g. process_combo.h */
 #ifdef COMBO_COUNT
 #error COMBO_COUNT redefined - define VIAL_COMBO_ENTRIES instead

From 99772b39c40acf3f74819f37f60878d0ab645e21 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 4 Jul 2021 10:23:58 -0400
Subject: [PATCH 17/44] vial/combo: remove the KC_NO workaround, no longer
 needed

---
 quantum/process_keycode/process_combo.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
index b844b565b4..373c49260a 100644
--- a/quantum/process_keycode/process_combo.c
+++ b/quantum/process_keycode/process_combo.c
@@ -145,10 +145,6 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
     drop_buffer                = false;
     bool no_combo_keys_pressed = true;
 
-    if (keycode == KC_NO) {
-        return true;
-    }
-
     if (keycode == CMB_ON && record->event.pressed) {
         combo_enable();
         return true;

From c9492cef89ce425bf1471bf44f77271044589b19 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 4 Jul 2021 15:07:31 -0400
Subject: [PATCH 18/44] fix combo handling for interrupted combos

---
 quantum/process_keycode/process_combo.c | 58 ++++++++++++++++---------
 quantum/process_keycode/process_combo.h |  9 ++--
 2 files changed, 44 insertions(+), 23 deletions(-)

diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
index 373c49260a..77122eada8 100644
--- a/quantum/process_keycode/process_combo.c
+++ b/quantum/process_keycode/process_combo.c
@@ -33,7 +33,6 @@ __attribute__((weak)) void process_combo_event(uint16_t combo_index, bool presse
 static uint16_t timer               = 0;
 static uint16_t current_combo_index = 0;
 static bool     drop_buffer         = false;
-static bool     is_active           = false;
 static bool     b_combo_enable      = true;  // defaults to enabled
 
 static uint8_t buffer_size = 0;
@@ -75,7 +74,19 @@ static inline void dump_key_buffer(bool emit) {
     buffer_size = 0;
 }
 
-#define ALL_COMBO_KEYS_ARE_DOWN (((1 << count) - 1) == combo->state)
+static void end_incomplete_combos(void) {
+#ifndef COMBO_VARIABLE_LEN
+    for (current_combo_index = 0; current_combo_index < COMBO_COUNT; ++current_combo_index) {
+#else
+    for (current_combo_index = 0; current_combo_index < COMBO_LEN; ++current_combo_index) {
+#endif
+        combo_t *combo = &key_combos[current_combo_index];
+        if (!(combo->state & COMBO_COMPLETE))
+            combo->state = 0;
+    }
+}
+
+#define ALL_COMBO_KEYS_ARE_DOWN (((1 << count) - 1) == (combo->state & ~COMBO_COMPLETE))
 #define KEY_STATE_DOWN(key)         \
     do {                            \
         combo->state |= (1 << key); \
@@ -84,6 +95,7 @@ static inline void dump_key_buffer(bool emit) {
     do {                             \
         combo->state &= ~(1 << key); \
     } while (0)
+#define KEY_STATE(key) (combo->state & (1 << (key)))
 
 static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record) {
     uint8_t  count = 0;
@@ -113,13 +125,15 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
     /* Continue processing if not a combo key */
     if (-1 == (int8_t)index) return false;
 
-    bool is_combo_active = is_active;
+    bool is_combo_active = true;
 
     if (record->event.pressed) {
         KEY_STATE_DOWN(index);
 
         if (is_combo_active) {
             if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was pressed */
+                dump_key_buffer(false);
+                combo->state |= COMBO_COMPLETE;
                 send_combo(combo->keycode, true);
                 drop_buffer = true;
             }
@@ -127,22 +141,31 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
     } else {
         if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */
             send_combo(combo->keycode, false);
-        } else {
-            /* continue processing without immediately returning */
-            is_combo_active = false;
         }
 
-        KEY_STATE_UP(index);
+        /* don't consume the key unless it is a part of a completed combo */
+        is_combo_active = false;
+        if (KEY_STATE(index)) {
+            KEY_STATE_UP(index);
+
+            if (combo->state & COMBO_COMPLETE) {
+                /* Consume the key on release, only if the combo was complete */
+                is_combo_active = true;
+
+                /* if all keys are up, reset the combo */
+                if (combo->state == COMBO_COMPLETE)
+                    combo->state = 0;
+            }
+        }
     }
 
     return is_combo_active;
 }
 
-#define NO_COMBO_KEYS_ARE_DOWN (0 == combo->state)
+#define NO_COMBO_KEYS_ARE_DOWN (0 == (combo->state & ~COMBO_COMPLETE))
 
 bool process_combo(uint16_t keycode, keyrecord_t *record) {
     bool is_combo_key          = false;
-    drop_buffer                = false;
     bool no_combo_keys_pressed = true;
 
     if (keycode == CMB_ON && record->event.pressed) {
@@ -177,17 +200,12 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
         /* buffer is only dropped when we complete a combo, so we refresh the timer
          * here */
         timer = timer_read();
-        dump_key_buffer(false);
+        drop_buffer = false;
     } else if (!is_combo_key) {
         /* if no combos claim the key we need to emit the keybuffer */
         dump_key_buffer(true);
-
-        // reset state if there are no combo keys pressed at all
-        if (no_combo_keys_pressed) {
-            timer     = 0;
-            is_active = true;
-        }
-    } else if (record->event.pressed && is_active) {
+        end_incomplete_combos();
+    } else if (record->event.pressed) {
         /* otherwise the key is consumed and placed in the buffer */
         timer = timer_read();
 
@@ -204,19 +222,19 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
 }
 
 void matrix_scan_combo(void) {
-    if (b_combo_enable && is_active && timer && timer_elapsed(timer) > COMBO_TERM) {
+    if (b_combo_enable && timer && timer_elapsed(timer) > COMBO_TERM) {
         /* This disables the combo, meaning key events for this
          * combo will be handled by the next processors in the chain
          */
-        is_active = false;
         dump_key_buffer(true);
+        end_incomplete_combos();
     }
 }
 
 void combo_enable(void) { b_combo_enable = true; }
 
 void combo_disable(void) {
-    b_combo_enable = is_active = false;
+    b_combo_enable = false;
     timer                      = 0;
     dump_key_buffer(true);
 }
diff --git a/quantum/process_keycode/process_combo.h b/quantum/process_keycode/process_combo.h
index 50a5de24c1..c3203dd0c8 100644
--- a/quantum/process_keycode/process_combo.h
+++ b/quantum/process_keycode/process_combo.h
@@ -25,11 +25,14 @@
 #include <stdint.h>
 
 #ifdef EXTRA_EXTRA_LONG_COMBOS
-#    define MAX_COMBO_LENGTH 32
+#    define MAX_COMBO_LENGTH 31
+#    define COMBO_COMPLETE 0x80000000u
 #elif EXTRA_LONG_COMBOS
-#    define MAX_COMBO_LENGTH 16
+#    define MAX_COMBO_LENGTH 15
+#    define COMBO_COMPLETE 0x8000u
 #else
-#    define MAX_COMBO_LENGTH 8
+#    define MAX_COMBO_LENGTH 7
+#    define COMBO_COMPLETE 0x80u
 #endif
 
 typedef struct {

From 7d23ffe1bc1f1ed63b7fff1a9576694af62eee91 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 4 Jul 2021 19:04:46 -0400
Subject: [PATCH 19/44] qmk_settings: wrap COMBO_TERM

---
 quantum/process_keycode/process_combo.c | 3 ++-
 quantum/qmk_settings.c                  | 4 ++++
 quantum/qmk_settings.h                  | 9 ++++++++-
 3 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
index 77122eada8..c210eb3c6f 100644
--- a/quantum/process_keycode/process_combo.c
+++ b/quantum/process_keycode/process_combo.c
@@ -16,6 +16,7 @@
 
 #include "print.h"
 #include "process_combo.h"
+#include "qmk_settings.h"
 
 #ifdef VIAL_COMBO_ENABLE
 #include "dynamic_keymap.h"
@@ -222,7 +223,7 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
 }
 
 void matrix_scan_combo(void) {
-    if (b_combo_enable && timer && timer_elapsed(timer) > COMBO_TERM) {
+    if (b_combo_enable && timer && timer_elapsed(timer) > QS_combo_term) {
         /* This disables the combo, meaning key events for this
          * combo will be handled by the next processors in the chain
          */
diff --git a/quantum/qmk_settings.c b/quantum/qmk_settings.c
index ea71add9cd..933a3b78f9 100644
--- a/quantum/qmk_settings.c
+++ b/quantum/qmk_settings.c
@@ -7,6 +7,7 @@
 #include "dynamic_keymap.h"
 #include "process_auto_shift.h"
 #include "mousekey.h"
+#include "process_combo.h"
 
 qmk_settings_t QS;
 
@@ -30,6 +31,7 @@ static void mousekey_apply(void) {
 
 static const qmk_settings_proto_t protos[] PROGMEM = {
    DECLARE_SETTING(1, grave_esc_override),
+   DECLARE_SETTING(2, combo_term),
    DECLARE_SETTING(3, auto_shift),
    DECLARE_SETTING_CB(4, auto_shift_timeout, auto_shift_timeout_apply),
    DECLARE_SETTING(5, osk_tap_toggle),
@@ -98,6 +100,8 @@ void qmk_settings_reset(void) {
     QS.mousekey_wheel_max_speed = MOUSEKEY_WHEEL_MAX_SPEED;
     QS.mousekey_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;
 
+    QS.combo_term = COMBO_TERM;
+
     save_settings();
     /* to trigger all callbacks */
     qmk_settings_init();
diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index b3b0bfe0c7..52b4d76ac6 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -94,12 +94,13 @@ typedef struct {
     uint16_t mousekey_wheel_interval;
     uint16_t mousekey_wheel_max_speed;
     uint16_t mousekey_wheel_time_to_max;
+    uint16_t combo_term;
     uint8_t grave_esc_override;
     uint8_t auto_shift;
     uint8_t osk_tap_toggle;
     uint8_t padding0;
 } qmk_settings_t;
-_Static_assert(sizeof(qmk_settings_t) == 26, "unexpected size of the qmk_settings_t structure");
+_Static_assert(sizeof(qmk_settings_t) == 28, "unexpected size of the qmk_settings_t structure");
 
 typedef void (*qmk_setting_callback_t)(void);
 
@@ -141,6 +142,9 @@ extern qmk_settings_t QS;
 /* Mouse keys */
 #define QS_mousekey_move_delta (QS.mousekey_move_delta)
 
+/* Combo */
+#define QS_combo_term (QS.combo_term)
+
 #else
 /* dynamic settings framework is disabled => hardcode the settings and let the compiler optimize extra branches out */
 
@@ -166,4 +170,7 @@ extern qmk_settings_t QS;
 /* Mouse keys */
 #define QS_mousekey_move_delta MOUSEKEY_MOVE_DELTA
 
+/* Combo */
+#define QS_combo_term COMBO_TERM
+
 #endif

From ab47d18274d4a0a25225eaf9777e9cfd25a03cf6 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 4 Jul 2021 23:15:06 -0400
Subject: [PATCH 20/44] qmk_settings: fix compile errors when disabled

---
 quantum/qmk_settings.h | 11 +++++++++++
 quantum/vial.c         |  6 ++++++
 2 files changed, 17 insertions(+)

diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index 52b4d76ac6..1dfcca50a4 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -77,6 +77,17 @@
 #define AUTO_SHIFT_NO_AUTO_REPEAT_Defined 0
 #endif
 
+/* ========================================================================== */
+/* One shot keys                                                              */
+/* ========================================================================== */
+#ifndef ONESHOT_TAP_TOGGLE
+#define ONESHOT_TAP_TOGGLE 5
+#endif
+
+#ifndef ONESHOT_TIMEOUT
+#define ONESHOT_TIMEOUT 5000
+#endif
+
 #ifdef QMK_SETTINGS
 /* dynamic settings framework is enabled */
 
diff --git a/quantum/vial.c b/quantum/vial.c
index 601008bd4f..6b67f7762f 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -182,10 +182,15 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
             break;
         }
         case vial_qmk_settings_query: {
+#ifdef QMK_SETTINGS
             uint16_t qsid_greater_than = msg[2] | (msg[3] << 8);
             qmk_settings_query(qsid_greater_than, msg, length);
+#else
+            memset(msg, 0xFF, length); /* indicate that we don't support any qsid */
+#endif
             break;
         }
+#ifdef QMK_SETTINGS
         case vial_qmk_settings_get: {
             uint16_t qsid = msg[2] | (msg[3] << 8);
             msg[0] = qmk_settings_get(qsid, &msg[1], length - 1);
@@ -202,6 +207,7 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
             qmk_settings_reset();
             break;
         }
+#endif
         case vial_dynamic_entry_op: {
             switch (msg[2]) {
             case dynamic_vial_get_number_of_entries: {

From 01b92fcb45ad83a4483abf5b5b1746432a09a949 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 4 Jul 2021 23:15:16 -0400
Subject: [PATCH 21/44] qmk_settings: enable by default

---
 common_features.mk | 1 +
 1 file changed, 1 insertion(+)

diff --git a/common_features.mk b/common_features.mk
index 3e0c6e9983..17321c37df 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -453,6 +453,7 @@ ifeq ($(strip $(VIA_ENABLE)), yes)
 endif
 
 ifeq ($(strip $(VIAL_ENABLE)), yes)
+    QMK_SETTINGS ?= yes
     TAP_DANCE_ENABLE ?= yes
     COMBO_ENABLE ?= yes
     SRC += $(QUANTUM_DIR)/vial.c

From 4a95d01faa7f1249eccc6296555c3495f54de62b Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Mon, 5 Jul 2021 00:32:04 -0400
Subject: [PATCH 22/44] qmk_settings: pick up oneshot defaults from macros

---
 quantum/qmk_settings.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/quantum/qmk_settings.c b/quantum/qmk_settings.c
index 933a3b78f9..a258a05822 100644
--- a/quantum/qmk_settings.c
+++ b/quantum/qmk_settings.c
@@ -87,8 +87,8 @@ void qmk_settings_reset(void) {
     QS.grave_esc_override = 0;
     QS.auto_shift = 0;
     QS.auto_shift_timeout = AUTO_SHIFT_TIMEOUT;
-    QS.osk_tap_toggle = 0;
-    QS.osk_timeout = 5000;
+    QS.osk_tap_toggle = ONESHOT_TAP_TOGGLE;
+    QS.osk_timeout = ONESHOT_TIMEOUT;
 
     QS.mousekey_delay = MOUSEKEY_DELAY;
     QS.mousekey_interval = MOUSEKEY_INTERVAL;

From 6a48f2be282c9883208a1c4e7778894a82f168a3 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 8 Jul 2021 18:12:04 -0400
Subject: [PATCH 23/44] vial: fix build with features disabled

---
 quantum/vial.c | 6 ++++++
 quantum/vial.h | 2 ++
 2 files changed, 8 insertions(+)

diff --git a/quantum/vial.c b/quantum/vial.c
index 6b67f7762f..c7afe3fa07 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -216,6 +216,7 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
                 msg[1] = VIAL_COMBO_ENTRIES;
                 break;
             }
+#ifdef VIAL_TAP_DANCE_ENABLE
             case dynamic_vial_tap_dance_get: {
                 uint8_t idx = msg[3];
                 vial_tap_dance_entry_t td = { 0 };
@@ -231,6 +232,8 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
                 reload_tap_dance();
                 break;
             }
+#endif
+#ifdef VIAL_COMBO_ENABLE
             case dynamic_vial_combo_get: {
                 uint8_t idx = msg[3];
                 vial_combo_entry_t entry = { 0 };
@@ -245,6 +248,7 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
                 msg[0] = dynamic_keymap_set_combo(idx, &entry);
                 break;
             }
+#endif
             }
 
             break;
@@ -278,6 +282,8 @@ static void vial_keycode_up(uint16_t keycode) {
     }
 }
 
+static void vial_keycode_tap(uint16_t keycode) __attribute__((unused));
+
 static void vial_keycode_tap(uint16_t keycode) {
     vial_keycode_down(keycode);
 
diff --git a/quantum/vial.h b/quantum/vial.h
index dc17bcd93e..9a910dd8a9 100644
--- a/quantum/vial.h
+++ b/quantum/vial.h
@@ -102,4 +102,6 @@ _Static_assert(sizeof(vial_combo_entry_t) == 10, "Unexpected size of the vial_co
 
 #define COMBO_COUNT VIAL_COMBO_ENTRIES
 
+#else
+#define VIAL_COMBO_ENTRIES 0
 #endif

From a28433681946abaaca2c83878fed0f03a2aba07d Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 8 Jul 2021 18:12:13 -0400
Subject: [PATCH 24/44] qmk_settings: wrap tap/hold settings

---
 common_features.mk     |  2 +-
 quantum/qmk_settings.c | 25 +++++++++++++++++++++++++
 quantum/qmk_settings.h |  5 +++--
 3 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/common_features.mk b/common_features.mk
index 17321c37df..fe349018e6 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -482,7 +482,7 @@ endif
 ifeq ($(strip $(QMK_SETTINGS)), yes)
     AUTO_SHIFT_ENABLE := yes
     SRC += $(QUANTUM_DIR)/qmk_settings.c
-    OPT_DEFS += -DQMK_SETTINGS -DAUTO_SHIFT_NO_SETUP
+    OPT_DEFS += -DQMK_SETTINGS -DAUTO_SHIFT_NO_SETUP -DTAPPING_TERM_PER_KEY -DPERMISSIVE_HOLD_PER_KEY -DIGNORE_MOD_TAP_INTERRUPT_PER_KEY -DTAPPING_FORCE_HOLD_PER_KEY -DRETRO_TAPPING_PER_KEY
 endif
 
 ifeq ($(strip $(DIP_SWITCH_ENABLE)), yes)
diff --git a/quantum/qmk_settings.c b/quantum/qmk_settings.c
index a258a05822..6199e8420c 100644
--- a/quantum/qmk_settings.c
+++ b/quantum/qmk_settings.c
@@ -8,6 +8,7 @@
 #include "process_auto_shift.h"
 #include "mousekey.h"
 #include "process_combo.h"
+#include "action_tapping.h"
 
 qmk_settings_t QS;
 
@@ -36,6 +37,8 @@ static const qmk_settings_proto_t protos[] PROGMEM = {
    DECLARE_SETTING_CB(4, auto_shift_timeout, auto_shift_timeout_apply),
    DECLARE_SETTING(5, osk_tap_toggle),
    DECLARE_SETTING(6, osk_timeout),
+   DECLARE_SETTING(7, tapping_term),
+   DECLARE_SETTING(8, tapping),
    DECLARE_SETTING_CB(9, mousekey_delay, mousekey_apply),
    DECLARE_SETTING_CB(10, mousekey_interval, mousekey_apply),
    DECLARE_SETTING_CB(11, mousekey_move_delta, mousekey_apply),
@@ -101,6 +104,8 @@ void qmk_settings_reset(void) {
     QS.mousekey_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;
 
     QS.combo_term = COMBO_TERM;
+    QS.tapping_term = TAPPING_TERM;
+    QS.tapping = 0;
 
     save_settings();
     /* to trigger all callbacks */
@@ -146,3 +151,23 @@ int qmk_settings_set(uint16_t qsid, const void *setting, size_t maxsz) {
         cb();
     return 0;
 }
+
+uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) {
+    return QS.tapping_term;
+}
+
+bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) {
+    return QS.tapping & 1;
+}
+
+bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) {
+    return QS.tapping & 2;
+}
+
+bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record) {
+    return QS.tapping & 4;
+}
+
+bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) {
+    return QS.tapping & 8;
+}
diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index 1dfcca50a4..8b24827f9f 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -106,12 +106,13 @@ typedef struct {
     uint16_t mousekey_wheel_max_speed;
     uint16_t mousekey_wheel_time_to_max;
     uint16_t combo_term;
+    uint16_t tapping_term;
     uint8_t grave_esc_override;
     uint8_t auto_shift;
     uint8_t osk_tap_toggle;
-    uint8_t padding0;
+    uint8_t tapping;
 } qmk_settings_t;
-_Static_assert(sizeof(qmk_settings_t) == 28, "unexpected size of the qmk_settings_t structure");
+_Static_assert(sizeof(qmk_settings_t) == 30, "unexpected size of the qmk_settings_t structure");
 
 typedef void (*qmk_setting_callback_t)(void);
 

From fa26d6e1bc2343fe39cbcea0e27554db93966852 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 8 Jul 2021 23:02:17 -0400
Subject: [PATCH 25/44] qmk_settings: wrap TAP_CODE_DELAY and
 TAP_HOLD_CAPS_DELAY

---
 quantum/process_keycode/process_auto_shift.c |  4 +---
 quantum/qmk_settings.c                       |  4 ++++
 quantum/qmk_settings.h                       | 19 ++++++++++++++++++-
 quantum/quantum.c                            |  5 ++---
 quantum/vial.c                               |  7 ++-----
 tmk_core/common/action.c                     | 18 ++++++------------
 6 files changed, 33 insertions(+), 24 deletions(-)

diff --git a/quantum/process_keycode/process_auto_shift.c b/quantum/process_keycode/process_auto_shift.c
index 521fc1e6e2..9ef953c4b9 100644
--- a/quantum/process_keycode/process_auto_shift.c
+++ b/quantum/process_keycode/process_auto_shift.c
@@ -110,9 +110,7 @@ if (QS_auto_shift_repeat && !QS_auto_shift_no_auto_repeat) {
 }
         }
 
-#    if TAP_CODE_DELAY > 0
-        wait_ms(TAP_CODE_DELAY);
-#    endif
+        wait_ms(QS_tap_code_delay);
         unregister_code(autoshift_lastkey);
         del_weak_mods(MOD_BIT(KC_LSFT));
     } else {
diff --git a/quantum/qmk_settings.c b/quantum/qmk_settings.c
index 6199e8420c..8e273e65fc 100644
--- a/quantum/qmk_settings.c
+++ b/quantum/qmk_settings.c
@@ -48,6 +48,8 @@ static const qmk_settings_proto_t protos[] PROGMEM = {
    DECLARE_SETTING_CB(15, mousekey_wheel_interval, mousekey_apply),
    DECLARE_SETTING_CB(16, mousekey_wheel_max_speed, mousekey_apply),
    DECLARE_SETTING_CB(17, mousekey_wheel_time_to_max, mousekey_apply),
+   DECLARE_SETTING(18, tap_code_delay),
+   DECLARE_SETTING(19, tap_hold_caps_delay),
 };
 
 static const qmk_settings_proto_t *find_setting(uint16_t qsid) {
@@ -106,6 +108,8 @@ void qmk_settings_reset(void) {
     QS.combo_term = COMBO_TERM;
     QS.tapping_term = TAPPING_TERM;
     QS.tapping = 0;
+    QS.tap_code_delay = TAP_CODE_DELAY;
+    QS.tap_hold_caps_delay = TAP_HOLD_CAPS_DELAY;
 
     save_settings();
     /* to trigger all callbacks */
diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index 8b24827f9f..1c7e8704d6 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -5,6 +5,13 @@
 
 /* take qmk config macros and set up helper variables for default settings */
 
+#ifndef TAP_CODE_DELAY
+#    define TAP_CODE_DELAY 0
+#endif
+#ifndef TAP_HOLD_CAPS_DELAY
+#    define TAP_HOLD_CAPS_DELAY 80
+#endif
+
 /* ========================================================================== */
 /* Grave escape                                                               */
 /* ========================================================================== */
@@ -111,8 +118,10 @@ typedef struct {
     uint8_t auto_shift;
     uint8_t osk_tap_toggle;
     uint8_t tapping;
+    uint16_t tap_code_delay;
+    uint16_t tap_hold_caps_delay;
 } qmk_settings_t;
-_Static_assert(sizeof(qmk_settings_t) == 30, "unexpected size of the qmk_settings_t structure");
+_Static_assert(sizeof(qmk_settings_t) == 34, "unexpected size of the qmk_settings_t structure");
 
 typedef void (*qmk_setting_callback_t)(void);
 
@@ -157,6 +166,10 @@ extern qmk_settings_t QS;
 /* Combo */
 #define QS_combo_term (QS.combo_term)
 
+/* Tap delays */
+#define QS_tap_code_delay (QS.tap_code_delay)
+#define QS_tap_hold_caps_delay (QS.tap_hold_caps_delay)
+
 #else
 /* dynamic settings framework is disabled => hardcode the settings and let the compiler optimize extra branches out */
 
@@ -185,4 +198,8 @@ extern qmk_settings_t QS;
 /* Combo */
 #define QS_combo_term COMBO_TERM
 
+/* Tap delays */
+#define QS_tap_code_delay TAP_CODE_DELAY
+#define QS_tap_hold_caps_delay TAP_HOLD_CAPS_DELAY
+
 #endif
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 03f51ff921..b71faa7f84 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -16,6 +16,7 @@
 
 #include "quantum.h"
 #include "magic.h"
+#include "qmk_settings.h"
 
 #ifdef BLUETOOTH_ENABLE
 #    include "outputselect.h"
@@ -107,9 +108,7 @@ void unregister_code16(uint16_t code) {
 
 void tap_code16(uint16_t code) {
     register_code16(code);
-#if TAP_CODE_DELAY > 0
-    wait_ms(TAP_CODE_DELAY);
-#endif
+    wait_ms(QS_tap_code_delay);
     unregister_code16(code);
 }
 
diff --git a/quantum/vial.c b/quantum/vial.c
index c7afe3fa07..b67aaa9f21 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -286,11 +286,7 @@ static void vial_keycode_tap(uint16_t keycode) __attribute__((unused));
 
 static void vial_keycode_tap(uint16_t keycode) {
     vial_keycode_down(keycode);
-
-#if TAP_CODE_DELAY > 0
-    wait_ms(TAP_CODE_DELAY);
-#endif
-
+    wait_ms(QS_tap_code_delay);
     vial_keycode_up(keycode);
 }
 
@@ -429,6 +425,7 @@ 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(QS_tap_code_delay);
     uint8_t st = dance_state[index];
     state->count = 0;
     dance_state[index] = 0;
diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c
index c1f3a2968a..09e21e769e 100644
--- a/tmk_core/common/action.c
+++ b/tmk_core/common/action.c
@@ -56,12 +56,6 @@ __attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrec
 __attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; }
 #endif
 
-#ifndef TAP_CODE_DELAY
-#    define TAP_CODE_DELAY 0
-#endif
-#ifndef TAP_HOLD_CAPS_DELAY
-#    define TAP_HOLD_CAPS_DELAY 80
-#endif
 /** \brief Called to execute an action.
  *
  * FIXME: Needs documentation.
@@ -370,9 +364,9 @@ if (QS_oneshot_tap_toggle > 1) {
                         if (tap_count > 0) {
                             dprint("MODS_TAP: Tap: unregister_code\n");
                             if (action.layer_tap.code == KC_CAPS) {
-                                wait_ms(TAP_HOLD_CAPS_DELAY);
+                                wait_ms(QS_tap_hold_caps_delay);
                             } else {
-                                wait_ms(TAP_CODE_DELAY);
+                                wait_ms(QS_tap_code_delay);
                             }
                             unregister_code(action.key.code);
                         } else {
@@ -595,9 +589,9 @@ if (QS_oneshot_tap_toggle > 1) {
                         if (tap_count > 0) {
                             dprint("KEYMAP_TAP_KEY: Tap: unregister_code\n");
                             if (action.layer_tap.code == KC_CAPS) {
-                                wait_ms(TAP_HOLD_CAPS_DELAY);
+                                wait_ms(QS_tap_hold_caps_delay);
                             } else {
-                                wait_ms(TAP_CODE_DELAY);
+                                wait_ms(QS_tap_code_delay);
                             }
                             unregister_code(action.layer_tap.code);
                         } else {
@@ -676,7 +670,7 @@ if (QS_oneshot_tap_toggle > 1) {
                         if (event.pressed) {
                             register_code(action.swap.code);
                         } else {
-                            wait_ms(TAP_CODE_DELAY);
+                            wait_ms(QS_tap_code_delay);
                             unregister_code(action.swap.code);
                             *record = (keyrecord_t){};  // hack: reset tap mode
                         }
@@ -932,7 +926,7 @@ void tap_code_delay(uint8_t code, uint16_t delay) {
  *
  * \param code The basic keycode to tap. If `code` is `KC_CAPS`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined.
  */
-void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); }
+void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS ? QS_tap_hold_caps_delay : QS_tap_code_delay); }
 
 /** \brief Adds the given physically pressed modifiers and sends a keyboard report immediately.
  *

From 7ee6ddd9a763cdc742d36d002d6a834c651efd28 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 8 Jul 2021 19:27:26 -0400
Subject: [PATCH 26/44] vialrgb: initial

---
 common_features.mk |  5 +++++
 quantum/via.c      | 19 +++++++++++++++---
 quantum/vial.c     |  6 ++++--
 quantum/vial.h     |  1 +
 quantum/vialrgb.c  | 50 ++++++++++++++++++++++++++++++++++++++++++++++
 quantum/vialrgb.h  | 21 +++++++++++++++++++
 6 files changed, 97 insertions(+), 5 deletions(-)
 create mode 100644 quantum/vialrgb.c
 create mode 100644 quantum/vialrgb.h

diff --git a/common_features.mk b/common_features.mk
index fe349018e6..26eaf4829b 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -474,6 +474,11 @@ ifeq ($(strip $(VIAL_ENCODERS_ENABLE)), yes)
     OPT_DEFS += -DVIAL_ENCODERS_ENABLE
 endif
 
+ifeq ($(strip $(VIALRGB_ENABLE)), yes)
+    SRC += $(QUANTUM_DIR)/vialrgb.c
+    OPT_DEFS += -DVIALRGB_ENABLE
+endif
+
 ifeq ($(strip $(DYNAMIC_KEYMAP_ENABLE)), yes)
     OPT_DEFS += -DDYNAMIC_KEYMAP_ENABLE
     SRC += $(QUANTUM_DIR)/dynamic_keymap.c
diff --git a/quantum/via.c b/quantum/via.c
index 32d428682d..6403c5f41a 100644
--- a/quantum/via.c
+++ b/quantum/via.c
@@ -52,6 +52,10 @@
 #include "vial.h"
 #endif
 
+#ifdef VIALRGB_ENABLE
+#include "vialrgb.h"
+#endif
+
 // Forward declare some helpers.
 #if defined(VIA_QMK_BACKLIGHT_ENABLE)
 void via_qmk_backlight_set_value(uint8_t *data);
@@ -322,10 +326,13 @@ void raw_hid_receive(uint8_t *data, uint8_t length) {
 #if defined(VIA_QMK_RGBLIGHT_ENABLE)
             via_qmk_rgblight_set_value(command_data);
 #endif
+#if defined(VIALRGB_ENABLE)
+            vialrgb_set_value(data, length);
+#endif
 #if defined(VIA_CUSTOM_LIGHTING_ENABLE)
             raw_hid_receive_kb(data, length);
 #endif
-#if !defined(VIA_QMK_BACKLIGHT_ENABLE) && !defined(VIA_QMK_RGBLIGHT_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE)
+#if !defined(VIA_QMK_BACKLIGHT_ENABLE) && !defined(VIA_QMK_RGBLIGHT_ENABLE) && !defined(VIALRGB_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE)
             // Return the unhandled state
             *command_id = id_unhandled;
 #endif
@@ -338,10 +345,13 @@ void raw_hid_receive(uint8_t *data, uint8_t length) {
 #if defined(VIA_QMK_RGBLIGHT_ENABLE)
             via_qmk_rgblight_get_value(command_data);
 #endif
+#if defined(VIALRGB_ENABLE)
+            vialrgb_get_value(data, length);
+#endif
 #if defined(VIA_CUSTOM_LIGHTING_ENABLE)
             raw_hid_receive_kb(data, length);
 #endif
-#if !defined(VIA_QMK_BACKLIGHT_ENABLE) && !defined(VIA_QMK_RGBLIGHT_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE)
+#if !defined(VIA_QMK_BACKLIGHT_ENABLE) && !defined(VIA_QMK_RGBLIGHT_ENABLE) && !defined(VIALRGB_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE)
             // Return the unhandled state
             *command_id = id_unhandled;
 #endif
@@ -354,10 +364,13 @@ void raw_hid_receive(uint8_t *data, uint8_t length) {
 #if defined(VIA_QMK_RGBLIGHT_ENABLE)
             eeconfig_update_rgblight_current();
 #endif
+#if defined(VIALRGB_ENABLE)
+            vialrgb_save(data, length);
+#endif
 #if defined(VIA_CUSTOM_LIGHTING_ENABLE)
             raw_hid_receive_kb(data, length);
 #endif
-#if !defined(VIA_QMK_BACKLIGHT_ENABLE) && !defined(VIA_QMK_RGBLIGHT_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE)
+#if !defined(VIA_QMK_BACKLIGHT_ENABLE) && !defined(VIA_QMK_RGBLIGHT_ENABLE) && !defined(VIALRGB_ENABLE) && !defined(VIA_CUSTOM_LIGHTING_ENABLE)
             // Return the unhandled state
             *command_id = id_unhandled;
 #endif
diff --git a/quantum/vial.c b/quantum/vial.c
index b67aaa9f21..94a370db87 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -44,8 +44,6 @@ _Static_assert(VIAL_UNLOCK_NUM_KEYS < 15, "Max 15 unlock keys");
 _Static_assert(sizeof(vial_unlock_combo_rows) == sizeof(vial_unlock_combo_cols), "The number of unlock cols and rows should be the same");
 #endif
 
-#define VIAL_RAW_EPSIZE 32
-
 #ifndef VIAL_ENCODER_KEYCODE_DELAY
 #define VIAL_ENCODER_KEYCODE_DELAY 10
 #endif
@@ -82,11 +80,15 @@ void vial_handle_cmd(uint8_t *msg, uint8_t length) {
         case vial_get_keyboard_id: {
             uint8_t keyboard_uid[] = VIAL_KEYBOARD_UID;
 
+            memset(msg, 0, length);
             msg[0] = VIAL_PROTOCOL_VERSION & 0xFF;
             msg[1] = (VIAL_PROTOCOL_VERSION >> 8) & 0xFF;
             msg[2] = (VIAL_PROTOCOL_VERSION >> 16) & 0xFF;
             msg[3] = (VIAL_PROTOCOL_VERSION >> 24) & 0xFF;
             memcpy(&msg[4], keyboard_uid, 8);
+#ifdef VIALRGB_ENABLE
+            msg[12] = 1; /* bit flag to indicate vialrgb is supported - so third-party apps don't have to query json */
+#endif
             break;
         }
         /* Retrieve keyboard definition size */
diff --git a/quantum/vial.h b/quantum/vial.h
index 9a910dd8a9..74e648fa23 100644
--- a/quantum/vial.h
+++ b/quantum/vial.h
@@ -20,6 +20,7 @@
 #include <stdbool.h>
 
 #define VIAL_PROTOCOL_VERSION ((uint32_t)0x00000004)
+#define VIAL_RAW_EPSIZE 32
 
 void vial_init(void);
 void vial_handle_cmd(uint8_t *data, uint8_t length);
diff --git a/quantum/vialrgb.c b/quantum/vialrgb.c
new file mode 100644
index 0000000000..e29d2b4cf9
--- /dev/null
+++ b/quantum/vialrgb.c
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "vialrgb.h"
+
+#include <inttypes.h>
+#include "rgb_matrix.h"
+#include "vial.h"
+
+/* Based on https://github.com/qmk/qmk_firmware/pull/13036 */
+
+void vialrgb_get_value(uint8_t *data, uint8_t length) {
+    if (length != VIAL_RAW_EPSIZE) return;
+
+    /* data[0] is used by VIA command id */
+    uint8_t cmd = data[1];
+    uint8_t *args = &data[2];
+    switch (cmd) {
+    case vialrgb_get_mode: {
+        args[0] = rgb_matrix_get_mode();
+        args[1] = rgb_matrix_get_speed();
+        args[2] = rgb_matrix_get_hue();
+        args[3] = rgb_matrix_get_sat();
+        args[4] = rgb_matrix_get_val();
+        break;
+    }
+    }
+}
+
+void vialrgb_set_value(uint8_t *data, uint8_t length) {
+    if (length != VIAL_RAW_EPSIZE) return;
+
+    /* data[0] is used by VIA command id */
+    uint8_t cmd = data[1];
+    uint8_t *args = &data[2];
+    switch (cmd) {
+    case vialrgb_set_mode: {
+        rgb_matrix_mode_noeeprom(args[0]);
+        rgb_matrix_set_speed_noeeprom(args[1]);
+        rgb_matrix_sethsv_noeeprom(args[2], args[3], args[4]);
+        break;
+    }
+    }
+}
+
+void vialrgb_save(uint8_t *data, uint8_t length) {
+    (void)data;
+    (void)length;
+
+    eeconfig_update_rgb_matrix();
+}
diff --git a/quantum/vialrgb.h b/quantum/vialrgb.h
new file mode 100644
index 0000000000..a18a6c8ebf
--- /dev/null
+++ b/quantum/vialrgb.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/* Start at 0x40 in order to not conflict with existing "enum via_lighting_value",
+   even though they likely wouldn't be enabled together with vialrgb */
+enum {
+    vialrgb_set_mode = 0x40,
+};
+
+enum {
+    vialrgb_get_mode = 0x40,
+};
+
+void vialrgb_get_value(uint8_t *data, uint8_t length);
+void vialrgb_set_value(uint8_t *data, uint8_t length);
+void vialrgb_save(uint8_t *data, uint8_t length);
+
+#if defined(VIALRGB_ENABLE) && !defined(RGB_MATRIX_ENABLE)
+#error VIALRGB_ENABLE=yes requires RGB_MATRIX_ENABLE=yes
+#endif

From 881c8027d2dba6b4f2fd5336bc49dca4fea56a1b Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 8 Jul 2021 20:20:27 -0400
Subject: [PATCH 27/44] vialrgb: add rgb info command

---
 quantum/vialrgb.c | 10 ++++++++++
 quantum/vialrgb.h |  7 +++++--
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/quantum/vialrgb.c b/quantum/vialrgb.c
index e29d2b4cf9..d06ddffcc1 100644
--- a/quantum/vialrgb.c
+++ b/quantum/vialrgb.c
@@ -6,6 +6,11 @@
 #include "rgb_matrix.h"
 #include "vial.h"
 
+#if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX
+#    undef RGB_MATRIX_MAXIMUM_BRIGHTNESS
+#    define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX
+#endif
+
 /* Based on https://github.com/qmk/qmk_firmware/pull/13036 */
 
 void vialrgb_get_value(uint8_t *data, uint8_t length) {
@@ -15,6 +20,11 @@ void vialrgb_get_value(uint8_t *data, uint8_t length) {
     uint8_t cmd = data[1];
     uint8_t *args = &data[2];
     switch (cmd) {
+    case vialrgb_get_info:
+        args[0] = VIALRGB_PROTOCOL_VERSION & 0xFF;
+        args[1] = VIALRGB_PROTOCOL_VERSION >> 8;
+        args[2] = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
+        break;
     case vialrgb_get_mode: {
         args[0] = rgb_matrix_get_mode();
         args[1] = rgb_matrix_get_speed();
diff --git a/quantum/vialrgb.h b/quantum/vialrgb.h
index a18a6c8ebf..f06eb316c6 100644
--- a/quantum/vialrgb.h
+++ b/quantum/vialrgb.h
@@ -2,14 +2,17 @@
 
 #pragma once
 
+#define VIALRGB_PROTOCOL_VERSION 1
+
 /* Start at 0x40 in order to not conflict with existing "enum via_lighting_value",
    even though they likely wouldn't be enabled together with vialrgb */
 enum {
-    vialrgb_set_mode = 0x40,
+    vialrgb_set_mode = 0x41,
 };
 
 enum {
-    vialrgb_get_mode = 0x40,
+    vialrgb_get_info = 0x40,
+    vialrgb_get_mode = 0x41,
 };
 
 void vialrgb_get_value(uint8_t *data, uint8_t length);

From 5bc1373a2781046d485e7d934f8a42db89d83de7 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 8 Jul 2021 21:49:51 -0400
Subject: [PATCH 28/44] move RGB_MATRIX_MAXIMUM_BRIGHTNESS to header file

---
 quantum/rgb_matrix.c | 5 -----
 quantum/rgb_matrix.h | 5 +++++
 quantum/vialrgb.c    | 5 -----
 3 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c
index ab8dbd849b..47d6044ee1 100644
--- a/quantum/rgb_matrix.c
+++ b/quantum/rgb_matrix.c
@@ -71,11 +71,6 @@ __attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv
 #    undef RGB_DISABLE_WHEN_USB_SUSPENDED
 #endif
 
-#if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX
-#    undef RGB_MATRIX_MAXIMUM_BRIGHTNESS
-#    define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX
-#endif
-
 #if !defined(RGB_MATRIX_HUE_STEP)
 #    define RGB_MATRIX_HUE_STEP 8
 #endif
diff --git a/quantum/rgb_matrix.h b/quantum/rgb_matrix.h
index a615b8422c..2e597500e6 100644
--- a/quantum/rgb_matrix.h
+++ b/quantum/rgb_matrix.h
@@ -224,3 +224,8 @@ extern last_hit_t g_last_hit_tracker;
 #ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS
 extern uint8_t g_rgb_frame_buffer[MATRIX_ROWS][MATRIX_COLS];
 #endif
+
+#if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX
+#    undef RGB_MATRIX_MAXIMUM_BRIGHTNESS
+#    define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX
+#endif
diff --git a/quantum/vialrgb.c b/quantum/vialrgb.c
index d06ddffcc1..37fc4ea1a5 100644
--- a/quantum/vialrgb.c
+++ b/quantum/vialrgb.c
@@ -6,11 +6,6 @@
 #include "rgb_matrix.h"
 #include "vial.h"
 
-#if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX
-#    undef RGB_MATRIX_MAXIMUM_BRIGHTNESS
-#    define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX
-#endif
-
 /* Based on https://github.com/qmk/qmk_firmware/pull/13036 */
 
 void vialrgb_get_value(uint8_t *data, uint8_t length) {

From 1d5ed0b24aaac2b845bf48dacf6520fe108aaf3c Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Thu, 8 Jul 2021 22:38:45 -0400
Subject: [PATCH 29/44] vialrgb: convert between vialrgb id and qmk id for
 modes

---
 .../rgb_matrix_animations/alpha_mods_anim.h   |   1 +
 .../rgb_matrix_animations/solid_color_anim.h  |   1 +
 quantum/vialrgb.c                             | 105 +++++++++++++++++-
 quantum/vialrgb.h                             |   1 +
 4 files changed, 105 insertions(+), 3 deletions(-)

diff --git a/quantum/rgb_matrix_animations/alpha_mods_anim.h b/quantum/rgb_matrix_animations/alpha_mods_anim.h
index 426d88ef35..730b07128c 100644
--- a/quantum/rgb_matrix_animations/alpha_mods_anim.h
+++ b/quantum/rgb_matrix_animations/alpha_mods_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
+#define RGB_MATRIX_EFFECT_ALPHAS_MODS
 RGB_MATRIX_EFFECT(ALPHAS_MODS)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/solid_color_anim.h b/quantum/rgb_matrix_animations/solid_color_anim.h
index 79d63cf133..0c6b92eb1f 100644
--- a/quantum/rgb_matrix_animations/solid_color_anim.h
+++ b/quantum/rgb_matrix_animations/solid_color_anim.h
@@ -1,3 +1,4 @@
+#define RGB_MATRIX_EFFECT_SOLID_COLOR
 RGB_MATRIX_EFFECT(SOLID_COLOR)
 #ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/vialrgb.c b/quantum/vialrgb.c
index 37fc4ea1a5..5bec65282e 100644
--- a/quantum/vialrgb.c
+++ b/quantum/vialrgb.c
@@ -1,12 +1,107 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Based on https://github.com/qmk/qmk_firmware/pull/13036 */
 
 #include "vialrgb.h"
 
 #include <inttypes.h>
+#include <string.h>
 #include "rgb_matrix.h"
 #include "vial.h"
 
-/* Based on https://github.com/qmk/qmk_firmware/pull/13036 */
+/* Note - never reorder or remove these, only add new animations at the end */
+enum {
+    VIALRGB_EFFECT_OFF = 0,
+    VIALRGB_EFFECT_DIRECT,
+    VIALRGB_EFFECT_SOLID_COLOR,
+    VIALRGB_EFFECT_ALPHAS_MODS,
+    VIALRGB_EFFECT_GRADIENT_UP_DOWN_ANIM,
+    VIALRGB_EFFECT_GRADIENT_LEFT_RIGHT_ANIM,
+    VIALRGB_EFFECT_BREATHING_ANIM,
+    VIALRGB_EFFECT_COLORBAND_SAT_ANIM,
+    VIALRGB_EFFECT_COLORBAND_VAL_ANIM,
+    VIALRGB_EFFECT_COLORBAND_PINWHEEL_SAT_ANIM,
+    VIALRGB_EFFECT_COLORBAND_PINWHEEL_VAL_ANIM,
+    VIALRGB_EFFECT_COLORBAND_SPIRAL_SAT_ANIM,
+    VIALRGB_EFFECT_COLORBAND_SPIRAL_VAL_ANIM,
+    VIALRGB_EFFECT_CYCLE_ALL_ANIM,
+    VIALRGB_EFFECT_CYCLE_LEFT_RIGHT_ANIM,
+    VIALRGB_EFFECT_CYCLE_UP_DOWN_ANIM,
+    VIALRGB_EFFECT_RAINBOW_MOVING_CHEVRON_ANIM,
+    VIALRGB_EFFECT_CYCLE_OUT_IN_ANIM,
+    VIALRGB_EFFECT_CYCLE_OUT_IN_DUAL_ANIM,
+    VIALRGB_EFFECT_CYCLE_PINWHEEL_ANIM,
+    VIALRGB_EFFECT_CYCLE_SPIRAL_ANIM,
+    VIALRGB_EFFECT_DUAL_BEACON_ANIM,
+    VIALRGB_EFFECT_RAINBOW_BEACON_ANIM,
+    VIALRGB_EFFECT_RAINBOW_PINWHEELS_ANIM,
+    VIALRGB_EFFECT_RAINDROPS_ANIM,
+    VIALRGB_EFFECT_JELLYBEAN_RAINDROPS_ANIM,
+    VIALRGB_EFFECT_HUE_BREATHING_ANIM,
+    VIALRGB_EFFECT_HUE_PENDULUM_ANIM,
+    VIALRGB_EFFECT_HUE_WAVE_ANIM,
+    VIALRGB_EFFECT_TYPING_HEATMAP_ANIM,
+    VIALRGB_EFFECT_DIGITAL_RAIN_ANIM,
+    VIALRGB_EFFECT_SOLID_REACTIVE_SIMPLE_ANIM,
+    VIALRGB_EFFECT_SOLID_REACTIVE_ANIM,
+    VIALRGB_EFFECT_SOLID_REACTIVE_WIDE,
+    VIALRGB_EFFECT_SOLID_REACTIVE_CROSS,
+    VIALRGB_EFFECT_SOLID_REACTIVE_NEXUS,
+    VIALRGB_EFFECT_SPLASH_ANIM,
+    VIALRGB_EFFECT_SOLID_SPLASH_ANIM,
+};
+
+typedef struct {
+    uint16_t vialrgb_id;
+    uint16_t qmk_id;
+} vialrgb_supported_mode_t;
+
+static const PROGMEM vialrgb_supported_mode_t supported_modes[] = {
+    { VIALRGB_EFFECT_OFF, 0 },
+    // { VIALRGB_EFFECT_DIRECT, 0 }, TODO
+#ifdef RGB_MATRIX_EFFECT_SOLID_COLOR
+    { VIALRGB_EFFECT_SOLID_COLOR, RGB_MATRIX_SOLID_COLOR },
+#endif
+#ifdef RGB_MATRIX_EFFECT_ALPHAS_MODS
+    { VIALRGB_EFFECT_ALPHAS_MODS, RGB_MATRIX_ALPHAS_MODS },
+#endif
+};
+
+#define SUPPORTED_MODES_LENGTH (sizeof(supported_modes)/sizeof(*supported_modes))
+
+static void get_supported(uint8_t *args, uint8_t length) {
+    /* retrieve supported effects (VialRGB IDs) with ID > gt */
+    uint16_t gt;
+    memcpy(&gt, args, sizeof(gt));
+    memset(args, 0xFF, length);
+    for (size_t i = 0; i < SUPPORTED_MODES_LENGTH; ++i) {
+        uint16_t id = pgm_read_word(&supported_modes[i].vialrgb_id);
+        if (id > gt && length >= sizeof(id)) {
+            memcpy(args, &id, sizeof(id));
+            length -= sizeof(id);
+            args += sizeof(id);
+        }
+    }
+}
+
+static uint16_t qmk_id_to_vialrgb_id(uint16_t id) {
+    for (size_t i = 0; i < SUPPORTED_MODES_LENGTH; ++i) {
+        uint16_t qmk_id = pgm_read_word(&supported_modes[i].qmk_id);
+        uint16_t vialrgb_id = pgm_read_word(&supported_modes[i].vialrgb_id);
+        if (qmk_id == id)
+            return vialrgb_id;
+    }
+    return 0;
+}
+
+static uint16_t vialrgb_id_to_qmk_id(uint16_t id) {
+    for (size_t i = 0; i < SUPPORTED_MODES_LENGTH; ++i) {
+        uint16_t qmk_id = pgm_read_word(&supported_modes[i].qmk_id);
+        uint16_t vialrgb_id = pgm_read_word(&supported_modes[i].vialrgb_id);
+        if (vialrgb_id == id)
+            return qmk_id;
+    }
+    return 0;
+}
 
 void vialrgb_get_value(uint8_t *data, uint8_t length) {
     if (length != VIAL_RAW_EPSIZE) return;
@@ -21,13 +116,17 @@ void vialrgb_get_value(uint8_t *data, uint8_t length) {
         args[2] = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
         break;
     case vialrgb_get_mode: {
-        args[0] = rgb_matrix_get_mode();
+        args[0] = qmk_id_to_vialrgb_id(rgb_matrix_get_mode());
         args[1] = rgb_matrix_get_speed();
         args[2] = rgb_matrix_get_hue();
         args[3] = rgb_matrix_get_sat();
         args[4] = rgb_matrix_get_val();
         break;
     }
+    case vialrgb_get_supported: {
+        get_supported(args, length - 2);
+        break;
+    }
     }
 }
 
@@ -39,7 +138,7 @@ void vialrgb_set_value(uint8_t *data, uint8_t length) {
     uint8_t *args = &data[2];
     switch (cmd) {
     case vialrgb_set_mode: {
-        rgb_matrix_mode_noeeprom(args[0]);
+        rgb_matrix_mode_noeeprom(vialrgb_id_to_qmk_id(args[0]));
         rgb_matrix_set_speed_noeeprom(args[1]);
         rgb_matrix_sethsv_noeeprom(args[2], args[3], args[4]);
         break;
diff --git a/quantum/vialrgb.h b/quantum/vialrgb.h
index f06eb316c6..170d110c34 100644
--- a/quantum/vialrgb.h
+++ b/quantum/vialrgb.h
@@ -13,6 +13,7 @@ enum {
 enum {
     vialrgb_get_info = 0x40,
     vialrgb_get_mode = 0x41,
+    vialrgb_get_supported = 0x42,
 };
 
 void vialrgb_get_value(uint8_t *data, uint8_t length);

From 23e8adc8b27bf7542fc7fac9e8fbcc7bdf833ea1 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Fri, 9 Jul 2021 17:54:33 -0400
Subject: [PATCH 30/44] vialrgb: add all effects

---
 .../rgb_matrix_animations/breathing_anim.h    |   1 +
 .../colorband_pinwheel_sat_anim.h             |   1 +
 .../colorband_pinwheel_val_anim.h             |   1 +
 .../colorband_sat_anim.h                      |   1 +
 .../colorband_spiral_sat_anim.h               |   1 +
 .../colorband_spiral_val_anim.h               |   1 +
 .../colorband_val_anim.h                      |   1 +
 .../rgb_matrix_animations/cycle_all_anim.h    |   1 +
 .../cycle_left_right_anim.h                   |   1 +
 .../rgb_matrix_animations/cycle_out_in_anim.h |   1 +
 .../cycle_out_in_dual_anim.h                  |   1 +
 .../cycle_pinwheel_anim.h                     |   1 +
 .../rgb_matrix_animations/cycle_spiral_anim.h |   1 +
 .../cycle_up_down_anim.h                      |   1 +
 .../rgb_matrix_animations/digital_rain_anim.h |   1 +
 .../rgb_matrix_animations/dual_beacon_anim.h  |   1 +
 .../gradient_left_right_anim.h                |   1 +
 .../gradient_up_down_anim.h                   |   1 +
 .../hue_breathing_anim.h                      |   1 +
 .../rgb_matrix_animations/hue_pendulum_anim.h |   1 +
 quantum/rgb_matrix_animations/hue_wave_anim.h |   1 +
 .../jellybean_raindrops_anim.h                |   1 +
 .../rainbow_beacon_anim.h                     |   1 +
 .../rainbow_moving_chevron_anim.h             |   1 +
 .../rainbow_pinwheels_anim.h                  |   1 +
 .../rgb_matrix_animations/raindrops_anim.h    |   1 +
 .../solid_reactive_anim.h                     |   1 +
 .../solid_reactive_cross.h                    |   2 +
 .../solid_reactive_nexus.h                    |   2 +
 .../solid_reactive_simple_anim.h              |   1 +
 .../solid_reactive_wide.h                     |   2 +
 .../rgb_matrix_animations/solid_splash_anim.h |   2 +
 quantum/rgb_matrix_animations/splash_anim.h   |   2 +
 .../typing_heatmap_anim.h                     |   1 +
 quantum/vialrgb.c                             |  53 +-----
 quantum/vialrgb_effects.inc                   | 176 ++++++++++++++++++
 36 files changed, 216 insertions(+), 52 deletions(-)
 create mode 100644 quantum/vialrgb_effects.inc

diff --git a/quantum/rgb_matrix_animations/breathing_anim.h b/quantum/rgb_matrix_animations/breathing_anim.h
index 340bd93e5d..dbb606eaa6 100644
--- a/quantum/rgb_matrix_animations/breathing_anim.h
+++ b/quantum/rgb_matrix_animations/breathing_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_BREATHING
+#define RGB_MATRIX_EFFECT_BREATHING
 RGB_MATRIX_EFFECT(BREATHING)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/colorband_pinwheel_sat_anim.h b/quantum/rgb_matrix_animations/colorband_pinwheel_sat_anim.h
index 3df3cfda7d..a15fe0f801 100644
--- a/quantum/rgb_matrix_animations/colorband_pinwheel_sat_anim.h
+++ b/quantum/rgb_matrix_animations/colorband_pinwheel_sat_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_BAND_PINWHEEL_SAT
+#define RGB_MATRIX_EFFECT_BAND_PINWHEEL_SAT
 RGB_MATRIX_EFFECT(BAND_PINWHEEL_SAT)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/colorband_pinwheel_val_anim.h b/quantum/rgb_matrix_animations/colorband_pinwheel_val_anim.h
index 7d80074fd5..fe4c1b233c 100644
--- a/quantum/rgb_matrix_animations/colorband_pinwheel_val_anim.h
+++ b/quantum/rgb_matrix_animations/colorband_pinwheel_val_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_BAND_PINWHEEL_VAL
+#define RGB_MATRIX_EFFECT_BAND_PINWHEEL_VAL
 RGB_MATRIX_EFFECT(BAND_PINWHEEL_VAL)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/colorband_sat_anim.h b/quantum/rgb_matrix_animations/colorband_sat_anim.h
index 35b830af6b..ac93b1ec41 100644
--- a/quantum/rgb_matrix_animations/colorband_sat_anim.h
+++ b/quantum/rgb_matrix_animations/colorband_sat_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_BAND_SAT
+#define RGB_MATRIX_EFFECT_BAND_SAT
 RGB_MATRIX_EFFECT(BAND_SAT)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/colorband_spiral_sat_anim.h b/quantum/rgb_matrix_animations/colorband_spiral_sat_anim.h
index 048157aa1b..8a9fe0acea 100644
--- a/quantum/rgb_matrix_animations/colorband_spiral_sat_anim.h
+++ b/quantum/rgb_matrix_animations/colorband_spiral_sat_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_BAND_SPIRAL_SAT
+#define RGB_MATRIX_EFFECT_BAND_SPIRAL_SAT
 RGB_MATRIX_EFFECT(BAND_SPIRAL_SAT)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/colorband_spiral_val_anim.h b/quantum/rgb_matrix_animations/colorband_spiral_val_anim.h
index bff2da1616..aaf5bd2076 100644
--- a/quantum/rgb_matrix_animations/colorband_spiral_val_anim.h
+++ b/quantum/rgb_matrix_animations/colorband_spiral_val_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_BAND_SPIRAL_VAL
+#define RGB_MATRIX_EFFECT_BAND_SPIRAL_VAL
 RGB_MATRIX_EFFECT(BAND_SPIRAL_VAL)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/colorband_val_anim.h b/quantum/rgb_matrix_animations/colorband_val_anim.h
index f1aaf1d067..7b1c70ec35 100644
--- a/quantum/rgb_matrix_animations/colorband_val_anim.h
+++ b/quantum/rgb_matrix_animations/colorband_val_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_BAND_VAL
+#define RGB_MATRIX_EFFECT_BAND_VAL
 RGB_MATRIX_EFFECT(BAND_VAL)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/cycle_all_anim.h b/quantum/rgb_matrix_animations/cycle_all_anim.h
index faf8598a39..9ca8eecb08 100644
--- a/quantum/rgb_matrix_animations/cycle_all_anim.h
+++ b/quantum/rgb_matrix_animations/cycle_all_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
+#define RGB_MATRIX_EFFECT_CYCLE_ALL
 RGB_MATRIX_EFFECT(CYCLE_ALL)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/cycle_left_right_anim.h b/quantum/rgb_matrix_animations/cycle_left_right_anim.h
index cf911eb937..eb248172e8 100644
--- a/quantum/rgb_matrix_animations/cycle_left_right_anim.h
+++ b/quantum/rgb_matrix_animations/cycle_left_right_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
+#define RGB_MATRIX_EFFECT_CYCLE_LEFT_RIGHT
 RGB_MATRIX_EFFECT(CYCLE_LEFT_RIGHT)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/cycle_out_in_anim.h b/quantum/rgb_matrix_animations/cycle_out_in_anim.h
index d66acd4b2b..f51758665d 100644
--- a/quantum/rgb_matrix_animations/cycle_out_in_anim.h
+++ b/quantum/rgb_matrix_animations/cycle_out_in_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_CYCLE_OUT_IN
+#define RGB_MATRIX_EFFECT_CYCLE_OUT_IN
 RGB_MATRIX_EFFECT(CYCLE_OUT_IN)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/cycle_out_in_dual_anim.h b/quantum/rgb_matrix_animations/cycle_out_in_dual_anim.h
index fe8396140f..f0e9eb199e 100644
--- a/quantum/rgb_matrix_animations/cycle_out_in_dual_anim.h
+++ b/quantum/rgb_matrix_animations/cycle_out_in_dual_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL
+#define RGB_MATRIX_EFFECT_CYCLE_OUT_IN_DUAL
 RGB_MATRIX_EFFECT(CYCLE_OUT_IN_DUAL)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/cycle_pinwheel_anim.h b/quantum/rgb_matrix_animations/cycle_pinwheel_anim.h
index 7799887099..ac81b69453 100644
--- a/quantum/rgb_matrix_animations/cycle_pinwheel_anim.h
+++ b/quantum/rgb_matrix_animations/cycle_pinwheel_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_CYCLE_PINWHEEL
+#define RGB_MATRIX_EFFECT_CYCLE_PINWHEEL
 RGB_MATRIX_EFFECT(CYCLE_PINWHEEL)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/cycle_spiral_anim.h b/quantum/rgb_matrix_animations/cycle_spiral_anim.h
index 80cfb0dbc7..5d7fd2cff7 100644
--- a/quantum/rgb_matrix_animations/cycle_spiral_anim.h
+++ b/quantum/rgb_matrix_animations/cycle_spiral_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_CYCLE_SPIRAL
+#define RGB_MATRIX_EFFECT_CYCLE_SPIRAL
 RGB_MATRIX_EFFECT(CYCLE_SPIRAL)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/cycle_up_down_anim.h b/quantum/rgb_matrix_animations/cycle_up_down_anim.h
index 5016f739d6..d24201d886 100644
--- a/quantum/rgb_matrix_animations/cycle_up_down_anim.h
+++ b/quantum/rgb_matrix_animations/cycle_up_down_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
+#define RGB_MATRIX_EFFECT_CYCLE_UP_DOWN
 RGB_MATRIX_EFFECT(CYCLE_UP_DOWN)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/digital_rain_anim.h b/quantum/rgb_matrix_animations/digital_rain_anim.h
index 1de45f8e8d..367c8aaefe 100644
--- a/quantum/rgb_matrix_animations/digital_rain_anim.h
+++ b/quantum/rgb_matrix_animations/digital_rain_anim.h
@@ -1,4 +1,5 @@
 #if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(DISABLE_RGB_MATRIX_DIGITAL_RAIN)
+#define RGB_MATRIX_EFFECT_DIGITAL_RAIN
 RGB_MATRIX_EFFECT(DIGITAL_RAIN)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/dual_beacon_anim.h b/quantum/rgb_matrix_animations/dual_beacon_anim.h
index ce94871681..f0fa777596 100644
--- a/quantum/rgb_matrix_animations/dual_beacon_anim.h
+++ b/quantum/rgb_matrix_animations/dual_beacon_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
+#define RGB_MATRIX_EFFECT_DUAL_BEACON
 RGB_MATRIX_EFFECT(DUAL_BEACON)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/gradient_left_right_anim.h b/quantum/rgb_matrix_animations/gradient_left_right_anim.h
index 53dfd04e2c..7709cee5ce 100644
--- a/quantum/rgb_matrix_animations/gradient_left_right_anim.h
+++ b/quantum/rgb_matrix_animations/gradient_left_right_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT
+#define RGB_MATRIX_EFFECT_GRADIENT_LEFT_RIGHT
 RGB_MATRIX_EFFECT(GRADIENT_LEFT_RIGHT)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/gradient_up_down_anim.h b/quantum/rgb_matrix_animations/gradient_up_down_anim.h
index 7e0d2898cf..d95523ab7f 100644
--- a/quantum/rgb_matrix_animations/gradient_up_down_anim.h
+++ b/quantum/rgb_matrix_animations/gradient_up_down_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
+#define RGB_MATRIX_EFFECT_GRADIENT_UP_DOWN
 RGB_MATRIX_EFFECT(GRADIENT_UP_DOWN)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/hue_breathing_anim.h b/quantum/rgb_matrix_animations/hue_breathing_anim.h
index 54dea958af..74c6087263 100644
--- a/quantum/rgb_matrix_animations/hue_breathing_anim.h
+++ b/quantum/rgb_matrix_animations/hue_breathing_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_HUE_BREATHING
+#define RGB_MATRIX_EFFECT_HUE_BREATHING
 RGB_MATRIX_EFFECT(HUE_BREATHING)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/hue_pendulum_anim.h b/quantum/rgb_matrix_animations/hue_pendulum_anim.h
index 2d8d36174f..23c3928d9c 100644
--- a/quantum/rgb_matrix_animations/hue_pendulum_anim.h
+++ b/quantum/rgb_matrix_animations/hue_pendulum_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_HUE_PENDULUM
+#define RGB_MATRIX_EFFECT_HUE_PENDULUM
 RGB_MATRIX_EFFECT(HUE_PENDULUM)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/hue_wave_anim.h b/quantum/rgb_matrix_animations/hue_wave_anim.h
index fd9026fc90..2fa717be1a 100644
--- a/quantum/rgb_matrix_animations/hue_wave_anim.h
+++ b/quantum/rgb_matrix_animations/hue_wave_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_HUE_WAVE
+#define RGB_MATRIX_EFFECT_HUE_WAVE
 RGB_MATRIX_EFFECT(HUE_WAVE)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h b/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h
index 9493b38508..1951ce5cdc 100644
--- a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h
+++ b/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
+#define RGB_MATRIX_EFFECT_JELLYBEAN_RAINDROPS
 RGB_MATRIX_EFFECT(JELLYBEAN_RAINDROPS)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/rainbow_beacon_anim.h b/quantum/rgb_matrix_animations/rainbow_beacon_anim.h
index 977261182f..f634f4e73a 100644
--- a/quantum/rgb_matrix_animations/rainbow_beacon_anim.h
+++ b/quantum/rgb_matrix_animations/rainbow_beacon_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
+#define RGB_MATRIX_EFFECT_RAINBOW_BEACON
 RGB_MATRIX_EFFECT(RAINBOW_BEACON)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h b/quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h
index e51e7b2516..abb5ad8fbb 100644
--- a/quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h
+++ b/quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+#define RGB_MATRIX_EFFECT_RAINBOW_MOVING_CHEVRON
 RGB_MATRIX_EFFECT(RAINBOW_MOVING_CHEVRON)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h b/quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h
index 1cd4ed2acf..b4fff8507b 100644
--- a/quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h
+++ b/quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
+#define RGB_MATRIX_EFFECT_RAINBOW_PINWHEELS
 RGB_MATRIX_EFFECT(RAINBOW_PINWHEELS)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/raindrops_anim.h b/quantum/rgb_matrix_animations/raindrops_anim.h
index 38359cdca7..b5e44c6389 100644
--- a/quantum/rgb_matrix_animations/raindrops_anim.h
+++ b/quantum/rgb_matrix_animations/raindrops_anim.h
@@ -1,4 +1,5 @@
 #ifndef DISABLE_RGB_MATRIX_RAINDROPS
+#define RGB_MATRIX_EFFECT_RAINDROPS
 RGB_MATRIX_EFFECT(RAINDROPS)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/solid_reactive_anim.h b/quantum/rgb_matrix_animations/solid_reactive_anim.h
index d45bb961bc..ed1d71241e 100644
--- a/quantum/rgb_matrix_animations/solid_reactive_anim.h
+++ b/quantum/rgb_matrix_animations/solid_reactive_anim.h
@@ -1,5 +1,6 @@
 #ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
 #    ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
+#define RGB_MATRIX_EFFECT_SOLID_REACTIVE
 RGB_MATRIX_EFFECT(SOLID_REACTIVE)
 #        ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/solid_reactive_cross.h b/quantum/rgb_matrix_animations/solid_reactive_cross.h
index f76c68e8c7..7cc93919cd 100644
--- a/quantum/rgb_matrix_animations/solid_reactive_cross.h
+++ b/quantum/rgb_matrix_animations/solid_reactive_cross.h
@@ -2,10 +2,12 @@
 #    if !defined(DISABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS) || !defined(DISABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS)
 
 #        ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS
+#define RGB_MATRIX_EFFECT_SOLID_REACTIVE_CROSS
 RGB_MATRIX_EFFECT(SOLID_REACTIVE_CROSS)
 #        endif
 
 #        ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS
+#define RGB_MATRIX_EFFECT_SOLID_REACTIVE_MULTICROSS
 RGB_MATRIX_EFFECT(SOLID_REACTIVE_MULTICROSS)
 #        endif
 
diff --git a/quantum/rgb_matrix_animations/solid_reactive_nexus.h b/quantum/rgb_matrix_animations/solid_reactive_nexus.h
index 17f94e3c18..b7a0d94b76 100644
--- a/quantum/rgb_matrix_animations/solid_reactive_nexus.h
+++ b/quantum/rgb_matrix_animations/solid_reactive_nexus.h
@@ -2,10 +2,12 @@
 #    if !defined(DISABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS) || !defined(DISABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS)
 
 #        ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS
+#define RGB_MATRIX_EFFECT_SOLID_REACTIVE_NEXUS
 RGB_MATRIX_EFFECT(SOLID_REACTIVE_NEXUS)
 #        endif
 
 #        ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS
+#define RGB_MATRIX_EFFECT_SOLID_REACTIVE_MULTINEXUS
 RGB_MATRIX_EFFECT(SOLID_REACTIVE_MULTINEXUS)
 #        endif
 
diff --git a/quantum/rgb_matrix_animations/solid_reactive_simple_anim.h b/quantum/rgb_matrix_animations/solid_reactive_simple_anim.h
index 12eb248cc0..9eb74c7550 100644
--- a/quantum/rgb_matrix_animations/solid_reactive_simple_anim.h
+++ b/quantum/rgb_matrix_animations/solid_reactive_simple_anim.h
@@ -1,5 +1,6 @@
 #ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
 #    ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
+#define RGB_MATRIX_EFFECT_SOLID_REACTIVE_SIMPLE
 RGB_MATRIX_EFFECT(SOLID_REACTIVE_SIMPLE)
 #        ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/rgb_matrix_animations/solid_reactive_wide.h b/quantum/rgb_matrix_animations/solid_reactive_wide.h
index 1cc4dca728..a0071bef66 100644
--- a/quantum/rgb_matrix_animations/solid_reactive_wide.h
+++ b/quantum/rgb_matrix_animations/solid_reactive_wide.h
@@ -2,10 +2,12 @@
 #    if !defined(DISABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE) || !defined(DISABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE)
 
 #        ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE
+#define RGB_MATRIX_EFFECT_SOLID_REACTIVE_WIDE
 RGB_MATRIX_EFFECT(SOLID_REACTIVE_WIDE)
 #        endif
 
 #        ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE
+#define RGB_MATRIX_EFFECT_SOLID_REACTIVE_MULTIWIDE
 RGB_MATRIX_EFFECT(SOLID_REACTIVE_MULTIWIDE)
 #        endif
 
diff --git a/quantum/rgb_matrix_animations/solid_splash_anim.h b/quantum/rgb_matrix_animations/solid_splash_anim.h
index 99efb4996a..c28c712634 100644
--- a/quantum/rgb_matrix_animations/solid_splash_anim.h
+++ b/quantum/rgb_matrix_animations/solid_splash_anim.h
@@ -2,10 +2,12 @@
 #    if !defined(DISABLE_RGB_MATRIX_SOLID_SPLASH) || !defined(DISABLE_RGB_MATRIX_SOLID_MULTISPLASH)
 
 #        ifndef DISABLE_RGB_MATRIX_SOLID_SPLASH
+#define RGB_MATRIX_EFFECT_SOLID_SPLASH
 RGB_MATRIX_EFFECT(SOLID_SPLASH)
 #        endif
 
 #        ifndef DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
+#define RGB_MATRIX_EFFECT_SOLID_MULTISPLASH
 RGB_MATRIX_EFFECT(SOLID_MULTISPLASH)
 #        endif
 
diff --git a/quantum/rgb_matrix_animations/splash_anim.h b/quantum/rgb_matrix_animations/splash_anim.h
index 1415bcc0fa..d2f92a62ea 100644
--- a/quantum/rgb_matrix_animations/splash_anim.h
+++ b/quantum/rgb_matrix_animations/splash_anim.h
@@ -2,10 +2,12 @@
 #    if !defined(DISABLE_RGB_MATRIX_SPLASH) || !defined(DISABLE_RGB_MATRIX_MULTISPLASH)
 
 #        ifndef DISABLE_RGB_MATRIX_SPLASH
+#define RGB_MATRIX_EFFECT_SPLASH
 RGB_MATRIX_EFFECT(SPLASH)
 #        endif
 
 #        ifndef DISABLE_RGB_MATRIX_MULTISPLASH
+#define RGB_MATRIX_EFFECT_MULTISPLASH
 RGB_MATRIX_EFFECT(MULTISPLASH)
 #        endif
 
diff --git a/quantum/rgb_matrix_animations/typing_heatmap_anim.h b/quantum/rgb_matrix_animations/typing_heatmap_anim.h
index e7dda11a2f..bd1a7b43fd 100644
--- a/quantum/rgb_matrix_animations/typing_heatmap_anim.h
+++ b/quantum/rgb_matrix_animations/typing_heatmap_anim.h
@@ -1,4 +1,5 @@
 #if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(DISABLE_RGB_MATRIX_TYPING_HEATMAP)
+#define RGB_MATRIX_EFFECT_TYPING_HEATMAP
 RGB_MATRIX_EFFECT(TYPING_HEATMAP)
 #    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
 
diff --git a/quantum/vialrgb.c b/quantum/vialrgb.c
index 5bec65282e..4216afa65a 100644
--- a/quantum/vialrgb.c
+++ b/quantum/vialrgb.c
@@ -8,63 +8,12 @@
 #include "rgb_matrix.h"
 #include "vial.h"
 
-/* Note - never reorder or remove these, only add new animations at the end */
-enum {
-    VIALRGB_EFFECT_OFF = 0,
-    VIALRGB_EFFECT_DIRECT,
-    VIALRGB_EFFECT_SOLID_COLOR,
-    VIALRGB_EFFECT_ALPHAS_MODS,
-    VIALRGB_EFFECT_GRADIENT_UP_DOWN_ANIM,
-    VIALRGB_EFFECT_GRADIENT_LEFT_RIGHT_ANIM,
-    VIALRGB_EFFECT_BREATHING_ANIM,
-    VIALRGB_EFFECT_COLORBAND_SAT_ANIM,
-    VIALRGB_EFFECT_COLORBAND_VAL_ANIM,
-    VIALRGB_EFFECT_COLORBAND_PINWHEEL_SAT_ANIM,
-    VIALRGB_EFFECT_COLORBAND_PINWHEEL_VAL_ANIM,
-    VIALRGB_EFFECT_COLORBAND_SPIRAL_SAT_ANIM,
-    VIALRGB_EFFECT_COLORBAND_SPIRAL_VAL_ANIM,
-    VIALRGB_EFFECT_CYCLE_ALL_ANIM,
-    VIALRGB_EFFECT_CYCLE_LEFT_RIGHT_ANIM,
-    VIALRGB_EFFECT_CYCLE_UP_DOWN_ANIM,
-    VIALRGB_EFFECT_RAINBOW_MOVING_CHEVRON_ANIM,
-    VIALRGB_EFFECT_CYCLE_OUT_IN_ANIM,
-    VIALRGB_EFFECT_CYCLE_OUT_IN_DUAL_ANIM,
-    VIALRGB_EFFECT_CYCLE_PINWHEEL_ANIM,
-    VIALRGB_EFFECT_CYCLE_SPIRAL_ANIM,
-    VIALRGB_EFFECT_DUAL_BEACON_ANIM,
-    VIALRGB_EFFECT_RAINBOW_BEACON_ANIM,
-    VIALRGB_EFFECT_RAINBOW_PINWHEELS_ANIM,
-    VIALRGB_EFFECT_RAINDROPS_ANIM,
-    VIALRGB_EFFECT_JELLYBEAN_RAINDROPS_ANIM,
-    VIALRGB_EFFECT_HUE_BREATHING_ANIM,
-    VIALRGB_EFFECT_HUE_PENDULUM_ANIM,
-    VIALRGB_EFFECT_HUE_WAVE_ANIM,
-    VIALRGB_EFFECT_TYPING_HEATMAP_ANIM,
-    VIALRGB_EFFECT_DIGITAL_RAIN_ANIM,
-    VIALRGB_EFFECT_SOLID_REACTIVE_SIMPLE_ANIM,
-    VIALRGB_EFFECT_SOLID_REACTIVE_ANIM,
-    VIALRGB_EFFECT_SOLID_REACTIVE_WIDE,
-    VIALRGB_EFFECT_SOLID_REACTIVE_CROSS,
-    VIALRGB_EFFECT_SOLID_REACTIVE_NEXUS,
-    VIALRGB_EFFECT_SPLASH_ANIM,
-    VIALRGB_EFFECT_SOLID_SPLASH_ANIM,
-};
-
 typedef struct {
     uint16_t vialrgb_id;
     uint16_t qmk_id;
 } vialrgb_supported_mode_t;
 
-static const PROGMEM vialrgb_supported_mode_t supported_modes[] = {
-    { VIALRGB_EFFECT_OFF, 0 },
-    // { VIALRGB_EFFECT_DIRECT, 0 }, TODO
-#ifdef RGB_MATRIX_EFFECT_SOLID_COLOR
-    { VIALRGB_EFFECT_SOLID_COLOR, RGB_MATRIX_SOLID_COLOR },
-#endif
-#ifdef RGB_MATRIX_EFFECT_ALPHAS_MODS
-    { VIALRGB_EFFECT_ALPHAS_MODS, RGB_MATRIX_ALPHAS_MODS },
-#endif
-};
+#include "vialrgb_effects.inc"
 
 #define SUPPORTED_MODES_LENGTH (sizeof(supported_modes)/sizeof(*supported_modes))
 
diff --git a/quantum/vialrgb_effects.inc b/quantum/vialrgb_effects.inc
new file mode 100644
index 0000000000..efe8f66de3
--- /dev/null
+++ b/quantum/vialrgb_effects.inc
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/* Note - never reorder or remove these, only add new animations at the end */
+enum {
+    VIALRGB_EFFECT_OFF = 0,
+    VIALRGB_EFFECT_DIRECT,
+    VIALRGB_EFFECT_SOLID_COLOR,
+    VIALRGB_EFFECT_ALPHAS_MODS,
+    VIALRGB_EFFECT_GRADIENT_UP_DOWN,
+    VIALRGB_EFFECT_GRADIENT_LEFT_RIGHT,
+    VIALRGB_EFFECT_BREATHING,
+    VIALRGB_EFFECT_BAND_SAT,
+    VIALRGB_EFFECT_BAND_VAL,
+    VIALRGB_EFFECT_BAND_PINWHEEL_SAT,
+    VIALRGB_EFFECT_BAND_PINWHEEL_VAL,
+    VIALRGB_EFFECT_BAND_SPIRAL_SAT,
+    VIALRGB_EFFECT_BAND_SPIRAL_VAL,
+    VIALRGB_EFFECT_CYCLE_ALL,
+    VIALRGB_EFFECT_CYCLE_LEFT_RIGHT,
+    VIALRGB_EFFECT_CYCLE_UP_DOWN,
+    VIALRGB_EFFECT_RAINBOW_MOVING_CHEVRON,
+    VIALRGB_EFFECT_CYCLE_OUT_IN,
+    VIALRGB_EFFECT_CYCLE_OUT_IN_DUAL,
+    VIALRGB_EFFECT_CYCLE_PINWHEEL,
+    VIALRGB_EFFECT_CYCLE_SPIRAL,
+    VIALRGB_EFFECT_DUAL_BEACON,
+    VIALRGB_EFFECT_RAINBOW_BEACON,
+    VIALRGB_EFFECT_RAINBOW_PINWHEELS,
+    VIALRGB_EFFECT_RAINDROPS,
+    VIALRGB_EFFECT_JELLYBEAN_RAINDROPS,
+    VIALRGB_EFFECT_HUE_BREATHING,
+    VIALRGB_EFFECT_HUE_PENDULUM,
+    VIALRGB_EFFECT_HUE_WAVE,
+    VIALRGB_EFFECT_TYPING_HEATMAP,
+    VIALRGB_EFFECT_DIGITAL_RAIN,
+    VIALRGB_EFFECT_SOLID_REACTIVE_SIMPLE,
+    VIALRGB_EFFECT_SOLID_REACTIVE,
+    VIALRGB_EFFECT_SOLID_REACTIVE_WIDE,
+    VIALRGB_EFFECT_SOLID_REACTIVE_MULTIWIDE,
+    VIALRGB_EFFECT_SOLID_REACTIVE_CROSS,
+    VIALRGB_EFFECT_SOLID_REACTIVE_MULTICROSS,
+    VIALRGB_EFFECT_SOLID_REACTIVE_NEXUS,
+    VIALRGB_EFFECT_SOLID_REACTIVE_MULTINEXUS,
+    VIALRGB_EFFECT_SPLASH,
+    VIALRGB_EFFECT_MULTISPLASH,
+    VIALRGB_EFFECT_SOLID_SPLASH,
+    VIALRGB_EFFECT_SOLID_MULTISPLASH,
+};
+
+static const PROGMEM vialrgb_supported_mode_t supported_modes[] = {
+    { VIALRGB_EFFECT_OFF, 0 },
+    // { VIALRGB_EFFECT_DIRECT, 0 }, TODO
+#ifdef RGB_MATRIX_EFFECT_SOLID_COLOR
+    { VIALRGB_EFFECT_SOLID_COLOR, RGB_MATRIX_SOLID_COLOR },
+#endif
+#ifdef RGB_MATRIX_EFFECT_ALPHAS_MODS
+    { VIALRGB_EFFECT_ALPHAS_MODS, RGB_MATRIX_ALPHAS_MODS },
+#endif
+#ifdef RGB_MATRIX_EFFECT_GRADIENT_UP_DOWN
+    { VIALRGB_EFFECT_GRADIENT_UP_DOWN, RGB_MATRIX_GRADIENT_UP_DOWN },
+#endif
+#ifdef RGB_MATRIX_EFFECT_GRADIENT_LEFT_RIGHT
+    { VIALRGB_EFFECT_GRADIENT_LEFT_RIGHT, RGB_MATRIX_GRADIENT_LEFT_RIGHT },
+#endif
+#ifdef RGB_MATRIX_EFFECT_BREATHING
+    { VIALRGB_EFFECT_BREATHING, RGB_MATRIX_BREATHING },
+#endif
+#ifdef RGB_MATRIX_EFFECT_BAND_SAT
+    { VIALRGB_EFFECT_BAND_SAT, RGB_MATRIX_BAND_SAT },
+#endif
+#ifdef RGB_MATRIX_EFFECT_BAND_VAL
+    { VIALRGB_EFFECT_BAND_VAL, RGB_MATRIX_BAND_VAL },
+#endif
+#ifdef RGB_MATRIX_EFFECT_BAND_PINWHEEL_SAT
+    { VIALRGB_EFFECT_BAND_PINWHEEL_SAT, RGB_MATRIX_BAND_PINWHEEL_SAT },
+#endif
+#ifdef RGB_MATRIX_EFFECT_BAND_PINWHEEL_VAL
+    { VIALRGB_EFFECT_BAND_PINWHEEL_VAL, RGB_MATRIX_BAND_PINWHEEL_VAL },
+#endif
+#ifdef RGB_MATRIX_EFFECT_BAND_SPIRAL_SAT
+    { VIALRGB_EFFECT_BAND_SPIRAL_SAT, RGB_MATRIX_BAND_SPIRAL_SAT },
+#endif
+#ifdef RGB_MATRIX_EFFECT_BAND_SPIRAL_VAL
+    { VIALRGB_EFFECT_BAND_SPIRAL_VAL, RGB_MATRIX_BAND_SPIRAL_VAL },
+#endif
+#ifdef RGB_MATRIX_EFFECT_CYCLE_ALL
+    { VIALRGB_EFFECT_CYCLE_ALL, RGB_MATRIX_CYCLE_ALL },
+#endif
+#ifdef RGB_MATRIX_EFFECT_CYCLE_LEFT_RIGHT
+    { VIALRGB_EFFECT_CYCLE_LEFT_RIGHT, RGB_MATRIX_CYCLE_LEFT_RIGHT },
+#endif
+#ifdef RGB_MATRIX_EFFECT_CYCLE_UP_DOWN
+    { VIALRGB_EFFECT_CYCLE_UP_DOWN, RGB_MATRIX_CYCLE_UP_DOWN },
+#endif
+#ifdef RGB_MATRIX_EFFECT_RAINBOW_MOVING_CHEVRON
+    { VIALRGB_EFFECT_RAINBOW_MOVING_CHEVRON, RGB_MATRIX_RAINBOW_MOVING_CHEVRON },
+#endif
+#ifdef RGB_MATRIX_EFFECT_CYCLE_OUT_IN
+    { VIALRGB_EFFECT_CYCLE_OUT_IN, RGB_MATRIX_CYCLE_OUT_IN },
+#endif
+#ifdef RGB_MATRIX_EFFECT_CYCLE_OUT_IN_DUAL
+    { VIALRGB_EFFECT_CYCLE_OUT_IN_DUAL, RGB_MATRIX_CYCLE_OUT_IN_DUAL },
+#endif
+#ifdef RGB_MATRIX_EFFECT_CYCLE_PINWHEEL
+    { VIALRGB_EFFECT_CYCLE_PINWHEEL, RGB_MATRIX_CYCLE_PINWHEEL },
+#endif
+#ifdef RGB_MATRIX_EFFECT_CYCLE_SPIRAL
+    { VIALRGB_EFFECT_CYCLE_SPIRAL, RGB_MATRIX_CYCLE_SPIRAL },
+#endif
+#ifdef RGB_MATRIX_EFFECT_DUAL_BEACON
+    { VIALRGB_EFFECT_DUAL_BEACON, RGB_MATRIX_DUAL_BEACON },
+#endif
+#ifdef RGB_MATRIX_EFFECT_RAINBOW_BEACON
+    { VIALRGB_EFFECT_RAINBOW_BEACON, RGB_MATRIX_RAINBOW_BEACON },
+#endif
+#ifdef RGB_MATRIX_EFFECT_RAINBOW_PINWHEELS
+    { VIALRGB_EFFECT_RAINBOW_PINWHEELS, RGB_MATRIX_RAINBOW_PINWHEELS },
+#endif
+#ifdef RGB_MATRIX_EFFECT_RAINDROPS
+    { VIALRGB_EFFECT_RAINDROPS, RGB_MATRIX_RAINDROPS },
+#endif
+#ifdef RGB_MATRIX_EFFECT_JELLYBEAN_RAINDROPS
+    { VIALRGB_EFFECT_JELLYBEAN_RAINDROPS, RGB_MATRIX_JELLYBEAN_RAINDROPS },
+#endif
+#ifdef RGB_MATRIX_EFFECT_HUE_BREATHING
+    { VIALRGB_EFFECT_HUE_BREATHING, RGB_MATRIX_HUE_BREATHING },
+#endif
+#ifdef RGB_MATRIX_EFFECT_HUE_PENDULUM
+    { VIALRGB_EFFECT_HUE_PENDULUM, RGB_MATRIX_HUE_PENDULUM },
+#endif
+#ifdef RGB_MATRIX_EFFECT_HUE_WAVE
+    { VIALRGB_EFFECT_HUE_WAVE, RGB_MATRIX_HUE_WAVE },
+#endif
+#ifdef RGB_MATRIX_EFFECT_TYPING_HEATMAP
+    { VIALRGB_EFFECT_TYPING_HEATMAP, RGB_MATRIX_TYPING_HEATMAP },
+#endif
+#ifdef RGB_MATRIX_EFFECT_DIGITAL_RAIN
+    { VIALRGB_EFFECT_DIGITAL_RAIN, RGB_MATRIX_DIGITAL_RAIN },
+#endif
+#ifdef RGB_MATRIX_EFFECT_SOLID_REACTIVE_SIMPLE
+    { VIALRGB_EFFECT_SOLID_REACTIVE_SIMPLE, RGB_MATRIX_SOLID_REACTIVE_SIMPLE },
+#endif
+#ifdef RGB_MATRIX_EFFECT_SOLID_REACTIVE
+    { VIALRGB_EFFECT_SOLID_REACTIVE, RGB_MATRIX_SOLID_REACTIVE },
+#endif
+#ifdef RGB_MATRIX_EFFECT_SOLID_REACTIVE_WIDE
+    { VIALRGB_EFFECT_SOLID_REACTIVE_WIDE, RGB_MATRIX_SOLID_REACTIVE_WIDE },
+#endif
+#ifdef RGB_MATRIX_EFFECT_SOLID_REACTIVE_MULTIWIDE
+    { VIALRGB_EFFECT_SOLID_REACTIVE_MULTIWIDE, RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE },
+#endif
+#ifdef RGB_MATRIX_EFFECT_SOLID_REACTIVE_CROSS
+    { VIALRGB_EFFECT_SOLID_REACTIVE_CROSS, RGB_MATRIX_SOLID_REACTIVE_CROSS },
+#endif
+#ifdef RGB_MATRIX_EFFECT_SOLID_REACTIVE_MULTICROSS
+    { VIALRGB_EFFECT_SOLID_REACTIVE_MULTICROSS, RGB_MATRIX_SOLID_REACTIVE_MULTICROSS },
+#endif
+#ifdef RGB_MATRIX_EFFECT_SOLID_REACTIVE_NEXUS
+    { VIALRGB_EFFECT_SOLID_REACTIVE_NEXUS, RGB_MATRIX_SOLID_REACTIVE_NEXUS },
+#endif
+#ifdef RGB_MATRIX_EFFECT_SOLID_REACTIVE_MULTINEXUS
+    { VIALRGB_EFFECT_SOLID_REACTIVE_MULTINEXUS, RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS },
+#endif
+#ifdef RGB_MATRIX_EFFECT_SPLASH
+    { VIALRGB_EFFECT_SPLASH, RGB_MATRIX_SPLASH },
+#endif
+#ifdef RGB_MATRIX_EFFECT_MULTISPLASH
+    { VIALRGB_EFFECT_MULTISPLASH, RGB_MATRIX_MULTISPLASH },
+#endif
+#ifdef RGB_MATRIX_EFFECT_SOLID_SPLASH
+    { VIALRGB_EFFECT_SOLID_SPLASH, RGB_MATRIX_SOLID_SPLASH },
+#endif
+#ifdef RGB_MATRIX_EFFECT_SOLID_MULTISPLASH
+    { VIALRGB_EFFECT_SOLID_MULTISPLASH, RGB_MATRIX_SOLID_MULTISPLASH },
+#endif
+};

From fa77ed964ecc9331702675bf370b8cbd18beb0af Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Fri, 9 Jul 2021 18:29:35 -0400
Subject: [PATCH 31/44] vialrgb: switch to 16-bit rgb mode for future proofing

---
 quantum/vialrgb.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/quantum/vialrgb.c b/quantum/vialrgb.c
index 4216afa65a..66bd3c1240 100644
--- a/quantum/vialrgb.c
+++ b/quantum/vialrgb.c
@@ -65,11 +65,13 @@ void vialrgb_get_value(uint8_t *data, uint8_t length) {
         args[2] = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
         break;
     case vialrgb_get_mode: {
-        args[0] = qmk_id_to_vialrgb_id(rgb_matrix_get_mode());
-        args[1] = rgb_matrix_get_speed();
-        args[2] = rgb_matrix_get_hue();
-        args[3] = rgb_matrix_get_sat();
-        args[4] = rgb_matrix_get_val();
+        uint16_t vialrgb_id = qmk_id_to_vialrgb_id(rgb_matrix_get_mode());
+        args[0] = vialrgb_id & 0xFF;
+        args[1] = vialrgb_id >> 8;
+        args[2] = rgb_matrix_get_speed();
+        args[3] = rgb_matrix_get_hue();
+        args[4] = rgb_matrix_get_sat();
+        args[5] = rgb_matrix_get_val();
         break;
     }
     case vialrgb_get_supported: {
@@ -87,9 +89,10 @@ void vialrgb_set_value(uint8_t *data, uint8_t length) {
     uint8_t *args = &data[2];
     switch (cmd) {
     case vialrgb_set_mode: {
-        rgb_matrix_mode_noeeprom(vialrgb_id_to_qmk_id(args[0]));
-        rgb_matrix_set_speed_noeeprom(args[1]);
-        rgb_matrix_sethsv_noeeprom(args[2], args[3], args[4]);
+        uint16_t vialrgb_id = args[0] | (args[1] << 8);
+        rgb_matrix_mode_noeeprom(vialrgb_id_to_qmk_id(vialrgb_id));
+        rgb_matrix_set_speed_noeeprom(args[2]);
+        rgb_matrix_sethsv_noeeprom(args[3], args[4], args[5]);
         break;
     }
     }

From b20bf26c6bce28cebf39ab732e6bacf0f4f787cc Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Fri, 9 Jul 2021 19:57:33 -0400
Subject: [PATCH 32/44] vialrgb: handle the disable mode

---
 quantum/vialrgb.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/quantum/vialrgb.c b/quantum/vialrgb.c
index 66bd3c1240..e8789fc310 100644
--- a/quantum/vialrgb.c
+++ b/quantum/vialrgb.c
@@ -52,6 +52,23 @@ static uint16_t vialrgb_id_to_qmk_id(uint16_t id) {
     return 0;
 }
 
+static uint16_t get_mode(void) {
+    /* Get current mode as vialrgb ID */
+    if (!rgb_matrix_is_enabled())
+        return VIALRGB_EFFECT_OFF;
+    return qmk_id_to_vialrgb_id(rgb_matrix_get_mode());
+}
+
+static void set_mode(uint16_t mode) {
+    /* Set a mode as vialrgb ID */
+    if (mode == VIALRGB_EFFECT_OFF) {
+        rgb_matrix_disable_noeeprom();
+    } else {
+        rgb_matrix_enable_noeeprom();
+        rgb_matrix_mode_noeeprom(vialrgb_id_to_qmk_id(mode));
+    }
+}
+
 void vialrgb_get_value(uint8_t *data, uint8_t length) {
     if (length != VIAL_RAW_EPSIZE) return;
 
@@ -65,7 +82,7 @@ void vialrgb_get_value(uint8_t *data, uint8_t length) {
         args[2] = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
         break;
     case vialrgb_get_mode: {
-        uint16_t vialrgb_id = qmk_id_to_vialrgb_id(rgb_matrix_get_mode());
+        uint16_t vialrgb_id = get_mode();
         args[0] = vialrgb_id & 0xFF;
         args[1] = vialrgb_id >> 8;
         args[2] = rgb_matrix_get_speed();
@@ -90,7 +107,7 @@ void vialrgb_set_value(uint8_t *data, uint8_t length) {
     switch (cmd) {
     case vialrgb_set_mode: {
         uint16_t vialrgb_id = args[0] | (args[1] << 8);
-        rgb_matrix_mode_noeeprom(vialrgb_id_to_qmk_id(vialrgb_id));
+        set_mode(vialrgb_id);
         rgb_matrix_set_speed_noeeprom(args[2]);
         rgb_matrix_sethsv_noeeprom(args[3], args[4], args[5]);
         break;

From 83d13b44c22f33463ad00cb93466008657e030d6 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Fri, 9 Jul 2021 22:11:50 -0400
Subject: [PATCH 33/44] vialrgb: support direct LED control

---
 .../rgb_matrix_effects.inc                    |  1 +
 .../vialrgb_direct_anim.h                     | 18 ++++++
 quantum/vialrgb.c                             | 58 +++++++++++++++++++
 quantum/vialrgb.h                             |  3 +
 quantum/vialrgb_effects.inc                   |  4 +-
 5 files changed, 83 insertions(+), 1 deletion(-)
 create mode 100644 quantum/rgb_matrix_animations/vialrgb_direct_anim.h

diff --git a/quantum/rgb_matrix_animations/rgb_matrix_effects.inc b/quantum/rgb_matrix_animations/rgb_matrix_effects.inc
index 053d441506..0e9b525de6 100644
--- a/quantum/rgb_matrix_animations/rgb_matrix_effects.inc
+++ b/quantum/rgb_matrix_animations/rgb_matrix_effects.inc
@@ -35,3 +35,4 @@
 #include "rgb_matrix_animations/solid_reactive_nexus.h"
 #include "rgb_matrix_animations/splash_anim.h"
 #include "rgb_matrix_animations/solid_splash_anim.h"
+#include "rgb_matrix_animations/vialrgb_direct_anim.h"
diff --git a/quantum/rgb_matrix_animations/vialrgb_direct_anim.h b/quantum/rgb_matrix_animations/vialrgb_direct_anim.h
new file mode 100644
index 0000000000..42fc9c7d83
--- /dev/null
+++ b/quantum/rgb_matrix_animations/vialrgb_direct_anim.h
@@ -0,0 +1,18 @@
+#if defined(VIALRGB_ENABLE) && !defined(VIALRGB_NO_DIRECT)
+#define RGB_MATRIX_EFFECT_VIALRGB_DIRECT
+RGB_MATRIX_EFFECT(VIALRGB_DIRECT)
+#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
+
+extern HSV g_direct_mode_colors[DRIVER_LED_TOTAL];
+
+bool VIALRGB_DIRECT(effect_params_t* params) {
+    RGB_MATRIX_USE_LIMITS(led_min, led_max);
+
+    for (uint8_t i = led_min; i < led_max; i++) {
+        RGB rgb = rgb_matrix_hsv_to_rgb(g_direct_mode_colors[i]);
+        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+    }
+    return led_max < DRIVER_LED_TOTAL;
+}
+#    endif
+#endif
diff --git a/quantum/vialrgb.c b/quantum/vialrgb.c
index e8789fc310..0efa56b283 100644
--- a/quantum/vialrgb.c
+++ b/quantum/vialrgb.c
@@ -17,6 +17,8 @@ typedef struct {
 
 #define SUPPORTED_MODES_LENGTH (sizeof(supported_modes)/sizeof(*supported_modes))
 
+HSV g_direct_mode_colors[DRIVER_LED_TOTAL];
+
 static void get_supported(uint8_t *args, uint8_t length) {
     /* retrieve supported effects (VialRGB IDs) with ID > gt */
     uint16_t gt;
@@ -69,6 +71,41 @@ static void set_mode(uint16_t mode) {
     }
 }
 
+static void get_matrix_pos_for_led(uint16_t led, uint8_t *output) {
+    /* reset initially so if we cannot locate the led, it's considered not part of kb matrix */
+    output[0] = output[1] = 0xFF;
+    /* this is kinda O(n^2) but what can you do */
+    for (size_t row = 0; row < MATRIX_ROWS; ++row)
+        for (size_t col = 0; col < MATRIX_COLS; ++col)
+            if (g_led_config.matrix_co[row][col] == led) {
+                output[0] = row;
+                output[1] = col;
+                return;
+            }
+}
+
+static void fast_set_leds(uint8_t *args, size_t length) {
+    /* Set multiple leds HSV, first 2 bytes are index of the first led, then number of leds, followed by HSV for the leds */
+    /* we have 32-2-2-1=27 total bytes available, so can set up to 9 leds per packet */
+    if (length < 3) return;
+
+    uint16_t first_index = args[0] | (args[1] << 8);
+    uint8_t num_leds = args[2];
+    length -= 3;
+    args += 3;
+
+    if (num_leds * 3 > length) return;
+
+    for (size_t i = 0; i < num_leds; ++i) {
+        if (i + first_index >= DRIVER_LED_TOTAL)
+            break;
+        g_direct_mode_colors[i + first_index].h = args[i * 3 + 0];
+        g_direct_mode_colors[i + first_index].s = args[i * 3 + 1];
+        uint8_t val = args[i * 3 + 2];
+        g_direct_mode_colors[i + first_index].v = (val > RGB_MATRIX_MAXIMUM_BRIGHTNESS) ? RGB_MATRIX_MAXIMUM_BRIGHTNESS : val;
+    }
+}
+
 void vialrgb_get_value(uint8_t *data, uint8_t length) {
     if (length != VIAL_RAW_EPSIZE) return;
 
@@ -95,6 +132,23 @@ void vialrgb_get_value(uint8_t *data, uint8_t length) {
         get_supported(args, length - 2);
         break;
     }
+    case vialrgb_get_number_leds: {
+        args[0] = DRIVER_LED_TOTAL & 0xFF;
+        args[1] = DRIVER_LED_TOTAL >> 8;
+        break;
+    }
+    case vialrgb_get_led_info: {
+        uint16_t led = (args[0] & 0xFF) | (args[1] >> 8);
+        if (led >= DRIVER_LED_TOTAL) return;
+        // x, y
+        args[0] = g_led_config.point[led].x;
+        args[1] = g_led_config.point[led].y;
+        // flags
+        args[2] = g_led_config.flags[led];
+        // position in keyboard matrix (if it's keyboard LED, otherwise 0xFF)
+        get_matrix_pos_for_led(led, &args[3]);
+        break;
+    }
     }
 }
 
@@ -112,6 +166,10 @@ void vialrgb_set_value(uint8_t *data, uint8_t length) {
         rgb_matrix_sethsv_noeeprom(args[3], args[4], args[5]);
         break;
     }
+    case vialrgb_direct_fastset: {
+        fast_set_leds(args, length);
+        break;
+    }
     }
 }
 
diff --git a/quantum/vialrgb.h b/quantum/vialrgb.h
index 170d110c34..486a8aa2f7 100644
--- a/quantum/vialrgb.h
+++ b/quantum/vialrgb.h
@@ -8,12 +8,15 @@
    even though they likely wouldn't be enabled together with vialrgb */
 enum {
     vialrgb_set_mode = 0x41,
+    vialrgb_direct_fastset = 0x42,
 };
 
 enum {
     vialrgb_get_info = 0x40,
     vialrgb_get_mode = 0x41,
     vialrgb_get_supported = 0x42,
+    vialrgb_get_number_leds = 0x43,
+    vialrgb_get_led_info = 0x44,
 };
 
 void vialrgb_get_value(uint8_t *data, uint8_t length);
diff --git a/quantum/vialrgb_effects.inc b/quantum/vialrgb_effects.inc
index efe8f66de3..1d21ce3813 100644
--- a/quantum/vialrgb_effects.inc
+++ b/quantum/vialrgb_effects.inc
@@ -49,7 +49,9 @@ enum {
 
 static const PROGMEM vialrgb_supported_mode_t supported_modes[] = {
     { VIALRGB_EFFECT_OFF, 0 },
-    // { VIALRGB_EFFECT_DIRECT, 0 }, TODO
+#ifdef RGB_MATRIX_EFFECT_VIALRGB_DIRECT
+    { VIALRGB_EFFECT_DIRECT, RGB_MATRIX_VIALRGB_DIRECT },
+#endif
 #ifdef RGB_MATRIX_EFFECT_SOLID_COLOR
     { VIALRGB_EFFECT_SOLID_COLOR, RGB_MATRIX_SOLID_COLOR },
 #endif

From bec9d622a15af1f479645a6209e1aa4e52047a75 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Fri, 9 Jul 2021 22:16:31 -0400
Subject: [PATCH 34/44] vialrgb: save flash/memory when direct control is
 disabled

---
 quantum/vialrgb.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/quantum/vialrgb.c b/quantum/vialrgb.c
index 0efa56b283..44d94c5276 100644
--- a/quantum/vialrgb.c
+++ b/quantum/vialrgb.c
@@ -17,7 +17,9 @@ typedef struct {
 
 #define SUPPORTED_MODES_LENGTH (sizeof(supported_modes)/sizeof(*supported_modes))
 
+#ifdef RGB_MATRIX_EFFECT_VIALRGB_DIRECT
 HSV g_direct_mode_colors[DRIVER_LED_TOTAL];
+#endif
 
 static void get_supported(uint8_t *args, uint8_t length) {
     /* retrieve supported effects (VialRGB IDs) with ID > gt */
@@ -71,6 +73,7 @@ static void set_mode(uint16_t mode) {
     }
 }
 
+#ifdef RGB_MATRIX_EFFECT_VIALRGB_DIRECT
 static void get_matrix_pos_for_led(uint16_t led, uint8_t *output) {
     /* reset initially so if we cannot locate the led, it's considered not part of kb matrix */
     output[0] = output[1] = 0xFF;
@@ -105,6 +108,7 @@ static void fast_set_leds(uint8_t *args, size_t length) {
         g_direct_mode_colors[i + first_index].v = (val > RGB_MATRIX_MAXIMUM_BRIGHTNESS) ? RGB_MATRIX_MAXIMUM_BRIGHTNESS : val;
     }
 }
+#endif
 
 void vialrgb_get_value(uint8_t *data, uint8_t length) {
     if (length != VIAL_RAW_EPSIZE) return;
@@ -132,6 +136,7 @@ void vialrgb_get_value(uint8_t *data, uint8_t length) {
         get_supported(args, length - 2);
         break;
     }
+#ifdef RGB_MATRIX_EFFECT_VIALRGB_DIRECT
     case vialrgb_get_number_leds: {
         args[0] = DRIVER_LED_TOTAL & 0xFF;
         args[1] = DRIVER_LED_TOTAL >> 8;
@@ -149,6 +154,7 @@ void vialrgb_get_value(uint8_t *data, uint8_t length) {
         get_matrix_pos_for_led(led, &args[3]);
         break;
     }
+#endif
     }
 }
 
@@ -166,10 +172,12 @@ void vialrgb_set_value(uint8_t *data, uint8_t length) {
         rgb_matrix_sethsv_noeeprom(args[3], args[4], args[5]);
         break;
     }
+#ifdef RGB_MATRIX_EFFECT_VIALRGB_DIRECT
     case vialrgb_direct_fastset: {
         fast_set_leds(args, length);
         break;
     }
+#endif
     }
 }
 

From cdf16e79a377498b0aa384b4d149207e36042c18 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Fri, 9 Jul 2021 22:34:41 -0400
Subject: [PATCH 35/44] qmk_settings: default to 10ms tap code delay

---
 quantum/qmk_settings.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index 1c7e8704d6..f1086b637f 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -6,7 +6,7 @@
 /* take qmk config macros and set up helper variables for default settings */
 
 #ifndef TAP_CODE_DELAY
-#    define TAP_CODE_DELAY 0
+#    define TAP_CODE_DELAY 10
 #endif
 #ifndef TAP_HOLD_CAPS_DELAY
 #    define TAP_HOLD_CAPS_DELAY 80

From ae1d581ca7817464a7c7eeebed7ee2b38c952c43 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 11 Jul 2021 14:44:46 -0400
Subject: [PATCH 36/44] eeprom_stm32: implement denser emulation, default to 4k

---
 quantum/dynamic_keymap.c               |  6 +++
 tmk_core/common/chibios/eeprom_stm32.c | 61 ++++++++++++++------------
 tmk_core/common/chibios/eeprom_stm32.h | 42 +++++++++++-------
 3 files changed, 65 insertions(+), 44 deletions(-)

diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c
index 2fb37cf634..c9a5cc793c 100644
--- a/quantum/dynamic_keymap.c
+++ b/quantum/dynamic_keymap.c
@@ -22,6 +22,10 @@
 #include "dynamic_keymap.h"
 #include "via.h"  // for default VIA_EEPROM_ADDR_END
 
+#ifdef STM32_EEPROM_ENABLE
+#    include "eeprom_stm32.h"
+#endif
+
 #ifdef VIAL_ENABLE
 #include "vial.h"
 #endif
@@ -41,6 +45,8 @@
 #        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 4095
 #    elif defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATtiny85__)
 #        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 511
+#    elif defined(FEE_DENSITY_BYTES)
+#        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR FEE_DENSITY_BYTES-1
 #    else
 #        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 1023
 #    endif
diff --git a/tmk_core/common/chibios/eeprom_stm32.c b/tmk_core/common/chibios/eeprom_stm32.c
index e63a5c6f57..2b0c78f714 100644
--- a/tmk_core/common/chibios/eeprom_stm32.c
+++ b/tmk_core/common/chibios/eeprom_stm32.c
@@ -21,18 +21,24 @@
 #include <string.h>
 #include "eeprom_stm32.h"
 
-/* In-memory contents of emulated eeprom for faster access */
+#define SNAPSHOT_START (FEE_BASE_ADDRESS)
+#define SNAPSHOT_END (FEE_BASE_ADDRESS+FEE_SNAPSHOT_SIZE)
+#define WRITELOG_START (SNAPSHOT_END)
+#define WRITELOG_END (WRITELOG_START+FEE_WRITELOG_SIZE)
+
+/* In-memory contents of emulated eeprom for direct faster access */
 static uint8_t DataBuf[FEE_DENSITY_BYTES];
 
-/* Pointer to the first available slot within flash area */
+/* Pointer to the first available slot within the writelog */
 static uint8_t *empty_slot;
 
 void EEPROM_Init(void) {
-    memset(DataBuf, 0, sizeof(DataBuf));
+    /* First, load the snapshot directly from flash */
+    memcpy(DataBuf, (void*)FEE_BASE_ADDRESS, FEE_SNAPSHOT_SIZE);
 
-    /* Load emulated eeprom contents from flash into memory */
+    /* Then, process writelog to update DataBuf entries */
     uint8_t *addr;
-    for (addr = (uint8_t*)FEE_PAGE_BASE_ADDRESS; addr < (uint8_t*)FEE_LAST_PAGE_ADDRESS; addr += 4) {
+    for (addr = (uint8_t*)WRITELOG_START; addr < (uint8_t*)WRITELOG_END; addr += 4) {
         uint16_t address;
         uint8_t value;
         memcpy(&address, addr, sizeof(address));
@@ -46,16 +52,16 @@ void EEPROM_Init(void) {
     empty_slot = addr;
 }
 
-/* Clear flash contents (doesn't touch in-memory DataBuf) */
+/* Erase flash contents so we can put updated data in (doesn't touch in-memory DataBuf) */
 static void eeprom_clear(void) {
     FLASH_Unlock();
 
-    for (uint32_t page_num = 0; page_num < FEE_DENSITY_PAGES; ++page_num)
-        FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE));
+    for (uint32_t erase_address = SNAPSHOT_START; erase_address < WRITELOG_END; erase_address += FEE_PAGE_SIZE)
+        FLASH_ErasePage(erase_address);
 
     FLASH_Lock();
 
-    empty_slot = (void*)FEE_PAGE_BASE_ADDRESS;
+    empty_slot = (void*)WRITELOG_START;
 }
 
 /* Erase emulated eeprom */
@@ -67,29 +73,28 @@ void EEPROM_Erase(void) {
 
 static void eeprom_writedatabyte(uint16_t Address, uint8_t DataByte);
 
-/* Dump in-memory contents into flash */
-static void eeprom_restore(void) {
-    for (uint32_t i = 0; i < FEE_DENSITY_BYTES; ++i) {
-        /* don't bother writing zeroes */
-        if (DataBuf[i]) {
-            eeprom_writedatabyte(i, DataBuf[i]);
-        }
+/* Dump in-memory eeprom contents into the snapshot area */
+static void eeprom_write_snapshot(void) {
+    FLASH_Unlock();
+
+    for (uint32_t i = 0; i < FEE_DENSITY_BYTES; i += 2) {
+        uint16_t halfword;
+        memcpy(&halfword, &DataBuf[i], sizeof(halfword));
+
+        FLASH_ProgramHalfWord(SNAPSHOT_START + i, halfword);
     }
+
+    FLASH_Lock();
 }
 
 static void eeprom_writedatabyte(uint16_t Address, uint8_t DataByte) {
     /* if couldn't find an empty spot, we must re-initialize emulated eeprom */
-    if (empty_slot >= (uint8_t*)FEE_LAST_PAGE_ADDRESS) {
-        /* ensure that the following call to eeprom_restore will write our desired byte value */
-        DataBuf[Address] = DataByte;
-
+    if (empty_slot >= (uint8_t*)WRITELOG_END) {
         /* fully erase emulated eeprom */
         eeprom_clear();
 
         /* and then write DataBuf contents back into flash */
-        eeprom_restore();
-
-        /* don't need to do anything else as eeprom_restore already wrote our value */
+        eeprom_write_snapshot();
         return;
     }
 
@@ -106,7 +111,7 @@ static void eeprom_writedatabyte(uint16_t Address, uint8_t DataByte) {
     empty_slot += 4;
 }
 
-void EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
+static void EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
     /* if the address is out-of-bounds, do nothing */
     if (Address >= FEE_DENSITY_BYTES)
         return;
@@ -115,14 +120,14 @@ void EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
     if (DataBuf[Address] == DataByte)
         return;
 
-    /* perform the write into flash memory */
-    eeprom_writedatabyte(Address, DataByte);
-
     /* keep DataBuf cache in sync */
     DataBuf[Address] = DataByte;
+
+    /* perform the write into flash memory */
+    eeprom_writedatabyte(Address, DataByte);
 }
 
-uint8_t EEPROM_ReadDataByte(uint16_t Address) {
+static uint8_t EEPROM_ReadDataByte(uint16_t Address) {
     uint8_t DataByte = 0x00;
 
     if (Address < FEE_DENSITY_BYTES)
diff --git a/tmk_core/common/chibios/eeprom_stm32.h b/tmk_core/common/chibios/eeprom_stm32.h
index a9b2dc2e50..4fefd29f30 100644
--- a/tmk_core/common/chibios/eeprom_stm32.h
+++ b/tmk_core/common/chibios/eeprom_stm32.h
@@ -37,11 +37,10 @@
 #    define MCU_STM32F072CB
 #elif defined(EEPROM_EMU_STM32F042x6)
 #    define MCU_STM32F042K6
-#else
-#    error "not implemented."
 #endif
 
-#ifndef EEPROM_PAGE_SIZE
+/* The page_size * density_pages should provide 8k of space, split 4k/4k between snapshot and writelog in the default config */
+#ifndef FEE_DENSITY_PAGES
 #    if defined(MCU_STM32F103RB) || defined(MCU_STM32F042K6)
 #        define FEE_PAGE_SIZE (uint16_t)0x400  // Page size = 1KByte
 #        define FEE_DENSITY_PAGES 8            // How many pages are used
@@ -49,11 +48,13 @@
 #        define FEE_PAGE_SIZE (uint16_t)0x800  // Page size = 2KByte
 #        define FEE_DENSITY_PAGES 4            // How many pages are used
 #    else
-#        error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)."
+#        error "No MCU type specified and FEE_DENSITY_PAGES not defined.\
+Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)\
+or define FEE_DENSITY_PAGES yourself."
 #    endif
 #endif
 
-#ifndef EEPROM_START_ADDRESS
+#ifndef FEE_MCU_FLASH_SIZE
 #    if defined(MCU_STM32F103RB) || defined(MCU_STM32F072CB)
 #        define FEE_MCU_FLASH_SIZE 128  // Size in Kb
 #    elif defined(MCU_STM32F042K6)
@@ -65,23 +66,32 @@
 #    elif defined(MCU_STM32F303CC)
 #        define FEE_MCU_FLASH_SIZE 256  // Size in Kb
 #    else
-#        error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)."
+#        error "No MCU type specified and FEE_MCU_FLASH_SIZE not defined.\
+Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)\
+or define FEE_MCU_FLASH_SIZE yourself."
 #    endif
 #endif
 
-/* Start of the emulated eeprom flash area */
-#define FEE_PAGE_BASE_ADDRESS ((uint32_t)(0x8000000 + FEE_MCU_FLASH_SIZE * 1024 - FEE_DENSITY_PAGES * FEE_PAGE_SIZE))
-/* End of the emulated eeprom flash area */
-#define FEE_LAST_PAGE_ADDRESS (FEE_PAGE_BASE_ADDRESS + (FEE_PAGE_SIZE * FEE_DENSITY_PAGES))
-/* Size of emulated eeprom */
-#define FEE_DENSITY_BYTES 1024
+#ifndef FEE_BASE_ADDRESS
+    /* Start of the emulated eeprom flash area, place it at the end of the flash memory */
+    #define FEE_BASE_ADDRESS ((uint32_t)(0x8000000 + FEE_MCU_FLASH_SIZE * 1024 - FEE_DENSITY_PAGES * FEE_PAGE_SIZE))
+#endif
+
+#ifndef FEE_SNAPSHOT_SIZE
+    /* Size of eeprom snapshot, in bytes. This is equal to emulated eeprom size. */
+    #define FEE_SNAPSHOT_SIZE 4096
+#endif
+
+#ifndef FEE_WRITELOG_SIZE
+    /* Size of eeprom writelog, in bytes */
+    #define FEE_WRITELOG_SIZE 4096
+#endif
+
 /* Flash word value after erase */
 #define FEE_EMPTY_WORD ((uint16_t)0xFFFF)
 
-_Static_assert(FEE_DENSITY_PAGES * FEE_PAGE_SIZE >= FEE_DENSITY_BYTES * 8,
-    "flash memory for emulated eeprom is too small; for correct functionality ensure it is at least 8x FEE_DENSITY_BYTES");
+/* Size of emulated eeprom */
+#define FEE_DENSITY_BYTES FEE_SNAPSHOT_SIZE
 
 void     EEPROM_Init(void);
 void     EEPROM_Erase(void);
-void     EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte);
-uint8_t  EEPROM_ReadDataByte(uint16_t Address);

From 1ba3126ae43915b712c759285c5e37f736159989 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 11 Jul 2021 19:20:41 -0400
Subject: [PATCH 37/44] eeprom_stm32: add stm32f4 series support

based on code by @yulei in https://github.com/qmk/qmk_firmware/pull/7803
---
 common_features.mk                     |  5 +++
 tmk_core/common/chibios/eeprom_stm32.c |  4 ++
 tmk_core/common/chibios/eeprom_stm32.h | 12 +++++-
 tmk_core/common/chibios/flash_stm32.c  | 60 ++++++++++++++++----------
 tmk_core/common/chibios/flash_stm32.h  | 20 +++++++++
 5 files changed, 76 insertions(+), 25 deletions(-)

diff --git a/common_features.mk b/common_features.mk
index 26eaf4829b..5ec5c2e96c 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -171,6 +171,11 @@ else
         OPT_DEFS += -DEEPROM_DRIVER
         COMMON_VPATH += $(DRIVER_PATH)/eeprom
         SRC += eeprom_driver.c eeprom_stm32_L0_L1.c
+      else ifeq ($(MCU_SERIES), STM32F4xx)
+        SRC += $(PLATFORM_COMMON_DIR)/eeprom_stm32.c
+        SRC += $(PLATFORM_COMMON_DIR)/flash_stm32.c
+        OPT_DEFS += -DEEPROM_EMU_STM32F411xC
+        OPT_DEFS += -DSTM32_EEPROM_ENABLE
       else
         # This will effectively work the same as "transient" if not supported by the chip
         SRC += $(PLATFORM_COMMON_DIR)/eeprom_teensy.c
diff --git a/tmk_core/common/chibios/eeprom_stm32.c b/tmk_core/common/chibios/eeprom_stm32.c
index 2b0c78f714..056f886c6f 100644
--- a/tmk_core/common/chibios/eeprom_stm32.c
+++ b/tmk_core/common/chibios/eeprom_stm32.c
@@ -56,8 +56,12 @@ void EEPROM_Init(void) {
 static void eeprom_clear(void) {
     FLASH_Unlock();
 
+#if defined(EEPROM_EMU_STM32F4)
+    FLASH_ErasePage(FEE_SECTOR_ID);
+#else
     for (uint32_t erase_address = SNAPSHOT_START; erase_address < WRITELOG_END; erase_address += FEE_PAGE_SIZE)
         FLASH_ErasePage(erase_address);
+#endif
 
     FLASH_Lock();
 
diff --git a/tmk_core/common/chibios/eeprom_stm32.h b/tmk_core/common/chibios/eeprom_stm32.h
index 4fefd29f30..4a15ee7cc2 100644
--- a/tmk_core/common/chibios/eeprom_stm32.h
+++ b/tmk_core/common/chibios/eeprom_stm32.h
@@ -37,6 +37,8 @@
 #    define MCU_STM32F072CB
 #elif defined(EEPROM_EMU_STM32F042x6)
 #    define MCU_STM32F042K6
+#elif defined(EEPROM_EMU_STM32F411xC)
+#    define MCU_STM32F411xC
 #endif
 
 /* The page_size * density_pages should provide 8k of space, split 4k/4k between snapshot and writelog in the default config */
@@ -47,6 +49,10 @@
 #    elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) || defined(MCU_STM32F103RD) || defined(MCU_STM32F303CC) || defined(MCU_STM32F072CB)
 #        define FEE_PAGE_SIZE (uint16_t)0x800  // Page size = 2KByte
 #        define FEE_DENSITY_PAGES 4            // How many pages are used
+#    elif defined(MCU_STM32F411xC)
+#        define FEE_PAGE_SIZE (uint32_t)0x20000  // Page size = 128KByte
+#        define FEE_DENSITY_PAGES 1              // How many pages are used
+#        define FEE_SECTOR_ID 5                  // sector id of the flash
 #    else
 #        error "No MCU type specified and FEE_DENSITY_PAGES not defined.\
 Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)\
@@ -65,6 +71,8 @@ or define FEE_DENSITY_PAGES yourself."
 #        define FEE_MCU_FLASH_SIZE 384  // Size in Kb
 #    elif defined(MCU_STM32F303CC)
 #        define FEE_MCU_FLASH_SIZE 256  // Size in Kb
+#    elif defined(MCU_STM32F411xC)
+#        define FEE_MCU_FLASH_SIZE 256  // Size in Kb
 #    else
 #        error "No MCU type specified and FEE_MCU_FLASH_SIZE not defined.\
 Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)\
@@ -83,8 +91,8 @@ or define FEE_MCU_FLASH_SIZE yourself."
 #endif
 
 #ifndef FEE_WRITELOG_SIZE
-    /* Size of eeprom writelog, in bytes */
-    #define FEE_WRITELOG_SIZE 4096
+    /* Size of eeprom writelog, in bytes. By default, use all the remaining amount of flash bank. */
+    #define FEE_WRITELOG_SIZE (FEE_DENSITY_PAGES * FEE_PAGE_SIZE - FEE_SNAPSHOT_SIZE)
 #endif
 
 /* Flash word value after erase */
diff --git a/tmk_core/common/chibios/flash_stm32.c b/tmk_core/common/chibios/flash_stm32.c
index e8b3dc4bc9..0bead21039 100644
--- a/tmk_core/common/chibios/flash_stm32.c
+++ b/tmk_core/common/chibios/flash_stm32.c
@@ -16,28 +16,24 @@
  * Modifications for QMK and STM32F303 by Yiancar
  */
 
-#if defined(EEPROM_EMU_STM32F303xC)
-#    define STM32F303xC
-#    include "stm32f3xx.h"
-#elif defined(EEPROM_EMU_STM32F103xB)
-#    define STM32F103xB
-#    include "stm32f1xx.h"
-#elif defined(EEPROM_EMU_STM32F072xB)
-#    define STM32F072xB
-#    include "stm32f0xx.h"
-#elif defined(EEPROM_EMU_STM32F042x6)
-#    define STM32F042x6
-#    include "stm32f0xx.h"
-#else
-#    error "not implemented."
-#endif
-
 #include "flash_stm32.h"
 
 #if defined(EEPROM_EMU_STM32F103xB)
 #    define FLASH_SR_WRPERR FLASH_SR_WRPRTERR
 #endif
 
+#if defined(EEPROM_EMU_STM32F4)
+#    define FLASH_PSIZE_HFWORD FLASH_CR_PSIZE_0
+#    define FLASH_PSIZE_WORD FLASH_CR_PSIZE_1
+#    define FLASH_CR_SNB_POS 3
+/* the flash key was not defined in the CMSIS used by current chibios */
+#    define FLASH_KEY1 0x45670123
+#    define FLASH_KEY2 0xCDEF89AB
+#    define FLASH_SR_FLAGS (FLASH_SR_PGAERR|FLASH_SR_PGPERR|FLASH_SR_PGSERR|FLASH_SR_WRPERR)
+#else
+#    define FLASH_SR_FLAGS (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR)
+#endif
+
 /* Delay definition */
 #define EraseTimeout ((uint32_t)0x00000FFF)
 #define ProgramTimeout ((uint32_t)0x0000001F)
@@ -64,11 +60,14 @@ static void delay(void) {
 FLASH_Status FLASH_GetStatus(void) {
     if ((FLASH->SR & FLASH_SR_BSY) == FLASH_SR_BSY) return FLASH_BUSY;
 
+#if defined(EEPROM_EMU_STM32F4)
+    if ((FLASH->SR & (FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR))) return FLASH_ERROR_PG;
+    if ((FLASH->SR & FLASH_SR_WRPERR)) return FLASH_ERROR_WRP;
+#else
     if ((FLASH->SR & FLASH_SR_PGERR) != 0) return FLASH_ERROR_PG;
-
     if ((FLASH->SR & FLASH_SR_WRPERR) != 0) return FLASH_ERROR_WRP;
-
     if ((FLASH->SR & FLASH_OBR_OPTERR) != 0) return FLASH_ERROR_OPT;
+#endif
 
     return FLASH_COMPLETE;
 }
@@ -97,30 +96,41 @@ FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) {
 
 /**
  * @brief  Erases a specified FLASH page.
- * @param  Page_Address: The page address to be erased.
+ * @param  Page_Address: The page address or sector to be erased.
  * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG,
  *   FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
  */
 FLASH_Status FLASH_ErasePage(uint32_t Page_Address) {
     FLASH_Status status = FLASH_COMPLETE;
+#ifndef EEPROM_EMU_STM32F4
     /* Check the parameters */
     ASSERT(IS_FLASH_ADDRESS(Page_Address));
+#endif
     /* Wait for last operation to be completed */
     status = FLASH_WaitForLastOperation(EraseTimeout);
 
     if (status == FLASH_COMPLETE) {
         /* if the previous operation is completed, proceed to erase the page */
+#if defined(EEPROM_EMU_STM32F4)
+        FLASH->CR &= ~FLASH_CR_SNB;
+        FLASH->CR |= FLASH_CR_SER | (Page_Address << FLASH_CR_SNB_POS);
+#else
         FLASH->CR |= FLASH_CR_PER;
         FLASH->AR = Page_Address;
+#endif
         FLASH->CR |= FLASH_CR_STRT;
 
         /* Wait for last operation to be completed */
         status = FLASH_WaitForLastOperation(EraseTimeout);
         if (status != FLASH_TIMEOUT) {
-            /* if the erase operation is completed, disable the PER Bit */
+            /* clear the SER or PER Bit */
+#if defined(EEPROM_EMU_STM32F4)
+            FLASH->CR &= ~(FLASH_CR_SER | FLASH_CR_SNB);
+#else
             FLASH->CR &= ~FLASH_CR_PER;
+#endif
         }
-        FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR);
+        FLASH_ClearFlag(FLASH_SR_FLAGS);
     }
     /* Return the Erase Status */
     return status;
@@ -136,11 +146,15 @@ FLASH_Status FLASH_ErasePage(uint32_t Page_Address) {
 FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {
     FLASH_Status status = FLASH_BAD_ADDRESS;
 
-    if (IS_FLASH_ADDRESS(Address)) {
+    if (IS_FLASH_ADDRESS(Address) && (Address % sizeof(Data)) == 0) {
         /* Wait for last operation to be completed */
         status = FLASH_WaitForLastOperation(ProgramTimeout);
         if (status == FLASH_COMPLETE) {
             /* if the previous operation is completed, proceed to program the new data */
+#if defined(EEPROM_EMU_STM32F4)
+            FLASH->CR &= ~FLASH_CR_PSIZE;
+            FLASH->CR |= FLASH_PSIZE_HFWORD;
+#endif
             FLASH->CR |= FLASH_CR_PG;
             *(__IO uint16_t*)Address = Data;
             /* Wait for last operation to be completed */
@@ -149,7 +163,7 @@ FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {
                 /* if the program operation is completed, disable the PG Bit */
                 FLASH->CR &= ~FLASH_CR_PG;
             }
-            FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR);
+            FLASH_ClearFlag(FLASH_SR_FLAGS);
         }
     }
     return status;
diff --git a/tmk_core/common/chibios/flash_stm32.h b/tmk_core/common/chibios/flash_stm32.h
index 90d5bff47e..f50507f3a5 100644
--- a/tmk_core/common/chibios/flash_stm32.h
+++ b/tmk_core/common/chibios/flash_stm32.h
@@ -22,6 +22,26 @@
 extern "C" {
 #endif
 
+#if defined(EEPROM_EMU_STM32F303xC)
+#    define STM32F303xC
+#    include "stm32f3xx.h"
+#elif defined(EEPROM_EMU_STM32F103xB)
+#    define STM32F103xB
+#    include "stm32f1xx.h"
+#elif defined(EEPROM_EMU_STM32F072xB)
+#    define STM32F072xB
+#    include "stm32f0xx.h"
+#elif defined(EEPROM_EMU_STM32F042x6)
+#    define STM32F042x6
+#    include "stm32f0xx.h"
+#elif defined(EEPROM_EMU_STM32F411xC)
+#    define EEPROM_EMU_STM32F4
+#    define STM32F411xE
+#    include "stm32f4xx.h"
+#else
+#    error "not implemented."
+#endif
+
 #include <ch.h>
 #include <hal.h>
 

From e25205e35d84d8c7b2b8e88bab2a0a0f70df1a32 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 11 Jul 2021 19:23:11 -0400
Subject: [PATCH 38/44] vial_example: add STM32F401 vial example

---
 .../vial_example/vial_stm32f401/config.h      | 27 +++++++++++++++++++
 .../vial_stm32f401/keymaps/default/keymap.c   | 15 +++++++++++
 .../vial_stm32f401/keymaps/vial/config.h      |  7 +++++
 .../vial_stm32f401/keymaps/vial/keymap.c      | 25 +++++++++++++++++
 .../vial_stm32f401/keymaps/vial/rules.mk      |  2 ++
 .../vial_stm32f401/keymaps/vial/vial.json     | 18 +++++++++++++
 .../vial_example/vial_stm32f401/rules.mk      | 25 +++++++++++++++++
 .../vial_stm32f401/vial_stm32f401.c           |  3 +++
 .../vial_stm32f401/vial_stm32f401.h           | 13 +++++++++
 9 files changed, 135 insertions(+)
 create mode 100644 keyboards/vial_example/vial_stm32f401/config.h
 create mode 100644 keyboards/vial_example/vial_stm32f401/keymaps/default/keymap.c
 create mode 100644 keyboards/vial_example/vial_stm32f401/keymaps/vial/config.h
 create mode 100644 keyboards/vial_example/vial_stm32f401/keymaps/vial/keymap.c
 create mode 100644 keyboards/vial_example/vial_stm32f401/keymaps/vial/rules.mk
 create mode 100644 keyboards/vial_example/vial_stm32f401/keymaps/vial/vial.json
 create mode 100644 keyboards/vial_example/vial_stm32f401/rules.mk
 create mode 100644 keyboards/vial_example/vial_stm32f401/vial_stm32f401.c
 create mode 100644 keyboards/vial_example/vial_stm32f401/vial_stm32f401.h

diff --git a/keyboards/vial_example/vial_stm32f401/config.h b/keyboards/vial_example/vial_stm32f401/config.h
new file mode 100644
index 0000000000..3699ca9f94
--- /dev/null
+++ b/keyboards/vial_example/vial_stm32f401/config.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/* USB Device descriptor parameter */
+#define VENDOR_ID       0xFEED
+#define PRODUCT_ID      0x0000
+#define DEVICE_VER      0x0001
+#define MANUFACTURER    Vial
+#define PRODUCT         STM32F401 example
+
+/* key matrix size */
+#define MATRIX_ROWS 2
+#define MATRIX_COLS 2
+
+#define MATRIX_ROW_PINS { B10, B11 }
+#define MATRIX_COL_PINS { B1, B0 }
+
+#define DIODE_DIRECTION COL2ROW
+
+/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
+#define LOCKING_SUPPORT_ENABLE
+/* Locking resynchronize hack */
+#define LOCKING_RESYNC_ENABLE
+
+/* Use 1000hz polling */
+#define USB_POLLING_INTERVAL_MS 1
diff --git a/keyboards/vial_example/vial_stm32f401/keymaps/default/keymap.c b/keyboards/vial_example/vial_stm32f401/keymaps/default/keymap.c
new file mode 100644
index 0000000000..6c76cbf73d
--- /dev/null
+++ b/keyboards/vial_example/vial_stm32f401/keymaps/default/keymap.c
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include QMK_KEYBOARD_H
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+    [0] = LAYOUT(
+        KC_1, KC_2,
+        KC_3, KC_4
+    ),
+
+    [1] = LAYOUT(
+        KC_A, KC_B,
+        KC_C, KC_D
+    )
+};
diff --git a/keyboards/vial_example/vial_stm32f401/keymaps/vial/config.h b/keyboards/vial_example/vial_stm32f401/keymaps/vial/config.h
new file mode 100644
index 0000000000..7a27fda00c
--- /dev/null
+++ b/keyboards/vial_example/vial_stm32f401/keymaps/vial/config.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#define VIAL_KEYBOARD_UID {0xD8, 0x0B, 0x9A, 0xC5, 0x1E, 0xD3, 0xD2, 0xBE}
+#define VIAL_UNLOCK_COMBO_ROWS { 0, 1 }
+#define VIAL_UNLOCK_COMBO_COLS { 0, 1 }
diff --git a/keyboards/vial_example/vial_stm32f401/keymaps/vial/keymap.c b/keyboards/vial_example/vial_stm32f401/keymaps/vial/keymap.c
new file mode 100644
index 0000000000..c8fb892589
--- /dev/null
+++ b/keyboards/vial_example/vial_stm32f401/keymaps/vial/keymap.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include QMK_KEYBOARD_H
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+    [0] = LAYOUT(
+        KC_1, KC_2,
+        KC_3, KC_4
+    ),
+
+    [1] = LAYOUT(
+        KC_A, KC_B,
+        KC_C, KC_D
+    ),
+
+    [2] = LAYOUT(
+        KC_TRNS, KC_TRNS,
+        KC_TRNS, KC_TRNS
+    ),
+
+    [3] = LAYOUT(
+        KC_TRNS, KC_TRNS,
+        KC_TRNS, KC_TRNS
+    )
+};
diff --git a/keyboards/vial_example/vial_stm32f401/keymaps/vial/rules.mk b/keyboards/vial_example/vial_stm32f401/keymaps/vial/rules.mk
new file mode 100644
index 0000000000..4f7618e9b2
--- /dev/null
+++ b/keyboards/vial_example/vial_stm32f401/keymaps/vial/rules.mk
@@ -0,0 +1,2 @@
+VIA_ENABLE = yes
+VIAL_ENABLE = yes
diff --git a/keyboards/vial_example/vial_stm32f401/keymaps/vial/vial.json b/keyboards/vial_example/vial_stm32f401/keymaps/vial/vial.json
new file mode 100644
index 0000000000..7525baeac5
--- /dev/null
+++ b/keyboards/vial_example/vial_stm32f401/keymaps/vial/vial.json
@@ -0,0 +1,18 @@
+{
+  "matrix": {
+    "rows": 2,
+    "cols": 2
+  },
+  "layouts": {
+    "keymap": [
+      [
+        "0,0",
+        "0,1"
+      ],
+      [
+        "1,0",
+        "1,1"
+      ]
+    ]
+  }
+}
diff --git a/keyboards/vial_example/vial_stm32f401/rules.mk b/keyboards/vial_example/vial_stm32f401/rules.mk
new file mode 100644
index 0000000000..18899be9d3
--- /dev/null
+++ b/keyboards/vial_example/vial_stm32f401/rules.mk
@@ -0,0 +1,25 @@
+# MCU name
+MCU = STM32F401
+
+# Build Options
+#   change yes to no to disable
+#
+BOOTMAGIC_ENABLE = lite       # Virtual DIP switch configuration
+MOUSEKEY_ENABLE = yes       # Mouse keys
+EXTRAKEY_ENABLE = yes       # Audio control and System control
+CONSOLE_ENABLE = no        # Console for debug
+COMMAND_ENABLE = no        # Commands for debug and configuration
+# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
+SLEEP_LED_ENABLE = no      # Breathing sleep LED during USB suspend
+# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
+NKRO_ENABLE = yes           # USB Nkey Rollover
+BACKLIGHT_ENABLE = no       # Enable keyboard backlight functionality
+RGBLIGHT_ENABLE = no       # Enable keyboard RGB underglow
+MIDI_ENABLE = no            # MIDI support
+UNICODE_ENABLE = no         # Unicode
+BLUETOOTH_ENABLE = no       # Enable Bluetooth with the Adafruit EZ-Key HID
+AUDIO_ENABLE = no           # Audio output on port C6
+FAUXCLICKY_ENABLE = no      # Use buzzer to emulate clicky switches
+
+# Enter lower-power sleep mode when on the ChibiOS idle thread
+OPT_DEFS += -DCORTEX_ENABLE_WFI_IDLE=TRUE
diff --git a/keyboards/vial_example/vial_stm32f401/vial_stm32f401.c b/keyboards/vial_example/vial_stm32f401/vial_stm32f401.c
new file mode 100644
index 0000000000..8e14bd3387
--- /dev/null
+++ b/keyboards/vial_example/vial_stm32f401/vial_stm32f401.c
@@ -0,0 +1,3 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "vial_stm32f401.h"
diff --git a/keyboards/vial_example/vial_stm32f401/vial_stm32f401.h b/keyboards/vial_example/vial_stm32f401/vial_stm32f401.h
new file mode 100644
index 0000000000..12c2bedb5f
--- /dev/null
+++ b/keyboards/vial_example/vial_stm32f401/vial_stm32f401.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "quantum.h"
+
+#define LAYOUT( \
+	K00, K01, \
+	K10, K11  \
+) { \
+	{ K00,   K01 }, \
+	{ K10,   K11 }  \
+}

From e165e8c94ae97534b1c04ff5136117beaa64b9e9 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 11 Jul 2021 22:34:23 -0400
Subject: [PATCH 39/44] vial: always include qmk_settings.h for fallback
 defines

---
 quantum/vial.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/quantum/vial.c b/quantum/vial.c
index 94a370db87..3193ead7a8 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -48,9 +48,7 @@ _Static_assert(sizeof(vial_unlock_combo_rows) == sizeof(vial_unlock_combo_cols),
 #define VIAL_ENCODER_KEYCODE_DELAY 10
 #endif
 
-#ifdef QMK_SETTINGS
 #include "qmk_settings.h"
-#endif
 
 #ifdef VIAL_TAP_DANCE_ENABLE
 static void reload_tap_dance(void);

From 14c7ba62b01a9e17090a79b904867fe0c3b54815 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sun, 11 Jul 2021 23:41:53 -0400
Subject: [PATCH 40/44] qmk_settings: fix variable delays on avr

---
 quantum/process_keycode/process_auto_shift.c |  2 +-
 quantum/qmk_settings.h                       |  9 +++++++++
 quantum/quantum.c                            |  2 +-
 quantum/vial.c                               |  4 ++--
 tmk_core/common/action.c                     | 10 +++++-----
 5 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/quantum/process_keycode/process_auto_shift.c b/quantum/process_keycode/process_auto_shift.c
index 9ef953c4b9..e5e3778b65 100644
--- a/quantum/process_keycode/process_auto_shift.c
+++ b/quantum/process_keycode/process_auto_shift.c
@@ -110,7 +110,7 @@ if (QS_auto_shift_repeat && !QS_auto_shift_no_auto_repeat) {
 }
         }
 
-        wait_ms(QS_tap_code_delay);
+        qs_wait_ms(QS_tap_code_delay);
         unregister_code(autoshift_lastkey);
         del_weak_mods(MOD_BIT(KC_LSFT));
     } else {
diff --git a/quantum/qmk_settings.h b/quantum/qmk_settings.h
index f1086b637f..4ce67103c3 100644
--- a/quantum/qmk_settings.h
+++ b/quantum/qmk_settings.h
@@ -203,3 +203,12 @@ extern qmk_settings_t QS;
 #define QS_tap_hold_caps_delay TAP_HOLD_CAPS_DELAY
 
 #endif
+
+#if defined(__AVR__) && defined(QMK_SETTINGS)
+#include <util/delay.h>
+static inline void qs_wait_ms(uint16_t timer) {
+    while (timer--) _delay_ms(1);
+}
+#else
+    #define qs_wait_ms wait_ms
+#endif
diff --git a/quantum/quantum.c b/quantum/quantum.c
index b71faa7f84..47477c5c5c 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -108,7 +108,7 @@ void unregister_code16(uint16_t code) {
 
 void tap_code16(uint16_t code) {
     register_code16(code);
-    wait_ms(QS_tap_code_delay);
+    qs_wait_ms(QS_tap_code_delay);
     unregister_code16(code);
 }
 
diff --git a/quantum/vial.c b/quantum/vial.c
index 3193ead7a8..8a43c01423 100644
--- a/quantum/vial.c
+++ b/quantum/vial.c
@@ -286,7 +286,7 @@ static void vial_keycode_tap(uint16_t keycode) __attribute__((unused));
 
 static void vial_keycode_tap(uint16_t keycode) {
     vial_keycode_down(keycode);
-    wait_ms(QS_tap_code_delay);
+    qs_wait_ms(QS_tap_code_delay);
     vial_keycode_up(keycode);
 }
 
@@ -425,7 +425,7 @@ 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(QS_tap_code_delay);
+    qs_wait_ms(QS_tap_code_delay);
     uint8_t st = dance_state[index];
     state->count = 0;
     dance_state[index] = 0;
diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c
index 09e21e769e..d7c29c6d55 100644
--- a/tmk_core/common/action.c
+++ b/tmk_core/common/action.c
@@ -364,9 +364,9 @@ if (QS_oneshot_tap_toggle > 1) {
                         if (tap_count > 0) {
                             dprint("MODS_TAP: Tap: unregister_code\n");
                             if (action.layer_tap.code == KC_CAPS) {
-                                wait_ms(QS_tap_hold_caps_delay);
+                                qs_wait_ms(QS_tap_hold_caps_delay);
                             } else {
-                                wait_ms(QS_tap_code_delay);
+                                qs_wait_ms(QS_tap_code_delay);
                             }
                             unregister_code(action.key.code);
                         } else {
@@ -589,9 +589,9 @@ if (QS_oneshot_tap_toggle > 1) {
                         if (tap_count > 0) {
                             dprint("KEYMAP_TAP_KEY: Tap: unregister_code\n");
                             if (action.layer_tap.code == KC_CAPS) {
-                                wait_ms(QS_tap_hold_caps_delay);
+                                qs_wait_ms(QS_tap_hold_caps_delay);
                             } else {
-                                wait_ms(QS_tap_code_delay);
+                                qs_wait_ms(QS_tap_code_delay);
                             }
                             unregister_code(action.layer_tap.code);
                         } else {
@@ -670,7 +670,7 @@ if (QS_oneshot_tap_toggle > 1) {
                         if (event.pressed) {
                             register_code(action.swap.code);
                         } else {
-                            wait_ms(QS_tap_code_delay);
+                            qs_wait_ms(QS_tap_code_delay);
                             unregister_code(action.swap.code);
                             *record = (keyrecord_t){};  // hack: reset tap mode
                         }

From e7789a9502ffe7166599823e577e7f8c8a27456b Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Mon, 12 Jul 2021 00:00:32 -0400
Subject: [PATCH 41/44] vial: change number of tapdance/combos enabled based on
 eeprom size

---
 quantum/dynamic_keymap.c        | 23 +----------------------
 quantum/dynamic_keymap.h        |  4 ++++
 quantum/dynamic_keymap_eeprom.h | 25 +++++++++++++++++++++++++
 quantum/vial.h                  | 23 +++++++++++++++++++++--
 4 files changed, 51 insertions(+), 24 deletions(-)
 create mode 100644 quantum/dynamic_keymap_eeprom.h

diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c
index c9a5cc793c..fb2338b69e 100644
--- a/quantum/dynamic_keymap.c
+++ b/quantum/dynamic_keymap.c
@@ -21,10 +21,7 @@
 #include "quantum.h"  // for send_string()
 #include "dynamic_keymap.h"
 #include "via.h"  // for default VIA_EEPROM_ADDR_END
-
-#ifdef STM32_EEPROM_ENABLE
-#    include "eeprom_stm32.h"
-#endif
+#include "dynamic_keymap_eeprom.h"
 
 #ifdef VIAL_ENABLE
 #include "vial.h"
@@ -34,24 +31,6 @@
 #    define DYNAMIC_KEYMAP_MACRO_COUNT 16
 #endif
 
-// This is the default EEPROM max address to use for dynamic keymaps.
-// The default is the ATmega32u4 EEPROM max address.
-// Explicitly override it if the keyboard uses a microcontroller with
-// more EEPROM *and* it makes sense to increase it.
-#ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR
-#    if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__)
-#        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 2047
-#    elif defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
-#        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 4095
-#    elif defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATtiny85__)
-#        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 511
-#    elif defined(FEE_DENSITY_BYTES)
-#        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR FEE_DENSITY_BYTES-1
-#    else
-#        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 1023
-#    endif
-#endif
-
 // Due to usage of uint16_t check for max 65535
 #if DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > 65535
 #    error DYNAMIC_KEYMAP_EEPROM_MAX_ADDR must be less than 65536
diff --git a/quantum/dynamic_keymap.h b/quantum/dynamic_keymap.h
index 5114cbe46a..023af87ea7 100644
--- a/quantum/dynamic_keymap.h
+++ b/quantum/dynamic_keymap.h
@@ -18,6 +18,10 @@
 #include <stdint.h>
 #include <stdbool.h>
 
+#ifdef VIAL_ENABLE
+#include "vial.h"
+#endif
+
 #ifndef DYNAMIC_KEYMAP_LAYER_COUNT
 #    define DYNAMIC_KEYMAP_LAYER_COUNT 4
 #endif
diff --git a/quantum/dynamic_keymap_eeprom.h b/quantum/dynamic_keymap_eeprom.h
new file mode 100644
index 0000000000..4ce8d4baeb
--- /dev/null
+++ b/quantum/dynamic_keymap_eeprom.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#ifdef STM32_EEPROM_ENABLE
+#    include "eeprom_stm32.h"
+#endif
+
+// This is the default EEPROM max address to use for dynamic keymaps.
+// The default is the ATmega32u4 EEPROM max address.
+// Explicitly override it if the keyboard uses a microcontroller with
+// more EEPROM *and* it makes sense to increase it.
+#ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR
+#    if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__)
+#        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 2047
+#    elif defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
+#        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 4095
+#    elif defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATtiny85__)
+#        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 511
+#    elif defined(FEE_DENSITY_BYTES)
+#        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR FEE_DENSITY_BYTES-1
+#    else
+#        define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 1023
+#    endif
+#endif
diff --git a/quantum/vial.h b/quantum/vial.h
index 74e648fa23..3ace285444 100644
--- a/quantum/vial.h
+++ b/quantum/vial.h
@@ -19,6 +19,8 @@
 #include <inttypes.h>
 #include <stdbool.h>
 
+#include "dynamic_keymap_eeprom.h"
+
 #define VIAL_PROTOCOL_VERSION ((uint32_t)0x00000004)
 #define VIAL_RAW_EPSIZE 32
 
@@ -66,7 +68,15 @@ enum {
 #define VIAL_TAP_DANCE_ENABLE
 
 #ifndef VIAL_TAP_DANCE_ENTRIES
-#define VIAL_TAP_DANCE_ENTRIES 16
+    #if DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > 4000
+        #define VIAL_TAP_DANCE_ENTRIES 32
+    #elif DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > 2000
+        #define VIAL_TAP_DANCE_ENTRIES 16
+    #elif DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > 1000
+        #define VIAL_TAP_DANCE_ENTRIES 8
+    #else
+        #define VIAL_TAP_DANCE_ENTRIES 4
+    #endif
 #endif
 
 typedef struct {
@@ -87,7 +97,15 @@ _Static_assert(sizeof(vial_tap_dance_entry_t) == 10, "Unexpected size of the via
 #define VIAL_COMBO_ENABLE
 
 #ifndef VIAL_COMBO_ENTRIES
-#define VIAL_COMBO_ENTRIES 16
+    #if DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > 4000
+        #define VIAL_COMBO_ENTRIES 32
+    #elif DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > 2000
+        #define VIAL_COMBO_ENTRIES 16
+    #elif DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > 1000
+        #define VIAL_COMBO_ENTRIES 8
+    #else
+        #define VIAL_COMBO_ENTRIES 4
+    #endif
 #endif
 
 typedef struct {
@@ -104,5 +122,6 @@ _Static_assert(sizeof(vial_combo_entry_t) == 10, "Unexpected size of the vial_co
 #define COMBO_COUNT VIAL_COMBO_ENTRIES
 
 #else
+#undef VIAL_COMBO_ENTRIES
 #define VIAL_COMBO_ENTRIES 0
 #endif

From 829a9039d565845103e279c8a6808f116d1896bd Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Mon, 12 Jul 2021 00:22:28 -0400
Subject: [PATCH 42/44] Make vial firmware fit pass 1

---
 keyboards/3by2/keymaps/via/rules.mk                       | 3 +++
 keyboards/bm60poker/keymaps/vial/rules.mk                 | 2 ++
 keyboards/capsunlocked/cu7/keymaps/via/rules.mk           | 2 ++
 keyboards/cx60/keymaps/vial/rules.mk                      | 2 +-
 keyboards/cx60/keymaps/vial_caps/rules.mk                 | 2 +-
 keyboards/gray_studio/think65/solder/keymaps/via/rules.mk | 1 +
 keyboards/misterdeck/keymaps/via/rules.mk                 | 1 +
 keyboards/tw40/keymaps/via/rules.mk                       | 3 ++-
 keyboards/yd60mq/keymaps/vial/rules.mk                    | 2 +-
 9 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/keyboards/3by2/keymaps/via/rules.mk b/keyboards/3by2/keymaps/via/rules.mk
index 79b8374ce5..6ba624c8e0 100644
--- a/keyboards/3by2/keymaps/via/rules.mk
+++ b/keyboards/3by2/keymaps/via/rules.mk
@@ -1,2 +1,5 @@
 VIA_ENABLE 		= yes
 VIAL_ENABLE		= yes
+QMK_SETTINGS = no
+TAP_DANCE_ENABLE = no
+COMBO_ENABLE = no
diff --git a/keyboards/bm60poker/keymaps/vial/rules.mk b/keyboards/bm60poker/keymaps/vial/rules.mk
index 46f9f1360f..7e0cf67c8a 100644
--- a/keyboards/bm60poker/keymaps/vial/rules.mk
+++ b/keyboards/bm60poker/keymaps/vial/rules.mk
@@ -1,3 +1,5 @@
 VIA_ENABLE = yes
 VIAL_ENABLE = yes
 LTO_ENABLE = yes
+QMK_SETTINGS = no
+TAP_DANCE_ENABLE = no
diff --git a/keyboards/capsunlocked/cu7/keymaps/via/rules.mk b/keyboards/capsunlocked/cu7/keymaps/via/rules.mk
index 57da7154e7..fcf431c3ca 100644
--- a/keyboards/capsunlocked/cu7/keymaps/via/rules.mk
+++ b/keyboards/capsunlocked/cu7/keymaps/via/rules.mk
@@ -1,3 +1,5 @@
 VIA_ENABLE = yes
 VIAL_ENABLE = yes
 VIAL_ENCODERS_ENABLE = yes
+QMK_SETTINGS = no
+TAP_DANCE_ENABLE = no
diff --git a/keyboards/cx60/keymaps/vial/rules.mk b/keyboards/cx60/keymaps/vial/rules.mk
index 7b9328992c..b823138c83 100644
--- a/keyboards/cx60/keymaps/vial/rules.mk
+++ b/keyboards/cx60/keymaps/vial/rules.mk
@@ -1,4 +1,4 @@
 LTO_ENABLE = yes
 VIA_ENABLE = yes
 VIAL_ENABLE = yes
-
+QMK_SETTINGS = no
diff --git a/keyboards/cx60/keymaps/vial_caps/rules.mk b/keyboards/cx60/keymaps/vial_caps/rules.mk
index 7b9328992c..b823138c83 100644
--- a/keyboards/cx60/keymaps/vial_caps/rules.mk
+++ b/keyboards/cx60/keymaps/vial_caps/rules.mk
@@ -1,4 +1,4 @@
 LTO_ENABLE = yes
 VIA_ENABLE = yes
 VIAL_ENABLE = yes
-
+QMK_SETTINGS = no
diff --git a/keyboards/gray_studio/think65/solder/keymaps/via/rules.mk b/keyboards/gray_studio/think65/solder/keymaps/via/rules.mk
index 77a7d30f7b..35a4c68336 100644
--- a/keyboards/gray_studio/think65/solder/keymaps/via/rules.mk
+++ b/keyboards/gray_studio/think65/solder/keymaps/via/rules.mk
@@ -3,3 +3,4 @@ LTO_ENABLE = yes
 VIAL_ENABLE = yes
 
 CONSOLE_ENABLE = no
+QMK_SETTINGS = no
diff --git a/keyboards/misterdeck/keymaps/via/rules.mk b/keyboards/misterdeck/keymaps/via/rules.mk
index 79b8374ce5..c5bd63c851 100644
--- a/keyboards/misterdeck/keymaps/via/rules.mk
+++ b/keyboards/misterdeck/keymaps/via/rules.mk
@@ -1,2 +1,3 @@
 VIA_ENABLE 		= yes
 VIAL_ENABLE		= yes
+QMK_SETTINGS = no
diff --git a/keyboards/tw40/keymaps/via/rules.mk b/keyboards/tw40/keymaps/via/rules.mk
index 2da96e96b9..148dfa63c4 100644
--- a/keyboards/tw40/keymaps/via/rules.mk
+++ b/keyboards/tw40/keymaps/via/rules.mk
@@ -1,3 +1,4 @@
 VIA_ENABLE = yes
 LTO_ENABLE = yes
-VIAL_ENABLE = yes
\ No newline at end of file
+VIAL_ENABLE = yes
+QMK_SETTINGS = no
diff --git a/keyboards/yd60mq/keymaps/vial/rules.mk b/keyboards/yd60mq/keymaps/vial/rules.mk
index e294ae26fe..148dfa63c4 100644
--- a/keyboards/yd60mq/keymaps/vial/rules.mk
+++ b/keyboards/yd60mq/keymaps/vial/rules.mk
@@ -1,4 +1,4 @@
 VIA_ENABLE = yes
 LTO_ENABLE = yes
 VIAL_ENABLE = yes
-
+QMK_SETTINGS = no

From 933c641e5da30f6ef2b1ac2549c47a300e28810e Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Mon, 12 Jul 2021 00:31:37 -0400
Subject: [PATCH 43/44] Make vial firmware fit pass 2

---
 keyboards/bpiphany/frosty_flake/keymaps/via/rules.mk | 3 +++
 keyboards/hineybush/h87a/keymaps/via/rules.mk        | 3 +++
 keyboards/hineybush/h88/keymaps/via/rules.mk         | 3 +++
 keyboards/ilumkb/primus75/keymaps/vial/rules.mk      | 1 +
 4 files changed, 10 insertions(+)

diff --git a/keyboards/bpiphany/frosty_flake/keymaps/via/rules.mk b/keyboards/bpiphany/frosty_flake/keymaps/via/rules.mk
index 8ff1f2319b..b62def6d90 100644
--- a/keyboards/bpiphany/frosty_flake/keymaps/via/rules.mk
+++ b/keyboards/bpiphany/frosty_flake/keymaps/via/rules.mk
@@ -1,3 +1,6 @@
 VIA_ENABLE = yes
 LTO_ENABLE = yes
 VIAL_ENABLE = yes
+TAP_DANCE_ENABLE = no
+COMBO_ENABLE = no
+QMK_SETTINGS = no
diff --git a/keyboards/hineybush/h87a/keymaps/via/rules.mk b/keyboards/hineybush/h87a/keymaps/via/rules.mk
index 4f7618e9b2..c674759413 100644
--- a/keyboards/hineybush/h87a/keymaps/via/rules.mk
+++ b/keyboards/hineybush/h87a/keymaps/via/rules.mk
@@ -1,2 +1,5 @@
 VIA_ENABLE = yes
 VIAL_ENABLE = yes
+TAP_DANCE_ENABLE = no
+COMBO_ENABLE = no
+QMK_SETTINGS = no
diff --git a/keyboards/hineybush/h88/keymaps/via/rules.mk b/keyboards/hineybush/h88/keymaps/via/rules.mk
index 4f7618e9b2..c674759413 100644
--- a/keyboards/hineybush/h88/keymaps/via/rules.mk
+++ b/keyboards/hineybush/h88/keymaps/via/rules.mk
@@ -1,2 +1,5 @@
 VIA_ENABLE = yes
 VIAL_ENABLE = yes
+TAP_DANCE_ENABLE = no
+COMBO_ENABLE = no
+QMK_SETTINGS = no
diff --git a/keyboards/ilumkb/primus75/keymaps/vial/rules.mk b/keyboards/ilumkb/primus75/keymaps/vial/rules.mk
index 46f9f1360f..69354a7147 100644
--- a/keyboards/ilumkb/primus75/keymaps/vial/rules.mk
+++ b/keyboards/ilumkb/primus75/keymaps/vial/rules.mk
@@ -1,3 +1,4 @@
 VIA_ENABLE = yes
 VIAL_ENABLE = yes
 LTO_ENABLE = yes
+TAP_DANCE_ENABLE = no

From 50565b91178260086d7f92e7393e3b16faa33822 Mon Sep 17 00:00:00 2001
From: Ilya Zhuravlev <whatever@xyz.is>
Date: Sat, 17 Jul 2021 23:56:06 -0400
Subject: [PATCH 44/44] Fix compile for newly added keyboards

---
 keyboards/bm40hsrgb/keymaps/vial/rules.mk             | 2 ++
 keyboards/boston/keymaps/via/rules.mk                 | 2 +-
 keyboards/kbdfans/kbd67/hotswap/keymaps/vial/rules.mk | 1 +
 keyboards/kbdfans/kbd67/rev2/keymaps/vial/rules.mk    | 3 ++-
 keyboards/kbdfans/kbd75/keymaps/vial_rev2/rules.mk    | 2 ++
 5 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/keyboards/bm40hsrgb/keymaps/vial/rules.mk b/keyboards/bm40hsrgb/keymaps/vial/rules.mk
index 46f9f1360f..7e0cf67c8a 100644
--- a/keyboards/bm40hsrgb/keymaps/vial/rules.mk
+++ b/keyboards/bm40hsrgb/keymaps/vial/rules.mk
@@ -1,3 +1,5 @@
 VIA_ENABLE = yes
 VIAL_ENABLE = yes
 LTO_ENABLE = yes
+QMK_SETTINGS = no
+TAP_DANCE_ENABLE = no
diff --git a/keyboards/boston/keymaps/via/rules.mk b/keyboards/boston/keymaps/via/rules.mk
index 674971821e..33c942274e 100644
--- a/keyboards/boston/keymaps/via/rules.mk
+++ b/keyboards/boston/keymaps/via/rules.mk
@@ -8,7 +8,7 @@ BOOTLOADER = stm32-dfu
 #   change yes to no to disable
 #
 BOOTMAGIC_ENABLE = lite     # Virtual DIP switch configuration
-MOUSEKEY_ENABLE = no       # Mouse keys
+MOUSEKEY_ENABLE = yes       # Mouse keys
 EXTRAKEY_ENABLE = yes      # Audio control and System control
 CONSOLE_ENABLE = no        # Console for debug
 COMMAND_ENABLE = no        # Commands for debug and configuration
diff --git a/keyboards/kbdfans/kbd67/hotswap/keymaps/vial/rules.mk b/keyboards/kbdfans/kbd67/hotswap/keymaps/vial/rules.mk
index 8ff1f2319b..148dfa63c4 100644
--- a/keyboards/kbdfans/kbd67/hotswap/keymaps/vial/rules.mk
+++ b/keyboards/kbdfans/kbd67/hotswap/keymaps/vial/rules.mk
@@ -1,3 +1,4 @@
 VIA_ENABLE = yes
 LTO_ENABLE = yes
 VIAL_ENABLE = yes
+QMK_SETTINGS = no
diff --git a/keyboards/kbdfans/kbd67/rev2/keymaps/vial/rules.mk b/keyboards/kbdfans/kbd67/rev2/keymaps/vial/rules.mk
index 7b9328992c..68303d8bae 100644
--- a/keyboards/kbdfans/kbd67/rev2/keymaps/vial/rules.mk
+++ b/keyboards/kbdfans/kbd67/rev2/keymaps/vial/rules.mk
@@ -1,4 +1,5 @@
 LTO_ENABLE = yes
 VIA_ENABLE = yes
 VIAL_ENABLE = yes
-
+QMK_SETTINGS = no
+TAP_DANCE_ENABLE = no
diff --git a/keyboards/kbdfans/kbd75/keymaps/vial_rev2/rules.mk b/keyboards/kbdfans/kbd75/keymaps/vial_rev2/rules.mk
index 8ff1f2319b..256d6a187b 100644
--- a/keyboards/kbdfans/kbd75/keymaps/vial_rev2/rules.mk
+++ b/keyboards/kbdfans/kbd75/keymaps/vial_rev2/rules.mk
@@ -1,3 +1,5 @@
 VIA_ENABLE = yes
 LTO_ENABLE = yes
 VIAL_ENABLE = yes
+QMK_SETTINGS = no
+TAP_DANCE_ENABLE = no