summaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
Diffstat (limited to 'quantum')
-rw-r--r--quantum/color.h54
-rw-r--r--quantum/command.c2
-rw-r--r--quantum/crc.c59
-rw-r--r--quantum/crc.h (renamed from quantum/rgb.h)45
-rw-r--r--quantum/debounce.h2
-rw-r--r--quantum/debounce/asym_eager_defer_pk.c171
-rw-r--r--quantum/debounce/none.c31
-rw-r--r--quantum/debounce/sym_defer_g.c26
-rw-r--r--quantum/debounce/sym_defer_pk.c67
-rw-r--r--quantum/debounce/sym_eager_pk.c72
-rw-r--r--quantum/debounce/sym_eager_pr.c76
-rw-r--r--quantum/debounce/tests/asym_eager_defer_pk_tests.cpp374
-rw-r--r--quantum/debounce/tests/debounce_test_common.cpp229
-rw-r--r--quantum/debounce/tests/debounce_test_common.h83
-rw-r--r--quantum/debounce/tests/rules.mk44
-rw-r--r--quantum/debounce/tests/sym_defer_g_tests.cpp223
-rw-r--r--quantum/debounce/tests/sym_defer_pk_tests.cpp225
-rw-r--r--quantum/debounce/tests/sym_eager_pk_tests.cpp237
-rw-r--r--quantum/debounce/tests/sym_eager_pr_tests.cpp280
-rw-r--r--quantum/debounce/tests/testlist.mk6
-rw-r--r--quantum/dip_switch.c8
-rw-r--r--quantum/dip_switch.h8
-rw-r--r--quantum/haptic.c295
-rw-r--r--quantum/haptic.h77
-rw-r--r--quantum/keymap_extras/keymap_belgian.h13
-rw-r--r--quantum/keymap_extras/keymap_bepo.h167
-rw-r--r--quantum/keymap_extras/keymap_br_abnt2.h20
-rw-r--r--quantum/keymap_extras/keymap_canadian_multilingual.h179
-rw-r--r--quantum/keymap_extras/keymap_colemak.h32
-rw-r--r--quantum/keymap_extras/keymap_fr_ch.h79
-rw-r--r--quantum/keymap_extras/keymap_french.h11
-rw-r--r--quantum/keymap_extras/keymap_french_osx.h12
-rw-r--r--quantum/keymap_extras/keymap_german.h13
-rw-r--r--quantum/keymap_extras/keymap_german_ch.h18
-rw-r--r--quantum/keymap_extras/keymap_german_osx.h80
-rw-r--r--quantum/keymap_extras/keymap_hungarian.h19
-rw-r--r--quantum/keymap_extras/keymap_italian.h19
-rw-r--r--quantum/keymap_extras/keymap_italian_osx_ansi.h18
-rw-r--r--quantum/keymap_extras/keymap_italian_osx_iso.h18
-rw-r--r--quantum/keymap_extras/keymap_jp.h9
-rw-r--r--quantum/keymap_extras/keymap_neo2.h52
-rw-r--r--quantum/keymap_extras/keymap_norwegian.h23
-rw-r--r--quantum/keymap_extras/keymap_slovenian.h8
-rw-r--r--quantum/keymap_extras/keymap_spanish.h9
-rw-r--r--quantum/keymap_extras/keymap_swedish.h23
-rw-r--r--quantum/keymap_extras/keymap_uk.h75
-rw-r--r--quantum/keymap_extras/keymap_workman.h29
-rw-r--r--quantum/led_matrix/animations/alpha_mods_anim.h (renamed from quantum/led_matrix_animations/alpha_mods_anim.h)2
-rw-r--r--quantum/led_matrix/animations/band_anim.h (renamed from quantum/led_matrix_animations/band_anim.h)2
-rw-r--r--quantum/led_matrix/animations/band_pinwheel_anim.h (renamed from quantum/led_matrix_animations/band_pinwheel_anim.h)2
-rw-r--r--quantum/led_matrix/animations/band_spiral_anim.h (renamed from quantum/led_matrix_animations/band_spiral_anim.h)2
-rw-r--r--quantum/led_matrix/animations/breathing_anim.h (renamed from quantum/led_matrix_animations/breathing_anim.h)2
-rw-r--r--quantum/led_matrix/animations/cycle_left_right_anim.h (renamed from quantum/led_matrix_animations/cycle_left_right_anim.h)2
-rw-r--r--quantum/led_matrix/animations/cycle_out_in_anim.h (renamed from quantum/led_matrix_animations/cycle_out_in_anim.h)2
-rw-r--r--quantum/led_matrix/animations/cycle_up_down_anim.h (renamed from quantum/led_matrix_animations/cycle_up_down_anim.h)2
-rw-r--r--quantum/led_matrix/animations/dual_beacon_anim.h (renamed from quantum/led_matrix_animations/dual_beacon_anim.h)2
-rw-r--r--quantum/led_matrix/animations/led_matrix_effects.inc18
-rw-r--r--quantum/led_matrix/animations/runners/effect_runner_dx_dy.h (renamed from quantum/led_matrix_runners/effect_runner_dx_dy.h)0
-rw-r--r--quantum/led_matrix/animations/runners/effect_runner_dx_dy_dist.h (renamed from quantum/led_matrix_runners/effect_runner_dx_dy_dist.h)0
-rw-r--r--quantum/led_matrix/animations/runners/effect_runner_i.h (renamed from quantum/led_matrix_runners/effect_runner_i.h)0
-rw-r--r--quantum/led_matrix/animations/runners/effect_runner_reactive.h (renamed from quantum/led_matrix_runners/effect_runner_reactive.h)0
-rw-r--r--quantum/led_matrix/animations/runners/effect_runner_reactive_splash.h (renamed from quantum/led_matrix_runners/effect_runner_reactive_splash.h)0
-rw-r--r--quantum/led_matrix/animations/runners/effect_runner_sin_cos_i.h (renamed from quantum/led_matrix_runners/effect_runner_sin_cos_i.h)0
-rw-r--r--quantum/led_matrix/animations/runners/led_matrix_runners.inc6
-rw-r--r--quantum/led_matrix/animations/solid_anim.h (renamed from quantum/led_matrix_animations/solid_anim.h)0
-rw-r--r--quantum/led_matrix/animations/solid_reactive_cross.h (renamed from quantum/led_matrix_animations/solid_reactive_cross.h)4
-rw-r--r--quantum/led_matrix/animations/solid_reactive_nexus.h (renamed from quantum/led_matrix_animations/solid_reactive_nexus.h)4
-rw-r--r--quantum/led_matrix/animations/solid_reactive_simple_anim.h (renamed from quantum/led_matrix_animations/solid_reactive_simple_anim.h)4
-rw-r--r--quantum/led_matrix/animations/solid_reactive_wide.h (renamed from quantum/led_matrix_animations/solid_reactive_wide.h)4
-rw-r--r--quantum/led_matrix/animations/solid_splash_anim.h (renamed from quantum/led_matrix_animations/solid_splash_anim.h)4
-rw-r--r--quantum/led_matrix/animations/wave_left_right_anim.h (renamed from quantum/led_matrix_animations/wave_left_right_anim.h)2
-rw-r--r--quantum/led_matrix/animations/wave_up_down_anim.h (renamed from quantum/led_matrix_animations/wave_up_down_anim.h)2
-rw-r--r--quantum/led_matrix/led_matrix.c (renamed from quantum/led_matrix.c)50
-rw-r--r--quantum/led_matrix/led_matrix.h (renamed from quantum/led_matrix.h)2
-rw-r--r--quantum/led_matrix/led_matrix_drivers.c (renamed from quantum/led_matrix_drivers.c)0
-rw-r--r--quantum/led_matrix/led_matrix_types.h (renamed from quantum/led_matrix_types.h)0
-rw-r--r--quantum/led_matrix_animations/led_matrix_effects.inc18
-rw-r--r--quantum/matrix.c283
-rw-r--r--quantum/matrix.h2
-rw-r--r--quantum/matrix_common.c2
-rw-r--r--quantum/mcu_selection.mk72
-rw-r--r--quantum/mousekey.c2
-rw-r--r--quantum/mousekey.h11
-rw-r--r--quantum/process_keycode/process_combo.c2
-rw-r--r--quantum/process_keycode/process_combo.h2
-rw-r--r--quantum/process_keycode/process_haptic.c147
-rw-r--r--quantum/process_keycode/process_haptic.h21
-rw-r--r--quantum/process_keycode/process_key_override.c518
-rw-r--r--quantum/process_keycode/process_key_override.h153
-rw-r--r--quantum/process_keycode/process_music.c2
-rw-r--r--quantum/process_keycode/process_music.h2
-rw-r--r--quantum/process_keycode/process_rgb.c1
-rw-r--r--quantum/process_keycode/process_tap_dance.c2
-rw-r--r--quantum/process_keycode/process_tap_dance.h2
-rw-r--r--quantum/quantum.c38
-rw-r--r--quantum/quantum.h13
-rw-r--r--quantum/quantum_keycodes.h5
-rw-r--r--quantum/rgb_matrix/animations/alpha_mods_anim.h (renamed from quantum/rgb_matrix_animations/alpha_mods_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/breathing_anim.h (renamed from quantum/rgb_matrix_animations/breathing_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/colorband_pinwheel_sat_anim.h (renamed from quantum/rgb_matrix_animations/colorband_pinwheel_sat_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/colorband_pinwheel_val_anim.h (renamed from quantum/rgb_matrix_animations/colorband_pinwheel_val_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/colorband_sat_anim.h (renamed from quantum/rgb_matrix_animations/colorband_sat_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/colorband_spiral_sat_anim.h (renamed from quantum/rgb_matrix_animations/colorband_spiral_sat_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/colorband_spiral_val_anim.h (renamed from quantum/rgb_matrix_animations/colorband_spiral_val_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/colorband_val_anim.h (renamed from quantum/rgb_matrix_animations/colorband_val_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/cycle_all_anim.h (renamed from quantum/rgb_matrix_animations/cycle_all_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/cycle_left_right_anim.h (renamed from quantum/rgb_matrix_animations/cycle_left_right_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/cycle_out_in_anim.h (renamed from quantum/rgb_matrix_animations/cycle_out_in_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/cycle_out_in_dual_anim.h (renamed from quantum/rgb_matrix_animations/cycle_out_in_dual_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/cycle_pinwheel_anim.h (renamed from quantum/rgb_matrix_animations/cycle_pinwheel_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/cycle_spiral_anim.h (renamed from quantum/rgb_matrix_animations/cycle_spiral_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/cycle_up_down_anim.h (renamed from quantum/rgb_matrix_animations/cycle_up_down_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/digital_rain_anim.h (renamed from quantum/rgb_matrix_animations/digital_rain_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/dual_beacon_anim.h (renamed from quantum/rgb_matrix_animations/dual_beacon_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/gradient_left_right_anim.h (renamed from quantum/rgb_matrix_animations/gradient_left_right_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/gradient_up_down_anim.h (renamed from quantum/rgb_matrix_animations/gradient_up_down_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/hue_breathing_anim.h (renamed from quantum/rgb_matrix_animations/hue_breathing_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/hue_pendulum_anim.h (renamed from quantum/rgb_matrix_animations/hue_pendulum_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/hue_wave_anim.h (renamed from quantum/rgb_matrix_animations/hue_wave_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/jellybean_raindrops_anim.h (renamed from quantum/rgb_matrix_animations/jellybean_raindrops_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/rainbow_beacon_anim.h (renamed from quantum/rgb_matrix_animations/rainbow_beacon_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/rainbow_moving_chevron_anim.h (renamed from quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/rainbow_pinwheels_anim.h (renamed from quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/raindrops_anim.h (renamed from quantum/rgb_matrix_animations/raindrops_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/rgb_matrix_effects.inc37
-rw-r--r--quantum/rgb_matrix/animations/runners/effect_runner_dx_dy.h (renamed from quantum/rgb_matrix_runners/effect_runner_dx_dy.h)0
-rw-r--r--quantum/rgb_matrix/animations/runners/effect_runner_dx_dy_dist.h (renamed from quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h)0
-rw-r--r--quantum/rgb_matrix/animations/runners/effect_runner_i.h (renamed from quantum/rgb_matrix_runners/effect_runner_i.h)0
-rw-r--r--quantum/rgb_matrix/animations/runners/effect_runner_reactive.h (renamed from quantum/rgb_matrix_runners/effect_runner_reactive.h)0
-rw-r--r--quantum/rgb_matrix/animations/runners/effect_runner_reactive_splash.h (renamed from quantum/rgb_matrix_runners/effect_runner_reactive_splash.h)0
-rw-r--r--quantum/rgb_matrix/animations/runners/effect_runner_sin_cos_i.h (renamed from quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h)0
-rw-r--r--quantum/rgb_matrix/animations/runners/rgb_matrix_runners.inc6
-rw-r--r--quantum/rgb_matrix/animations/solid_color_anim.h (renamed from quantum/rgb_matrix_animations/solid_color_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/solid_reactive_anim.h (renamed from quantum/rgb_matrix_animations/solid_reactive_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/solid_reactive_cross.h (renamed from quantum/rgb_matrix_animations/solid_reactive_cross.h)0
-rw-r--r--quantum/rgb_matrix/animations/solid_reactive_nexus.h (renamed from quantum/rgb_matrix_animations/solid_reactive_nexus.h)0
-rw-r--r--quantum/rgb_matrix/animations/solid_reactive_simple_anim.h (renamed from quantum/rgb_matrix_animations/solid_reactive_simple_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/solid_reactive_wide.h (renamed from quantum/rgb_matrix_animations/solid_reactive_wide.h)0
-rw-r--r--quantum/rgb_matrix/animations/solid_splash_anim.h (renamed from quantum/rgb_matrix_animations/solid_splash_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/splash_anim.h (renamed from quantum/rgb_matrix_animations/splash_anim.h)0
-rw-r--r--quantum/rgb_matrix/animations/typing_heatmap_anim.h (renamed from quantum/rgb_matrix_animations/typing_heatmap_anim.h)0
-rw-r--r--quantum/rgb_matrix/rgb_matrix.c (renamed from quantum/rgb_matrix.c)48
-rw-r--r--quantum/rgb_matrix/rgb_matrix.h (renamed from quantum/rgb_matrix.h)5
-rw-r--r--quantum/rgb_matrix/rgb_matrix_drivers.c (renamed from quantum/rgb_matrix_drivers.c)39
-rw-r--r--quantum/rgb_matrix/rgb_matrix_types.h (renamed from quantum/rgb_matrix_types.h)0
-rw-r--r--quantum/rgb_matrix_animations/rgb_matrix_effects.inc37
-rw-r--r--quantum/rgblight/rgblight.c (renamed from quantum/rgblight.c)9
-rw-r--r--quantum/rgblight/rgblight.h (renamed from quantum/rgblight.h)1
-rw-r--r--quantum/rgblight/rgblight_breathe_table.h (renamed from quantum/rgblight_breathe_table.h)0
-rw-r--r--quantum/rgblight/rgblight_list.h (renamed from quantum/rgblight_list.h)46
-rw-r--r--quantum/rgblight/rgblight_modes.h (renamed from quantum/rgblight_modes.h)0
-rw-r--r--quantum/rgblight/rgblight_post_config.h (renamed from quantum/rgblight_post_config.h)0
-rw-r--r--quantum/send_string.c8
-rw-r--r--quantum/sequencer/sequencer.c2
-rw-r--r--quantum/sequencer/sequencer.h2
-rw-r--r--quantum/sequencer/tests/sequencer_tests.cpp26
-rw-r--r--quantum/serial_link/system/serial_link.c22
-rw-r--r--quantum/split_common/matrix.c309
-rw-r--r--quantum/split_common/post_config.h9
-rw-r--r--quantum/split_common/split_util.c6
-rw-r--r--quantum/split_common/transaction_id_define.h94
-rw-r--r--quantum/split_common/transactions.c655
-rw-r--r--quantum/split_common/transactions.h54
-rw-r--r--quantum/split_common/transport.c484
-rw-r--r--quantum/split_common/transport.h165
165 files changed, 4996 insertions, 2106 deletions
diff --git a/quantum/color.h b/quantum/color.h
index 4783f6839c..e2cfc46927 100644
--- a/quantum/color.h
+++ b/quantum/color.h
@@ -19,6 +19,60 @@
#include <stdint.h>
#include <stdbool.h>
+// clang-format off
+
+/*
+ * RGB Colors
+ */
+#define RGB_AZURE 0x99, 0xF5, 0xFF
+#define RGB_BLACK 0x00, 0x00, 0x00
+#define RGB_BLUE 0x00, 0x00, 0xFF
+#define RGB_CHARTREUSE 0x80, 0xFF, 0x00
+#define RGB_CORAL 0xFF, 0x7C, 0x4D
+#define RGB_CYAN 0x00, 0xFF, 0xFF
+#define RGB_GOLD 0xFF, 0xD9, 0x00
+#define RGB_GOLDENROD 0xD9, 0xA5, 0x21
+#define RGB_GREEN 0x00, 0xFF, 0x00
+#define RGB_MAGENTA 0xFF, 0x00, 0xFF
+#define RGB_ORANGE 0xFF, 0x80, 0x00
+#define RGB_PINK 0xFF, 0x80, 0xBF
+#define RGB_PURPLE 0x7A, 0x00, 0xFF
+#define RGB_RED 0xFF, 0x00, 0x00
+#define RGB_SPRINGGREEN 0x00, 0xFF, 0x80
+#define RGB_TEAL 0x00, 0x80, 0x80
+#define RGB_TURQUOISE 0x47, 0x6E, 0x6A
+#define RGB_WHITE 0xFF, 0xFF, 0xFF
+#define RGB_YELLOW 0xFF, 0xFF, 0x00
+#define RGB_OFF RGB_BLACK
+
+/*
+ * HSV Colors
+ *
+ * All values (including hue) are scaled to 0-255
+ */
+#define HSV_AZURE 132, 102, 255
+#define HSV_BLACK 0, 0, 0
+#define HSV_BLUE 170, 255, 255
+#define HSV_CHARTREUSE 64, 255, 255
+#define HSV_CORAL 11, 176, 255
+#define HSV_CYAN 128, 255, 255
+#define HSV_GOLD 36, 255, 255
+#define HSV_GOLDENROD 30, 218, 218
+#define HSV_GREEN 85, 255, 255
+#define HSV_MAGENTA 213, 255, 255
+#define HSV_ORANGE 28, 255, 255
+#define HSV_PINK 234, 128, 255
+#define HSV_PURPLE 191, 255, 255
+#define HSV_RED 0, 255, 255
+#define HSV_SPRINGGREEN 106, 255, 255
+#define HSV_TEAL 128, 255, 128
+#define HSV_TURQUOISE 123, 90, 112
+#define HSV_WHITE 0, 0, 255
+#define HSV_YELLOW 43, 255, 255
+#define HSV_OFF HSV_BLACK
+
+// clang-format on
+
#if defined(__GNUC__)
# define PACKED __attribute__((__packed__))
#else
diff --git a/quantum/command.c b/quantum/command.c
index 34c4b36b1c..3a7dc0f8ca 100644
--- a/quantum/command.c
+++ b/quantum/command.c
@@ -781,6 +781,6 @@ uint8_t numkey2num(uint8_t code) {
static void switch_default_layer(uint8_t layer) {
xprintf("L%d\n", layer);
- default_layer_set(1UL << layer);
+ default_layer_set((layer_state_t)1 << layer);
clear_keyboard();
}
diff --git a/quantum/crc.c b/quantum/crc.c
new file mode 100644
index 0000000000..0d8b9d6017
--- /dev/null
+++ b/quantum/crc.c
@@ -0,0 +1,59 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "crc.h"
+
+__attribute__((weak)) void crc_init(void){
+ /* Software implementation nothing todo here. */
+};
+
+#if defined(CRC8_USE_TABLE)
+/**
+ * Static table used for the table_driven implementation.
+ */
+static const crc_t crc_table[256] = {0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a,
+ 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3};
+
+__attribute__((weak)) uint8_t crc8(const void *data, size_t data_len) {
+ const uint8_t *d = (const uint8_t *)data;
+ crc_t crc = 0xff;
+ size_t tbl_idx;
+
+ while (data_len--) {
+ tbl_idx = crc ^ *d;
+ crc = crc_table[tbl_idx] & 0xff;
+ d++;
+ }
+ return crc & 0xff;
+}
+#else
+__attribute__((weak)) uint8_t crc8(const void *data, size_t data_len) {
+ const uint8_t *d = (const uint8_t *)data;
+ crc_t crc = 0xff;
+ size_t i, j;
+
+ for (i = 0; i < data_len; i++) {
+ crc ^= d[i];
+ for (j = 0; j < 8; j++) {
+ if ((crc & 0x80) != 0)
+ crc = (crc_t)((crc << 1) ^ 0x31);
+ else
+ crc <<= 1;
+ }
+ }
+ return crc;
+}
+#endif \ No newline at end of file
diff --git a/quantum/rgb.h b/quantum/crc.h
index 2602fc0b20..c17f5888e2 100644
--- a/quantum/rgb.h
+++ b/quantum/crc.h
@@ -1,4 +1,4 @@
-/* Copyright 2017 Jack Humbert
+/* Copyright 2021 QMK
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,24 +16,29 @@
#pragma once
-__attribute__((weak)) void rgblight_toggle(void){};
+#include "quantum.h"
-__attribute__((weak)) void rgblight_step(void){};
-
-__attribute__((weak)) void rgblight_step_reverse(void){};
-
-__attribute__((weak)) void rgblight_increase_hue(void){};
-
-__attribute__((weak)) void rgblight_decrease_hue(void){};
-
-__attribute__((weak)) void rgblight_increase_sat(void){};
-
-__attribute__((weak)) void rgblight_decrease_sat(void){};
-
-__attribute__((weak)) void rgblight_increase_val(void){};
-
-__attribute__((weak)) void rgblight_decrease_val(void){};
-
-__attribute__((weak)) void rgblight_increase_speed(void){};
+/**
+ * The type of the CRC values.
+ *
+ * This type must be big enough to contain at least 8 bits.
+ */
+#if defined(CRC8_OPTIMIZE_SPEED)
+typedef uint_fast8_t crc_t;
+#else
+typedef uint_least8_t crc_t;
+#endif
+
+/**
+ * Initialize crc subsystem.
+ */
+__attribute__((weak)) void crc_init(void);
-__attribute__((weak)) void rgblight_decrease_speed(void){};
+/**
+ * Generate CRC8 value from given data.
+ *
+ * \param[in] data Pointer to a buffer of \a data_len bytes.
+ * \param[in] data_len Number of bytes in the \a data buffer.
+ * \return The calculated crc value.
+ */
+__attribute__((weak)) uint8_t crc8(const void *data, size_t data_len); \ No newline at end of file
diff --git a/quantum/debounce.h b/quantum/debounce.h
index 9ca05c6824..5043868289 100644
--- a/quantum/debounce.h
+++ b/quantum/debounce.h
@@ -9,3 +9,5 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
bool debounce_active(void);
void debounce_init(uint8_t num_rows);
+
+void debounce_free(void);
diff --git a/quantum/debounce/asym_eager_defer_pk.c b/quantum/debounce/asym_eager_defer_pk.c
new file mode 100644
index 0000000000..24380dc5e5
--- /dev/null
+++ b/quantum/debounce/asym_eager_defer_pk.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2017 Alex Ong <the.onga@gmail.com>
+ * Copyright 2020 Andrei Purdea <andrei@purdea.ro>
+ * Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+Basic symmetric per-key algorithm. Uses an 8-bit counter per key.
+When no state changes have occured for DEBOUNCE milliseconds, we push the state.
+*/
+
+#include "matrix.h"
+#include "timer.h"
+#include "quantum.h"
+#include <stdlib.h>
+
+#ifdef PROTOCOL_CHIBIOS
+# if CH_CFG_USE_MEMCORE == FALSE
+# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
+# endif
+#endif
+
+#ifndef DEBOUNCE
+# define DEBOUNCE 5
+#endif
+
+// Maximum debounce: 127ms
+#if DEBOUNCE > 127
+# undef DEBOUNCE
+# define DEBOUNCE 127
+#endif
+
+#define ROW_SHIFTER ((matrix_row_t)1)
+
+typedef struct {
+ bool pressed : 1;
+ uint8_t time : 7;
+} debounce_counter_t;
+
+#if DEBOUNCE > 0
+static debounce_counter_t *debounce_counters;
+static fast_timer_t last_time;
+static bool counters_need_update;
+static bool matrix_need_update;
+
+#define DEBOUNCE_ELAPSED 0
+
+static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
+static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
+
+// we use num_rows rather than MATRIX_ROWS to support split keyboards
+void debounce_init(uint8_t num_rows) {
+ debounce_counters = malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));
+ int i = 0;
+ for (uint8_t r = 0; r < num_rows; r++) {
+ for (uint8_t c = 0; c < MATRIX_COLS; c++) {
+ debounce_counters[i++].time = DEBOUNCE_ELAPSED;
+ }
+ }
+}
+
+void debounce_free(void) {
+ free(debounce_counters);
+ debounce_counters = NULL;
+}
+
+void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
+ bool updated_last = false;
+
+ if (counters_need_update) {
+ fast_timer_t now = timer_read_fast();
+ fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
+
+ last_time = now;
+ updated_last = true;
+ if (elapsed_time > UINT8_MAX) {
+ elapsed_time = UINT8_MAX;
+ }
+
+ if (elapsed_time > 0) {
+ update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time);
+ }
+ }
+
+ if (changed || matrix_need_update) {
+ if (!updated_last) {
+ last_time = timer_read_fast();
+ }
+
+ transfer_matrix_values(raw, cooked, num_rows);
+ }
+}
+
+static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) {
+ debounce_counter_t *debounce_pointer = debounce_counters;
+
+ counters_need_update = false;
+ matrix_need_update = false;
+
+ for (uint8_t row = 0; row < num_rows; row++) {
+ for (uint8_t col = 0; col < MATRIX_COLS; col++) {
+ matrix_row_t col_mask = (ROW_SHIFTER << col);
+
+ if (debounce_pointer->time != DEBOUNCE_ELAPSED) {
+ if (debounce_pointer->time <= elapsed_time) {
+ debounce_pointer->time = DEBOUNCE_ELAPSED;
+
+ if (debounce_pointer->pressed) {
+ // key-down: eager
+ matrix_need_update = true;
+ } else {
+ // key-up: defer
+ cooked[row] = (cooked[row] & ~col_mask) | (raw[row] & col_mask);
+ }
+ } else {
+ debounce_pointer->time -= elapsed_time;
+ counters_need_update = true;
+ }
+ }
+ debounce_pointer++;
+ }
+ }
+}
+
+static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
+ debounce_counter_t *debounce_pointer = debounce_counters;
+
+ for (uint8_t row = 0; row < num_rows; row++) {
+ matrix_row_t delta = raw[row] ^ cooked[row];
+ for (uint8_t col = 0; col < MATRIX_COLS; col++) {
+ matrix_row_t col_mask = (ROW_SHIFTER << col);
+
+ if (delta & col_mask) {
+ if (debounce_pointer->time == DEBOUNCE_ELAPSED) {
+ debounce_pointer->pressed = (raw[row] & col_mask);
+ debounce_pointer->time = DEBOUNCE;
+ counters_need_update = true;
+
+ if (debounce_pointer->pressed) {
+ // key-down: eager
+ cooked[row] ^= col_mask;
+ }
+ }
+ } else if (debounce_pointer->time != DEBOUNCE_ELAPSED) {
+ if (!debounce_pointer->pressed) {
+ // key-up: defer
+ debounce_pointer->time = DEBOUNCE_ELAPSED;
+ }
+ }
+ debounce_pointer++;
+ }
+ }
+}
+
+bool debounce_active(void) { return true; }
+#else
+# include "none.c"
+#endif
diff --git a/quantum/debounce/none.c b/quantum/debounce/none.c
new file mode 100644
index 0000000000..b03892bc5b
--- /dev/null
+++ b/quantum/debounce/none.c
@@ -0,0 +1,31 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "matrix.h"
+#include "quantum.h"
+#include <stdlib.h>
+
+void debounce_init(uint8_t num_rows) {}
+
+void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
+ for (int i = 0; i < num_rows; i++) {
+ cooked[i] = raw[i];
+ }
+}
+
+bool debounce_active(void) { return false; }
+
+void debounce_free(void) {}
diff --git a/quantum/debounce/sym_defer_g.c b/quantum/debounce/sym_defer_g.c
index 3ed9055d2a..fbefd55ede 100644
--- a/quantum/debounce/sym_defer_g.c
+++ b/quantum/debounce/sym_defer_g.c
@@ -1,5 +1,6 @@
/*
Copyright 2017 Alex Ong<the.onga@gmail.com>
+Copyright 2021 Simon Arlott
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
@@ -23,30 +24,29 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
# define DEBOUNCE 5
#endif
-void debounce_init(uint8_t num_rows) {}
+#if DEBOUNCE > 0
static bool debouncing = false;
+static fast_timer_t debouncing_time;
-#if DEBOUNCE > 0
-static uint16_t debouncing_time;
-void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
+void debounce_init(uint8_t num_rows) {}
+
+void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
if (changed) {
debouncing = true;
- debouncing_time = timer_read();
+ debouncing_time = timer_read_fast();
}
- if (debouncing && timer_elapsed(debouncing_time) > DEBOUNCE) {
+ if (debouncing && timer_elapsed_fast(debouncing_time) >= DEBOUNCE) {
for (int i = 0; i < num_rows; i++) {
cooked[i] = raw[i];
}
debouncing = false;
}
}
-#else // no debouncing.
-void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
- for (int i = 0; i < num_rows; i++) {
- cooked[i] = raw[i];
- }
-}
-#endif
bool debounce_active(void) { return debouncing; }
+
+void debounce_free(void) {}
+#else // no debouncing.
+# include "none.c"
+#endif
diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c
index 60513f98e1..626a9be841 100644
--- a/quantum/debounce/sym_defer_pk.c
+++ b/quantum/debounce/sym_defer_pk.c
@@ -1,6 +1,7 @@
/*
Copyright 2017 Alex Ong<the.onga@gmail.com>
Copyright 2020 Andrei Purdea<andrei@purdea.ro>
+Copyright 2021 Simon Arlott
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
@@ -33,28 +34,25 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
# define DEBOUNCE 5
#endif
+// Maximum debounce: 255ms
+#if DEBOUNCE > UINT8_MAX
+# undef DEBOUNCE
+# define DEBOUNCE UINT8_MAX
+#endif
+
#define ROW_SHIFTER ((matrix_row_t)1)
-#define debounce_counter_t uint8_t
+typedef uint8_t debounce_counter_t;
+#if DEBOUNCE > 0
static debounce_counter_t *debounce_counters;
+static fast_timer_t last_time;
static bool counters_need_update;
-#define DEBOUNCE_ELAPSED 251
-#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1)
-
-static uint8_t wrapping_timer_read(void) {
- static uint16_t time = 0;
- static uint8_t last_result = 0;
- uint16_t new_time = timer_read();
- uint16_t diff = new_time - time;
- time = new_time;
- last_result = (last_result + diff) % (MAX_DEBOUNCE + 1);
- return last_result;
-}
+#define DEBOUNCE_ELAPSED 0
-void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time);
-void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time);
+static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
+static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
// we use num_rows rather than MATRIX_ROWS to support split keyboards
void debounce_init(uint8_t num_rows) {
@@ -67,27 +65,49 @@ void debounce_init(uint8_t num_rows) {
}
}
+void debounce_free(void) {
+ free(debounce_counters);
+ debounce_counters = NULL;
+}
+
void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
- uint8_t current_time = wrapping_timer_read();
+ bool updated_last = false;
+
if (counters_need_update) {
- update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, current_time);
+ fast_timer_t now = timer_read_fast();
+ fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
+
+ last_time = now;
+ updated_last = true;
+ if (elapsed_time > UINT8_MAX) {
+ elapsed_time = UINT8_MAX;
+ }
+
+ if (elapsed_time > 0) {
+ update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time);
+ }
}
if (changed) {
- start_debounce_counters(raw, cooked, num_rows, current_time);
+ if (!updated_last) {
+ last_time = timer_read_fast();
+ }
+
+ start_debounce_counters(raw, cooked, num_rows);
}
}
-void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) {
+static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) {
counters_need_update = false;
debounce_counter_t *debounce_pointer = debounce_counters;
for (uint8_t row = 0; row < num_rows; row++) {
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
if (*debounce_pointer != DEBOUNCE_ELAPSED) {
- if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) {
+ if (*debounce_pointer <= elapsed_time) {
*debounce_pointer = DEBOUNCE_ELAPSED;
cooked[row] = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col));
} else {
+ *debounce_pointer -= elapsed_time;
counters_need_update = true;
}
}
@@ -96,14 +116,14 @@ void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix
}
}
-void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) {
+static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
debounce_counter_t *debounce_pointer = debounce_counters;
for (uint8_t row = 0; row < num_rows; row++) {
matrix_row_t delta = raw[row] ^ cooked[row];
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
if (delta & (ROW_SHIFTER << col)) {
if (*debounce_pointer == DEBOUNCE_ELAPSED) {
- *debounce_pointer = current_time;
+ *debounce_pointer = DEBOUNCE;
counters_need_update = true;
}
} else {
@@ -115,3 +135,6 @@ void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t
}
bool debounce_active(void) { return true; }
+#else
+# include "none.c"
+#endif
diff --git a/quantum/debounce/sym_eager_pk.c b/quantum/debounce/sym_eager_pk.c
index e66cf92d79..15a3242e68 100644
--- a/quantum/debounce/sym_eager_pk.c
+++ b/quantum/debounce/sym_eager_pk.c
@@ -1,5 +1,6 @@
/*
Copyright 2017 Alex Ong<the.onga@gmail.com>
+Copyright 2021 Simon Arlott
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
@@ -33,29 +34,26 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
# define DEBOUNCE 5
#endif
+// Maximum debounce: 255ms
+#if DEBOUNCE > UINT8_MAX
+# undef DEBOUNCE
+# define DEBOUNCE UINT8_MAX
+#endif
+
#define ROW_SHIFTER ((matrix_row_t)1)
-#define debounce_counter_t uint8_t
+typedef uint8_t debounce_counter_t;
+#if DEBOUNCE > 0
static debounce_counter_t *debounce_counters;
+static fast_timer_t last_time;
static bool counters_need_update;
static bool matrix_need_update;
-#define DEBOUNCE_ELAPSED 251
-#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1)
-
-static uint8_t wrapping_timer_read(void) {
- static uint16_t time = 0;
- static uint8_t last_result = 0;
- uint16_t new_time = timer_read();
- uint16_t diff = new_time - time;
- time = new_time;
- last_result = (last_result + diff) % (MAX_DEBOUNCE + 1);
- return last_result;
-}
+#define DEBOUNCE_ELAPSED 0
-void update_debounce_counters(uint8_t num_rows, uint8_t current_time);
-void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time);
+static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
+static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
// we use num_rows rather than MATRIX_ROWS to support split keyboards
void debounce_init(uint8_t num_rows) {
@@ -68,27 +66,51 @@ void debounce_init(uint8_t num_rows) {
}
}
+void debounce_free(void) {
+ free(debounce_counters);
+ debounce_counters = NULL;
+}
+
void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
- uint8_t current_time = wrapping_timer_read();
+ bool updated_last = false;
+
if (counters_need_update) {
- update_debounce_counters(num_rows, current_time);
+ fast_timer_t now = timer_read_fast();
+ fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
+
+ last_time = now;
+ updated_last = true;
+ if (elapsed_time > UINT8_MAX) {
+ elapsed_time = UINT8_MAX;
+ }
+
+ if (elapsed_time > 0) {
+ update_debounce_counters(num_rows, elapsed_time);
+ }
}
if (changed || matrix_need_update) {
- transfer_matrix_values(raw, cooked, num_rows, current_time);
+ if (!updated_last) {
+ last_time = timer_read_fast();
+ }
+
+ transfer_matrix_values(raw, cooked, num_rows);
}
}
// If the current time is > debounce counter, set the counter to enable input.
-void update_debounce_counters(uint8_t num_rows, uint8_t current_time) {
+static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {
counters_need_update = false;
+ matrix_need_update = false;
debounce_counter_t *debounce_pointer = debounce_counters;
for (uint8_t row = 0; row < num_rows; row++) {
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
if (*debounce_pointer != DEBOUNCE_ELAPSED) {
- if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) {
+ if (*debounce_pointer <= elapsed_time) {
*debounce_pointer = DEBOUNCE_ELAPSED;
+ matrix_need_update = true;
} else {
+ *debounce_pointer -= elapsed_time;
counters_need_update = true;
}
}
@@ -98,8 +120,7 @@ void update_debounce_counters(uint8_t num_rows, uint8_t current_time) {
}
// upload from raw_matrix to final matrix;
-void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) {
- matrix_need_update = false;
+static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
debounce_counter_t *debounce_pointer = debounce_counters;
for (uint8_t row = 0; row < num_rows; row++) {
matrix_row_t delta = raw[row] ^ cooked[row];
@@ -108,11 +129,9 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n
matrix_row_t col_mask = (ROW_SHIFTER << col);
if (delta & col_mask) {
if (*debounce_pointer == DEBOUNCE_ELAPSED) {
- *debounce_pointer = current_time;
+ *debounce_pointer = DEBOUNCE;
counters_need_update = true;
existing_row ^= col_mask; // flip the bit.
- } else {
- matrix_need_update = true;
}
}
debounce_pointer++;
@@ -122,3 +141,6 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n
}
bool debounce_active(void) { return true; }
+#else
+# include "none.c"
+#endif
diff --git a/quantum/debounce/sym_eager_pr.c b/quantum/debounce/sym_eager_pr.c
index 20ccb46f1d..2ad592c5a6 100644
--- a/quantum/debounce/sym_eager_pr.c
+++ b/quantum/debounce/sym_eager_pr.c
@@ -1,5 +1,6 @@
/*
Copyright 2019 Alex Ong<the.onga@gmail.com>
+Copyright 2021 Simon Arlott
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
@@ -33,27 +34,25 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
# define DEBOUNCE 5
#endif
-#define debounce_counter_t uint8_t
+// Maximum debounce: 255ms
+#if DEBOUNCE > UINT8_MAX
+# undef DEBOUNCE
+# define DEBOUNCE UINT8_MAX
+#endif
+
+typedef uint8_t debounce_counter_t;
+
+#if DEBOUNCE > 0
static bool matrix_need_update;
static debounce_counter_t *debounce_counters;
+static fast_timer_t last_time;
static bool counters_need_update;
-#define DEBOUNCE_ELAPSED 251
-#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1)
-
-static uint8_t wrapping_timer_read(void) {
- static uint16_t time = 0;
- static uint8_t last_result = 0;
- uint16_t new_time = timer_read();
- uint16_t diff = new_time - time;
- time = new_time;
- last_result = (last_result + diff) % (MAX_DEBOUNCE + 1);
- return last_result;
-}
+#define DEBOUNCE_ELAPSED 0
-void update_debounce_counters(uint8_t num_rows, uint8_t current_time);
-void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time);
+static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
+static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
// we use num_rows rather than MATRIX_ROWS to support split keyboards
void debounce_init(uint8_t num_rows) {
@@ -63,27 +62,50 @@ void debounce_init(uint8_t num_rows) {
}
}
+void debounce_free(void) {
+ free(debounce_counters);
+ debounce_counters = NULL;
+}
+
void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
- uint8_t current_time = wrapping_timer_read();
- bool needed_update = counters_need_update;
+ bool updated_last = false;
+
if (counters_need_update) {
- update_debounce_counters(num_rows, current_time);
+ fast_timer_t now = timer_read_fast();
+ fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
+
+ last_time = now;
+ updated_last = true;
+ if (elapsed_time > UINT8_MAX) {
+ elapsed_time = UINT8_MAX;
+ }
+
+ if (elapsed_time > 0) {
+ update_debounce_counters(num_rows, elapsed_time);
+ }
}
- if (changed || (needed_update && !counters_need_update) || matrix_need_update) {
- transfer_matrix_values(raw, cooked, num_rows, current_time);
+ if (changed || matrix_need_update) {
+ if (!updated_last) {
+ last_time = timer_read_fast();
+ }
+
+ transfer_matrix_values(raw, cooked, num_rows);
}
}
// If the current time is > debounce counter, set the counter to enable input.
-void update_debounce_counters(uint8_t num_rows, uint8_t current_time) {
+static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {
counters_need_update = false;
+ matrix_need_update = false;
debounce_counter_t *debounce_pointer = debounce_counters;
for (uint8_t row = 0; row < num_rows; row++) {
if (*debounce_pointer != DEBOUNCE_ELAPSED) {
- if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) {
+ if (*debounce_pointer <= elapsed_time) {
*debounce_pointer = DEBOUNCE_ELAPSED;
+ matrix_need_update = true;
} else {
+ *debounce_pointer -= elapsed_time;
counters_need_update = true;
}
}
@@ -92,8 +114,7 @@ void update_debounce_counters(uint8_t num_rows, uint8_t current_time) {
}
// upload from raw_matrix to final matrix;
-void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) {
- matrix_need_update = false;
+static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
debounce_counter_t *debounce_pointer = debounce_counters;
for (uint8_t row = 0; row < num_rows; row++) {
matrix_row_t existing_row = cooked[row];
@@ -102,11 +123,9 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n
// determine new value basd on debounce pointer + raw value
if (existing_row != raw_row) {
if (*debounce_pointer == DEBOUNCE_ELAPSED) {
- *debounce_pointer = current_time;
+ *debounce_pointer = DEBOUNCE;
cooked[row] = raw_row;
counters_need_update = true;
- } else {
- matrix_need_update = true;
}
}
debounce_pointer++;
@@ -114,3 +133,6 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n
}
bool debounce_active(void) { return true; }
+#else
+# include "none.c"
+#endif
diff --git a/quantum/debounce/tests/asym_eager_defer_pk_tests.cpp b/quantum/debounce/tests/asym_eager_defer_pk_tests.cpp
new file mode 100644
index 0000000000..fe374c3dfa
--- /dev/null
+++ b/quantum/debounce/tests/asym_eager_defer_pk_tests.cpp
@@ -0,0 +1,374 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtest/gtest.h"
+
+#include "debounce_test_common.h"
+
+TEST_F(DebounceTest, OneKeyShort1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ /* Release key after 1ms delay */
+ {1, {{0, 1, UP}}, {}},
+
+ /*
+ * Until the eager timer on DOWN is observed to finish, the defer timer
+ * on UP can't start. There's no workaround for this because it's not
+ * possible to debounce an event that isn't being tracked.
+ *
+ * sym_defer_pk has the same problem but the test has to track that the
+ * key changed state so the DOWN timer is always allowed to finish
+ * before starting the UP timer.
+ */
+ {5, {}, {}},
+
+ {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */
+ /* Press key again after 1ms delay */
+ {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ /* Release key after 2ms delay */
+ {2, {{0, 1, UP}}, {}},
+
+ {5, {}, {}}, /* See OneKeyShort1 */
+
+ {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */
+ /* Press key again after 1ms delay */
+ {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort3) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ /* Release key after 3ms delay */
+ {3, {{0, 1, UP}}, {}},
+
+ {5, {}, {}}, /* See OneKeyShort1 */
+
+ {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */
+ /* Press key again after 1ms delay */
+ {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort4) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ /* Release key after 4ms delay */
+ {4, {{0, 1, UP}}, {}},
+
+ {5, {}, {}}, /* See OneKeyShort1 */
+
+ {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */
+ /* Press key again after 1ms delay */
+ {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort5) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Release key after 5ms delay */
+ {5, {{0, 1, UP}}, {}},
+
+ {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */
+ /* Press key again after 1ms delay */
+ {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort6) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Release key after 6ms delay */
+ {6, {{0, 1, UP}}, {}},
+
+ {11, {}, {{0, 1, UP}}}, /* 5ms after UP at time 6 */
+ /* Press key again after 1ms delay */
+ {12, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort7) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Release key after 7ms delay */
+ {7, {{0, 1, UP}}, {}},
+
+ {12, {}, {{0, 1, UP}}}, /* 5ms after UP at time 7 */
+ /* Press key again after 1ms delay */
+ {13, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort8) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ /* Release key after 1ms delay */
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {}}, /* See OneKeyShort1 */
+
+ {10, {}, {{0, 1, UP}}}, /* 5ms after UP at time 7 */
+ /* Press key again after 0ms delay (scan 2) */
+ {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort9) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ /* Release key after 1ms delay */
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {}}, /* See OneKeyShort1 */
+
+ /* Press key again after 0ms delay (same scan) before debounce finishes */
+ {10, {{0, 1, DOWN}}, {}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+ {2, {{0, 1, DOWN}}, {}},
+ {3, {{0, 1, UP}}, {}},
+ {4, {{0, 1, DOWN}}, {}},
+ {5, {{0, 1, UP}}, {}},
+ {6, {{0, 1, DOWN}}, {}},
+ {7, {{0, 1, UP}}, {}},
+ {8, {{0, 1, DOWN}}, {}},
+ {9, {{0, 1, UP}}, {}},
+ {10, {{0, 1, DOWN}}, {}},
+ {11, {{0, 1, UP}}, {}},
+ {12, {{0, 1, DOWN}}, {}},
+ {13, {{0, 1, UP}}, {}},
+ {14, {{0, 1, DOWN}}, {}},
+ {15, {{0, 1, UP}}, {}},
+
+ {20, {}, {{0, 1, UP}}},
+ /* Press key again after 1ms delay */
+ {21, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ /* Change twice in the same time period */
+ {1, {{0, 1, UP}}, {}},
+ {1, {{0, 1, DOWN}}, {}},
+ /* Change three times in the same time period */
+ {2, {{0, 1, UP}}, {}},
+ {2, {{0, 1, DOWN}}, {}},
+ {2, {{0, 1, UP}}, {}},
+ /* Change twice in the same time period */
+ {6, {{0, 1, DOWN}}, {}},
+ {6, {{0, 1, UP}}, {}},
+ /* Change three times in the same time period */
+ {7, {{0, 1, DOWN}}, {}},
+ {7, {{0, 1, UP}}, {}},
+ {7, {{0, 1, DOWN}}, {}},
+ /* Change twice in the same time period */
+ {8, {{0, 1, UP}}, {}},
+ {8, {{0, 1, DOWN}}, {}},
+ /* Change three times in the same time period */
+ {9, {{0, 1, UP}}, {}},
+ {9, {{0, 1, DOWN}}, {}},
+ {9, {{0, 1, UP}}, {}},
+
+ {14, {}, {{0, 1, UP}}},
+ /* Press key again after 1ms delay */
+ {15, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyLong) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ {25, {{0, 1, UP}}, {}},
+
+ {30, {}, {{0, 1, UP}}},
+
+ {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ {75, {{0, 1, UP}}, {}},
+
+ {80, {}, {{0, 1, UP}}},
+
+ {100, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysShort) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 2, DOWN}}, {{0, 2, DOWN}}},
+ /* Release key after 2ms delay */
+ {2, {{0, 1, UP}}, {}},
+ {3, {{0, 2, UP}}, {}},
+
+ {5, {}, {}}, /* See OneKeyShort1 */
+ {6, {}, {}}, /* See OneKeyShort1 */
+
+ {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */
+ /* Press key again after 1ms delay */
+ {11, {{0, 1, DOWN}}, {{0, 1, DOWN}, {0, 2, UP}}}, /* 5ms+5ms after DOWN at time 0 */
+ {12, {{0, 2, DOWN}}, {{0, 2, DOWN}}}, /* 5ms+5ms after DOWN at time 0 */
+ });
+ runEvents();
+}
+
+
+TEST_F(DebounceTest, OneKeyDelayedScan1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late, immediately release key */
+ {300, {{0, 1, UP}}, {}},
+
+ {305, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late, immediately release key */
+ {300, {{0, 1, UP}}, {}},
+
+ /* Processing is very late again */
+ {600, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan3) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late */
+ {300, {}, {}},
+ /* Release key after 1ms */
+ {301, {{0, 1, UP}}, {}},
+
+ {306, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan4) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late */
+ {300, {}, {}},
+ /* Release key after 1ms */
+ {301, {{0, 1, UP}}, {}},
+
+ /* Processing is very late again */
+ {600, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan5) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ {5, {{0, 1, UP}}, {}},
+
+ /* Processing is very late */
+ {300, {}, {{0, 1, UP}}},
+ /* Immediately press key again */
+ {300, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan6) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ {5, {{0, 1, UP}}, {}},
+
+ /* Processing is very late */
+ {300, {}, {{0, 1, UP}}},
+
+ /* Press key again after 1ms */
+ {301, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan7) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ {5, {{0, 1, UP}}, {}},
+
+ /* Press key again before debounce expires */
+ {300, {{0, 1, DOWN}}, {}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan8) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is a bit late */
+ {50, {}, {}},
+ /* Release key after 1ms */
+ {51, {{0, 1, UP}}, {}},
+
+ /* Processing is a bit late again */
+ {100, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
diff --git a/quantum/debounce/tests/debounce_test_common.cpp b/quantum/debounce/tests/debounce_test_common.cpp
new file mode 100644
index 0000000000..1c5e7c9f4e
--- /dev/null
+++ b/quantum/debounce/tests/debounce_test_common.cpp
@@ -0,0 +1,229 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtest/gtest.h"
+
+#include "debounce_test_common.h"
+
+#include <algorithm>
+#include <iomanip>
+#include <sstream>
+
+extern "C" {
+#include "quantum.h"
+#include "timer.h"
+#include "debounce.h"
+
+void set_time(uint32_t t);
+void advance_time(uint32_t ms);
+}
+
+void DebounceTest::addEvents(std::initializer_list<DebounceTestEvent> events) {
+ events_.insert(events_.end(), events.begin(), events.end());
+}
+
+void DebounceTest::runEvents() {
+ /* Run the test multiple times, from 1kHz to 10kHz scan rate */
+ for (extra_iterations_ = 0; extra_iterations_ < 10; extra_iterations_++) {
+ if (time_jumps_) {
+ /* Don't advance time smoothly, jump to the next event (some tests require this) */
+ auto_advance_time_ = false;
+ runEventsInternal();
+ } else {
+ /* Run the test with both smooth and irregular time; it must produce the same result */
+ auto_advance_time_ = true;
+ runEventsInternal();
+ auto_advance_time_ = false;
+ runEventsInternal();
+ }
+ }
+}
+
+void DebounceTest::runEventsInternal() {
+ fast_timer_t previous = 0;
+ bool first = true;
+
+ /* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */
+ debounce_init(MATRIX_ROWS);
+ set_time(time_offset_);
+ std::fill(std::begin(input_matrix_), std::end(input_matrix_), 0);
+ std::fill(std::begin(output_matrix_), std::end(output_matrix_), 0);
+
+ for (auto &event : events_) {
+ if (!auto_advance_time_) {
+ /* Jump to the next event */
+ set_time(time_offset_ + event.time_);
+ } else if (!first && event.time_ == previous + 1) {
+ /* This event immediately follows the previous one, don't make extra debounce() calls */
+ advance_time(1);
+ } else {
+ /* Fast forward to the time for this event, calling debounce() with no changes */
+ ASSERT_LT((time_offset_ + event.time_) - timer_read_fast(), 60000) << "Test tries to advance more than 1 minute of time";
+
+ while (timer_read_fast() != time_offset_ + event.time_) {
+ runDebounce(false);
+ checkCookedMatrix(false, "debounce() modified cooked matrix");
+ advance_time(1);
+ }
+ }
+
+ first = false;
+ previous = event.time_;
+
+ /* Prepare input matrix */
+ for (auto &input : event.inputs_) {
+ matrixUpdate(input_matrix_, "input", input);
+ }
+
+ /* Call debounce */
+ runDebounce(!event.inputs_.empty());
+
+ /* Prepare output matrix */
+ for (auto &output : event.outputs_) {
+ matrixUpdate(output_matrix_, "output", output);
+ }
+
+ /* Check output matrix has expected change events */
+ for (auto &output : event.outputs_) {
+ EXPECT_EQ(!!(cooked_matrix_[output.row_] & (1U << output.col_)), directionValue(output.direction_))
+ << "Missing event at " << strTime()
+ << " expected key " << output.row_ << "," << output.col_ << " " << directionLabel(output.direction_)
+ << "\ninput_matrix: changed=" << !event.inputs_.empty() << "\n" << strMatrix(input_matrix_)
+ << "\nexpected_matrix:\n" << strMatrix(output_matrix_)
+ << "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
+ }
+
+ /* Check output matrix has no other changes */
+ checkCookedMatrix(!event.inputs_.empty(), "debounce() cooked matrix does not match expected output matrix");
+
+ /* Perform some extra iterations of the matrix scan with no changes */
+ for (int i = 0; i < extra_iterations_; i++) {
+ runDebounce(false);
+ checkCookedMatrix(false, "debounce() modified cooked matrix");
+ }
+ }
+
+ /* Check that no further changes happen for 1 minute */
+ for (int i = 0; i < 60000; i++) {
+ runDebounce(false);
+ checkCookedMatrix(false, "debounce() modified cooked matrix");
+ advance_time(1);
+ }
+
+ debounce_free();
+}
+
+void DebounceTest::runDebounce(bool changed) {
+ std::copy(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_));
+ std::copy(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_));
+
+ debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed);
+
+ if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) {
+ FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime()
+ << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_)
+ << "\nraw_matrix:\n" << strMatrix(raw_matrix_);
+ }
+}
+
+void DebounceTest::checkCookedMatrix(bool changed, const std::string &error_message) {
+ if (!std::equal(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_))) {
+ FAIL() << "Unexpected event: " << error_message << " at " << strTime()
+ << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_)
+ << "\nexpected_matrix:\n" << strMatrix(output_matrix_)
+ << "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
+ }
+}
+
+std::string DebounceTest::strTime() {
+ std::stringstream text;
+
+ text << "time " << (timer_read_fast() - time_offset_)
+ << " (extra_iterations=" << extra_iterations_
+ << ", auto_advance_time=" << auto_advance_time_ << ")";
+
+ return text.str();
+}
+
+std::string DebounceTest::strMatrix(matrix_row_t matrix[]) {
+ std::stringstream text;
+
+ text << "\t" << std::setw(3) << "";
+ for (int col = 0; col < MATRIX_COLS; col++) {
+ text << " " << std::setw(2) << col;
+ }
+ text << "\n";
+
+ for (int row = 0; row < MATRIX_ROWS; row++) {
+ text << "\t" << std::setw(2) << row << ":";
+ for (int col = 0; col < MATRIX_COLS; col++) {
+ text << ((matrix[row] & (1U << col)) ? " XX" : " __");
+ }
+
+ text << "\n";
+ }
+
+ return text.str();
+}
+
+bool DebounceTest::directionValue(Direction direction) {
+ switch (direction) {
+ case DOWN:
+ return true;
+
+ case UP:
+ return false;
+ }
+}
+
+std::string DebounceTest::directionLabel(Direction direction) {
+ switch (direction) {
+ case DOWN:
+ return "DOWN";
+
+ case UP:
+ return "UP";
+ }
+}
+
+/* Modify a matrix and verify that events always specify a change */
+void DebounceTest::matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event) {
+ ASSERT_NE(!!(matrix[event.row_] & (1U << event.col_)), directionValue(event.direction_))
+ << "Test " << name << " at " << strTime()
+ << " sets key " << event.row_ << "," << event.col_ << " " << directionLabel(event.direction_)
+ << " but it is already " << directionLabel(event.direction_)
+ << "\n" << name << "_matrix:\n" << strMatrix(matrix);
+
+ switch (event.direction_) {
+ case DOWN:
+ matrix[event.row_] |= (1U << event.col_);
+ break;
+
+ case UP:
+ matrix[event.row_] &= ~(1U << event.col_);
+ break;
+ }
+}
+
+DebounceTestEvent::DebounceTestEvent(fast_timer_t time,
+ std::initializer_list<MatrixTestEvent> inputs,
+ std::initializer_list<MatrixTestEvent> outputs)
+ : time_(time), inputs_(inputs), outputs_(outputs) {
+}
+
+MatrixTestEvent::MatrixTestEvent(int row, int col, Direction direction)
+ : row_(row), col_(col), direction_(direction) {
+}
diff --git a/quantum/debounce/tests/debounce_test_common.h b/quantum/debounce/tests/debounce_test_common.h
new file mode 100644
index 0000000000..d87e310594
--- /dev/null
+++ b/quantum/debounce/tests/debounce_test_common.h
@@ -0,0 +1,83 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtest/gtest.h"
+
+#include <initializer_list>
+#include <list>
+#include <string>
+
+extern "C" {
+#include "quantum.h"
+#include "timer.h"
+}
+
+enum Direction {
+ DOWN,
+ UP,
+};
+
+class MatrixTestEvent {
+public:
+ MatrixTestEvent(int row, int col, Direction direction);
+
+ const int row_;
+ const int col_;
+ const Direction direction_;
+};
+
+class DebounceTestEvent {
+public:
+ // 0, {{0, 1, DOWN}}, {{0, 1, DOWN}})
+ DebounceTestEvent(fast_timer_t time,
+ std::initializer_list<MatrixTestEvent> inputs,
+ std::initializer_list<MatrixTestEvent> outputs);
+
+ const fast_timer_t time_;
+ const std::list<MatrixTestEvent> inputs_;
+ const std::list<MatrixTestEvent> outputs_;
+};
+
+class DebounceTest : public ::testing::Test {
+protected:
+ void addEvents(std::initializer_list<DebounceTestEvent> events);
+ void runEvents();
+
+ fast_timer_t time_offset_ = 7777;
+ bool time_jumps_ = false;
+
+private:
+ static bool directionValue(Direction direction);
+ static std::string directionLabel(Direction direction);
+
+ void runEventsInternal();
+ void runDebounce(bool changed);
+ void checkCookedMatrix(bool changed, const std::string &error_message);
+ void matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event);
+
+ std::string strTime();
+ std::string strMatrix(matrix_row_t matrix[]);
+
+ std::list<DebounceTestEvent> events_;
+
+ matrix_row_t input_matrix_[MATRIX_ROWS];
+ matrix_row_t raw_matrix_[MATRIX_ROWS];
+ matrix_row_t cooked_matrix_[MATRIX_ROWS];
+ matrix_row_t output_matrix_[MATRIX_ROWS];
+
+ int extra_iterations_;
+ bool auto_advance_time_;
+};
diff --git a/quantum/debounce/tests/rules.mk b/quantum/debounce/tests/rules.mk
new file mode 100644
index 0000000000..66928d7eb6
--- /dev/null
+++ b/quantum/debounce/tests/rules.mk
@@ -0,0 +1,44 @@
+# Copyright 2021 Simon Arlott
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+DEBOUNCE_COMMON_DEFS := -DMATRIX_ROWS=4 -DMATRIX_COLS=10 -DDEBOUNCE=5
+
+DEBOUNCE_COMMON_SRC := $(QUANTUM_PATH)/debounce/tests/debounce_test_common.cpp \
+ $(TMK_PATH)/common/test/timer.c
+
+debounce_sym_defer_g_DEFS := $(DEBOUNCE_COMMON_DEFS)
+debounce_sym_defer_g_SRC := $(DEBOUNCE_COMMON_SRC) \
+ $(QUANTUM_PATH)/debounce/sym_defer_g.c \
+ $(QUANTUM_PATH)/debounce/tests/sym_defer_g_tests.cpp
+
+debounce_sym_defer_pk_DEFS := $(DEBOUNCE_COMMON_DEFS)
+debounce_sym_defer_pk_SRC := $(DEBOUNCE_COMMON_SRC) \
+ $(QUANTUM_PATH)/debounce/sym_defer_pk.c \
+ $(QUANTUM_PATH)/debounce/tests/sym_defer_pk_tests.cpp
+
+debounce_sym_eager_pk_DEFS := $(DEBOUNCE_COMMON_DEFS)
+debounce_sym_eager_pk_SRC := $(DEBOUNCE_COMMON_SRC) \
+ $(QUANTUM_PATH)/debounce/sym_eager_pk.c \
+ $(QUANTUM_PATH)/debounce/tests/sym_eager_pk_tests.cpp
+
+debounce_sym_eager_pr_DEFS := $(DEBOUNCE_COMMON_DEFS)
+debounce_sym_eager_pr_SRC := $(DEBOUNCE_COMMON_SRC) \
+ $(QUANTUM_PATH)/debounce/sym_eager_pr.c \
+ $(QUANTUM_PATH)/debounce/tests/sym_eager_pr_tests.cpp
+
+debounce_asym_eager_defer_pk_DEFS := $(DEBOUNCE_COMMON_DEFS)
+debounce_asym_eager_defer_pk_SRC := $(DEBOUNCE_COMMON_SRC) \
+ $(QUANTUM_PATH)/debounce/asym_eager_defer_pk.c \
+ $(QUANTUM_PATH)/debounce/tests/asym_eager_defer_pk_tests.cpp
diff --git a/quantum/debounce/tests/sym_defer_g_tests.cpp b/quantum/debounce/tests/sym_defer_g_tests.cpp
new file mode 100644
index 0000000000..a56aecd8f3
--- /dev/null
+++ b/quantum/debounce/tests/sym_defer_g_tests.cpp
@@ -0,0 +1,223 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtest/gtest.h"
+
+#include "debounce_test_common.h"
+
+TEST_F(DebounceTest, OneKeyShort1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ /* 0ms delay (fast scan rate) */
+ {5, {{0, 1, UP}}, {}},
+
+ {10, {}, {{0, 1, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ /* 1ms delay */
+ {6, {{0, 1, UP}}, {}},
+
+ {11, {}, {{0, 1, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort3) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ /* 2ms delay */
+ {7, {{0, 1, UP}}, {}},
+
+ {12, {}, {{0, 1, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyTooQuick1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ /* Release key exactly on the debounce time */
+ {5, {{0, 1, UP}}, {}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyTooQuick2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ {6, {{0, 1, UP}}, {}},
+
+ /* Press key exactly on the debounce time */
+ {11, {{0, 1, DOWN}}, {}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {1, {{0, 1, UP}}, {}},
+ {2, {{0, 1, DOWN}}, {}},
+ {3, {{0, 1, UP}}, {}},
+ {4, {{0, 1, DOWN}}, {}},
+ {5, {{0, 1, UP}}, {}},
+ {6, {{0, 1, DOWN}}, {}},
+ {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {5, {}, {{0, 1, DOWN}}},
+ {6, {{0, 1, UP}}, {}},
+ {7, {{0, 1, DOWN}}, {}},
+ {8, {{0, 1, UP}}, {}},
+ {9, {{0, 1, DOWN}}, {}},
+ {10, {{0, 1, UP}}, {}},
+ {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyLong) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+
+ {25, {{0, 1, UP}}, {}},
+
+ {30, {}, {{0, 1, UP}}},
+
+ {50, {{0, 1, DOWN}}, {}},
+
+ {55, {}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysShort) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {1, {{0, 2, DOWN}}, {}},
+
+ {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
+
+ {7, {{0, 1, UP}}, {}},
+ {8, {{0, 2, UP}}, {}},
+
+ {13, {}, {{0, 1, UP}, {0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysSimultaneous1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
+ {6, {{0, 1, UP}, {0, 2, UP}}, {}},
+
+ {11, {}, {{0, 1, UP}, {0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysSimultaneous2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {1, {{0, 2, DOWN}}, {}},
+
+ {5, {}, {}},
+ {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
+ {7, {{0, 1, UP}}, {}},
+ {8, {{0, 2, UP}}, {}},
+
+ {13, {}, {{0, 1, UP}, {0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Processing is very late */
+ {300, {}, {{0, 1, DOWN}}},
+ /* Immediately release key */
+ {300, {{0, 1, UP}}, {}},
+
+ {305, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Processing is very late */
+ {300, {}, {{0, 1, DOWN}}},
+ /* Release key after 1ms */
+ {301, {{0, 1, UP}}, {}},
+
+ {306, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan3) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Release key before debounce expires */
+ {300, {{0, 1, UP}}, {}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan4) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Processing is a bit late */
+ {50, {}, {{0, 1, DOWN}}},
+ /* Release key after 1ms */
+ {51, {{0, 1, UP}}, {}},
+
+ {56, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
diff --git a/quantum/debounce/tests/sym_defer_pk_tests.cpp b/quantum/debounce/tests/sym_defer_pk_tests.cpp
new file mode 100644
index 0000000000..1f3061e59c
--- /dev/null
+++ b/quantum/debounce/tests/sym_defer_pk_tests.cpp
@@ -0,0 +1,225 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtest/gtest.h"
+
+#include "debounce_test_common.h"
+
+TEST_F(DebounceTest, OneKeyShort1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ /* 0ms delay (fast scan rate) */
+ {5, {{0, 1, UP}}, {}},
+
+ {10, {}, {{0, 1, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ /* 1ms delay */
+ {6, {{0, 1, UP}}, {}},
+
+ {11, {}, {{0, 1, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort3) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ /* 2ms delay */
+ {7, {{0, 1, UP}}, {}},
+
+ {12, {}, {{0, 1, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyTooQuick1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ /* Release key exactly on the debounce time */
+ {5, {{0, 1, UP}}, {}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyTooQuick2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ {6, {{0, 1, UP}}, {}},
+
+ /* Press key exactly on the debounce time */
+ {11, {{0, 1, DOWN}}, {}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {1, {{0, 1, UP}}, {}},
+ {2, {{0, 1, DOWN}}, {}},
+ {3, {{0, 1, UP}}, {}},
+ {4, {{0, 1, DOWN}}, {}},
+ {5, {{0, 1, UP}}, {}},
+ {6, {{0, 1, DOWN}}, {}},
+ {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {5, {}, {{0, 1, DOWN}}},
+ {6, {{0, 1, UP}}, {}},
+ {7, {{0, 1, DOWN}}, {}},
+ {8, {{0, 1, UP}}, {}},
+ {9, {{0, 1, DOWN}}, {}},
+ {10, {{0, 1, UP}}, {}},
+ {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyLong) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+
+ {25, {{0, 1, UP}}, {}},
+
+ {30, {}, {{0, 1, UP}}},
+
+ {50, {{0, 1, DOWN}}, {}},
+
+ {55, {}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysShort) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {1, {{0, 2, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ {6, {}, {{0, 2, DOWN}}},
+
+ {7, {{0, 1, UP}}, {}},
+ {8, {{0, 2, UP}}, {}},
+
+ {12, {}, {{0, 1, UP}}},
+ {13, {}, {{0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysSimultaneous1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
+ {6, {{0, 1, UP}, {0, 2, UP}}, {}},
+
+ {11, {}, {{0, 1, UP}, {0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysSimultaneous2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {1, {{0, 2, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ {6, {{0, 1, UP}}, {{0, 2, DOWN}}},
+ {7, {{0, 2, UP}}, {}},
+
+ {11, {}, {{0, 1, UP}}},
+ {12, {}, {{0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Processing is very late */
+ {300, {}, {{0, 1, DOWN}}},
+ /* Immediately release key */
+ {300, {{0, 1, UP}}, {}},
+
+ {305, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Processing is very late */
+ {300, {}, {{0, 1, DOWN}}},
+ /* Release key after 1ms */
+ {301, {{0, 1, UP}}, {}},
+
+ {306, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan3) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Release key before debounce expires */
+ {300, {{0, 1, UP}}, {}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan4) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Processing is a bit late */
+ {50, {}, {{0, 1, DOWN}}},
+ /* Release key after 1ms */
+ {51, {{0, 1, UP}}, {}},
+
+ {56, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
diff --git a/quantum/debounce/tests/sym_eager_pk_tests.cpp b/quantum/debounce/tests/sym_eager_pk_tests.cpp
new file mode 100644
index 0000000000..e0fc205e33
--- /dev/null
+++ b/quantum/debounce/tests/sym_eager_pk_tests.cpp
@@ -0,0 +1,237 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtest/gtest.h"
+
+#include "debounce_test_common.h"
+
+TEST_F(DebounceTest, OneKeyShort1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 1ms delay (debounce has not yet finished) */
+ {6, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 2ms delay (debounce has not yet finished) */
+ {7, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort3) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 3ms delay (debounce has not yet finished) */
+ {8, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort4) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 4ms delay (debounce has not yet finished) */
+ {9, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort5) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 5ms delay (debounce has finished) */
+ {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort6) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key after after 6ms delay (debounce has finished) */
+ {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+ {2, {{0, 1, DOWN}}, {}},
+ {3, {{0, 1, UP}}, {}},
+ {4, {{0, 1, DOWN}}, {}},
+ {5, {{0, 1, UP}}, {{0, 1, UP}}},
+ /* Press key again after 1ms delay (debounce has not yet finished) */
+ {6, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ /* Change twice in the same time period */
+ {1, {{0, 1, UP}}, {}},
+ {1, {{0, 1, DOWN}}, {}},
+ /* Change three times in the same time period */
+ {2, {{0, 1, UP}}, {}},
+ {2, {{0, 1, DOWN}}, {}},
+ {2, {{0, 1, UP}}, {}},
+ /* Change three times in the same time period */
+ {3, {{0, 1, DOWN}}, {}},
+ {3, {{0, 1, UP}}, {}},
+ {3, {{0, 1, DOWN}}, {}},
+ /* Change twice in the same time period */
+ {4, {{0, 1, UP}}, {}},
+ {4, {{0, 1, DOWN}}, {}},
+ {5, {{0, 1, UP}}, {{0, 1, UP}}},
+ /* Press key again after 1ms delay (debounce has not yet finished) */
+ {6, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyLong) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ {25, {{0, 1, UP}}, {{0, 1, UP}}},
+
+ {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysShort) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+ {2, {{0, 2, DOWN}}, {{0, 2, DOWN}}},
+ {3, {{0, 2, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 1ms delay (debounce has not yet finished) */
+ {6, {{0, 1, DOWN}}, {}},
+ {7, {}, {{0, 2, UP}}},
+
+ /* Press key again after 1ms delay (debounce has not yet finished) */
+ {9, {{0, 2, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+
+ {12, {}, {{0, 2, DOWN}}}, /* 5ms after UP at time 7 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late but the change will now be accepted */
+ {300, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late but the change will now be accepted even with a 1 scan delay */
+ {300, {}, {}},
+ {300, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan3) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late but the change will now be accepted even with a 1ms delay */
+ {300, {}, {}},
+ {301, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan4) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is a bit late but the change will now be accepted */
+ {50, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan5) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late but the change will now be accepted even with a 1 scan delay */
+ {50, {}, {}},
+ {50, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan6) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late but the change will now be accepted even with a 1ms delay */
+ {50, {}, {}},
+ {51, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
diff --git a/quantum/debounce/tests/sym_eager_pr_tests.cpp b/quantum/debounce/tests/sym_eager_pr_tests.cpp
new file mode 100644
index 0000000000..2c4bca127e
--- /dev/null
+++ b/quantum/debounce/tests/sym_eager_pr_tests.cpp
@@ -0,0 +1,280 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtest/gtest.h"
+
+#include "debounce_test_common.h"
+
+TEST_F(DebounceTest, OneKeyShort1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 1ms delay (debounce has not yet finished) */
+ {6, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 2ms delay (debounce has not yet finished) */
+ {7, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort3) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 3ms delay (debounce has not yet finished) */
+ {8, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort4) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 4ms delay (debounce has not yet finished) */
+ {9, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort5) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 5ms delay (debounce has finished) */
+ {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort6) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key after after 6ms delay (debounce has finished) */
+ {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+ {2, {{0, 1, DOWN}}, {}},
+ {3, {{0, 1, UP}}, {}},
+ {4, {{0, 1, DOWN}}, {}},
+ {5, {{0, 1, UP}}, {{0, 1, UP}}},
+ /* Press key again after 1ms delay (debounce has not yet finished) */
+ {6, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ /* Change twice in the same time period */
+ {1, {{0, 1, UP}}, {}},
+ {1, {{0, 1, DOWN}}, {}},
+ /* Change three times in the same time period */
+ {2, {{0, 1, UP}}, {}},
+ {2, {{0, 1, DOWN}}, {}},
+ {2, {{0, 1, UP}}, {}},
+ /* Change three times in the same time period */
+ {3, {{0, 1, DOWN}}, {}},
+ {3, {{0, 1, UP}}, {}},
+ {3, {{0, 1, DOWN}}, {}},
+ /* Change twice in the same time period */
+ {4, {{0, 1, UP}}, {}},
+ {4, {{0, 1, DOWN}}, {}},
+ {5, {{0, 1, UP}}, {{0, 1, UP}}},
+ /* Press key again after 1ms delay (debounce has not yet finished) */
+ {6, {{0, 1, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyLong) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ {25, {{0, 1, UP}}, {{0, 1, UP}}},
+
+ {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoRowsShort) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+ {2, {{2, 0, DOWN}}, {{2, 0, DOWN}}},
+ {3, {{2, 0, UP}}, {}},
+
+ {5, {}, {{0, 1, UP}}},
+ /* Press key again after 1ms delay (debounce has not yet finished) */
+ {6, {{0, 1, DOWN}}, {}},
+ {7, {}, {{2, 0, UP}}},
+
+ /* Press key again after 1ms delay (debounce has not yet finished) */
+ {9, {{2, 0, DOWN}}, {}},
+ {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */
+
+ {12, {}, {{2, 0, DOWN}}}, /* 5ms after UP at time 7 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysOverlap) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+ {1, {{0, 1, UP}}, {}},
+ /* Press a second key during the first debounce */
+ {2, {{0, 2, DOWN}}, {}},
+
+ /* Key registers as soon as debounce finishes, 5ms after time 0 */
+ {5, {}, {{0, 1, UP}, {0, 2, DOWN}}},
+ {6, {{0, 1, DOWN}}, {}},
+
+ /* Key registers as soon as debounce finishes, 5ms after time 5 */
+ {10, {}, {{0, 1, DOWN}}},
+ /* Release both keys */
+ {11, {{0, 1, UP}}, {}},
+ {12, {{0, 2, UP}}, {}},
+
+ /* Keys register as soon as debounce finishes, 5ms after time 10 */
+ {15, {}, {{0, 1, UP}, {0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysSimultaneous1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}},
+ {20, {{0, 1, UP}}, {{0, 1, UP}}},
+ {21, {{0, 2, UP}}, {}},
+
+ /* Key registers as soon as debounce finishes, 5ms after time 20 */
+ {25, {}, {{0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysSimultaneous2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}},
+ {20, {{0, 1, UP}, {0, 2, UP}}, {{0, 1, UP}, {0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan1) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late but the change will now be accepted */
+ {300, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan2) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late but the change will now be accepted even with a 1 scan delay */
+ {300, {}, {}},
+ {300, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan3) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late but the change will now be accepted even with a 1ms delay */
+ {300, {}, {}},
+ {301, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan4) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is a bit late but the change will now be accepted */
+ {50, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan5) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late but the change will now be accepted even with a 1 scan delay */
+ {50, {}, {}},
+ {50, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan6) {
+ addEvents({ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
+
+ /* Processing is very late but the change will now be accepted even with a 1ms delay */
+ {50, {}, {}},
+ {51, {{0, 1, UP}}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
diff --git a/quantum/debounce/tests/testlist.mk b/quantum/debounce/tests/testlist.mk
new file mode 100644
index 0000000000..c54c45aa63
--- /dev/null
+++ b/quantum/debounce/tests/testlist.mk
@@ -0,0 +1,6 @@
+TEST_LIST += \
+ debounce_sym_defer_g \
+ debounce_sym_defer_pk \
+ debounce_sym_eager_pk \
+ debounce_sym_eager_pr \
+ debounce_asym_eager_defer_pk
diff --git a/quantum/dip_switch.c b/quantum/dip_switch.c
index cda69bd0ef..72789ca8e8 100644
--- a/quantum/dip_switch.c
+++ b/quantum/dip_switch.c
@@ -49,13 +49,13 @@ static uint16_t scan_count;
static bool dip_switch_state[NUMBER_OF_DIP_SWITCHES] = {0};
static bool last_dip_switch_state[NUMBER_OF_DIP_SWITCHES] = {0};
-__attribute__((weak)) void dip_switch_update_user(uint8_t index, bool active) {}
+__attribute__((weak)) bool dip_switch_update_user(uint8_t index, bool active) { return true; }
-__attribute__((weak)) void dip_switch_update_kb(uint8_t index, bool active) { dip_switch_update_user(index, active); }
+__attribute__((weak)) bool dip_switch_update_kb(uint8_t index, bool active) { return dip_switch_update_user(index, active); }
-__attribute__((weak)) void dip_switch_update_mask_user(uint32_t state) {}
+__attribute__((weak)) bool dip_switch_update_mask_user(uint32_t state) { return true; }
-__attribute__((weak)) void dip_switch_update_mask_kb(uint32_t state) { dip_switch_update_mask_user(state); }
+__attribute__((weak)) bool dip_switch_update_mask_kb(uint32_t state) { return dip_switch_update_mask_user(state); }
void dip_switch_init(void) {
#ifdef DIP_SWITCH_PINS
diff --git a/quantum/dip_switch.h b/quantum/dip_switch.h
index 61ef1cc19d..058a10f41f 100644
--- a/quantum/dip_switch.h
+++ b/quantum/dip_switch.h
@@ -20,10 +20,10 @@
#include "quantum.h"
-void dip_switch_update_kb(uint8_t index, bool active);
-void dip_switch_update_user(uint8_t index, bool active);
-void dip_switch_update_mask_user(uint32_t state);
-void dip_switch_update_mask_kb(uint32_t state);
+bool dip_switch_update_kb(uint8_t index, bool active);
+bool dip_switch_update_user(uint8_t index, bool active);
+bool dip_switch_update_mask_user(uint32_t state);
+bool dip_switch_update_mask_kb(uint32_t state);
void dip_switch_init(void);
void dip_switch_read(bool forced);
diff --git a/quantum/haptic.c b/quantum/haptic.c
new file mode 100644
index 0000000000..65abcc15fa
--- /dev/null
+++ b/quantum/haptic.c
@@ -0,0 +1,295 @@
+/* Copyright 2019 ishtob
+ * Driver for haptic feedback written for QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "haptic.h"
+#include "eeconfig.h"
+#include "debug.h"
+#ifdef DRV2605L
+# include "DRV2605L.h"
+#endif
+#ifdef SOLENOID_ENABLE
+# include "solenoid.h"
+#endif
+
+haptic_config_t haptic_config;
+
+void haptic_init(void) {
+ if (!eeconfig_is_enabled()) {
+ eeconfig_init();
+ }
+ haptic_config.raw = eeconfig_read_haptic();
+#ifdef SOLENOID_ENABLE
+ solenoid_set_dwell(haptic_config.dwell);
+#endif
+ if ((haptic_config.raw == 0)
+#ifdef SOLENOID_ENABLE
+ || (haptic_config.dwell == 0)
+#endif
+ ) {
+ // this will be called, if the eeprom is not corrupt,
+ // but the previous firmware didn't have haptic enabled,
+ // or the previous firmware didn't have solenoid enabled,
+ // and the current one has solenoid enabled.
+ haptic_reset();
+ }
+#ifdef SOLENOID_ENABLE
+ solenoid_setup();
+ dprintf("Solenoid driver initialized\n");
+#endif
+#ifdef DRV2605L
+ DRV_init();
+ dprintf("DRV2605 driver initialized\n");
+#endif
+ eeconfig_debug_haptic();
+}
+
+void haptic_task(void) {
+#ifdef SOLENOID_ENABLE
+ solenoid_check();
+#endif
+}
+
+void eeconfig_debug_haptic(void) {
+ dprintf("haptic_config eeprom\n");
+ dprintf("haptic_config.enable = %d\n", haptic_config.enable);
+ dprintf("haptic_config.mode = %d\n", haptic_config.mode);
+}
+
+void haptic_enable(void) {
+ haptic_config.enable = 1;
+ xprintf("haptic_config.enable = %u\n", haptic_config.enable);
+ eeconfig_update_haptic(haptic_config.raw);
+}
+
+void haptic_disable(void) {
+ haptic_config.enable = 0;
+ xprintf("haptic_config.enable = %u\n", haptic_config.enable);
+ eeconfig_update_haptic(haptic_config.raw);
+}
+
+void haptic_toggle(void) {
+ if (haptic_config.enable) {
+ haptic_disable();
+ } else {
+ haptic_enable();
+ }
+ eeconfig_update_haptic(haptic_config.raw);
+}
+
+void haptic_feedback_toggle(void) {
+ haptic_config.feedback++;
+ if (haptic_config.feedback >= HAPTIC_FEEDBACK_MAX) haptic_config.feedback = KEY_PRESS;
+ xprintf("haptic_config.feedback = %u\n", !haptic_config.feedback);
+ eeconfig_update_haptic(haptic_config.raw);
+}
+
+void haptic_buzz_toggle(void) {
+ bool buzz_stat = !haptic_config.buzz;
+ haptic_config.buzz = buzz_stat;
+ haptic_set_buzz(buzz_stat);
+}
+
+void haptic_mode_increase(void) {
+ uint8_t mode = haptic_config.mode + 1;
+#ifdef DRV2605L
+ if (haptic_config.mode >= drv_effect_max) {
+ mode = 1;
+ }
+#endif
+ haptic_set_mode(mode);
+}
+
+void haptic_mode_decrease(void) {
+ uint8_t mode = haptic_config.mode - 1;
+#ifdef DRV2605L
+ if (haptic_config.mode < 1) {
+ mode = (drv_effect_max - 1);
+ }
+#endif
+ haptic_set_mode(mode);
+}
+
+void haptic_dwell_increase(void) {
+#ifdef SOLENOID_ENABLE
+ int16_t next_dwell = ((int16_t)haptic_config.dwell) + SOLENOID_DWELL_STEP_SIZE;
+ if (haptic_config.dwell >= SOLENOID_MAX_DWELL) {
+ // if it's already at max, we wrap back to min
+ next_dwell = SOLENOID_MIN_DWELL;
+ } else if (next_dwell > SOLENOID_MAX_DWELL) {
+ // if we overshoot the max, then cap at max
+ next_dwell = SOLENOID_MAX_DWELL;
+ }
+ solenoid_set_dwell(next_dwell);
+#else
+ int16_t next_dwell = ((int16_t)haptic_config.dwell) + 1;
+#endif
+ haptic_set_dwell(next_dwell);
+}
+
+void haptic_dwell_decrease(void) {
+#ifdef SOLENOID_ENABLE
+ int16_t next_dwell = ((int16_t)haptic_config.dwell) - SOLENOID_DWELL_STEP_SIZE;
+ if (haptic_config.dwell <= SOLENOID_MIN_DWELL) {
+ // if it's already at min, we wrap to max
+ next_dwell = SOLENOID_MAX_DWELL;
+ } else if (next_dwell < SOLENOID_MIN_DWELL) {
+ // if we go below min, then we cap to min
+ next_dwell = SOLENOID_MIN_DWELL;
+ }
+ solenoid_set_dwell(next_dwell);
+#else
+ int16_t next_dwell = ((int16_t)haptic_config.dwell) - 1;
+#endif
+ haptic_set_dwell(next_dwell);
+}
+
+void haptic_reset(void) {
+ haptic_config.enable = true;
+ uint8_t feedback = HAPTIC_FEEDBACK_DEFAULT;
+ haptic_config.feedback = feedback;
+#ifdef DRV2605L
+ uint8_t mode = HAPTIC_MODE_DEFAULT;
+ haptic_config.mode = mode;
+#endif
+#ifdef SOLENOID_ENABLE
+ uint8_t dwell = SOLENOID_DEFAULT_DWELL;
+ haptic_config.dwell = dwell;
+ haptic_config.buzz = SOLENOID_DEFAULT_BUZZ;
+ solenoid_set_dwell(dwell);
+#else
+ // This is to trigger haptic_reset again, if solenoid is enabled in the future.
+ haptic_config.dwell = 0;
+ haptic_config.buzz = 0;
+#endif
+ eeconfig_update_haptic(haptic_config.raw);
+ xprintf("haptic_config.feedback = %u\n", haptic_config.feedback);
+ xprintf("haptic_config.mode = %u\n", haptic_config.mode);
+}
+
+void haptic_set_feedback(uint8_t feedback) {
+ haptic_config.feedback = feedback;
+ eeconfig_update_haptic(haptic_config.raw);
+ xprintf("haptic_config.feedback = %u\n", haptic_config.feedback);
+}
+
+void haptic_set_mode(uint8_t mode) {
+ haptic_config.mode = mode;
+ eeconfig_update_haptic(haptic_config.raw);
+ xprintf("haptic_config.mode = %u\n", haptic_config.mode);
+}
+
+void haptic_set_amplitude(uint8_t amp) {
+ haptic_config.amplitude = amp;
+ eeconfig_update_haptic(haptic_config.raw);
+ xprintf("haptic_config.amplitude = %u\n", haptic_config.amplitude);
+#ifdef DRV2605L
+ DRV_amplitude(amp);
+#endif
+}
+
+void haptic_set_buzz(uint8_t buzz) {
+ haptic_config.buzz = buzz;
+ eeconfig_update_haptic(haptic_config.raw);
+ xprintf("haptic_config.buzz = %u\n", haptic_config.buzz);
+}
+
+void haptic_set_dwell(uint8_t dwell) {
+ haptic_config.dwell = dwell;
+ eeconfig_update_haptic(haptic_config.raw);
+ xprintf("haptic_config.dwell = %u\n", haptic_config.dwell);
+}
+
+uint8_t haptic_get_enable(void) { return haptic_config.enable; }
+
+uint8_t haptic_get_mode(void) {
+ if (!haptic_config.enable) {
+ return false;
+ }
+ return haptic_config.mode;
+}
+
+uint8_t haptic_get_feedback(void) {
+ if (!haptic_config.enable) {
+ return false;
+ }
+ return haptic_config.feedback;
+}
+
+uint8_t haptic_get_dwell(void) {
+ if (!haptic_config.enable) {
+ return false;
+ }
+ return haptic_config.dwell;
+}
+
+void haptic_enable_continuous(void) {
+ haptic_config.cont = 1;
+ xprintf("haptic_config.cont = %u\n", haptic_config.cont);
+ eeconfig_update_haptic(haptic_config.raw);
+#ifdef DRV2605L
+ DRV_rtp_init();
+#endif
+}
+
+void haptic_disable_continuous(void) {
+ haptic_config.cont = 0;
+ xprintf("haptic_config.cont = %u\n", haptic_config.cont);
+ eeconfig_update_haptic(haptic_config.raw);
+#ifdef DRV2605L
+ DRV_write(DRV_MODE, 0x00);
+#endif
+}
+
+void haptic_toggle_continuous(void) {
+ if (haptic_config.cont) {
+ haptic_disable_continuous();
+ } else {
+ haptic_enable_continuous();
+ }
+}
+
+void haptic_cont_increase(void) {
+ uint8_t amp = haptic_config.amplitude + 10;
+ if (haptic_config.amplitude >= 120) {
+ amp = 120;
+ }
+ haptic_set_amplitude(amp);
+}
+
+void haptic_cont_decrease(void) {
+ uint8_t amp = haptic_config.amplitude - 10;
+ if (haptic_config.amplitude < 20) {
+ amp = 20;
+ }
+ haptic_set_amplitude(amp);
+}
+
+void haptic_play(void) {
+#ifdef DRV2605L
+ uint8_t play_eff = 0;
+ play_eff = haptic_config.mode;
+ DRV_pulse(play_eff);
+#endif
+#ifdef SOLENOID_ENABLE
+ solenoid_fire();
+#endif
+}
+
+void haptic_shutdown(void) {
+#ifdef SOLENOID_ENABLE
+ solenoid_shutdown();
+#endif
+}
diff --git a/quantum/haptic.h b/quantum/haptic.h
new file mode 100644
index 0000000000..fc7ca2f3e6
--- /dev/null
+++ b/quantum/haptic.h
@@ -0,0 +1,77 @@
+/* Copyright 2019 ishtob
+ * Driver for haptic feedback written for QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifndef HAPTIC_FEEDBACK_DEFAULT
+# define HAPTIC_FEEDBACK_DEFAULT 0
+#endif
+#ifndef HAPTIC_MODE_DEFAULT
+# define HAPTIC_MODE_DEFAULT DRV_MODE_DEFAULT
+#endif
+
+/* EEPROM config settings */
+typedef union {
+ uint32_t raw;
+ struct {
+ bool enable : 1;
+ uint8_t feedback : 2;
+ uint8_t mode : 7;
+ bool buzz : 1;
+ uint8_t dwell : 7;
+ bool cont : 1;
+ uint8_t amplitude : 8;
+ uint8_t reserved : 5;
+ };
+} haptic_config_t;
+
+typedef enum HAPTIC_FEEDBACK {
+ KEY_PRESS,
+ KEY_PRESS_RELEASE,
+ KEY_RELEASE,
+ HAPTIC_FEEDBACK_MAX,
+} HAPTIC_FEEDBACK;
+
+void haptic_init(void);
+void haptic_task(void);
+void eeconfig_debug_haptic(void);
+void haptic_enable(void);
+void haptic_disable(void);
+void haptic_toggle(void);
+void haptic_feedback_toggle(void);
+void haptic_mode_increase(void);
+void haptic_mode_decrease(void);
+void haptic_mode(uint8_t mode);
+void haptic_reset(void);
+void haptic_set_feedback(uint8_t feedback);
+void haptic_set_mode(uint8_t mode);
+void haptic_set_dwell(uint8_t dwell);
+void haptic_set_buzz(uint8_t buzz);
+void haptic_buzz_toggle(void);
+uint8_t haptic_get_enable(void);
+uint8_t haptic_get_mode(void);
+uint8_t haptic_get_feedback(void);
+void haptic_dwell_increase(void);
+void haptic_dwell_decrease(void);
+void haptic_toggle_continuous(void);
+void haptic_cont_increase(void);
+void haptic_cont_decrease(void);
+
+void haptic_play(void);
+void haptic_shutdown(void);
diff --git a/quantum/keymap_extras/keymap_belgian.h b/quantum/keymap_extras/keymap_belgian.h
index 6aaadf9787..207905b291 100644
--- a/quantum/keymap_extras/keymap_belgian.h
+++ b/quantum/keymap_extras/keymap_belgian.h
@@ -156,16 +156,3 @@
// Row 4
#define BE_BSLS ALGR(BE_LABK) // (backslash)
#define BE_TILD ALGR(BE_EQL) // ~
-
-// DEPRECATED
-#define BE_AMP BE_AMPR
-#define BE_APOS BE_QUOT
-#define BE_PARA BE_SECT
-#define BE_MU BE_MICR
-#define BE_LESS BE_LABK
-#define BE_OVRR BE_DEG
-#define BE_UMLT BE_DIAE
-#define BE_GRTR BE_RABK
-#define BE_LSBR BE_LBRC
-#define BE_RSBR BE_RBRC
-#define BE_TILT BE_TILD
diff --git a/quantum/keymap_extras/keymap_bepo.h b/quantum/keymap_extras/keymap_bepo.h
index b6244898aa..72d5b81f32 100644
--- a/quantum/keymap_extras/keymap_bepo.h
+++ b/quantum/keymap_extras/keymap_bepo.h
@@ -181,7 +181,7 @@
#define BP_RCBR ALGR(BP_X) // }
#define BP_ELLP ALGR(BP_DOT) // …
#define BP_TILD ALGR(BP_K) // ~
-#define BP_IQUE ALGR(BP_QEST) // ¿
+#define BP_IQUE ALGR(BP_QUES) // ¿
#define BP_RNGA ALGR(BP_Q) // ° (dead)
#define BP_DGRK ALGR(BP_G) // µ (dead Greek key)
#define BP_DAGG ALGR(BP_H) // †
@@ -238,168 +238,3 @@
#define BP_FORD S(ALGR(BP_F)) // ª
// Row 5
#define BP_NNBS S(ALGR(BP_)) //   (narrow non-breaking space)
-
-// DEPRECATED
-#define BP_DOLLAR BP_DLR
-#define BP_DOUBLE_QUOTE BP_DQUO
-#define BP_DQOT BP_DQUO
-#define BP_LEFT_GUILLEMET BP_LDAQ
-#define BP_LGIL BP_LDAQ
-#define BP_RIGHT_GUILLEMET BP_RDAQ
-#define BP_RGIL BP_RDAQ
-#define BP_LEFT_PAREN BP_LPRN
-#define BP_RIGHT_PAREN BP_RPRN
-#define BP_MINUS BP_MINS
-#define BP_SLASH BP_SLSH
-#define BP_ASTERISK BP_ASTR
-#define BP_EQUAL BP_EQL
-#define BP_PERCENT BP_PERC
-#define BP_E_ACUTE BP_EACU
-#define BP_ECUT BP_EACU
-#define BP_E_GRAVE BP_EGRV
-#define BP_DEAD_CIRCUMFLEX BP_DCIR
-#define BP_DCRC BP_DCIR
-#define BP_COMMA BP_COMM
-#define BP_C_CEDILLA BP_CCED
-#define BP_E_CIRCUMFLEX BP_ECIR
-#define BP_ECRC BP_ECIR
-#define BP_A_GRAVE BP_AGRV
-#define BP_APOSTROPHE BP_QUOT
-#define BP_APOS BP_QUOT
-#define BP_DEGREE BP_DEG
-#define BP_DEGR BP_DEG
-#define BP_GRAVE BP_GRV
-#define BP_EXCLAIM BP_EXLM
-#define BP_SCOLON BP_SCLN
-#define BP_COLON BP_COLN
-#define BP_QUESTION BP_QUES
-#define BP_QEST BP_QUES
-#define BP_NON_BREAKING_SPACE BP_NBSP
-#define BP_EN_DASH BP_NDSH
-#define BP_EM_DASH BP_MDSH
-#define BP_LESS BP_LABK
-#define BP_GREATER BP_RABK
-#define BP_GRTR BP_RABK
-#define BP_LBRACKET BP_LBRC
-#define BP_RBRACKET BP_RBRC
-#define BP_CIRCUMFLEX CIRC
-#define BP_PLUS_MINUS BP_PLMN
-#define BP_PSMS BP_PLMN
-#define BP_MATH_MINUS BP_MMNS
-#define BP_OBELUS BP_DIV
-#define BP_OBEL BP_DIV
-#define BP_DIVISION_SIGN BP_DIV
-#define BP_DVSN BP_DIV
-#define BP_TIMES BP_MUL
-#define BP_TIMS BP_MUL
-#define BP_DIFFERENT BP_NEQL
-#define BP_DIFF BP_NEQL
-#define BP_PERMILLE BP_PERM
-#define BP_PMIL BP_PERM
-#define BP_DEAD_ACUTE BP_ACUT
-#define BP_DACT BP_ACUT
-#define BP_AMPERSAND BP_AMPR
-#define BP_OE_LIGATURE BP_OE
-#define BP_DEAD_GRAVE BP_DGRV
-#define BP_INVERTED_EXCLAIM BP_IEXL
-#define BP_IXLM BP_IEXL
-#define BP_DEAD_CARON BP_CARN
-#define BP_DCAR BP_CARN
-#define BP_DEAD_SLASH BP_DSLS
-#define BP_DSLH BP_DSLS
-#define BP_IJ_LIGATURE BP_IJ
-#define BP_SCHWA BP_SCHW
-#define BP_SCWA BP_SCHW
-#define BP_DEAD_BREVE BP_BREV
-#define BP_DBRV BP_BREV
-#define BP_AE_LIGATURE BP_AE
-#define BP_U_GRAVE BP_UGRV
-#define BP_DEAD_TREMA BP_DIAE
-#define BP_DTRM BP_DIAE
-#define BP_TYPOGRAPHICAL_APOSTROPHE BP_COMM
-#define BP_TAPO BP_COMM
-#define BP_COPYRIGHT BP_COPY
-#define BP_CPRT BP_COPY
-#define BP_THORN BP_THRN
-#define BP_SHARP_S BP_SS
-#define BP_SRPS BP_SS
-#define BP_REGISTERED_TRADEMARK BP_REGD
-#define BP_RTM BP_REGD
-#define BP_DEAD_TILDE BP_DTIL
-#define BP_DTLD BP_DTIL
-#define BP_DEAD_MACRON BP_MACR
-#define BP_DMCR BP_MACR
-#define BP_DEAD_CEDILLA BP_CEDL
-#define BP_DCED BP_CEDL
-#define BP_NONUS_SLASH BP_SLSH
-#define BP_NUSL BP_SLSH
-#define BP_BACKSLASH BP_BSLS
-#define BP_LEFT_CURLY_BRACE BP_LCBR
-#define BP_RIGHT_CURLY_BRACE BP_RCBR
-#define BP_ELLIPSIS BP_ELLP
-#define BP_ELPS BP_ELLP
-#define BP_TILDE BP_TILD
-#define BP_INVERTED_QUESTION BP_IQUE
-#define BP_IQST BP_IQUE
-#define BP_DEAD_RING BP_RNGA
-#define BP_DRNG BP_RNGA
-#define BP_DEAD_GREEK BP_DGRK
-#define BP_DAGGER BP_DAGG
-#define BP_DAGR BP_DAGG
-#define BP_DEAD_OGONEK BP_OGON
-#define BP_DOGO BP_OGON
-#define BP_UNDERSCORE BP_UNDS
-#define BP_PARAGRAPH BP_PARA
-#define BP_PARG BP_PARA
-#define BP_LOW_DOUBLE_QUOTE BP_DLQU
-#define BP_LWQT BP_DLQU
-#define BP_LEFT_DOUBLE_QUOTE BP_RDQU
-#define BP_RIGHT_DOUBLE_QUOTE BP_RDQU
-#define BP_LESS_OR_EQUAL BP_LEQL
-#define BP_GREATER_OR_EQUAL BP_GEQL
-#define BP_NEGATION BP_NOT
-#define BP_NEGT BP_NOT
-#define BP_ONE_QUARTER BP_QRTR
-#define BP_1QRT BP_QRTR
-#define BP_ONE_HALF BP_HALF
-#define BP_1HLF BP_HALF
-#define BP_THREE_QUARTERS TQTR
-#define BP_3QRT BP_TQTR
-#define BP_MINUTES BP_PRIM
-#define BP_MNUT BP_PRIM
-#define BP_SECONDS BP_DPRM
-#define BP_SCND BP_DPRM
-#define BP_BROKEN_PIPE BP_BRKP
-#define BP_BPIP BP_BRKP
-#define BP_DEAD_DOUBLE_ACUTE BP_DACU
-#define BP_DDCT BP_DACU
-#define BP_SECTION BP_SECT
-#define BP_GRAVE_BIS BP_GRV
-#define BP_GRVB BP_GRV
-#define BP_DEAD_DOT_ABOVE BP_DOTA
-#define BP_DDTA BP_DOTA
-#define BP_DEAD_CURRENCY BP_CURR
-#define BP_DCUR BP_CURR
-#define BP_DEAD_HORN BP_HORN
-#define BP_DHRN BP_HORN
-#define BP_LONG_S BP_LNGS
-#define BP_TRADEMARK BP_TM
-#define BP_ORDINAL_INDICATOR_O MORD
-#define BP_ORDO BP_MORD
-#define BP_DEAD_COMMA BP_DCMM
-#define BP_DCOM BP_DCMM
-#define BP_LEFT_QUOTE BP_LSQU
-#define BP_LQOT BP_LSQU
-#define BP_RIGHT_QUOTE BP_RSQU
-#define BP_RQOT BP_RSQU
-#define BP_INTERPUNCT BP_MDDT
-#define BP_IPCT BP_MDDT
-#define BP_DEAD_HOOK_ABOVE BP_HOKA
-#define BP_DHKA BP_HOKA
-#define BP_DEAD_UNDERDOT BP_DOTB
-#define BP_DUDT BP_DOTB
-#define BP_DOUBLE_DAGGER BP_DDAG
-#define BP_DDGR BP_DDAG
-#define BP_ORDINAL_INDICATOR_A BP_FORD
-#define BP_ORDA BP_FORD
-#define BP_NARROW_NON_BREAKING_SPACE BP_NNBS
diff --git a/quantum/keymap_extras/keymap_br_abnt2.h b/quantum/keymap_extras/keymap_br_abnt2.h
index 310950d49d..e91718013a 100644
--- a/quantum/keymap_extras/keymap_br_abnt2.h
+++ b/quantum/keymap_extras/keymap_br_abnt2.h
@@ -158,23 +158,3 @@
#define BR_MORD ALGR(BR_RBRC) // º
// Row 4
#define BR_CRUZ ALGR(BR_C) // ₢
-
-// DEPRECATED
-#define BR_CCDL BR_CCED
-#define BR_DQT BR_DQUO
-#define BR_TRMA BR_DIAE
-#define BR_GRAV BR_GRV
-#define BR_KPDT BR_PDOT
-#define BR_KPCM BR_PCMM
-#define BR_1UP BR_SUP1
-#define BR_2UP BR_SUP2
-#define BR_3UP BR_SUP3
-#define BR_ASLS BR_SLSH
-#define BR_AQST BR_QUES
-
-// Not present on Windows 10?
-#define BR_NDTD ALGR(BR_TILD) // ~
-#define BR_NDAC ALGR(BR_ACUT) // ´
-#define BR_NDGV ALGR(BR_QUOT) // `
-#define BR_NDCR ALGR(BR_CIRC) // ^
-#define BR_NDTR ALGR(BR_DIAE) // ¨
diff --git a/quantum/keymap_extras/keymap_canadian_multilingual.h b/quantum/keymap_extras/keymap_canadian_multilingual.h
index d72ea3979f..e328cf65e6 100644
--- a/quantum/keymap_extras/keymap_canadian_multilingual.h
+++ b/quantum/keymap_extras/keymap_canadian_multilingual.h
@@ -252,182 +252,3 @@
#define CA_MORD RCTL(S(CA_M)) // º
#define CA_MUL RCTL(S(CA_COMM)) // ×
#define CA_DIV RCTL(S(CA_DOT)) // ÷
-
-// DEPRECATED
-#define GR2A(kc) RCTL(kc)
-#define CSA_SLASH CA_SLSH
-#define CSA_SLSH CA_SLSH
-#define CSA_DEAD_CIRCUMFLEX CA_CIRC
-#define CSA_DCRC CA_CIRC
-#define CSA_C_CEDILLA CA_CCED
-#define CSA_CCED CA_CCED
-#define CSA_E_GRAVE CA_EGRV
-#define CSA_EGRV CA_EGRV
-#define CSA_A_GRAVE CA_AGRV
-#define CSA_AGRV CA_AGRV
-#define CSA_U_GRAVE CA_UGRV
-#define CSA_UGRV CA_UGRV
-#define CSA_E_ACUTE CA_EACU
-#define CSA_ECUT CA_EACU
-#define CSA_BACKSLASH CA_BSLS
-#define CSA_BSLS CA_BSLS
-#define CSA_QUESTION CA_QUES
-#define CSA_QEST CA_QUES
-#define CSA_DEAD_TREMA CA_DIAE
-#define CSA_DTRM CA_DIAE
-#define CSA_APOSTROPHE CA_QUOT
-#define CSA_APOS CA_QUOT
-#define CSA_DOUBLE_QUOTE CA_DQUO
-#define CSA_DQOT CA_DQUO
-#define CSA_PIPE CA_PIPE
-#define CSA_CURRENCY CA_CURR
-#define CSA_CURR CA_CURR
-#define CSA_LEFT_CURLY_BRACE CA_LCBR
-#define CSA_LCBR CA_LCBR
-#define CSA_RIGHT_CURLY_BRACE CA_RCBR
-#define CSA_RCBR CA_RCBR
-#define CSA_LBRACKET CA_LBRC
-#define CSA_LBRC CA_LBRC
-#define CSA_RBRACKET CA_RBRC
-#define CSA_RBRC CA_RBRC
-#define CSA_NEGATION CA_NOT
-#define CSA_NEGT CA_NOT
-#define CSA_EURO CA_EURO
-#define CSA_DEAD_GRAVE CA_GRV
-#define CSA_DGRV CA_GRV
-#define CSA_DEAD_TILDE CA_DTIL
-#define CSA_DTLD CA_DTIL
-#define CSA_DEGREE CA_DEG
-#define CSA_DEGR CA_DEG
-#define CSA_LEFT_GUILLEMET CA_LDAQ
-#define CSA_LGIL CA_LDAQ
-#define CSA_RIGHT_GUILLEMET CA_RDAQ
-#define CSA_RGIL CA_RDAQ
-#define CSA_LESS CA_LABK
-#define CSA_GREATER CA_RABK
-#define CSA_GRTR CA_RABK
-#define CSA_NON_BREAKING_SPACE ALGR(KC_SPC)
-#define CSA_NBSP ALGR(KC_SPC)
-#define CSA_SUPERSCRIPT_ONE CA_SUP1
-#define CSA_SUP1 CA_SUP1
-#define CSA_SUPERSCRIPT_TWO CA_SUP2
-#define CSA_SUP2 CA_SUP2
-#define CSA_SUPERSCRIPT_THREE CA_SUP3
-#define CSA_SUP3 CA_SUP3
-#define CSA_ONE_QUARTER CA_QRTR
-#define CSA_1QRT CA_QRTR
-#define CSA_ONE_HALF CA_HALF
-#define CSA_1HLF CA_HALF
-#define CSA_THREE_QUARTERS CA_TQTR
-#define CSA_3QRT CA_TQTR
-#define CSA_DEAD_CEDILLA CA_CEDL
-#define CSA_DCED CA_CEDL
-#define CSA_OMEGA CA_OMEG
-#define CSA_OMEG CA_OMEG
-#define CSA_L_STROKE CA_LSTR
-#define CSA_LSTK CA_LSTR
-#define CSA_OE_LIGATURE CA_OE
-#define CSA_OE CA_OE
-#define CSA_PARAGRAPH CA_PARA
-#define CSA_PARG CA_PARA
-#define CSA_T_STROKE CA_TSTR
-#define CSA_LEFT_ARROW CA_LARR
-#define CSA_LARW CA_LARR
-#define CSA_DOWN_ARROW CA_DARR
-#define CSA_DARW CA_DARR
-#define CSA_RIGHT_ARROW CA_RARR
-#define CSA_RARW CA_RARR
-#define CSA_O_STROKE CA_OSTR
-#define CSA_OSTK CA_OSTR
-#define CSA_THORN CA_THRN
-#define CSA_THRN CA_THRN
-#define CSA_TILDE CA_TILD
-#define CSA_TILD CA_TILD
-#define CSA_AE_LIGATURE CA_AE
-#define CSA_AE CA_AE
-#define CSA_SHARP_S CA_SS
-#define CSA_SRPS CA_SS
-#define CSA_ETH CA_ETH
-#define CSA_ENG CA_ENG
-#define CSA_H_SRTOKE CA_HSTR
-#define CSA_HSTK CA_HSTR
-#define CSA_IJ_LIGATURE CA_IJ
-#define CSA_IJ CA_IJ
-#define CSA_KRA CA_KRA
-#define CSA_L_FLOWN_DOT CA_LMDT
-#define CSA_LFLD CA_LMDT
-#define CSA_DEAD_ACUTE CA_ACUT
-#define CSA_DACT CA_ACUT
-#define CSA_CENT CA_CENT
-#define CSA_LEFT_DOUBLE_QUOTE CA_LDQU
-#define CSA_LDQT CA_LDQU
-#define CSA_RIGHT_DOUBLE_QUOTE CA_RDQU
-#define CSA_RDQT CA_RDQU
-#define CSA_N_APOSTROPHE CA_APSN
-#define CSA_NAPO CA_APSN
-#define CSA_MU CA_MICR
-#define CSA_HORIZONTAL_BAR CA_HRZB
-#define CSA_HZBR CA_HRZB
-#define CSA_DEAD_DOT_ABOVE CA_DOTA
-#define CSA_DDTA CA_DOTA
-#define CSA_SOFT_HYPHEN CA_SHYP
-#define CSA_SHYP CA_SHYP
-#define CSA_INVERTED_EXCLAIM CA_IEXL
-#define CSA_IXLM CA_IEXL
-#define CSA_POUND CA_PND
-#define CSA_GBP CA_PND
-#define CSA_EURO_BIS CA_EURO
-#define CSA_EURB CA_EURO
-#define CSA_THREE_EIGHTHS CA_TEIG
-#define CSA_3ON8 CA_TEIG
-#define CSA_FIVE_EIGHTHS CA_FEIG
-#define CSA_5ON8 CA_FEIG
-#define CSA_SEVEN_EIGHTHS CA_SEIG
-#define CSA_7ON8 CA_SEIG
-#define CSA_TRADEMARK CA_TM
-#define CSA_TM CA_TM
-#define CSA_PLUS_MINUS CA_PLMN
-#define CSA_PSMS CA_PLMN
-#define CSA_INVERTED_QUESTION CA_IQUE
-#define CSA_IQST CA_IQUE
-#define CSA_DEAD_OGONEK CA_OGON
-#define CSA_DOGO CA_OGON
-#define CSA_REGISTERED_TRADEMARK CA_REGD
-#define CSA_RTM CA_REGD
-#define CSA_YEN CA_YEN
-#define CSA_YUAN CA_YEN
-#define CSA_UP_ARROW CA_UARR
-#define CSA_DOTLESS_I CA_DLSI
-#define CSA_DLSI CA_DLSI
-#define CSA_DEAD_RING CA_RNGA
-#define CSA_DRNG CA_RNGA
-#define CSA_DEAD_MACRON CA_MACR
-#define CSA_DMCR CA_MACR
-#define CSA_SECTION CA_SECT
-#define CSA_SECT CA_SECT
-#define CSA_ORDINAL_INDICATOR_A CA_FORD
-#define CSA_ORDA CA_FORD
-#define CSA_DEAD_DOUBLE_ACUTE CA_DACU
-#define CSA_DDCT CA_DACU
-#define CSA_DEAD_CARON CA_CARN
-#define CSA_DCAR CA_CARN
-#define CSA_DEAD_BREVE CA_BREV
-#define CSA_DBRV CA_BREV
-#define CSA_BROKEN_PIPE CA_BRKP
-#define CSA_BPIP CA_BRKP
-#define CSA_COPYRIGHT CA_COPY
-#define CSA_CPRT CA_COPY
-#define CSA_LEFT_QUOTE CA_LSQU
-#define CSA_LQOT CA_LSQU
-#define CSA_RIGHT_QUOTE CA_RSQU
-#define CSA_RQOT CA_RSQU
-#define CSA_EIGHTH_NOTE CA_ENOT
-#define CSA_8NOT CA_ENOT
-#define CSA_ORDINAL_INDICATOR_O CA_MORD
-#define CSA_ORDO CA_MORD
-#define CSA_TIMES CA_MUL
-#define CSA_TIMS CA_MUL
-#define CSA_OBELUS CA_DIV
-#define CSA_OBEL CA_DIV
-#define CSA_DIVISION_SIGN CA_DIV
-#define CSA_DVSN CA_DIV
diff --git a/quantum/keymap_extras/keymap_colemak.h b/quantum/keymap_extras/keymap_colemak.h
index a97be9ad5d..6658cc1301 100644
--- a/quantum/keymap_extras/keymap_colemak.h
+++ b/quantum/keymap_extras/keymap_colemak.h
@@ -123,35 +123,3 @@
#define CM_LABK S(CM_COMM) // <
#define CM_RABK S(CM_DOT) // >
#define CM_QUES S(CM_SLSH) // /
-
-// DEPRECATED
-#define KC_CM_Q CM_Q
-#define KC_CM_W CM_W
-#define KC_CM_F CM_F
-#define KC_CM_P CM_P
-#define KC_CM_G CM_G
-#define KC_CM_J CM_J
-#define KC_CM_L CM_L
-#define KC_CM_U CM_U
-#define KC_CM_Y CM_Y
-#define KC_CM_SCLN CM_SCLN
-#define KC_CM_A CM_A
-#define KC_CM_R CM_R
-#define KC_CM_S CM_S
-#define KC_CM_T CM_T
-#define KC_CM_D CM_D
-#define KC_CM_H CM_H
-#define KC_CM_N CM_N
-#define KC_CM_E CM_E
-#define KC_CM_I CM_I
-#define KC_CM_O CM_O
-#define KC_CM_Z CM_Z
-#define KC_CM_X CM_X
-#define KC_CM_C CM_C
-#define KC_CM_V CM_V
-#define KC_CM_B CM_B
-#define KC_CM_K CM_K
-#define KC_CM_M CM_M
-#define KC_CM_COMM CM_COMM
-#define KC_CM_DOT CM_DOT
-#define KC_CM_SLSH CM_SLSH
diff --git a/quantum/keymap_extras/keymap_fr_ch.h b/quantum/keymap_extras/keymap_fr_ch.h
index fea44324b1..b1f2455a68 100644
--- a/quantum/keymap_extras/keymap_fr_ch.h
+++ b/quantum/keymap_extras/keymap_fr_ch.h
@@ -162,82 +162,3 @@
#define CH_RCBR ALGR(CH_DLR) // }
// Row 4
#define CH_BSLS ALGR(CH_LABK) // (backslash)
-
-// DEPRECATED
-#define FR_CH_Z CH_Z
-#define FR_CH_Y CH_Y
-#define FR_CH_A CH_A
-#define FR_CH_B CH_B
-#define FR_CH_C CH_C
-#define FR_CH_D CH_D
-#define FR_CH_E CH_E
-#define FR_CH_F CH_F
-#define FR_CH_G CH_G
-#define FR_CH_H CH_H
-#define FR_CH_I CH_I
-#define FR_CH_J CH_J
-#define FR_CH_K CH_K
-#define FR_CH_L CH_L
-#define FR_CH_M CH_M
-#define FR_CH_N CH_N
-#define FR_CH_O CH_O
-#define FR_CH_P CH_P
-#define FR_CH_Q CH_Q
-#define FR_CH_R CH_R
-#define FR_CH_S CH_S
-#define FR_CH_T CH_T
-#define FR_CH_U CH_U
-#define FR_CH_V CH_V
-#define FR_CH_W CH_W
-#define FR_CH_X CH_X
-#define FR_CH_0 CH_0
-#define FR_CH_1 CH_1
-#define FR_CH_2 CH_2
-#define FR_CH_3 CH_3
-#define FR_CH_4 CH_4
-#define FR_CH_5 CH_5
-#define FR_CH_6 CH_6
-#define FR_CH_7 CH_7
-#define FR_CH_8 CH_8
-#define FR_CH_9 CH_9
-#define FR_CH_DOT CH_DOT
-#define FR_CH_COMM CH_COMM
-#define FR_CH_QUOT CH_QUOT
-#define FR_CH_AE CH_AGRV
-#define FR_CH_UE CH_EGRV
-#define FR_CH_OE CH_EACU
-#define FR_CH_CIRC CH_CIRC
-#define FR_CH_LESS CH_LABK
-#define FR_CH_MINS CH_MINS
-#define FR_CH_DLR CH_DLR
-#define FR_CH_PARA CH_SECT
-#define FR_CH_DIAE CH_DIAE
-#define FR_CH_RING CH_DEG
-#define FR_CH_EXLM CH_EXLM
-#define FR_CH_PLUS CH_PLUS
-#define FR_CH_DQOT CH_DQUO
-#define FR_CH_ASTR CH_ASTR
-#define FR_CH_PERC CH_PERC
-#define FR_CH_AMPR CH_AMPR
-#define FR_CH_SLSH CH_SLSH
-#define FR_CH_LPRN CH_LPRN
-#define FR_CH_RPRN CH_RPRN
-#define FR_CH_EQL CH_EQL
-#define FR_CH_QST CH_QUES
-#define FR_CH_MORE CH_RABK
-#define FR_CH_COLN CH_COLN
-#define FR_CH_SCLN CH_SCLN
-#define FR_CH_UNDS CH_UNDS
-#define FR_CH_CCED CH_CCED
-#define FR_CH_GRV CH_GRV
-#define FR_CH_LCBR CH_LCBR
-#define FR_CH_LBRC CH_LBRC
-#define FR_CH_RBRC CH_RBRC
-#define FR_CH_RCBR CH_RCBR
-#define FR_CH_BSLS CH_BSLS
-#define FR_CH_AT CH_AT
-#define FR_CH_EURO CH_EURO
-#define FR_CH_TILD CH_TILD
-#define FR_CH_PIPE CH_PIPE
-#define FR_CH_HASH CH_HASH
-#define FR_CH_ACUT CH_ACUT
diff --git a/quantum/keymap_extras/keymap_french.h b/quantum/keymap_extras/keymap_french.h
index c62f2c4036..0be53f0a9c 100644
--- a/quantum/keymap_extras/keymap_french.h
+++ b/quantum/keymap_extras/keymap_french.h
@@ -152,14 +152,3 @@
// Row 2
#define FR_EURO ALGR(KC_E) // €
#define FR_CURR ALGR(FR_DLR) // ¤
-
-// DEPRECATED
-#define FR_AMP FR_AMPR
-#define FR_APOS FR_QUOT
-#define FR_LESS FR_LABK
-#define FR_OVRR FR_DEG
-#define FR_UMLT FR_DIAE
-#define FR_MU FR_MICR
-#define FR_GRTR FR_RABK
-#define FR_CCIRC FR_CIRC
-#define FR_BULT FR_CURR
diff --git a/quantum/keymap_extras/keymap_french_osx.h b/quantum/keymap_extras/keymap_french_osx.h
index 76eb221918..590a57e55c 100644
--- a/quantum/keymap_extras/keymap_french_osx.h
+++ b/quantum/keymap_extras/keymap_french_osx.h
@@ -246,15 +246,3 @@
#define FR_IQUE S(A(FR_COMM)) // ¿
#define FR_BSLS S(A(FR_COLN)) // (backslash)
#define FR_PLMN S(A(FR_EQL)) // ±
-
-// DEPRECATED
-#define FR_AMP FR_AMPR
-#define FR_EACU FR_LEAC
-#define FR_APOS FR_QUOT
-#define FR_EGRV FR_LEGR
-#define FR_CCED FR_LCCE
-#define FR_AGRV FR_LAGR
-#define FR_UGRV FR_LUGR
-#define FR_LESS FR_LABK
-#define FR_UMLT FR_DIAE
-#define FR_GRTR FR_RABK
diff --git a/quantum/keymap_extras/keymap_german.h b/quantum/keymap_extras/keymap_german.h
index 924bde6d36..085995b0c6 100644
--- a/quantum/keymap_extras/keymap_german.h
+++ b/quantum/keymap_extras/keymap_german.h
@@ -151,16 +151,3 @@
// Row 4
#define DE_PIPE ALGR(DE_LABK) // |
#define DE_MICR ALGR(DE_M) // µ
-
-// DEPRECATED
-#define DE_UE DE_UDIA
-#define DE_OE DE_ODIA
-#define DE_AE DE_ADIA
-#define DE_LESS DE_LABK
-#define DE_RING DE_DEG
-#define DE_DQOT DE_DQUO
-#define DE_PARA DE_SECT
-#define DE_QST DE_QUES
-#define DE_MORE DE_RABK
-#define DE_SQ2 DE_SUP2
-#define DE_SQ3 DE_SUP3
diff --git a/quantum/keymap_extras/keymap_german_ch.h b/quantum/keymap_extras/keymap_german_ch.h
index fee37eabb8..6723836870 100644
--- a/quantum/keymap_extras/keymap_german_ch.h
+++ b/quantum/keymap_extras/keymap_german_ch.h
@@ -162,21 +162,3 @@
#define CH_RCBR ALGR(CH_DLR) // }
// Row 4
#define CH_BSLS ALGR(CH_LABK) // (backslash)
-
-// DEPRECATED
-#define CH_AE CH_ADIA
-#define CH_UE CH_UDIA
-#define CH_OE CH_ODIA
-#define CH_PARA CH_SECT
-#define CH_CARR CH_CIRC
-#define CH_DIER CH_DIAE
-#define CH_LESS CH_LABK
-#define CH_RING CH_DEG
-#define CH_DQOT CH_DQUO
-#define CH_PAST CH_ASTR
-#define CH_CELA CH_CCED
-#define CH_QST CH_QUES
-#define CH_POND CH_PND
-#define CH_MORE CH_RABK
-#define CH_BRBR CH_BRKP
-#define CH_NOTL CH_NOT
diff --git a/quantum/keymap_extras/keymap_german_osx.h b/quantum/keymap_extras/keymap_german_osx.h
index be109a721e..82404fa5fd 100644
--- a/quantum/keymap_extras/keymap_german_osx.h
+++ b/quantum/keymap_extras/keymap_german_osx.h
@@ -241,83 +241,3 @@
#define DE_OGON S(A(DE_COMM)) // ˛
#define DE_DIV S(A(DE_DOT)) // ÷
#define DE_MDSH S(A(DE_MINS)) // —
-
-// DEPRECATED
-#define DE_OSX_CIRC DE_CIRC
-#define DE_OSX_1 DE_1
-#define DE_OSX_2 DE_2
-#define DE_OSX_3 DE_3
-#define DE_OSX_4 DE_4
-#define DE_OSX_5 DE_5
-#define DE_OSX_6 DE_6
-#define DE_OSX_7 DE_7
-#define DE_OSX_8 DE_8
-#define DE_OSX_9 DE_9
-#define DE_OSX_0 DE_0
-#define DE_OSX_SS DE_SS
-#define DE_OSX_ACUT DE_ACUT
-#define DE_OSX_Q DE_Q
-#define DE_OSX_W DE_W
-#define DE_OSX_E DE_E
-#define DE_OSX_R DE_R
-#define DE_OSX_T DE_T
-#define DE_OSX_Z DE_Z
-#define DE_OSX_U DE_U
-#define DE_OSX_I DE_I
-#define DE_OSX_O DE_O
-#define DE_OSX_P DE_P
-#define DE_OSX_UE DE_UDIA
-#define DE_OSX_PLUS DE_PLUS
-#define DE_OSX_A DE_A
-#define DE_OSX_S DE_S
-#define DE_OSX_D DE_D
-#define DE_OSX_F DE_F
-#define DE_OSX_G DE_G
-#define DE_OSX_H DE_H
-#define DE_OSX_J DE_J
-#define DE_OSX_K DE_K
-#define DE_OSX_L DE_L
-#define DE_OSX_OE DE_ODIA
-#define DE_OSX_AE DE_ADIA
-#define DE_OSX_HASH DE_HASH
-#define DE_OSX_LESS DE_LABK
-#define DE_OSX_Y DE_Y
-#define DE_OSX_X DE_X
-#define DE_OSX_C DE_C
-#define DE_OSX_V DE_V
-#define DE_OSX_B DE_B
-#define DE_OSX_N DE_N
-#define DE_OSX_M DE_M
-#define DE_OSX_COMM DE_COMM
-#define DE_OSX_DOT DE_DOT
-#define DE_OSX_MINS DE_MINS
-
-#define DE_OSX_RING DE_DEG
-#define DE_OSX_EXLM DE_EXLM
-#define DE_OSX_DQOT DE_DQUO
-#define DE_OSX_PARA DE_SECT
-#define DE_OSX_DLR DE_DLR
-#define DE_OSX_PERC DE_PERC
-#define DE_OSX_AMPR DE_AMPR
-#define DE_OSX_SLSH DE_SLSH
-#define DE_OSX_LPRN DE_LPRN
-#define DE_OSX_RPRN DE_RPRN
-#define DE_OSX_EQL DE_EQL
-#define DE_OSX_QST DE_QUES
-#define DE_OSX_GRV DE_GRV
-#define DE_OSX_ASTR DE_ASTR
-#define DE_OSX_QUOT DE_QUOT
-#define DE_OSX_MORE DE_RABK
-#define DE_OSX_COLN DE_COLN
-#define DE_OSX_SCLN DE_SCLN
-#define DE_OSX_UNDS DE_UNDS
-
-#define DE_OSX_LBRC DE_LBRC
-#define DE_OSX_RBRC DE_RBRC
-#define DE_OSX_PIPE DE_PIPE
-#define DE_OSX_LCBR DE_LCBR
-#define DE_OSX_RCBR DE_RCBR
-#define DE_OSX_AT DE_AT
-#define DE_OSX_EURO DE_EURO
-#define DE_OSX_TILD DE_TILD
-#define DE_OSX_BSLS DE_BSLS
diff --git a/quantum/keymap_extras/keymap_hungarian.h b/quantum/keymap_extras/keymap_hungarian.h
index 1b282a4615..a4e4b1a522 100644
--- a/quantum/keymap_extras/keymap_hungarian.h
+++ b/quantum/keymap_extras/keymap_hungarian.h
@@ -169,22 +169,3 @@
#define HU_RCBR ALGR(HU_N) // }
#define HU_SCLN ALGR(HU_COMM) // ;
#define HU_ASTR ALGR(HU_MINS) // *
-
-// DEPRECATED
-#define HU_OE HU_ODIA
-#define HU_UE HU_UDIA
-#define HU_OO HU_OACU
-#define HU_OEE HU_ODAC
-#define HU_UU HU_UACU
-#define HU_EE HU_EACU
-#define HU_AA HU_AACU
-#define HU_UEE HU_UDAC
-#define HU_II HU_IACU
-#define HU_PARA HU_SECT
-#define HU_DQOT HU_DQUO
-#define HU_QST HU_QUES
-#define HU_BRV HU_BREV
-#define HU_RING HU_RNGA
-#define HU_CRSS HU_MUL
-#define HU_LESS HU_LABK
-#define HU_MORE HU_RABK
diff --git a/quantum/keymap_extras/keymap_italian.h b/quantum/keymap_extras/keymap_italian.h
index 5d19e56d38..be495f85ba 100644
--- a/quantum/keymap_extras/keymap_italian.h
+++ b/quantum/keymap_extras/keymap_italian.h
@@ -163,22 +163,3 @@
// Row 2
#define IT_LCBR S(ALGR(IT_EGRV)) // {
#define IT_RCBR S(ALGR(IT_PLUS)) // }
-
-// DEPRECATED
-#define IT_BKSL IT_BSLS
-#define IT_APOS IT_QUOT
-#define IT_IACC IT_IGRV
-#define IT_EACC IT_EGRV
-#define IT_OACC IT_OGRV
-#define IT_AACC IT_AGRV
-#define IT_UACC IT_UGRV
-#define IT_LESS IT_LABK
-#define IT_DQOT IT_DQUO
-#define IT_STRL IT_PND
-#define IT_QST IT_QUES
-#define IT_CRC IT_CIRC
-#define IT_MORE IT_RABK
-#define IT_SHRP IT_HASH
-
-#define IT_X_PLUS X_RBRACKET
-#define IT_ACUT
diff --git a/quantum/keymap_extras/keymap_italian_osx_ansi.h b/quantum/keymap_extras/keymap_italian_osx_ansi.h
index 488a6561ee..c2b8e3cad6 100644
--- a/quantum/keymap_extras/keymap_italian_osx_ansi.h
+++ b/quantum/keymap_extras/keymap_italian_osx_ansi.h
@@ -248,21 +248,3 @@
#define IT_CUAC S(A(IT_M)) // Ú
#define IT_MDDT S(A(IT_DOT)) // ·
#define IT_MDSH S(A(IT_MINS)) // —
-
-// DEPRECATED
-#define IT_LESS IT_LABK
-#define IT_APOS IT_QUOT
-#define IT_IACC IT_IGRV
-#define IT_EACC IT_EGRV
-#define IT_UACC IT_UGRV
-#define IT_OACC IT_OGRV
-#define IT_AACC IT_AGRV
-#define IT_MORE IT_RABK
-#define IT_DQOT IT_DQUO
-#define IT_STRL IT_PND
-#define IT_QST IT_QUES
-#define IT_CRC IT_CIRC
-#define IT_DEGR IT_DEG
-#define IT_TILDE IT_TILD
-#define IT_GRAVE IT_GRV
-#define IT_SHRP IT_HASH
diff --git a/quantum/keymap_extras/keymap_italian_osx_iso.h b/quantum/keymap_extras/keymap_italian_osx_iso.h
index e4fb03acf7..61f76ddba7 100644
--- a/quantum/keymap_extras/keymap_italian_osx_iso.h
+++ b/quantum/keymap_extras/keymap_italian_osx_iso.h
@@ -249,21 +249,3 @@
#define IT_CUAC S(A(IT_M)) // Ú
#define IT_MDDT S(A(IT_DOT)) // ·
#define IT_MDSH S(A(IT_MINS)) // —
-
-// DEPRECATED
-#define IT_APOS IT_QUOT
-#define IT_IACC IT_IGRV
-#define IT_EACC IT_EGRV
-#define IT_OACC IT_OGRV
-#define IT_AACC IT_AGRV
-#define IT_UACC IT_UGRV
-#define IT_LESS IT_LABK
-#define IT_DQOT IT_DQUO
-#define IT_STRL IT_PND
-#define IT_QST IT_QUES
-#define IT_CRC IT_CIRC
-#define IT_DEGR IT_DEG
-#define IT_MORE IT_RABK
-#define IT_TILDE IT_TILD
-#define IT_GRAVE IT_GRV
-#define IT_SHRP IT_HASH
diff --git a/quantum/keymap_extras/keymap_jp.h b/quantum/keymap_extras/keymap_jp.h
index cd3c08f9f0..d10feb5856 100644
--- a/quantum/keymap_extras/keymap_jp.h
+++ b/quantum/keymap_extras/keymap_jp.h
@@ -137,12 +137,3 @@
#define JP_RABK S(JP_DOT) // >
#define JP_QUES S(JP_SLSH) // ?
#define JP_UNDS S(JP_BSLS) // _
-
-// DEPRECATED
-#define JP_ZHTG JP_ZKHK
-#define JP_DQT JP_DQUO
-#define JP_LT JP_LABK
-#define JP_GT JP_RABK
-
-#define JP_MEISU KC_LANG2 // Eisū (英数) on macOS
-#define JP_MKANA KC_LANG1 // Kana (かな) on macOS
diff --git a/quantum/keymap_extras/keymap_neo2.h b/quantum/keymap_extras/keymap_neo2.h
index 8d5323c6a8..f9fc00d794 100644
--- a/quantum/keymap_extras/keymap_neo2.h
+++ b/quantum/keymap_extras/keymap_neo2.h
@@ -88,55 +88,3 @@
#define NE_J KC_SLSH // J
// Row 5
#define NE_L4R KC_ALGR // (layer 4)
-
-// DEPRECATED
-#define NEO_A NE_A
-#define NEO_B NE_B
-#define NEO_C NE_C
-#define NEO_D NE_D
-#define NEO_E NE_E
-#define NEO_F NE_F
-#define NEO_G NE_G
-#define NEO_H NE_H
-#define NEO_I NE_I
-#define NEO_J NE_J
-#define NEO_K NE_K
-#define NEO_L NE_L
-#define NEO_M NE_M
-#define NEO_N NE_N
-#define NEO_O NE_O
-#define NEO_P NE_P
-#define NEO_Q NE_Q
-#define NEO_R NE_R
-#define NEO_S NE_S
-#define NEO_T NE_T
-#define NEO_U NE_U
-#define NEO_V NE_V
-#define NEO_W NE_W
-#define NEO_X NE_X
-#define NEO_Y NE_Y
-#define NEO_Z NE_Z
-#define NEO_AE NE_ADIA
-#define NEO_OE NE_ODIA
-#define NEO_UE NE_UDIA
-#define NEO_SS NE_SS
-#define NEO_DOT NE_DOT
-#define NEO_COMM NE_COMM
-#define NEO_1 NE_1
-#define NEO_2 NE_2
-#define NEO_3 NE_3
-#define NEO_4 NE_4
-#define NEO_5 NE_5
-#define NEO_6 NE_6
-#define NEO_7 NE_7
-#define NEO_8 NE_8
-#define NEO_9 NE_9
-#define NEO_0 NE_0
-#define NEO_MINS NE_MINS
-#define NEO_ACUT NE_ACUT
-#define NEO_GRV NE_GRV
-#define NEO_CIRC NE_CIRC
-#define NEO_L1_L NE_L3L
-#define NEO_L1_R NE_L3R
-#define NEO_L2_L NE_L4L
-#define NEO_L2_R NE_L4R
diff --git a/quantum/keymap_extras/keymap_norwegian.h b/quantum/keymap_extras/keymap_norwegian.h
index 74c0c1ae27..b2499f4fda 100644
--- a/quantum/keymap_extras/keymap_norwegian.h
+++ b/quantum/keymap_extras/keymap_norwegian.h
@@ -150,26 +150,3 @@
#define NO_TILD ALGR(NO_DIAE) // ~ (dead)
// Row 4
#define NO_MICR ALGR(NO_M) // µ
-
-// DEPRECATED
-#define NO_AM NO_ARNG
-#define NO_AA NO_ARNG
-#define NO_OSLH NO_OSTR
-#define NO_APOS NO_QUOT
-#define NO_LESS NO_LABK
-#define NO_QUO2 NO_DQUO
-#define NO_BULT NO_CURR
-#define NO_GRTR NO_RABK
-#define NO_MU NO_MICR
-// Norwegian macOS symbols
-#define NO_ACUT_MAC NO_BSLS // ´
-#define NO_APOS_MAC NO_LABK // '
-#define NO_AT_MAC NO_QUOT // @
-#define NO_BSLS_MAC S(ALGR(NO_7)) // (backslash)
-#define NO_DLR_MAC S(NO_4) // $
-#define NO_GRV_MAC ALGR(NO_BSLS) // `
-#define NO_GRTR_MAC S(NO_PIPE) // >
-#define NO_LCBR_MAC S(ALGR(NO_8)) // {
-#define NO_LESS_MAC NO_PIPE // <
-#define NO_PIPE_MAC ALGR(NO_7) // |
-#define NO_RCBR_MAC S(ALGR(NO_9)) // }
diff --git a/quantum/keymap_extras/keymap_slovenian.h b/quantum/keymap_extras/keymap_slovenian.h
index 06be62cf33..827fa06c25 100644
--- a/quantum/keymap_extras/keymap_slovenian.h
+++ b/quantum/keymap_extras/keymap_slovenian.h
@@ -161,11 +161,3 @@
#define SI_LCBR ALGR(SI_B) // {
#define SI_RCBR ALGR(SI_N) // }
#define SI_SECT ALGR(SI_M) // §
-
-// DEPRECATED
-#define SI_QOT SI_QUOT
-#define SI_SV SI_SCAR
-#define SI_CV SI_CCAR
-#define SI_ZV SI_ZCAR
-#define SI_DQOT SI_DQUO
-#define SI_QST SI_QUES
diff --git a/quantum/keymap_extras/keymap_spanish.h b/quantum/keymap_extras/keymap_spanish.h
index 4e888c5133..8432c56e99 100644
--- a/quantum/keymap_extras/keymap_spanish.h
+++ b/quantum/keymap_extras/keymap_spanish.h
@@ -151,12 +151,3 @@
// Row 3
#define ES_LCBR ALGR(ES_ACUT) // {
#define ES_RCBR ALGR(ES_CCED) // }
-
-// DEPRECATED
-#define ES_OVRR ES_MORD
-#define ES_APOS ES_QUOT
-#define ES_LESS ES_LABK
-#define ES_ASML ES_FORD
-#define ES_OVDT ES_BULT
-#define ES_UMLT ES_DIAE
-#define ES_GRTR ES_RABK
diff --git a/quantum/keymap_extras/keymap_swedish.h b/quantum/keymap_extras/keymap_swedish.h
index aef8d003d6..cadb66d3bd 100644
--- a/quantum/keymap_extras/keymap_swedish.h
+++ b/quantum/keymap_extras/keymap_swedish.h
@@ -154,26 +154,3 @@
// DEPRECATED
#include "keymap_nordic.h"
-
-#define SE_OSLH SE_ODIA
-#define SE_APOS SE_QUOT
-#define SE_LESS SE_LABK
-#define SE_QUO2 SE_DQUO
-#define SE_BULT SE_CURR
-#define SE_GRTR SE_RABK
-#define SE_AA SE_ARNG
-#define SE_AE SE_ADIA
-#define SE_AM SE_ARNG
-#define SE_MU SE_MICR
-// Swedish macOS symbols (not vetted)
-#define SE_ACUT_MAC SE_ACUT
-#define SE_APOS_MAC SE_LABK
-#define SE_AT_MAC SE_ADIA
-#define SE_BSLS_MAC S(SE_LCBR)
-#define SE_DLR_MAC SE_CURR
-#define SE_GRV_MAC SE_BSLS
-#define SE_GRTR_MAC SE_HALF
-#define SE_LCBR_MAC S(SE_LBRC)
-#define SE_LESS_MAC SE_SECT
-#define SE_PIPE_MAC SE_LCBR
-#define SE_RCBR_MAC S(SE_RBRC)
diff --git a/quantum/keymap_extras/keymap_uk.h b/quantum/keymap_extras/keymap_uk.h
index 1eba0ed2f4..03fe8149f0 100644
--- a/quantum/keymap_extras/keymap_uk.h
+++ b/quantum/keymap_extras/keymap_uk.h
@@ -149,78 +149,3 @@
#define UK_OACU ALGR(KC_O) // Ó
// Row 3
#define UK_AACU ALGR(KC_A) // Á
-
-// DEPRECATED
-#define UK_ESC KC_ESC
-#define UK_F1 KC_F1
-#define UK_F2 KC_F2
-#define UK_F3 KC_F3
-#define UK_F4 KC_F4
-#define UK_F5 KC_F5
-#define UK_F6 KC_F6
-#define UK_F7 KC_F7
-#define UK_F8 KC_F8
-#define UK_F9 KC_F9
-#define UK_F10 KC_F10
-#define UK_F11 KC_F11
-#define UK_F12 KC_F12
-#define UK_PSCR KC_PSCR
-#define UK_SLCK KC_SLCK
-#define UK_PAUS KC_PAUS
-#define UK_BSPC KC_BSPC
-#define UK_TAB KC_TAB
-#define UK_ENT KC_ENT
-#define UK_LSFT KC_LSFT
-#define UK_RSFT KC_RSFT
-#define UK_LCTL KC_LCTL
-#define UK_LGUI KC_LGUI
-#define UK_LALT KC_LALT
-#define UK_SPC KC_SPC
-#define UK_RALT KC_RALT
-#define UK_RGUI KC_RGUI
-#define UK_RCTL KC_RCTL
-#define UK_INS KC_INS
-#define UK_DEL KC_DEL
-#define UK_HOME KC_HOME
-#define UK_END KC_END
-#define UK_PGUP KC_PGUP
-#define UK_PGDN KC_PGDN
-#define UK_UP KC_UP
-#define UK_LEFT KC_LEFT
-#define UK_DOWN KC_DOWN
-#define UK_RGHT KC_RGHT
-#define UK_PSLS KC_PSLS
-#define UK_PAST KC_PAST
-#define UK_PMNS KC_PMNS
-#define UK_PPLS KC_PPLS
-#define UK_PENT KC_PENT
-#define UK_P1 KC_P1
-#define UK_P2 KC_P2
-#define UK_P3 KC_P3
-#define UK_P4 KC_P4
-#define UK_P5 KC_P5
-#define UK_P6 KC_P6
-#define UK_P7 KC_P7
-#define UK_P8 KC_P8
-#define UK_P9 KC_P9
-#define UK_P0 KC_P0
-#define UK_PDOT KC_PDOT
-#define UK_PEQL KC_PEQL
-#define UK_PCMM KC_PCMM
-#define UK_F13 KC_F13
-#define UK_F14 KC_F14
-#define UK_F15 KC_F15
-#define UK_F16 KC_F16
-#define UK_F17 KC_F17
-#define UK_F18 KC_F18
-#define UK_F19 KC_F19
-#define UK_F20 KC_F20
-#define UK_F21 KC_F21
-#define UK_F22 KC_F22
-#define UK_F23 KC_F23
-#define UK_F24 KC_F24
-#define UK_EACT UK_EACU
-#define UK_UACT UK_UACU
-#define UK_IACT UK_IACU
-#define UK_OACT UK_OACU
-#define UK_AACT UK_OACU
diff --git a/quantum/keymap_extras/keymap_workman.h b/quantum/keymap_extras/keymap_workman.h
index 4c7530aa07..6367d68351 100644
--- a/quantum/keymap_extras/keymap_workman.h
+++ b/quantum/keymap_extras/keymap_workman.h
@@ -123,32 +123,3 @@
#define WK_LABK S(WK_COMM) // <
#define WK_RABK S(WK_DOT) // >
#define WK_QUES S(WK_SLSH) // ?
-
-// DEPRECATED
-#define KC_WK_Q WK_Q
-#define KC_WK_D WK_D
-#define KC_WK_R WK_R
-#define KC_WK_W WK_W
-#define KC_WK_B WK_B
-#define KC_WK_J WK_J
-#define KC_WK_F WK_F
-#define KC_WK_U WK_U
-#define KC_WK_P WK_P
-#define KC_WK_SCLN WK_SCLN
-#define KC_WK_A WK_A
-#define KC_WK_S WK_S
-#define KC_WK_H WK_H
-#define KC_WK_T WK_T
-#define KC_WK_G WK_G
-#define KC_WK_Y WK_Y
-#define KC_WK_N WK_N
-#define KC_WK_E WK_E
-#define KC_WK_O WK_O
-#define KC_WK_I WK_I
-#define KC_WK_Z WK_Z
-#define KC_WK_X WK_X
-#define KC_WK_M WK_M
-#define KC_WK_C WK_C
-#define KC_WK_V WK_V
-#define KC_WK_K WK_K
-#define KC_WK_L WK_L
diff --git a/quantum/led_matrix_animations/alpha_mods_anim.h b/quantum/led_matrix/animations/alpha_mods_anim.h
index 6f69f6892b..a4638fde69 100644
--- a/quantum/led_matrix_animations/alpha_mods_anim.h
+++ b/quantum/led_matrix/animations/alpha_mods_anim.h
@@ -21,4 +21,4 @@ bool ALPHAS_MODS(effect_params_t* params) {
}
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#endif // DISABLE_LED_MATRIX_ALPHAS_MODS
+#endif // DISABLE_LED_MATRIX_ALPHAS_MODS
diff --git a/quantum/led_matrix_animations/band_anim.h b/quantum/led_matrix/animations/band_anim.h
index 523dba1b78..f9cb85dc4f 100644
--- a/quantum/led_matrix_animations/band_anim.h
+++ b/quantum/led_matrix/animations/band_anim.h
@@ -10,4 +10,4 @@ static uint8_t BAND_math(uint8_t val, uint8_t i, uint8_t time) {
bool BAND(effect_params_t* params) { return effect_runner_i(params, &BAND_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#endif // DISABLE_LED_MATRIX_BAND
+#endif // DISABLE_LED_MATRIX_BAND
diff --git a/quantum/led_matrix_animations/band_pinwheel_anim.h b/quantum/led_matrix/animations/band_pinwheel_anim.h
index fb3b835cad..d3144bffbf 100644
--- a/quantum/led_matrix_animations/band_pinwheel_anim.h
+++ b/quantum/led_matrix/animations/band_pinwheel_anim.h
@@ -7,4 +7,4 @@ static uint8_t BAND_PINWHEEL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t t
bool BAND_PINWHEEL(effect_params_t* params) { return effect_runner_dx_dy(params, &BAND_PINWHEEL_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#endif // DISABLE_LED_MATRIX_BAND_PINWHEEL
+#endif // DISABLE_LED_MATRIX_BAND_PINWHEEL
diff --git a/quantum/led_matrix_animations/band_spiral_anim.h b/quantum/led_matrix/animations/band_spiral_anim.h
index fca22aad9c..defbe69676 100644
--- a/quantum/led_matrix_animations/band_spiral_anim.h
+++ b/quantum/led_matrix/animations/band_spiral_anim.h
@@ -7,4 +7,4 @@ static uint8_t BAND_SPIRAL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dis
bool BAND_SPIRAL(effect_params_t* params) { return effect_runner_dx_dy_dist(params, &BAND_SPIRAL_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#endif // DISABLE_LED_MATRIX_BAND_SPIRAL
+#endif // DISABLE_LED_MATRIX_BAND_SPIRAL
diff --git a/quantum/led_matrix_animations/breathing_anim.h b/quantum/led_matrix/animations/breathing_anim.h
index 00310e3f65..4f49f50690 100644
--- a/quantum/led_matrix_animations/breathing_anim.h
+++ b/quantum/led_matrix/animations/breathing_anim.h
@@ -16,4 +16,4 @@ bool BREATHING(effect_params_t* params) {
}
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#endif // DISABLE_LED_MATRIX_BREATHING
+#endif // DISABLE_LED_MATRIX_BREATHING
diff --git a/quantum/led_matrix_animations/cycle_left_right_anim.h b/quantum/led_matrix/animations/cycle_left_right_anim.h
index 51e81d57ca..c426d02fd5 100644
--- a/quantum/led_matrix_animations/cycle_left_right_anim.h
+++ b/quantum/led_matrix/animations/cycle_left_right_anim.h
@@ -7,4 +7,4 @@ static uint8_t CYCLE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) { ret
bool CYCLE_LEFT_RIGHT(effect_params_t* params) { return effect_runner_i(params, &CYCLE_LEFT_RIGHT_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#endif // DISABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
+#endif // DISABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
diff --git a/quantum/led_matrix_animations/cycle_out_in_anim.h b/quantum/led_matrix/animations/cycle_out_in_anim.h
index f62061552c..55527556fd 100644
--- a/quantum/led_matrix_animations/cycle_out_in_anim.h
+++ b/quantum/led_matrix/animations/cycle_out_in_anim.h
@@ -7,4 +7,4 @@ static uint8_t CYCLE_OUT_IN_math(uint8_t val, int16_t dx, int16_t dy, uint8_t di
bool CYCLE_OUT_IN(effect_params_t* params) { return effect_runner_dx_dy_dist(params, &CYCLE_OUT_IN_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#endif // DISABLE_LED_MATRIX_CYCLE_OUT_IN
+#endif // DISABLE_LED_MATRIX_CYCLE_OUT_IN
diff --git a/quantum/led_matrix_animations/cycle_up_down_anim.h b/quantum/led_matrix/animations/cycle_up_down_anim.h
index bd1d125672..d97de0d1ec 100644
--- a/quantum/led_matrix_animations/cycle_up_down_anim.h
+++ b/quantum/led_matrix/animations/cycle_up_down_anim.h
@@ -7,4 +7,4 @@ static uint8_t CYCLE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) { return
bool CYCLE_UP_DOWN(effect_params_t* params) { return effect_runner_i(params, &CYCLE_UP_DOWN_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#endif // DISABLE_LED_MATRIX_CYCLE_UP_DOWN
+#endif // DISABLE_LED_MATRIX_CYCLE_UP_DOWN
diff --git a/quantum/led_matrix_animations/dual_beacon_anim.h b/quantum/led_matrix/animations/dual_beacon_anim.h
index 9b8a7877c9..e1bc5ae464 100644
--- a/quantum/led_matrix_animations/dual_beacon_anim.h
+++ b/quantum/led_matrix/animations/dual_beacon_anim.h
@@ -7,4 +7,4 @@ static uint8_t DUAL_BEACON_math(uint8_t val, int8_t sin, int8_t cos, uint8_t i,
bool DUAL_BEACON(effect_params_t* params) { return effect_runner_sin_cos_i(params, &DUAL_BEACON_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#endif // DISABLE_LED_MATRIX_DUAL_BEACON
+#endif // DISABLE_LED_MATRIX_DUAL_BEACON
diff --git a/quantum/led_matrix/animations/led_matrix_effects.inc b/quantum/led_matrix/animations/led_matrix_effects.inc
new file mode 100644
index 0000000000..ad1f46b242
--- /dev/null
+++ b/quantum/led_matrix/animations/led_matrix_effects.inc
@@ -0,0 +1,18 @@
+// Add your new core led matrix effect here, order determines enum order
+#include "solid_anim.h"
+#include "alpha_mods_anim.h"
+#include "breathing_anim.h"
+#include "band_anim.h"
+#include "band_pinwheel_anim.h"
+#include "band_spiral_anim.h"
+#include "cycle_left_right_anim.h"
+#include "cycle_up_down_anim.h"
+#include "cycle_out_in_anim.h"
+#include "dual_beacon_anim.h"
+#include "solid_reactive_simple_anim.h"
+#include "solid_reactive_wide.h"
+#include "solid_reactive_cross.h"
+#include "solid_reactive_nexus.h"
+#include "solid_splash_anim.h"
+#include "wave_left_right_anim.h"
+#include "wave_up_down_anim.h"
diff --git a/quantum/led_matrix_runners/effect_runner_dx_dy.h b/quantum/led_matrix/animations/runners/effect_runner_dx_dy.h
index ef97631b90..ef97631b90 100644
--- a/quantum/led_matrix_runners/effect_runner_dx_dy.h
+++ b/quantum/led_matrix/animations/runners/effect_runner_dx_dy.h
diff --git a/quantum/led_matrix_runners/effect_runner_dx_dy_dist.h b/quantum/led_matrix/animations/runners/effect_runner_dx_dy_dist.h
index 5ef5938be0..5ef5938be0 100644
--- a/quantum/led_matrix_runners/effect_runner_dx_dy_dist.h
+++ b/quantum/led_matrix/animations/runners/effect_runner_dx_dy_dist.h
diff --git a/quantum/led_matrix_runners/effect_runner_i.h b/quantum/led_matrix/animations/runners/effect_runner_i.h
index b3015759be..b3015759be 100644
--- a/quantum/led_matrix_runners/effect_runner_i.h
+++ b/quantum/led_matrix/animations/runners/effect_runner_i.h
diff --git a/quantum/led_matrix_runners/effect_runner_reactive.h b/quantum/led_matrix/animations/runners/effect_runner_reactive.h
index 4369ea8c49..4369ea8c49 100644
--- a/quantum/led_matrix_runners/effect_runner_reactive.h
+++ b/quantum/led_matrix/animations/runners/effect_runner_reactive.h
diff --git a/quantum/led_matrix_runners/effect_runner_reactive_splash.h b/quantum/led_matrix/animations/runners/effect_runner_reactive_splash.h
index d6eb9731ee..d6eb9731ee 100644
--- a/quantum/led_matrix_runners/effect_runner_reactive_splash.h
+++ b/quantum/led_matrix/animations/runners/effect_runner_reactive_splash.h
diff --git a/quantum/led_matrix_runners/effect_runner_sin_cos_i.h b/quantum/led_matrix/animations/runners/effect_runner_sin_cos_i.h
index 4a5219abd1..4a5219abd1 100644
--- a/quantum/led_matrix_runners/effect_runner_sin_cos_i.h
+++ b/quantum/led_matrix/animations/runners/effect_runner_sin_cos_i.h
diff --git a/quantum/led_matrix/animations/runners/led_matrix_runners.inc b/quantum/led_matrix/animations/runners/led_matrix_runners.inc
new file mode 100644
index 0000000000..c09022bb0f
--- /dev/null
+++ b/quantum/led_matrix/animations/runners/led_matrix_runners.inc
@@ -0,0 +1,6 @@
+#include "effect_runner_dx_dy_dist.h"
+#include "effect_runner_dx_dy.h"
+#include "effect_runner_i.h"
+#include "effect_runner_sin_cos_i.h"
+#include "effect_runner_reactive.h"
+#include "effect_runner_reactive_splash.h"
diff --git a/quantum/led_matrix_animations/solid_anim.h b/quantum/led_matrix/animations/solid_anim.h
index 4c9e43c581..4c9e43c581 100644
--- a/quantum/led_matrix_animations/solid_anim.h
+++ b/quantum/led_matrix/animations/solid_anim.h
diff --git a/quantum/led_matrix_animations/solid_reactive_cross.h b/quantum/led_matrix/animations/solid_reactive_cross.h
index f402d99b37..94425c959f 100644
--- a/quantum/led_matrix_animations/solid_reactive_cross.h
+++ b/quantum/led_matrix/animations/solid_reactive_cross.h
@@ -31,5 +31,5 @@ bool SOLID_REACTIVE_MULTICROSS(effect_params_t* params) { return effect_runner_r
# endif
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)
-#endif // LED_MATRIX_KEYREACTIVE_ENABLED
+# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)
+#endif // LED_MATRIX_KEYREACTIVE_ENABLED
diff --git a/quantum/led_matrix_animations/solid_reactive_nexus.h b/quantum/led_matrix/animations/solid_reactive_nexus.h
index 4d0d252263..504b1104f1 100644
--- a/quantum/led_matrix_animations/solid_reactive_nexus.h
+++ b/quantum/led_matrix/animations/solid_reactive_nexus.h
@@ -28,5 +28,5 @@ bool SOLID_REACTIVE_MULTINEXUS(effect_params_t* params) { return effect_runner_r
# endif
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS)
-#endif // LED_MATRIX_KEYREACTIVE_ENABLED
+# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS)
+#endif // LED_MATRIX_KEYREACTIVE_ENABLED
diff --git a/quantum/led_matrix_animations/solid_reactive_simple_anim.h b/quantum/led_matrix/animations/solid_reactive_simple_anim.h
index 30e2527f60..4752a84162 100644
--- a/quantum/led_matrix_animations/solid_reactive_simple_anim.h
+++ b/quantum/led_matrix/animations/solid_reactive_simple_anim.h
@@ -8,5 +8,5 @@ static uint8_t SOLID_REACTIVE_SIMPLE_math(uint8_t val, uint16_t offset) { return
bool SOLID_REACTIVE_SIMPLE(effect_params_t* params) { return effect_runner_reactive(params, &SOLID_REACTIVE_SIMPLE_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-# endif // DISABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
-#endif // LED_MATRIX_KEYREACTIVE_ENABLED
+# endif // DISABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
+#endif // LED_MATRIX_KEYREACTIVE_ENABLED
diff --git a/quantum/led_matrix_animations/solid_reactive_wide.h b/quantum/led_matrix/animations/solid_reactive_wide.h
index 34a230c259..922e32fe5f 100644
--- a/quantum/led_matrix_animations/solid_reactive_wide.h
+++ b/quantum/led_matrix/animations/solid_reactive_wide.h
@@ -26,5 +26,5 @@ bool SOLID_REACTIVE_MULTIWIDE(effect_params_t* params) { return effect_runner_re
# endif
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)
-#endif // LED_MATRIX_KEYREACTIVE_ENABLED
+# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)
+#endif // LED_MATRIX_KEYREACTIVE_ENABLED
diff --git a/quantum/led_matrix_animations/solid_splash_anim.h b/quantum/led_matrix/animations/solid_splash_anim.h
index 4f6ba3d343..d95889b813 100644
--- a/quantum/led_matrix_animations/solid_splash_anim.h
+++ b/quantum/led_matrix/animations/solid_splash_anim.h
@@ -26,5 +26,5 @@ bool SOLID_MULTISPLASH(effect_params_t* params) { return effect_runner_reactive_
# endif
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-# endif // !defined(DISABLE_LED_MATRIX_SPLASH) && !defined(DISABLE_LED_MATRIX_MULTISPLASH)
-#endif // LED_MATRIX_KEYREACTIVE_ENABLED
+# endif // !defined(DISABLE_LED_MATRIX_SPLASH) && !defined(DISABLE_LED_MATRIX_MULTISPLASH)
+#endif // LED_MATRIX_KEYREACTIVE_ENABLED
diff --git a/quantum/led_matrix_animations/wave_left_right_anim.h b/quantum/led_matrix/animations/wave_left_right_anim.h
index 736f22ddc5..8579f1b45f 100644
--- a/quantum/led_matrix_animations/wave_left_right_anim.h
+++ b/quantum/led_matrix/animations/wave_left_right_anim.h
@@ -7,4 +7,4 @@ static uint8_t WAVE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) { retu
bool WAVE_LEFT_RIGHT(effect_params_t* params) { return effect_runner_i(params, &WAVE_LEFT_RIGHT_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#endif // DISABLE_LED_MATRIX_WAVE_LEFT_RIGHT
+#endif // DISABLE_LED_MATRIX_WAVE_LEFT_RIGHT
diff --git a/quantum/led_matrix_animations/wave_up_down_anim.h b/quantum/led_matrix/animations/wave_up_down_anim.h
index 3cab0597d4..635c608414 100644
--- a/quantum/led_matrix_animations/wave_up_down_anim.h
+++ b/quantum/led_matrix/animations/wave_up_down_anim.h
@@ -7,4 +7,4 @@ static uint8_t WAVE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) { return
bool WAVE_UP_DOWN(effect_params_t* params) { return effect_runner_i(params, &WAVE_UP_DOWN_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#endif // DISABLE_LED_MATRIX_WAVE_UP_DOWN
+#endif // DISABLE_LED_MATRIX_WAVE_UP_DOWN
diff --git a/quantum/led_matrix.c b/quantum/led_matrix/led_matrix.c
index 7e0fdf896a..bcf258b260 100644
--- a/quantum/led_matrix.c
+++ b/quantum/led_matrix/led_matrix.c
@@ -33,20 +33,23 @@ const led_point_t k_led_matrix_center = {112, 32};
const led_point_t k_led_matrix_center = LED_MATRIX_CENTER;
#endif
+// clang-format off
+#ifndef LED_MATRIX_IMMEDIATE_EEPROM
+# define led_eeconfig_update(v) led_update_eeprom |= v
+#else
+# define led_eeconfig_update(v) if (v) eeconfig_update_led_matrix()
+#endif
+// clang-format on
+
// Generic effect runners
-#include "led_matrix_runners/effect_runner_dx_dy_dist.h"
-#include "led_matrix_runners/effect_runner_dx_dy.h"
-#include "led_matrix_runners/effect_runner_i.h"
-#include "led_matrix_runners/effect_runner_sin_cos_i.h"
-#include "led_matrix_runners/effect_runner_reactive.h"
-#include "led_matrix_runners/effect_runner_reactive_splash.h"
+#include "led_matrix_runners.inc"
// ------------------------------------------
// -----Begin led effect includes macros-----
#define LED_MATRIX_EFFECT(name)
#define LED_MATRIX_CUSTOM_EFFECT_IMPLS
-#include "led_matrix_animations/led_matrix_effects.inc"
+#include "led_matrix_effects.inc"
#ifdef LED_MATRIX_CUSTOM_KB
# include "led_matrix_kb.inc"
#endif
@@ -67,10 +70,6 @@ const led_point_t k_led_matrix_center = LED_MATRIX_CENTER;
# define LED_DISABLE_TIMEOUT 0
#endif
-#if LED_DISABLE_WHEN_USB_SUSPENDED != 1
-# undef LED_DISABLE_WHEN_USB_SUSPENDED
-#endif
-
#if !defined(LED_MATRIX_MAXIMUM_BRIGHTNESS) || LED_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX
# undef LED_MATRIX_MAXIMUM_BRIGHTNESS
# define LED_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX
@@ -108,6 +107,7 @@ last_hit_t g_last_hit_tracker;
// internals
static bool suspend_state = false;
+static bool led_update_eeprom = false;
static uint8_t led_last_enable = UINT8_MAX;
static uint8_t led_last_effect = UINT8_MAX;
static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false};
@@ -219,7 +219,7 @@ void process_led_matrix(uint8_t row, uint8_t col, bool pressed) {
memcpy(&last_hit_buffer.y[0], &last_hit_buffer.y[led_count], LED_HITS_TO_REMEMBER - led_count);
memcpy(&last_hit_buffer.tick[0], &last_hit_buffer.tick[led_count], (LED_HITS_TO_REMEMBER - led_count) * 2); // 16 bit
memcpy(&last_hit_buffer.index[0], &last_hit_buffer.index[led_count], LED_HITS_TO_REMEMBER - led_count);
- last_hit_buffer.count--;
+ last_hit_buffer.count = LED_HITS_TO_REMEMBER - led_count;
}
for (uint8_t i = 0; i < led_count; i++) {
@@ -280,6 +280,8 @@ static void led_task_timers(void) {
static void led_task_sync(void) {
// next task
+ if (led_update_eeprom) eeconfig_update_led_matrix();
+ led_update_eeprom = false;
if (sync_timer_elapsed32(g_led_timer) >= LED_MATRIX_LED_FLUSH_LIMIT) led_task_state = STARTING;
}
@@ -318,7 +320,7 @@ static void led_task_render(uint8_t effect) {
case LED_MATRIX_##name: \
rendering = name(&led_effect_params); \
break;
-#include "led_matrix_animations/led_matrix_effects.inc"
+#include "led_matrix_effects.inc"
#undef LED_MATRIX_EFFECT
#if defined(LED_MATRIX_CUSTOM_KB) || defined(LED_MATRIX_CUSTOM_USER)
@@ -457,7 +459,7 @@ void led_matrix_init(void) {
void led_matrix_set_suspend_state(bool state) {
#ifdef LED_DISABLE_WHEN_USB_SUSPENDED
- if (state) {
+ if (state && is_keyboard_master()) {
led_matrix_set_value_all(0); // turn off all LEDs when suspending
}
suspend_state = state;
@@ -469,9 +471,7 @@ bool led_matrix_get_suspend_state(void) { return suspend_state; }
void led_matrix_toggle_eeprom_helper(bool write_to_eeprom) {
led_matrix_eeconfig.enable ^= 1;
led_task_state = STARTING;
- if (write_to_eeprom) {
- eeconfig_update_led_matrix();
- }
+ led_eeconfig_update(write_to_eeprom);
dprintf("led matrix toggle [%s]: led_matrix_eeconfig.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.enable);
}
void led_matrix_toggle_noeeprom(void) { led_matrix_toggle_eeprom_helper(false); }
@@ -479,7 +479,7 @@ void led_matrix_toggle(void) { led_matrix_toggle_eeprom_helper(true); }
void led_matrix_enable(void) {
led_matrix_enable_noeeprom();
- eeconfig_update_led_matrix();
+ led_eeconfig_update(true);
}
void led_matrix_enable_noeeprom(void) {
@@ -489,7 +489,7 @@ void led_matrix_enable_noeeprom(void) {
void led_matrix_disable(void) {
led_matrix_disable_noeeprom();
- eeconfig_update_led_matrix();
+ led_eeconfig_update(true);
}
void led_matrix_disable_noeeprom(void) {
@@ -511,9 +511,7 @@ void led_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
led_matrix_eeconfig.mode = mode;
}
led_task_state = STARTING;
- if (write_to_eeprom) {
- eeconfig_update_led_matrix();
- }
+ led_eeconfig_update(write_to_eeprom);
dprintf("led matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.mode);
}
void led_matrix_mode_noeeprom(uint8_t mode) { led_matrix_mode_eeprom_helper(mode, false); }
@@ -540,9 +538,7 @@ void led_matrix_set_val_eeprom_helper(uint8_t val, bool write_to_eeprom) {
return;
}
led_matrix_eeconfig.val = (val > LED_MATRIX_MAXIMUM_BRIGHTNESS) ? LED_MATRIX_MAXIMUM_BRIGHTNESS : val;
- if (write_to_eeprom) {
- eeconfig_update_led_matrix();
- }
+ led_eeconfig_update(write_to_eeprom);
dprintf("led matrix set val [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.val);
}
void led_matrix_set_val_noeeprom(uint8_t val) { led_matrix_set_val_eeprom_helper(val, false); }
@@ -560,9 +556,7 @@ void led_matrix_decrease_val(void) { led_matrix_decrease_val_helper(true); }
void led_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {
led_matrix_eeconfig.speed = speed;
- if (write_to_eeprom) {
- eeconfig_update_led_matrix();
- }
+ led_eeconfig_update(write_to_eeprom);
dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.speed);
}
void led_matrix_set_speed_noeeprom(uint8_t speed) { led_matrix_set_speed_eeprom_helper(speed, false); }
diff --git a/quantum/led_matrix.h b/quantum/led_matrix/led_matrix.h
index 0984de73b3..6f85854fbe 100644
--- a/quantum/led_matrix.h
+++ b/quantum/led_matrix/led_matrix.h
@@ -56,7 +56,7 @@ enum led_matrix_effects {
// --------------------------------------
// -----Begin led effect enum macros-----
#define LED_MATRIX_EFFECT(name, ...) LED_MATRIX_##name,
-#include "led_matrix_animations/led_matrix_effects.inc"
+#include "led_matrix_effects.inc"
#undef LED_MATRIX_EFFECT
#if defined(LED_MATRIX_CUSTOM_KB) || defined(LED_MATRIX_CUSTOM_USER)
diff --git a/quantum/led_matrix_drivers.c b/quantum/led_matrix/led_matrix_drivers.c
index 1d46b2c506..1d46b2c506 100644
--- a/quantum/led_matrix_drivers.c
+++ b/quantum/led_matrix/led_matrix_drivers.c
diff --git a/quantum/led_matrix_types.h b/quantum/led_matrix/led_matrix_types.h
index 61cdbd9b8e..61cdbd9b8e 100644
--- a/quantum/led_matrix_types.h
+++ b/quantum/led_matrix/led_matrix_types.h
diff --git a/quantum/led_matrix_animations/led_matrix_effects.inc b/quantum/led_matrix_animations/led_matrix_effects.inc
deleted file mode 100644
index 67237c5683..0000000000
--- a/quantum/led_matrix_animations/led_matrix_effects.inc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Add your new core led matrix effect here, order determins enum order, requires "led_matrix_animations/ directory
-#include "led_matrix_animations/solid_anim.h"
-#include "led_matrix_animations/alpha_mods_anim.h"
-#include "led_matrix_animations/breathing_anim.h"
-#include "led_matrix_animations/band_anim.h"
-#include "led_matrix_animations/band_pinwheel_anim.h"
-#include "led_matrix_animations/band_spiral_anim.h"
-#include "led_matrix_animations/cycle_left_right_anim.h"
-#include "led_matrix_animations/cycle_up_down_anim.h"
-#include "led_matrix_animations/cycle_out_in_anim.h"
-#include "led_matrix_animations/dual_beacon_anim.h"
-#include "led_matrix_animations/solid_reactive_simple_anim.h"
-#include "led_matrix_animations/solid_reactive_wide.h"
-#include "led_matrix_animations/solid_reactive_cross.h"
-#include "led_matrix_animations/solid_reactive_nexus.h"
-#include "led_matrix_animations/solid_splash_anim.h"
-#include "led_matrix_animations/wave_left_right_anim.h"
-#include "led_matrix_animations/wave_up_down_anim.h"
diff --git a/quantum/matrix.c b/quantum/matrix.c
index 34d6af2e6d..d22817bf48 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -16,22 +16,69 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdbool.h>
+#include <string.h>
#include "util.h"
#include "matrix.h"
#include "debounce.h"
#include "quantum.h"
+#ifdef SPLIT_KEYBOARD
+# include "split_common/split_util.h"
+# include "split_common/transactions.h"
+
+# ifndef ERROR_DISCONNECT_COUNT
+# define ERROR_DISCONNECT_COUNT 5
+# endif // ERROR_DISCONNECT_COUNT
+
+# define ROWS_PER_HAND (MATRIX_ROWS / 2)
+#else
+# define ROWS_PER_HAND (MATRIX_ROWS)
+#endif
+
+#ifdef DIRECT_PINS_RIGHT
+# define SPLIT_MUTABLE
+#else
+# define SPLIT_MUTABLE const
+#endif
+#ifdef MATRIX_ROW_PINS_RIGHT
+# define SPLIT_MUTABLE_ROW
+#else
+# define SPLIT_MUTABLE_ROW const
+#endif
+#ifdef MATRIX_COL_PINS_RIGHT
+# define SPLIT_MUTABLE_COL
+#else
+# define SPLIT_MUTABLE_COL const
+#endif
#ifdef DIRECT_PINS
-static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS;
+static SPLIT_MUTABLE pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS;
#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW)
-static const pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
-static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
+# ifdef MATRIX_ROW_PINS
+static SPLIT_MUTABLE_ROW pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
+# endif // MATRIX_ROW_PINS
+# ifdef MATRIX_COL_PINS
+static SPLIT_MUTABLE_COL pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
+# endif // MATRIX_COL_PINS
#endif
/* matrix state(1:on, 0:off) */
extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values
+#ifdef SPLIT_KEYBOARD
+// row offsets for each hand
+uint8_t thisHand, thatHand;
+#endif
+
+// user-defined overridable functions
+__attribute__((weak)) void matrix_init_pins(void);
+__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
+__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
+#ifdef SPLIT_KEYBOARD
+__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); }
+__attribute__((weak)) void matrix_slave_scan_user(void) {}
+#endif
+
static inline void setPinOutput_writeLow(pin_t pin) {
ATOMIC_BLOCK_FORCEON {
setPinOutput(pin);
@@ -43,11 +90,19 @@ static inline void setPinInputHigh_atomic(pin_t pin) {
ATOMIC_BLOCK_FORCEON { setPinInputHigh(pin); }
}
+static inline uint8_t readMatrixPin(pin_t pin) {
+ if (pin != NO_PIN) {
+ return readPin(pin);
+ } else {
+ return 1;
+ }
+}
+
// matrix code
#ifdef DIRECT_PINS
-static void init_pins(void) {
+__attribute__((weak)) void matrix_init_pins(void) {
for (int row = 0; row < MATRIX_ROWS; row++) {
for (int col = 0; col < MATRIX_COLS; col++) {
pin_t pin = direct_pins[row][col];
@@ -58,7 +113,7 @@ static void init_pins(void) {
}
}
-static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
+__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
// Start with a clear matrix row
matrix_row_t current_row_value = 0;
@@ -69,46 +124,57 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
}
}
- // If the row has changed, store the row and return the changed flag.
- if (current_matrix[current_row] != current_row_value) {
- current_matrix[current_row] = current_row_value;
- return true;
- }
- return false;
+ // Update the matrix
+ current_matrix[current_row] = current_row_value;
}
#elif defined(DIODE_DIRECTION)
-# if (DIODE_DIRECTION == COL2ROW)
+# if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)
+# if (DIODE_DIRECTION == COL2ROW)
-static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); }
+static bool select_row(uint8_t row) {
+ pin_t pin = row_pins[row];
+ if (pin != NO_PIN) {
+ setPinOutput_writeLow(pin);
+ return true;
+ }
+ return false;
+}
-static void unselect_row(uint8_t row) { setPinInputHigh_atomic(row_pins[row]); }
+static void unselect_row(uint8_t row) {
+ pin_t pin = row_pins[row];
+ if (pin != NO_PIN) {
+ setPinInputHigh_atomic(pin);
+ }
+}
static void unselect_rows(void) {
- for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
- setPinInputHigh_atomic(row_pins[x]);
+ for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
+ unselect_row(x);
}
}
-static void init_pins(void) {
+__attribute__((weak)) void matrix_init_pins(void) {
unselect_rows();
for (uint8_t x = 0; x < MATRIX_COLS; x++) {
- setPinInputHigh_atomic(col_pins[x]);
+ if (col_pins[x] != NO_PIN) {
+ setPinInputHigh_atomic(col_pins[x]);
+ }
}
}
-static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
+__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
// Start with a clear matrix row
matrix_row_t current_row_value = 0;
- // Select row
- select_row(current_row);
+ if (!select_row(current_row)) { // Select row
+ return; // skip NO_PIN row
+ }
matrix_output_select_delay();
// For each col...
for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
- // Select the col pin to read (active low)
- uint8_t pin_state = readPin(col_pins[col_index]);
+ uint8_t pin_state = readMatrixPin(col_pins[col_index]);
// Populate the matrix row with the state of the col pin
current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index);
@@ -116,81 +182,114 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
// Unselect row
unselect_row(current_row);
- matrix_output_unselect_delay(); // wait for all Col signals to go HIGH
+ matrix_output_unselect_delay(current_row, current_row_value != 0); // wait for all Col signals to go HIGH
- // If the row has changed, store the row and return the changed flag.
- if (current_matrix[current_row] != current_row_value) {
- current_matrix[current_row] = current_row_value;
+ // Update the matrix
+ current_matrix[current_row] = current_row_value;
+}
+
+# elif (DIODE_DIRECTION == ROW2COL)
+
+static bool select_col(uint8_t col) {
+ pin_t pin = col_pins[col];
+ if (pin != NO_PIN) {
+ setPinOutput_writeLow(pin);
return true;
}
return false;
}
-# elif (DIODE_DIRECTION == ROW2COL)
-
-static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); }
-
-static void unselect_col(uint8_t col) { setPinInputHigh_atomic(col_pins[col]); }
+static void unselect_col(uint8_t col) {
+ pin_t pin = col_pins[col];
+ if (pin != NO_PIN) {
+ setPinInputHigh_atomic(pin);
+ }
+}
static void unselect_cols(void) {
for (uint8_t x = 0; x < MATRIX_COLS; x++) {
- setPinInputHigh_atomic(col_pins[x]);
+ unselect_col(x);
}
}
-static void init_pins(void) {
+__attribute__((weak)) void matrix_init_pins(void) {
unselect_cols();
- for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
- setPinInputHigh_atomic(row_pins[x]);
+ for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
+ if (row_pins[x] != NO_PIN) {
+ setPinInputHigh_atomic(row_pins[x]);
+ }
}
}
-static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
- bool matrix_changed = false;
+__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
+ bool key_pressed = false;
// Select col
- select_col(current_col);
+ if (!select_col(current_col)) { // select col
+ return; // skip NO_PIN col
+ }
matrix_output_select_delay();
// For each row...
- for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) {
- // Store last value of row prior to reading
- matrix_row_t last_row_value = current_matrix[row_index];
- matrix_row_t current_row_value = last_row_value;
-
+ for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) {
// Check row pin state
- if (readPin(row_pins[row_index]) == 0) {
+ if (readMatrixPin(row_pins[row_index]) == 0) {
// Pin LO, set col bit
- current_row_value |= (MATRIX_ROW_SHIFTER << current_col);
+ current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col);
+ key_pressed = true;
} else {
// Pin HI, clear col bit
- current_row_value &= ~(MATRIX_ROW_SHIFTER << current_col);
- }
-
- // Determine if the matrix changed state
- if ((last_row_value != current_row_value)) {
- matrix_changed |= true;
- current_matrix[row_index] = current_row_value;
+ current_matrix[row_index] &= ~(MATRIX_ROW_SHIFTER << current_col);
}
}
// Unselect col
unselect_col(current_col);
- matrix_output_unselect_delay(); // wait for all Row signals to go HIGH
-
- return matrix_changed;
+ matrix_output_unselect_delay(current_col, key_pressed); // wait for all Row signals to go HIGH
}
-# else
-# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL!
-# endif
+# else
+# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL!
+# endif
+# endif // defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)
#else
# error DIODE_DIRECTION is not defined!
#endif
void matrix_init(void) {
+#ifdef SPLIT_KEYBOARD
+ split_pre_init();
+
+ // Set pinout for right half if pinout for that half is defined
+ if (!isLeftHand) {
+# ifdef DIRECT_PINS_RIGHT
+ const pin_t direct_pins_right[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS_RIGHT;
+ for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
+ for (uint8_t j = 0; j < MATRIX_COLS; j++) {
+ direct_pins[i][j] = direct_pins_right[i][j];
+ }
+ }
+# endif
+# ifdef MATRIX_ROW_PINS_RIGHT
+ const pin_t row_pins_right[MATRIX_ROWS] = MATRIX_ROW_PINS_RIGHT;
+ for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
+ row_pins[i] = row_pins_right[i];
+ }
+# endif
+# ifdef MATRIX_COL_PINS_RIGHT
+ const pin_t col_pins_right[MATRIX_COLS] = MATRIX_COL_PINS_RIGHT;
+ for (uint8_t i = 0; i < MATRIX_COLS; i++) {
+ col_pins[i] = col_pins_right[i];
+ }
+# endif
+ }
+
+ thisHand = isLeftHand ? 0 : (ROWS_PER_HAND);
+ thatHand = ROWS_PER_HAND - thisHand;
+#endif
+
// initialize key pins
- init_pins();
+ matrix_init_pins();
// initialize matrix state: all keys off
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
@@ -198,28 +297,80 @@ void matrix_init(void) {
matrix[i] = 0;
}
- debounce_init(MATRIX_ROWS);
+ debounce_init(ROWS_PER_HAND);
matrix_init_quantum();
+
+#ifdef SPLIT_KEYBOARD
+ split_post_init();
+#endif
}
-uint8_t matrix_scan(void) {
+#ifdef SPLIT_KEYBOARD
+bool matrix_post_scan(void) {
bool changed = false;
+ if (is_keyboard_master()) {
+ static uint8_t error_count;
+
+ matrix_row_t slave_matrix[ROWS_PER_HAND] = {0};
+ if (!transport_master(matrix + thisHand, slave_matrix)) {
+ error_count++;
+
+ if (error_count > ERROR_DISCONNECT_COUNT) {
+ // reset other half if disconnected
+ for (int i = 0; i < ROWS_PER_HAND; ++i) {
+ matrix[thatHand + i] = 0;
+ slave_matrix[i] = 0;
+ }
+
+ changed = true;
+ }
+ } else {
+ error_count = 0;
+
+ for (int i = 0; i < ROWS_PER_HAND; ++i) {
+ if (matrix[thatHand + i] != slave_matrix[i]) {
+ matrix[thatHand + i] = slave_matrix[i];
+ changed = true;
+ }
+ }
+ }
+
+ matrix_scan_quantum();
+ } else {
+ transport_slave(matrix + thatHand, matrix + thisHand);
+
+ matrix_slave_scan_kb();
+ }
+
+ return changed;
+}
+#endif
+
+uint8_t matrix_scan(void) {
+ matrix_row_t curr_matrix[MATRIX_ROWS] = {0};
#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)
// Set row, read cols
- for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
- changed |= read_cols_on_row(raw_matrix, current_row);
+ for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
+ matrix_read_cols_on_row(curr_matrix, current_row);
}
#elif (DIODE_DIRECTION == ROW2COL)
// Set col, read rows
for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
- changed |= read_rows_on_col(raw_matrix, current_col);
+ matrix_read_rows_on_col(curr_matrix, current_col);
}
#endif
- debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
+ bool changed = memcmp(raw_matrix, curr_matrix, sizeof(curr_matrix)) != 0;
+ if (changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix));
+#ifdef SPLIT_KEYBOARD
+ debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed);
+ changed = (changed || matrix_post_scan());
+#else
+ debounce(raw_matrix, matrix, ROWS_PER_HAND, changed);
matrix_scan_quantum();
+#endif
return (uint8_t)changed;
}
diff --git a/quantum/matrix.h b/quantum/matrix.h
index 3fe691aaee..5c696622fc 100644
--- a/quantum/matrix.h
+++ b/quantum/matrix.h
@@ -56,7 +56,7 @@ matrix_row_t matrix_get_row(uint8_t row);
void matrix_print(void);
/* delay between changing matrix pin state and reading values */
void matrix_output_select_delay(void);
-void matrix_output_unselect_delay(void);
+void matrix_output_unselect_delay(uint8_t line, bool key_pressed);
/* only for backwards compatibility. delay between changing matrix pin state and reading values */
void matrix_io_delay(void);
diff --git a/quantum/matrix_common.c b/quantum/matrix_common.c
index efbad6a5fd..66c89970b1 100644
--- a/quantum/matrix_common.c
+++ b/quantum/matrix_common.c
@@ -88,7 +88,7 @@ uint8_t matrix_key_count(void) {
__attribute__((weak)) void matrix_io_delay(void) { wait_us(MATRIX_IO_DELAY); }
__attribute__((weak)) void matrix_output_select_delay(void) { waitInputPinDelay(); }
-__attribute__((weak)) void matrix_output_unselect_delay(void) { matrix_io_delay(); }
+__attribute__((weak)) void matrix_output_unselect_delay(uint8_t line, bool key_pressed) { matrix_io_delay(); }
// CUSTOM MATRIX 'LITE'
__attribute__((weak)) void matrix_init_custom(void) {}
diff --git a/quantum/mcu_selection.mk b/quantum/mcu_selection.mk
index 9268c4522e..ca0accd719 100644
--- a/quantum/mcu_selection.mk
+++ b/quantum/mcu_selection.mk
@@ -136,10 +136,6 @@ ifneq ($(findstring STM32F042, $(MCU)),)
USE_FPU ?= no
- # Options to pass to dfu-util when flashing
- DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
- DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
-
# UF2 settings
UF2_FAMILY ?= STM32F0
endif
@@ -172,10 +168,6 @@ ifneq ($(findstring STM32F072, $(MCU)),)
USE_FPU ?= no
- # Options to pass to dfu-util when flashing
- DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
- DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
-
# UF2 settings
UF2_FAMILY ?= STM32F0
endif
@@ -208,10 +200,6 @@ ifneq ($(findstring STM32F103, $(MCU)),)
USE_FPU ?= no
- # Options to pass to dfu-util when flashing
- DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
- DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
-
# UF2 settings
UF2_FAMILY ?= STM32F1
endif
@@ -244,10 +232,6 @@ ifneq ($(findstring STM32F303, $(MCU)),)
USE_FPU ?= yes
- # Options to pass to dfu-util when flashing
- DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
- DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
-
# UF2 settings
UF2_FAMILY ?= STM32F3
endif
@@ -280,10 +264,6 @@ ifneq ($(findstring STM32F401, $(MCU)),)
USE_FPU ?= yes
- # Options to pass to dfu-util when flashing
- DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
- DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
-
# UF2 settings
UF2_FAMILY ?= STM32F4
endif
@@ -321,10 +301,6 @@ ifneq ($(findstring STM32F411, $(MCU)),)
USE_FPU ?= yes
- # Options to pass to dfu-util when flashing
- DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
- DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
-
# UF2 settings
UF2_FAMILY ?= STM32F4
endif
@@ -357,10 +333,6 @@ ifneq ($(findstring STM32F446, $(MCU)),)
BOARD ?= GENERIC_STM32_F446XE
USE_FPU ?= yes
-
- # Options to pass to dfu-util when flashing
- DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
- DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
endif
ifneq ($(findstring STM32G431, $(MCU)),)
@@ -391,10 +363,6 @@ ifneq ($(findstring STM32G431, $(MCU)),)
USE_FPU ?= yes
- # Options to pass to dfu-util when flashing
- DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
- DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
-
# UF2 settings
UF2_FAMILY ?= STM32G4
endif
@@ -427,10 +395,6 @@ ifneq ($(findstring STM32G474, $(MCU)),)
USE_FPU ?= yes
- # Options to pass to dfu-util when flashing
- DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
- DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
-
# UF2 settings
UF2_FAMILY ?= STM32G4
endif
@@ -465,9 +429,39 @@ ifneq (,$(filter $(MCU),STM32L433 STM32L443))
USE_FPU ?= yes
- # Options to pass to dfu-util when flashing
- DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
- DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
+ # UF2 settings
+ UF2_FAMILY ?= STM32L4
+endif
+
+ifneq (,$(filter $(MCU),STM32L412 STM32L422))
+ # Cortex version
+ MCU = cortex-m4
+
+ # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
+ ARMV = 7
+
+ ## chip/board settings
+ # - the next two should match the directories in
+ # <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
+ MCU_FAMILY = STM32
+ MCU_SERIES = STM32L4xx
+
+ # Linker script to use
+ # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
+ # or <keyboard_dir>/ld/
+ MCU_LDSCRIPT ?= STM32L412xB
+
+ # Startup code to use
+ # - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
+ MCU_STARTUP ?= stm32l4xx
+
+ # Board: it should exist either in <chibios>/os/hal/boards/,
+ # <keyboard_dir>/boards/, or drivers/boards/
+ BOARD ?= GENERIC_STM32_L412XB
+
+ PLATFORM_NAME ?= platform_l432
+
+ USE_FPU ?= yes
# UF2 settings
UF2_FAMILY ?= STM32L4
diff --git a/quantum/mousekey.c b/quantum/mousekey.c
index 99bfd6b96f..c2291fb397 100644
--- a/quantum/mousekey.c
+++ b/quantum/mousekey.c
@@ -486,3 +486,5 @@ static void mousekey_debug(void) {
print_dec(mousekey_accel);
print(")\n");
}
+
+report_mouse_t mousekey_get_report(void) { return mouse_report; }
diff --git a/quantum/mousekey.h b/quantum/mousekey.h
index 70dc4bb5c5..56c91b5f1b 100644
--- a/quantum/mousekey.h
+++ b/quantum/mousekey.h
@@ -168,11 +168,12 @@ extern uint8_t mk_time_to_max;
extern uint8_t mk_wheel_max_speed;
extern uint8_t mk_wheel_time_to_max;
-void mousekey_task(void);
-void mousekey_on(uint8_t code);
-void mousekey_off(uint8_t code);
-void mousekey_clear(void);
-void mousekey_send(void);
+void mousekey_task(void);
+void mousekey_on(uint8_t code);
+void mousekey_off(uint8_t code);
+void mousekey_clear(void);
+void mousekey_send(void);
+report_mouse_t mousekey_get_report(void);
#ifdef __cplusplus
}
diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
index f38d7d47a0..9e16292486 100644
--- a/quantum/process_keycode/process_combo.c
+++ b/quantum/process_keycode/process_combo.c
@@ -184,7 +184,7 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
return !is_combo_key;
}
-void matrix_scan_combo(void) {
+void combo_task(void) {
if (b_combo_enable && is_active && 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
diff --git a/quantum/process_keycode/process_combo.h b/quantum/process_keycode/process_combo.h
index e51a2f1f4e..9af97588b2 100644
--- a/quantum/process_keycode/process_combo.h
+++ b/quantum/process_keycode/process_combo.h
@@ -54,7 +54,7 @@ typedef struct {
#endif
bool process_combo(uint16_t keycode, keyrecord_t *record);
-void matrix_scan_combo(void);
+void combo_task(void);
void process_combo_event(uint16_t combo_index, bool pressed);
void combo_enable(void);
diff --git a/quantum/process_keycode/process_haptic.c b/quantum/process_keycode/process_haptic.c
new file mode 100644
index 0000000000..29a4ffd10a
--- /dev/null
+++ b/quantum/process_keycode/process_haptic.c
@@ -0,0 +1,147 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "haptic.h"
+#include "process_haptic.h"
+#include "quantum_keycodes.h"
+
+__attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t *record) {
+ switch (keycode) {
+#ifdef NO_HAPTIC_MOD
+ case QK_MOD_TAP ... QK_MOD_TAP_MAX:
+ if (record->tap.count == 0) return false;
+ break;
+ case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
+ if (record->tap.count != TAPPING_TOGGLE) return false;
+ break;
+ case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
+ if (record->tap.count == 0) return false;
+ break;
+ case KC_LCTRL ... KC_RGUI:
+ case QK_MOMENTARY ... QK_MOMENTARY_MAX:
+#endif
+#ifdef NO_HAPTIC_FN
+ case KC_FN0 ... KC_FN31:
+#endif
+#ifdef NO_HAPTIC_ALPHA
+ case KC_A ... KC_Z:
+#endif
+#ifdef NO_HAPTIC_PUNCTUATION
+ case KC_ENTER:
+ case KC_ESCAPE:
+ case KC_BSPACE:
+ case KC_SPACE:
+ case KC_MINUS:
+ case KC_EQUAL:
+ case KC_LBRACKET:
+ case KC_RBRACKET:
+ case KC_BSLASH:
+ case KC_NONUS_HASH:
+ case KC_SCOLON:
+ case KC_QUOTE:
+ case KC_GRAVE:
+ case KC_COMMA:
+ case KC_SLASH:
+ case KC_DOT:
+ case KC_NONUS_BSLASH:
+#endif
+#ifdef NO_HAPTIC_LOCKKEYS
+ case KC_CAPSLOCK:
+ case KC_SCROLLLOCK:
+ case KC_NUMLOCK:
+#endif
+#ifdef NO_HAPTIC_NAV
+ case KC_PSCREEN:
+ case KC_PAUSE:
+ case KC_INSERT:
+ case KC_DELETE:
+ case KC_PGDOWN:
+ case KC_PGUP:
+ case KC_LEFT:
+ case KC_UP:
+ case KC_RIGHT:
+ case KC_DOWN:
+ case KC_END:
+ case KC_HOME:
+#endif
+#ifdef NO_HAPTIC_NUMERIC
+ case KC_1 ... KC_0:
+#endif
+ return false;
+ }
+ return true;
+}
+
+bool process_haptic(uint16_t keycode, keyrecord_t *record) {
+ if (record->event.pressed) {
+ switch (keycode) {
+ case HPT_ON:
+ haptic_enable();
+ break;
+ case HPT_OFF:
+ haptic_disable();
+ break;
+ case HPT_TOG:
+ haptic_toggle();
+ break;
+ case HPT_RST:
+ haptic_reset();
+ break;
+ case HPT_FBK:
+ haptic_feedback_toggle();
+ break;
+ case HPT_BUZ:
+ haptic_buzz_toggle();
+ break;
+ case HPT_MODI:
+ haptic_mode_increase();
+ break;
+ case HPT_MODD:
+ haptic_mode_decrease();
+ break;
+ case HPT_DWLI:
+ haptic_dwell_increase();
+ break;
+ case HPT_DWLD:
+ haptic_dwell_decrease();
+ break;
+ case HPT_CONT:
+ haptic_toggle_continuous();
+ break;
+ case HPT_CONI:
+ haptic_cont_increase();
+ break;
+ case HPT_COND:
+ haptic_cont_decrease();
+ break;
+ }
+ }
+
+ if (haptic_get_enable()) {
+ if (record->event.pressed) {
+ // keypress
+ if (haptic_get_feedback() < 2 && get_haptic_enabled_key(keycode, record)) {
+ haptic_play();
+ }
+ } else {
+ // keyrelease
+ if (haptic_get_feedback() > 0 && get_haptic_enabled_key(keycode, record)) {
+ haptic_play();
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/quantum/process_keycode/process_haptic.h b/quantum/process_keycode/process_haptic.h
new file mode 100644
index 0000000000..6dbb0f014d
--- /dev/null
+++ b/quantum/process_keycode/process_haptic.h
@@ -0,0 +1,21 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <stdbool.h>
+#include "action.h"
+
+bool process_haptic(uint16_t keycode, keyrecord_t *record);
diff --git a/quantum/process_keycode/process_key_override.c b/quantum/process_keycode/process_key_override.c
new file mode 100644
index 0000000000..8b45a94043
--- /dev/null
+++ b/quantum/process_keycode/process_key_override.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright 2021 Jonas Gessner
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "quantum.h"
+#include "report.h"
+#include "timer.h"
+#include "process_key_override_private.h"
+
+#include <debug.h>
+
+#ifndef KEY_OVERRIDE_REPEAT_DELAY
+# define KEY_OVERRIDE_REPEAT_DELAY 500
+#endif
+
+// For benchmarking the time it takes to call process_key_override on every key press (needs keyboard debugging enabled as well)
+// #define BENCH_KEY_OVERRIDE
+
+// For debug output (needs keyboard debugging enabled as well)
+// #define DEBUG_KEY_OVERRIDE
+
+#ifdef DEBUG_KEY_OVERRIDE
+# define key_override_printf dprintf
+#else
+# define key_override_printf(str, ...) \
+ {}
+#endif
+
+// Helpers
+
+// Private functions implemented elsewhere in qmk/tmk
+extern uint8_t extract_mod_bits(uint16_t code);
+extern void set_weak_override_mods(uint8_t mods);
+extern void clear_weak_override_mods(void);
+extern void set_suppressed_override_mods(uint8_t mods);
+extern void clear_suppressed_override_mods(void);
+
+static uint16_t clear_mods_from(uint16_t keycode) {
+ switch (keycode) {
+ case QK_MODS ... QK_MODS_MAX:
+ break;
+ default:
+ return keycode;
+ }
+
+ static const uint16_t all_mods = QK_LCTL | QK_LSFT | QK_LALT | QK_LGUI | QK_RCTL | QK_RSFT | QK_RALT | QK_RGUI;
+
+ return (keycode & ~(all_mods));
+}
+
+// Internal variables
+static const key_override_t *active_override = NULL;
+static bool active_override_trigger_is_down = false;
+
+// Used to keep track of what non-modifier key was last pressed down. We never want to activate an override for a trigger key that is not the last non-mod key that was pressed down. OSes internally completely unregister a key that is held when a different key is held down after. We want to respect this here.
+static uint16_t last_key_down = 0;
+// When was the last key pressed down?
+static uint32_t last_key_down_time = 0;
+
+// What timestamp are we comparing to when waiting to register a deferred key?
+static uint32_t defer_reference_time = 0;
+// What delay should pass until deferred key is registered?
+static uint32_t defer_delay = 0;
+
+// Holds the keycode that should be registered at a later time, in order to not get false key presses
+static uint16_t deferred_register = 0;
+
+// TODO: in future maybe save in EEPROM?
+static bool enabled = true;
+
+// Public variables
+__attribute__((weak)) const key_override_t **key_overrides = NULL;
+
+// Forward decls
+static const key_override_t *clear_active_override(const bool allow_reregister);
+
+void key_override_on(void) {
+ enabled = true;
+ key_override_printf("Key override ON\n");
+}
+
+void key_override_off(void) {
+ enabled = false;
+ clear_active_override(false);
+ key_override_printf("Key override OFF\n");
+}
+
+void key_override_toggle(void) {
+ if (key_override_is_enabled()) {
+ key_override_off();
+ } else {
+ key_override_on();
+ }
+}
+
+bool key_override_is_enabled(void) { return enabled; }
+
+// Returns whether the modifiers that are pressed are such that the override should activate
+static bool key_override_matches_active_modifiers(const key_override_t *override, const uint8_t mods) {
+ // Check that negative keys pass
+ if ((override->negative_mod_mask & mods) != 0) {
+ return false;
+ }
+
+ // Immediately return true if the override requires no mods down
+ if (override->trigger_mods == 0) {
+ return true;
+ }
+
+ if ((override->options & ko_option_one_mod) != 0) {
+ // At least one of the trigger modifiers must be down
+ return (override->trigger_mods & mods) != 0;
+ } else {
+ // All trigger modifiers must be down, but each mod can be active on either side (if both sides are specified).
+
+ // Which mods, regardless of side, are required?
+ uint8_t one_sided_required_mods = (override->trigger_mods & 0b1111) | (override->trigger_mods >> 4);
+
+ // Which of the required modifiers are active?
+ uint8_t active_required_mods = override->trigger_mods & mods;
+
+ // Move the active requird mods to one side
+ uint8_t one_sided_active_required_mods = (active_required_mods & 0b1111) | (active_required_mods >> 4);
+
+ // Check that there is a full match between the required one-sided mods and active required one sided mods
+ return one_sided_active_required_mods == one_sided_required_mods;
+ }
+
+ return false;
+}
+
+static void schedule_deferred_register(const uint16_t keycode) {
+ if (timer_elapsed32(last_key_down_time) < KEY_OVERRIDE_REPEAT_DELAY) {
+ // Defer until KEY_OVERRIDE_REPEAT_DELAY has passed since the trigger key was pressed down. This emulates the behavior as holding down a key x, then holding down shift shortly after. Usually the shifted key X is not immediately produced, but rather a 'key repeat delay' passes before any repeated character is output.
+ defer_reference_time = last_key_down_time;
+ defer_delay = KEY_OVERRIDE_REPEAT_DELAY;
+ } else {
+ // Wait a very short time when a modifier event triggers the override to avoid false activations when e.g. a modifier is pressed just before a key is released (with the intention of pairing the modifier with a different key), or a modifier is lifted shortly before the trigger key is lifted. Operating systems by default reject modifier-events that happen very close to a non-modifier event.
+ defer_reference_time = timer_read32();
+ defer_delay = 50; // 50ms
+ }
+ deferred_register = keycode;
+}
+
+const key_override_t *clear_active_override(const bool allow_reregister) {
+ if (active_override == NULL) {
+ return NULL;
+ }
+
+ key_override_printf("Deactivating override\n");
+
+ deferred_register = 0;
+
+ // Clear the suppressed mods
+ clear_suppressed_override_mods();
+
+ // Unregister the replacement. First remove the weak override mods
+ clear_weak_override_mods();
+
+ const key_override_t *const old = active_override;
+
+ const uint8_t mod_free_replacement = clear_mods_from(active_override->replacement);
+
+ bool unregister_replacement = mod_free_replacement != KC_NO && // KC_NO is never registered
+ mod_free_replacement < SAFE_RANGE; // Custom keycodes are never registered
+
+ // Try firing the custom handler
+ if (active_override->custom_action != NULL) {
+ unregister_replacement &= active_override->custom_action(false, active_override->context);
+ }
+
+ // Then unregister the mod-free replacement key if desired
+ if (unregister_replacement) {
+ if (IS_KEY(mod_free_replacement)) {
+ del_key(mod_free_replacement);
+ } else {
+ key_override_printf("NOT KEY 1\n");
+ send_keyboard_report();
+ unregister_code(mod_free_replacement);
+ }
+ }
+
+ const uint16_t trigger = active_override->trigger;
+
+ const bool reregister_trigger = allow_reregister && // Check if allowed from caller
+ (active_override->options & ko_option_no_reregister_trigger) == 0 && // Check if override allows
+ active_override_trigger_is_down && // Check if trigger is even down
+ trigger != KC_NO && // KC_NO is never registered
+ trigger < SAFE_RANGE; // A custom keycode should not be registered
+
+ // Optionally re-register the trigger if it is still down
+ if (reregister_trigger) {
+ key_override_printf("Re-registering trigger deferred: %u\n", trigger);
+
+ // This will always be a modifier event, so defer always
+ schedule_deferred_register(trigger);
+ }
+
+ send_keyboard_report();
+
+ active_override = NULL;
+ active_override_trigger_is_down = false;
+
+ return old;
+}
+
+/** Checks if the key event is an allowed activation event for the provided override. Does not check things like whether the correct mods or correct trigger key is down. */
+static bool check_activation_event(const key_override_t *override, const bool key_down, const bool is_mod) {
+ ko_option_t options = override->options;
+
+ if ((options & ko_options_all_activations) == 0) {
+ // No activation option provided at all. This is wrong, but let's assume the default activations (ko_options_all_activations) were meant...
+ options = ko_options_all_activations;
+ }
+
+ if (is_mod) {
+ if (key_down) {
+ return (options & ko_option_activation_required_mod_down) != 0;
+ } else {
+ return (options & ko_option_activation_negative_mod_up) != 0;
+ }
+ } else {
+ if (key_down) {
+ return (options & ko_option_activation_trigger_down) != 0;
+ } else {
+ return false;
+ }
+ }
+}
+
+/** Iterates through the list of key overrides and tries activating each, until it finds one that activates or reaches the end of overrides. Returns true if the key action for `keycode` should be sent */
+static bool try_activating_override(const uint16_t keycode, const uint8_t layer, const bool key_down, const bool is_mod, const uint8_t active_mods, bool *activated) {
+ if (key_overrides == NULL) {
+ return true;
+ }
+
+ for (uint8_t i = 0;; i++) {
+ const key_override_t *const override = key_overrides[i];
+
+ // End of array
+ if (override == NULL) {
+ break;
+ }
+
+ // Fast, but not full mods check. Most key presses will not have any mods down, and most overrides will require mods. Hence here we filter overrides that require mods to be down while no mods are down
+ if (active_mods == 0 && override->trigger_mods != 0) {
+ key_override_printf("Not activating override: Modifiers don't match\n");
+ continue;
+ }
+
+ // Check layer
+ if ((override->layers & (1 << layer)) == 0) {
+ key_override_printf("Not activating override: Not set to activate on pressed layer\n");
+ continue;
+ }
+
+ // Check allowed activation events
+ if (!check_activation_event(override, key_down, is_mod)) {
+ key_override_printf("Not activating override: Activation event not allowed\n");
+ continue;
+ }
+
+ const bool is_trigger = override->trigger == keycode;
+
+ // Check if trigger lifted. This is a small optimization in order to skip the remaining checks
+ if (is_trigger && !key_down) {
+ key_override_printf("Not activating override: Trigger lifted\n");
+ continue;
+ }
+
+ // If the trigger is KC_NO it means 'no key', so only the required modifiers need to be down.
+ const bool no_trigger = override->trigger == KC_NO;
+
+ // Check if aleady active
+ if (override == active_override) {
+ key_override_printf("Not activating override: Alerady actived\n");
+ continue;
+ }
+
+ // Check if enabled
+ if (override->enabled != NULL && !((*(override->enabled) & 1))) {
+ key_override_printf("Not activating override: Not enabled\n");
+ continue;
+ }
+
+ // Check mods precisely
+ if (!key_override_matches_active_modifiers(override, active_mods)) {
+ key_override_printf("Not activating override: Modifiers don't match\n");
+ continue;
+ }
+
+ // Check if trigger key is down.
+ const bool trigger_down = is_trigger && key_down;
+
+ // At this point, all requirements for activation are checked, except whether the trigger key is pressed. Now we check if the required trigger is down
+ // If no trigger key is required, yes.
+ // If the trigger was just pressed, yes.
+ // If the last non-mod key that was pressed down is the trigger key, yes.
+ bool should_activate = no_trigger || trigger_down || last_key_down == override->trigger;
+
+ if (!should_activate) {
+ key_override_printf("Not activating override. Trigger not down\n");
+ continue;
+ }
+
+ key_override_printf("Activating override\n");
+
+ clear_active_override(false);
+
+ active_override = override;
+ active_override_trigger_is_down = true;
+
+ set_suppressed_override_mods(override->suppressed_mods);
+
+ if (!trigger_down && !no_trigger) {
+ // When activating a key override the trigger is is always unregistered. In the case where the key that newly pressed is not the trigger key, we have to explicitly remove the trigger key from the keyboard report. If the trigger was just pressed down we simply suppress the event which also has the effect of the trigger key not being registered in the keyboard report.
+ if (IS_KEY(override->trigger)) {
+ del_key(override->trigger);
+ } else {
+ unregister_code(override->trigger);
+ }
+ }
+
+ const uint16_t mod_free_replacement = clear_mods_from(override->replacement);
+
+ bool register_replacement = mod_free_replacement != KC_NO && // KC_NO is never registered
+ mod_free_replacement < SAFE_RANGE; // Custom keycodes are never registered
+
+ // Try firing the custom handler
+ if (override->custom_action != NULL) {
+ register_replacement &= override->custom_action(true, override->context);
+ }
+
+ if (register_replacement) {
+ const uint8_t override_mods = extract_mod_bits(override->replacement);
+ set_weak_override_mods(override_mods);
+
+ // If this is a modifier event that activates the key override we _always_ defer the actual full activation of the override
+ if (is_mod) {
+ key_override_printf("Deferring register replacement key\n");
+ schedule_deferred_register(mod_free_replacement);
+ send_keyboard_report();
+ } else {
+ if (IS_KEY(mod_free_replacement)) {
+ add_key(mod_free_replacement);
+ } else {
+ key_override_printf("NOT KEY 2\n");
+ send_keyboard_report();
+ // On macOS there seems to be a race condition when it comes to the keyboard report and consumer keycodes. It seems the OS may recognize a consumer keycode before an updated keyboard report, even if the keyboard report is actually sent before the consumer key. I assume it is some sort of race condition because it happens infrequently and very irregularly. Waiting for about at least 10ms between sending the keyboard report and sending the consumer code has shown to fix this.
+ wait_ms(10);
+ register_code(mod_free_replacement);
+ }
+ }
+ } else {
+ // If not registering the replacement key send keyboard report to update the unregistered keys.
+ send_keyboard_report();
+ }
+
+ *activated = true;
+
+ // If the trigger is down, suppress the event so that it does not get added to the keyboard report.
+ return !trigger_down;
+ }
+
+ *activated = false;
+
+ return true;
+}
+
+void key_override_task(void) {
+ if (deferred_register == 0) {
+ return;
+ }
+
+ if (timer_elapsed32(defer_reference_time) >= defer_delay) {
+ key_override_printf("Registering deferred key\n");
+ register_code16(deferred_register);
+ deferred_register = 0;
+ defer_reference_time = 0;
+ defer_delay = 0;
+ }
+}
+
+bool process_key_override(const uint16_t keycode, const keyrecord_t *const record) {
+#ifdef BENCH_KEY_OVERRIDE
+ uint16_t start = timer_read();
+#endif
+
+ const bool key_down = record->event.pressed;
+ const bool is_mod = IS_MOD(keycode);
+
+ if (key_down) {
+ switch (keycode) {
+ case KEY_OVERRIDE_TOGGLE:
+ key_override_toggle();
+ return false;
+
+ case KEY_OVERRIDE_ON:
+ key_override_on();
+ return false;
+
+ case KEY_OVERRIDE_OFF:
+ key_override_off();
+ return false;
+
+ default:
+ break;
+ }
+ }
+
+ if (!enabled) {
+ return true;
+ }
+
+ uint8_t effective_mods = get_mods();
+
+#ifdef KEY_OVERRIDE_INCLUDE_WEAK_MODS
+ effective_mods |= get_weak_mods();
+#endif
+
+#ifndef NO_ACTION_ONESHOT
+ // Locked one shot mods are added to get_mods(), I think (why??) while oneshot mods are in get_oneshot_mods(). Still OR with get_locked_oneshot_mods because that's where those mods _should_ be saved.
+ effective_mods |= get_oneshot_locked_mods() | get_oneshot_mods();
+#endif
+
+ if (is_mod) {
+ // The mods returned from get_mods() will be updated with this new event _after_ this code runs. Hence we manually update the effective mods here to really know the effective mods.
+ if (key_down) {
+ effective_mods |= MOD_BIT(keycode);
+ } else {
+ effective_mods &= ~MOD_BIT(keycode);
+ }
+ } else {
+ if (key_down) {
+ last_key_down = keycode;
+ last_key_down_time = timer_read32();
+ deferred_register = 0;
+ }
+
+ // The last key that was pressed was just released. No more keys are therefore sending input
+ if (!key_down && keycode == last_key_down) {
+ last_key_down = 0;
+ last_key_down_time = 0;
+ // We also cancel any deferred registers because, again, no keys are sending any input. Only the last key that is pressed creates an input – this key was just lifted.
+ deferred_register = 0;
+ }
+ }
+
+ key_override_printf("key down: %u keycode: %u is mod: %u effective mods: %u\n", key_down, keycode, is_mod, effective_mods);
+
+ bool send_key_action = true;
+ bool activated = false;
+
+ // Non-mod key up events never activate a key override
+ if (is_mod || key_down) {
+ // Get the exact layer that was hit. It will be cached at this point
+ const uint8_t layer = read_source_layers_cache(record->event.key);
+
+ // Use blocked to ensure the same override is not activated again immediately after it is deactivated
+ send_key_action = try_activating_override(keycode, layer, key_down, is_mod, effective_mods, &activated);
+
+ if (!send_key_action) {
+ send_keyboard_report();
+ }
+ }
+
+ if (!activated && active_override != NULL) {
+ if (is_mod) {
+ // Check if necessary modifier of current override goes up or a negative mod goes down
+ if (!key_override_matches_active_modifiers(active_override, effective_mods)) {
+ key_override_printf("Deactivating override because necessary modifier lifted or negative mod pressed\n");
+ clear_active_override(true);
+ }
+ } else {
+ // Check if trigger of current override goes up or if override does not allow additional keys to be down and another key goes down
+ const bool is_trigger = keycode == active_override->trigger;
+ bool should_deactivate = false;
+
+ // Check if trigger key lifted
+ if (is_trigger && !key_down) {
+ should_deactivate = true;
+ active_override_trigger_is_down = false;
+ key_override_printf("Deactivating override because trigger key up\n");
+ }
+
+ // Check if another key was pressed
+ if (key_down && (active_override->options & ko_option_no_unregister_on_other_key_down) == 0) {
+ should_deactivate = true;
+ key_override_printf("Deactivating override because another key was pressed\n");
+ }
+
+ if (should_deactivate) {
+ clear_active_override(false);
+ }
+ }
+ }
+
+#ifdef BENCH_KEY_OVERRIDE
+ uint16_t elapsed = timer_elapsed(start);
+
+ dprintf("Processing key overrides took: %u ms\n", elapsed);
+#endif
+
+ return send_key_action;
+}
diff --git a/quantum/process_keycode/process_key_override.h b/quantum/process_keycode/process_key_override.h
new file mode 100644
index 0000000000..fd76f297a8
--- /dev/null
+++ b/quantum/process_keycode/process_key_override.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2021 Jonas Gessner
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "action_layer.h"
+
+/**
+ * Key overrides allow you to send a different key-modifier combination or perform a custom action when a certain modifier-key combination is pressed.
+ *
+ * For example, you may configure a key override to send the delete key when shift + backspace are pressed together, or that your volume keys become screen brightness keys when holding ctrl. The possibilities are quite vast and the documentation contains a few examples for inspiration.
+ *
+ * See the documentation and examples here: https://docs.qmk.fm/#/feature_key_overrides
+ */
+
+/** Bitfield with various options controlling the behavior of a key override. */
+typedef enum {
+ /** Allow activating when the trigger key is pressed down. */
+ ko_option_activation_trigger_down = (1 << 0),
+ /** Allow activating when a necessary modifier is pressed down. */
+ ko_option_activation_required_mod_down = (1 << 1),
+ /** Allow activating when a negative modifier is released. */
+ ko_option_activation_negative_mod_up = (1 << 2),
+
+ ko_options_all_activations = ko_option_activation_negative_mod_up | ko_option_activation_required_mod_down | ko_option_activation_trigger_down,
+
+ /** If set, any of the modifiers in trigger_mods will be enough to activate the override (logical OR of modifiers). If not set, all the modifiers in trigger_mods have to be pressed (logical AND of modifiers). */
+ ko_option_one_mod = (1 << 3),
+
+ /** If set, the trigger key will never be registered again after the override is deactivated. */
+ ko_option_no_reregister_trigger = (1 << 4),
+
+ /** If set, the override will not deactivate when another key is pressed down. Use only if you really know you need this. */
+ ko_option_no_unregister_on_other_key_down = (1 << 5),
+
+ /** The default options used by the ko_make_xxx functions. */
+ ko_options_default = ko_options_all_activations,
+} ko_option_t;
+
+/** Defines a single key override */
+typedef struct {
+ // The non-modifier keycode that triggers the override. This keycode, and the necessary modifiers (trigger_mods) must be pressed to activate this override. Set this to the keycode of the key that should activate the override. Set to KC_NO to require only the necessary modifiers to be pressed and no non-modifier.
+ uint16_t trigger;
+
+ // Which mods need to be down for activation. If both sides of a modifier are set (e.g. left ctrl and right ctrl) then only one is required to be pressed (e.g. left ctrl suffices). Use the MOD_MASK_XXX and MOD_BIT() macros for this.
+ uint8_t trigger_mods;
+
+ // This is a BITMASK (!), defining which layers this override applies to. To use this override on layer i set the ith bit (1 << i).
+ layer_state_t layers;
+
+ // Which modifiers cannot be down. It must hold that (active_mods & negative_mod_mask) == 0, otherwise the key override will not be activated. An active override will be deactivated once this is no longer true.
+ uint8_t negative_mod_mask;
+
+ // Modifiers to 'suppress' while the override is active. To suppress a modifier means that even though the modifier key is held down, the host OS sees the modifier as not pressed. Can be used to suppress the trigger modifiers, as a trivial example.
+ uint8_t suppressed_mods;
+
+ // The complex keycode to send as replacement when this override is triggered. This can be a simple keycode, a key-modifier combination (e.g. C(KC_A)), or KC_NO (to register no replacement keycode). Use in combination with suppressed_mods to get the correct modifiers to be sent.
+ uint16_t replacement;
+
+ // Options controlling the behavior of the override, such as what actions are allowed to activate the override.
+ ko_option_t options;
+
+ // If not NULL, this function will be called right before the replacement key is registered, along with the provided context and a flag indicating whether the override was activated or deactivated. This function allows you to run some custom actions for specific key overrides. If you return `false`, the replacement key is not registered/unregistered as it would normally. Return `true` to register and unregister the override normally.
+ bool (*custom_action)(bool activated, void *context);
+
+ // A context that will be passed to the custom action function.
+ void *context;
+
+ // If this points to false this override will not be used. Set to NULL to always have this override enabled.
+ bool *enabled;
+} key_override_t;
+
+/** Define this as a null-terminated array of pointers to key overrides. These key overrides will be used by qmk. */
+extern const key_override_t **key_overrides;
+
+/** Turns key overrides on */
+void key_override_on(void);
+
+/** Turns key overrides off */
+void key_override_off(void);
+
+/** Toggles key overrides on */
+void key_override_toggle(void);
+
+/** Returns whether key overrides are enabled */
+bool key_override_is_enabled(void);
+
+/** Handling of key overrides and its implemented keycodes */
+bool process_key_override(const uint16_t keycode, const keyrecord_t *const record);
+
+/** Perform any deferred keys */
+void key_override_task(void);
+
+/**
+ * Preferrably use these macros to create key overrides. They fix many of the options to a standard setting that should satisfy most basic use-cases. Only directly create a key_override_t struct when you really need to.
+ */
+
+// clang-format off
+
+/**
+ * Convenience initializer to create a basic key override. Activates the override on all layers.
+ */
+#define ko_make_basic(trigger_mods, trigger_key, replacement_key) \
+ ko_make_with_layers(trigger_mods, trigger_key, replacement_key, ~0)
+
+/**
+ * Convenience initializer to create a basic key override. Provide a bitmap (of type layer_state_t) with the bits set for each layer on which the override should activate.
+ */
+#define ko_make_with_layers(trigger_mods, trigger_key, replacement_key, layers) \
+ ko_make_with_layers_and_negmods(trigger_mods, trigger_key, replacement_key, layers, 0)
+
+/**
+ * Convenience initializer to create a basic key override. Provide a bitmap with the bits set for each layer on which the override should activate. Also provide a negative modifier mask, that is used to define which modifiers may not be pressed.
+ */
+#define ko_make_with_layers_and_negmods(trigger_mods, trigger_key, replacement_key, layers, negative_mask) \
+ ko_make_with_layers_negmods_and_options(trigger_mods, trigger_key, replacement_key, layers, negative_mask, ko_options_default)
+
+ /**
+ * Convenience initializer to create a basic key override. Provide a bitmap with the bits set for each layer on which the override should activate. Also provide a negative modifier mask, that is used to define which modifiers may not be pressed. Provide options for additional control of the behavior of the override.
+ */
+#define ko_make_with_layers_negmods_and_options(trigger_mods_, trigger_key, replacement_key, layer_mask, negative_mask, options_) \
+ ((const key_override_t){ \
+ .trigger_mods = (trigger_mods_), \
+ .layers = (layer_mask), \
+ .suppressed_mods = (trigger_mods_), \
+ .options = (options_), \
+ .negative_mod_mask = (negative_mask), \
+ .custom_action = NULL, \
+ .context = NULL, \
+ .trigger = (trigger_key), \
+ .replacement = (replacement_key), \
+ .enabled = NULL \
+ })
+
+// clang-format on
diff --git a/quantum/process_keycode/process_music.c b/quantum/process_keycode/process_music.c
index eb06be96c2..2beccbd8f9 100644
--- a/quantum/process_keycode/process_music.c
+++ b/quantum/process_keycode/process_music.c
@@ -296,7 +296,7 @@ void music_mode_cycle(void) {
# endif
}
-void matrix_scan_music(void) {
+void music_task(void) {
if (music_sequence_playing) {
if ((music_sequence_timer == 0) || (timer_elapsed(music_sequence_timer) > music_sequence_interval)) {
music_sequence_timer = timer_read();
diff --git a/quantum/process_keycode/process_music.h b/quantum/process_keycode/process_music.h
index 01014aa6c2..e275cd9d26 100644
--- a/quantum/process_keycode/process_music.h
+++ b/quantum/process_keycode/process_music.h
@@ -44,7 +44,7 @@ void music_scale_user(void);
void music_all_notes_off(void);
void music_mode_cycle(void);
-void matrix_scan_music(void);
+void music_task(void);
bool music_mask(uint16_t keycode);
bool music_mask_kb(uint16_t keycode);
diff --git a/quantum/process_keycode/process_rgb.c b/quantum/process_keycode/process_rgb.c
index 167c0c03c9..b9fee1ca59 100644
--- a/quantum/process_keycode/process_rgb.c
+++ b/quantum/process_keycode/process_rgb.c
@@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "process_rgb.h"
-#include "rgb.h"
typedef void (*rgb_func_pointer)(void);
diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c
index 17dc540a64..c8712d919f 100644
--- a/quantum/process_keycode/process_tap_dance.c
+++ b/quantum/process_keycode/process_tap_dance.c
@@ -161,7 +161,7 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {
return true;
}
-void matrix_scan_tap_dance() {
+void tap_dance_task() {
if (highest_td == -1) return;
uint16_t tap_user_defined;
diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h
index a013c5cabf..d9ffb1e73d 100644
--- a/quantum/process_keycode/process_tap_dance.h
+++ b/quantum/process_keycode/process_tap_dance.h
@@ -85,7 +85,7 @@ extern qk_tap_dance_action_t tap_dance_actions[];
void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record);
bool process_tap_dance(uint16_t keycode, keyrecord_t *record);
-void matrix_scan_tap_dance(void);
+void tap_dance_task(void);
void reset_tap_dance(qk_tap_dance_state_t *state);
void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data);
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 8ccdb774bd..f35939216c 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -49,21 +49,18 @@ float goodbye_song[][2] = GOODBYE_SONG;
# ifdef DEFAULT_LAYER_SONGS
float default_layer_songs[][16][2] = DEFAULT_LAYER_SONGS;
# endif
-# ifdef SENDSTRING_BELL
-float bell_song[][2] = SONG(TERMINAL_SOUND);
-# endif
#endif
#ifdef AUTO_SHIFT_ENABLE
# include "process_auto_shift.h"
#endif
-static void do_code16(uint16_t code, void (*f)(uint8_t)) {
+uint8_t extract_mod_bits(uint16_t code) {
switch (code) {
case QK_MODS ... QK_MODS_MAX:
break;
default:
- return;
+ return 0;
}
uint8_t mods_to_send = 0;
@@ -80,9 +77,11 @@ static void do_code16(uint16_t code, void (*f)(uint8_t)) {
if (code & QK_LGUI) mods_to_send |= MOD_BIT(KC_LGUI);
}
- f(mods_to_send);
+ return mods_to_send;
}
+static void do_code16(uint16_t code, void (*f)(uint8_t)) { f(extract_mod_bits(code)); }
+
void register_code16(uint16_t code) {
if (IS_MOD(code) || code == KC_NO) {
do_code16(code, register_mods);
@@ -217,10 +216,10 @@ bool process_record_quantum(keyrecord_t *record) {
#endif
#if defined(AUDIO_ENABLE) && defined(AUDIO_CLICKY)
process_clicky(keycode, record) &&
-#endif // AUDIO_CLICKY
+#endif
#ifdef HAPTIC_ENABLE
process_haptic(keycode, record) &&
-#endif // HAPTIC_ENABLE
+#endif
#if defined(VIA_ENABLE)
process_record_via(keycode, record) &&
#endif
@@ -243,6 +242,9 @@ bool process_record_quantum(keyrecord_t *record) {
#if (defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))) && !defined(NO_MUSIC_MODE)
process_music(keycode, record) &&
#endif
+#ifdef KEY_OVERRIDE_ENABLE
+ process_key_override(keycode, record) &&
+#endif
#ifdef TAP_DANCE_ENABLE
process_tap_dance(keycode, record) &&
#endif
@@ -340,13 +342,13 @@ void set_single_persistent_default_layer(uint8_t default_layer) {
#if defined(AUDIO_ENABLE) && defined(DEFAULT_LAYER_SONGS)
PLAY_SONG(default_layer_songs[default_layer]);
#endif
- eeconfig_update_default_layer(1U << default_layer);
- default_layer_set(1U << default_layer);
+ eeconfig_update_default_layer((layer_state_t)1 << default_layer);
+ default_layer_set((layer_state_t)1 << default_layer);
}
layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3) {
- layer_state_t mask12 = (1UL << layer1) | (1UL << layer2);
- layer_state_t mask3 = 1UL << layer3;
+ layer_state_t mask12 = ((layer_state_t)1 << layer1) | ((layer_state_t)1 << layer2);
+ layer_state_t mask3 = (layer_state_t)1 << layer3;
return (state & mask12) == mask12 ? (state | mask3) : (state & ~mask3);
}
@@ -405,19 +407,23 @@ void matrix_scan_quantum() {
#endif
#if defined(AUDIO_ENABLE) && !defined(NO_MUSIC_MODE)
- matrix_scan_music();
+ music_task();
+#endif
+
+#ifdef KEY_OVERRIDE_ENABLE
+ key_override_task();
#endif
#ifdef SEQUENCER_ENABLE
- matrix_scan_sequencer();
+ sequencer_task();
#endif
#ifdef TAP_DANCE_ENABLE
- matrix_scan_tap_dance();
+ tap_dance_task();
#endif
#ifdef COMBO_ENABLE
- matrix_scan_combo();
+ combo_task();
#endif
#ifdef LED_MATRIX_ENABLE
diff --git a/quantum/quantum.h b/quantum/quantum.h
index 55f6dbb7da..d44dc26d2e 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -105,6 +105,10 @@ extern layer_state_t layer_state;
# include "process_unicodemap.h"
#endif
+#ifdef KEY_OVERRIDE_ENABLE
+# include "process_key_override.h"
+#endif
+
#ifdef TAP_DANCE_ENABLE
# include "process_tap_dance.h"
#endif
@@ -157,12 +161,17 @@ extern layer_state_t layer_state;
#ifdef HAPTIC_ENABLE
# include "haptic.h"
+# include "process_haptic.h"
#endif
#ifdef OLED_DRIVER_ENABLE
# include "oled_driver.h"
#endif
+#ifdef ST7565_ENABLE
+# include "st7565.h"
+#endif
+
#ifdef DIP_SWITCH_ENABLE
# include "dip_switch.h"
#endif
@@ -203,10 +212,6 @@ void set_single_persistent_default_layer(uint8_t default_layer);
#define IS_LAYER_ON_STATE(state, layer) layer_state_cmp(state, layer)
#define IS_LAYER_OFF_STATE(state, layer) !layer_state_cmp(state, layer)
-void matrix_init_kb(void);
-void matrix_scan_kb(void);
-void matrix_init_user(void);
-void matrix_scan_user(void);
uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache);
uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);
bool process_action_kb(keyrecord_t *record);
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
index c361dd670e..3d2dbde922 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -514,6 +514,11 @@ enum quantum_keycodes {
// RGB underglow/matrix (continued)
RGB_MODE_TWINKLE,
+ // Key Overrides
+ KEY_OVERRIDE_TOGGLE,
+ KEY_OVERRIDE_ON,
+ KEY_OVERRIDE_OFF,
+
// Start of custom keycode range for keyboards and keymaps - always leave at the end
SAFE_RANGE
};
diff --git a/quantum/rgb_matrix_animations/alpha_mods_anim.h b/quantum/rgb_matrix/animations/alpha_mods_anim.h
index 426d88ef35..426d88ef35 100644
--- a/quantum/rgb_matrix_animations/alpha_mods_anim.h
+++ b/quantum/rgb_matrix/animations/alpha_mods_anim.h
diff --git a/quantum/rgb_matrix_animations/breathing_anim.h b/quantum/rgb_matrix/animations/breathing_anim.h
index 340bd93e5d..340bd93e5d 100644
--- a/quantum/rgb_matrix_animations/breathing_anim.h
+++ b/quantum/rgb_matrix/animations/breathing_anim.h
diff --git a/quantum/rgb_matrix_animations/colorband_pinwheel_sat_anim.h b/quantum/rgb_matrix/animations/colorband_pinwheel_sat_anim.h
index 3df3cfda7d..3df3cfda7d 100644
--- a/quantum/rgb_matrix_animations/colorband_pinwheel_sat_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_pinwheel_sat_anim.h
diff --git a/quantum/rgb_matrix_animations/colorband_pinwheel_val_anim.h b/quantum/rgb_matrix/animations/colorband_pinwheel_val_anim.h
index 7d80074fd5..7d80074fd5 100644
--- a/quantum/rgb_matrix_animations/colorband_pinwheel_val_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_pinwheel_val_anim.h
diff --git a/quantum/rgb_matrix_animations/colorband_sat_anim.h b/quantum/rgb_matrix/animations/colorband_sat_anim.h
index 35b830af6b..35b830af6b 100644
--- a/quantum/rgb_matrix_animations/colorband_sat_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_sat_anim.h
diff --git a/quantum/rgb_matrix_animations/colorband_spiral_sat_anim.h b/quantum/rgb_matrix/animations/colorband_spiral_sat_anim.h
index 048157aa1b..048157aa1b 100644
--- a/quantum/rgb_matrix_animations/colorband_spiral_sat_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_spiral_sat_anim.h
diff --git a/quantum/rgb_matrix_animations/colorband_spiral_val_anim.h b/quantum/rgb_matrix/animations/colorband_spiral_val_anim.h
index bff2da1616..bff2da1616 100644
--- a/quantum/rgb_matrix_animations/colorband_spiral_val_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_spiral_val_anim.h
diff --git a/quantum/rgb_matrix_animations/colorband_val_anim.h b/quantum/rgb_matrix/animations/colorband_val_anim.h
index f1aaf1d067..f1aaf1d067 100644
--- a/quantum/rgb_matrix_animations/colorband_val_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_val_anim.h
diff --git a/quantum/rgb_matrix_animations/cycle_all_anim.h b/quantum/rgb_matrix/animations/cycle_all_anim.h
index faf8598a39..faf8598a39 100644
--- a/quantum/rgb_matrix_animations/cycle_all_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_all_anim.h
diff --git a/quantum/rgb_matrix_animations/cycle_left_right_anim.h b/quantum/rgb_matrix/animations/cycle_left_right_anim.h
index cf911eb937..cf911eb937 100644
--- a/quantum/rgb_matrix_animations/cycle_left_right_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_left_right_anim.h
diff --git a/quantum/rgb_matrix_animations/cycle_out_in_anim.h b/quantum/rgb_matrix/animations/cycle_out_in_anim.h
index d66acd4b2b..d66acd4b2b 100644
--- a/quantum/rgb_matrix_animations/cycle_out_in_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_out_in_anim.h
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..fe8396140f 100644
--- a/quantum/rgb_matrix_animations/cycle_out_in_dual_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_out_in_dual_anim.h
diff --git a/quantum/rgb_matrix_animations/cycle_pinwheel_anim.h b/quantum/rgb_matrix/animations/cycle_pinwheel_anim.h
index 7799887099..7799887099 100644
--- a/quantum/rgb_matrix_animations/cycle_pinwheel_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_pinwheel_anim.h
diff --git a/quantum/rgb_matrix_animations/cycle_spiral_anim.h b/quantum/rgb_matrix/animations/cycle_spiral_anim.h
index 80cfb0dbc7..80cfb0dbc7 100644
--- a/quantum/rgb_matrix_animations/cycle_spiral_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_spiral_anim.h
diff --git a/quantum/rgb_matrix_animations/cycle_up_down_anim.h b/quantum/rgb_matrix/animations/cycle_up_down_anim.h
index 5016f739d6..5016f739d6 100644
--- a/quantum/rgb_matrix_animations/cycle_up_down_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_up_down_anim.h
diff --git a/quantum/rgb_matrix_animations/digital_rain_anim.h b/quantum/rgb_matrix/animations/digital_rain_anim.h
index 1de45f8e8d..1de45f8e8d 100644
--- a/quantum/rgb_matrix_animations/digital_rain_anim.h
+++ b/quantum/rgb_matrix/animations/digital_rain_anim.h
diff --git a/quantum/rgb_matrix_animations/dual_beacon_anim.h b/quantum/rgb_matrix/animations/dual_beacon_anim.h
index ce94871681..ce94871681 100644
--- a/quantum/rgb_matrix_animations/dual_beacon_anim.h
+++ b/quantum/rgb_matrix/animations/dual_beacon_anim.h
diff --git a/quantum/rgb_matrix_animations/gradient_left_right_anim.h b/quantum/rgb_matrix/animations/gradient_left_right_anim.h
index 53dfd04e2c..53dfd04e2c 100644
--- a/quantum/rgb_matrix_animations/gradient_left_right_anim.h
+++ b/quantum/rgb_matrix/animations/gradient_left_right_anim.h
diff --git a/quantum/rgb_matrix_animations/gradient_up_down_anim.h b/quantum/rgb_matrix/animations/gradient_up_down_anim.h
index 7e0d2898cf..7e0d2898cf 100644
--- a/quantum/rgb_matrix_animations/gradient_up_down_anim.h
+++ b/quantum/rgb_matrix/animations/gradient_up_down_anim.h
diff --git a/quantum/rgb_matrix_animations/hue_breathing_anim.h b/quantum/rgb_matrix/animations/hue_breathing_anim.h
index 54dea958af..54dea958af 100644
--- a/quantum/rgb_matrix_animations/hue_breathing_anim.h
+++ b/quantum/rgb_matrix/animations/hue_breathing_anim.h
diff --git a/quantum/rgb_matrix_animations/hue_pendulum_anim.h b/quantum/rgb_matrix/animations/hue_pendulum_anim.h
index 2d8d36174f..2d8d36174f 100644
--- a/quantum/rgb_matrix_animations/hue_pendulum_anim.h
+++ b/quantum/rgb_matrix/animations/hue_pendulum_anim.h
diff --git a/quantum/rgb_matrix_animations/hue_wave_anim.h b/quantum/rgb_matrix/animations/hue_wave_anim.h
index fd9026fc90..fd9026fc90 100644
--- a/quantum/rgb_matrix_animations/hue_wave_anim.h
+++ b/quantum/rgb_matrix/animations/hue_wave_anim.h
diff --git a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h b/quantum/rgb_matrix/animations/jellybean_raindrops_anim.h
index 9493b38508..a17e954b1b 100644
--- a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h
+++ b/quantum/rgb_matrix/animations/jellybean_raindrops_anim.h
@@ -4,7 +4,7 @@ RGB_MATRIX_EFFECT(JELLYBEAN_RAINDROPS)
static void jellybean_raindrops_set_color(int i, effect_params_t* params) {
if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return;
- HSV hsv = {rand() & 0xFF, rand() & 0xFF, rgb_matrix_config.hsv.v};
+ HSV hsv = {rand() & 0xFF, qadd8(rand() & 0x7F, 0x80), rgb_matrix_config.hsv.v};
RGB rgb = rgb_matrix_hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
diff --git a/quantum/rgb_matrix_animations/rainbow_beacon_anim.h b/quantum/rgb_matrix/animations/rainbow_beacon_anim.h
index 977261182f..977261182f 100644
--- a/quantum/rgb_matrix_animations/rainbow_beacon_anim.h
+++ b/quantum/rgb_matrix/animations/rainbow_beacon_anim.h
diff --git a/quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h b/quantum/rgb_matrix/animations/rainbow_moving_chevron_anim.h
index e51e7b2516..e51e7b2516 100644
--- a/quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h
+++ b/quantum/rgb_matrix/animations/rainbow_moving_chevron_anim.h
diff --git a/quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h b/quantum/rgb_matrix/animations/rainbow_pinwheels_anim.h
index 1cd4ed2acf..1cd4ed2acf 100644
--- a/quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h
+++ b/quantum/rgb_matrix/animations/rainbow_pinwheels_anim.h
diff --git a/quantum/rgb_matrix_animations/raindrops_anim.h b/quantum/rgb_matrix/animations/raindrops_anim.h
index 38359cdca7..38359cdca7 100644
--- a/quantum/rgb_matrix_animations/raindrops_anim.h
+++ b/quantum/rgb_matrix/animations/raindrops_anim.h
diff --git a/quantum/rgb_matrix/animations/rgb_matrix_effects.inc b/quantum/rgb_matrix/animations/rgb_matrix_effects.inc
new file mode 100644
index 0000000000..302ad79c04
--- /dev/null
+++ b/quantum/rgb_matrix/animations/rgb_matrix_effects.inc
@@ -0,0 +1,37 @@
+// Add your new core rgb matrix effect here, order determines enum order
+#include "solid_color_anim.h"
+#include "alpha_mods_anim.h"
+#include "gradient_up_down_anim.h"
+#include "gradient_left_right_anim.h"
+#include "breathing_anim.h"
+#include "colorband_sat_anim.h"
+#include "colorband_val_anim.h"
+#include "colorband_pinwheel_sat_anim.h"
+#include "colorband_pinwheel_val_anim.h"
+#include "colorband_spiral_sat_anim.h"
+#include "colorband_spiral_val_anim.h"
+#include "cycle_all_anim.h"
+#include "cycle_left_right_anim.h"
+#include "cycle_up_down_anim.h"
+#include "rainbow_moving_chevron_anim.h"
+#include "cycle_out_in_anim.h"
+#include "cycle_out_in_dual_anim.h"
+#include "cycle_pinwheel_anim.h"
+#include "cycle_spiral_anim.h"
+#include "dual_beacon_anim.h"
+#include "rainbow_beacon_anim.h"
+#include "rainbow_pinwheels_anim.h"
+#include "raindrops_anim.h"
+#include "jellybean_raindrops_anim.h"
+#include "hue_breathing_anim.h"
+#include "hue_pendulum_anim.h"
+#include "hue_wave_anim.h"
+#include "typing_heatmap_anim.h"
+#include "digital_rain_anim.h"
+#include "solid_reactive_simple_anim.h"
+#include "solid_reactive_anim.h"
+#include "solid_reactive_wide.h"
+#include "solid_reactive_cross.h"
+#include "solid_reactive_nexus.h"
+#include "splash_anim.h"
+#include "solid_splash_anim.h"
diff --git a/quantum/rgb_matrix_runners/effect_runner_dx_dy.h b/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy.h
index 4867609c81..4867609c81 100644
--- a/quantum/rgb_matrix_runners/effect_runner_dx_dy.h
+++ b/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy.h
diff --git a/quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h b/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy_dist.h
index 9545b418d9..9545b418d9 100644
--- a/quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h
+++ b/quantum/rgb_matrix/animations/runners/effect_runner_dx_dy_dist.h
diff --git a/quantum/rgb_matrix_runners/effect_runner_i.h b/quantum/rgb_matrix/animations/runners/effect_runner_i.h
index 1881cd6c60..1881cd6c60 100644
--- a/quantum/rgb_matrix_runners/effect_runner_i.h
+++ b/quantum/rgb_matrix/animations/runners/effect_runner_i.h
diff --git a/quantum/rgb_matrix_runners/effect_runner_reactive.h b/quantum/rgb_matrix/animations/runners/effect_runner_reactive.h
index 75b7c0df4e..75b7c0df4e 100644
--- a/quantum/rgb_matrix_runners/effect_runner_reactive.h
+++ b/quantum/rgb_matrix/animations/runners/effect_runner_reactive.h
diff --git a/quantum/rgb_matrix_runners/effect_runner_reactive_splash.h b/quantum/rgb_matrix/animations/runners/effect_runner_reactive_splash.h
index 2e46ffb350..2e46ffb350 100644
--- a/quantum/rgb_matrix_runners/effect_runner_reactive_splash.h
+++ b/quantum/rgb_matrix/animations/runners/effect_runner_reactive_splash.h
diff --git a/quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h b/quantum/rgb_matrix/animations/runners/effect_runner_sin_cos_i.h
index 02351de51e..02351de51e 100644
--- a/quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h
+++ b/quantum/rgb_matrix/animations/runners/effect_runner_sin_cos_i.h
diff --git a/quantum/rgb_matrix/animations/runners/rgb_matrix_runners.inc b/quantum/rgb_matrix/animations/runners/rgb_matrix_runners.inc
new file mode 100644
index 0000000000..c09022bb0f
--- /dev/null
+++ b/quantum/rgb_matrix/animations/runners/rgb_matrix_runners.inc
@@ -0,0 +1,6 @@
+#include "effect_runner_dx_dy_dist.h"
+#include "effect_runner_dx_dy.h"
+#include "effect_runner_i.h"
+#include "effect_runner_sin_cos_i.h"
+#include "effect_runner_reactive.h"
+#include "effect_runner_reactive_splash.h"
diff --git a/quantum/rgb_matrix_animations/solid_color_anim.h b/quantum/rgb_matrix/animations/solid_color_anim.h
index 79d63cf133..79d63cf133 100644
--- a/quantum/rgb_matrix_animations/solid_color_anim.h
+++ b/quantum/rgb_matrix/animations/solid_color_anim.h
diff --git a/quantum/rgb_matrix_animations/solid_reactive_anim.h b/quantum/rgb_matrix/animations/solid_reactive_anim.h
index d45bb961bc..d45bb961bc 100644
--- a/quantum/rgb_matrix_animations/solid_reactive_anim.h
+++ b/quantum/rgb_matrix/animations/solid_reactive_anim.h
diff --git a/quantum/rgb_matrix_animations/solid_reactive_cross.h b/quantum/rgb_matrix/animations/solid_reactive_cross.h
index f76c68e8c7..f76c68e8c7 100644
--- a/quantum/rgb_matrix_animations/solid_reactive_cross.h
+++ b/quantum/rgb_matrix/animations/solid_reactive_cross.h
diff --git a/quantum/rgb_matrix_animations/solid_reactive_nexus.h b/quantum/rgb_matrix/animations/solid_reactive_nexus.h
index 17f94e3c18..17f94e3c18 100644
--- a/quantum/rgb_matrix_animations/solid_reactive_nexus.h
+++ b/quantum/rgb_matrix/animations/solid_reactive_nexus.h
diff --git a/quantum/rgb_matrix_animations/solid_reactive_simple_anim.h b/quantum/rgb_matrix/animations/solid_reactive_simple_anim.h
index 12eb248cc0..12eb248cc0 100644
--- a/quantum/rgb_matrix_animations/solid_reactive_simple_anim.h
+++ b/quantum/rgb_matrix/animations/solid_reactive_simple_anim.h
diff --git a/quantum/rgb_matrix_animations/solid_reactive_wide.h b/quantum/rgb_matrix/animations/solid_reactive_wide.h
index 1cc4dca728..1cc4dca728 100644
--- a/quantum/rgb_matrix_animations/solid_reactive_wide.h
+++ b/quantum/rgb_matrix/animations/solid_reactive_wide.h
diff --git a/quantum/rgb_matrix_animations/solid_splash_anim.h b/quantum/rgb_matrix/animations/solid_splash_anim.h
index 99efb4996a..99efb4996a 100644
--- a/quantum/rgb_matrix_animations/solid_splash_anim.h
+++ b/quantum/rgb_matrix/animations/solid_splash_anim.h
diff --git a/quantum/rgb_matrix_animations/splash_anim.h b/quantum/rgb_matrix/animations/splash_anim.h
index 1415bcc0fa..1415bcc0fa 100644
--- a/quantum/rgb_matrix_animations/splash_anim.h
+++ b/quantum/rgb_matrix/animations/splash_anim.h
diff --git a/quantum/rgb_matrix_animations/typing_heatmap_anim.h b/quantum/rgb_matrix/animations/typing_heatmap_anim.h
index e7dda11a2f..e7dda11a2f 100644
--- a/quantum/rgb_matrix_animations/typing_heatmap_anim.h
+++ b/quantum/rgb_matrix/animations/typing_heatmap_anim.h
diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c
index ab8dbd849b..624e851581 100644
--- a/quantum/rgb_matrix.c
+++ b/quantum/rgb_matrix/rgb_matrix.c
@@ -31,22 +31,25 @@ const led_point_t k_rgb_matrix_center = {112, 32};
const led_point_t k_rgb_matrix_center = RGB_MATRIX_CENTER;
#endif
+// clang-format off
+#ifndef RGB_MATRIX_IMMEDIATE_EEPROM
+# define rgb_eeconfig_update(v) rgb_update_eeprom |= v
+#else
+# define rgb_eeconfig_update(v) if (v) eeconfig_update_rgb_matrix()
+#endif
+// clang-format on
+
__attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv); }
// Generic effect runners
-#include "rgb_matrix_runners/effect_runner_dx_dy_dist.h"
-#include "rgb_matrix_runners/effect_runner_dx_dy.h"
-#include "rgb_matrix_runners/effect_runner_i.h"
-#include "rgb_matrix_runners/effect_runner_sin_cos_i.h"
-#include "rgb_matrix_runners/effect_runner_reactive.h"
-#include "rgb_matrix_runners/effect_runner_reactive_splash.h"
+#include "rgb_matrix_runners.inc"
// ------------------------------------------
// -----Begin rgb effect includes macros-----
#define RGB_MATRIX_EFFECT(name)
#define RGB_MATRIX_CUSTOM_EFFECT_IMPLS
-#include "rgb_matrix_animations/rgb_matrix_effects.inc"
+#include "rgb_matrix_effects.inc"
#ifdef RGB_MATRIX_CUSTOM_KB
# include "rgb_matrix_kb.inc"
#endif
@@ -67,10 +70,6 @@ __attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv
# define RGB_DISABLE_TIMEOUT 0
#endif
-#if RGB_DISABLE_WHEN_USB_SUSPENDED != 1
-# 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
@@ -129,6 +128,7 @@ last_hit_t g_last_hit_tracker;
// internals
static bool suspend_state = false;
+static bool rgb_update_eeprom = false;
static uint8_t rgb_last_enable = UINT8_MAX;
static uint8_t rgb_last_effect = UINT8_MAX;
static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false};
@@ -230,7 +230,7 @@ void process_rgb_matrix(uint8_t row, uint8_t col, bool pressed) {
memcpy(&last_hit_buffer.y[0], &last_hit_buffer.y[led_count], LED_HITS_TO_REMEMBER - led_count);
memcpy(&last_hit_buffer.tick[0], &last_hit_buffer.tick[led_count], (LED_HITS_TO_REMEMBER - led_count) * 2); // 16 bit
memcpy(&last_hit_buffer.index[0], &last_hit_buffer.index[led_count], LED_HITS_TO_REMEMBER - led_count);
- last_hit_buffer.count--;
+ last_hit_buffer.count = LED_HITS_TO_REMEMBER - led_count;
}
for (uint8_t i = 0; i < led_count; i++) {
@@ -315,6 +315,8 @@ static void rgb_task_timers(void) {
static void rgb_task_sync(void) {
// next task
+ if (rgb_update_eeprom) eeconfig_update_rgb_matrix();
+ rgb_update_eeprom = false;
if (sync_timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING;
}
@@ -353,7 +355,7 @@ static void rgb_task_render(uint8_t effect) {
case RGB_MATRIX_##name: \
rendering = name(&rgb_effect_params); \
break;
-#include "rgb_matrix_animations/rgb_matrix_effects.inc"
+#include "rgb_matrix_effects.inc"
#undef RGB_MATRIX_EFFECT
#if defined(RGB_MATRIX_CUSTOM_KB) || defined(RGB_MATRIX_CUSTOM_USER)
@@ -511,9 +513,7 @@ bool rgb_matrix_get_suspend_state(void) { return suspend_state; }
void rgb_matrix_toggle_eeprom_helper(bool write_to_eeprom) {
rgb_matrix_config.enable ^= 1;
rgb_task_state = STARTING;
- if (write_to_eeprom) {
- eeconfig_update_rgb_matrix();
- }
+ rgb_eeconfig_update(write_to_eeprom);
dprintf("rgb matrix toggle [%s]: rgb_matrix_config.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.enable);
}
void rgb_matrix_toggle_noeeprom(void) { rgb_matrix_toggle_eeprom_helper(false); }
@@ -521,7 +521,7 @@ void rgb_matrix_toggle(void) { rgb_matrix_toggle_eeprom_helper(true); }
void rgb_matrix_enable(void) {
rgb_matrix_enable_noeeprom();
- eeconfig_update_rgb_matrix();
+ rgb_eeconfig_update(true);
}
void rgb_matrix_enable_noeeprom(void) {
@@ -531,7 +531,7 @@ void rgb_matrix_enable_noeeprom(void) {
void rgb_matrix_disable(void) {
rgb_matrix_disable_noeeprom();
- eeconfig_update_rgb_matrix();
+ rgb_eeconfig_update(true);
}
void rgb_matrix_disable_noeeprom(void) {
@@ -553,9 +553,7 @@ void rgb_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
rgb_matrix_config.mode = mode;
}
rgb_task_state = STARTING;
- if (write_to_eeprom) {
- eeconfig_update_rgb_matrix();
- }
+ rgb_eeconfig_update(write_to_eeprom);
dprintf("rgb matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.mode);
}
void rgb_matrix_mode_noeeprom(uint8_t mode) { rgb_matrix_mode_eeprom_helper(mode, false); }
@@ -584,9 +582,7 @@ void rgb_matrix_sethsv_eeprom_helper(uint16_t hue, uint8_t sat, uint8_t val, boo
rgb_matrix_config.hsv.h = hue;
rgb_matrix_config.hsv.s = sat;
rgb_matrix_config.hsv.v = (val > RGB_MATRIX_MAXIMUM_BRIGHTNESS) ? RGB_MATRIX_MAXIMUM_BRIGHTNESS : val;
- if (write_to_eeprom) {
- eeconfig_update_rgb_matrix();
- }
+ rgb_eeconfig_update(write_to_eeprom);
dprintf("rgb matrix set hsv [%s]: %u,%u,%u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.hsv.h, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v);
}
void rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) { rgb_matrix_sethsv_eeprom_helper(hue, sat, val, false); }
@@ -623,9 +619,7 @@ void rgb_matrix_decrease_val(void) { rgb_matrix_decrease_val_helper(true); }
void rgb_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {
rgb_matrix_config.speed = speed;
- if (write_to_eeprom) {
- eeconfig_update_rgb_matrix();
- }
+ rgb_eeconfig_update(write_to_eeprom);
dprintf("rgb matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.speed);
}
void rgb_matrix_set_speed_noeeprom(uint8_t speed) { rgb_matrix_set_speed_eeprom_helper(speed, false); }
diff --git a/quantum/rgb_matrix.h b/quantum/rgb_matrix/rgb_matrix.h
index a615b8422c..58ac29da32 100644
--- a/quantum/rgb_matrix.h
+++ b/quantum/rgb_matrix/rgb_matrix.h
@@ -23,7 +23,6 @@
#include "rgb_matrix_types.h"
#include "color.h"
#include "quantum.h"
-#include "rgblight_list.h"
#ifdef IS31FL3731
# include "is31fl3731.h"
@@ -33,6 +32,8 @@
# include "is31fl3737.h"
#elif defined(IS31FL3741)
# include "is31fl3741.h"
+#elif defined(AW20216)
+# include "aw20216.h"
#elif defined(WS2812)
# include "ws2812.h"
#endif
@@ -70,7 +71,7 @@ enum rgb_matrix_effects {
// --------------------------------------
// -----Begin rgb effect enum macros-----
#define RGB_MATRIX_EFFECT(name, ...) RGB_MATRIX_##name,
-#include "rgb_matrix_animations/rgb_matrix_effects.inc"
+#include "rgb_matrix_effects.inc"
#undef RGB_MATRIX_EFFECT
#if defined(RGB_MATRIX_CUSTOM_KB) || defined(RGB_MATRIX_CUSTOM_USER)
diff --git a/quantum/rgb_matrix_drivers.c b/quantum/rgb_matrix/rgb_matrix_drivers.c
index 896fa6d0ef..bfaedaa4a0 100644
--- a/quantum/rgb_matrix_drivers.c
+++ b/quantum/rgb_matrix/rgb_matrix_drivers.c
@@ -65,6 +65,9 @@ static void init(void) {
# endif
# elif defined(IS31FL3737)
IS31FL3737_init(DRIVER_ADDR_1);
+# if defined(DRIVER_ADDR_2) && (DRIVER_ADDR_2 != DRIVER_ADDR_1) // provides backward compatibility
+ IS31FL3737_init(DRIVER_ADDR_2);
+# endif
# else
IS31FL3741_init(DRIVER_ADDR_1);
# endif
@@ -105,7 +108,10 @@ static void init(void) {
IS31FL3733_update_led_control_registers(DRIVER_ADDR_4, 3);
# endif
# elif defined(IS31FL3737)
- IS31FL3737_update_led_control_registers(DRIVER_ADDR_1, DRIVER_ADDR_2);
+ IS31FL3737_update_led_control_registers(DRIVER_ADDR_1, 0);
+# if defined(DRIVER_ADDR_2) && (DRIVER_ADDR_2 != DRIVER_ADDR_1) // provides backward compatibility
+ IS31FL3737_update_led_control_registers(DRIVER_ADDR_2, 1);
+# endif
# else
IS31FL3741_update_led_control_registers(DRIVER_ADDR_1, 0);
# endif
@@ -152,7 +158,12 @@ const rgb_matrix_driver_t rgb_matrix_driver = {
.set_color_all = IS31FL3733_set_color_all,
};
# elif defined(IS31FL3737)
-static void flush(void) { IS31FL3737_update_pwm_buffers(DRIVER_ADDR_1, DRIVER_ADDR_2); }
+static void flush(void) {
+ IS31FL3737_update_pwm_buffers(DRIVER_ADDR_1, 0);
+# if defined(DRIVER_ADDR_2) && (DRIVER_ADDR_2 != DRIVER_ADDR_1) // provides backward compatibility
+ IS31FL3737_update_pwm_buffers(DRIVER_ADDR_2, 1);
+# endif
+}
const rgb_matrix_driver_t rgb_matrix_driver = {
.init = init,
@@ -171,6 +182,30 @@ const rgb_matrix_driver_t rgb_matrix_driver = {
};
# endif
+#elif defined(AW20216)
+# include "spi_master.h"
+static void init(void) {
+ spi_init();
+ AW20216_init(DRIVER_1_CS, DRIVER_1_EN);
+# ifdef DRIVER_2_CS
+ AW20216_init(DRIVER_2_CS, DRIVER_2_EN);
+# endif
+}
+
+static void flush(void) {
+ AW20216_update_pwm_buffers(DRIVER_1_CS, 0);
+# ifdef DRIVER_2_CS
+ AW20216_update_pwm_buffers(DRIVER_2_CS, 1);
+# endif
+}
+
+const rgb_matrix_driver_t rgb_matrix_driver = {
+ .init = init,
+ .flush = flush,
+ .set_color = AW20216_set_color,
+ .set_color_all = AW20216_set_color_all,
+};
+
#elif defined(WS2812)
# if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_CUSTOM_DRIVER)
# pragma message "Cannot use RGBLIGHT and RGB Matrix using WS2812 at the same time."
diff --git a/quantum/rgb_matrix_types.h b/quantum/rgb_matrix/rgb_matrix_types.h
index df575d6577..df575d6577 100644
--- a/quantum/rgb_matrix_types.h
+++ b/quantum/rgb_matrix/rgb_matrix_types.h
diff --git a/quantum/rgb_matrix_animations/rgb_matrix_effects.inc b/quantum/rgb_matrix_animations/rgb_matrix_effects.inc
deleted file mode 100644
index 053d441506..0000000000
--- a/quantum/rgb_matrix_animations/rgb_matrix_effects.inc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Add your new core rgb matrix effect here, order determins enum order, requires "rgb_matrix_animations/ directory
-#include "rgb_matrix_animations/solid_color_anim.h"
-#include "rgb_matrix_animations/alpha_mods_anim.h"
-#include "rgb_matrix_animations/gradient_up_down_anim.h"
-#include "rgb_matrix_animations/gradient_left_right_anim.h"
-#include "rgb_matrix_animations/breathing_anim.h"
-#include "rgb_matrix_animations/colorband_sat_anim.h"
-#include "rgb_matrix_animations/colorband_val_anim.h"
-#include "rgb_matrix_animations/colorband_pinwheel_sat_anim.h"
-#include "rgb_matrix_animations/colorband_pinwheel_val_anim.h"
-#include "rgb_matrix_animations/colorband_spiral_sat_anim.h"
-#include "rgb_matrix_animations/colorband_spiral_val_anim.h"
-#include "rgb_matrix_animations/cycle_all_anim.h"
-#include "rgb_matrix_animations/cycle_left_right_anim.h"
-#include "rgb_matrix_animations/cycle_up_down_anim.h"
-#include "rgb_matrix_animations/rainbow_moving_chevron_anim.h"
-#include "rgb_matrix_animations/cycle_out_in_anim.h"
-#include "rgb_matrix_animations/cycle_out_in_dual_anim.h"
-#include "rgb_matrix_animations/cycle_pinwheel_anim.h"
-#include "rgb_matrix_animations/cycle_spiral_anim.h"
-#include "rgb_matrix_animations/dual_beacon_anim.h"
-#include "rgb_matrix_animations/rainbow_beacon_anim.h"
-#include "rgb_matrix_animations/rainbow_pinwheels_anim.h"
-#include "rgb_matrix_animations/raindrops_anim.h"
-#include "rgb_matrix_animations/jellybean_raindrops_anim.h"
-#include "rgb_matrix_animations/hue_breathing_anim.h"
-#include "rgb_matrix_animations/hue_pendulum_anim.h"
-#include "rgb_matrix_animations/hue_wave_anim.h"
-#include "rgb_matrix_animations/typing_heatmap_anim.h"
-#include "rgb_matrix_animations/digital_rain_anim.h"
-#include "rgb_matrix_animations/solid_reactive_simple_anim.h"
-#include "rgb_matrix_animations/solid_reactive_anim.h"
-#include "rgb_matrix_animations/solid_reactive_wide.h"
-#include "rgb_matrix_animations/solid_reactive_cross.h"
-#include "rgb_matrix_animations/solid_reactive_nexus.h"
-#include "rgb_matrix_animations/splash_anim.h"
-#include "rgb_matrix_animations/solid_splash_anim.h"
diff --git a/quantum/rgblight.c b/quantum/rgblight/rgblight.c
index 04bbb1283b..148dae78f4 100644
--- a/quantum/rgblight.c
+++ b/quantum/rgblight/rgblight.c
@@ -686,6 +686,9 @@ bool rgblight_get_layer_state(uint8_t layer) {
// Write any enabled LED layers into the buffer
static void rgblight_layers_write(void) {
+# ifdef RGBLIGHT_LAYERS_RETAIN_VAL
+ uint8_t current_val = rgblight_get_val();
+# endif
uint8_t i = 0;
// For each layer
for (const rgblight_segment_t *const *layer_ptr = rgblight_layers; i < RGBLIGHT_MAX_LAYERS; layer_ptr++, i++) {
@@ -706,7 +709,11 @@ static void rgblight_layers_write(void) {
// Write segment.count LEDs
LED_TYPE *const limit = &led[MIN(segment.index + segment.count, RGBLED_NUM)];
for (LED_TYPE *led_ptr = &led[segment.index]; led_ptr < limit; led_ptr++) {
+# ifdef RGBLIGHT_LAYERS_RETAIN_VAL
+ sethsv(segment.hue, segment.sat, current_val, led_ptr);
+# else
sethsv(segment.hue, segment.sat, segment.val, led_ptr);
+# endif
}
segment_ptr++;
}
@@ -882,7 +889,7 @@ void rgblight_update_sync(rgblight_syncinfo_t *syncinfo, bool write_to_eeprom) {
animation_status.restart = true;
}
# endif /* RGBLIGHT_SPLIT_NO_ANIMATION_SYNC */
-# endif /* RGBLIGHT_USE_TIMER */
+# endif /* RGBLIGHT_USE_TIMER */
}
#endif /* RGBLIGHT_SPLIT */
diff --git a/quantum/rgblight.h b/quantum/rgblight/rgblight.h
index c05870f498..5b90b8f49e 100644
--- a/quantum/rgblight.h
+++ b/quantum/rgblight/rgblight.h
@@ -79,7 +79,6 @@
# define RGBLIGHT_EFFECT_STATIC_GRADIENT
# define RGBLIGHT_EFFECT_RGB_TEST
# define RGBLIGHT_EFFECT_ALTERNATING
-# define RGBLIGHT_EFFECT_TWINKLE
#endif
#ifdef RGBLIGHT_STATIC_PATTERNS
diff --git a/quantum/rgblight_breathe_table.h b/quantum/rgblight/rgblight_breathe_table.h
index 30245318b6..30245318b6 100644
--- a/quantum/rgblight_breathe_table.h
+++ b/quantum/rgblight/rgblight_breathe_table.h
diff --git a/quantum/rgblight_list.h b/quantum/rgblight/rgblight_list.h
index f29a646b66..0fd68b75f3 100644
--- a/quantum/rgblight_list.h
+++ b/quantum/rgblight/rgblight_list.h
@@ -15,49 +15,7 @@
*/
#pragma once
-/* RGB COLORS */
-#define RGB_WHITE 0xFF, 0xFF, 0xFF
-#define RGB_RED 0xFF, 0x00, 0x00
-#define RGB_CORAL 0xFF, 0x7C, 0x4D
-#define RGB_ORANGE 0xFF, 0x80, 0x00
-#define RGB_GOLDENROD 0xD9, 0xA5, 0x21
-#define RGB_GOLD 0xFF, 0xD9, 0x00
-#define RGB_YELLOW 0xFF, 0xFF, 0x00
-#define RGB_CHARTREUSE 0x80, 0xFF, 0x00
-#define RGB_GREEN 0x00, 0xFF, 0x00
-#define RGB_SPRINGGREEN 0x00, 0xFF, 0x80
-#define RGB_TURQUOISE 0x47, 0x6E, 0x6A
-#define RGB_TEAL 0x00, 0x80, 0x80
-#define RGB_CYAN 0x00, 0xFF, 0xFF
-#define RGB_AZURE 0x99, 0xf5, 0xFF
-#define RGB_BLUE 0x00, 0x00, 0xFF
-#define RGB_PURPLE 0x7A, 0x00, 0xFF
-#define RGB_MAGENTA 0xFF, 0x00, 0xFF
-#define RGB_PINK 0xFF, 0x80, 0xBF
-#define RGB_BLACK 0x00, 0x00, 0x00
-#define RGB_OFF RGB_BLACK
-
-/* HSV COLORS */
-#define HSV_WHITE 0, 0, 255
-#define HSV_RED 0, 255, 255
-#define HSV_CORAL 11, 176, 255
-#define HSV_ORANGE 28, 255, 255
-#define HSV_GOLDENROD 30, 218, 218
-#define HSV_GOLD 36, 255, 255
-#define HSV_YELLOW 43, 255, 255
-#define HSV_CHARTREUSE 64, 255, 255
-#define HSV_GREEN 85, 255, 255
-#define HSV_SPRINGGREEN 106, 255, 255
-#define HSV_TURQUOISE 123, 90, 112
-#define HSV_TEAL 128, 255, 128
-#define HSV_CYAN 128, 255, 255
-#define HSV_AZURE 132, 102, 255
-#define HSV_BLUE 170, 255, 255
-#define HSV_PURPLE 191, 255, 255
-#define HSV_MAGENTA 213, 255, 255
-#define HSV_PINK 234, 128, 255
-#define HSV_BLACK 0, 0, 0
-#define HSV_OFF HSV_BLACK
+#include "color.h"
/*
########################################################################################
@@ -66,7 +24,7 @@
## ##
## The functions below have been deprecated and may be removed in a future release. ##
## ##
-## Please use the values above with the RGB functions. ##
+## Please use the values in color.h with the RGB functions. ##
## ##
## ##
## ##
diff --git a/quantum/rgblight_modes.h b/quantum/rgblight/rgblight_modes.h
index 7abdb87bc6..7abdb87bc6 100644
--- a/quantum/rgblight_modes.h
+++ b/quantum/rgblight/rgblight_modes.h
diff --git a/quantum/rgblight_post_config.h b/quantum/rgblight/rgblight_post_config.h
index 3c14cb6109..3c14cb6109 100644
--- a/quantum/rgblight_post_config.h
+++ b/quantum/rgblight/rgblight_post_config.h
diff --git a/quantum/send_string.c b/quantum/send_string.c
index 7d096b4273..1a7f7a1315 100644
--- a/quantum/send_string.c
+++ b/quantum/send_string.c
@@ -20,6 +20,14 @@
#include "send_string.h"
+#if defined(AUDIO_ENABLE) && defined(SENDSTRING_BELL)
+# include "audio.h"
+# ifndef BELL_SOUND
+# define BELL_SOUND TERMINAL_SOUND
+# endif
+float bell_song[][2] = SONG(BELL_SOUND);
+#endif
+
// clang-format off
/* Bit-Packed look-up table to convert an ASCII character to whether
diff --git a/quantum/sequencer/sequencer.c b/quantum/sequencer/sequencer.c
index 0eaf3a17aa..18a83661ec 100644
--- a/quantum/sequencer/sequencer.c
+++ b/quantum/sequencer/sequencer.c
@@ -211,7 +211,7 @@ void sequencer_phase_pause(void) {
sequencer_internal_state.phase = SEQUENCER_PHASE_ATTACK;
}
-void matrix_scan_sequencer(void) {
+void sequencer_task(void) {
if (!sequencer_config.enabled) {
return;
}
diff --git a/quantum/sequencer/sequencer.h b/quantum/sequencer/sequencer.h
index aeca7a1e9b..4017ae764e 100644
--- a/quantum/sequencer/sequencer.h
+++ b/quantum/sequencer/sequencer.h
@@ -119,4 +119,4 @@ uint16_t sequencer_get_step_duration(void);
uint16_t get_beat_duration(uint8_t tempo);
uint16_t get_step_duration(uint8_t tempo, sequencer_resolution_t resolution);
-void matrix_scan_sequencer(void);
+void sequencer_task(void);
diff --git a/quantum/sequencer/tests/sequencer_tests.cpp b/quantum/sequencer/tests/sequencer_tests.cpp
index e81984e5b5..290605a52a 100644
--- a/quantum/sequencer/tests/sequencer_tests.cpp
+++ b/quantum/sequencer/tests/sequencer_tests.cpp
@@ -386,7 +386,7 @@ void setUpMatrixScanSequencerTest(void) {
TEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackFirstTrackOfFirstStep) {
setUpMatrixScanSequencerTest();
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(last_noteon, MI_C);
EXPECT_EQ(last_noteoff, 0);
}
@@ -394,7 +394,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackFirstTrackOfFirstStep)
TEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackSecondTrackAfterFirstTrackOfFirstStep) {
setUpMatrixScanSequencerTest();
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(sequencer_internal_state.current_step, 0);
EXPECT_EQ(sequencer_internal_state.current_track, 1);
EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);
@@ -409,7 +409,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldNotAttackInactiveTrackFirstSt
// Wait some time after the first track has been attacked
advance_time(SEQUENCER_TRACK_THROTTLE);
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(last_noteon, 0);
EXPECT_EQ(last_noteoff, 0);
}
@@ -423,7 +423,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackThirdTrackAfterSecondTr
// Wait some time after the second track has been attacked
advance_time(2 * SEQUENCER_TRACK_THROTTLE);
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(sequencer_internal_state.current_step, 0);
EXPECT_EQ(sequencer_internal_state.current_track, 2);
EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);
@@ -438,7 +438,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldEnterReleasePhaseAfterLastTra
// Wait until all notes have been attacked
advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(last_noteon, 0);
EXPECT_EQ(last_noteoff, 0);
EXPECT_EQ(sequencer_internal_state.current_step, 0);
@@ -458,7 +458,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldReleaseBackwards) {
// + the release timeout
advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(sequencer_internal_state.current_step, 0);
EXPECT_EQ(sequencer_internal_state.current_track, SEQUENCER_TRACKS - 2);
EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_RELEASE);
@@ -476,7 +476,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldNotReleaseInactiveTrackFirstS
// + the release timeout
advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(last_noteon, 0);
EXPECT_EQ(last_noteoff, 0);
}
@@ -495,7 +495,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldReleaseFirstTrackFirstStep) {
// + all the other notes have been released
advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(last_noteon, 0);
EXPECT_EQ(last_noteoff, MI_C);
}
@@ -514,7 +514,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldEnterPausePhaseAfterRelease)
// + all the other notes have been released
advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(sequencer_internal_state.current_step, 0);
EXPECT_EQ(sequencer_internal_state.current_track, 0);
EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_PAUSE);
@@ -536,7 +536,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessFirstTrackOfSecondStep
// + the step duration (one 16th at tempo=120 lasts 125ms)
advance_time(125);
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(sequencer_internal_state.current_step, 1);
EXPECT_EQ(sequencer_internal_state.current_track, 1);
EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);
@@ -548,7 +548,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessSecondTrackTooEarly) {
sequencer_internal_state.current_step = 2;
sequencer_internal_state.current_track = 1;
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(last_noteon, 0);
EXPECT_EQ(last_noteoff, 0);
}
@@ -562,7 +562,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessSecondTrackOnTime) {
// Wait until first track has been attacked
advance_time(SEQUENCER_TRACK_THROTTLE);
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(last_noteon, MI_D);
EXPECT_EQ(last_noteoff, 0);
}
@@ -583,7 +583,7 @@ TEST_F(SequencerTest, TestMatrixScanSequencerShouldLoopOnceSequenceIsOver) {
// + the step duration (one 16th at tempo=120 lasts 125ms)
advance_time(125);
- matrix_scan_sequencer();
+ sequencer_task();
EXPECT_EQ(sequencer_internal_state.current_step, 0);
EXPECT_EQ(sequencer_internal_state.current_track, 1);
EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);
diff --git a/quantum/serial_link/system/serial_link.c b/quantum/serial_link/system/serial_link.c
index f77483ad8c..6363f8ff3b 100644
--- a/quantum/serial_link/system/serial_link.c
+++ b/quantum/serial_link/system/serial_link.c
@@ -29,10 +29,13 @@ SOFTWARE.
#include "serial_link/protocol/transport.h"
#include "serial_link/protocol/frame_router.h"
#include "matrix.h"
+#include "sync_timer.h"
#include <stdbool.h>
#include "print.h"
#include "config.h"
+#define SYNC_TIMER_OFFSET 2
+
static event_source_t new_data_event;
static bool serial_link_connected;
static bool is_master = false;
@@ -159,10 +162,16 @@ static matrix_object_t last_matrix = {};
SLAVE_TO_MASTER_OBJECT(keyboard_matrix, matrix_object_t);
MASTER_TO_ALL_SLAVES_OBJECT(serial_link_connected, bool);
+#ifndef DISABLE_SYNC_TIMER
+MASTER_TO_ALL_SLAVES_OBJECT(sync_timer, uint32_t);
+#endif
static remote_object_t* remote_objects[] = {
REMOTE_OBJECT(serial_link_connected),
REMOTE_OBJECT(keyboard_matrix),
+#ifndef DISABLE_SYNC_TIMER
+ REMOTE_OBJECT(sync_timer),
+#endif
};
void init_serial_link(void) {
@@ -200,14 +209,27 @@ void serial_link_update(void) {
m->rows[i] = matrix.rows[i];
}
end_write_keyboard_matrix();
+
*begin_write_serial_link_connected() = true;
end_write_serial_link_connected();
+
+#ifndef DISABLE_SYNC_TIMER
+ *begin_write_sync_timer() = sync_timer_read32() + SYNC_TIMER_OFFSET;
+ end_write_sync_timer();
+#endif
}
matrix_object_t* m = read_keyboard_matrix(0);
if (m) {
matrix_set_remote(m->rows, 0);
}
+
+#ifndef DISABLE_SYNC_TIMER
+ uint32_t* t = read_sync_timer();
+ if (t) {
+ sync_timer_update(*t);
+ }
+#endif
}
void signal_data_written(void) { chEvtBroadcast(&new_data_event); }
diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c
deleted file mode 100644
index 039e7d9773..0000000000
--- a/quantum/split_common/matrix.c
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
-Copyright 2012 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-#include <stdint.h>
-#include <stdbool.h>
-#include "util.h"
-#include "matrix.h"
-#include "debounce.h"
-#include "quantum.h"
-#include "split_util.h"
-#include "config.h"
-#include "transport.h"
-
-#define ERROR_DISCONNECT_COUNT 5
-
-#define ROWS_PER_HAND (MATRIX_ROWS / 2)
-
-#ifdef DIRECT_PINS
-static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS;
-#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW)
-static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
-static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
-#endif
-
-/* matrix state(1:on, 0:off) */
-extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
-extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values
-
-// row offsets for each hand
-uint8_t thisHand, thatHand;
-
-// user-defined overridable functions
-__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); }
-__attribute__((weak)) void matrix_slave_scan_user(void) {}
-
-static inline void setPinOutput_writeLow(pin_t pin) {
- ATOMIC_BLOCK_FORCEON {
- setPinOutput(pin);
- writePinLow(pin);
- }
-}
-
-static inline void setPinInputHigh_atomic(pin_t pin) {
- ATOMIC_BLOCK_FORCEON { setPinInputHigh(pin); }
-}
-
-// matrix code
-
-#ifdef DIRECT_PINS
-
-static void init_pins(void) {
- for (int row = 0; row < MATRIX_ROWS; row++) {
- for (int col = 0; col < MATRIX_COLS; col++) {
- pin_t pin = direct_pins[row][col];
- if (pin != NO_PIN) {
- setPinInputHigh(pin);
- }
- }
- }
-}
-
-static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
- // Start with a clear matrix row
- matrix_row_t current_row_value = 0;
-
- for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
- pin_t pin = direct_pins[current_row][col_index];
- if (pin != NO_PIN) {
- current_row_value |= readPin(pin) ? 0 : (MATRIX_ROW_SHIFTER << col_index);
- }
- }
-
- // If the row has changed, store the row and return the changed flag.
- if (current_matrix[current_row] != current_row_value) {
- current_matrix[current_row] = current_row_value;
- return true;
- }
- return false;
-}
-
-#elif defined(DIODE_DIRECTION)
-# if (DIODE_DIRECTION == COL2ROW)
-
-static void select_row(uint8_t row) { setPinOutput_writeLow(row_pins[row]); }
-
-static void unselect_row(uint8_t row) { setPinInputHigh_atomic(row_pins[row]); }
-
-static void unselect_rows(void) {
- for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
- setPinInputHigh_atomic(row_pins[x]);
- }
-}
-
-static void init_pins(void) {
- unselect_rows();
- for (uint8_t x = 0; x < MATRIX_COLS; x++) {
- setPinInputHigh_atomic(col_pins[x]);
- }
-}
-
-static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
- // Start with a clear matrix row
- matrix_row_t current_row_value = 0;
-
- // Select row
- select_row(current_row);
- matrix_output_select_delay();
-
- // For each col...
- for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
- // Select the col pin to read (active low)
- uint8_t pin_state = readPin(col_pins[col_index]);
-
- // Populate the matrix row with the state of the col pin
- current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index);
- }
-
- // Unselect row
- unselect_row(current_row);
- matrix_output_unselect_delay(); // wait for all Col signals to go HIGH
-
- // If the row has changed, store the row and return the changed flag.
- if (current_matrix[current_row] != current_row_value) {
- current_matrix[current_row] = current_row_value;
- return true;
- }
- return false;
-}
-
-# elif (DIODE_DIRECTION == ROW2COL)
-
-static void select_col(uint8_t col) { setPinOutput_writeLow(col_pins[col]); }
-
-static void unselect_col(uint8_t col) { setPinInputHigh_atomic(col_pins[col]); }
-
-static void unselect_cols(void) {
- for (uint8_t x = 0; x < MATRIX_COLS; x++) {
- setPinInputHigh_atomic(col_pins[x]);
- }
-}
-
-static void init_pins(void) {
- unselect_cols();
- for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
- setPinInputHigh_atomic(row_pins[x]);
- }
-}
-
-static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
- bool matrix_changed = false;
-
- // Select col
- select_col(current_col);
- matrix_output_select_delay();
-
- // For each row...
- for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) {
- // Store last value of row prior to reading
- matrix_row_t last_row_value = current_matrix[row_index];
- matrix_row_t current_row_value = last_row_value;
-
- // Check row pin state
- if (readPin(row_pins[row_index]) == 0) {
- // Pin LO, set col bit
- current_row_value |= (MATRIX_ROW_SHIFTER << current_col);
- } else {
- // Pin HI, clear col bit
- current_row_value &= ~(MATRIX_ROW_SHIFTER << current_col);
- }
-
- // Determine if the matrix changed state
- if ((last_row_value != current_row_value)) {
- matrix_changed |= true;
- current_matrix[row_index] = current_row_value;
- }
- }
-
- // Unselect col
- unselect_col(current_col);
- matrix_output_unselect_delay(); // wait for all Row signals to go HIGH
-
- return matrix_changed;
-}
-
-# else
-# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL!
-# endif
-#else
-# error DIODE_DIRECTION is not defined!
-#endif
-
-void matrix_init(void) {
- split_pre_init();
-
- // Set pinout for right half if pinout for that half is defined
- if (!isLeftHand) {
-#ifdef DIRECT_PINS_RIGHT
- const pin_t direct_pins_right[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS_RIGHT;
- for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
- for (uint8_t j = 0; j < MATRIX_COLS; j++) {
- direct_pins[i][j] = direct_pins_right[i][j];
- }
- }
-#endif
-#ifdef MATRIX_ROW_PINS_RIGHT
- const pin_t row_pins_right[MATRIX_ROWS] = MATRIX_ROW_PINS_RIGHT;
- for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
- row_pins[i] = row_pins_right[i];
- }
-#endif
-#ifdef MATRIX_COL_PINS_RIGHT
- const pin_t col_pins_right[MATRIX_COLS] = MATRIX_COL_PINS_RIGHT;
- for (uint8_t i = 0; i < MATRIX_COLS; i++) {
- col_pins[i] = col_pins_right[i];
- }
-#endif
- }
-
- thisHand = isLeftHand ? 0 : (ROWS_PER_HAND);
- thatHand = ROWS_PER_HAND - thisHand;
-
- // initialize key pins
- init_pins();
-
- // initialize matrix state: all keys off
- for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
- raw_matrix[i] = 0;
- matrix[i] = 0;
- }
-
- debounce_init(ROWS_PER_HAND);
-
- matrix_init_quantum();
-
- split_post_init();
-}
-
-bool matrix_post_scan(void) {
- bool changed = false;
- if (is_keyboard_master()) {
- static uint8_t error_count;
-
- matrix_row_t slave_matrix[ROWS_PER_HAND] = {0};
- if (!transport_master(matrix + thisHand, slave_matrix)) {
- error_count++;
-
- if (error_count > ERROR_DISCONNECT_COUNT) {
- // reset other half if disconnected
- for (int i = 0; i < ROWS_PER_HAND; ++i) {
- matrix[thatHand + i] = 0;
- slave_matrix[i] = 0;
- }
-
- changed = true;
- }
- } else {
- error_count = 0;
-
- for (int i = 0; i < ROWS_PER_HAND; ++i) {
- if (matrix[thatHand + i] != slave_matrix[i]) {
- matrix[thatHand + i] = slave_matrix[i];
- changed = true;
- }
- }
- }
-
- matrix_scan_quantum();
- } else {
- transport_slave(matrix + thatHand, matrix + thisHand);
-
- matrix_slave_scan_kb();
- }
-
- return changed;
-}
-
-uint8_t matrix_scan(void) {
- bool local_changed = false;
-
-#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)
- // Set row, read cols
- for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
- local_changed |= read_cols_on_row(raw_matrix, current_row);
- }
-#elif (DIODE_DIRECTION == ROW2COL)
- // Set col, read rows
- for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
- local_changed |= read_rows_on_col(raw_matrix, current_col);
- }
-#endif
-
- debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed);
-
- bool remote_changed = matrix_post_scan();
- return (uint8_t)(local_changed || remote_changed);
-}
diff --git a/quantum/split_common/post_config.h b/quantum/split_common/post_config.h
index 4ae1d52732..a4c0a1956b 100644
--- a/quantum/split_common/post_config.h
+++ b/quantum/split_common/post_config.h
@@ -7,13 +7,4 @@
# ifndef F_SCL
# define F_SCL 100000UL // SCL frequency
# endif
-
-#else // use serial
-// When using serial, the user must define RGBLIGHT_SPLIT explicitly
-// in config.h as needed.
-// see quantum/rgblight_post_config.h
-# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
-// When using serial and RGBLIGHT_SPLIT need separate transaction
-# define SERIAL_USE_MULTI_TRANSACTION
-# endif
#endif
diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c
index 9e75e19ce0..8d414f6fe6 100644
--- a/quantum/split_common/split_util.c
+++ b/quantum/split_common/split_util.c
@@ -77,7 +77,11 @@ __attribute__((weak)) bool is_keyboard_left(void) {
#if defined(SPLIT_HAND_PIN)
// Test pin SPLIT_HAND_PIN for High/Low, if low it's right hand
setPinInput(SPLIT_HAND_PIN);
+# ifdef SPLIT_HAND_PIN_LOW_IS_LEFT
+ return !readPin(SPLIT_HAND_PIN);
+# else
return readPin(SPLIT_HAND_PIN);
+# endif
#elif defined(SPLIT_HAND_MATRIX_GRID)
# ifdef SPLIT_HAND_MATRIX_GRID_LOW_IS_RIGHT
return peek_matrix_intersection(SPLIT_HAND_MATRIX_GRID);
@@ -102,7 +106,7 @@ __attribute__((weak)) bool is_keyboard_master(void) {
// Avoid NO_USB_STARTUP_CHECK - Disable USB as the previous checks seem to enable it somehow
if (usbstate == SLAVE) {
- usb_disable();
+ usb_disconnect();
}
}
diff --git a/quantum/split_common/transaction_id_define.h b/quantum/split_common/transaction_id_define.h
new file mode 100644
index 0000000000..464c73478a
--- /dev/null
+++ b/quantum/split_common/transaction_id_define.h
@@ -0,0 +1,94 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+enum serial_transaction_id {
+#ifdef USE_I2C
+ I2C_EXECUTE_CALLBACK,
+#endif // USE_I2C
+
+ GET_SLAVE_MATRIX_CHECKSUM,
+ GET_SLAVE_MATRIX_DATA,
+
+#ifdef SPLIT_TRANSPORT_MIRROR
+ PUT_MASTER_MATRIX,
+#endif // SPLIT_TRANSPORT_MIRROR
+
+#ifdef ENCODER_ENABLE
+ GET_ENCODERS_CHECKSUM,
+ GET_ENCODERS_DATA,
+#endif // ENCODER_ENABLE
+
+#ifndef DISABLE_SYNC_TIMER
+ PUT_SYNC_TIMER,
+#endif // DISABLE_SYNC_TIMER
+
+#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
+ PUT_LAYER_STATE,
+ PUT_DEFAULT_LAYER_STATE,
+#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
+
+#ifdef SPLIT_LED_STATE_ENABLE
+ PUT_LED_STATE,
+#endif // SPLIT_LED_STATE_ENABLE
+
+#ifdef SPLIT_MODS_ENABLE
+ PUT_MODS,
+#endif // SPLIT_MODS_ENABLE
+
+#ifdef BACKLIGHT_ENABLE
+ PUT_BACKLIGHT,
+#endif // BACKLIGHT_ENABLE
+
+#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+ PUT_RGBLIGHT,
+#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+
+#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
+ PUT_LED_MATRIX,
+#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
+
+#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
+ PUT_RGB_MATRIX,
+#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+
+#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
+ PUT_WPM,
+#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
+
+#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+ PUT_RPC_INFO,
+ PUT_RPC_REQ_DATA,
+ EXECUTE_RPC,
+ GET_RPC_RESP_DATA,
+#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+
+// keyboard-specific
+#ifdef SPLIT_TRANSACTION_IDS_KB
+ SPLIT_TRANSACTION_IDS_KB,
+#endif // SPLIT_TRANSACTION_IDS_KB
+
+// user/keymap-specific
+#ifdef SPLIT_TRANSACTION_IDS_USER
+ SPLIT_TRANSACTION_IDS_USER,
+#endif // SPLIT_TRANSACTION_IDS_USER
+
+ NUM_TOTAL_TRANSACTIONS
+};
+
+// Ensure we only use 5 bits for transaction
+_Static_assert(NUM_TOTAL_TRANSACTIONS <= (1 << 5), "Max number of usable transactions exceeded");
diff --git a/quantum/split_common/transactions.c b/quantum/split_common/transactions.c
new file mode 100644
index 0000000000..abad626e00
--- /dev/null
+++ b/quantum/split_common/transactions.c
@@ -0,0 +1,655 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stddef.h>
+
+#include "crc.h"
+#include "debug.h"
+#include "matrix.h"
+#include "quantum.h"
+#include "transactions.h"
+#include "transport.h"
+#include "transaction_id_define.h"
+
+#define SYNC_TIMER_OFFSET 2
+
+#ifndef FORCED_SYNC_THROTTLE_MS
+# define FORCED_SYNC_THROTTLE_MS 100
+#endif // FORCED_SYNC_THROTTLE_MS
+
+#define sizeof_member(type, member) sizeof(((type *)NULL)->member)
+
+#define trans_initiator2target_initializer_cb(member, cb) \
+ { &dummy, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb }
+#define trans_initiator2target_initializer(member) trans_initiator2target_initializer_cb(member, NULL)
+
+#define trans_target2initiator_initializer_cb(member, cb) \
+ { &dummy, 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb }
+#define trans_target2initiator_initializer(member) trans_target2initiator_initializer_cb(member, NULL)
+
+#define transport_write(id, data, length) transport_execute_transaction(id, data, length, NULL, 0)
+#define transport_read(id, data, length) transport_execute_transaction(id, NULL, 0, data, length)
+
+#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+// Forward-declare the RPC callback handlers
+void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
+void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
+#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+
+////////////////////////////////////////////////////
+// Helpers
+
+bool transaction_handler_master(bool okay, matrix_row_t master_matrix[], matrix_row_t slave_matrix[], const char *prefix, bool (*handler)(matrix_row_t master_matrix[], matrix_row_t slave_matrix[])) {
+ if (okay) {
+ bool this_okay = true;
+ for (int iter = 1; iter <= 10; ++iter) {
+ if (!this_okay) {
+ for (int i = 0; i < iter * iter; ++i) {
+ wait_us(10);
+ }
+ }
+ ATOMIC_BLOCK_FORCEON { this_okay = handler(master_matrix, slave_matrix); };
+ if (this_okay) break;
+ }
+ okay &= this_okay;
+ if (!okay) {
+ dprintf("Failed to execute %s\n", prefix);
+ }
+ }
+ return okay;
+}
+
+#define TRANSACTION_HANDLER_MASTER(prefix) \
+ do { \
+ okay &= transaction_handler_master(okay, master_matrix, slave_matrix, #prefix, &prefix##_master); \
+ } while (0)
+
+#define TRANSACTION_HANDLER_SLAVE(prefix) \
+ do { \
+ ATOMIC_BLOCK_FORCEON { prefix##_slave(master_matrix, slave_matrix); }; \
+ } while (0)
+
+inline static bool read_if_checksum_mismatch(int8_t trans_id_checksum, int8_t trans_id_retrieve, uint32_t *last_update, void *destination, const void *equiv_shmem, size_t length) {
+ uint8_t curr_checksum;
+ bool okay = transport_read(trans_id_checksum, &curr_checksum, sizeof(curr_checksum));
+ if (okay && (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || curr_checksum != crc8(equiv_shmem, length))) {
+ okay &= transport_read(trans_id_retrieve, destination, length);
+ okay &= curr_checksum == crc8(equiv_shmem, length);
+ if (okay) {
+ *last_update = timer_read32();
+ }
+ } else {
+ memcpy(destination, equiv_shmem, length);
+ }
+ return okay;
+}
+
+inline static bool send_if_condition(int8_t trans_id, uint32_t *last_update, bool condition, void *source, size_t length) {
+ bool okay = true;
+ if (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || condition) {
+ okay &= transport_write(trans_id, source, length);
+ if (okay) {
+ *last_update = timer_read32();
+ }
+ }
+ return okay;
+}
+
+inline static bool send_if_data_mismatch(int8_t trans_id, uint32_t *last_update, void *source, const void *equiv_shmem, size_t length) {
+ // Just run a memcmp to compare the source and equivalent shmem location
+ return send_if_condition(trans_id, last_update, (memcmp(source, equiv_shmem, length) != 0), source, length);
+}
+
+////////////////////////////////////////////////////
+// Slave matrix
+
+static bool slave_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ static matrix_row_t last_matrix[(MATRIX_ROWS) / 2] = {0}; // last successfully-read matrix, so we can replicate if there are checksum errors
+ matrix_row_t temp_matrix[(MATRIX_ROWS) / 2]; // holding area while we test whether or not checksum is correct
+
+ bool okay = read_if_checksum_mismatch(GET_SLAVE_MATRIX_CHECKSUM, GET_SLAVE_MATRIX_DATA, &last_update, temp_matrix, split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix));
+ if (okay) {
+ // Checksum matches the received data, save as the last matrix state
+ memcpy(last_matrix, temp_matrix, sizeof(temp_matrix));
+ }
+ // Copy out the last-known-good matrix state to the slave matrix
+ memcpy(slave_matrix, last_matrix, sizeof(last_matrix));
+ return okay;
+}
+
+static void slave_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ memcpy(split_shmem->smatrix.matrix, slave_matrix, sizeof(split_shmem->smatrix.matrix));
+ split_shmem->smatrix.checksum = crc8(split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix));
+}
+
+// clang-format off
+#define TRANSACTIONS_SLAVE_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(slave_matrix_handlers)
+#define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(slave_matrix_handlers)
+#define TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS \
+ [GET_SLAVE_MATRIX_CHECKSUM] = trans_target2initiator_initializer(smatrix.checksum), \
+ [GET_SLAVE_MATRIX_DATA] = trans_target2initiator_initializer(smatrix.matrix),
+// clang-format on
+
+////////////////////////////////////////////////////
+// Master matrix
+
+#ifdef SPLIT_TRANSPORT_MIRROR
+
+static bool master_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ return send_if_data_mismatch(PUT_MASTER_MATRIX, &last_update, master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
+}
+
+static void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ // Always copy to the master matrix
+ memcpy(master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));
+}
+
+# define TRANSACTIONS_MASTER_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(master_matrix_handlers)
+# define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(master_matrix_handlers)
+# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS [PUT_MASTER_MATRIX] = trans_initiator2target_initializer(mmatrix.matrix),
+
+#else // SPLIT_TRANSPORT_MIRROR
+
+# define TRANSACTIONS_MASTER_MATRIX_MASTER()
+# define TRANSACTIONS_MASTER_MATRIX_SLAVE()
+# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
+
+#endif // SPLIT_TRANSPORT_MIRROR
+
+////////////////////////////////////////////////////
+// Encoders
+
+#ifdef ENCODER_ENABLE
+
+static bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ uint8_t temp_state[NUMBER_OF_ENCODERS];
+
+ bool okay = read_if_checksum_mismatch(GET_ENCODERS_CHECKSUM, GET_ENCODERS_DATA, &last_update, temp_state, split_shmem->encoders.state, sizeof(temp_state));
+ if (okay) encoder_update_raw(temp_state);
+ return okay;
+}
+
+static void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ uint8_t encoder_state[NUMBER_OF_ENCODERS];
+ encoder_state_raw(encoder_state);
+ // Always prepare the encoder state for read.
+ memcpy(split_shmem->encoders.state, encoder_state, sizeof(encoder_state));
+ // Now update the checksum given that the encoders has been written to
+ split_shmem->encoders.checksum = crc8(encoder_state, sizeof(encoder_state));
+}
+
+// clang-format off
+# define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder_handlers)
+# define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder_handlers)
+# define TRANSACTIONS_ENCODERS_REGISTRATIONS \
+ [GET_ENCODERS_CHECKSUM] = trans_target2initiator_initializer(encoders.checksum), \
+ [GET_ENCODERS_DATA] = trans_target2initiator_initializer(encoders.state),
+// clang-format on
+
+#else // ENCODER_ENABLE
+
+# define TRANSACTIONS_ENCODERS_MASTER()
+# define TRANSACTIONS_ENCODERS_SLAVE()
+# define TRANSACTIONS_ENCODERS_REGISTRATIONS
+
+#endif // ENCODER_ENABLE
+
+////////////////////////////////////////////////////
+// Sync timer
+
+#ifndef DISABLE_SYNC_TIMER
+
+static bool sync_timer_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+
+ bool okay = true;
+ if (timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS) {
+ uint32_t sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
+ okay &= transport_write(PUT_SYNC_TIMER, &sync_timer, sizeof(sync_timer));
+ if (okay) {
+ last_update = timer_read32();
+ }
+ }
+ return okay;
+}
+
+static void sync_timer_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_sync_timer = 0;
+ if (last_sync_timer != split_shmem->sync_timer) {
+ last_sync_timer = split_shmem->sync_timer;
+ sync_timer_update(last_sync_timer);
+ }
+}
+
+# define TRANSACTIONS_SYNC_TIMER_MASTER() TRANSACTION_HANDLER_MASTER(sync_timer_handlers)
+# define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE(sync_timer_handlers)
+# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS [PUT_SYNC_TIMER] = trans_initiator2target_initializer(sync_timer),
+
+#else // DISABLE_SYNC_TIMER
+
+# define TRANSACTIONS_SYNC_TIMER_MASTER()
+# define TRANSACTIONS_SYNC_TIMER_SLAVE()
+# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
+
+#endif // DISABLE_SYNC_TIMER
+
+////////////////////////////////////////////////////
+// Layer state
+
+#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
+
+static bool layer_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_layer_state_update = 0;
+ static uint32_t last_default_layer_state_update = 0;
+
+ bool okay = send_if_condition(PUT_LAYER_STATE, &last_layer_state_update, (layer_state != split_shmem->layers.layer_state), &layer_state, sizeof(layer_state));
+ if (okay) {
+ okay &= send_if_condition(PUT_DEFAULT_LAYER_STATE, &last_default_layer_state_update, (default_layer_state != split_shmem->layers.default_layer_state), &default_layer_state, sizeof(default_layer_state));
+ }
+ return okay;
+}
+
+static void layer_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ layer_state = split_shmem->layers.layer_state;
+ default_layer_state = split_shmem->layers.default_layer_state;
+}
+
+// clang-format off
+# define TRANSACTIONS_LAYER_STATE_MASTER() TRANSACTION_HANDLER_MASTER(layer_state_handlers)
+# define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(layer_state_handlers)
+# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS \
+ [PUT_LAYER_STATE] = trans_initiator2target_initializer(layers.layer_state), \
+ [PUT_DEFAULT_LAYER_STATE] = trans_initiator2target_initializer(layers.default_layer_state),
+// clang-format on
+
+#else // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
+
+# define TRANSACTIONS_LAYER_STATE_MASTER()
+# define TRANSACTIONS_LAYER_STATE_SLAVE()
+# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS
+
+#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
+
+////////////////////////////////////////////////////
+// LED state
+
+#ifdef SPLIT_LED_STATE_ENABLE
+
+static bool led_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ uint8_t led_state = host_keyboard_leds();
+ return send_if_data_mismatch(PUT_LED_STATE, &last_update, &led_state, &split_shmem->led_state, sizeof(led_state));
+}
+
+static void led_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ void set_split_host_keyboard_leds(uint8_t led_state);
+ set_split_host_keyboard_leds(split_shmem->led_state);
+}
+
+# define TRANSACTIONS_LED_STATE_MASTER() TRANSACTION_HANDLER_MASTER(led_state_handlers)
+# define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(led_state_handlers)
+# define TRANSACTIONS_LED_STATE_REGISTRATIONS [PUT_LED_STATE] = trans_initiator2target_initializer(led_state),
+
+#else // SPLIT_LED_STATE_ENABLE
+
+# define TRANSACTIONS_LED_STATE_MASTER()
+# define TRANSACTIONS_LED_STATE_SLAVE()
+# define TRANSACTIONS_LED_STATE_REGISTRATIONS
+
+#endif // SPLIT_LED_STATE_ENABLE
+
+////////////////////////////////////////////////////
+// Mods
+
+#ifdef SPLIT_MODS_ENABLE
+
+static bool mods_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ bool mods_need_sync = timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS;
+ split_mods_sync_t new_mods;
+ new_mods.real_mods = get_mods();
+ if (!mods_need_sync && new_mods.real_mods != split_shmem->mods.real_mods) {
+ mods_need_sync = true;
+ }
+
+ new_mods.weak_mods = get_weak_mods();
+ if (!mods_need_sync && new_mods.weak_mods != split_shmem->mods.weak_mods) {
+ mods_need_sync = true;
+ }
+
+# ifndef NO_ACTION_ONESHOT
+ new_mods.oneshot_mods = get_oneshot_mods();
+ if (!mods_need_sync && new_mods.oneshot_mods != split_shmem->mods.oneshot_mods) {
+ mods_need_sync = true;
+ }
+# endif // NO_ACTION_ONESHOT
+
+ bool okay = true;
+ if (mods_need_sync) {
+ okay &= transport_write(PUT_MODS, &new_mods, sizeof(new_mods));
+ if (okay) {
+ last_update = timer_read32();
+ }
+ }
+
+ return okay;
+}
+
+static void mods_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ set_mods(split_shmem->mods.real_mods);
+ set_weak_mods(split_shmem->mods.weak_mods);
+# ifndef NO_ACTION_ONESHOT
+ set_oneshot_mods(split_shmem->mods.oneshot_mods);
+# endif
+}
+
+# define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods_handlers)
+# define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods_handlers)
+# define TRANSACTIONS_MODS_REGISTRATIONS [PUT_MODS] = trans_initiator2target_initializer(mods),
+
+#else // SPLIT_MODS_ENABLE
+
+# define TRANSACTIONS_MODS_MASTER()
+# define TRANSACTIONS_MODS_SLAVE()
+# define TRANSACTIONS_MODS_REGISTRATIONS
+
+#endif // SPLIT_MODS_ENABLE
+
+////////////////////////////////////////////////////
+// Backlight
+
+#ifdef BACKLIGHT_ENABLE
+
+static bool backlight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
+ return send_if_condition(PUT_BACKLIGHT, &last_update, (level != split_shmem->backlight_level), &level, sizeof(level));
+}
+
+static void backlight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { backlight_set(split_shmem->backlight_level); }
+
+# define TRANSACTIONS_BACKLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(backlight_handlers)
+# define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight_handlers)
+# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS [PUT_BACKLIGHT] = trans_initiator2target_initializer(backlight_level),
+
+#else // BACKLIGHT_ENABLE
+
+# define TRANSACTIONS_BACKLIGHT_MASTER()
+# define TRANSACTIONS_BACKLIGHT_SLAVE()
+# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS
+
+#endif // BACKLIGHT_ENABLE
+
+////////////////////////////////////////////////////
+// RGBLIGHT
+
+#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+
+static bool rgblight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ rgblight_syncinfo_t rgblight_sync;
+ rgblight_get_syncinfo(&rgblight_sync);
+ if (send_if_condition(PUT_RGBLIGHT, &last_update, (rgblight_sync.status.change_flags != 0), &rgblight_sync, sizeof(rgblight_sync))) {
+ rgblight_clear_change_flags();
+ } else {
+ return false;
+ }
+ return true;
+}
+
+static void rgblight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ // Update the RGB with the new data
+ if (split_shmem->rgblight_sync.status.change_flags != 0) {
+ rgblight_update_sync(&split_shmem->rgblight_sync, false);
+ split_shmem->rgblight_sync.status.change_flags = 0;
+ }
+}
+
+# define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight_handlers)
+# define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight_handlers)
+# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS [PUT_RGBLIGHT] = trans_initiator2target_initializer(rgblight_sync),
+
+#else // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+
+# define TRANSACTIONS_RGBLIGHT_MASTER()
+# define TRANSACTIONS_RGBLIGHT_SLAVE()
+# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS
+
+#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+
+////////////////////////////////////////////////////
+// LED Matrix
+
+#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
+
+static bool led_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ led_matrix_sync_t led_matrix_sync;
+ memcpy(&led_matrix_sync.led_matrix, &led_matrix_eeconfig, sizeof(led_eeconfig_t));
+ led_matrix_sync.led_suspend_state = led_matrix_get_suspend_state();
+ return send_if_data_mismatch(PUT_LED_MATRIX, &last_update, &led_matrix_sync, &split_shmem->led_matrix_sync, sizeof(led_matrix_sync));
+}
+
+static void led_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ memcpy(&led_matrix_eeconfig, &split_shmem->led_matrix_sync.led_matrix, sizeof(led_eeconfig_t));
+ led_matrix_set_suspend_state(split_shmem->led_matrix_sync.led_suspend_state);
+}
+
+# define TRANSACTIONS_LED_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(led_matrix_handlers)
+# define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix_handlers)
+# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS [PUT_LED_MATRIX] = trans_initiator2target_initializer(led_matrix_sync),
+
+#else // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
+
+# define TRANSACTIONS_LED_MATRIX_MASTER()
+# define TRANSACTIONS_LED_MATRIX_SLAVE()
+# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS
+
+#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
+
+////////////////////////////////////////////////////
+// RGB Matrix
+
+#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
+
+static bool rgb_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ rgb_matrix_sync_t rgb_matrix_sync;
+ memcpy(&rgb_matrix_sync.rgb_matrix, &rgb_matrix_config, sizeof(rgb_config_t));
+ rgb_matrix_sync.rgb_suspend_state = rgb_matrix_get_suspend_state();
+ return send_if_data_mismatch(PUT_RGB_MATRIX, &last_update, &rgb_matrix_sync, &split_shmem->rgb_matrix_sync, sizeof(rgb_matrix_sync));
+}
+
+static void rgb_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ memcpy(&rgb_matrix_config, &split_shmem->rgb_matrix_sync.rgb_matrix, sizeof(rgb_config_t));
+ rgb_matrix_set_suspend_state(split_shmem->rgb_matrix_sync.rgb_suspend_state);
+}
+
+# define TRANSACTIONS_RGB_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(rgb_matrix_handlers)
+# define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix_handlers)
+# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS [PUT_RGB_MATRIX] = trans_initiator2target_initializer(rgb_matrix_sync),
+
+#else // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
+
+# define TRANSACTIONS_RGB_MATRIX_MASTER()
+# define TRANSACTIONS_RGB_MATRIX_SLAVE()
+# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
+
+#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
+
+////////////////////////////////////////////////////
+// WPM
+
+#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
+
+static bool wpm_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ uint8_t current_wpm = get_current_wpm();
+ return send_if_condition(PUT_WPM, &last_update, (current_wpm != split_shmem->current_wpm), &current_wpm, sizeof(current_wpm));
+}
+
+static void wpm_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { set_current_wpm(split_shmem->current_wpm); }
+
+# define TRANSACTIONS_WPM_MASTER() TRANSACTION_HANDLER_MASTER(wpm_handlers)
+# define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE(wpm_handlers)
+# define TRANSACTIONS_WPM_REGISTRATIONS [PUT_WPM] = trans_initiator2target_initializer(current_wpm),
+
+#else // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
+
+# define TRANSACTIONS_WPM_MASTER()
+# define TRANSACTIONS_WPM_SLAVE()
+# define TRANSACTIONS_WPM_REGISTRATIONS
+
+#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
+
+////////////////////////////////////////////////////
+
+uint8_t dummy;
+split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = {
+ // Set defaults
+ [0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {NULL, 0, 0, 0, 0, 0},
+
+#ifdef USE_I2C
+ [I2C_EXECUTE_CALLBACK] = trans_initiator2target_initializer(transaction_id),
+#endif // USE_I2C
+
+ // clang-format off
+ TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS
+ TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS
+ TRANSACTIONS_ENCODERS_REGISTRATIONS
+ TRANSACTIONS_SYNC_TIMER_REGISTRATIONS
+ TRANSACTIONS_LAYER_STATE_REGISTRATIONS
+ TRANSACTIONS_LED_STATE_REGISTRATIONS
+ TRANSACTIONS_MODS_REGISTRATIONS
+ TRANSACTIONS_BACKLIGHT_REGISTRATIONS
+ TRANSACTIONS_RGBLIGHT_REGISTRATIONS
+ TRANSACTIONS_LED_MATRIX_REGISTRATIONS
+ TRANSACTIONS_RGB_MATRIX_REGISTRATIONS
+ TRANSACTIONS_WPM_REGISTRATIONS
+// clang-format on
+
+#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+ [PUT_RPC_INFO] = trans_initiator2target_initializer_cb(rpc_info, slave_rpc_info_callback),
+ [PUT_RPC_REQ_DATA] = trans_initiator2target_initializer(rpc_m2s_buffer),
+ [EXECUTE_RPC] = trans_initiator2target_initializer_cb(rpc_info.transaction_id, slave_rpc_exec_callback),
+ [GET_RPC_RESP_DATA] = trans_target2initiator_initializer(rpc_s2m_buffer),
+#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+};
+
+bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ bool okay = true;
+ TRANSACTIONS_SLAVE_MATRIX_MASTER();
+ TRANSACTIONS_MASTER_MATRIX_MASTER();
+ TRANSACTIONS_ENCODERS_MASTER();
+ TRANSACTIONS_SYNC_TIMER_MASTER();
+ TRANSACTIONS_LAYER_STATE_MASTER();
+ TRANSACTIONS_LED_STATE_MASTER();
+ TRANSACTIONS_MODS_MASTER();
+ TRANSACTIONS_BACKLIGHT_MASTER();
+ TRANSACTIONS_RGBLIGHT_MASTER();
+ TRANSACTIONS_LED_MATRIX_MASTER();
+ TRANSACTIONS_RGB_MATRIX_MASTER();
+ TRANSACTIONS_WPM_MASTER();
+ return okay;
+}
+
+void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ TRANSACTIONS_SLAVE_MATRIX_SLAVE();
+ TRANSACTIONS_MASTER_MATRIX_SLAVE();
+ TRANSACTIONS_ENCODERS_SLAVE();
+ TRANSACTIONS_SYNC_TIMER_SLAVE();
+ TRANSACTIONS_LAYER_STATE_SLAVE();
+ TRANSACTIONS_LED_STATE_SLAVE();
+ TRANSACTIONS_MODS_SLAVE();
+ TRANSACTIONS_BACKLIGHT_SLAVE();
+ TRANSACTIONS_RGBLIGHT_SLAVE();
+ TRANSACTIONS_LED_MATRIX_SLAVE();
+ TRANSACTIONS_RGB_MATRIX_SLAVE();
+ TRANSACTIONS_WPM_SLAVE();
+}
+
+#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+
+void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback) {
+ // Prevent invoking RPC on QMK core sync data
+ if (transaction_id <= GET_RPC_RESP_DATA) return;
+
+ // Set the callback
+ split_transaction_table[transaction_id].slave_callback = callback;
+ split_transaction_table[transaction_id].initiator2target_offset = offsetof(split_shared_memory_t, rpc_m2s_buffer);
+ split_transaction_table[transaction_id].target2initiator_offset = offsetof(split_shared_memory_t, rpc_s2m_buffer);
+}
+
+bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
+ // Prevent invoking RPC on QMK core sync data
+ if (transaction_id <= GET_RPC_RESP_DATA) return false;
+ // Prevent sizing issues
+ if (initiator2target_buffer_size > RPC_M2S_BUFFER_SIZE) return false;
+ if (target2initiator_buffer_size > RPC_S2M_BUFFER_SIZE) return false;
+
+ // Prepare the metadata block
+ rpc_sync_info_t info = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size};
+
+ // Make sure the local side knows that we're not sending the full block of data
+ split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = initiator2target_buffer_size;
+ split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = target2initiator_buffer_size;
+
+ // Run through the sequence:
+ // * set the transaction ID and lengths
+ // * send the request data
+ // * execute RPC callback
+ // * retrieve the response data
+ if (!transport_write(PUT_RPC_INFO, &info, sizeof(info))) {
+ return false;
+ }
+ if (!transport_write(PUT_RPC_REQ_DATA, initiator2target_buffer, initiator2target_buffer_size)) {
+ return false;
+ }
+ if (!transport_write(EXECUTE_RPC, &transaction_id, sizeof(transaction_id))) {
+ return false;
+ }
+ if (!transport_read(GET_RPC_RESP_DATA, target2initiator_buffer, target2initiator_buffer_size)) {
+ return false;
+ }
+ return true;
+}
+
+void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
+ // The RPC info block contains the intended transaction ID, as well as the sizes for both inbound and outbound data.
+ // Ignore the args -- the `split_shmem` already has the info, we just need to act upon it.
+ // We must keep the `split_transaction_table` non-const, so that it is able to be modified at runtime.
+
+ split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = split_shmem->rpc_info.m2s_length;
+ split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.s2m_length;
+}
+
+void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {
+ // We can assume that the buffer lengths are correctly set, now, given that sequentially the rpc_info callback was already executed.
+ // Go through the rpc_info and execute _that_ transaction's callback, with the scratch buffers as inputs.
+ int8_t transaction_id = split_shmem->rpc_info.transaction_id;
+ if (transaction_id < NUM_TOTAL_TRANSACTIONS) {
+ split_transaction_desc_t *trans = &split_transaction_table[transaction_id];
+ if (trans->slave_callback) {
+ trans->slave_callback(split_shmem->rpc_info.m2s_length, split_shmem->rpc_m2s_buffer, split_shmem->rpc_info.s2m_length, split_shmem->rpc_s2m_buffer);
+ }
+ }
+}
+
+#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
diff --git a/quantum/split_common/transactions.h b/quantum/split_common/transactions.h
new file mode 100644
index 0000000000..4306ba1d87
--- /dev/null
+++ b/quantum/split_common/transactions.h
@@ -0,0 +1,54 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "stdint.h"
+#include "stdbool.h"
+
+#include "matrix.h"
+#include "transaction_id_define.h"
+#include "transport.h"
+
+typedef void (*slave_callback_t)(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
+
+// Split transaction Descriptor
+typedef struct _split_transaction_desc_t {
+ uint8_t * status;
+ uint8_t initiator2target_buffer_size;
+ uint16_t initiator2target_offset;
+ uint8_t target2initiator_buffer_size;
+ uint16_t target2initiator_offset;
+ slave_callback_t slave_callback;
+} split_transaction_desc_t;
+
+// Forward declaration for the split transactions
+extern split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS];
+
+#define split_shmem_offset_ptr(offset) ((void *)(((uint8_t *)split_shmem) + (offset)))
+#define split_trans_initiator2target_buffer(trans) (split_shmem_offset_ptr((trans)->initiator2target_offset))
+#define split_trans_target2initiator_buffer(trans) (split_shmem_offset_ptr((trans)->target2initiator_offset))
+
+// returns false if valid data not received from slave
+bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
+void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
+
+void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback);
+
+bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
+
+#define transaction_rpc_send(transaction_id, initiator2target_buffer_size, initiator2target_buffer) transaction_rpc_exec(transaction_id, initiator2target_buffer_size, initiator2target_buffer, 0, NULL)
+#define transaction_rpc_recv(transaction_id, target2initiator_buffer_size, target2initiator_buffer) transaction_rpc_exec(transaction_id, 0, NULL, target2initiator_buffer_size, target2initiator_buffer)
diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c
index 9ed0f7591b..a711ef85f0 100644
--- a/quantum/split_common/transport.c
+++ b/quantum/split_common/transport.c
@@ -1,452 +1,118 @@
-#include <string.h>
-#include <stddef.h>
-
-#include "config.h"
-#include "matrix.h"
-#include "quantum.h"
-
-#define ROWS_PER_HAND (MATRIX_ROWS / 2)
-#define SYNC_TIMER_OFFSET 2
-
-#ifdef RGBLIGHT_ENABLE
-# include "rgblight.h"
-#endif
-
-#ifdef BACKLIGHT_ENABLE
-# include "backlight.h"
-#endif
-
-#ifdef ENCODER_ENABLE
-# include "encoder.h"
-static pin_t encoders_pad[] = ENCODERS_PAD_A;
-# define NUMBER_OF_ENCODERS (sizeof(encoders_pad) / sizeof(pin_t))
-#endif
-
-#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
-# include "led_matrix.h"
-#endif
-#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
-# include "rgb_matrix.h"
-#endif
-
-#if defined(USE_I2C)
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
-# include "i2c_master.h"
-# include "i2c_slave.h"
-
-typedef struct _I2C_slave_buffer_t {
-# ifndef DISABLE_SYNC_TIMER
- uint32_t sync_timer;
-# endif
-# ifdef SPLIT_TRANSPORT_MIRROR
- matrix_row_t mmatrix[ROWS_PER_HAND];
-# endif
- matrix_row_t smatrix[ROWS_PER_HAND];
-# ifdef SPLIT_MODS_ENABLE
- uint8_t real_mods;
- uint8_t weak_mods;
-# ifndef NO_ACTION_ONESHOT
- uint8_t oneshot_mods;
-# endif
-# endif
-# ifdef BACKLIGHT_ENABLE
- uint8_t backlight_level;
-# endif
-# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
- rgblight_syncinfo_t rgblight_sync;
-# endif
-# ifdef ENCODER_ENABLE
- uint8_t encoder_state[NUMBER_OF_ENCODERS];
-# endif
-# ifdef WPM_ENABLE
- uint8_t current_wpm;
-# endif
-# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
- led_eeconfig_t led_matrix;
- bool led_suspend_state;
-# endif
-# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
- rgb_config_t rgb_matrix;
- bool rgb_suspend_state;
-# endif
-} I2C_slave_buffer_t;
+#include <string.h>
+#include <debug.h>
-static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg;
+#include "transactions.h"
+#include "transport.h"
+#include "transaction_id_define.h"
+#include "atomic_util.h"
-# define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer)
-# define I2C_KEYMAP_MASTER_START offsetof(I2C_slave_buffer_t, mmatrix)
-# define I2C_KEYMAP_SLAVE_START offsetof(I2C_slave_buffer_t, smatrix)
-# define I2C_REAL_MODS_START offsetof(I2C_slave_buffer_t, real_mods)
-# define I2C_WEAK_MODS_START offsetof(I2C_slave_buffer_t, weak_mods)
-# define I2C_ONESHOT_MODS_START offsetof(I2C_slave_buffer_t, oneshot_mods)
-# define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level)
-# define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync)
-# define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state)
-# define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm)
-# define I2C_LED_MATRIX_START offsetof(I2C_slave_buffer_t, led_matrix)
-# define I2C_LED_SUSPEND_START offsetof(I2C_slave_buffer_t, led_suspend_state)
-# define I2C_RGB_MATRIX_START offsetof(I2C_slave_buffer_t, rgb_matrix)
-# define I2C_RGB_SUSPEND_START offsetof(I2C_slave_buffer_t, rgb_suspend_state)
+#ifdef USE_I2C
-# define TIMEOUT 100
+# ifndef SLAVE_I2C_TIMEOUT
+# define SLAVE_I2C_TIMEOUT 100
+# endif // SLAVE_I2C_TIMEOUT
# ifndef SLAVE_I2C_ADDRESS
# define SLAVE_I2C_ADDRESS 0x32
# endif
-// Get rows from other half over i2c
-bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
- i2c_readReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_SLAVE_START, (void *)slave_matrix, sizeof(i2c_buffer->smatrix), TIMEOUT);
-# ifdef SPLIT_TRANSPORT_MIRROR
- i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_MASTER_START, (void *)master_matrix, sizeof(i2c_buffer->mmatrix), TIMEOUT);
-# endif
+# include "i2c_master.h"
+# include "i2c_slave.h"
- // write backlight info
-# ifdef BACKLIGHT_ENABLE
- uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
- if (level != i2c_buffer->backlight_level) {
- if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_BACKLIGHT_START, (void *)&level, sizeof(level), TIMEOUT) >= 0) {
- i2c_buffer->backlight_level = level;
- }
- }
-# endif
+// Ensure the I2C buffer has enough space
+_Static_assert(sizeof(split_shared_memory_t) <= I2C_SLAVE_REG_COUNT, "split_shared_memory_t too large for I2C_SLAVE_REG_COUNT");
-# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
- if (rgblight_get_change_flags()) {
- rgblight_syncinfo_t rgblight_sync;
- rgblight_get_syncinfo(&rgblight_sync);
- if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_START, (void *)&rgblight_sync, sizeof(rgblight_sync), TIMEOUT) >= 0) {
- rgblight_clear_change_flags();
- }
- }
-# endif
+split_shared_memory_t *const split_shmem = (split_shared_memory_t *)i2c_slave_reg;
-# ifdef ENCODER_ENABLE
- i2c_readReg(SLAVE_I2C_ADDRESS, I2C_ENCODER_START, (void *)i2c_buffer->encoder_state, sizeof(i2c_buffer->encoder_state), TIMEOUT);
- encoder_update_raw(i2c_buffer->encoder_state);
-# endif
+void transport_master_init(void) { i2c_init(); }
+void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); }
-# ifdef WPM_ENABLE
- uint8_t current_wpm = get_current_wpm();
- if (current_wpm != i2c_buffer->current_wpm) {
- if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WPM_START, (void *)&current_wpm, sizeof(current_wpm), TIMEOUT) >= 0) {
- i2c_buffer->current_wpm = current_wpm;
- }
+i2c_status_t transport_trigger_callback(int8_t id) {
+ // If there's no callback, indicate that we were successful
+ if (!split_transaction_table[id].slave_callback) {
+ return I2C_STATUS_SUCCESS;
}
-# endif
-# ifdef SPLIT_MODS_ENABLE
- uint8_t real_mods = get_mods();
- if (real_mods != i2c_buffer->real_mods) {
- if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_REAL_MODS_START, (void *)&real_mods, sizeof(real_mods), TIMEOUT) >= 0) {
- i2c_buffer->real_mods = real_mods;
+ // Kick off the "callback executor", now that data has been written to the slave
+ split_shmem->transaction_id = id;
+ split_transaction_desc_t *trans = &split_transaction_table[I2C_EXECUTE_CALLBACK];
+ return i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, SLAVE_I2C_TIMEOUT);
+}
+
+bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) {
+ i2c_status_t status;
+ split_transaction_desc_t *trans = &split_transaction_table[id];
+ if (initiator2target_length > 0) {
+ size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length;
+ memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len);
+ if ((status = i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) {
+ return false;
}
}
- uint8_t weak_mods = get_weak_mods();
- if (weak_mods != i2c_buffer->weak_mods) {
- if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WEAK_MODS_START, (void *)&weak_mods, sizeof(weak_mods), TIMEOUT) >= 0) {
- i2c_buffer->weak_mods = weak_mods;
- }
+ // If we need to execute a callback on the slave, do so
+ if ((status = transport_trigger_callback(id)) < 0) {
+ return false;
}
-# ifndef NO_ACTION_ONESHOT
- uint8_t oneshot_mods = get_oneshot_mods();
- if (oneshot_mods != i2c_buffer->oneshot_mods) {
- if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_ONESHOT_MODS_START, (void *)&oneshot_mods, sizeof(oneshot_mods), TIMEOUT) >= 0) {
- i2c_buffer->oneshot_mods = oneshot_mods;
+ if (target2initiator_length > 0) {
+ size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length;
+ if ((status = i2c_readReg(SLAVE_I2C_ADDRESS, trans->target2initiator_offset, split_trans_target2initiator_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) {
+ return false;
}
+ memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len);
}
-# endif
-# endif
-# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
- i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_MATRIX_START, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix), TIMEOUT);
- bool suspend_state = led_matrix_get_suspend_state();
- i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->led_suspend_state), TIMEOUT);
-# endif
-# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
- i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_MATRIX_START, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix), TIMEOUT);
- bool suspend_state = rgb_matrix_get_suspend_state();
- i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->rgb_suspend_state), TIMEOUT);
-# endif
-
-# ifndef DISABLE_SYNC_TIMER
- i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
- i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT);
-# endif
return true;
}
-void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
-# ifndef DISABLE_SYNC_TIMER
- sync_timer_update(i2c_buffer->sync_timer);
-# endif
- // Copy matrix to I2C buffer
- memcpy((void *)i2c_buffer->smatrix, (void *)slave_matrix, sizeof(i2c_buffer->smatrix));
-# ifdef SPLIT_TRANSPORT_MIRROR
- memcpy((void *)master_matrix, (void *)i2c_buffer->mmatrix, sizeof(i2c_buffer->mmatrix));
-# endif
-
-// Read Backlight Info
-# ifdef BACKLIGHT_ENABLE
- backlight_set(i2c_buffer->backlight_level);
-# endif
-
-# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
- // Update the RGB with the new data
- if (i2c_buffer->rgblight_sync.status.change_flags != 0) {
- rgblight_update_sync(&i2c_buffer->rgblight_sync, false);
- i2c_buffer->rgblight_sync.status.change_flags = 0;
- }
-# endif
-
-# ifdef ENCODER_ENABLE
- encoder_state_raw(i2c_buffer->encoder_state);
-# endif
-
-# ifdef WPM_ENABLE
- set_current_wpm(i2c_buffer->current_wpm);
-# endif
-
-# ifdef SPLIT_MODS_ENABLE
- set_mods(i2c_buffer->real_mods);
- set_weak_mods(i2c_buffer->weak_mods);
-# ifndef NO_ACTION_ONESHOT
- set_oneshot_mods(i2c_buffer->oneshot_mods);
-# endif
-# endif
-
-# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
- memcpy((void *)i2c_buffer->led_matrix, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix));
- led_matrix_set_suspend_state(i2c_buffer->led_suspend_state);
-# endif
-# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
- memcpy((void *)i2c_buffer->rgb_matrix, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix));
- rgb_matrix_set_suspend_state(i2c_buffer->rgb_suspend_state);
-# endif
-}
-
-void transport_master_init(void) { i2c_init(); }
-
-void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); }
-
-#else // USE_SERIAL
+#else // USE_I2C
# include "serial.h"
-typedef struct _Serial_s2m_buffer_t {
- // TODO: if MATRIX_COLS > 8 change to uint8_t packed_matrix[] for pack/unpack
- matrix_row_t smatrix[ROWS_PER_HAND];
-
-# ifdef ENCODER_ENABLE
- uint8_t encoder_state[NUMBER_OF_ENCODERS];
-# endif
-
-} Serial_s2m_buffer_t;
-
-typedef struct _Serial_m2s_buffer_t {
-# ifdef SPLIT_MODS_ENABLE
- uint8_t real_mods;
- uint8_t weak_mods;
-# ifndef NO_ACTION_ONESHOT
- uint8_t oneshot_mods;
-# endif
-# endif
-# ifndef DISABLE_SYNC_TIMER
- uint32_t sync_timer;
-# endif
-# ifdef SPLIT_TRANSPORT_MIRROR
- matrix_row_t mmatrix[ROWS_PER_HAND];
-# endif
-# ifdef BACKLIGHT_ENABLE
- uint8_t backlight_level;
-# endif
-# ifdef WPM_ENABLE
- uint8_t current_wpm;
-# endif
-# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
- led_eeconfig_t led_matrix;
- bool led_suspend_state;
-# endif
-# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
- rgb_config_t rgb_matrix;
- bool rgb_suspend_state;
-# endif
-} Serial_m2s_buffer_t;
-
-# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
-// When MCUs on both sides drive their respective RGB LED chains,
-// it is necessary to synchronize, so it is necessary to communicate RGB
-// information. In that case, define RGBLIGHT_SPLIT with info on the number
-// of LEDs on each half.
-//
-// Otherwise, if the master side MCU drives both sides RGB LED chains,
-// there is no need to communicate.
-
-typedef struct _Serial_rgblight_t {
- rgblight_syncinfo_t rgblight_sync;
-} Serial_rgblight_t;
+static split_shared_memory_t shared_memory;
+split_shared_memory_t *const split_shmem = &shared_memory;
-volatile Serial_rgblight_t serial_rgblight = {};
-uint8_t volatile status_rgblight = 0;
-# endif
-
-volatile Serial_s2m_buffer_t serial_s2m_buffer = {};
-volatile Serial_m2s_buffer_t serial_m2s_buffer = {};
-uint8_t volatile status0 = 0;
-
-enum serial_transaction_id {
- GET_SLAVE_MATRIX = 0,
-# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
- PUT_RGBLIGHT,
-# endif
-};
-
-SSTD_t transactions[] = {
- [GET_SLAVE_MATRIX] =
- {
- (uint8_t *)&status0,
- sizeof(serial_m2s_buffer),
- (uint8_t *)&serial_m2s_buffer,
- sizeof(serial_s2m_buffer),
- (uint8_t *)&serial_s2m_buffer,
- },
-# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
- [PUT_RGBLIGHT] =
- {
- (uint8_t *)&status_rgblight, sizeof(serial_rgblight), (uint8_t *)&serial_rgblight, 0, NULL // no slave to master transfer
- },
-# endif
-};
-
-void transport_master_init(void) { soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }
-
-void transport_slave_init(void) { soft_serial_target_init(transactions, TID_LIMIT(transactions)); }
+void transport_master_init(void) { soft_serial_initiator_init(); }
+void transport_slave_init(void) { soft_serial_target_init(); }
-# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
-
-// rgblight synchronization information communication.
-
-void transport_rgblight_master(void) {
- if (rgblight_get_change_flags()) {
- rgblight_get_syncinfo((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync);
- if (soft_serial_transaction(PUT_RGBLIGHT) == TRANSACTION_END) {
- rgblight_clear_change_flags();
- }
- }
-}
-
-void transport_rgblight_slave(void) {
- if (status_rgblight == TRANSACTION_ACCEPTED) {
- rgblight_update_sync((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync, false);
- status_rgblight = TRANSACTION_END;
+bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) {
+ split_transaction_desc_t *trans = &split_transaction_table[id];
+ if (initiator2target_length > 0) {
+ size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length;
+ memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len);
}
-}
-# else
-# define transport_rgblight_master()
-# define transport_rgblight_slave()
-# endif
-
-bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
-# ifndef SERIAL_USE_MULTI_TRANSACTION
- if (soft_serial_transaction() != TRANSACTION_END) {
- return false;
- }
-# else
- transport_rgblight_master();
- if (soft_serial_transaction(GET_SLAVE_MATRIX) != TRANSACTION_END) {
+ if (soft_serial_transaction(id) != TRANSACTION_END) {
return false;
}
-# endif
- // TODO: if MATRIX_COLS > 8 change to unpack()
- for (int i = 0; i < ROWS_PER_HAND; ++i) {
- slave_matrix[i] = serial_s2m_buffer.smatrix[i];
-# ifdef SPLIT_TRANSPORT_MIRROR
- serial_m2s_buffer.mmatrix[i] = master_matrix[i];
-# endif
+ if (target2initiator_length > 0) {
+ size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length;
+ memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len);
}
-# ifdef BACKLIGHT_ENABLE
- // Write backlight level for slave to read
- serial_m2s_buffer.backlight_level = is_backlight_enabled() ? get_backlight_level() : 0;
-# endif
-
-# ifdef ENCODER_ENABLE
- encoder_update_raw((uint8_t *)serial_s2m_buffer.encoder_state);
-# endif
-
-# ifdef WPM_ENABLE
- // Write wpm to slave
- serial_m2s_buffer.current_wpm = get_current_wpm();
-# endif
-
-# ifdef SPLIT_MODS_ENABLE
- serial_m2s_buffer.real_mods = get_mods();
- serial_m2s_buffer.weak_mods = get_weak_mods();
-# ifndef NO_ACTION_ONESHOT
- serial_m2s_buffer.oneshot_mods = get_oneshot_mods();
-# endif
-# endif
-
-# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
- serial_m2s_buffer.led_matrix = led_matrix_eeconfig;
- serial_m2s_buffer.led_suspend_state = led_matrix_get_suspend_state();
-# endif
-# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
- serial_m2s_buffer.rgb_matrix = rgb_matrix_config;
- serial_m2s_buffer.rgb_suspend_state = rgb_matrix_get_suspend_state();
-# endif
-
-# ifndef DISABLE_SYNC_TIMER
- serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
-# endif
return true;
}
-void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
- transport_rgblight_slave();
-# ifndef DISABLE_SYNC_TIMER
- sync_timer_update(serial_m2s_buffer.sync_timer);
-# endif
-
- // TODO: if MATRIX_COLS > 8 change to pack()
- for (int i = 0; i < ROWS_PER_HAND; ++i) {
- serial_s2m_buffer.smatrix[i] = slave_matrix[i];
-# ifdef SPLIT_TRANSPORT_MIRROR
- master_matrix[i] = serial_m2s_buffer.mmatrix[i];
-# endif
- }
-# ifdef BACKLIGHT_ENABLE
- backlight_set(serial_m2s_buffer.backlight_level);
-# endif
-
-# ifdef ENCODER_ENABLE
- encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state);
-# endif
+#endif // USE_I2C
-# ifdef WPM_ENABLE
- set_current_wpm(serial_m2s_buffer.current_wpm);
-# endif
-
-# ifdef SPLIT_MODS_ENABLE
- set_mods(serial_m2s_buffer.real_mods);
- set_weak_mods(serial_m2s_buffer.weak_mods);
-# ifndef NO_ACTION_ONESHOT
- set_oneshot_mods(serial_m2s_buffer.oneshot_mods);
-# endif
-# endif
-
-# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
- led_matrix_eeconfig = serial_m2s_buffer.led_matrix;
- led_matrix_set_suspend_state(serial_m2s_buffer.led_suspend_state);
-# endif
-# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
- rgb_matrix_config = serial_m2s_buffer.rgb_matrix;
- rgb_matrix_set_suspend_state(serial_m2s_buffer.rgb_suspend_state);
-# endif
-}
+bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { return transactions_master(master_matrix, slave_matrix); }
-#endif
+void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { transactions_slave(master_matrix, slave_matrix); } \ No newline at end of file
diff --git a/quantum/split_common/transport.h b/quantum/split_common/transport.h
index a9f66301bf..2e07f6b25c 100644
--- a/quantum/split_common/transport.h
+++ b/quantum/split_common/transport.h
@@ -1,10 +1,175 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#pragma once
+#include "stdint.h"
+#include "stdbool.h"
+
+#include "progmem.h"
+#include "action_layer.h"
#include "matrix.h"
+#ifndef RPC_M2S_BUFFER_SIZE
+# define RPC_M2S_BUFFER_SIZE 32
+#endif // RPC_M2S_BUFFER_SIZE
+
+#ifndef RPC_S2M_BUFFER_SIZE
+# define RPC_S2M_BUFFER_SIZE 32
+#endif // RPC_S2M_BUFFER_SIZE
+
void transport_master_init(void);
void transport_slave_init(void);
// returns false if valid data not received from slave
bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
+
+bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length);
+
+#ifdef ENCODER_ENABLE
+# include "encoder.h"
+# define NUMBER_OF_ENCODERS (sizeof((pin_t[])ENCODERS_PAD_A) / sizeof(pin_t))
+#endif // ENCODER_ENABLE
+
+#ifdef BACKLIGHT_ENABLE
+# include "backlight.h"
+#endif // BACKLIGHT_ENABLE
+
+#ifdef RGBLIGHT_ENABLE
+# include "rgblight.h"
+#endif // RGBLIGHT_ENABLE
+
+typedef struct _split_slave_matrix_sync_t {
+ uint8_t checksum;
+ matrix_row_t matrix[(MATRIX_ROWS) / 2];
+} split_slave_matrix_sync_t;
+
+#ifdef SPLIT_TRANSPORT_MIRROR
+typedef struct _split_master_matrix_sync_t {
+ matrix_row_t matrix[(MATRIX_ROWS) / 2];
+} split_master_matrix_sync_t;
+#endif // SPLIT_TRANSPORT_MIRROR
+
+#ifdef ENCODER_ENABLE
+typedef struct _split_slave_encoder_sync_t {
+ uint8_t checksum;
+ uint8_t state[NUMBER_OF_ENCODERS];
+} split_slave_encoder_sync_t;
+#endif // ENCODER_ENABLE
+
+#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
+typedef struct _split_layers_sync_t {
+ layer_state_t layer_state;
+ layer_state_t default_layer_state;
+} split_layers_sync_t;
+#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
+
+#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
+# include "led_matrix.h"
+
+typedef struct _led_matrix_sync_t {
+ led_eeconfig_t led_matrix;
+ bool led_suspend_state;
+} led_matrix_sync_t;
+#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
+
+#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
+# include "rgb_matrix.h"
+
+typedef struct _rgb_matrix_sync_t {
+ rgb_config_t rgb_matrix;
+ bool rgb_suspend_state;
+} rgb_matrix_sync_t;
+#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
+
+#ifdef SPLIT_MODS_ENABLE
+typedef struct _split_mods_sync_t {
+ uint8_t real_mods;
+ uint8_t weak_mods;
+# ifndef NO_ACTION_ONESHOT
+ uint8_t oneshot_mods;
+# endif // NO_ACTION_ONESHOT
+} split_mods_sync_t;
+#endif // SPLIT_MODS_ENABLE
+
+#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+typedef struct _rpc_sync_info_t {
+ int8_t transaction_id;
+ uint8_t m2s_length;
+ uint8_t s2m_length;
+} rpc_sync_info_t;
+#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+
+typedef struct _split_shared_memory_t {
+#ifdef USE_I2C
+ int8_t transaction_id;
+#endif // USE_I2C
+
+ split_slave_matrix_sync_t smatrix;
+
+#ifdef SPLIT_TRANSPORT_MIRROR
+ split_master_matrix_sync_t mmatrix;
+#endif // SPLIT_TRANSPORT_MIRROR
+
+#ifdef ENCODER_ENABLE
+ split_slave_encoder_sync_t encoders;
+#endif // ENCODER_ENABLE
+
+#ifndef DISABLE_SYNC_TIMER
+ uint32_t sync_timer;
+#endif // DISABLE_SYNC_TIMER
+
+#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
+ split_layers_sync_t layers;
+#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)
+
+#ifdef SPLIT_LED_STATE_ENABLE
+ uint8_t led_state;
+#endif // SPLIT_LED_STATE_ENABLE
+
+#ifdef SPLIT_MODS_ENABLE
+ split_mods_sync_t mods;
+#endif // SPLIT_MODS_ENABLE
+
+#ifdef BACKLIGHT_ENABLE
+ uint8_t backlight_level;
+#endif // BACKLIGHT_ENABLE
+
+#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+ rgblight_syncinfo_t rgblight_sync;
+#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+
+#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
+ led_matrix_sync_t led_matrix_sync;
+#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
+
+#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
+ rgb_matrix_sync_t rgb_matrix_sync;
+#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
+
+#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
+ uint8_t current_wpm;
+#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)
+
+#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+ rpc_sync_info_t rpc_info;
+ uint8_t rpc_m2s_buffer[RPC_M2S_BUFFER_SIZE];
+ uint8_t rpc_s2m_buffer[RPC_S2M_BUFFER_SIZE];
+#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
+} split_shared_memory_t;
+
+extern split_shared_memory_t *const split_shmem; \ No newline at end of file