summaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
Diffstat (limited to 'quantum')
-rw-r--r--quantum/action.c1154
-rw-r--r--quantum/action.h137
-rw-r--r--quantum/action_code.h308
-rw-r--r--quantum/action_layer.c279
-rw-r--r--quantum/action_layer.h147
-rw-r--r--quantum/action_macro.c93
-rw-r--r--quantum/action_macro.h123
-rw-r--r--quantum/action_tapping.c491
-rw-r--r--quantum/action_tapping.h42
-rw-r--r--quantum/action_util.c455
-rw-r--r--quantum/action_util.h105
-rw-r--r--quantum/audio/audio.c3
-rw-r--r--quantum/bootmagic/bootmagic.h4
-rw-r--r--quantum/bootmagic/bootmagic_full.c147
-rw-r--r--quantum/bootmagic/bootmagic_full.h115
-rw-r--r--quantum/bootmagic/bootmagic_lite.c8
-rw-r--r--quantum/color.h54
-rw-r--r--quantum/command.c593
-rw-r--r--quantum/command.h3
-rw-r--r--quantum/crc.c59
-rw-r--r--quantum/crc.h44
-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/digitizer.c34
-rw-r--r--quantum/digitizer.h (renamed from quantum/rgb.h)32
-rw-r--r--quantum/dip_switch.c19
-rw-r--r--quantum/dip_switch.h8
-rw-r--r--quantum/eeconfig.c240
-rw-r--r--quantum/eeconfig.h113
-rw-r--r--quantum/encoder.c5
-rw-r--r--quantum/haptic.c295
-rw-r--r--quantum/haptic.h77
-rw-r--r--quantum/keyboard.c569
-rw-r--r--quantum/keyboard.h90
-rw-r--r--quantum/keycode.h560
-rw-r--r--quantum/keymap_common.c28
-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_steno.h18
-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)4
-rw-r--r--quantum/led_matrix/animations/band_anim.h (renamed from quantum/led_matrix_animations/band_anim.h)4
-rw-r--r--quantum/led_matrix/animations/band_pinwheel_anim.h (renamed from quantum/led_matrix_animations/band_pinwheel_anim.h)4
-rw-r--r--quantum/led_matrix/animations/band_spiral_anim.h (renamed from quantum/led_matrix_animations/band_spiral_anim.h)4
-rw-r--r--quantum/led_matrix/animations/breathing_anim.h (renamed from quantum/led_matrix_animations/breathing_anim.h)4
-rw-r--r--quantum/led_matrix/animations/cycle_left_right_anim.h (renamed from quantum/led_matrix_animations/cycle_left_right_anim.h)4
-rw-r--r--quantum/led_matrix/animations/cycle_out_in_anim.h (renamed from quantum/led_matrix_animations/cycle_out_in_anim.h)4
-rw-r--r--quantum/led_matrix/animations/cycle_up_down_anim.h (renamed from quantum/led_matrix_animations/cycle_up_down_anim.h)4
-rw-r--r--quantum/led_matrix/animations/dual_beacon_anim.h (renamed from quantum/led_matrix_animations/dual_beacon_anim.h)4
-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)12
-rw-r--r--quantum/led_matrix/animations/solid_reactive_nexus.h (renamed from quantum/led_matrix_animations/solid_reactive_nexus.h)14
-rw-r--r--quantum/led_matrix/animations/solid_reactive_simple_anim.h (renamed from quantum/led_matrix_animations/solid_reactive_simple_anim.h)6
-rw-r--r--quantum/led_matrix/animations/solid_reactive_wide.h (renamed from quantum/led_matrix_animations/solid_reactive_wide.h)12
-rw-r--r--quantum/led_matrix/animations/solid_splash_anim.h (renamed from quantum/led_matrix_animations/solid_splash_anim.h)12
-rw-r--r--quantum/led_matrix/animations/wave_left_right_anim.h (renamed from quantum/led_matrix_animations/wave_left_right_anim.h)4
-rw-r--r--quantum/led_matrix/animations/wave_up_down_anim.h (renamed from quantum/led_matrix_animations/wave_up_down_anim.h)4
-rw-r--r--quantum/led_matrix/led_matrix.c (renamed from quantum/led_matrix.c)53
-rw-r--r--quantum/led_matrix/led_matrix.h (renamed from quantum/led_matrix.h)3
-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_legacy_enables.h82
-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/logging/debug.c25
-rw-r--r--quantum/logging/debug.h169
-rw-r--r--quantum/logging/nodebug.h26
-rw-r--r--quantum/logging/print.c27
-rw-r--r--quantum/logging/print.h135
-rw-r--r--quantum/logging/print.mk9
-rw-r--r--quantum/logging/sendchar.c20
-rw-r--r--quantum/logging/sendchar.h33
-rw-r--r--quantum/main.c41
-rw-r--r--quantum/matrix.c277
-rw-r--r--quantum/matrix.h2
-rw-r--r--quantum/matrix_common.c2
-rw-r--r--quantum/mcu_selection.mk109
-rw-r--r--quantum/mousekey.c2
-rw-r--r--quantum/mousekey.h11
-rw-r--r--quantum/process_keycode/process_combo.c558
-rw-r--r--quantum/process_keycode/process_combo.h38
-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_magic.c3
-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.c7
-rw-r--r--quantum/process_keycode/process_steno.c15
-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.c126
-rw-r--r--quantum/quantum.h31
-rw-r--r--quantum/quantum_keycodes.h11
-rw-r--r--quantum/rgb_matrix/animations/alpha_mods_anim.h (renamed from quantum/rgb_matrix_animations/alpha_mods_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/breathing_anim.h (renamed from quantum/rgb_matrix_animations/breathing_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/colorband_pinwheel_sat_anim.h (renamed from quantum/rgb_matrix_animations/colorband_pinwheel_sat_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/colorband_pinwheel_val_anim.h (renamed from quantum/rgb_matrix_animations/colorband_pinwheel_val_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/colorband_sat_anim.h (renamed from quantum/rgb_matrix_animations/colorband_sat_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/colorband_spiral_sat_anim.h (renamed from quantum/rgb_matrix_animations/colorband_spiral_sat_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/colorband_spiral_val_anim.h (renamed from quantum/rgb_matrix_animations/colorband_spiral_val_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/colorband_val_anim.h (renamed from quantum/rgb_matrix_animations/colorband_val_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/cycle_all_anim.h (renamed from quantum/rgb_matrix_animations/cycle_all_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/cycle_left_right_anim.h (renamed from quantum/rgb_matrix_animations/cycle_left_right_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/cycle_out_in_anim.h (renamed from quantum/rgb_matrix_animations/cycle_out_in_anim.h)2
-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)2
-rw-r--r--quantum/rgb_matrix/animations/cycle_pinwheel_anim.h (renamed from quantum/rgb_matrix_animations/cycle_pinwheel_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/cycle_spiral_anim.h (renamed from quantum/rgb_matrix_animations/cycle_spiral_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/cycle_up_down_anim.h (renamed from quantum/rgb_matrix_animations/cycle_up_down_anim.h)2
-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)2
-rw-r--r--quantum/rgb_matrix/animations/gradient_left_right_anim.h (renamed from quantum/rgb_matrix_animations/gradient_left_right_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/gradient_up_down_anim.h (renamed from quantum/rgb_matrix_animations/gradient_up_down_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/hue_breathing_anim.h (renamed from quantum/rgb_matrix_animations/hue_breathing_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/hue_pendulum_anim.h (renamed from quantum/rgb_matrix_animations/hue_pendulum_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/hue_wave_anim.h (renamed from quantum/rgb_matrix_animations/hue_wave_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/jellybean_raindrops_anim.h (renamed from quantum/rgb_matrix_animations/jellybean_raindrops_anim.h)4
-rw-r--r--quantum/rgb_matrix/animations/rainbow_beacon_anim.h (renamed from quantum/rgb_matrix_animations/rainbow_beacon_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/rainbow_moving_chevron_anim.h (renamed from quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/rainbow_pinwheels_anim.h (renamed from quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h)2
-rw-r--r--quantum/rgb_matrix/animations/raindrops_anim.h (renamed from quantum/rgb_matrix_animations/raindrops_anim.h)2
-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)2
-rw-r--r--quantum/rgb_matrix/rgb_matrix.c (renamed from quantum/rgb_matrix.c)59
-rw-r--r--quantum/rgb_matrix/rgb_matrix.h (renamed from quantum/rgb_matrix.h)6
-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_legacy_enables.h153
-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.c57
-rw-r--r--quantum/split_common/split_util.h5
-rw-r--r--quantum/split_common/transaction_id_define.h102
-rw-r--r--quantum/split_common/transactions.c723
-rw-r--r--quantum/split_common/transactions.h54
-rw-r--r--quantum/split_common/transport.c484
-rw-r--r--quantum/split_common/transport.h173
-rw-r--r--quantum/template/avr/rules.mk2
-rw-r--r--quantum/template/ps2avrgb/rules.mk2
-rw-r--r--quantum/via.c42
-rw-r--r--quantum/via.h8
210 files changed, 11818 insertions, 2989 deletions
diff --git a/quantum/action.c b/quantum/action.c
new file mode 100644
index 0000000000..ec9fcd9c9c
--- /dev/null
+++ b/quantum/action.c
@@ -0,0 +1,1154 @@
+/*
+Copyright 2012,2013 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 "host.h"
+#include "keycode.h"
+#include "keyboard.h"
+#include "mousekey.h"
+#include "command.h"
+#include "led.h"
+#include "action_layer.h"
+#include "action_tapping.h"
+#include "action_macro.h"
+#include "action_util.h"
+#include "action.h"
+#include "wait.h"
+
+#ifdef BACKLIGHT_ENABLE
+# include "backlight.h"
+#endif
+
+#ifdef DEBUG_ACTION
+# include "debug.h"
+#else
+# include "nodebug.h"
+#endif
+
+#ifdef POINTING_DEVICE_ENABLE
+# include "pointing_device.h"
+#endif
+
+int tp_buttons;
+
+#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
+int retro_tapping_counter = 0;
+#endif
+
+#ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
+__attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { return false; }
+#endif
+
+#ifdef RETRO_TAPPING_PER_KEY
+__attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; }
+#endif
+
+__attribute__((weak)) bool pre_process_record_quantum(keyrecord_t *record) { return true; }
+
+#ifndef TAP_CODE_DELAY
+# define TAP_CODE_DELAY 0
+#endif
+#ifndef TAP_HOLD_CAPS_DELAY
+# define TAP_HOLD_CAPS_DELAY 80
+#endif
+/** \brief Called to execute an action.
+ *
+ * FIXME: Needs documentation.
+ */
+void action_exec(keyevent_t event) {
+ if (!IS_NOEVENT(event)) {
+ dprint("\n---- action_exec: start -----\n");
+ dprint("EVENT: ");
+ debug_event(event);
+ dprintln();
+#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
+ retro_tapping_counter++;
+#endif
+ }
+
+ if (event.pressed) {
+ // clear the potential weak mods left by previously pressed keys
+ clear_weak_mods();
+ }
+
+#ifdef SWAP_HANDS_ENABLE
+ if (!IS_NOEVENT(event)) {
+ process_hand_swap(&event);
+ }
+#endif
+
+ keyrecord_t record = {.event = event};
+
+#ifndef NO_ACTION_ONESHOT
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+ if (has_oneshot_layer_timed_out()) {
+ clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
+ }
+ if (has_oneshot_mods_timed_out()) {
+ clear_oneshot_mods();
+ }
+# ifdef SWAP_HANDS_ENABLE
+ if (has_oneshot_swaphands_timed_out()) {
+ clear_oneshot_swaphands();
+ }
+# endif
+# endif
+#endif
+
+#ifndef NO_ACTION_TAPPING
+ if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) {
+ action_tapping_process(record);
+ }
+#else
+ if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) {
+ process_record(&record);
+ }
+ if (!IS_NOEVENT(record.event)) {
+ dprint("processed: ");
+ debug_record(record);
+ dprintln();
+ }
+#endif
+}
+
+#ifdef SWAP_HANDS_ENABLE
+bool swap_hands = false;
+bool swap_held = false;
+
+/** \brief Process Hand Swap
+ *
+ * FIXME: Needs documentation.
+ */
+void process_hand_swap(keyevent_t *event) {
+ static swap_state_row_t swap_state[MATRIX_ROWS];
+
+ keypos_t pos = event->key;
+ swap_state_row_t col_bit = (swap_state_row_t)1 << pos.col;
+ bool do_swap = event->pressed ? swap_hands : swap_state[pos.row] & (col_bit);
+
+ if (do_swap) {
+ event->key.row = pgm_read_byte(&hand_swap_config[pos.row][pos.col].row);
+ event->key.col = pgm_read_byte(&hand_swap_config[pos.row][pos.col].col);
+ swap_state[pos.row] |= col_bit;
+ } else {
+ swap_state[pos.row] &= ~(col_bit);
+ }
+}
+#endif
+
+#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
+bool disable_action_cache = false;
+
+void process_record_nocache(keyrecord_t *record) {
+ disable_action_cache = true;
+ process_record(record);
+ disable_action_cache = false;
+}
+#else
+void process_record_nocache(keyrecord_t *record) { process_record(record); }
+#endif
+
+__attribute__((weak)) bool process_record_quantum(keyrecord_t *record) { return true; }
+
+__attribute__((weak)) void post_process_record_quantum(keyrecord_t *record) {}
+
+#ifndef NO_ACTION_TAPPING
+/** \brief Allows for handling tap-hold actions immediately instead of waiting for TAPPING_TERM or another keypress.
+ *
+ * FIXME: Needs documentation.
+ */
+void process_record_tap_hint(keyrecord_t *record) {
+ action_t action = layer_switch_get_action(record->event.key);
+
+ switch (action.kind.id) {
+# ifdef SWAP_HANDS_ENABLE
+ case ACT_SWAP_HANDS:
+ switch (action.swap.code) {
+ case OP_SH_ONESHOT:
+ break;
+ case OP_SH_TAP_TOGGLE:
+ default:
+ swap_hands = !swap_hands;
+ swap_held = true;
+ }
+ break;
+# endif
+ }
+}
+#endif
+
+/** \brief Take a key event (key press or key release) and processes it.
+ *
+ * FIXME: Needs documentation.
+ */
+void process_record(keyrecord_t *record) {
+ if (IS_NOEVENT(record->event)) {
+ return;
+ }
+
+ if (!process_record_quantum(record)) {
+#ifndef NO_ACTION_ONESHOT
+ if (is_oneshot_layer_active() && record->event.pressed) {
+ clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
+ }
+#endif
+ return;
+ }
+
+ process_record_handler(record);
+ post_process_record_quantum(record);
+}
+
+void process_record_handler(keyrecord_t *record) {
+#ifdef COMBO_ENABLE
+ action_t action;
+ if (record->keycode) {
+ action = action_for_keycode(record->keycode);
+ } else {
+ action = store_or_get_action(record->event.pressed, record->event.key);
+ }
+#else
+ action_t action = store_or_get_action(record->event.pressed, record->event.key);
+#endif
+ dprint("ACTION: ");
+ debug_action(action);
+#ifndef NO_ACTION_LAYER
+ dprint(" layer_state: ");
+ layer_debug();
+ dprint(" default_layer_state: ");
+ default_layer_debug();
+#endif
+ dprintln();
+
+ process_action(record, action);
+}
+
+#if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE)
+void register_button(bool pressed, enum mouse_buttons button) {
+# ifdef PS2_MOUSE_ENABLE
+ tp_buttons = pressed ? tp_buttons | button : tp_buttons & ~button;
+# endif
+# ifdef POINTING_DEVICE_ENABLE
+ report_mouse_t currentReport = pointing_device_get_report();
+ currentReport.buttons = pressed ? currentReport.buttons | button : currentReport.buttons & ~button;
+ pointing_device_set_report(currentReport);
+# endif
+}
+#endif
+
+/** \brief Take an action and processes it.
+ *
+ * FIXME: Needs documentation.
+ */
+void process_action(keyrecord_t *record, action_t action) {
+ keyevent_t event = record->event;
+#ifndef NO_ACTION_TAPPING
+ uint8_t tap_count = record->tap.count;
+#endif
+
+#ifndef NO_ACTION_ONESHOT
+ bool do_release_oneshot = false;
+ // notice we only clear the one shot layer if the pressed key is not a modifier.
+ if (is_oneshot_layer_active() && event.pressed && (action.kind.id == ACT_USAGE || !IS_MOD(action.key.code))
+# ifdef SWAP_HANDS_ENABLE
+ && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)
+# endif
+ ) {
+ clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
+ do_release_oneshot = !is_oneshot_layer_active();
+ }
+#endif
+
+ switch (action.kind.id) {
+ /* Key and Mods */
+ case ACT_LMODS:
+ case ACT_RMODS: {
+ uint8_t mods = (action.kind.id == ACT_LMODS) ? action.key.mods : action.key.mods << 4;
+ if (event.pressed) {
+ if (mods) {
+ if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
+ // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless.
+ // This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT).
+ // Same applies for some keys like KC_MEH which are declared as MEH(KC_NO).
+ add_mods(mods);
+ } else {
+ add_weak_mods(mods);
+ }
+ send_keyboard_report();
+ }
+ register_code(action.key.code);
+ } else {
+ unregister_code(action.key.code);
+ if (mods) {
+ if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
+ del_mods(mods);
+ } else {
+ del_weak_mods(mods);
+ }
+ send_keyboard_report();
+ }
+ }
+ } break;
+#ifndef NO_ACTION_TAPPING
+ case ACT_LMODS_TAP:
+ case ACT_RMODS_TAP: {
+ uint8_t mods = (action.kind.id == ACT_LMODS_TAP) ? action.key.mods : action.key.mods << 4;
+ switch (action.layer_tap.code) {
+# ifndef NO_ACTION_ONESHOT
+ case MODS_ONESHOT:
+ // Oneshot modifier
+ if (event.pressed) {
+ if (tap_count == 0) {
+ dprint("MODS_TAP: Oneshot: 0\n");
+ register_mods(mods | get_oneshot_mods());
+ } else if (tap_count == 1) {
+ dprint("MODS_TAP: Oneshot: start\n");
+ set_oneshot_mods(mods | get_oneshot_mods());
+# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
+ } else if (tap_count == ONESHOT_TAP_TOGGLE) {
+ dprint("MODS_TAP: Toggling oneshot");
+ clear_oneshot_mods();
+ set_oneshot_locked_mods(mods);
+ register_mods(mods);
+# endif
+ } else {
+ register_mods(mods | get_oneshot_mods());
+ }
+ } else {
+ if (tap_count == 0) {
+ clear_oneshot_mods();
+ unregister_mods(mods);
+ } else if (tap_count == 1) {
+ // Retain Oneshot mods
+# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
+ if (mods & get_mods()) {
+ clear_oneshot_locked_mods();
+ clear_oneshot_mods();
+ unregister_mods(mods);
+ }
+ } else if (tap_count == ONESHOT_TAP_TOGGLE) {
+ // Toggle Oneshot Layer
+# endif
+ } else {
+ clear_oneshot_mods();
+ unregister_mods(mods);
+ }
+ }
+ break;
+# endif
+ case MODS_TAP_TOGGLE:
+ if (event.pressed) {
+ if (tap_count <= TAPPING_TOGGLE) {
+ register_mods(mods);
+ }
+ } else {
+ if (tap_count < TAPPING_TOGGLE) {
+ unregister_mods(mods);
+ }
+ }
+ break;
+ default:
+ if (event.pressed) {
+ if (tap_count > 0) {
+# if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
+ if (
+# ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
+ !get_ignore_mod_tap_interrupt(get_event_keycode(record->event, false), record) &&
+# endif
+ record->tap.interrupted) {
+ dprint("mods_tap: tap: cancel: add_mods\n");
+ // ad hoc: set 0 to cancel tap
+ record->tap.count = 0;
+ register_mods(mods);
+ } else
+# endif
+ {
+ dprint("MODS_TAP: Tap: register_code\n");
+ register_code(action.key.code);
+ }
+ } else {
+ dprint("MODS_TAP: No tap: add_mods\n");
+ register_mods(mods);
+ }
+ } else {
+ if (tap_count > 0) {
+ dprint("MODS_TAP: Tap: unregister_code\n");
+ if (action.layer_tap.code == KC_CAPS) {
+ wait_ms(TAP_HOLD_CAPS_DELAY);
+ } else {
+ wait_ms(TAP_CODE_DELAY);
+ }
+ unregister_code(action.key.code);
+ } else {
+ dprint("MODS_TAP: No tap: add_mods\n");
+ unregister_mods(mods);
+ }
+ }
+ break;
+ }
+ } break;
+#endif
+#ifdef EXTRAKEY_ENABLE
+ /* other HID usage */
+ case ACT_USAGE:
+ switch (action.usage.page) {
+ case PAGE_SYSTEM:
+ if (event.pressed) {
+ host_system_send(action.usage.code);
+ } else {
+ host_system_send(0);
+ }
+ break;
+ case PAGE_CONSUMER:
+ if (event.pressed) {
+ host_consumer_send(action.usage.code);
+ } else {
+ host_consumer_send(0);
+ }
+ break;
+ }
+ break;
+#endif
+#ifdef MOUSEKEY_ENABLE
+ /* Mouse key */
+ case ACT_MOUSEKEY:
+ if (event.pressed) {
+ mousekey_on(action.key.code);
+ } else {
+ mousekey_off(action.key.code);
+ }
+ switch (action.key.code) {
+# if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE)
+# ifdef POINTING_DEVICE_ENABLE
+ case KC_MS_BTN1 ... KC_MS_BTN8:
+# else
+ case KC_MS_BTN1 ... KC_MS_BTN3:
+# endif
+ register_button(event.pressed, MOUSE_BTN_MASK(action.key.code - KC_MS_BTN1));
+ break;
+# endif
+ default:
+ mousekey_send();
+ break;
+ }
+ break;
+#endif
+#ifndef NO_ACTION_LAYER
+ case ACT_LAYER:
+ if (action.layer_bitop.on == 0) {
+ /* Default Layer Bitwise Operation */
+ if (!event.pressed) {
+ uint8_t shift = action.layer_bitop.part * 4;
+ layer_state_t bits = ((layer_state_t)action.layer_bitop.bits) << shift;
+ layer_state_t mask = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0;
+ switch (action.layer_bitop.op) {
+ case OP_BIT_AND:
+ default_layer_and(bits | mask);
+ break;
+ case OP_BIT_OR:
+ default_layer_or(bits | mask);
+ break;
+ case OP_BIT_XOR:
+ default_layer_xor(bits | mask);
+ break;
+ case OP_BIT_SET:
+ default_layer_set(bits | mask);
+ break;
+ }
+ }
+ } else {
+ /* Layer Bitwise Operation */
+ if (event.pressed ? (action.layer_bitop.on & ON_PRESS) : (action.layer_bitop.on & ON_RELEASE)) {
+ uint8_t shift = action.layer_bitop.part * 4;
+ layer_state_t bits = ((layer_state_t)action.layer_bitop.bits) << shift;
+ layer_state_t mask = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0;
+ switch (action.layer_bitop.op) {
+ case OP_BIT_AND:
+ layer_and(bits | mask);
+ break;
+ case OP_BIT_OR:
+ layer_or(bits | mask);
+ break;
+ case OP_BIT_XOR:
+ layer_xor(bits | mask);
+ break;
+ case OP_BIT_SET:
+ layer_state_set(bits | mask);
+ break;
+ }
+ }
+ }
+ break;
+ case ACT_LAYER_MODS:
+ if (event.pressed) {
+ layer_on(action.layer_mods.layer);
+ register_mods(action.layer_mods.mods);
+ } else {
+ unregister_mods(action.layer_mods.mods);
+ layer_off(action.layer_mods.layer);
+ }
+ break;
+# ifndef NO_ACTION_TAPPING
+ case ACT_LAYER_TAP:
+ case ACT_LAYER_TAP_EXT:
+ switch (action.layer_tap.code) {
+ case OP_TAP_TOGGLE:
+ /* tap toggle */
+ if (event.pressed) {
+ if (tap_count < TAPPING_TOGGLE) {
+ layer_invert(action.layer_tap.val);
+ }
+ } else {
+ if (tap_count <= TAPPING_TOGGLE) {
+ layer_invert(action.layer_tap.val);
+ }
+ }
+ break;
+ case OP_ON_OFF:
+ event.pressed ? layer_on(action.layer_tap.val) : layer_off(action.layer_tap.val);
+ break;
+ case OP_OFF_ON:
+ event.pressed ? layer_off(action.layer_tap.val) : layer_on(action.layer_tap.val);
+ break;
+ case OP_SET_CLEAR:
+ event.pressed ? layer_move(action.layer_tap.val) : layer_clear();
+ break;
+# ifndef NO_ACTION_ONESHOT
+ case OP_ONESHOT:
+ // Oneshot modifier
+# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
+ do_release_oneshot = false;
+ if (event.pressed) {
+ del_mods(get_oneshot_locked_mods());
+ if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
+ reset_oneshot_layer();
+ layer_off(action.layer_tap.val);
+ break;
+ } else if (tap_count < ONESHOT_TAP_TOGGLE) {
+ layer_on(action.layer_tap.val);
+ set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
+ }
+ } else {
+ add_mods(get_oneshot_locked_mods());
+ if (tap_count >= ONESHOT_TAP_TOGGLE) {
+ reset_oneshot_layer();
+ clear_oneshot_locked_mods();
+ set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
+ } else {
+ clear_oneshot_layer_state(ONESHOT_PRESSED);
+ }
+ }
+# else
+ if (event.pressed) {
+ layer_on(action.layer_tap.val);
+ set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
+ } else {
+ clear_oneshot_layer_state(ONESHOT_PRESSED);
+ if (tap_count > 1) {
+ clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
+ }
+ }
+# endif
+ break;
+# endif
+ default:
+ /* tap key */
+ if (event.pressed) {
+ if (tap_count > 0) {
+ dprint("KEYMAP_TAP_KEY: Tap: register_code\n");
+ register_code(action.layer_tap.code);
+ } else {
+ dprint("KEYMAP_TAP_KEY: No tap: On on press\n");
+ layer_on(action.layer_tap.val);
+ }
+ } else {
+ if (tap_count > 0) {
+ dprint("KEYMAP_TAP_KEY: Tap: unregister_code\n");
+ if (action.layer_tap.code == KC_CAPS) {
+ wait_ms(TAP_HOLD_CAPS_DELAY);
+ } else {
+ wait_ms(TAP_CODE_DELAY);
+ }
+ unregister_code(action.layer_tap.code);
+ } else {
+ dprint("KEYMAP_TAP_KEY: No tap: Off on release\n");
+ layer_off(action.layer_tap.val);
+ }
+ }
+ break;
+ }
+ break;
+# endif
+#endif
+ /* Extentions */
+#ifndef NO_ACTION_MACRO
+ case ACT_MACRO:
+ action_macro_play(action_get_macro(record, action.func.id, action.func.opt));
+ break;
+#endif
+#ifdef SWAP_HANDS_ENABLE
+ case ACT_SWAP_HANDS:
+ switch (action.swap.code) {
+ case OP_SH_TOGGLE:
+ if (event.pressed) {
+ swap_hands = !swap_hands;
+ }
+ break;
+ case OP_SH_ON_OFF:
+ swap_hands = event.pressed;
+ break;
+ case OP_SH_OFF_ON:
+ swap_hands = !event.pressed;
+ break;
+ case OP_SH_ON:
+ if (!event.pressed) {
+ swap_hands = true;
+ }
+ break;
+ case OP_SH_OFF:
+ if (!event.pressed) {
+ swap_hands = false;
+ }
+ break;
+# ifndef NO_ACTION_ONESHOT
+ case OP_SH_ONESHOT:
+ if (event.pressed) {
+ set_oneshot_swaphands();
+ } else {
+ release_oneshot_swaphands();
+ }
+ break;
+# endif
+
+# ifndef NO_ACTION_TAPPING
+ case OP_SH_TAP_TOGGLE:
+ /* tap toggle */
+
+ if (event.pressed) {
+ if (swap_held) {
+ swap_held = false;
+ } else {
+ swap_hands = !swap_hands;
+ }
+ } else {
+ if (tap_count < TAPPING_TOGGLE) {
+ swap_hands = !swap_hands;
+ }
+ }
+ break;
+ default:
+ /* tap key */
+ if (tap_count > 0) {
+ if (swap_held) {
+ swap_hands = !swap_hands; // undo hold set up in _tap_hint
+ swap_held = false;
+ }
+ if (event.pressed) {
+ register_code(action.swap.code);
+ } else {
+ wait_ms(TAP_CODE_DELAY);
+ unregister_code(action.swap.code);
+ *record = (keyrecord_t){}; // hack: reset tap mode
+ }
+ } else {
+ if (swap_held && !event.pressed) {
+ swap_hands = !swap_hands; // undo hold set up in _tap_hint
+ swap_held = false;
+ }
+ }
+# endif
+ }
+#endif
+#ifndef NO_ACTION_FUNCTION
+ case ACT_FUNCTION:
+ action_function(record, action.func.id, action.func.opt);
+ break;
+#endif
+ default:
+ break;
+ }
+
+#ifndef NO_ACTION_LAYER
+ // if this event is a layer action, update the leds
+ switch (action.kind.id) {
+ case ACT_LAYER:
+ case ACT_LAYER_MODS:
+# ifndef NO_ACTION_TAPPING
+ case ACT_LAYER_TAP:
+ case ACT_LAYER_TAP_EXT:
+# endif
+ led_set(host_keyboard_leds());
+ break;
+ default:
+ break;
+ }
+#endif
+
+#ifndef NO_ACTION_TAPPING
+# if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
+ if (!is_tap_action(action)) {
+ retro_tapping_counter = 0;
+ } else {
+ if (event.pressed) {
+ if (tap_count > 0) {
+ retro_tapping_counter = 0;
+ }
+ } else {
+ if (tap_count > 0) {
+ retro_tapping_counter = 0;
+ } else {
+ if (
+# ifdef RETRO_TAPPING_PER_KEY
+ get_retro_tapping(get_event_keycode(record->event, false), record) &&
+# endif
+ retro_tapping_counter == 2) {
+ tap_code(action.layer_tap.code);
+ }
+ retro_tapping_counter = 0;
+ }
+ }
+ }
+# endif
+#endif
+
+#ifdef SWAP_HANDS_ENABLE
+# ifndef NO_ACTION_ONESHOT
+ if (event.pressed && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)) {
+ use_oneshot_swaphands();
+ }
+# endif
+#endif
+
+#ifndef NO_ACTION_ONESHOT
+ /* Because we switch layers after a oneshot event, we need to release the
+ * key before we leave the layer or no key up event will be generated.
+ */
+ if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED)) {
+ record->event.pressed = false;
+ layer_on(get_oneshot_layer());
+ process_record(record);
+ layer_off(get_oneshot_layer());
+ }
+#endif
+}
+
+/** \brief Utilities for actions. (FIXME: Needs better description)
+ *
+ * FIXME: Needs documentation.
+ */
+void register_code(uint8_t code) {
+ if (code == KC_NO) {
+ return;
+ }
+#ifdef LOCKING_SUPPORT_ENABLE
+ else if (KC_LOCKING_CAPS == code) {
+# ifdef LOCKING_RESYNC_ENABLE
+ // Resync: ignore if caps lock already is on
+ if (host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK)) return;
+# endif
+ add_key(KC_CAPSLOCK);
+ send_keyboard_report();
+ wait_ms(100);
+ del_key(KC_CAPSLOCK);
+ send_keyboard_report();
+ }
+
+ else if (KC_LOCKING_NUM == code) {
+# ifdef LOCKING_RESYNC_ENABLE
+ if (host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)) return;
+# endif
+ add_key(KC_NUMLOCK);
+ send_keyboard_report();
+ wait_ms(100);
+ del_key(KC_NUMLOCK);
+ send_keyboard_report();
+ }
+
+ else if (KC_LOCKING_SCROLL == code) {
+# ifdef LOCKING_RESYNC_ENABLE
+ if (host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK)) return;
+# endif
+ add_key(KC_SCROLLLOCK);
+ send_keyboard_report();
+ wait_ms(100);
+ del_key(KC_SCROLLLOCK);
+ send_keyboard_report();
+ }
+#endif
+
+ else if IS_KEY (code) {
+ // TODO: should push command_proc out of this block?
+ if (command_proc(code)) return;
+
+#ifndef NO_ACTION_ONESHOT
+/* TODO: remove
+ if (oneshot_state.mods && !oneshot_state.disabled) {
+ uint8_t tmp_mods = get_mods();
+ add_mods(oneshot_state.mods);
+
+ add_key(code);
+ send_keyboard_report();
+
+ set_mods(tmp_mods);
+ send_keyboard_report();
+ oneshot_cancel();
+ } else
+*/
+#endif
+ {
+ // Force a new key press if the key is already pressed
+ // without this, keys with the same keycode, but different
+ // modifiers will be reported incorrectly, see issue #1708
+ if (is_key_pressed(keyboard_report, code)) {
+ del_key(code);
+ send_keyboard_report();
+ }
+ add_key(code);
+ send_keyboard_report();
+ }
+ } else if IS_MOD (code) {
+ add_mods(MOD_BIT(code));
+ send_keyboard_report();
+ }
+#ifdef EXTRAKEY_ENABLE
+ else if IS_SYSTEM (code) {
+ host_system_send(KEYCODE2SYSTEM(code));
+ } else if IS_CONSUMER (code) {
+ host_consumer_send(KEYCODE2CONSUMER(code));
+ }
+#endif
+#ifdef MOUSEKEY_ENABLE
+ else if IS_MOUSEKEY (code) {
+ mousekey_on(code);
+ mousekey_send();
+ }
+#endif
+}
+
+/** \brief Utilities for actions. (FIXME: Needs better description)
+ *
+ * FIXME: Needs documentation.
+ */
+void unregister_code(uint8_t code) {
+ if (code == KC_NO) {
+ return;
+ }
+#ifdef LOCKING_SUPPORT_ENABLE
+ else if (KC_LOCKING_CAPS == code) {
+# ifdef LOCKING_RESYNC_ENABLE
+ // Resync: ignore if caps lock already is off
+ if (!(host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK))) return;
+# endif
+ add_key(KC_CAPSLOCK);
+ send_keyboard_report();
+ del_key(KC_CAPSLOCK);
+ send_keyboard_report();
+ }
+
+ else if (KC_LOCKING_NUM == code) {
+# ifdef LOCKING_RESYNC_ENABLE
+ if (!(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK))) return;
+# endif
+ add_key(KC_NUMLOCK);
+ send_keyboard_report();
+ del_key(KC_NUMLOCK);
+ send_keyboard_report();
+ }
+
+ else if (KC_LOCKING_SCROLL == code) {
+# ifdef LOCKING_RESYNC_ENABLE
+ if (!(host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK))) return;
+# endif
+ add_key(KC_SCROLLLOCK);
+ send_keyboard_report();
+ del_key(KC_SCROLLLOCK);
+ send_keyboard_report();
+ }
+#endif
+
+ else if IS_KEY (code) {
+ del_key(code);
+ send_keyboard_report();
+ } else if IS_MOD (code) {
+ del_mods(MOD_BIT(code));
+ send_keyboard_report();
+ } else if IS_SYSTEM (code) {
+ host_system_send(0);
+ } else if IS_CONSUMER (code) {
+ host_consumer_send(0);
+ }
+#ifdef MOUSEKEY_ENABLE
+ else if IS_MOUSEKEY (code) {
+ mousekey_off(code);
+ mousekey_send();
+ }
+#endif
+}
+
+/** \brief Tap a keycode with a delay.
+ *
+ * \param code The basic keycode to tap.
+ * \param delay The amount of time in milliseconds to leave the keycode registered, before unregistering it.
+ */
+void tap_code_delay(uint8_t code, uint16_t delay) {
+ register_code(code);
+ for (uint16_t i = delay; i > 0; i--) {
+ wait_ms(1);
+ }
+ unregister_code(code);
+}
+
+/** \brief Tap a keycode with the default delay.
+ *
+ * \param code The basic keycode to tap. If `code` is `KC_CAPS`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined.
+ */
+void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); }
+
+/** \brief Adds the given physically pressed modifiers and sends a keyboard report immediately.
+ *
+ * \param mods A bitfield of modifiers to register.
+ */
+void register_mods(uint8_t mods) {
+ if (mods) {
+ add_mods(mods);
+ send_keyboard_report();
+ }
+}
+
+/** \brief Removes the given physically pressed modifiers and sends a keyboard report immediately.
+ *
+ * \param mods A bitfield of modifiers to unregister.
+ */
+void unregister_mods(uint8_t mods) {
+ if (mods) {
+ del_mods(mods);
+ send_keyboard_report();
+ }
+}
+
+/** \brief Adds the given weak modifiers and sends a keyboard report immediately.
+ *
+ * \param mods A bitfield of modifiers to register.
+ */
+void register_weak_mods(uint8_t mods) {
+ if (mods) {
+ add_weak_mods(mods);
+ send_keyboard_report();
+ }
+}
+
+/** \brief Removes the given weak modifiers and sends a keyboard report immediately.
+ *
+ * \param mods A bitfield of modifiers to unregister.
+ */
+void unregister_weak_mods(uint8_t mods) {
+ if (mods) {
+ del_weak_mods(mods);
+ send_keyboard_report();
+ }
+}
+
+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);
+ } else {
+ do_code16(code, register_weak_mods);
+ }
+ register_code(code);
+}
+
+void unregister_code16(uint16_t code) {
+ unregister_code(code);
+ if (IS_MOD(code) || code == KC_NO) {
+ do_code16(code, unregister_mods);
+ } else {
+ do_code16(code, unregister_weak_mods);
+ }
+}
+
+void tap_code16(uint16_t code) {
+ register_code16(code);
+#if TAP_CODE_DELAY > 0
+ wait_ms(TAP_CODE_DELAY);
+#endif
+ unregister_code16(code);
+}
+
+/** \brief Utilities for actions. (FIXME: Needs better description)
+ *
+ * FIXME: Needs documentation.
+ */
+void clear_keyboard(void) {
+ clear_mods();
+ clear_keyboard_but_mods();
+}
+
+/** \brief Utilities for actions. (FIXME: Needs better description)
+ *
+ * FIXME: Needs documentation.
+ */
+void clear_keyboard_but_mods(void) {
+ clear_keys();
+ clear_keyboard_but_mods_and_keys();
+}
+
+/** \brief Utilities for actions. (FIXME: Needs better description)
+ *
+ * FIXME: Needs documentation.
+ */
+void clear_keyboard_but_mods_and_keys() {
+#ifdef EXTRAKEY_ENABLE
+ host_system_send(0);
+ host_consumer_send(0);
+#endif
+ clear_weak_mods();
+ clear_macro_mods();
+ send_keyboard_report();
+#ifdef MOUSEKEY_ENABLE
+ mousekey_clear();
+ mousekey_send();
+#endif
+}
+
+/** \brief Utilities for actions. (FIXME: Needs better description)
+ *
+ * FIXME: Needs documentation.
+ */
+bool is_tap_key(keypos_t key) {
+ action_t action = layer_switch_get_action(key);
+ return is_tap_action(action);
+}
+
+/** \brief Utilities for actions. (FIXME: Needs better description)
+ *
+ * FIXME: Needs documentation.
+ */
+bool is_tap_record(keyrecord_t *record) {
+#ifdef COMBO_ENABLE
+ action_t action;
+ if (record->keycode) {
+ action = action_for_keycode(record->keycode);
+ } else {
+ action = layer_switch_get_action(record->event.key);
+ }
+#else
+ action_t action = layer_switch_get_action(record->event.key);
+#endif
+ return is_tap_action(action);
+}
+
+/** \brief Utilities for actions. (FIXME: Needs better description)
+ *
+ * FIXME: Needs documentation.
+ */
+bool is_tap_action(action_t action) {
+ switch (action.kind.id) {
+ case ACT_LMODS_TAP:
+ case ACT_RMODS_TAP:
+ case ACT_LAYER_TAP:
+ case ACT_LAYER_TAP_EXT:
+ switch (action.layer_tap.code) {
+ case KC_NO ... KC_RGUI:
+ case OP_TAP_TOGGLE:
+ case OP_ONESHOT:
+ return true;
+ }
+ return false;
+ case ACT_SWAP_HANDS:
+ switch (action.swap.code) {
+ case KC_NO ... KC_RGUI:
+ case OP_SH_TAP_TOGGLE:
+ return true;
+ }
+ return false;
+ case ACT_MACRO:
+ case ACT_FUNCTION:
+ if (action.func.opt & FUNC_TAP) {
+ return true;
+ }
+ return false;
+ }
+ return false;
+}
+
+/** \brief Debug print (FIXME: Needs better description)
+ *
+ * FIXME: Needs documentation.
+ */
+void debug_event(keyevent_t event) { dprintf("%04X%c(%u)", (event.key.row << 8 | event.key.col), (event.pressed ? 'd' : 'u'), event.time); }
+/** \brief Debug print (FIXME: Needs better description)
+ *
+ * FIXME: Needs documentation.
+ */
+void debug_record(keyrecord_t record) {
+ debug_event(record.event);
+#ifndef NO_ACTION_TAPPING
+ dprintf(":%u%c", record.tap.count, (record.tap.interrupted ? '-' : ' '));
+#endif
+}
+
+/** \brief Debug print (FIXME: Needs better description)
+ *
+ * FIXME: Needs documentation.
+ */
+void debug_action(action_t action) {
+ switch (action.kind.id) {
+ case ACT_LMODS:
+ dprint("ACT_LMODS");
+ break;
+ case ACT_RMODS:
+ dprint("ACT_RMODS");
+ break;
+ case ACT_LMODS_TAP:
+ dprint("ACT_LMODS_TAP");
+ break;
+ case ACT_RMODS_TAP:
+ dprint("ACT_RMODS_TAP");
+ break;
+ case ACT_USAGE:
+ dprint("ACT_USAGE");
+ break;
+ case ACT_MOUSEKEY:
+ dprint("ACT_MOUSEKEY");
+ break;
+ case ACT_LAYER:
+ dprint("ACT_LAYER");
+ break;
+ case ACT_LAYER_MODS:
+ dprint("ACT_LAYER_MODS");
+ break;
+ case ACT_LAYER_TAP:
+ dprint("ACT_LAYER_TAP");
+ break;
+ case ACT_LAYER_TAP_EXT:
+ dprint("ACT_LAYER_TAP_EXT");
+ break;
+ case ACT_MACRO:
+ dprint("ACT_MACRO");
+ break;
+ case ACT_FUNCTION:
+ dprint("ACT_FUNCTION");
+ break;
+ case ACT_SWAP_HANDS:
+ dprint("ACT_SWAP_HANDS");
+ break;
+ default:
+ dprint("UNKNOWN");
+ break;
+ }
+ dprintf("[%X:%02X]", action.kind.param >> 8, action.kind.param & 0xff);
+}
diff --git a/quantum/action.h b/quantum/action.h
new file mode 100644
index 0000000000..4382c7ba4a
--- /dev/null
+++ b/quantum/action.h
@@ -0,0 +1,137 @@
+/*
+Copyright 2012,2013 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/>.
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "keyboard.h"
+#include "keycode.h"
+#include "action_code.h"
+#include "action_macro.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Disable macro and function features when LTO is enabled, since they break */
+#ifdef LTO_ENABLE
+# ifndef NO_ACTION_MACRO
+# define NO_ACTION_MACRO
+# endif
+# ifndef NO_ACTION_FUNCTION
+# define NO_ACTION_FUNCTION
+# endif
+#endif
+
+/* tapping count and state */
+typedef struct {
+ bool interrupted : 1;
+ bool reserved2 : 1;
+ bool reserved1 : 1;
+ bool reserved0 : 1;
+ uint8_t count : 4;
+} tap_t;
+
+/* Key event container for recording */
+typedef struct {
+ keyevent_t event;
+#ifndef NO_ACTION_TAPPING
+ tap_t tap;
+#endif
+#ifdef COMBO_ENABLE
+ uint16_t keycode;
+#endif
+} keyrecord_t;
+
+/* Execute action per keyevent */
+void action_exec(keyevent_t event);
+
+/* action for key */
+action_t action_for_key(uint8_t layer, keypos_t key);
+action_t action_for_keycode(uint16_t keycode);
+
+/* macro */
+const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt);
+
+/* user defined special function */
+void action_function(keyrecord_t *record, uint8_t id, uint8_t opt);
+
+/* keyboard-specific key event (pre)processing */
+bool process_record_quantum(keyrecord_t *record);
+
+/* Utilities for actions. */
+#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
+extern bool disable_action_cache;
+#endif
+
+/* Code for handling one-handed key modifiers. */
+#ifdef SWAP_HANDS_ENABLE
+extern bool swap_hands;
+extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS];
+# if (MATRIX_COLS <= 8)
+typedef uint8_t swap_state_row_t;
+# elif (MATRIX_COLS <= 16)
+typedef uint16_t swap_state_row_t;
+# elif (MATRIX_COLS <= 32)
+typedef uint32_t swap_state_row_t;
+# else
+# error "MATRIX_COLS: invalid value"
+# endif
+
+void process_hand_swap(keyevent_t *record);
+#endif
+
+void process_record_nocache(keyrecord_t *record);
+void process_record(keyrecord_t *record);
+void process_record_handler(keyrecord_t *record);
+void post_process_record_quantum(keyrecord_t *record);
+void process_action(keyrecord_t *record, action_t action);
+void register_code(uint8_t code);
+void unregister_code(uint8_t code);
+void tap_code(uint8_t code);
+void tap_code_delay(uint8_t code, uint16_t delay);
+void register_mods(uint8_t mods);
+void unregister_mods(uint8_t mods);
+void register_weak_mods(uint8_t mods);
+void unregister_weak_mods(uint8_t mods);
+void register_code16(uint16_t code);
+void unregister_code16(uint16_t code);
+void tap_code16(uint16_t code);
+// void set_mods(uint8_t mods);
+void clear_keyboard(void);
+void clear_keyboard_but_mods(void);
+void clear_keyboard_but_mods_and_keys(void);
+void layer_switch(uint8_t new_layer);
+bool is_tap_key(keypos_t key);
+bool is_tap_record(keyrecord_t *record);
+bool is_tap_action(action_t action);
+
+uint8_t extract_mod_bits(uint16_t code);
+
+#ifndef NO_ACTION_TAPPING
+void process_record_tap_hint(keyrecord_t *record);
+#endif
+
+/* debug */
+void debug_event(keyevent_t event);
+void debug_record(keyrecord_t record);
+void debug_action(action_t action);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/quantum/action_code.h b/quantum/action_code.h
new file mode 100644
index 0000000000..eb18c36ae8
--- /dev/null
+++ b/quantum/action_code.h
@@ -0,0 +1,308 @@
+/*
+Copyright 2013 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/>.
+*/
+
+#pragma once
+
+/** \brief Action codes
+ *
+ * 16bit code: action_kind(4bit) + action_parameter(12bit)
+ *
+ * Key Actions(00xx)
+ * -----------------
+ * ACT_MODS(000r):
+ * 000r|0000|0000 0000 No action code
+ * 000r|0000|0000 0001 Transparent code
+ * 000r|0000| keycode Key
+ * 000r|mods|0000 0000 Modifiers
+ * 000r|mods| keycode Modifiers+Key(Modified key)
+ * r: Left/Right flag(Left:0, Right:1)
+ *
+ * ACT_MODS_TAP(001r):
+ * 001r|mods|0000 0000 Modifiers with OneShot
+ * 001r|mods|0000 0001 Modifiers with tap toggle
+ * 001r|mods|0000 00xx (reserved)
+ * 001r|mods| keycode Modifiers with Tap Key(Dual role)
+ *
+ * Other Keys(01xx)
+ * ----------------
+ * ACT_USAGE(0100): TODO: Not needed?
+ * 0100|00| usage(10) System control(0x80) - General Desktop page(0x01)
+ * 0100|01| usage(10) Consumer control(0x01) - Consumer page(0x0C)
+ * 0100|10| usage(10) (reserved)
+ * 0100|11| usage(10) (reserved)
+ *
+ * ACT_MOUSEKEY(0101): TODO: Merge these two actions to conserve space?
+ * 0101|xxxx| keycode Mouse key
+ *
+ * ACT_SWAP_HANDS(0110):
+ * 0110|xxxx| keycode Swap hands (keycode on tap, or options)
+ *
+ * 0111|xxxx xxxx xxxx (reserved)
+ *
+ * Layer Actions(10xx)
+ * -------------------
+ * ACT_LAYER(1000):
+ * 1000|oo00|pppE BBBB Default Layer Bitwise operation
+ * oo: operation(00:AND, 01:OR, 10:XOR, 11:SET)
+ * ppp: 4-bit chunk part(0-7)
+ * EBBBB: bits and extra bit
+ * 1000|ooee|pppE BBBB Layer Bitwise Operation
+ * oo: operation(00:AND, 01:OR, 10:XOR, 11:SET)
+ * ppp: 4-bit chunk part(0-7)
+ * EBBBB: bits and extra bit
+ * ee: on event(01:press, 10:release, 11:both)
+ *
+ * ACT_LAYER_MODS(1001):
+ * 1001|LLLL| mods Layer with modifiers held
+ *
+ * ACT_LAYER_TAP(101x):
+ * 101E|LLLL| keycode On/Off with tap key (0x00-DF)[TAP]
+ * 101E|LLLL|1110 mods On/Off with modifiers (0xE0-EF)[NOT TAP]
+ * 101E|LLLL|1111 0000 Invert with tap toggle (0xF0) [TAP]
+ * 101E|LLLL|1111 0001 On/Off (0xF1) [NOT TAP]
+ * 101E|LLLL|1111 0010 Off/On (0xF2) [NOT TAP]
+ * 101E|LLLL|1111 0011 Set/Clear (0xF3) [NOT TAP]
+ * 101E|LLLL|1111 0100 One Shot Layer (0xF4) [TAP]
+ * 101E|LLLL|1111 xxxx Reserved (0xF5-FF)
+ * ELLLL: layer 0-31(E: extra bit for layer 16-31)
+ *
+ * Extensions(11xx)
+ * ----------------
+ * ACT_MACRO(1100):
+ * 1100|opt | id(8) Macro play?
+ * 1100|1111| id(8) Macro record?
+ *
+ * 1101|xxxx xxxx xxxx (reserved)
+ * 1110|xxxx xxxx xxxx (reserved)
+ *
+ * ACT_FUNCTION(1111):
+ * 1111| address(12) Function?
+ * 1111|opt | id(8) Function?
+ */
+enum action_kind_id {
+ /* Key Actions */
+ ACT_MODS = 0b0000,
+ ACT_LMODS = 0b0000,
+ ACT_RMODS = 0b0001,
+ ACT_MODS_TAP = 0b0010,
+ ACT_LMODS_TAP = 0b0010,
+ ACT_RMODS_TAP = 0b0011,
+ /* Other Keys */
+ ACT_USAGE = 0b0100,
+ ACT_MOUSEKEY = 0b0101,
+ /* One-hand Support */
+ ACT_SWAP_HANDS = 0b0110,
+ /* Layer Actions */
+ ACT_LAYER = 0b1000,
+ ACT_LAYER_MODS = 0b1001,
+ ACT_LAYER_TAP = 0b1010, /* Layer 0-15 */
+ ACT_LAYER_TAP_EXT = 0b1011, /* Layer 16-31 */
+ /* Extensions */
+ ACT_MACRO = 0b1100,
+ ACT_FUNCTION = 0b1111
+};
+
+/** \brief Action Code Struct
+ *
+ * NOTE:
+ * In avr-gcc bit field seems to be assigned from LSB(bit0) to MSB(bit15).
+ * AVR looks like a little endian in avr-gcc.
+ * Not portable across compiler/endianness?
+ *
+ * Byte order and bit order of 0x1234:
+ * Big endian: Little endian:
+ * -------------------- --------------------
+ * FEDC BA98 7654 3210 0123 4567 89AB CDEF
+ * 0001 0010 0011 0100 0010 1100 0100 1000
+ * 0x12 0x34 0x34 0x12
+ */
+typedef union {
+ uint16_t code;
+ struct action_kind {
+ uint16_t param : 12;
+ uint8_t id : 4;
+ } kind;
+ struct action_key {
+ uint8_t code : 8;
+ uint8_t mods : 4;
+ uint8_t kind : 4;
+ } key;
+ struct action_layer_bitop {
+ uint8_t bits : 4;
+ uint8_t xbit : 1;
+ uint8_t part : 3;
+ uint8_t on : 2;
+ uint8_t op : 2;
+ uint8_t kind : 4;
+ } layer_bitop;
+ struct action_layer_mods {
+ uint8_t mods : 8;
+ uint8_t layer : 4;
+ uint8_t kind : 4;
+ } layer_mods;
+ struct action_layer_tap {
+ uint8_t code : 8;
+ uint8_t val : 5;
+ uint8_t kind : 3;
+ } layer_tap;
+ struct action_usage {
+ uint16_t code : 10;
+ uint8_t page : 2;
+ uint8_t kind : 4;
+ } usage;
+ struct action_function {
+ uint8_t id : 8;
+ uint8_t opt : 4;
+ uint8_t kind : 4;
+ } func;
+ struct action_swap {
+ uint8_t code : 8;
+ uint8_t opt : 4;
+ uint8_t kind : 4;
+ } swap;
+} action_t;
+
+/* action utility */
+#define ACTION_NO 0
+#define ACTION_TRANSPARENT 1
+#define ACTION(kind, param) ((kind) << 12 | (param))
+
+/** \brief Key Actions
+ *
+ * Mod bits: 43210
+ * bit 0 ||||+- Control
+ * bit 1 |||+-- Shift
+ * bit 2 ||+--- Alt
+ * bit 3 |+---- Gui
+ * bit 4 +----- LR flag(Left:0, Right:1)
+ */
+enum mods_bit {
+ MOD_LCTL = 0x01,
+ MOD_LSFT = 0x02,
+ MOD_LALT = 0x04,
+ MOD_LGUI = 0x08,
+ MOD_RCTL = 0x11,
+ MOD_RSFT = 0x12,
+ MOD_RALT = 0x14,
+ MOD_RGUI = 0x18,
+};
+enum mods_codes {
+ MODS_ONESHOT = 0x00,
+ MODS_TAP_TOGGLE = 0x01,
+};
+#define ACTION_KEY(key) ACTION(ACT_MODS, (key))
+#define ACTION_MODS(mods) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | 0)
+#define ACTION_MODS_KEY(mods, key) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | (key))
+#define ACTION_MODS_TAP_KEY(mods, key) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | (key))
+#define ACTION_MODS_ONESHOT(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_ONESHOT)
+#define ACTION_MODS_TAP_TOGGLE(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_TAP_TOGGLE)
+
+/** \brief Other Keys
+ */
+enum usage_pages { PAGE_SYSTEM, PAGE_CONSUMER };
+#define ACTION_USAGE_SYSTEM(id) ACTION(ACT_USAGE, PAGE_SYSTEM << 10 | (id))
+#define ACTION_USAGE_CONSUMER(id) ACTION(ACT_USAGE, PAGE_CONSUMER << 10 | (id))
+#define ACTION_MOUSEKEY(key) ACTION(ACT_MOUSEKEY, key)
+
+/** \brief Layer Actions
+ */
+enum layer_param_on {
+ ON_PRESS = 1,
+ ON_RELEASE = 2,
+ ON_BOTH = 3,
+};
+
+/** \brief Layer Actions
+ */
+enum layer_param_bit_op {
+ OP_BIT_AND = 0,
+ OP_BIT_OR = 1,
+ OP_BIT_XOR = 2,
+ OP_BIT_SET = 3,
+};
+
+/** \brief Layer Actions
+ */
+enum layer_param_tap_op {
+ OP_TAP_TOGGLE = 0xF0,
+ OP_ON_OFF,
+ OP_OFF_ON,
+ OP_SET_CLEAR,
+ OP_ONESHOT,
+};
+#define ACTION_LAYER_BITOP(op, part, bits, on) ACTION(ACT_LAYER, (op) << 10 | (on) << 8 | (part) << 5 | ((bits)&0x1f))
+#define ACTION_LAYER_TAP(layer, key) ACTION(ACT_LAYER_TAP, (layer) << 8 | (key))
+/* Default Layer */
+#define ACTION_DEFAULT_LAYER_SET(layer) ACTION_DEFAULT_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4))
+/* Layer Operation */
+#define ACTION_LAYER_CLEAR(on) ACTION_LAYER_BIT_AND(0, 0, (on))
+#define ACTION_LAYER_MOMENTARY(layer) ACTION_LAYER_ON_OFF(layer)
+#define ACTION_LAYER_TOGGLE(layer) ACTION_LAYER_INVERT(layer, ON_RELEASE)
+#define ACTION_LAYER_INVERT(layer, on) ACTION_LAYER_BIT_XOR((layer) / 4, 1 << ((layer) % 4), (on))
+#define ACTION_LAYER_ON(layer, on) ACTION_LAYER_BIT_OR((layer) / 4, 1 << ((layer) % 4), (on))
+#define ACTION_LAYER_OFF(layer, on) ACTION_LAYER_BIT_AND((layer) / 4, ~(1 << ((layer) % 4)), (on))
+#define ACTION_LAYER_SET(layer, on) ACTION_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4), (on))
+#define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF)
+#define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON)
+#define ACTION_LAYER_SET_CLEAR(layer) ACTION_LAYER_TAP((layer), OP_SET_CLEAR)
+#define ACTION_LAYER_ONESHOT(layer) ACTION_LAYER_TAP((layer), OP_ONESHOT)
+#define ACTION_LAYER_MODS(layer, mods) ACTION(ACT_LAYER_MODS, (layer) << 8 | (mods))
+/* With Tapping */
+#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key))
+#define ACTION_LAYER_TAP_TOGGLE(layer) ACTION_LAYER_TAP((layer), OP_TAP_TOGGLE)
+/* Bitwise Operation */
+#define ACTION_LAYER_BIT_AND(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), (on))
+#define ACTION_LAYER_BIT_OR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), (on))
+#define ACTION_LAYER_BIT_XOR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), (on))
+#define ACTION_LAYER_BIT_SET(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), (on))
+/* Default Layer Bitwise Operation */
+#define ACTION_DEFAULT_LAYER_BIT_AND(part, bits) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), 0)
+#define ACTION_DEFAULT_LAYER_BIT_OR(part, bits) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), 0)
+#define ACTION_DEFAULT_LAYER_BIT_XOR(part, bits) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), 0)
+#define ACTION_DEFAULT_LAYER_BIT_SET(part, bits) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), 0)
+
+/* Macro */
+#define ACTION_MACRO(id) ACTION(ACT_MACRO, (id))
+#define ACTION_MACRO_TAP(id) ACTION(ACT_MACRO, FUNC_TAP << 8 | (id))
+#define ACTION_MACRO_OPT(id, opt) ACTION(ACT_MACRO, (opt) << 8 | (id))
+/* Function */
+enum function_opts {
+ FUNC_TAP = 0x8, /* indciates function is tappable */
+};
+#define ACTION_FUNCTION(id) ACTION(ACT_FUNCTION, (id))
+#define ACTION_FUNCTION_TAP(id) ACTION(ACT_FUNCTION, FUNC_TAP << 8 | (id))
+#define ACTION_FUNCTION_OPT(id, opt) ACTION(ACT_FUNCTION, (opt) << 8 | (id))
+/* OneHand Support */
+enum swap_hands_param_tap_op {
+ OP_SH_TOGGLE = 0xF0,
+ OP_SH_TAP_TOGGLE,
+ OP_SH_ON_OFF,
+ OP_SH_OFF_ON,
+ OP_SH_OFF,
+ OP_SH_ON,
+ OP_SH_ONESHOT,
+};
+
+#define ACTION_SWAP_HANDS() ACTION_SWAP_HANDS_ON_OFF()
+#define ACTION_SWAP_HANDS_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TOGGLE)
+#define ACTION_SWAP_HANDS_TAP_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TAP_TOGGLE)
+#define ACTION_SWAP_HANDS_ONESHOT() ACTION(ACT_SWAP_HANDS, OP_SH_ONESHOT)
+#define ACTION_SWAP_HANDS_TAP_KEY(key) ACTION(ACT_SWAP_HANDS, key)
+#define ACTION_SWAP_HANDS_ON_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_ON_OFF)
+#define ACTION_SWAP_HANDS_OFF_ON() ACTION(ACT_SWAP_HANDS, OP_SH_OFF_ON)
+#define ACTION_SWAP_HANDS_ON() ACTION(ACT_SWAP_HANDS, OP_SH_ON)
+#define ACTION_SWAP_HANDS_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_OFF)
diff --git a/quantum/action_layer.c b/quantum/action_layer.c
new file mode 100644
index 0000000000..ed1a4bd20d
--- /dev/null
+++ b/quantum/action_layer.c
@@ -0,0 +1,279 @@
+#include <stdint.h>
+#include "keyboard.h"
+#include "action.h"
+#include "util.h"
+#include "action_layer.h"
+
+#ifdef DEBUG_ACTION
+# include "debug.h"
+#else
+# include "nodebug.h"
+#endif
+
+/** \brief Default Layer State
+ */
+layer_state_t default_layer_state = 0;
+
+/** \brief Default Layer State Set At user Level
+ *
+ * Run user code on default layer state change
+ */
+__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state) { return state; }
+
+/** \brief Default Layer State Set At Keyboard Level
+ *
+ * Run keyboard code on default layer state change
+ */
+__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state) { return default_layer_state_set_user(state); }
+
+/** \brief Default Layer State Set
+ *
+ * Static function to set the default layer state, prints debug info and clears keys
+ */
+static void default_layer_state_set(layer_state_t state) {
+ state = default_layer_state_set_kb(state);
+ debug("default_layer_state: ");
+ default_layer_debug();
+ debug(" to ");
+ default_layer_state = state;
+ default_layer_debug();
+ debug("\n");
+#ifdef STRICT_LAYER_RELEASE
+ clear_keyboard_but_mods(); // To avoid stuck keys
+#else
+ clear_keyboard_but_mods_and_keys(); // Don't reset held keys
+#endif
+}
+
+/** \brief Default Layer Print
+ *
+ * Print out the hex value of the 32-bit default layer state, as well as the value of the highest bit.
+ */
+void default_layer_debug(void) { dprintf("%08lX(%u)", default_layer_state, get_highest_layer(default_layer_state)); }
+
+/** \brief Default Layer Set
+ *
+ * Sets the default layer state.
+ */
+void default_layer_set(layer_state_t state) { default_layer_state_set(state); }
+
+#ifndef NO_ACTION_LAYER
+/** \brief Default Layer Or
+ *
+ * Turns on the default layer based on matching bits between specifed layer and existing layer state
+ */
+void default_layer_or(layer_state_t state) { default_layer_state_set(default_layer_state | state); }
+/** \brief Default Layer And
+ *
+ * Turns on default layer based on matching enabled bits between specifed layer and existing layer state
+ */
+void default_layer_and(layer_state_t state) { default_layer_state_set(default_layer_state & state); }
+/** \brief Default Layer Xor
+ *
+ * Turns on default layer based on non-matching bits between specifed layer and existing layer state
+ */
+void default_layer_xor(layer_state_t state) { default_layer_state_set(default_layer_state ^ state); }
+#endif
+
+#ifndef NO_ACTION_LAYER
+/** \brief Keymap Layer State
+ */
+layer_state_t layer_state = 0;
+
+/** \brief Layer state set user
+ *
+ * Runs user code on layer state change
+ */
+__attribute__((weak)) layer_state_t layer_state_set_user(layer_state_t state) { return state; }
+
+/** \brief Layer state set keyboard
+ *
+ * Runs keyboard code on layer state change
+ */
+__attribute__((weak)) layer_state_t layer_state_set_kb(layer_state_t state) { return layer_state_set_user(state); }
+
+/** \brief Layer state set
+ *
+ * Sets the layer to match the specifed state (a bitmask)
+ */
+void layer_state_set(layer_state_t state) {
+ state = layer_state_set_kb(state);
+ dprint("layer_state: ");
+ layer_debug();
+ dprint(" to ");
+ layer_state = state;
+ layer_debug();
+ dprintln();
+# ifdef STRICT_LAYER_RELEASE
+ clear_keyboard_but_mods(); // To avoid stuck keys
+# else
+ clear_keyboard_but_mods_and_keys(); // Don't reset held keys
+# endif
+}
+
+/** \brief Layer clear
+ *
+ * Turn off all layers
+ */
+void layer_clear(void) { layer_state_set(0); }
+
+/** \brief Layer state is
+ *
+ * Return whether the given state is on (it might still be shadowed by a higher state, though)
+ */
+bool layer_state_is(uint8_t layer) { return layer_state_cmp(layer_state, layer); }
+
+/** \brief Layer state compare
+ *
+ * Used for comparing layers {mostly used for unit testing}
+ */
+bool layer_state_cmp(layer_state_t cmp_layer_state, uint8_t layer) {
+ if (!cmp_layer_state) {
+ return layer == 0;
+ }
+ return (cmp_layer_state & ((layer_state_t)1 << layer)) != 0;
+}
+
+/** \brief Layer move
+ *
+ * Turns on the given layer and turn off all other layers
+ */
+void layer_move(uint8_t layer) { layer_state_set((layer_state_t)1 << layer); }
+
+/** \brief Layer on
+ *
+ * Turns on given layer
+ */
+void layer_on(uint8_t layer) { layer_state_set(layer_state | ((layer_state_t)1 << layer)); }
+
+/** \brief Layer off
+ *
+ * Turns off given layer
+ */
+void layer_off(uint8_t layer) { layer_state_set(layer_state & ~((layer_state_t)1 << layer)); }
+
+/** \brief Layer invert
+ *
+ * Toggle the given layer (set it if it's unset, or unset it if it's set)
+ */
+void layer_invert(uint8_t layer) { layer_state_set(layer_state ^ ((layer_state_t)1 << layer)); }
+
+/** \brief Layer or
+ *
+ * Turns on layers based on matching bits between specifed layer and existing layer state
+ */
+void layer_or(layer_state_t state) { layer_state_set(layer_state | state); }
+/** \brief Layer and
+ *
+ * Turns on layers based on matching enabled bits between specifed layer and existing layer state
+ */
+void layer_and(layer_state_t state) { layer_state_set(layer_state & state); }
+/** \brief Layer xor
+ *
+ * Turns on layers based on non-matching bits between specifed layer and existing layer state
+ */
+void layer_xor(layer_state_t state) { layer_state_set(layer_state ^ state); }
+
+/** \brief Layer debug printing
+ *
+ * Print out the hex value of the 32-bit layer state, as well as the value of the highest bit.
+ */
+void layer_debug(void) { dprintf("%08lX(%u)", layer_state, get_highest_layer(layer_state)); }
+#endif
+
+#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
+/** \brief source layer cache
+ */
+
+uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS] = {{0}};
+
+/** \brief update source layers cache
+ *
+ * Updates the cached keys when changing layers
+ */
+void update_source_layers_cache(keypos_t key, uint8_t layer) {
+ const uint8_t key_number = key.col + (key.row * MATRIX_COLS);
+ const uint8_t storage_row = key_number / 8;
+ const uint8_t storage_bit = key_number % 8;
+
+ for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
+ source_layers_cache[storage_row][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ source_layers_cache[storage_row][bit_number]) & (1U << storage_bit);
+ }
+}
+
+/** \brief read source layers cache
+ *
+ * reads the cached keys stored when the layer was changed
+ */
+uint8_t read_source_layers_cache(keypos_t key) {
+ const uint8_t key_number = key.col + (key.row * MATRIX_COLS);
+ const uint8_t storage_row = key_number / 8;
+ const uint8_t storage_bit = key_number % 8;
+ uint8_t layer = 0;
+
+ for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {
+ layer |= ((source_layers_cache[storage_row][bit_number] & (1U << storage_bit)) != 0) << bit_number;
+ }
+
+ return layer;
+}
+#endif
+
+/** \brief Store or get action (FIXME: Needs better summary)
+ *
+ * Make sure the action triggered when the key is released is the same
+ * one as the one triggered on press. It's important for the mod keys
+ * when the layer is switched after the down event but before the up
+ * event as they may get stuck otherwise.
+ */
+action_t store_or_get_action(bool pressed, keypos_t key) {
+#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
+ if (disable_action_cache) {
+ return layer_switch_get_action(key);
+ }
+
+ uint8_t layer;
+
+ if (pressed) {
+ layer = layer_switch_get_layer(key);
+ update_source_layers_cache(key, layer);
+ } else {
+ layer = read_source_layers_cache(key);
+ }
+ return action_for_key(layer, key);
+#else
+ return layer_switch_get_action(key);
+#endif
+}
+
+/** \brief Layer switch get layer
+ *
+ * Gets the layer based on key info
+ */
+uint8_t layer_switch_get_layer(keypos_t key) {
+#ifndef NO_ACTION_LAYER
+ action_t action;
+ action.code = ACTION_TRANSPARENT;
+
+ layer_state_t layers = layer_state | default_layer_state;
+ /* check top layer first */
+ for (int8_t i = MAX_LAYER - 1; i >= 0; i--) {
+ if (layers & ((layer_state_t)1 << i)) {
+ action = action_for_key(i, key);
+ if (action.code != ACTION_TRANSPARENT) {
+ return i;
+ }
+ }
+ }
+ /* fall back to layer 0 */
+ return 0;
+#else
+ return get_highest_layer(default_layer_state);
+#endif
+}
+
+/** \brief Layer switch get layer
+ *
+ * Gets action code based on key position
+ */
+action_t layer_switch_get_action(keypos_t key) { return action_for_key(layer_switch_get_layer(key), key); }
diff --git a/quantum/action_layer.h b/quantum/action_layer.h
new file mode 100644
index 0000000000..b87d096eed
--- /dev/null
+++ b/quantum/action_layer.h
@@ -0,0 +1,147 @@
+/*
+Copyright 2013 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/>.
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include "keyboard.h"
+#include "action.h"
+
+#ifdef DYNAMIC_KEYMAP_ENABLE
+# ifndef DYNAMIC_KEYMAP_LAYER_COUNT
+# define DYNAMIC_KEYMAP_LAYER_COUNT 4
+# endif
+# if DYNAMIC_KEYMAP_LAYER_COUNT <= 8
+# ifndef LAYER_STATE_8BIT
+# define LAYER_STATE_8BIT
+# endif
+# elif DYNAMIC_KEYMAP_LAYER_COUNT <= 16
+# ifndef LAYER_STATE_16BIT
+# define LAYER_STATE_16BIT
+# endif
+# else
+# ifndef LAYER_STATE_32BIT
+# define LAYER_STATE_32BIT
+# endif
+# endif
+#endif
+
+#if !defined(LAYER_STATE_8BIT) && !defined(LAYER_STATE_16BIT) && !defined(LAYER_STATE_32BIT)
+# define LAYER_STATE_32BIT
+#endif
+
+#if defined(LAYER_STATE_8BIT)
+typedef uint8_t layer_state_t;
+# define MAX_LAYER_BITS 3
+# ifndef MAX_LAYER
+# define MAX_LAYER 8
+# endif
+# define get_highest_layer(state) biton(state)
+#elif defined(LAYER_STATE_16BIT)
+typedef uint16_t layer_state_t;
+# define MAX_LAYER_BITS 4
+# ifndef MAX_LAYER
+# define MAX_LAYER 16
+# endif
+# define get_highest_layer(state) biton16(state)
+#elif defined(LAYER_STATE_32BIT)
+typedef uint32_t layer_state_t;
+# define MAX_LAYER_BITS 5
+# ifndef MAX_LAYER
+# define MAX_LAYER 32
+# endif
+# define get_highest_layer(state) biton32(state)
+#else
+# error Layer Mask size not specified. HOW?!
+#endif
+
+/*
+ * Default Layer
+ */
+extern layer_state_t default_layer_state;
+void default_layer_debug(void);
+void default_layer_set(layer_state_t state);
+
+__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state);
+__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state);
+
+#ifndef NO_ACTION_LAYER
+/* bitwise operation */
+void default_layer_or(layer_state_t state);
+void default_layer_and(layer_state_t state);
+void default_layer_xor(layer_state_t state);
+#else
+# define default_layer_or(state)
+# define default_layer_and(state)
+# define default_layer_xor(state)
+#endif
+
+/*
+ * Keymap Layer
+ */
+#ifndef NO_ACTION_LAYER
+extern layer_state_t layer_state;
+
+void layer_state_set(layer_state_t state);
+bool layer_state_is(uint8_t layer);
+bool layer_state_cmp(layer_state_t layer1, uint8_t layer2);
+
+void layer_debug(void);
+void layer_clear(void);
+void layer_move(uint8_t layer);
+void layer_on(uint8_t layer);
+void layer_off(uint8_t layer);
+void layer_invert(uint8_t layer);
+/* bitwise operation */
+void layer_or(layer_state_t state);
+void layer_and(layer_state_t state);
+void layer_xor(layer_state_t state);
+layer_state_t layer_state_set_user(layer_state_t state);
+layer_state_t layer_state_set_kb(layer_state_t state);
+#else
+# define layer_state 0
+
+# define layer_state_set(layer)
+# define layer_state_is(layer) (layer == 0)
+# define layer_state_cmp(state, layer) (state == 0 ? layer == 0 : (state & (layer_state_t)1 << layer) != 0)
+
+# define layer_debug()
+# define layer_clear()
+# define layer_move(layer) (void)layer
+# define layer_on(layer) (void)layer
+# define layer_off(layer) (void)layer
+# define layer_invert(layer) (void)layer
+# define layer_or(state) (void)state
+# define layer_and(state) (void)state
+# define layer_xor(state) (void)state
+# define layer_state_set_kb(state) (void)state
+# define layer_state_set_user(state) (void)state
+#endif
+
+/* pressed actions cache */
+#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
+
+void update_source_layers_cache(keypos_t key, uint8_t layer);
+uint8_t read_source_layers_cache(keypos_t key);
+#endif
+action_t store_or_get_action(bool pressed, keypos_t key);
+
+/* return the topmost non-transparent layer currently associated with key */
+uint8_t layer_switch_get_layer(keypos_t key);
+
+/* return action depending on current layer status */
+action_t layer_switch_get_action(keypos_t key);
diff --git a/quantum/action_macro.c b/quantum/action_macro.c
new file mode 100644
index 0000000000..92228c0ba8
--- /dev/null
+++ b/quantum/action_macro.c
@@ -0,0 +1,93 @@
+/*
+Copyright 2013 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 "action.h"
+#include "action_util.h"
+#include "action_macro.h"
+#include "wait.h"
+
+#ifdef DEBUG_ACTION
+# include "debug.h"
+#else
+# include "nodebug.h"
+#endif
+
+#ifndef NO_ACTION_MACRO
+
+# define MACRO_READ() (macro = MACRO_GET(macro_p++))
+/** \brief Action Macro Play
+ *
+ * FIXME: Needs doc
+ */
+void action_macro_play(const macro_t *macro_p) {
+ macro_t macro = END;
+ uint8_t interval = 0;
+
+ if (!macro_p) return;
+ while (true) {
+ switch (MACRO_READ()) {
+ case KEY_DOWN:
+ MACRO_READ();
+ dprintf("KEY_DOWN(%02X)\n", macro);
+ if (IS_MOD(macro)) {
+ add_macro_mods(MOD_BIT(macro));
+ send_keyboard_report();
+ } else {
+ register_code(macro);
+ }
+ break;
+ case KEY_UP:
+ MACRO_READ();
+ dprintf("KEY_UP(%02X)\n", macro);
+ if (IS_MOD(macro)) {
+ del_macro_mods(MOD_BIT(macro));
+ send_keyboard_report();
+ } else {
+ unregister_code(macro);
+ }
+ break;
+ case WAIT:
+ MACRO_READ();
+ dprintf("WAIT(%u)\n", macro);
+ {
+ uint8_t ms = macro;
+ while (ms--) wait_ms(1);
+ }
+ break;
+ case INTERVAL:
+ interval = MACRO_READ();
+ dprintf("INTERVAL(%u)\n", interval);
+ break;
+ case 0x04 ... 0x73:
+ dprintf("DOWN(%02X)\n", macro);
+ register_code(macro);
+ break;
+ case 0x84 ... 0xF3:
+ dprintf("UP(%02X)\n", macro);
+ unregister_code(macro & 0x7F);
+ break;
+ case END:
+ default:
+ return;
+ }
+ // interval
+ {
+ uint8_t ms = interval;
+ while (ms--) wait_ms(1);
+ }
+ }
+}
+#endif
diff --git a/quantum/action_macro.h b/quantum/action_macro.h
new file mode 100644
index 0000000000..685e2c6ffc
--- /dev/null
+++ b/quantum/action_macro.h
@@ -0,0 +1,123 @@
+/*
+Copyright 2013 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/>.
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include "progmem.h"
+
+typedef uint8_t macro_t;
+
+#define MACRO_NONE (macro_t *)0
+#define MACRO(...) \
+ ({ \
+ static const macro_t __m[] PROGMEM = {__VA_ARGS__}; \
+ &__m[0]; \
+ })
+#define MACRO_GET(p) pgm_read_byte(p)
+
+// Sends press when the macro key is pressed, release when release, or tap_macro when the key has been tapped
+#define MACRO_TAP_HOLD(record, press, release, tap_macro) (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? (press) : MACRO_NONE) : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (tap_macro) : (release)))
+
+// Holds down the modifier mod when the macro key is held, or sends macro instead when tapped
+#define MACRO_TAP_HOLD_MOD(record, macro, mod) MACRO_TAP_HOLD(record, (MACRO(D(mod), END)), MACRO(U(mod), END), macro)
+
+// Holds down the modifier mod when the macro key is held, or pressed a shifted key when tapped (eg: shift+3 for #)
+#define MACRO_TAP_SHFT_KEY_HOLD_MOD(record, key, mod) MACRO_TAP_HOLD_MOD(record, (MACRO(I(10), D(LSFT), T(key), U(LSFT), END)), mod)
+
+// Momentary switch layer when held, sends macro if tapped
+#define MACRO_TAP_HOLD_LAYER(record, macro, layer) \
+ (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? ({ \
+ layer_on((layer)); \
+ MACRO_NONE; \
+ }) \
+ : MACRO_NONE) \
+ : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (macro) : ({ \
+ layer_off((layer)); \
+ MACRO_NONE; \
+ })))
+
+// Momentary switch layer when held, presses a shifted key when tapped (eg: shift+3 for #)
+#define MACRO_TAP_SHFT_KEY_HOLD_LAYER(record, key, layer) MACRO_TAP_HOLD_LAYER(record, MACRO(I(10), D(LSFT), T(key), U(LSFT), END), layer)
+
+#ifndef NO_ACTION_MACRO
+void action_macro_play(const macro_t *macro_p);
+#else
+# define action_macro_play(macro)
+#endif
+
+/* Macro commands
+ * code(0x04-73) // key down(1byte)
+ * code(0x04-73) | 0x80 // key up(1byte)
+ * { KEY_DOWN, code(0x04-0xff) } // key down(2bytes)
+ * { KEY_UP, code(0x04-0xff) } // key up(2bytes)
+ * WAIT // wait milli-seconds
+ * INTERVAL // set interval between macro commands
+ * END // stop macro execution
+ *
+ * Ideas(Not implemented):
+ * modifiers
+ * system usage
+ * consumer usage
+ * unicode usage
+ * function call
+ * conditionals
+ * loop
+ */
+enum macro_command_id {
+ /* 0x00 - 0x03 */
+ END = 0x00,
+ KEY_DOWN,
+ KEY_UP,
+
+ /* 0x04 - 0x73 (reserved for keycode down) */
+
+ /* 0x74 - 0x83 */
+ WAIT = 0x74,
+ INTERVAL,
+
+ /* 0x84 - 0xf3 (reserved for keycode up) */
+
+ /* 0xf4 - 0xff */
+};
+
+/* TODO: keycode:0x04-0x73 can be handled by 1byte command else 2bytes are needed
+ * if keycode between 0x04 and 0x73
+ * keycode / (keycode|0x80)
+ * else
+ * {KEY_DOWN, keycode} / {KEY_UP, keycode}
+ */
+#define DOWN(key) KEY_DOWN, (key)
+#define UP(key) KEY_UP, (key)
+#define TYPE(key) DOWN(key), UP(key)
+#define WAIT(ms) WAIT, (ms)
+#define INTERVAL(ms) INTERVAL, (ms)
+
+/* key down */
+#define D(key) DOWN(KC_##key)
+/* key up */
+#define U(key) UP(KC_##key)
+/* key type */
+#define T(key) TYPE(KC_##key)
+/* wait */
+#define W(ms) WAIT(ms)
+/* interval */
+#define I(ms) INTERVAL(ms)
+
+/* for backward comaptibility */
+#define MD(key) DOWN(KC_##key)
+#define MU(key) UP(KC_##key)
diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c
new file mode 100644
index 0000000000..eef6ed1b7d
--- /dev/null
+++ b/quantum/action_tapping.c
@@ -0,0 +1,491 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include "action.h"
+#include "action_layer.h"
+#include "action_tapping.h"
+#include "keycode.h"
+#include "timer.h"
+#include "keymap.h"
+
+#ifdef DEBUG_ACTION
+# include "debug.h"
+#else
+# include "nodebug.h"
+#endif
+
+#ifndef NO_ACTION_TAPPING
+
+# define IS_TAPPING() !IS_NOEVENT(tapping_key.event)
+# define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed)
+# define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed)
+# define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k)))
+#ifndef COMBO_ENABLE
+# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)))
+#else
+# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode)
+#endif
+
+__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; }
+
+# ifdef TAPPING_TERM_PER_KEY
+# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_record_keycode(&tapping_key, false), &tapping_key))
+# else
+# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM)
+# endif
+
+# ifdef TAPPING_FORCE_HOLD_PER_KEY
+__attribute__((weak)) bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record) { return false; }
+# endif
+
+# ifdef PERMISSIVE_HOLD_PER_KEY
+__attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) { return false; }
+# endif
+
+# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
+__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { return false; }
+# endif
+
+static keyrecord_t tapping_key = {};
+static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
+static uint8_t waiting_buffer_head = 0;
+static uint8_t waiting_buffer_tail = 0;
+
+static bool process_tapping(keyrecord_t *record);
+static bool waiting_buffer_enq(keyrecord_t record);
+static void waiting_buffer_clear(void);
+static bool waiting_buffer_typed(keyevent_t event);
+static bool waiting_buffer_has_anykey_pressed(void);
+static void waiting_buffer_scan_tap(void);
+static void debug_tapping_key(void);
+static void debug_waiting_buffer(void);
+
+/* Convert record into usable keycode via the contained event. */
+uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache) {
+#ifdef COMBO_ENABLE
+ if (record->keycode) { return record->keycode; }
+#endif
+ return get_event_keycode(record->event, update_layer_cache);
+}
+
+/* Convert event into usable keycode. Checks the layer cache to ensure that it
+ * retains the correct keycode after a layer change, if the key is still pressed.
+ * "update_layer_cache" is to ensure that it only updates the layer cache when
+ * appropriate, otherwise, it will update it and cause layer tap (and other keys)
+ * from triggering properly.
+ */
+uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache) {
+ const keypos_t key = event.key;
+
+#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
+ /* TODO: Use store_or_get_action() or a similar function. */
+ if (!disable_action_cache) {
+ uint8_t layer;
+
+ if (event.pressed && update_layer_cache) {
+ layer = layer_switch_get_layer(key);
+ update_source_layers_cache(key, layer);
+ } else {
+ layer = read_source_layers_cache(key);
+ }
+ return keymap_key_to_keycode(layer, key);
+ }
+#endif
+ return keymap_key_to_keycode(layer_switch_get_layer(key), key);
+}
+
+/** \brief Action Tapping Process
+ *
+ * FIXME: Needs doc
+ */
+void action_tapping_process(keyrecord_t record) {
+ if (process_tapping(&record)) {
+ if (!IS_NOEVENT(record.event)) {
+ debug("processed: ");
+ debug_record(record);
+ debug("\n");
+ }
+ } else {
+ if (!waiting_buffer_enq(record)) {
+ // clear all in case of overflow.
+ debug("OVERFLOW: CLEAR ALL STATES\n");
+ clear_keyboard();
+ waiting_buffer_clear();
+ tapping_key = (keyrecord_t){};
+ }
+ }
+
+ // process waiting_buffer
+ if (!IS_NOEVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) {
+ debug("---- action_exec: process waiting_buffer -----\n");
+ }
+ for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {
+ if (process_tapping(&waiting_buffer[waiting_buffer_tail])) {
+ debug("processed: waiting_buffer[");
+ debug_dec(waiting_buffer_tail);
+ debug("] = ");
+ debug_record(waiting_buffer[waiting_buffer_tail]);
+ debug("\n\n");
+ } else {
+ break;
+ }
+ }
+ if (!IS_NOEVENT(record.event)) {
+ debug("\n");
+ }
+}
+
+/** \brief Tapping
+ *
+ * Rule: Tap key is typed(pressed and released) within TAPPING_TERM.
+ * (without interfering by typing other key)
+ */
+/* return true when key event is processed or consumed. */
+bool process_tapping(keyrecord_t *keyp) {
+ keyevent_t event = keyp->event;
+
+ // if tapping
+ if (IS_TAPPING_PRESSED()) {
+ if (WITHIN_TAPPING_TERM(event)) {
+ if (tapping_key.tap.count == 0) {
+ if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
+ // first tap!
+ debug("Tapping: First tap(0->1).\n");
+ tapping_key.tap.count = 1;
+ debug_tapping_key();
+ process_record(&tapping_key);
+
+ // copy tapping state
+ keyp->tap = tapping_key.tap;
+ // enqueue
+ return false;
+ }
+ /* Process a key typed within TAPPING_TERM
+ * This can register the key before settlement of tapping,
+ * useful for long TAPPING_TERM but may prevent fast typing.
+ */
+# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY)
+ else if (((
+# ifdef TAPPING_TERM_PER_KEY
+ get_tapping_term(get_record_keycode(&tapping_key, false), keyp)
+# else
+ TAPPING_TERM
+# endif
+ >= 500)
+
+# ifdef PERMISSIVE_HOLD_PER_KEY
+ || get_permissive_hold(get_record_keycode(&tapping_key, false), keyp)
+# elif defined(PERMISSIVE_HOLD)
+ || true
+# endif
+ ) &&
+ IS_RELEASED(event) && waiting_buffer_typed(event)) {
+ debug("Tapping: End. No tap. Interfered by typing key\n");
+ process_record(&tapping_key);
+ tapping_key = (keyrecord_t){};
+ debug_tapping_key();
+ // enqueue
+ return false;
+ }
+# endif
+ /* Process release event of a key pressed before tapping starts
+ * Without this unexpected repeating will occur with having fast repeating setting
+ * https://github.com/tmk/tmk_keyboard/issues/60
+ */
+ else if (IS_RELEASED(event) && !waiting_buffer_typed(event)) {
+ // Modifier should be retained till end of this tapping.
+ action_t action = layer_switch_get_action(event.key);
+ switch (action.kind.id) {
+ case ACT_LMODS:
+ case ACT_RMODS:
+ if (action.key.mods && !action.key.code) return false;
+ if (IS_MOD(action.key.code)) return false;
+ break;
+ case ACT_LMODS_TAP:
+ case ACT_RMODS_TAP:
+ if (action.key.mods && keyp->tap.count == 0) return false;
+ if (IS_MOD(action.key.code)) return false;
+ break;
+ }
+ // Release of key should be process immediately.
+ debug("Tapping: release event of a key pressed before tapping\n");
+ process_record(keyp);
+ return true;
+ } else {
+ // set interrupted flag when other key preesed during tapping
+ if (event.pressed) {
+ tapping_key.tap.interrupted = true;
+# if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
+# if defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
+ if (get_hold_on_other_key_press(get_record_keycode(&tapping_key, false), keyp))
+# endif
+ {
+ debug("Tapping: End. No tap. Interfered by pressed key\n");
+ process_record(&tapping_key);
+ tapping_key = (keyrecord_t){};
+ debug_tapping_key();
+ // enqueue
+ return false;
+ }
+# endif
+ }
+ // enqueue
+ return false;
+ }
+ }
+ // tap_count > 0
+ else {
+ if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
+ debug("Tapping: Tap release(");
+ debug_dec(tapping_key.tap.count);
+ debug(")\n");
+ keyp->tap = tapping_key.tap;
+ process_record(keyp);
+ tapping_key = *keyp;
+ debug_tapping_key();
+ return true;
+ } else if (is_tap_record(keyp) && event.pressed) {
+ if (tapping_key.tap.count > 1) {
+ debug("Tapping: Start new tap with releasing last tap(>1).\n");
+ // unregister key
+ process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false,
+#ifdef COMBO_ENABLE
+ .keycode = tapping_key.keycode,
+#endif
+ });
+ } else {
+ debug("Tapping: Start while last tap(1).\n");
+ }
+ tapping_key = *keyp;
+ waiting_buffer_scan_tap();
+ debug_tapping_key();
+ return true;
+ } else {
+ if (!IS_NOEVENT(event)) {
+ debug("Tapping: key event while last tap(>0).\n");
+ }
+ process_record(keyp);
+ return true;
+ }
+ }
+ }
+ // after TAPPING_TERM
+ else {
+ if (tapping_key.tap.count == 0) {
+ debug("Tapping: End. Timeout. Not tap(0): ");
+ debug_event(event);
+ debug("\n");
+ process_record(&tapping_key);
+ tapping_key = (keyrecord_t){};
+ debug_tapping_key();
+ return false;
+ } else {
+ if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
+ debug("Tapping: End. last timeout tap release(>0).");
+ keyp->tap = tapping_key.tap;
+ process_record(keyp);
+ tapping_key = (keyrecord_t){};
+ return true;
+ } else if (is_tap_record(keyp) && event.pressed) {
+ if (tapping_key.tap.count > 1) {
+ debug("Tapping: Start new tap with releasing last timeout tap(>1).\n");
+ // unregister key
+ process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false,
+#ifdef COMBO_ENABLE
+ .keycode = tapping_key.keycode,
+#endif
+ });
+ } else {
+ debug("Tapping: Start while last timeout tap(1).\n");
+ }
+ tapping_key = *keyp;
+ waiting_buffer_scan_tap();
+ debug_tapping_key();
+ return true;
+ } else {
+ if (!IS_NOEVENT(event)) {
+ debug("Tapping: key event while last timeout tap(>0).\n");
+ }
+ process_record(keyp);
+ return true;
+ }
+ }
+ }
+ } else if (IS_TAPPING_RELEASED()) {
+ if (WITHIN_TAPPING_TERM(event)) {
+ if (event.pressed) {
+ if (IS_TAPPING_RECORD(keyp)) {
+//# ifndef TAPPING_FORCE_HOLD
+# if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY)
+ if (
+# ifdef TAPPING_FORCE_HOLD_PER_KEY
+ !get_tapping_force_hold(get_record_keycode(&tapping_key, false), keyp) &&
+# endif
+ !tapping_key.tap.interrupted && tapping_key.tap.count > 0) {
+ // sequential tap.
+ keyp->tap = tapping_key.tap;
+ if (keyp->tap.count < 15) keyp->tap.count += 1;
+ debug("Tapping: Tap press(");
+ debug_dec(keyp->tap.count);
+ debug(")\n");
+ process_record(keyp);
+ tapping_key = *keyp;
+ debug_tapping_key();
+ return true;
+ }
+# endif
+ // FIX: start new tap again
+ tapping_key = *keyp;
+ return true;
+ } else if (is_tap_record(keyp)) {
+ // Sequential tap can be interfered with other tap key.
+ debug("Tapping: Start with interfering other tap.\n");
+ tapping_key = *keyp;
+ waiting_buffer_scan_tap();
+ debug_tapping_key();
+ return true;
+ } else {
+ // should none in buffer
+ // FIX: interrupted when other key is pressed
+ tapping_key.tap.interrupted = true;
+ process_record(keyp);
+ return true;
+ }
+ } else {
+ if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n");
+ process_record(keyp);
+ return true;
+ }
+ } else {
+ // FIX: process_action here?
+ // timeout. no sequential tap.
+ debug("Tapping: End(Timeout after releasing last tap): ");
+ debug_event(event);
+ debug("\n");
+ tapping_key = (keyrecord_t){};
+ debug_tapping_key();
+ return false;
+ }
+ }
+ // not tapping state
+ else {
+ if (event.pressed && is_tap_record(keyp)) {
+ debug("Tapping: Start(Press tap key).\n");
+ tapping_key = *keyp;
+ process_record_tap_hint(&tapping_key);
+ waiting_buffer_scan_tap();
+ debug_tapping_key();
+ return true;
+ } else {
+ process_record(keyp);
+ return true;
+ }
+ }
+}
+
+/** \brief Waiting buffer enq
+ *
+ * FIXME: Needs docs
+ */
+bool waiting_buffer_enq(keyrecord_t record) {
+ if (IS_NOEVENT(record.event)) {
+ return true;
+ }
+
+ if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) {
+ debug("waiting_buffer_enq: Over flow.\n");
+ return false;
+ }
+
+ waiting_buffer[waiting_buffer_head] = record;
+ waiting_buffer_head = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE;
+
+ debug("waiting_buffer_enq: ");
+ debug_waiting_buffer();
+ return true;
+}
+
+/** \brief Waiting buffer clear
+ *
+ * FIXME: Needs docs
+ */
+void waiting_buffer_clear(void) {
+ waiting_buffer_head = 0;
+ waiting_buffer_tail = 0;
+}
+
+/** \brief Waiting buffer typed
+ *
+ * FIXME: Needs docs
+ */
+bool waiting_buffer_typed(keyevent_t event) {
+ for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
+ if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/** \brief Waiting buffer has anykey pressed
+ *
+ * FIXME: Needs docs
+ */
+__attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) {
+ for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
+ if (waiting_buffer[i].event.pressed) return true;
+ }
+ return false;
+}
+
+/** \brief Scan buffer for tapping
+ *
+ * FIXME: Needs docs
+ */
+void waiting_buffer_scan_tap(void) {
+ // tapping already is settled
+ if (tapping_key.tap.count > 0) return;
+ // invalid state: tapping_key released && tap.count == 0
+ if (!tapping_key.event.pressed) return;
+
+ for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
+ if (IS_TAPPING_KEY(waiting_buffer[i].event.key) && !waiting_buffer[i].event.pressed && WITHIN_TAPPING_TERM(waiting_buffer[i].event)) {
+ tapping_key.tap.count = 1;
+ waiting_buffer[i].tap.count = 1;
+ process_record(&tapping_key);
+
+ debug("waiting_buffer_scan_tap: found at [");
+ debug_dec(i);
+ debug("]\n");
+ debug_waiting_buffer();
+ return;
+ }
+ }
+}
+
+/** \brief Tapping key debug print
+ *
+ * FIXME: Needs docs
+ */
+static void debug_tapping_key(void) {
+ debug("TAPPING_KEY=");
+ debug_record(tapping_key);
+ debug("\n");
+}
+
+/** \brief Waiting buffer debug print
+ *
+ * FIXME: Needs docs
+ */
+static void debug_waiting_buffer(void) {
+ debug("{ ");
+ for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
+ debug("[");
+ debug_dec(i);
+ debug("]=");
+ debug_record(waiting_buffer[i]);
+ debug(" ");
+ }
+ debug("}\n");
+}
+
+#endif
diff --git a/quantum/action_tapping.h b/quantum/action_tapping.h
new file mode 100644
index 0000000000..7de8049c7f
--- /dev/null
+++ b/quantum/action_tapping.h
@@ -0,0 +1,42 @@
+/*
+Copyright 2013 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/>.
+*/
+
+#pragma once
+
+/* period of tapping(ms) */
+#ifndef TAPPING_TERM
+# define TAPPING_TERM 200
+#endif
+
+/* tap count needed for toggling a feature */
+#ifndef TAPPING_TOGGLE
+# define TAPPING_TOGGLE 5
+#endif
+
+#define WAITING_BUFFER_SIZE 8
+
+#ifndef NO_ACTION_TAPPING
+uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache);
+uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);
+void action_tapping_process(keyrecord_t record);
+
+uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record);
+bool get_permissive_hold(uint16_t keycode, keyrecord_t *record);
+bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record);
+bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record);
+bool get_retro_tapping(uint16_t keycode, keyrecord_t *record);
+#endif
diff --git a/quantum/action_util.c b/quantum/action_util.c
new file mode 100644
index 0000000000..2b3c00cba0
--- /dev/null
+++ b/quantum/action_util.c
@@ -0,0 +1,455 @@
+/*
+Copyright 2013 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 "host.h"
+#include "report.h"
+#include "debug.h"
+#include "action_util.h"
+#include "action_layer.h"
+#include "timer.h"
+#include "keycode_config.h"
+
+extern keymap_config_t keymap_config;
+
+static uint8_t real_mods = 0;
+static uint8_t weak_mods = 0;
+static uint8_t macro_mods = 0;
+#ifdef KEY_OVERRIDE_ENABLE
+static uint8_t weak_override_mods = 0;
+static uint8_t suppressed_mods = 0;
+#endif
+
+#ifdef USB_6KRO_ENABLE
+# define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS)
+# define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS)
+# define RO_INC(a) RO_ADD(a, 1)
+# define RO_DEC(a) RO_SUB(a, 1)
+static int8_t cb_head = 0;
+static int8_t cb_tail = 0;
+static int8_t cb_count = 0;
+#endif
+
+// TODO: pointer variable is not needed
+// report_keyboard_t keyboard_report = {};
+report_keyboard_t *keyboard_report = &(report_keyboard_t){};
+
+extern inline void add_key(uint8_t key);
+extern inline void del_key(uint8_t key);
+extern inline void clear_keys(void);
+
+#ifndef NO_ACTION_ONESHOT
+static uint8_t oneshot_mods = 0;
+static uint8_t oneshot_locked_mods = 0;
+uint8_t get_oneshot_locked_mods(void) { return oneshot_locked_mods; }
+void set_oneshot_locked_mods(uint8_t mods) {
+ if (mods != oneshot_locked_mods) {
+ oneshot_locked_mods = mods;
+ oneshot_locked_mods_changed_kb(oneshot_locked_mods);
+ }
+}
+void clear_oneshot_locked_mods(void) {
+ if (oneshot_locked_mods) {
+ oneshot_locked_mods = 0;
+ oneshot_locked_mods_changed_kb(oneshot_locked_mods);
+ }
+}
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+static uint16_t oneshot_time = 0;
+bool has_oneshot_mods_timed_out(void) { return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT; }
+# else
+bool has_oneshot_mods_timed_out(void) { return false; }
+# endif
+#endif
+
+/* oneshot layer */
+#ifndef NO_ACTION_ONESHOT
+/** \brief oneshot_layer_data bits
+ * LLLL LSSS
+ * where:
+ * L => are layer bits
+ * S => oneshot state bits
+ */
+static int8_t oneshot_layer_data = 0;
+
+inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; }
+inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; }
+
+# ifdef SWAP_HANDS_ENABLE
+enum {
+ SHO_OFF,
+ SHO_ACTIVE, // Swap hands button was pressed, and we didn't send any swapped keys yet
+ SHO_PRESSED, // Swap hands button is currently pressed
+ SHO_USED, // Swap hands button is still pressed, and we already sent swapped keys
+} swap_hands_oneshot = SHO_OFF;
+# endif
+
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+static uint16_t oneshot_layer_time = 0;
+inline bool has_oneshot_layer_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT && !(get_oneshot_layer_state() & ONESHOT_TOGGLED); }
+# ifdef SWAP_HANDS_ENABLE
+static uint16_t oneshot_swaphands_time = 0;
+inline bool has_oneshot_swaphands_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= ONESHOT_TIMEOUT && (swap_hands_oneshot == SHO_ACTIVE); }
+# endif
+# endif
+
+# ifdef SWAP_HANDS_ENABLE
+
+void set_oneshot_swaphands(void) {
+ swap_hands_oneshot = SHO_PRESSED;
+ swap_hands = true;
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+ oneshot_swaphands_time = timer_read();
+ if (oneshot_layer_time != 0) {
+ oneshot_layer_time = oneshot_swaphands_time;
+ }
+# endif
+}
+
+void release_oneshot_swaphands(void) {
+ if (swap_hands_oneshot == SHO_PRESSED) {
+ swap_hands_oneshot = SHO_ACTIVE;
+ }
+ if (swap_hands_oneshot == SHO_USED) {
+ clear_oneshot_swaphands();
+ }
+}
+
+void use_oneshot_swaphands(void) {
+ if (swap_hands_oneshot == SHO_PRESSED) {
+ swap_hands_oneshot = SHO_USED;
+ }
+ if (swap_hands_oneshot == SHO_ACTIVE) {
+ clear_oneshot_swaphands();
+ }
+}
+
+void clear_oneshot_swaphands(void) {
+ swap_hands_oneshot = SHO_OFF;
+ swap_hands = false;
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+ oneshot_swaphands_time = 0;
+# endif
+}
+
+# endif
+
+/** \brief Set oneshot layer
+ *
+ * FIXME: needs doc
+ */
+void set_oneshot_layer(uint8_t layer, uint8_t state) {
+ if (!keymap_config.oneshot_disable) {
+ oneshot_layer_data = layer << 3 | state;
+ layer_on(layer);
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+ oneshot_layer_time = timer_read();
+# endif
+ oneshot_layer_changed_kb(get_oneshot_layer());
+ } else {
+ layer_on(layer);
+ }
+}
+/** \brief Reset oneshot layer
+ *
+ * FIXME: needs doc
+ */
+void reset_oneshot_layer(void) {
+ oneshot_layer_data = 0;
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+ oneshot_layer_time = 0;
+# endif
+ oneshot_layer_changed_kb(get_oneshot_layer());
+}
+/** \brief Clear oneshot layer
+ *
+ * FIXME: needs doc
+ */
+void clear_oneshot_layer_state(oneshot_fullfillment_t state) {
+ uint8_t start_state = oneshot_layer_data;
+ oneshot_layer_data &= ~state;
+ if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) || keymap_config.oneshot_disable) {
+ layer_off(get_oneshot_layer());
+ reset_oneshot_layer();
+ }
+}
+/** \brief Is oneshot layer active
+ *
+ * FIXME: needs doc
+ */
+bool is_oneshot_layer_active(void) { return get_oneshot_layer_state(); }
+
+/** \brief set oneshot
+ *
+ * FIXME: needs doc
+ */
+void oneshot_set(bool active) {
+ if (keymap_config.oneshot_disable != active) {
+ keymap_config.oneshot_disable = active;
+ eeconfig_update_keymap(keymap_config.raw);
+ dprintf("Oneshot: active: %d\n", active);
+ }
+}
+
+/** \brief toggle oneshot
+ *
+ * FIXME: needs doc
+ */
+void oneshot_toggle(void) { oneshot_set(!keymap_config.oneshot_disable); }
+
+/** \brief enable oneshot
+ *
+ * FIXME: needs doc
+ */
+void oneshot_enable(void) { oneshot_set(true); }
+
+/** \brief disable oneshot
+ *
+ * FIXME: needs doc
+ */
+void oneshot_disable(void) { oneshot_set(false); }
+
+bool is_oneshot_enabled(void) { return keymap_config.oneshot_disable; }
+
+#endif
+
+/** \brief Send keyboard report
+ *
+ * FIXME: needs doc
+ */
+void send_keyboard_report(void) {
+ keyboard_report->mods = real_mods;
+ keyboard_report->mods |= weak_mods;
+ keyboard_report->mods |= macro_mods;
+
+#ifndef NO_ACTION_ONESHOT
+ if (oneshot_mods) {
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+ if (has_oneshot_mods_timed_out()) {
+ dprintf("Oneshot: timeout\n");
+ clear_oneshot_mods();
+ }
+# endif
+ keyboard_report->mods |= oneshot_mods;
+ if (has_anykey(keyboard_report)) {
+ clear_oneshot_mods();
+ }
+ }
+
+#endif
+
+#ifdef KEY_OVERRIDE_ENABLE
+ // These need to be last to be able to properly control key overrides
+ keyboard_report->mods &= ~suppressed_mods;
+ keyboard_report->mods |= weak_override_mods;
+#endif
+
+ host_keyboard_send(keyboard_report);
+}
+
+/** \brief Get mods
+ *
+ * FIXME: needs doc
+ */
+uint8_t get_mods(void) { return real_mods; }
+/** \brief add mods
+ *
+ * FIXME: needs doc
+ */
+void add_mods(uint8_t mods) { real_mods |= mods; }
+/** \brief del mods
+ *
+ * FIXME: needs doc
+ */
+void del_mods(uint8_t mods) { real_mods &= ~mods; }
+/** \brief set mods
+ *
+ * FIXME: needs doc
+ */
+void set_mods(uint8_t mods) { real_mods = mods; }
+/** \brief clear mods
+ *
+ * FIXME: needs doc
+ */
+void clear_mods(void) { real_mods = 0; }
+
+/** \brief get weak mods
+ *
+ * FIXME: needs doc
+ */
+uint8_t get_weak_mods(void) { return weak_mods; }
+/** \brief add weak mods
+ *
+ * FIXME: needs doc
+ */
+void add_weak_mods(uint8_t mods) { weak_mods |= mods; }
+/** \brief del weak mods
+ *
+ * FIXME: needs doc
+ */
+void del_weak_mods(uint8_t mods) { weak_mods &= ~mods; }
+/** \brief set weak mods
+ *
+ * FIXME: needs doc
+ */
+void set_weak_mods(uint8_t mods) { weak_mods = mods; }
+/** \brief clear weak mods
+ *
+ * FIXME: needs doc
+ */
+void clear_weak_mods(void) { weak_mods = 0; }
+
+#ifdef KEY_OVERRIDE_ENABLE
+/** \brief set weak mods used by key overrides. DO not call this manually
+ */
+void set_weak_override_mods(uint8_t mods) { weak_override_mods = mods; }
+/** \brief clear weak mods used by key overrides. DO not call this manually
+ */
+void clear_weak_override_mods(void) { weak_override_mods = 0; }
+
+/** \brief set suppressed mods used by key overrides. DO not call this manually
+ */
+void set_suppressed_override_mods(uint8_t mods) { suppressed_mods = mods; }
+/** \brief clear suppressed mods used by key overrides. DO not call this manually
+ */
+void clear_suppressed_override_mods(void) { suppressed_mods = 0; }
+#endif
+
+/* macro modifier */
+/** \brief get macro mods
+ *
+ * FIXME: needs doc
+ */
+uint8_t get_macro_mods(void) { return macro_mods; }
+/** \brief add macro mods
+ *
+ * FIXME: needs doc
+ */
+void add_macro_mods(uint8_t mods) { macro_mods |= mods; }
+/** \brief del macro mods
+ *
+ * FIXME: needs doc
+ */
+void del_macro_mods(uint8_t mods) { macro_mods &= ~mods; }
+/** \brief set macro mods
+ *
+ * FIXME: needs doc
+ */
+void set_macro_mods(uint8_t mods) { macro_mods = mods; }
+/** \brief clear macro mods
+ *
+ * FIXME: needs doc
+ */
+void clear_macro_mods(void) { macro_mods = 0; }
+
+#ifndef NO_ACTION_ONESHOT
+/** \brief get oneshot mods
+ *
+ * FIXME: needs doc
+ */
+uint8_t get_oneshot_mods(void) { return oneshot_mods; }
+
+void add_oneshot_mods(uint8_t mods) {
+ if ((oneshot_mods & mods) != mods) {
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+ oneshot_time = timer_read();
+# endif
+ oneshot_mods |= mods;
+ oneshot_mods_changed_kb(mods);
+ }
+}
+
+void del_oneshot_mods(uint8_t mods) {
+ if (oneshot_mods & mods) {
+ oneshot_mods &= ~mods;
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+ oneshot_time = oneshot_mods ? timer_read() : 0;
+# endif
+ oneshot_mods_changed_kb(oneshot_mods);
+ }
+}
+
+/** \brief set oneshot mods
+ *
+ * FIXME: needs doc
+ */
+void set_oneshot_mods(uint8_t mods) {
+ if (!keymap_config.oneshot_disable) {
+ if (oneshot_mods != mods) {
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+ oneshot_time = timer_read();
+# endif
+ oneshot_mods = mods;
+ oneshot_mods_changed_kb(mods);
+ }
+ }
+}
+
+/** \brief clear oneshot mods
+ *
+ * FIXME: needs doc
+ */
+void clear_oneshot_mods(void) {
+ if (oneshot_mods) {
+ oneshot_mods = 0;
+# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+ oneshot_time = 0;
+# endif
+ oneshot_mods_changed_kb(oneshot_mods);
+ }
+}
+#endif
+
+/** \brief Called when the one shot modifiers have been changed.
+ *
+ * \param mods Contains the active modifiers active after the change.
+ */
+__attribute__((weak)) void oneshot_locked_mods_changed_user(uint8_t mods) {}
+
+/** \brief Called when the locked one shot modifiers have been changed.
+ *
+ * \param mods Contains the active modifiers active after the change.
+ */
+__attribute__((weak)) void oneshot_locked_mods_changed_kb(uint8_t mods) { oneshot_locked_mods_changed_user(mods); }
+
+/** \brief Called when the one shot modifiers have been changed.
+ *
+ * \param mods Contains the active modifiers active after the change.
+ */
+__attribute__((weak)) void oneshot_mods_changed_user(uint8_t mods) {}
+
+/** \brief Called when the one shot modifiers have been changed.
+ *
+ * \param mods Contains the active modifiers active after the change.
+ */
+__attribute__((weak)) void oneshot_mods_changed_kb(uint8_t mods) { oneshot_mods_changed_user(mods); }
+
+/** \brief Called when the one shot layers have been changed.
+ *
+ * \param layer Contains the layer that is toggled on, or zero when toggled off.
+ */
+__attribute__((weak)) void oneshot_layer_changed_user(uint8_t layer) {}
+
+/** \brief Called when the one shot layers have been changed.
+ *
+ * \param layer Contains the layer that is toggled on, or zero when toggled off.
+ */
+__attribute__((weak)) void oneshot_layer_changed_kb(uint8_t layer) { oneshot_layer_changed_user(layer); }
+
+/** \brief inspect keyboard state
+ *
+ * FIXME: needs doc
+ */
+uint8_t has_anymod(void) { return bitpop(real_mods); }
diff --git a/quantum/action_util.h b/quantum/action_util.h
new file mode 100644
index 0000000000..f2b3897ae5
--- /dev/null
+++ b/quantum/action_util.h
@@ -0,0 +1,105 @@
+/*
+Copyright 2013 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/>.
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include "report.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern report_keyboard_t *keyboard_report;
+
+void send_keyboard_report(void);
+
+/* key */
+inline void add_key(uint8_t key) { add_key_to_report(keyboard_report, key); }
+
+inline void del_key(uint8_t key) { del_key_from_report(keyboard_report, key); }
+
+inline void clear_keys(void) { clear_keys_from_report(keyboard_report); }
+
+/* modifier */
+uint8_t get_mods(void);
+void add_mods(uint8_t mods);
+void del_mods(uint8_t mods);
+void set_mods(uint8_t mods);
+void clear_mods(void);
+
+/* weak modifier */
+uint8_t get_weak_mods(void);
+void add_weak_mods(uint8_t mods);
+void del_weak_mods(uint8_t mods);
+void set_weak_mods(uint8_t mods);
+void clear_weak_mods(void);
+
+/* macro modifier */
+uint8_t get_macro_mods(void);
+void add_macro_mods(uint8_t mods);
+void del_macro_mods(uint8_t mods);
+void set_macro_mods(uint8_t mods);
+void clear_macro_mods(void);
+
+/* oneshot modifier */
+uint8_t get_oneshot_mods(void);
+void add_oneshot_mods(uint8_t mods);
+void del_oneshot_mods(uint8_t mods);
+void set_oneshot_mods(uint8_t mods);
+void clear_oneshot_mods(void);
+bool has_oneshot_mods_timed_out(void);
+
+uint8_t get_oneshot_locked_mods(void);
+void set_oneshot_locked_mods(uint8_t mods);
+void clear_oneshot_locked_mods(void);
+
+typedef enum { ONESHOT_PRESSED = 0b01, ONESHOT_OTHER_KEY_PRESSED = 0b10, ONESHOT_START = 0b11, ONESHOT_TOGGLED = 0b100 } oneshot_fullfillment_t;
+void set_oneshot_layer(uint8_t layer, uint8_t state);
+uint8_t get_oneshot_layer(void);
+void clear_oneshot_layer_state(oneshot_fullfillment_t state);
+void reset_oneshot_layer(void);
+bool is_oneshot_layer_active(void);
+uint8_t get_oneshot_layer_state(void);
+bool has_oneshot_layer_timed_out(void);
+bool has_oneshot_swaphands_timed_out(void);
+
+void oneshot_locked_mods_changed_user(uint8_t mods);
+void oneshot_locked_mods_changed_kb(uint8_t mods);
+void oneshot_mods_changed_user(uint8_t mods);
+void oneshot_mods_changed_kb(uint8_t mods);
+void oneshot_layer_changed_user(uint8_t layer);
+void oneshot_layer_changed_kb(uint8_t layer);
+
+void oneshot_toggle(void);
+void oneshot_enable(void);
+void oneshot_disable(void);
+bool is_oneshot_enabled(void);
+
+/* inspect */
+uint8_t has_anymod(void);
+
+#ifdef SWAP_HANDS_ENABLE
+void set_oneshot_swaphands(void);
+void release_oneshot_swaphands(void);
+void use_oneshot_swaphands(void);
+void clear_oneshot_swaphands(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c
index 46277dd70b..49bb309e80 100644
--- a/quantum/audio/audio.c
+++ b/quantum/audio/audio.c
@@ -139,6 +139,9 @@ void audio_init() {
audio_initialized = true;
}
stop_all_notes();
+#ifndef AUDIO_INIT_DELAY
+ audio_startup();
+#endif
}
void audio_startup(void) {
diff --git a/quantum/bootmagic/bootmagic.h b/quantum/bootmagic/bootmagic.h
index 959750178d..db826025ce 100644
--- a/quantum/bootmagic/bootmagic.h
+++ b/quantum/bootmagic/bootmagic.h
@@ -15,9 +15,7 @@
*/
#pragma once
-#if defined(BOOTMAGIC_ENABLE)
-# include "bootmagic_full.h"
-#elif defined(BOOTMAGIC_LITE)
+#if defined(BOOTMAGIC_LITE)
# include "bootmagic_lite.h"
#endif
diff --git a/quantum/bootmagic/bootmagic_full.c b/quantum/bootmagic/bootmagic_full.c
deleted file mode 100644
index a7a0dcfcb2..0000000000
--- a/quantum/bootmagic/bootmagic_full.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/* 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 3 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 "wait.h"
-#include "matrix.h"
-#include "bootloader.h"
-#include "debug.h"
-#include "keymap.h"
-#include "host.h"
-#include "action_layer.h"
-#include "eeconfig.h"
-#include "bootmagic.h"
-
-/** \brief Scan Keycode
- *
- * FIXME: needs doc
- */
-static bool scan_keycode(uint8_t keycode) {
- for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
- matrix_row_t matrix_row = matrix_get_row(r);
- for (uint8_t c = 0; c < MATRIX_COLS; c++) {
- if (matrix_row & ((matrix_row_t)1 << c)) {
- if (keycode == keymap_key_to_keycode(0, (keypos_t){.row = r, .col = c})) {
- return true;
- }
- }
- }
- }
- return false;
-}
-
-/** \brief Bootmagic Scan Keycode
- *
- * FIXME: needs doc
- */
-static bool bootmagic_scan_keycode(uint8_t keycode) {
- if (!scan_keycode(BOOTMAGIC_KEY_SALT)) return false;
-
- return scan_keycode(keycode);
-}
-
-void bootmagic(void) {
- /* do scans in case of bounce */
- print("bootmagic scan: ... ");
- uint8_t scan = 100;
- while (scan--) {
- matrix_scan();
- wait_ms(10);
- }
- print("done.\n");
-
- /* bootmagic skip */
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SKIP)) {
- return;
- }
-
- /* eeconfig clear */
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_EEPROM_CLEAR)) {
- eeconfig_init();
- }
-
- /* bootloader */
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_BOOTLOADER)) {
- bootloader_jump();
- }
-
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEBUG_ENABLE)) {
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEBUG_MATRIX)) {
- debug_config.matrix = !debug_config.matrix;
- } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEBUG_KEYBOARD)) {
- debug_config.keyboard = !debug_config.keyboard;
- } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEBUG_MOUSE)) {
- debug_config.mouse = !debug_config.mouse;
- } else {
- debug_config.enable = !debug_config.enable;
- }
- }
- eeconfig_update_debug(debug_config.raw);
-
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SWAP_CONTROL_CAPSLOCK)) {
- keymap_config.swap_control_capslock = !keymap_config.swap_control_capslock;
- }
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_CAPSLOCK_TO_CONTROL)) {
- keymap_config.capslock_to_control = !keymap_config.capslock_to_control;
- }
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SWAP_LALT_LGUI)) {
- keymap_config.swap_lalt_lgui = !keymap_config.swap_lalt_lgui;
- }
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SWAP_RALT_RGUI)) {
- keymap_config.swap_ralt_rgui = !keymap_config.swap_ralt_rgui;
- }
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_NO_GUI)) {
- keymap_config.no_gui = !keymap_config.no_gui;
- }
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SWAP_GRAVE_ESC)) {
- keymap_config.swap_grave_esc = !keymap_config.swap_grave_esc;
- }
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SWAP_BACKSLASH_BACKSPACE)) {
- keymap_config.swap_backslash_backspace = !keymap_config.swap_backslash_backspace;
- }
- if (bootmagic_scan_keycode(BOOTMAGIC_HOST_NKRO)) {
- keymap_config.nkro = !keymap_config.nkro;
- }
- eeconfig_update_keymap(keymap_config.raw);
-
- /* default layer */
- uint8_t default_layer = 0;
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_0)) {
- default_layer |= (1 << 0);
- } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_1)) {
- default_layer |= (1 << 1);
- } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_2)) {
- default_layer |= (1 << 2);
- } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_3)) {
- default_layer |= (1 << 3);
- } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_4)) {
- default_layer |= (1 << 4);
- } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_5)) {
- default_layer |= (1 << 5);
- } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_6)) {
- default_layer |= (1 << 6);
- } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_7)) {
- default_layer |= (1 << 7);
- }
- eeconfig_update_default_layer(default_layer);
-
- /* EE_HANDS handedness */
- if (bootmagic_scan_keycode(BOOTMAGIC_KEY_EE_HANDS_LEFT)) {
- eeconfig_update_handedness(true);
- } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_EE_HANDS_RIGHT)) {
- eeconfig_update_handedness(false);
- }
-}
diff --git a/quantum/bootmagic/bootmagic_full.h b/quantum/bootmagic/bootmagic_full.h
deleted file mode 100644
index 28f914c1b6..0000000000
--- a/quantum/bootmagic/bootmagic_full.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/* 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 3 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
-
-/* FIXME: Add special doxygen comments for defines here. */
-
-/* bootmagic salt key */
-#ifndef BOOTMAGIC_KEY_SALT
-# define BOOTMAGIC_KEY_SALT KC_SPACE
-#endif
-
-/* skip bootmagic and eeconfig */
-#ifndef BOOTMAGIC_KEY_SKIP
-# define BOOTMAGIC_KEY_SKIP KC_ESC
-#endif
-
-/* eeprom clear */
-#ifndef BOOTMAGIC_KEY_EEPROM_CLEAR
-# define BOOTMAGIC_KEY_EEPROM_CLEAR KC_BSPACE
-#endif
-
-/* kick up bootloader */
-#ifndef BOOTMAGIC_KEY_BOOTLOADER
-# define BOOTMAGIC_KEY_BOOTLOADER KC_B
-#endif
-
-/* debug enable */
-#ifndef BOOTMAGIC_KEY_DEBUG_ENABLE
-# define BOOTMAGIC_KEY_DEBUG_ENABLE KC_D
-#endif
-#ifndef BOOTMAGIC_KEY_DEBUG_MATRIX
-# define BOOTMAGIC_KEY_DEBUG_MATRIX KC_X
-#endif
-#ifndef BOOTMAGIC_KEY_DEBUG_KEYBOARD
-# define BOOTMAGIC_KEY_DEBUG_KEYBOARD KC_K
-#endif
-#ifndef BOOTMAGIC_KEY_DEBUG_MOUSE
-# define BOOTMAGIC_KEY_DEBUG_MOUSE KC_M
-#endif
-#ifndef BOOTMAGIC_KEY_EE_HANDS_LEFT
-# define BOOTMAGIC_KEY_EE_HANDS_LEFT KC_L
-#endif
-#ifndef BOOTMAGIC_KEY_EE_HANDS_RIGHT
-# define BOOTMAGIC_KEY_EE_HANDS_RIGHT KC_R
-#endif
-
-/*
- * keymap config
- */
-#ifndef BOOTMAGIC_KEY_SWAP_CONTROL_CAPSLOCK
-# define BOOTMAGIC_KEY_SWAP_CONTROL_CAPSLOCK KC_LCTRL
-#endif
-#ifndef BOOTMAGIC_KEY_CAPSLOCK_TO_CONTROL
-# define BOOTMAGIC_KEY_CAPSLOCK_TO_CONTROL KC_CAPSLOCK
-#endif
-#ifndef BOOTMAGIC_KEY_SWAP_LALT_LGUI
-# define BOOTMAGIC_KEY_SWAP_LALT_LGUI KC_LALT
-#endif
-#ifndef BOOTMAGIC_KEY_SWAP_RALT_RGUI
-# define BOOTMAGIC_KEY_SWAP_RALT_RGUI KC_RALT
-#endif
-#ifndef BOOTMAGIC_KEY_NO_GUI
-# define BOOTMAGIC_KEY_NO_GUI KC_LGUI
-#endif
-#ifndef BOOTMAGIC_KEY_SWAP_GRAVE_ESC
-# define BOOTMAGIC_KEY_SWAP_GRAVE_ESC KC_GRAVE
-#endif
-#ifndef BOOTMAGIC_KEY_SWAP_BACKSLASH_BACKSPACE
-# define BOOTMAGIC_KEY_SWAP_BACKSLASH_BACKSPACE KC_BSLASH
-#endif
-#ifndef BOOTMAGIC_HOST_NKRO
-# define BOOTMAGIC_HOST_NKRO KC_N
-#endif
-
-/*
- * change default layer
- */
-#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_0
-# define BOOTMAGIC_KEY_DEFAULT_LAYER_0 KC_0
-#endif
-#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_1
-# define BOOTMAGIC_KEY_DEFAULT_LAYER_1 KC_1
-#endif
-#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_2
-# define BOOTMAGIC_KEY_DEFAULT_LAYER_2 KC_2
-#endif
-#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_3
-# define BOOTMAGIC_KEY_DEFAULT_LAYER_3 KC_3
-#endif
-#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_4
-# define BOOTMAGIC_KEY_DEFAULT_LAYER_4 KC_4
-#endif
-#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_5
-# define BOOTMAGIC_KEY_DEFAULT_LAYER_5 KC_5
-#endif
-#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_6
-# define BOOTMAGIC_KEY_DEFAULT_LAYER_6 KC_6
-#endif
-#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_7
-# define BOOTMAGIC_KEY_DEFAULT_LAYER_7 KC_7
-#endif \ No newline at end of file
diff --git a/quantum/bootmagic/bootmagic_lite.c b/quantum/bootmagic/bootmagic_lite.c
index 9cbdcb0bbd..54bbf5a2ee 100644
--- a/quantum/bootmagic/bootmagic_lite.c
+++ b/quantum/bootmagic/bootmagic_lite.c
@@ -19,13 +19,7 @@
*
* ...just incase someone wants to only change the eeprom behaviour
*/
-__attribute__((weak)) void bootmagic_lite_reset_eeprom(void) {
-#if defined(VIA_ENABLE)
- via_eeprom_reset();
-#else
- eeconfig_disable();
-#endif
-}
+__attribute__((weak)) void bootmagic_lite_reset_eeprom(void) { eeconfig_disable(); }
/** \brief The lite version of TMK's bootmagic based on Wilba.
*
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..2ff640a7bc 100644
--- a/quantum/command.c
+++ b/quantum/command.c
@@ -55,7 +55,6 @@ static bool command_console(uint8_t code);
static void command_console_help(void);
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
static bool mousekey_console(uint8_t code);
-static void mousekey_console_help(void);
#endif
static void switch_default_layer(uint8_t layer);
@@ -103,190 +102,220 @@ bool command_console_extra(uint8_t code) {
/***********************************************************
* Command common
***********************************************************/
+
static void command_common_help(void) {
- print("\n\t- Magic -\n" STR(MAGIC_KEY_DEBUG) ": Debug Message Toggle\n" STR(MAGIC_KEY_DEBUG_MATRIX) ": Matrix Debug Mode Toggle - Show keypresses in matrix grid\n" STR(MAGIC_KEY_DEBUG_KBD) ": Keyboard Debug Toggle - Show keypress report\n" STR(MAGIC_KEY_DEBUG_MOUSE) ": Debug Mouse Toggle\n" STR(MAGIC_KEY_VERSION) ": Version\n" STR(MAGIC_KEY_STATUS) ": Status\n" STR(MAGIC_KEY_CONSOLE) ": Activate Console Mode\n"
+ print(/* clang-format off */
+ "\n\t- Magic -\n"
+
+ STR(MAGIC_KEY_DEBUG) ": Debug Message Toggle\n"
+ STR(MAGIC_KEY_DEBUG_MATRIX) ": Matrix Debug Mode Toggle"
+ " - Show keypresses in matrix grid\n"
+ STR(MAGIC_KEY_DEBUG_KBD) ": Keyboard Debug Toggle"
+ " - Show keypress report\n"
+ STR(MAGIC_KEY_DEBUG_MOUSE) ": Debug Mouse Toggle\n"
+ STR(MAGIC_KEY_VERSION) ": Version\n"
+ STR(MAGIC_KEY_STATUS) ": Status\n"
+ STR(MAGIC_KEY_CONSOLE) ": Activate Console Mode\n"
#if MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM
- STR(MAGIC_KEY_LAYER0) ": Switch to Layer 0\n" STR(MAGIC_KEY_LAYER1) ": Switch to Layer 1\n" STR(MAGIC_KEY_LAYER2) ": Switch to Layer 2\n" STR(MAGIC_KEY_LAYER3) ": Switch to Layer 3\n" STR(MAGIC_KEY_LAYER4) ": Switch to Layer 4\n" STR(MAGIC_KEY_LAYER5) ": Switch to Layer 5\n" STR(MAGIC_KEY_LAYER6) ": Switch to Layer 6\n" STR(MAGIC_KEY_LAYER7) ": Switch to Layer 7\n" STR(MAGIC_KEY_LAYER8) ": Switch to Layer 8\n" STR(MAGIC_KEY_LAYER9) ": Switch to Layer 9\n"
+ STR(MAGIC_KEY_LAYER0) ": Switch to Layer 0\n"
+ STR(MAGIC_KEY_LAYER1) ": Switch to Layer 1\n"
+ STR(MAGIC_KEY_LAYER2) ": Switch to Layer 2\n"
+ STR(MAGIC_KEY_LAYER3) ": Switch to Layer 3\n"
+ STR(MAGIC_KEY_LAYER4) ": Switch to Layer 4\n"
+ STR(MAGIC_KEY_LAYER5) ": Switch to Layer 5\n"
+ STR(MAGIC_KEY_LAYER6) ": Switch to Layer 6\n"
+ STR(MAGIC_KEY_LAYER7) ": Switch to Layer 7\n"
+ STR(MAGIC_KEY_LAYER8) ": Switch to Layer 8\n"
+ STR(MAGIC_KEY_LAYER9) ": Switch to Layer 9\n"
#endif
#if MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS
- "F1-F10: Switch to Layer 0-9 (F10 = L0)\n"
+ "F1-F10: Switch to Layer 0-9 (F10 = L0)\n"
#endif
#if MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS
- "0-9: Switch to Layer 0-9\n"
+ "0-9: Switch to Layer 0-9\n"
#endif
- STR(MAGIC_KEY_LAYER0_ALT) ": Switch to Layer 0 (alternate)\n"
+ STR(MAGIC_KEY_LAYER0_ALT) ": Switch to Layer 0 (alternate)\n"
- STR(MAGIC_KEY_BOOTLOADER) ": Jump to Bootloader\n" STR(MAGIC_KEY_BOOTLOADER_ALT) ": Jump to Bootloader (alternate)\n"
+ STR(MAGIC_KEY_BOOTLOADER) ": Jump to Bootloader\n"
+ STR(MAGIC_KEY_BOOTLOADER_ALT) ": Jump to Bootloader (alternate)\n"
#ifdef KEYBOARD_LOCK_ENABLE
- STR(MAGIC_KEY_LOCK) ": Lock Keyboard\n"
+ STR(MAGIC_KEY_LOCK) ": Lock Keyboard\n"
#endif
- STR(MAGIC_KEY_EEPROM) ": Print EEPROM Settings\n" STR(MAGIC_KEY_EEPROM_CLEAR) ": Clear EEPROM\n"
+ STR(MAGIC_KEY_EEPROM) ": Print EEPROM Settings\n"
+ STR(MAGIC_KEY_EEPROM_CLEAR) ": Clear EEPROM\n"
#ifdef NKRO_ENABLE
- STR(MAGIC_KEY_NKRO) ": NKRO Toggle\n"
+ STR(MAGIC_KEY_NKRO) ": NKRO Toggle\n"
#endif
#ifdef SLEEP_LED_ENABLE
- STR(MAGIC_KEY_SLEEP_LED) ": Sleep LED Test\n"
+ STR(MAGIC_KEY_SLEEP_LED) ": Sleep LED Test\n"
#endif
- );
+ ); /* clang-format on */
}
static void print_version(void) {
- // print version & information
- print("\n\t- Version -\n");
- print("VID: " STR(VENDOR_ID) "(" STR(MANUFACTURER) ") "
- "PID: " STR(PRODUCT_ID) "(" STR(PRODUCT) ") "
- "VER: " STR(DEVICE_VER) "\n");
- print("BUILD: (" __DATE__ ")\n");
+ print(/* clang-format off */
+ "\n\t- Version -\n"
+ "VID: " STR(VENDOR_ID) "(" STR(MANUFACTURER) ") "
+ "PID: " STR(PRODUCT_ID) "(" STR(PRODUCT) ") "
+ "VER: " STR(DEVICE_VER) "\n"
+ "BUILD: (" __DATE__ ")\n"
#ifndef SKIP_VERSION
# ifdef PROTOCOL_CHIBIOS
- print("CHIBIOS: " STR(CHIBIOS_VERSION) ", CONTRIB: " STR(CHIBIOS_CONTRIB_VERSION) "\n");
+ "CHIBIOS: " STR(CHIBIOS_VERSION)
+ ", CONTRIB: " STR(CHIBIOS_CONTRIB_VERSION) "\n"
# endif
#endif
/* build options */
- print("OPTIONS:"
+ "OPTIONS:"
#ifdef PROTOCOL_LUFA
- " LUFA"
+ " LUFA"
#endif
#ifdef PROTOCOL_VUSB
- " VUSB"
+ " VUSB"
#endif
#ifdef BOOTMAGIC_ENABLE
- " BOOTMAGIC"
+ " BOOTMAGIC"
#endif
#ifdef MOUSEKEY_ENABLE
- " MOUSEKEY"
+ " MOUSEKEY"
#endif
#ifdef EXTRAKEY_ENABLE
- " EXTRAKEY"
+ " EXTRAKEY"
#endif
#ifdef CONSOLE_ENABLE
- " CONSOLE"
+ " CONSOLE"
#endif
#ifdef COMMAND_ENABLE
- " COMMAND"
+ " COMMAND"
#endif
#ifdef NKRO_ENABLE
- " NKRO"
+ " NKRO"
#endif
#ifdef LTO_ENABLE
- " LTO"
+ " LTO"
#endif
- " " STR(BOOTLOADER_SIZE) "\n");
+ " " STR(BOOTLOADER_SIZE) "\n"
- print("GCC: " STR(__GNUC__) "." STR(__GNUC_MINOR__) "." STR(__GNUC_PATCHLEVEL__)
+ "GCC: " STR(__GNUC__)
+ "." STR(__GNUC_MINOR__)
+ "." STR(__GNUC_PATCHLEVEL__)
#if defined(__AVR__)
- " AVR-LIBC: " __AVR_LIBC_VERSION_STRING__ " AVR_ARCH: avr" STR(__AVR_ARCH__)
+ " AVR-LIBC: " __AVR_LIBC_VERSION_STRING__
+ " AVR_ARCH: avr" STR(__AVR_ARCH__)
#endif
- "\n");
-
- return;
+ "\n"
+ ); /* clang-format on */
}
static void print_status(void) {
- print("\n\t- Status -\n");
+ xprintf(/* clang-format off */
+ "\n\t- Status -\n"
+
+ "host_keyboard_leds(): %02X\n"
+#ifndef PROTOCOL_VUSB
+ "keyboard_protocol: %02X\n"
+ "keyboard_idle: %02X\n"
+#endif
+#ifdef NKRO_ENABLE
+ "keymap_config.nkro: %02X\n"
+#endif
+ "timer_read32(): %08lX\n"
- print_val_hex8(host_keyboard_leds());
+ , host_keyboard_leds()
#ifndef PROTOCOL_VUSB
- // these aren't set on the V-USB protocol, so we just ignore them for now
- print_val_hex8(keyboard_protocol);
- print_val_hex8(keyboard_idle);
+ /* these aren't set on the V-USB protocol, so we just ignore them for now */
+ , keyboard_protocol
+ , keyboard_idle
#endif
#ifdef NKRO_ENABLE
- print_val_hex8(keymap_config.nkro);
+ , keymap_config.nkro
#endif
- print_val_hex32(timer_read32());
- return;
+ , timer_read32()
+
+ ); /* clang-format on */
}
-static void print_eeconfig(void) {
-// Print these variables if NO_PRINT or USER_PRINT are not defined.
#if !defined(NO_PRINT) && !defined(USER_PRINT)
-
- print("default_layer: ");
- print_dec(eeconfig_read_default_layer());
- print("\n");
+static void print_eeconfig(void) {
+ xprintf("eeconfig:\ndefault_layer: %u\n", eeconfig_read_default_layer());
debug_config_t dc;
dc.raw = eeconfig_read_debug();
- print("debug_config.raw: ");
- print_hex8(dc.raw);
- print("\n");
- print(".enable: ");
- print_dec(dc.enable);
- print("\n");
- print(".matrix: ");
- print_dec(dc.matrix);
- print("\n");
- print(".keyboard: ");
- print_dec(dc.keyboard);
- print("\n");
- print(".mouse: ");
- print_dec(dc.mouse);
- print("\n");
+ xprintf(/* clang-format off */
+
+ "debug_config.raw: %02X\n"
+ ".enable: %u\n"
+ ".matrix: %u\n"
+ ".keyboard: %u\n"
+ ".mouse: %u\n"
+
+ , dc.raw
+ , dc.enable
+ , dc.matrix
+ , dc.keyboard
+ , dc.mouse
+ ); /* clang-format on */
keymap_config_t kc;
kc.raw = eeconfig_read_keymap();
- print("keymap_config.raw: ");
- print_hex8(kc.raw);
- print("\n");
- print(".swap_control_capslock: ");
- print_dec(kc.swap_control_capslock);
- print("\n");
- print(".capslock_to_control: ");
- print_dec(kc.capslock_to_control);
- print("\n");
- print(".swap_lctl_lgui: ");
- print_dec(kc.swap_lctl_lgui);
- print("\n");
- print(".swap_rctl_rgui: ");
- print_dec(kc.swap_rctl_rgui);
- print("\n");
- print(".swap_lalt_lgui: ");
- print_dec(kc.swap_lalt_lgui);
- print("\n");
- print(".swap_ralt_rgui: ");
- print_dec(kc.swap_ralt_rgui);
- print("\n");
- print(".no_gui: ");
- print_dec(kc.no_gui);
- print("\n");
- print(".swap_grave_esc: ");
- print_dec(kc.swap_grave_esc);
- print("\n");
- print(".swap_backslash_backspace: ");
- print_dec(kc.swap_backslash_backspace);
- print("\n");
- print(".nkro: ");
- print_dec(kc.nkro);
- print("\n");
+ xprintf(/* clang-format off */
+
+ "keymap_config.raw: %02X\n"
+ ".swap_control_capslock: %u\n"
+ ".capslock_to_control: %u\n"
+ ".swap_lctl_lgui: %u\n"
+ ".swap_rctl_rgui: %u\n"
+ ".swap_lalt_lgui: %u\n"
+ ".swap_ralt_rgui: %u\n"
+ ".no_gui: %u\n"
+ ".swap_grave_esc: %u\n"
+ ".swap_backslash_backspace: %u\n"
+ ".nkro: %u\n"
+
+ , kc.raw
+ , kc.swap_control_capslock
+ , kc.capslock_to_control
+ , kc.swap_lctl_lgui
+ , kc.swap_rctl_rgui
+ , kc.swap_lalt_lgui
+ , kc.swap_ralt_rgui
+ , kc.no_gui
+ , kc.swap_grave_esc
+ , kc.swap_backslash_backspace
+ , kc.nkro
+ ); /* clang-format on */
# ifdef BACKLIGHT_ENABLE
+
backlight_config_t bc;
bc.raw = eeconfig_read_backlight();
- print("backlight_config.raw: ");
- print_hex8(bc.raw);
- print("\n");
- print(".enable: ");
- print_dec(bc.enable);
- print("\n");
- print(".level: ");
- print_dec(bc.level);
- print("\n");
-# endif /* BACKLIGHT_ENABLE */
+ xprintf(/* clang-format off */
+ "backlight_config"
+
+ ".raw: %02X\n"
+ ".enable: %u\n"
+ ".level: %u\n"
+
+ , bc.raw
+ , bc.enable
+ , bc.level
-#endif /* !NO_PRINT */
+ ); /* clang-format on */
+
+# endif /* BACKLIGHT_ENABLE */
}
+#endif /* !NO_PRINT && !USER_PRINT */
static bool command_common(uint8_t code) {
#ifdef KEYBOARD_LOCK_ENABLE
@@ -306,8 +335,9 @@ static bool command_common(uint8_t code) {
// print stored eeprom config
case MAGIC_KC(MAGIC_KEY_EEPROM):
- print("eeconfig:\n");
+#if !defined(NO_PRINT) && !defined(USER_PRINT)
print_eeconfig();
+#endif /* !NO_PRINT && !USER_PRINT */
break;
// clear eeprom
@@ -519,268 +549,199 @@ static bool command_console(uint8_t code) {
case KC_H:
case KC_SLASH: /* ? */
command_console_help();
- break;
+ print("C> ");
+ return true;
case KC_Q:
case KC_ESC:
command_state = ONESHOT;
return false;
-#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
+#if defined(MOUSEKEY_ENABLE)
case KC_M:
- mousekey_console_help();
- print("M> ");
command_state = MOUSEKEY;
+ mousekey_console(KC_SLASH /* ? */);
return true;
#endif
default:
print("?");
return false;
}
- print("C> ");
- return true;
}
-#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
/***********************************************************
* Mousekey console
***********************************************************/
-static uint8_t mousekey_param = 0;
-
-static void mousekey_param_print(void) {
-// Print these variables if NO_PRINT or USER_PRINT are not defined.
-# if !defined(NO_PRINT) && !defined(USER_PRINT)
- print("\n\t- Values -\n");
- print("1: delay(*10ms): ");
- print_dec(mk_delay);
- print("\n");
- print("2: interval(ms): ");
- print_dec(mk_interval);
- print("\n");
- print("3: max_speed: ");
- print_dec(mk_max_speed);
- print("\n");
- print("4: time_to_max: ");
- print_dec(mk_time_to_max);
- print("\n");
- print("5: wheel_max_speed: ");
- print_dec(mk_wheel_max_speed);
- print("\n");
- print("6: wheel_time_to_max: ");
- print_dec(mk_wheel_time_to_max);
- print("\n");
-# endif /* !NO_PRINT */
-}
-//#define PRINT_SET_VAL(v) print(#v " = "); print_dec(v); print("\n");
-# define PRINT_SET_VAL(v) xprintf(# v " = %d\n", (v))
-static void mousekey_param_inc(uint8_t param, uint8_t inc) {
- switch (param) {
- case 1:
- if (mk_delay + inc < UINT8_MAX)
- mk_delay += inc;
- else
- mk_delay = UINT8_MAX;
- PRINT_SET_VAL(mk_delay);
- break;
- case 2:
- if (mk_interval + inc < UINT8_MAX)
- mk_interval += inc;
- else
- mk_interval = UINT8_MAX;
- PRINT_SET_VAL(mk_interval);
- break;
- case 3:
- if (mk_max_speed + inc < UINT8_MAX)
- mk_max_speed += inc;
- else
- mk_max_speed = UINT8_MAX;
- PRINT_SET_VAL(mk_max_speed);
- break;
- case 4:
- if (mk_time_to_max + inc < UINT8_MAX)
- mk_time_to_max += inc;
- else
- mk_time_to_max = UINT8_MAX;
- PRINT_SET_VAL(mk_time_to_max);
- break;
- case 5:
- if (mk_wheel_max_speed + inc < UINT8_MAX)
- mk_wheel_max_speed += inc;
- else
- mk_wheel_max_speed = UINT8_MAX;
- PRINT_SET_VAL(mk_wheel_max_speed);
- break;
- case 6:
- if (mk_wheel_time_to_max + inc < UINT8_MAX)
- mk_wheel_time_to_max += inc;
- else
- mk_wheel_time_to_max = UINT8_MAX;
- PRINT_SET_VAL(mk_wheel_time_to_max);
- break;
- }
-}
+#if defined(MOUSEKEY_ENABLE)
-static void mousekey_param_dec(uint8_t param, uint8_t dec) {
- switch (param) {
- case 1:
- if (mk_delay > dec)
- mk_delay -= dec;
- else
- mk_delay = 0;
- PRINT_SET_VAL(mk_delay);
- break;
- case 2:
- if (mk_interval > dec)
- mk_interval -= dec;
- else
- mk_interval = 0;
- PRINT_SET_VAL(mk_interval);
- break;
- case 3:
- if (mk_max_speed > dec)
- mk_max_speed -= dec;
- else
- mk_max_speed = 0;
- PRINT_SET_VAL(mk_max_speed);
- break;
- case 4:
- if (mk_time_to_max > dec)
- mk_time_to_max -= dec;
- else
- mk_time_to_max = 0;
- PRINT_SET_VAL(mk_time_to_max);
- break;
- case 5:
- if (mk_wheel_max_speed > dec)
- mk_wheel_max_speed -= dec;
- else
- mk_wheel_max_speed = 0;
- PRINT_SET_VAL(mk_wheel_max_speed);
- break;
- case 6:
- if (mk_wheel_time_to_max > dec)
- mk_wheel_time_to_max -= dec;
- else
- mk_wheel_time_to_max = 0;
- PRINT_SET_VAL(mk_wheel_time_to_max);
- break;
- }
+# if !defined(NO_PRINT) && !defined(USER_PRINT)
+static void mousekey_param_print(void) {
+ xprintf(/* clang-format off */
+
+#ifndef MK_3_SPEED
+ "1: delay(*10ms): %u\n"
+ "2: interval(ms): %u\n"
+ "3: max_speed: %u\n"
+ "4: time_to_max: %u\n"
+ "5: wheel_max_speed: %u\n"
+ "6: wheel_time_to_max: %u\n"
+
+ , mk_delay
+ , mk_interval
+ , mk_max_speed
+ , mk_time_to_max
+ , mk_wheel_max_speed
+ , mk_wheel_time_to_max
+#else
+ "no knobs sorry\n"
+#endif
+
+ ); /* clang-format on */
}
+# endif /* !NO_PRINT && !USER_PRINT */
+# if !defined(NO_PRINT) && !defined(USER_PRINT)
static void mousekey_console_help(void) {
- print("\n\t- Mousekey -\n"
- "ESC/q: quit\n"
- "1: delay(*10ms)\n"
- "2: interval(ms)\n"
- "3: max_speed\n"
- "4: time_to_max\n"
- "5: wheel_max_speed\n"
- "6: wheel_time_to_max\n"
- "\n"
- "p: print values\n"
- "d: set defaults\n"
- "up: +1\n"
- "down: -1\n"
- "pgup: +10\n"
- "pgdown: -10\n"
- "\n"
- "speed = delta * max_speed * (repeat / time_to_max)\n");
- xprintf("where delta: cursor=%d, wheel=%d\n"
- "See http://en.wikipedia.org/wiki/Mouse_keys\n",
- MOUSEKEY_MOVE_DELTA, MOUSEKEY_WHEEL_DELTA);
+ mousekey_param_print();
+ xprintf(/* clang-format off */
+ "p: print values\n"
+ "d: set defaults\n"
+ "up: +1\n"
+ "dn: -1\n"
+ "lt: +10\n"
+ "rt: -10\n"
+ "ESC/q: quit\n"
+
+#ifndef MK_3_SPEED
+ "\n"
+ "speed = delta * max_speed * (repeat / time_to_max)\n"
+ "where delta: cursor=%d, wheel=%d\n"
+ "See http://en.wikipedia.org/wiki/Mouse_keys\n"
+ , MOUSEKEY_MOVE_DELTA, MOUSEKEY_WHEEL_DELTA
+#endif
+
+ ); /* clang-format on */
}
+# endif /* !NO_PRINT && !USER_PRINT */
+
+/* Only used by `quantum/command.c` / `command_proc()`. To avoid
+ * any doubt: we return `false` to return to the main console,
+ * which differs from the `bool` that `command_proc()` returns. */
+bool mousekey_console(uint8_t code) {
+ static uint8_t param = 0;
+ static uint8_t *pp = NULL;
+ static char * desc = NULL;
+
+# if defined(NO_PRINT) || defined(USER_PRINT) /* -Wunused-parameter */
+ (void)desc;
+# endif
+
+ int8_t change = 0;
-static bool mousekey_console(uint8_t code) {
switch (code) {
case KC_H:
case KC_SLASH: /* ? */
+# if !defined(NO_PRINT) && !defined(USER_PRINT)
+ print("\n\t- Mousekey -\n");
mousekey_console_help();
+# endif
break;
+
case KC_Q:
case KC_ESC:
- if (mousekey_param) {
- mousekey_param = 0;
- } else {
- print("C> ");
- command_state = CONSOLE;
- return false;
- }
+ print("q\n");
+ if (!param) return false;
+ param = 0;
+ pp = NULL;
+ desc = NULL;
break;
+
case KC_P:
+# if !defined(NO_PRINT) && !defined(USER_PRINT)
+ print("\n\t- Values -\n");
mousekey_param_print();
+# endif
break;
- case KC_1:
- case KC_2:
- case KC_3:
- case KC_4:
- case KC_5:
- case KC_6:
- mousekey_param = numkey2num(code);
- break;
- case KC_UP:
- mousekey_param_inc(mousekey_param, 1);
- break;
- case KC_DOWN:
- mousekey_param_dec(mousekey_param, 1);
- break;
- case KC_PGUP:
- mousekey_param_inc(mousekey_param, 10);
- break;
- case KC_PGDN:
- mousekey_param_dec(mousekey_param, 10);
- break;
+
+ case KC_1 ... KC_0: /* KC_0 gives param = 10 */
+ param = 1 + code - KC_1;
+ switch (param) { /* clang-format off */
+# define PARAM(n, v) case n: pp = &(v); desc = #v; break
+
+#ifndef MK_3_SPEED
+ PARAM(1, mk_delay);
+ PARAM(2, mk_interval);
+ PARAM(3, mk_max_speed);
+ PARAM(4, mk_time_to_max);
+ PARAM(5, mk_wheel_max_speed);
+ PARAM(6, mk_wheel_time_to_max);
+#endif /* MK_3_SPEED */
+
+# undef PARAM
+ default:
+ param = 0;
+ print("?\n");
+ break;
+ } /* clang-format on */
+ if (param) xprintf("%u\n", param);
+ break;
+
+ /* clang-format off */
+ case KC_UP: change = +1; break;
+ case KC_DOWN: change = -1; break;
+ case KC_LEFT: change = -10; break;
+ case KC_RIGHT: change = +10; break;
+ /* clang-format on */
+
case KC_D:
+
+# ifndef MK_3_SPEED
mk_delay = MOUSEKEY_DELAY / 10;
mk_interval = MOUSEKEY_INTERVAL;
mk_max_speed = MOUSEKEY_MAX_SPEED;
mk_time_to_max = MOUSEKEY_TIME_TO_MAX;
mk_wheel_max_speed = MOUSEKEY_WHEEL_MAX_SPEED;
mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;
- print("set default\n");
+# endif /* MK_3_SPEED */
+
+ print("defaults\n");
break;
+
default:
- print("?");
- return false;
+ print("?\n");
+ break;
+ }
+
+ if (change) {
+ if (pp) {
+ int16_t val = *pp + change;
+ if (val > (int16_t)UINT8_MAX)
+ *pp = UINT8_MAX;
+ else if (val < 0)
+ *pp = 0;
+ else
+ *pp = (uint8_t)val;
+ xprintf("= %u\n", *pp);
+ } else {
+ print("?\n");
+ }
}
- if (mousekey_param) {
- xprintf("M%d> ", mousekey_param);
+
+ if (param) {
+ xprintf("M%u:%s> ", param, desc ? desc : "???");
} else {
- print("M>");
+ print("M> ");
}
return true;
}
-#endif
+
+#endif /* MOUSEKEY_ENABLE */
/***********************************************************
* Utilities
***********************************************************/
-uint8_t numkey2num(uint8_t code) {
- switch (code) {
- case KC_1:
- return 1;
- case KC_2:
- return 2;
- case KC_3:
- return 3;
- case KC_4:
- return 4;
- case KC_5:
- return 5;
- case KC_6:
- return 6;
- case KC_7:
- return 7;
- case KC_8:
- return 8;
- case KC_9:
- return 9;
- case KC_0:
- return 0;
- }
- return 0;
-}
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/command.h b/quantum/command.h
index 4f77be085c..676507d3bd 100644
--- a/quantum/command.h
+++ b/quantum/command.h
@@ -28,8 +28,7 @@ bool command_extra(uint8_t code);
bool command_console_extra(uint8_t code);
#ifdef COMMAND_ENABLE
-uint8_t numkey2num(uint8_t code);
-bool command_proc(uint8_t code);
+bool command_proc(uint8_t code);
#else
# define command_proc(code) false
#endif
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/crc.h b/quantum/crc.h
new file mode 100644
index 0000000000..c17f5888e2
--- /dev/null
+++ b/quantum/crc.h
@@ -0,0 +1,44 @@
+/* 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 "quantum.h"
+
+/**
+ * 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);
+
+/**
+ * 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/digitizer.c b/quantum/digitizer.c
new file mode 100644
index 0000000000..e299867429
--- /dev/null
+++ b/quantum/digitizer.c
@@ -0,0 +1,34 @@
+/* Copyright 2021
+ *
+ * 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 "digitizer.h"
+
+digitizer_t digitizerReport = {.tipswitch = 0, .inrange = 0, .id = 0, .x = 0, .y = 0, .status = DZ_INITIALIZED};
+
+__attribute__((weak)) void digitizer_send(void) {
+ if (digitizerReport.status & DZ_UPDATED) {
+ host_digitizer_send(&digitizerReport);
+ digitizerReport.status &= ~DZ_UPDATED;
+ }
+}
+
+__attribute__((weak)) void digitizer_task(void) { digitizer_send(); }
+
+digitizer_t digitizer_get_report(void) { return digitizerReport; }
+
+void digitizer_set_report(digitizer_t newDigitizerReport) {
+ digitizerReport = newDigitizerReport;
+ digitizerReport.status |= DZ_UPDATED;
+} \ No newline at end of file
diff --git a/quantum/rgb.h b/quantum/digitizer.h
index 2602fc0b20..cef551567e 100644
--- a/quantum/rgb.h
+++ b/quantum/digitizer.h
@@ -1,4 +1,4 @@
-/* Copyright 2017 Jack Humbert
+/* Copyright 2021
*
* 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
@@ -13,27 +13,29 @@
* 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
-__attribute__((weak)) void rgblight_toggle(void){};
-
-__attribute__((weak)) void rgblight_step(void){};
-
-__attribute__((weak)) void rgblight_step_reverse(void){};
+#include "quantum.h"
-__attribute__((weak)) void rgblight_increase_hue(void){};
+#include <stdint.h>
-__attribute__((weak)) void rgblight_decrease_hue(void){};
+enum digitizer_status { DZ_INITIALIZED = 1, DZ_UPDATED = 2 };
-__attribute__((weak)) void rgblight_increase_sat(void){};
+typedef struct {
+ int8_t tipswitch;
+ int8_t inrange;
+ uint8_t id;
+ float x;
+ float y;
+ uint8_t status : 2;
+} digitizer_t;
-__attribute__((weak)) void rgblight_decrease_sat(void){};
+extern digitizer_t digitizer;
-__attribute__((weak)) void rgblight_increase_val(void){};
+digitizer_t digitizer_get_report(void);
-__attribute__((weak)) void rgblight_decrease_val(void){};
+void digitizer_set_report(digitizer_t newDigitizerReport);
-__attribute__((weak)) void rgblight_increase_speed(void){};
+void digitizer_task(void);
-__attribute__((weak)) void rgblight_decrease_speed(void){};
+void host_digitizer_send(digitizer_t *digitizer);
diff --git a/quantum/dip_switch.c b/quantum/dip_switch.c
index cda69bd0ef..133ec85027 100644
--- a/quantum/dip_switch.c
+++ b/quantum/dip_switch.c
@@ -17,6 +17,9 @@
*/
#include "dip_switch.h"
+#ifdef SPLIT_KEYBOARD
+# include "split_common/split_util.h"
+#endif
// for memcpy
#include <string.h>
@@ -49,16 +52,24 @@ 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
+# if defined(SPLIT_KEYBOARD) && defined(DIP_SWITCH_PINS_RIGHT)
+ if (!isLeftHand) {
+ const pin_t dip_switch_pad_right[] = DIP_SWITCH_PINS_RIGHT;
+ for (uint8_t i = 0; i < NUMBER_OF_DIP_SWITCHES; i++) {
+ dip_switch_pad[i] = dip_switch_pad_right[i];
+ }
+ }
+# endif
for (uint8_t i = 0; i < NUMBER_OF_DIP_SWITCHES; i++) {
setPinInputHigh(dip_switch_pad[i]);
}
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/eeconfig.c b/quantum/eeconfig.c
new file mode 100644
index 0000000000..92f0ac4439
--- /dev/null
+++ b/quantum/eeconfig.c
@@ -0,0 +1,240 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include "eeprom.h"
+#include "eeconfig.h"
+#include "action_layer.h"
+
+#ifdef STM32_EEPROM_ENABLE
+# include <hal.h>
+# include "eeprom_stm32.h"
+#endif
+
+#if defined(EEPROM_DRIVER)
+# include "eeprom_driver.h"
+#endif
+
+#if defined(HAPTIC_ENABLE)
+# include "haptic.h"
+#endif
+
+#if defined(VIA_ENABLE)
+bool via_eeprom_is_valid(void);
+void via_eeprom_set_valid(bool valid);
+void eeconfig_init_via(void);
+#endif
+
+/** \brief eeconfig enable
+ *
+ * FIXME: needs doc
+ */
+__attribute__((weak)) void eeconfig_init_user(void) {
+ // Reset user EEPROM value to blank, rather than to a set value
+ eeconfig_update_user(0);
+}
+
+__attribute__((weak)) void eeconfig_init_kb(void) {
+ // Reset Keyboard EEPROM value to blank, rather than to a set value
+ eeconfig_update_kb(0);
+
+ eeconfig_init_user();
+}
+
+/*
+ * FIXME: needs doc
+ */
+void eeconfig_init_quantum(void) {
+#ifdef STM32_EEPROM_ENABLE
+ EEPROM_Erase();
+#endif
+#if defined(EEPROM_DRIVER)
+ eeprom_driver_erase();
+#endif
+ eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER);
+ eeprom_update_byte(EECONFIG_DEBUG, 0);
+ eeprom_update_byte(EECONFIG_DEFAULT_LAYER, 0);
+ default_layer_state = 0;
+ eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, 0);
+ eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, 0);
+ eeprom_update_byte(EECONFIG_MOUSEKEY_ACCEL, 0);
+ eeprom_update_byte(EECONFIG_BACKLIGHT, 0);
+ eeprom_update_byte(EECONFIG_AUDIO, 0xFF); // On by default
+ eeprom_update_dword(EECONFIG_RGBLIGHT, 0);
+ eeprom_update_byte(EECONFIG_STENOMODE, 0);
+ eeprom_update_dword(EECONFIG_HAPTIC, 0);
+ eeprom_update_byte(EECONFIG_VELOCIKEY, 0);
+ eeprom_update_dword(EECONFIG_RGB_MATRIX, 0);
+ eeprom_update_word(EECONFIG_RGB_MATRIX_EXTENDED, 0);
+
+ // TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS
+ // within the emulated eeprom via dfu-util or another tool
+#if defined INIT_EE_HANDS_LEFT
+# pragma message "Faking EE_HANDS for left hand"
+ eeprom_update_byte(EECONFIG_HANDEDNESS, 1);
+#elif defined INIT_EE_HANDS_RIGHT
+# pragma message "Faking EE_HANDS for right hand"
+ eeprom_update_byte(EECONFIG_HANDEDNESS, 0);
+#endif
+
+#if defined(HAPTIC_ENABLE)
+ haptic_reset();
+#else
+ // this is used in case haptic is disabled, but we still want sane defaults
+ // in the haptic configuration eeprom. All zero will trigger a haptic_reset
+ // when a haptic-enabled firmware is loaded onto the keyboard.
+ eeprom_update_dword(EECONFIG_HAPTIC, 0);
+#endif
+#if defined(VIA_ENABLE)
+ // Invalidate VIA eeprom config, and then reset.
+ // Just in case if power is lost mid init, this makes sure that it pets
+ // properly re-initialized.
+ via_eeprom_set_valid(false);
+ eeconfig_init_via();
+#endif
+
+ eeconfig_init_kb();
+}
+
+/** \brief eeconfig initialization
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_init(void) { eeconfig_init_quantum(); }
+
+/** \brief eeconfig enable
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_enable(void) { eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); }
+
+/** \brief eeconfig disable
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_disable(void) {
+#ifdef STM32_EEPROM_ENABLE
+ EEPROM_Erase();
+#endif
+#if defined(EEPROM_DRIVER)
+ eeprom_driver_erase();
+#endif
+ eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER_OFF);
+}
+
+/** \brief eeconfig is enabled
+ *
+ * FIXME: needs doc
+ */
+bool eeconfig_is_enabled(void) {
+ bool is_eeprom_enabled = (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER);
+#ifdef VIA_ENABLE
+ if (is_eeprom_enabled) {
+ is_eeprom_enabled = via_eeprom_is_valid();
+ }
+#endif
+ return is_eeprom_enabled;
+}
+
+/** \brief eeconfig is disabled
+ *
+ * FIXME: needs doc
+ */
+bool eeconfig_is_disabled(void) {
+ bool is_eeprom_disabled = (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER_OFF);
+#ifdef VIA_ENABLE
+ if (!is_eeprom_disabled) {
+ is_eeprom_disabled = !via_eeprom_is_valid();
+ }
+#endif
+ return is_eeprom_disabled;
+}
+
+/** \brief eeconfig read debug
+ *
+ * FIXME: needs doc
+ */
+uint8_t eeconfig_read_debug(void) { return eeprom_read_byte(EECONFIG_DEBUG); }
+/** \brief eeconfig update debug
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_update_debug(uint8_t val) { eeprom_update_byte(EECONFIG_DEBUG, val); }
+
+/** \brief eeconfig read default layer
+ *
+ * FIXME: needs doc
+ */
+uint8_t eeconfig_read_default_layer(void) { return eeprom_read_byte(EECONFIG_DEFAULT_LAYER); }
+/** \brief eeconfig update default layer
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_update_default_layer(uint8_t val) { eeprom_update_byte(EECONFIG_DEFAULT_LAYER, val); }
+
+/** \brief eeconfig read keymap
+ *
+ * FIXME: needs doc
+ */
+uint16_t eeconfig_read_keymap(void) { return (eeprom_read_byte(EECONFIG_KEYMAP_LOWER_BYTE) | (eeprom_read_byte(EECONFIG_KEYMAP_UPPER_BYTE) << 8)); }
+/** \brief eeconfig update keymap
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_update_keymap(uint16_t val) {
+ eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, val & 0xFF);
+ eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, (val >> 8) & 0xFF);
+}
+
+/** \brief eeconfig read audio
+ *
+ * FIXME: needs doc
+ */
+uint8_t eeconfig_read_audio(void) { return eeprom_read_byte(EECONFIG_AUDIO); }
+/** \brief eeconfig update audio
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_update_audio(uint8_t val) { eeprom_update_byte(EECONFIG_AUDIO, val); }
+
+/** \brief eeconfig read kb
+ *
+ * FIXME: needs doc
+ */
+uint32_t eeconfig_read_kb(void) { return eeprom_read_dword(EECONFIG_KEYBOARD); }
+/** \brief eeconfig update kb
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_update_kb(uint32_t val) { eeprom_update_dword(EECONFIG_KEYBOARD, val); }
+
+/** \brief eeconfig read user
+ *
+ * FIXME: needs doc
+ */
+uint32_t eeconfig_read_user(void) { return eeprom_read_dword(EECONFIG_USER); }
+/** \brief eeconfig update user
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_update_user(uint32_t val) { eeprom_update_dword(EECONFIG_USER, val); }
+
+/** \brief eeconfig read haptic
+ *
+ * FIXME: needs doc
+ */
+uint32_t eeconfig_read_haptic(void) { return eeprom_read_dword(EECONFIG_HAPTIC); }
+/** \brief eeconfig update haptic
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_update_haptic(uint32_t val) { eeprom_update_dword(EECONFIG_HAPTIC, val); }
+
+/** \brief eeconfig read split handedness
+ *
+ * FIXME: needs doc
+ */
+bool eeconfig_read_handedness(void) { return !!eeprom_read_byte(EECONFIG_HANDEDNESS); }
+/** \brief eeconfig update split handedness
+ *
+ * FIXME: needs doc
+ */
+void eeconfig_update_handedness(bool val) { eeprom_update_byte(EECONFIG_HANDEDNESS, !!val); }
diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h
new file mode 100644
index 0000000000..bd39971b2c
--- /dev/null
+++ b/quantum/eeconfig.h
@@ -0,0 +1,113 @@
+/*
+Copyright 2013 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/>.
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifndef EECONFIG_MAGIC_NUMBER
+# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE9 // When changing, decrement this value to avoid future re-init issues
+#endif
+#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF
+
+/* EEPROM parameter address */
+#define EECONFIG_MAGIC (uint16_t *)0
+#define EECONFIG_DEBUG (uint8_t *)2
+#define EECONFIG_DEFAULT_LAYER (uint8_t *)3
+#define EECONFIG_KEYMAP (uint8_t *)4
+#define EECONFIG_MOUSEKEY_ACCEL (uint8_t *)5
+#define EECONFIG_BACKLIGHT (uint8_t *)6
+#define EECONFIG_AUDIO (uint8_t *)7
+#define EECONFIG_RGBLIGHT (uint32_t *)8
+#define EECONFIG_UNICODEMODE (uint8_t *)12
+#define EECONFIG_STENOMODE (uint8_t *)13
+// EEHANDS for two handed boards
+#define EECONFIG_HANDEDNESS (uint8_t *)14
+#define EECONFIG_KEYBOARD (uint32_t *)15
+#define EECONFIG_USER (uint32_t *)19
+#define EECONFIG_VELOCIKEY (uint8_t *)23
+
+#define EECONFIG_HAPTIC (uint32_t *)24
+
+// Mutually exclusive
+#define EECONFIG_LED_MATRIX (uint32_t *)28
+#define EECONFIG_RGB_MATRIX (uint32_t *)28
+// Speed & Flags
+#define EECONFIG_LED_MATRIX_EXTENDED (uint16_t *)32
+#define EECONFIG_RGB_MATRIX_EXTENDED (uint16_t *)32
+
+// TODO: Combine these into a single word and single block of EEPROM
+#define EECONFIG_KEYMAP_UPPER_BYTE (uint8_t *)34
+// Size of EEPROM being used, other code can refer to this for available EEPROM
+#define EECONFIG_SIZE 35
+/* debug bit */
+#define EECONFIG_DEBUG_ENABLE (1 << 0)
+#define EECONFIG_DEBUG_MATRIX (1 << 1)
+#define EECONFIG_DEBUG_KEYBOARD (1 << 2)
+#define EECONFIG_DEBUG_MOUSE (1 << 3)
+
+/* keyconf bit */
+#define EECONFIG_KEYMAP_SWAP_CONTROL_CAPSLOCK (1 << 0)
+#define EECONFIG_KEYMAP_CAPSLOCK_TO_CONTROL (1 << 1)
+#define EECONFIG_KEYMAP_SWAP_LALT_LGUI (1 << 2)
+#define EECONFIG_KEYMAP_SWAP_RALT_RGUI (1 << 3)
+#define EECONFIG_KEYMAP_NO_GUI (1 << 4)
+#define EECONFIG_KEYMAP_SWAP_GRAVE_ESC (1 << 5)
+#define EECONFIG_KEYMAP_SWAP_BACKSLASH_BACKSPACE (1 << 6)
+#define EECONFIG_KEYMAP_NKRO (1 << 7)
+
+#define EECONFIG_KEYMAP_LOWER_BYTE EECONFIG_KEYMAP
+
+bool eeconfig_is_enabled(void);
+bool eeconfig_is_disabled(void);
+
+void eeconfig_init(void);
+void eeconfig_init_quantum(void);
+void eeconfig_init_kb(void);
+void eeconfig_init_user(void);
+
+void eeconfig_enable(void);
+
+void eeconfig_disable(void);
+
+uint8_t eeconfig_read_debug(void);
+void eeconfig_update_debug(uint8_t val);
+
+uint8_t eeconfig_read_default_layer(void);
+void eeconfig_update_default_layer(uint8_t val);
+
+uint16_t eeconfig_read_keymap(void);
+void eeconfig_update_keymap(uint16_t val);
+
+#ifdef AUDIO_ENABLE
+uint8_t eeconfig_read_audio(void);
+void eeconfig_update_audio(uint8_t val);
+#endif
+
+uint32_t eeconfig_read_kb(void);
+void eeconfig_update_kb(uint32_t val);
+uint32_t eeconfig_read_user(void);
+void eeconfig_update_user(uint32_t val);
+
+#ifdef HAPTIC_ENABLE
+uint32_t eeconfig_read_haptic(void);
+void eeconfig_update_haptic(uint32_t val);
+#endif
+
+bool eeconfig_read_handedness(void);
+void eeconfig_update_handedness(bool val);
diff --git a/quantum/encoder.c b/quantum/encoder.c
index c30bf01cb2..8fb87281c2 100644
--- a/quantum/encoder.c
+++ b/quantum/encoder.c
@@ -119,6 +119,11 @@ static bool encoder_update(uint8_t index, uint8_t state) {
encoder_update_kb(index, ENCODER_CLOCKWISE);
}
encoder_pulses[i] %= resolution;
+#ifdef ENCODER_DEFAULT_POS
+ if ((state & 0x3) == ENCODER_DEFAULT_POS) {
+ encoder_pulses[i] = 0;
+ }
+#endif
return changed;
}
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/keyboard.c b/quantum/keyboard.c
new file mode 100644
index 0000000000..473306c65d
--- /dev/null
+++ b/quantum/keyboard.c
@@ -0,0 +1,569 @@
+/*
+Copyright 2011, 2012, 2013 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 "keyboard.h"
+#include "matrix.h"
+#include "keymap.h"
+#include "host.h"
+#include "led.h"
+#include "keycode.h"
+#include "timer.h"
+#include "sync_timer.h"
+#include "print.h"
+#include "debug.h"
+#include "command.h"
+#include "util.h"
+#include "sendchar.h"
+#include "eeconfig.h"
+#include "action_layer.h"
+#ifdef BACKLIGHT_ENABLE
+# include "backlight.h"
+#endif
+#ifdef MOUSEKEY_ENABLE
+# include "mousekey.h"
+#endif
+#ifdef PS2_MOUSE_ENABLE
+# include "ps2_mouse.h"
+#endif
+#ifdef SERIAL_MOUSE_ENABLE
+# include "serial_mouse.h"
+#endif
+#ifdef ADB_MOUSE_ENABLE
+# include "adb.h"
+#endif
+#ifdef RGBLIGHT_ENABLE
+# include "rgblight.h"
+#endif
+#ifdef LED_MATRIX_ENABLE
+# include "led_matrix.h"
+#endif
+#ifdef RGB_MATRIX_ENABLE
+# include "rgb_matrix.h"
+#endif
+#ifdef ENCODER_ENABLE
+# include "encoder.h"
+#endif
+#ifdef STENO_ENABLE
+# include "process_steno.h"
+#endif
+#ifdef SERIAL_LINK_ENABLE
+# include "serial_link/system/serial_link.h"
+#endif
+#ifdef VISUALIZER_ENABLE
+# include "visualizer/visualizer.h"
+#endif
+#ifdef POINTING_DEVICE_ENABLE
+# include "pointing_device.h"
+#endif
+#ifdef MIDI_ENABLE
+# include "process_midi.h"
+#endif
+#ifdef JOYSTICK_ENABLE
+# include "process_joystick.h"
+#endif
+#ifdef HD44780_ENABLE
+# include "hd44780.h"
+#endif
+#ifdef QWIIC_ENABLE
+# include "qwiic.h"
+#endif
+#ifdef OLED_ENABLE
+# include "oled_driver.h"
+#endif
+#ifdef ST7565_ENABLE
+# include "st7565.h"
+#endif
+#ifdef VELOCIKEY_ENABLE
+# include "velocikey.h"
+#endif
+#ifdef VIA_ENABLE
+# include "via.h"
+#endif
+#ifdef DIP_SWITCH_ENABLE
+# include "dip_switch.h"
+#endif
+#ifdef STM32_EEPROM_ENABLE
+# include "eeprom_stm32.h"
+#endif
+#ifdef EEPROM_DRIVER
+# include "eeprom_driver.h"
+#endif
+#if defined(CRC_ENABLE)
+# include "crc.h"
+#endif
+#ifdef DIGITIZER_ENABLE
+# include "digitizer.h"
+#endif
+
+static uint32_t last_input_modification_time = 0;
+uint32_t last_input_activity_time(void) { return last_input_modification_time; }
+uint32_t last_input_activity_elapsed(void) { return timer_elapsed32(last_input_modification_time); }
+
+static uint32_t last_matrix_modification_time = 0;
+uint32_t last_matrix_activity_time(void) { return last_matrix_modification_time; }
+uint32_t last_matrix_activity_elapsed(void) { return timer_elapsed32(last_matrix_modification_time); }
+void last_matrix_activity_trigger(void) { last_matrix_modification_time = last_input_modification_time = timer_read32(); }
+
+static uint32_t last_encoder_modification_time = 0;
+uint32_t last_encoder_activity_time(void) { return last_encoder_modification_time; }
+uint32_t last_encoder_activity_elapsed(void) { return timer_elapsed32(last_encoder_modification_time); }
+void last_encoder_activity_trigger(void) { last_encoder_modification_time = last_input_modification_time = timer_read32(); }
+
+// Only enable this if console is enabled to print to
+#if defined(DEBUG_MATRIX_SCAN_RATE)
+static uint32_t matrix_timer = 0;
+static uint32_t matrix_scan_count = 0;
+static uint32_t last_matrix_scan_count = 0;
+
+void matrix_scan_perf_task(void) {
+ matrix_scan_count++;
+
+ uint32_t timer_now = timer_read32();
+ if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) {
+# if defined(CONSOLE_ENABLE)
+ dprintf("matrix scan frequency: %lu\n", matrix_scan_count);
+# endif
+ last_matrix_scan_count = matrix_scan_count;
+ matrix_timer = timer_now;
+ matrix_scan_count = 0;
+ }
+}
+
+uint32_t get_matrix_scan_rate(void) { return last_matrix_scan_count; }
+#else
+# define matrix_scan_perf_task()
+#endif
+
+#ifdef MATRIX_HAS_GHOST
+extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS];
+static matrix_row_t get_real_keys(uint8_t row, matrix_row_t rowdata) {
+ matrix_row_t out = 0;
+ for (uint8_t col = 0; col < MATRIX_COLS; col++) {
+ // read each key in the row data and check if the keymap defines it as a real key
+ if (pgm_read_byte(&keymaps[0][row][col]) && (rowdata & (1 << col))) {
+ // this creates new row data, if a key is defined in the keymap, it will be set here
+ out |= 1 << col;
+ }
+ }
+ return out;
+}
+
+static inline bool popcount_more_than_one(matrix_row_t rowdata) {
+ rowdata &= rowdata - 1; // if there are less than two bits (keys) set, rowdata will become zero
+ return rowdata;
+}
+
+static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) {
+ /* No ghost exists when less than 2 keys are down on the row.
+ If there are "active" blanks in the matrix, the key can't be pressed by the user,
+ there is no doubt as to which keys are really being pressed.
+ The ghosts will be ignored, they are KC_NO. */
+ rowdata = get_real_keys(row, rowdata);
+ if ((popcount_more_than_one(rowdata)) == 0) {
+ return false;
+ }
+ /* Ghost occurs when the row shares a column line with other row,
+ and two columns are read on each row. Blanks in the matrix don't matter,
+ so they are filtered out.
+ If there are two or more real keys pressed and they match columns with
+ at least two of another row's real keys, the row will be ignored. Keep in mind,
+ we are checking one row at a time, not all of them at once.
+ */
+ for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
+ if (i != row && popcount_more_than_one(get_real_keys(i, matrix_get_row(i)) & rowdata)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif
+
+void disable_jtag(void) {
+// To use PF4-7 (PC2-5 on ATmega32A), disable JTAG by writing JTD bit twice within four cycles.
+#if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))
+ MCUCR |= _BV(JTD);
+ MCUCR |= _BV(JTD);
+#elif defined(__AVR_ATmega32A__)
+ MCUCSR |= _BV(JTD);
+ MCUCSR |= _BV(JTD);
+#endif
+}
+
+/** \brief matrix_setup
+ *
+ * FIXME: needs doc
+ */
+__attribute__((weak)) void matrix_setup(void) {}
+
+/** \brief keyboard_pre_init_user
+ *
+ * FIXME: needs doc
+ */
+__attribute__((weak)) void keyboard_pre_init_user(void) {}
+
+/** \brief keyboard_pre_init_kb
+ *
+ * FIXME: needs doc
+ */
+__attribute__((weak)) void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); }
+
+/** \brief keyboard_post_init_user
+ *
+ * FIXME: needs doc
+ */
+
+__attribute__((weak)) void keyboard_post_init_user() {}
+
+/** \brief keyboard_post_init_kb
+ *
+ * FIXME: needs doc
+ */
+
+__attribute__((weak)) void keyboard_post_init_kb(void) { keyboard_post_init_user(); }
+
+/** \brief keyboard_setup
+ *
+ * FIXME: needs doc
+ */
+void keyboard_setup(void) {
+#ifndef NO_JTAG_DISABLE
+ disable_jtag();
+#endif
+ print_set_sendchar(sendchar);
+#ifdef STM32_EEPROM_ENABLE
+ EEPROM_Init();
+#endif
+#ifdef EEPROM_DRIVER
+ eeprom_driver_init();
+#endif
+ matrix_setup();
+ keyboard_pre_init_kb();
+}
+
+#ifndef SPLIT_KEYBOARD
+
+/** \brief is_keyboard_master
+ *
+ * FIXME: needs doc
+ */
+__attribute__((weak)) bool is_keyboard_master(void) { return true; }
+
+/** \brief is_keyboard_left
+ *
+ * FIXME: needs doc
+ */
+__attribute__((weak)) bool is_keyboard_left(void) { return true; }
+
+#endif
+
+/** \brief should_process_keypress
+ *
+ * Override this function if you have a condition where keypresses processing should change:
+ * - splits where the slave side needs to process for rgb/oled functionality
+ */
+__attribute__((weak)) bool should_process_keypress(void) { return is_keyboard_master(); }
+
+/** \brief housekeeping_task_kb
+ *
+ * Override this function if you have a need to execute code for every keyboard main loop iteration.
+ * This is specific to keyboard-level functionality.
+ */
+__attribute__((weak)) void housekeeping_task_kb(void) {}
+
+/** \brief housekeeping_task_user
+ *
+ * Override this function if you have a need to execute code for every keyboard main loop iteration.
+ * This is specific to user/keymap-level functionality.
+ */
+__attribute__((weak)) void housekeeping_task_user(void) {}
+
+/** \brief housekeeping_task
+ *
+ * Invokes hooks for executing code after QMK is done after each loop iteration.
+ */
+void housekeeping_task(void) {
+ housekeeping_task_kb();
+ housekeeping_task_user();
+}
+
+/** \brief keyboard_init
+ *
+ * FIXME: needs doc
+ */
+void keyboard_init(void) {
+ timer_init();
+ sync_timer_init();
+#ifdef VIA_ENABLE
+ via_init();
+#endif
+ matrix_init();
+#if defined(CRC_ENABLE)
+ crc_init();
+#endif
+#ifdef QWIIC_ENABLE
+ qwiic_init();
+#endif
+#ifdef OLED_ENABLE
+ oled_init(OLED_ROTATION_0);
+#endif
+#ifdef ST7565_ENABLE
+ st7565_init(DISPLAY_ROTATION_0);
+#endif
+#ifdef PS2_MOUSE_ENABLE
+ ps2_mouse_init();
+#endif
+#ifdef SERIAL_MOUSE_ENABLE
+ serial_mouse_init();
+#endif
+#ifdef ADB_MOUSE_ENABLE
+ adb_mouse_init();
+#endif
+#ifdef BACKLIGHT_ENABLE
+ backlight_init();
+#endif
+#ifdef RGBLIGHT_ENABLE
+ rgblight_init();
+#endif
+#ifdef ENCODER_ENABLE
+ encoder_init();
+#endif
+#ifdef STENO_ENABLE
+ steno_init();
+#endif
+#ifdef POINTING_DEVICE_ENABLE
+ pointing_device_init();
+#endif
+#if defined(NKRO_ENABLE) && defined(FORCE_NKRO)
+ keymap_config.nkro = 1;
+ eeconfig_update_keymap(keymap_config.raw);
+#endif
+#ifdef DIP_SWITCH_ENABLE
+ dip_switch_init();
+#endif
+
+#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE)
+ debug_enable = true;
+#endif
+
+ keyboard_post_init_kb(); /* Always keep this last */
+}
+
+/** \brief key_event_task
+ *
+ * This function is responsible for calling into other systems when they need to respond to electrical switch press events.
+ * This is differnet than keycode events as no layer processing, or filtering occurs.
+ */
+void switch_events(uint8_t row, uint8_t col, bool pressed) {
+#if defined(LED_MATRIX_ENABLE)
+ process_led_matrix(row, col, pressed);
+#endif
+#if defined(RGB_MATRIX_ENABLE)
+ process_rgb_matrix(row, col, pressed);
+#endif
+}
+
+/** \brief Keyboard task: Do keyboard routine jobs
+ *
+ * Do routine keyboard jobs:
+ *
+ * * scan matrix
+ * * handle mouse movements
+ * * run visualizer code
+ * * handle midi commands
+ * * light LEDs
+ *
+ * This is repeatedly called as fast as possible.
+ */
+void keyboard_task(void) {
+ static matrix_row_t matrix_prev[MATRIX_ROWS];
+ static uint8_t led_status = 0;
+ matrix_row_t matrix_row = 0;
+ matrix_row_t matrix_change = 0;
+#ifdef QMK_KEYS_PER_SCAN
+ uint8_t keys_processed = 0;
+#endif
+#ifdef ENCODER_ENABLE
+ bool encoders_changed = false;
+#endif
+
+ uint8_t matrix_changed = matrix_scan();
+ if (matrix_changed) last_matrix_activity_trigger();
+
+ for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
+ matrix_row = matrix_get_row(r);
+ matrix_change = matrix_row ^ matrix_prev[r];
+ if (matrix_change) {
+#ifdef MATRIX_HAS_GHOST
+ if (has_ghost_in_row(r, matrix_row)) {
+ continue;
+ }
+#endif
+ if (debug_matrix) matrix_print();
+ matrix_row_t col_mask = 1;
+ for (uint8_t c = 0; c < MATRIX_COLS; c++, col_mask <<= 1) {
+ if (matrix_change & col_mask) {
+ if (should_process_keypress()) {
+ action_exec((keyevent_t){
+ .key = (keypos_t){.row = r, .col = c}, .pressed = (matrix_row & col_mask), .time = (timer_read() | 1) /* time should not be 0 */
+ });
+ }
+ // record a processed key
+ matrix_prev[r] ^= col_mask;
+
+ switch_events(r, c, (matrix_row & col_mask));
+
+#ifdef QMK_KEYS_PER_SCAN
+ // only jump out if we have processed "enough" keys.
+ if (++keys_processed >= QMK_KEYS_PER_SCAN)
+#endif
+ // process a key per task call
+ goto MATRIX_LOOP_END;
+ }
+ }
+ }
+ }
+ // call with pseudo tick event when no real key event.
+#ifdef QMK_KEYS_PER_SCAN
+ // we can get here with some keys processed now.
+ if (!keys_processed)
+#endif
+ action_exec(TICK);
+
+MATRIX_LOOP_END:
+
+#ifdef DEBUG_MATRIX_SCAN_RATE
+ matrix_scan_perf_task();
+#endif
+
+#if defined(RGBLIGHT_ENABLE)
+ rgblight_task();
+#endif
+
+#ifdef LED_MATRIX_ENABLE
+ led_matrix_task();
+#endif
+#ifdef RGB_MATRIX_ENABLE
+ rgb_matrix_task();
+#endif
+
+#if defined(BACKLIGHT_ENABLE)
+# if defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS)
+ backlight_task();
+# endif
+#endif
+
+#ifdef ENCODER_ENABLE
+ encoders_changed = encoder_read();
+ if (encoders_changed) last_encoder_activity_trigger();
+#endif
+
+#ifdef QWIIC_ENABLE
+ qwiic_task();
+#endif
+
+#ifdef OLED_ENABLE
+ oled_task();
+# ifndef OLED_DISABLE_TIMEOUT
+ // Wake up oled if user is using those fabulous keys or spinning those encoders!
+# ifdef ENCODER_ENABLE
+ if (matrix_changed || encoders_changed) oled_on();
+# else
+ if (matrix_changed) oled_on();
+# endif
+# endif
+#endif
+
+#ifdef ST7565_ENABLE
+ st7565_task();
+# ifndef ST7565_DISABLE_TIMEOUT
+ // Wake up display if user is using those fabulous keys or spinning those encoders!
+# ifdef ENCODER_ENABLE
+ if (matrix_changed || encoders_changed) st7565_on();
+# else
+ if (matrix_changed) st7565_on();
+# endif
+# endif
+#endif
+
+#ifdef MOUSEKEY_ENABLE
+ // mousekey repeat & acceleration
+ mousekey_task();
+#endif
+
+#ifdef PS2_MOUSE_ENABLE
+ ps2_mouse_task();
+#endif
+
+#ifdef SERIAL_MOUSE_ENABLE
+ serial_mouse_task();
+#endif
+
+#ifdef ADB_MOUSE_ENABLE
+ adb_mouse_task();
+#endif
+
+#ifdef SERIAL_LINK_ENABLE
+ serial_link_update();
+#endif
+
+#ifdef VISUALIZER_ENABLE
+ visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds());
+#endif
+
+#ifdef POINTING_DEVICE_ENABLE
+ pointing_device_task();
+#endif
+
+#ifdef MIDI_ENABLE
+ midi_task();
+#endif
+
+#ifdef VELOCIKEY_ENABLE
+ if (velocikey_enabled()) {
+ velocikey_decelerate();
+ }
+#endif
+
+#ifdef JOYSTICK_ENABLE
+ joystick_task();
+#endif
+
+#ifdef DIGITIZER_ENABLE
+ digitizer_task();
+#endif
+
+ // update LED
+ if (led_status != host_keyboard_leds()) {
+ led_status = host_keyboard_leds();
+ keyboard_set_leds(led_status);
+ }
+}
+
+/** \brief keyboard set leds
+ *
+ * FIXME: needs doc
+ */
+void keyboard_set_leds(uint8_t leds) {
+ if (debug_keyboard) {
+ debug("keyboard_set_led: ");
+ debug_hex8(leds);
+ debug("\n");
+ }
+ led_set(leds);
+}
diff --git a/quantum/keyboard.h b/quantum/keyboard.h
new file mode 100644
index 0000000000..08f4e84f94
--- /dev/null
+++ b/quantum/keyboard.h
@@ -0,0 +1,90 @@
+/*
+Copyright 2011,2012,2013 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/>.
+*/
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* key matrix position */
+typedef struct {
+ uint8_t col;
+ uint8_t row;
+} keypos_t;
+
+/* key event */
+typedef struct {
+ keypos_t key;
+ bool pressed;
+ uint16_t time;
+} keyevent_t;
+
+/* equivalent test of keypos_t */
+#define KEYEQ(keya, keyb) ((keya).row == (keyb).row && (keya).col == (keyb).col)
+
+/* Rules for No Event:
+ * 1) (time == 0) to handle (keyevent_t){} as empty event
+ * 2) Matrix(255, 255) to make TICK event available
+ */
+static inline bool IS_NOEVENT(keyevent_t event) { return event.time == 0 || (event.key.row == 255 && event.key.col == 255); }
+static inline bool IS_PRESSED(keyevent_t event) { return (!IS_NOEVENT(event) && event.pressed); }
+static inline bool IS_RELEASED(keyevent_t event) { return (!IS_NOEVENT(event) && !event.pressed); }
+
+/* Tick event */
+#define TICK \
+ (keyevent_t) { .key = (keypos_t){.row = 255, .col = 255}, .pressed = false, .time = (timer_read() | 1) }
+
+/* it runs once at early stage of startup before keyboard_init. */
+void keyboard_setup(void);
+/* it runs once after initializing host side protocol, debug and MCU peripherals. */
+void keyboard_init(void);
+/* it runs repeatedly in main loop */
+void keyboard_task(void);
+/* it runs when host LED status is updated */
+void keyboard_set_leds(uint8_t leds);
+/* it runs whenever code has to behave differently on a slave */
+bool is_keyboard_master(void);
+/* it runs whenever code has to behave differently on left vs right split */
+bool is_keyboard_left(void);
+
+void keyboard_pre_init_kb(void);
+void keyboard_pre_init_user(void);
+void keyboard_post_init_kb(void);
+void keyboard_post_init_user(void);
+
+void housekeeping_task(void); // To be executed by the main loop in each backend TMK protocol
+void housekeeping_task_kb(void); // To be overridden by keyboard-level code
+void housekeeping_task_user(void); // To be overridden by user/keymap-level code
+
+uint32_t last_input_activity_time(void); // Timestamp of the last matrix or encoder activity
+uint32_t last_input_activity_elapsed(void); // Number of milliseconds since the last matrix or encoder activity
+
+uint32_t last_matrix_activity_time(void); // Timestamp of the last matrix activity
+uint32_t last_matrix_activity_elapsed(void); // Number of milliseconds since the last matrix activity
+
+uint32_t last_encoder_activity_time(void); // Timestamp of the last encoder activity
+uint32_t last_encoder_activity_elapsed(void); // Number of milliseconds since the last encoder activity
+
+uint32_t get_matrix_scan_rate(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/quantum/keycode.h b/quantum/keycode.h
new file mode 100644
index 0000000000..8facabd818
--- /dev/null
+++ b/quantum/keycode.h
@@ -0,0 +1,560 @@
+/*
+Copyright 2011,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/>.
+*/
+
+/*
+ * Keycodes based on HID Keyboard/Keypad Usage Page (0x07) plus media keys from Generic Desktop Page (0x01) and Consumer Page (0x0C)
+ *
+ * See https://web.archive.org/web/20060218214400/http://www.usb.org/developers/devclass_docs/Hut1_12.pdf
+ * or http://www.usb.org/developers/hidpage/Hut1_12v2.pdf (older)
+ */
+
+#pragma once
+
+/* FIXME: Add doxygen comments here */
+
+#define IS_ERROR(code) (KC_ROLL_OVER <= (code) && (code) <= KC_UNDEFINED)
+#define IS_ANY(code) (KC_A <= (code) && (code) <= 0xFF)
+#define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL)
+#define IS_MOD(code) (KC_LCTRL <= (code) && (code) <= KC_RGUI)
+
+#define IS_SPECIAL(code) ((0xA5 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF))
+#define IS_SYSTEM(code) (KC_PWR <= (code) && (code) <= KC_WAKE)
+#define IS_CONSUMER(code) (KC_MUTE <= (code) && (code) <= KC_BRID)
+
+#define IS_FN(code) (KC_FN0 <= (code) && (code) <= KC_FN31)
+
+#define IS_MOUSEKEY(code) (KC_MS_UP <= (code) && (code) <= KC_MS_ACCEL2)
+#define IS_MOUSEKEY_MOVE(code) (KC_MS_UP <= (code) && (code) <= KC_MS_RIGHT)
+#define IS_MOUSEKEY_BUTTON(code) (KC_MS_BTN1 <= (code) && (code) <= KC_MS_BTN8)
+#define IS_MOUSEKEY_WHEEL(code) (KC_MS_WH_UP <= (code) && (code) <= KC_MS_WH_RIGHT)
+#define IS_MOUSEKEY_ACCEL(code) (KC_MS_ACCEL0 <= (code) && (code) <= KC_MS_ACCEL2)
+
+#define MOD_BIT(code) (1 << MOD_INDEX(code))
+#define MOD_INDEX(code) ((code)&0x07)
+
+#define MOD_MASK_CTRL (MOD_BIT(KC_LCTRL) | MOD_BIT(KC_RCTRL))
+#define MOD_MASK_SHIFT (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT))
+#define MOD_MASK_ALT (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT))
+#define MOD_MASK_GUI (MOD_BIT(KC_LGUI) | MOD_BIT(KC_RGUI))
+#define MOD_MASK_CS (MOD_MASK_CTRL | MOD_MASK_SHIFT)
+#define MOD_MASK_CA (MOD_MASK_CTRL | MOD_MASK_ALT)
+#define MOD_MASK_CG (MOD_MASK_CTRL | MOD_MASK_GUI)
+#define MOD_MASK_SA (MOD_MASK_SHIFT | MOD_MASK_ALT)
+#define MOD_MASK_SG (MOD_MASK_SHIFT | MOD_MASK_GUI)
+#define MOD_MASK_AG (MOD_MASK_ALT | MOD_MASK_GUI)
+#define MOD_MASK_CSA (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT)
+#define MOD_MASK_CSG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_GUI)
+#define MOD_MASK_CAG (MOD_MASK_CTRL | MOD_MASK_ALT | MOD_MASK_GUI)
+#define MOD_MASK_SAG (MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI)
+#define MOD_MASK_CSAG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI)
+
+#define FN_BIT(code) (1 << FN_INDEX(code))
+#define FN_INDEX(code) ((code)-KC_FN0)
+#define FN_MIN KC_FN0
+#define FN_MAX KC_FN31
+
+/*
+ * Short names for ease of definition of keymap
+ */
+/* Transparent */
+#define KC_TRANSPARENT 0x01
+#define KC_TRNS KC_TRANSPARENT
+
+/* Punctuation */
+#define KC_ENT KC_ENTER
+#define KC_ESC KC_ESCAPE
+#define KC_BSPC KC_BSPACE
+#define KC_SPC KC_SPACE
+#define KC_MINS KC_MINUS
+#define KC_EQL KC_EQUAL
+#define KC_LBRC KC_LBRACKET
+#define KC_RBRC KC_RBRACKET
+#define KC_BSLS KC_BSLASH
+#define KC_NUHS KC_NONUS_HASH
+#define KC_SCLN KC_SCOLON
+#define KC_QUOT KC_QUOTE
+#define KC_GRV KC_GRAVE
+#define KC_COMM KC_COMMA
+#define KC_SLSH KC_SLASH
+#define KC_NUBS KC_NONUS_BSLASH
+
+/* Lock Keys */
+#define KC_CLCK KC_CAPSLOCK
+#define KC_CAPS KC_CAPSLOCK
+#define KC_SLCK KC_SCROLLLOCK
+#define KC_NLCK KC_NUMLOCK
+#define KC_LCAP KC_LOCKING_CAPS
+#define KC_LNUM KC_LOCKING_NUM
+#define KC_LSCR KC_LOCKING_SCROLL
+
+/* Commands */
+#define KC_PSCR KC_PSCREEN
+#define KC_PAUS KC_PAUSE
+#define KC_BRK KC_PAUSE
+#define KC_INS KC_INSERT
+#define KC_DEL KC_DELETE
+#define KC_PGDN KC_PGDOWN
+#define KC_RGHT KC_RIGHT
+#define KC_APP KC_APPLICATION
+#define KC_EXEC KC_EXECUTE
+#define KC_SLCT KC_SELECT
+#define KC_AGIN KC_AGAIN
+#define KC_PSTE KC_PASTE
+#define KC_ERAS KC_ALT_ERASE
+#define KC_CLR KC_CLEAR
+
+/* Keypad */
+#define KC_PSLS KC_KP_SLASH
+#define KC_PAST KC_KP_ASTERISK
+#define KC_PMNS KC_KP_MINUS
+#define KC_PPLS KC_KP_PLUS
+#define KC_PENT KC_KP_ENTER
+#define KC_P1 KC_KP_1
+#define KC_P2 KC_KP_2
+#define KC_P3 KC_KP_3
+#define KC_P4 KC_KP_4
+#define KC_P5 KC_KP_5
+#define KC_P6 KC_KP_6
+#define KC_P7 KC_KP_7
+#define KC_P8 KC_KP_8
+#define KC_P9 KC_KP_9
+#define KC_P0 KC_KP_0
+#define KC_PDOT KC_KP_DOT
+#define KC_PEQL KC_KP_EQUAL
+#define KC_PCMM KC_KP_COMMA
+
+/* Japanese specific */
+#define KC_ZKHK KC_GRAVE
+#define KC_RO KC_INT1
+#define KC_KANA KC_INT2
+#define KC_JYEN KC_INT3
+#define KC_HENK KC_INT4
+#define KC_MHEN KC_INT5
+
+/* Korean specific */
+#define KC_HAEN KC_LANG1
+#define KC_HANJ KC_LANG2
+
+/* Modifiers */
+#define KC_LCTL KC_LCTRL
+#define KC_LSFT KC_LSHIFT
+#define KC_LOPT KC_LALT
+#define KC_LCMD KC_LGUI
+#define KC_LWIN KC_LGUI
+#define KC_RCTL KC_RCTRL
+#define KC_RSFT KC_RSHIFT
+#define KC_ALGR KC_RALT
+#define KC_ROPT KC_RALT
+#define KC_RCMD KC_RGUI
+#define KC_RWIN KC_RGUI
+
+/* Generic Desktop Page (0x01) */
+#define KC_PWR KC_SYSTEM_POWER
+#define KC_SLEP KC_SYSTEM_SLEEP
+#define KC_WAKE KC_SYSTEM_WAKE
+
+/* Consumer Page (0x0C) */
+#define KC_MUTE KC_AUDIO_MUTE
+#define KC_VOLU KC_AUDIO_VOL_UP
+#define KC_VOLD KC_AUDIO_VOL_DOWN
+#define KC_MNXT KC_MEDIA_NEXT_TRACK
+#define KC_MPRV KC_MEDIA_PREV_TRACK
+#define KC_MSTP KC_MEDIA_STOP
+#define KC_MPLY KC_MEDIA_PLAY_PAUSE
+#define KC_MSEL KC_MEDIA_SELECT
+#define KC_EJCT KC_MEDIA_EJECT
+#define KC_CALC KC_CALCULATOR
+#define KC_MYCM KC_MY_COMPUTER
+#define KC_WSCH KC_WWW_SEARCH
+#define KC_WHOM KC_WWW_HOME
+#define KC_WBAK KC_WWW_BACK
+#define KC_WFWD KC_WWW_FORWARD
+#define KC_WSTP KC_WWW_STOP
+#define KC_WREF KC_WWW_REFRESH
+#define KC_WFAV KC_WWW_FAVORITES
+#define KC_MFFD KC_MEDIA_FAST_FORWARD
+#define KC_MRWD KC_MEDIA_REWIND
+#define KC_BRIU KC_BRIGHTNESS_UP
+#define KC_BRID KC_BRIGHTNESS_DOWN
+
+/* System Specific */
+#define KC_BRMU KC_PAUSE
+#define KC_BRMD KC_SCROLLLOCK
+
+/* Mouse Keys */
+#define KC_MS_U KC_MS_UP
+#define KC_MS_D KC_MS_DOWN
+#define KC_MS_L KC_MS_LEFT
+#define KC_MS_R KC_MS_RIGHT
+#define KC_BTN1 KC_MS_BTN1
+#define KC_BTN2 KC_MS_BTN2
+#define KC_BTN3 KC_MS_BTN3
+#define KC_BTN4 KC_MS_BTN4
+#define KC_BTN5 KC_MS_BTN5
+#define KC_BTN6 KC_MS_BTN6
+#define KC_BTN7 KC_MS_BTN7
+#define KC_BTN8 KC_MS_BTN8
+#define KC_WH_U KC_MS_WH_UP
+#define KC_WH_D KC_MS_WH_DOWN
+#define KC_WH_L KC_MS_WH_LEFT
+#define KC_WH_R KC_MS_WH_RIGHT
+#define KC_ACL0 KC_MS_ACCEL0
+#define KC_ACL1 KC_MS_ACCEL1
+#define KC_ACL2 KC_MS_ACCEL2
+
+/* Keyboard/Keypad Page (0x07) */
+enum hid_keyboard_keypad_usage {
+ KC_NO = 0x00,
+ KC_ROLL_OVER,
+ KC_POST_FAIL,
+ KC_UNDEFINED,
+ KC_A,
+ KC_B,
+ KC_C,
+ KC_D,
+ KC_E,
+ KC_F,
+ KC_G,
+ KC_H,
+ KC_I,
+ KC_J,
+ KC_K,
+ KC_L,
+ KC_M, // 0x10
+ KC_N,
+ KC_O,
+ KC_P,
+ KC_Q,
+ KC_R,
+ KC_S,
+ KC_T,
+ KC_U,
+ KC_V,
+ KC_W,
+ KC_X,
+ KC_Y,
+ KC_Z,
+ KC_1,
+ KC_2,
+ KC_3, // 0x20
+ KC_4,
+ KC_5,
+ KC_6,
+ KC_7,
+ KC_8,
+ KC_9,
+ KC_0,
+ KC_ENTER,
+ KC_ESCAPE,
+ KC_BSPACE,
+ KC_TAB,
+ KC_SPACE,
+ KC_MINUS,
+ KC_EQUAL,
+ KC_LBRACKET,
+ KC_RBRACKET, // 0x30
+ KC_BSLASH,
+ KC_NONUS_HASH,
+ KC_SCOLON,
+ KC_QUOTE,
+ KC_GRAVE,
+ KC_COMMA,
+ KC_DOT,
+ KC_SLASH,
+ KC_CAPSLOCK,
+ KC_F1,
+ KC_F2,
+ KC_F3,
+ KC_F4,
+ KC_F5,
+ KC_F6,
+ KC_F7, // 0x40
+ KC_F8,
+ KC_F9,
+ KC_F10,
+ KC_F11,
+ KC_F12,
+ KC_PSCREEN,
+ KC_SCROLLLOCK,
+ KC_PAUSE,
+ KC_INSERT,
+ KC_HOME,
+ KC_PGUP,
+ KC_DELETE,
+ KC_END,
+ KC_PGDOWN,
+ KC_RIGHT,
+ KC_LEFT, // 0x50
+ KC_DOWN,
+ KC_UP,
+ KC_NUMLOCK,
+ KC_KP_SLASH,
+ KC_KP_ASTERISK,
+ KC_KP_MINUS,
+ KC_KP_PLUS,
+ KC_KP_ENTER,
+ KC_KP_1,
+ KC_KP_2,
+ KC_KP_3,
+ KC_KP_4,
+ KC_KP_5,
+ KC_KP_6,
+ KC_KP_7,
+ KC_KP_8, // 0x60
+ KC_KP_9,
+ KC_KP_0,
+ KC_KP_DOT,
+ KC_NONUS_BSLASH,
+ KC_APPLICATION,
+ KC_POWER,
+ KC_KP_EQUAL,
+ KC_F13,
+ KC_F14,
+ KC_F15,
+ KC_F16,
+ KC_F17,
+ KC_F18,
+ KC_F19,
+ KC_F20,
+ KC_F21, // 0x70
+ KC_F22,
+ KC_F23,
+ KC_F24,
+ KC_EXECUTE,
+ KC_HELP,
+ KC_MENU,
+ KC_SELECT,
+ KC_STOP,
+ KC_AGAIN,
+ KC_UNDO,
+ KC_CUT,
+ KC_COPY,
+ KC_PASTE,
+ KC_FIND,
+ KC__MUTE,
+ KC__VOLUP, // 0x80
+ KC__VOLDOWN,
+ KC_LOCKING_CAPS,
+ KC_LOCKING_NUM,
+ KC_LOCKING_SCROLL,
+ KC_KP_COMMA,
+ KC_KP_EQUAL_AS400,
+ KC_INT1,
+ KC_INT2,
+ KC_INT3,
+ KC_INT4,
+ KC_INT5,
+ KC_INT6,
+ KC_INT7,
+ KC_INT8,
+ KC_INT9,
+ KC_LANG1, // 0x90
+ KC_LANG2,
+ KC_LANG3,
+ KC_LANG4,
+ KC_LANG5,
+ KC_LANG6,
+ KC_LANG7,
+ KC_LANG8,
+ KC_LANG9,
+ KC_ALT_ERASE,
+ KC_SYSREQ,
+ KC_CANCEL,
+ KC_CLEAR,
+ KC_PRIOR,
+ KC_RETURN,
+ KC_SEPARATOR,
+ KC_OUT, // 0xA0
+ KC_OPER,
+ KC_CLEAR_AGAIN,
+ KC_CRSEL,
+ KC_EXSEL,
+
+#if 0
+ // ***************************************************************
+ // These keycodes are present in the HID spec, but are *
+ // nonfunctional on modern OSes. QMK uses this range (0xA5-0xDF) *
+ // for the media and function keys instead - see below. *
+ // ***************************************************************
+
+ KC_KP_00 = 0xB0,
+ KC_KP_000,
+ KC_THOUSANDS_SEPARATOR,
+ KC_DECIMAL_SEPARATOR,
+ KC_CURRENCY_UNIT,
+ KC_CURRENCY_SUB_UNIT,
+ KC_KP_LPAREN,
+ KC_KP_RPAREN,
+ KC_KP_LCBRACKET,
+ KC_KP_RCBRACKET,
+ KC_KP_TAB,
+ KC_KP_BSPACE,
+ KC_KP_A,
+ KC_KP_B,
+ KC_KP_C,
+ KC_KP_D,
+ KC_KP_E, //0xC0
+ KC_KP_F,
+ KC_KP_XOR,
+ KC_KP_HAT,
+ KC_KP_PERC,
+ KC_KP_LT,
+ KC_KP_GT,
+ KC_KP_AND,
+ KC_KP_LAZYAND,
+ KC_KP_OR,
+ KC_KP_LAZYOR,
+ KC_KP_COLON,
+ KC_KP_HASH,
+ KC_KP_SPACE,
+ KC_KP_ATMARK,
+ KC_KP_EXCLAMATION,
+ KC_KP_MEM_STORE, //0xD0
+ KC_KP_MEM_RECALL,
+ KC_KP_MEM_CLEAR,
+ KC_KP_MEM_ADD,
+ KC_KP_MEM_SUB,
+ KC_KP_MEM_MUL,
+ KC_KP_MEM_DIV,
+ KC_KP_PLUS_MINUS,
+ KC_KP_CLEAR,
+ KC_KP_CLEAR_ENTRY,
+ KC_KP_BINARY,
+ KC_KP_OCTAL,
+ KC_KP_DECIMAL,
+ KC_KP_HEXADECIMAL,
+#endif
+
+ /* Modifiers */
+ KC_LCTRL = 0xE0,
+ KC_LSHIFT,
+ KC_LALT,
+ KC_LGUI,
+ KC_RCTRL,
+ KC_RSHIFT,
+ KC_RALT,
+ KC_RGUI
+
+ // **********************************************
+ // * 0xF0-0xFF are unallocated in the HID spec. *
+ // * QMK uses these for Mouse Keys - see below. *
+ // **********************************************
+};
+
+/* Media and Function keys */
+enum internal_special_keycodes {
+ /* Generic Desktop Page (0x01) */
+ KC_SYSTEM_POWER = 0xA5,
+ KC_SYSTEM_SLEEP,
+ KC_SYSTEM_WAKE,
+
+ /* Consumer Page (0x0C) */
+ KC_AUDIO_MUTE,
+ KC_AUDIO_VOL_UP,
+ KC_AUDIO_VOL_DOWN,
+ KC_MEDIA_NEXT_TRACK,
+ KC_MEDIA_PREV_TRACK,
+ KC_MEDIA_STOP,
+ KC_MEDIA_PLAY_PAUSE,
+ KC_MEDIA_SELECT,
+ KC_MEDIA_EJECT, // 0xB0
+ KC_MAIL,
+ KC_CALCULATOR,
+ KC_MY_COMPUTER,
+ KC_WWW_SEARCH,
+ KC_WWW_HOME,
+ KC_WWW_BACK,
+ KC_WWW_FORWARD,
+ KC_WWW_STOP,
+ KC_WWW_REFRESH,
+ KC_WWW_FAVORITES,
+ KC_MEDIA_FAST_FORWARD,
+ KC_MEDIA_REWIND,
+ KC_BRIGHTNESS_UP,
+ KC_BRIGHTNESS_DOWN,
+
+ /* Fn keys */
+ KC_FN0 = 0xC0,
+ KC_FN1,
+ KC_FN2,
+ KC_FN3,
+ KC_FN4,
+ KC_FN5,
+ KC_FN6,
+ KC_FN7,
+ KC_FN8,
+ KC_FN9,
+ KC_FN10,
+ KC_FN11,
+ KC_FN12,
+ KC_FN13,
+ KC_FN14,
+ KC_FN15,
+ KC_FN16, // 0xD0
+ KC_FN17,
+ KC_FN18,
+ KC_FN19,
+ KC_FN20,
+ KC_FN21,
+ KC_FN22,
+ KC_FN23,
+ KC_FN24,
+ KC_FN25,
+ KC_FN26,
+ KC_FN27,
+ KC_FN28,
+ KC_FN29,
+ KC_FN30,
+ KC_FN31
+};
+
+enum mouse_keys {
+/* Mouse Buttons */
+#ifdef VIA_ENABLE
+ KC_MS_UP = 0xF0,
+#else
+ KC_MS_UP = 0xED,
+#endif
+ KC_MS_DOWN,
+ KC_MS_LEFT,
+ KC_MS_RIGHT, // 0xF0
+ KC_MS_BTN1,
+ KC_MS_BTN2,
+ KC_MS_BTN3,
+ KC_MS_BTN4,
+ KC_MS_BTN5,
+#ifdef VIA_ENABLE
+ KC_MS_BTN6 = KC_MS_BTN5,
+ KC_MS_BTN7 = KC_MS_BTN5,
+ KC_MS_BTN8 = KC_MS_BTN5,
+#else
+ KC_MS_BTN6,
+ KC_MS_BTN7,
+ KC_MS_BTN8,
+#endif
+
+ /* Mouse Wheel */
+ KC_MS_WH_UP,
+ KC_MS_WH_DOWN,
+ KC_MS_WH_LEFT,
+ KC_MS_WH_RIGHT,
+
+ /* Acceleration */
+ KC_MS_ACCEL0,
+ KC_MS_ACCEL1,
+ KC_MS_ACCEL2 // 0xFF
+};
diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c
index e0fd6d4793..008177bbee 100644
--- a/quantum/keymap_common.c
+++ b/quantum/keymap_common.c
@@ -36,11 +36,39 @@ extern keymap_config_t keymap_config;
#include <inttypes.h>
+uint8_t extract_mod_bits(uint16_t code) {
+ switch (code) {
+ case QK_MODS ... QK_MODS_MAX:
+ break;
+ default:
+ return 0;
+ }
+
+ uint8_t mods_to_send = 0;
+
+ if (code & QK_RMODS_MIN) { // Right mod flag is set
+ if (code & QK_LCTL) mods_to_send |= MOD_BIT(KC_RCTL);
+ if (code & QK_LSFT) mods_to_send |= MOD_BIT(KC_RSFT);
+ if (code & QK_LALT) mods_to_send |= MOD_BIT(KC_RALT);
+ if (code & QK_LGUI) mods_to_send |= MOD_BIT(KC_RGUI);
+ } else {
+ if (code & QK_LCTL) mods_to_send |= MOD_BIT(KC_LCTL);
+ if (code & QK_LSFT) mods_to_send |= MOD_BIT(KC_LSFT);
+ if (code & QK_LALT) mods_to_send |= MOD_BIT(KC_LALT);
+ if (code & QK_LGUI) mods_to_send |= MOD_BIT(KC_LGUI);
+ }
+
+ return mods_to_send;
+}
+
/* converts key to action */
action_t action_for_key(uint8_t layer, keypos_t key) {
// 16bit keycodes - important
uint16_t keycode = keymap_key_to_keycode(layer, key);
+ return action_for_keycode(keycode);
+};
+action_t action_for_keycode(uint16_t keycode) {
// keycode remapping
keycode = keycode_config(keycode);
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_steno.h b/quantum/keymap_extras/keymap_steno.h
index b9115fb8bf..ab95b43fdd 100644
--- a/quantum/keymap_extras/keymap_steno.h
+++ b/quantum/keymap_extras/keymap_steno.h
@@ -72,3 +72,21 @@ enum steno_keycodes {
STN_ZR,
STN__MAX = STN_ZR, // must be less than QK_STENO_BOLT
};
+
+#ifdef STENO_COMBINEDMAP
+enum steno_combined_keycodes
+{
+ STN_S3 = QK_STENO_COMB,
+ STN_TKL,
+ STN_PWL,
+ STN_HRL,
+ STN_FRR,
+ STN_PBR,
+ STN_LGR,
+ STN_TSR,
+ STN_DZR,
+ STN_AO,
+ STN_EU,
+ STN_COMB_MAX = STN_EU,
+};
+#endif
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..14038cd082 100644
--- a/quantum/led_matrix_animations/alpha_mods_anim.h
+++ b/quantum/led_matrix/animations/alpha_mods_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_LED_MATRIX_ALPHAS_MODS
+#ifdef ENABLE_LED_MATRIX_ALPHAS_MODS
LED_MATRIX_EFFECT(ALPHAS_MODS)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..5548787b88 100644
--- a/quantum/led_matrix_animations/band_anim.h
+++ b/quantum/led_matrix/animations/band_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_LED_MATRIX_BAND
+#ifdef ENABLE_LED_MATRIX_BAND
LED_MATRIX_EFFECT(BAND)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..89651582d5 100644
--- a/quantum/led_matrix_animations/band_pinwheel_anim.h
+++ b/quantum/led_matrix/animations/band_pinwheel_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_LED_MATRIX_BAND_PINWHEEL
+#ifdef ENABLE_LED_MATRIX_BAND_PINWHEEL
LED_MATRIX_EFFECT(BAND_PINWHEEL)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..70b0ffaea9 100644
--- a/quantum/led_matrix_animations/band_spiral_anim.h
+++ b/quantum/led_matrix/animations/band_spiral_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_LED_MATRIX_BAND_SPIRAL
+#ifdef ENABLE_LED_MATRIX_BAND_SPIRAL
LED_MATRIX_EFFECT(BAND_SPIRAL)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..e3f600c45c 100644
--- a/quantum/led_matrix_animations/breathing_anim.h
+++ b/quantum/led_matrix/animations/breathing_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_LED_MATRIX_BREATHING
+#ifdef ENABLE_LED_MATRIX_BREATHING
LED_MATRIX_EFFECT(BREATHING)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..769e6d7942 100644
--- a/quantum/led_matrix_animations/cycle_left_right_anim.h
+++ b/quantum/led_matrix/animations/cycle_left_right_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
+#ifdef ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
LED_MATRIX_EFFECT(CYCLE_LEFT_RIGHT)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..6adf9c25ff 100644
--- a/quantum/led_matrix_animations/cycle_out_in_anim.h
+++ b/quantum/led_matrix/animations/cycle_out_in_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_LED_MATRIX_CYCLE_OUT_IN
+#ifdef ENABLE_LED_MATRIX_CYCLE_OUT_IN
LED_MATRIX_EFFECT(CYCLE_OUT_IN)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..7a5868ac26 100644
--- a/quantum/led_matrix_animations/cycle_up_down_anim.h
+++ b/quantum/led_matrix/animations/cycle_up_down_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_LED_MATRIX_CYCLE_UP_DOWN
+#ifdef ENABLE_LED_MATRIX_CYCLE_UP_DOWN
LED_MATRIX_EFFECT(CYCLE_UP_DOWN)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..3552c9fc39 100644
--- a/quantum/led_matrix_animations/dual_beacon_anim.h
+++ b/quantum/led_matrix/animations/dual_beacon_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_LED_MATRIX_DUAL_BEACON
+#ifdef ENABLE_LED_MATRIX_DUAL_BEACON
LED_MATRIX_EFFECT(DUAL_BEACON)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..3a3c46be13 100644
--- a/quantum/led_matrix_animations/solid_reactive_cross.h
+++ b/quantum/led_matrix/animations/solid_reactive_cross.h
@@ -1,11 +1,11 @@
#ifdef LED_MATRIX_KEYREACTIVE_ENABLED
# if !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
LED_MATRIX_EFFECT(SOLID_REACTIVE_CROSS)
# endif
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS
LED_MATRIX_EFFECT(SOLID_REACTIVE_MULTICROSS)
# endif
@@ -22,14 +22,14 @@ static uint8_t SOLID_REACTIVE_CROSS_math(uint8_t val, int16_t dx, int16_t dy, ui
return qadd8(val, 255 - effect);
}
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
bool SOLID_REACTIVE_CROSS(effect_params_t* params) { return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_CROSS_math); }
# endif
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS
bool SOLID_REACTIVE_MULTICROSS(effect_params_t* params) { return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_CROSS_math); }
# 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..2520b8e1df 100644
--- a/quantum/led_matrix_animations/solid_reactive_nexus.h
+++ b/quantum/led_matrix/animations/solid_reactive_nexus.h
@@ -1,11 +1,11 @@
#ifdef LED_MATRIX_KEYREACTIVE_ENABLED
-# if !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS)
+# if defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS)
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS
LED_MATRIX_EFFECT(SOLID_REACTIVE_NEXUS)
# endif
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS
LED_MATRIX_EFFECT(SOLID_REACTIVE_MULTINEXUS)
# endif
@@ -19,14 +19,14 @@ static uint8_t SOLID_REACTIVE_NEXUS_math(uint8_t val, int16_t dx, int16_t dy, ui
return qadd8(val, 255 - effect);
}
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS
bool SOLID_REACTIVE_NEXUS(effect_params_t* params) { return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_NEXUS_math); }
# endif
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS
bool SOLID_REACTIVE_MULTINEXUS(effect_params_t* params) { return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_NEXUS_math); }
# 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..43f6ad9ab4 100644
--- a/quantum/led_matrix_animations/solid_reactive_simple_anim.h
+++ b/quantum/led_matrix/animations/solid_reactive_simple_anim.h
@@ -1,5 +1,5 @@
#ifdef LED_MATRIX_KEYREACTIVE_ENABLED
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
LED_MATRIX_EFFECT(SOLID_REACTIVE_SIMPLE)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..d683b02510 100644
--- a/quantum/led_matrix_animations/solid_reactive_wide.h
+++ b/quantum/led_matrix/animations/solid_reactive_wide.h
@@ -1,11 +1,11 @@
#ifdef LED_MATRIX_KEYREACTIVE_ENABLED
# if !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
LED_MATRIX_EFFECT(SOLID_REACTIVE_WIDE)
# endif
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE
LED_MATRIX_EFFECT(SOLID_REACTIVE_MULTIWIDE)
# endif
@@ -17,14 +17,14 @@ static uint8_t SOLID_REACTIVE_WIDE_math(uint8_t val, int16_t dx, int16_t dy, uin
return qadd8(val, 255 - effect);
}
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
bool SOLID_REACTIVE_WIDE(effect_params_t* params) { return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_WIDE_math); }
# endif
-# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE
+# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE
bool SOLID_REACTIVE_MULTIWIDE(effect_params_t* params) { return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_WIDE_math); }
# 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..cf599c8fe6 100644
--- a/quantum/led_matrix_animations/solid_splash_anim.h
+++ b/quantum/led_matrix/animations/solid_splash_anim.h
@@ -1,11 +1,11 @@
#ifdef LED_MATRIX_KEYREACTIVE_ENABLED
# if !defined(DISABLE_LED_MATRIX_SOLID_SPLASH) || !defined(DISABLE_LED_MATRIX_SOLID_MULTISPLASH)
-# ifndef DISABLE_LED_MATRIX_SOLID_SPLASH
+# ifdef ENABLE_LED_MATRIX_SOLID_SPLASH
LED_MATRIX_EFFECT(SOLID_SPLASH)
# endif
-# ifndef DISABLE_LED_MATRIX_SOLID_MULTISPLASH
+# ifdef ENABLE_LED_MATRIX_SOLID_MULTISPLASH
LED_MATRIX_EFFECT(SOLID_MULTISPLASH)
# endif
@@ -17,14 +17,14 @@ uint8_t SOLID_SPLASH_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uin
return qadd8(val, 255 - effect);
}
-# ifndef DISABLE_LED_MATRIX_SOLID_SPLASH
+# ifdef ENABLE_LED_MATRIX_SOLID_SPLASH
bool SOLID_SPLASH(effect_params_t* params) { return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_SPLASH_math); }
# endif
-# ifndef DISABLE_LED_MATRIX_SOLID_MULTISPLASH
+# ifdef ENABLE_LED_MATRIX_SOLID_MULTISPLASH
bool SOLID_MULTISPLASH(effect_params_t* params) { return effect_runner_reactive_splash(0, params, &SOLID_SPLASH_math); }
# 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..c062cf968e 100644
--- a/quantum/led_matrix_animations/wave_left_right_anim.h
+++ b/quantum/led_matrix/animations/wave_left_right_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_LED_MATRIX_WAVE_LEFT_RIGHT
+#ifdef ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT
LED_MATRIX_EFFECT(WAVE_LEFT_RIGHT)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..5e612f6b6d 100644
--- a/quantum/led_matrix_animations/wave_up_down_anim.h
+++ b/quantum/led_matrix/animations/wave_up_down_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_LED_MATRIX_WAVE_UP_DOWN
+#ifdef ENABLE_LED_MATRIX_WAVE_UP_DOWN
LED_MATRIX_EFFECT(WAVE_UP_DOWN)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
@@ -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..50510e49aa 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,8 +459,9 @@ void led_matrix_init(void) {
void led_matrix_set_suspend_state(bool state) {
#ifdef LED_DISABLE_WHEN_USB_SUSPENDED
- if (state) {
- led_matrix_set_value_all(0); // turn off all LEDs when suspending
+ if (state && !suspend_state && is_keyboard_master()) { // only run if turning off, and only once
+ led_task_render(0); // turn off all LEDs when suspending
+ led_task_flush(0); // and actually flash led state to LEDs
}
suspend_state = state;
#endif
@@ -469,9 +472,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 +480,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 +490,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 +512,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 +539,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 +557,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..a7a1c983f7 100644
--- a/quantum/led_matrix.h
+++ b/quantum/led_matrix/led_matrix.h
@@ -23,6 +23,7 @@
#include <stdbool.h>
#include "led_matrix_types.h"
#include "quantum.h"
+#include "led_matrix_legacy_enables.h"
#ifdef IS31FL3731
# include "is31fl3731-simple.h"
@@ -56,7 +57,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/led_matrix_legacy_enables.h b/quantum/led_matrix/led_matrix_legacy_enables.h
new file mode 100644
index 0000000000..7738d2f34e
--- /dev/null
+++ b/quantum/led_matrix/led_matrix_legacy_enables.h
@@ -0,0 +1,82 @@
+/* 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/>.
+ */
+
+// to-do: remove this
+
+#pragma once
+
+#ifndef DISABLE_LED_MATRIX_ALPHAS_MODS
+# define ENABLE_LED_MATRIX_ALPHAS_MODS
+#endif
+#ifndef DISABLE_LED_MATRIX_BREATHING
+# define ENABLE_LED_MATRIX_BREATHING
+#endif
+#ifndef DISABLE_LED_MATRIX_BAND
+# define ENABLE_LED_MATRIX_BAND
+#endif
+#ifndef DISABLE_LED_MATRIX_BAND_PINWHEEL
+# define ENABLE_LED_MATRIX_BAND_PINWHEEL
+#endif
+#ifndef DISABLE_LED_MATRIX_BAND_SPIRAL
+# define ENABLE_LED_MATRIX_BAND_SPIRAL
+#endif
+#ifndef DISABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
+# define ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
+#endif
+#ifndef DISABLE_LED_MATRIX_CYCLE_UP_DOWN
+# define ENABLE_LED_MATRIX_CYCLE_UP_DOWN
+#endif
+#ifndef DISABLE_LED_MATRIX_CYCLE_OUT_IN
+# define ENABLE_LED_MATRIX_CYCLE_OUT_IN
+#endif
+#ifndef DISABLE_LED_MATRIX_DUAL_BEACON
+# define ENABLE_LED_MATRIX_DUAL_BEACON
+#endif
+#if defined(LED_MATRIX_KEYREACTIVE_ENABLED)
+# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
+# define ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
+# endif
+# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
+# define ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
+# endif
+# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE
+# define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE
+# endif
+# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
+# define ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
+# endif
+# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS
+# define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS
+# endif
+# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS
+# define ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS
+# endif
+# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS
+# define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS
+# endif
+# ifndef DISABLE_LED_MATRIX_SPLASH
+# define ENABLE_LED_MATRIX_SPLASH
+# endif
+# ifndef DISABLE_LED_MATRIX_MULTISPLASH
+# define ENABLE_LED_MATRIX_MULTISPLASH
+# endif
+#endif
+#ifndef DISABLE_LED_MATRIX_WAVE_LEFT_RIGHT
+# define ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT
+#endif
+#ifndef DISABLE_LED_MATRIX_WAVE_UP_DOWN
+# define ENABLE_LED_MATRIX_WAVE_UP_DOWN
+#endif
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/logging/debug.c b/quantum/logging/debug.c
new file mode 100644
index 0000000000..ea62deaa8c
--- /dev/null
+++ b/quantum/logging/debug.c
@@ -0,0 +1,25 @@
+/*
+Copyright 2011 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 "debug.h"
+
+debug_config_t debug_config = {
+ .enable = false, //
+ .matrix = false, //
+ .keyboard = false, //
+ .mouse = false, //
+ .reserved = 0 //
+};
diff --git a/quantum/logging/debug.h b/quantum/logging/debug.h
new file mode 100644
index 0000000000..8415310356
--- /dev/null
+++ b/quantum/logging/debug.h
@@ -0,0 +1,169 @@
+/*
+Copyright 2011 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/>.
+*/
+
+#pragma once
+
+#include <stdbool.h>
+#include "print.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Debug output control
+ */
+typedef union {
+ struct {
+ bool enable : 1;
+ bool matrix : 1;
+ bool keyboard : 1;
+ bool mouse : 1;
+ uint8_t reserved : 4;
+ };
+ uint8_t raw;
+} debug_config_t;
+
+extern debug_config_t debug_config;
+
+#ifdef __cplusplus
+}
+#endif
+
+/* for backward compatibility */
+#define debug_enable (debug_config.enable)
+#define debug_matrix (debug_config.matrix)
+#define debug_keyboard (debug_config.keyboard)
+#define debug_mouse (debug_config.mouse)
+
+/*
+ * Debug print utils
+ */
+#ifndef NO_DEBUG
+
+# define dprint(s) \
+ do { \
+ if (debug_enable) print(s); \
+ } while (0)
+# define dprintln(s) \
+ do { \
+ if (debug_enable) println(s); \
+ } while (0)
+# define dprintf(fmt, ...) \
+ do { \
+ if (debug_enable) xprintf(fmt, ##__VA_ARGS__); \
+ } while (0)
+# define dmsg(s) dprintf("%s at %d: %s\n", __FILE__, __LINE__, s)
+
+/* Deprecated. DO NOT USE these anymore, use dprintf instead. */
+# define debug(s) \
+ do { \
+ if (debug_enable) print(s); \
+ } while (0)
+# define debugln(s) \
+ do { \
+ if (debug_enable) println(s); \
+ } while (0)
+# define debug_msg(s) \
+ do { \
+ if (debug_enable) { \
+ print(__FILE__); \
+ print(" at "); \
+ print_dec(__LINE__); \
+ print(" in "); \
+ print(": "); \
+ print(s); \
+ } \
+ } while (0)
+# define debug_dec(data) \
+ do { \
+ if (debug_enable) print_dec(data); \
+ } while (0)
+# define debug_decs(data) \
+ do { \
+ if (debug_enable) print_decs(data); \
+ } while (0)
+# define debug_hex4(data) \
+ do { \
+ if (debug_enable) print_hex4(data); \
+ } while (0)
+# define debug_hex8(data) \
+ do { \
+ if (debug_enable) print_hex8(data); \
+ } while (0)
+# define debug_hex16(data) \
+ do { \
+ if (debug_enable) print_hex16(data); \
+ } while (0)
+# define debug_hex32(data) \
+ do { \
+ if (debug_enable) print_hex32(data); \
+ } while (0)
+# define debug_bin8(data) \
+ do { \
+ if (debug_enable) print_bin8(data); \
+ } while (0)
+# define debug_bin16(data) \
+ do { \
+ if (debug_enable) print_bin16(data); \
+ } while (0)
+# define debug_bin32(data) \
+ do { \
+ if (debug_enable) print_bin32(data); \
+ } while (0)
+# define debug_bin_reverse8(data) \
+ do { \
+ if (debug_enable) print_bin_reverse8(data); \
+ } while (0)
+# define debug_bin_reverse16(data) \
+ do { \
+ if (debug_enable) print_bin_reverse16(data); \
+ } while (0)
+# define debug_bin_reverse32(data) \
+ do { \
+ if (debug_enable) print_bin_reverse32(data); \
+ } while (0)
+# define debug_hex(data) debug_hex8(data)
+# define debug_bin(data) debug_bin8(data)
+# define debug_bin_reverse(data) debug_bin8(data)
+
+#else /* NO_DEBUG */
+
+# define dprint(s)
+# define dprintln(s)
+# define dprintf(fmt, ...)
+# define dmsg(s)
+# define debug(s)
+# define debugln(s)
+# define debug_msg(s)
+# define debug_dec(data)
+# define debug_decs(data)
+# define debug_hex4(data)
+# define debug_hex8(data)
+# define debug_hex16(data)
+# define debug_hex32(data)
+# define debug_bin8(data)
+# define debug_bin16(data)
+# define debug_bin32(data)
+# define debug_bin_reverse8(data)
+# define debug_bin_reverse16(data)
+# define debug_bin_reverse32(data)
+# define debug_hex(data)
+# define debug_bin(data)
+# define debug_bin_reverse(data)
+
+#endif /* NO_DEBUG */
diff --git a/quantum/logging/nodebug.h b/quantum/logging/nodebug.h
new file mode 100644
index 0000000000..0b176684bd
--- /dev/null
+++ b/quantum/logging/nodebug.h
@@ -0,0 +1,26 @@
+/*
+Copyright 2013 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/>.
+*/
+
+#pragma once
+
+#ifndef NO_DEBUG
+# define NO_DEBUG
+# include "debug.h"
+# undef NO_DEBUG
+#else
+# include "debug.h"
+#endif
diff --git a/quantum/logging/print.c b/quantum/logging/print.c
new file mode 100644
index 0000000000..e8440e55ee
--- /dev/null
+++ b/quantum/logging/print.c
@@ -0,0 +1,27 @@
+/*
+Copyright 2011 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 <stddef.h>
+#include "sendchar.h"
+
+// bind lib/printf to console interface - sendchar
+
+static int8_t null_sendchar_func(uint8_t c) { return 0; }
+static sendchar_func_t func = null_sendchar_func;
+
+void print_set_sendchar(sendchar_func_t send) { func = send; }
+
+void _putchar(char character) { func(character); }
diff --git a/quantum/logging/print.h b/quantum/logging/print.h
new file mode 100644
index 0000000000..8c055f549e
--- /dev/null
+++ b/quantum/logging/print.h
@@ -0,0 +1,135 @@
+/* Copyright 2012 Jun Wako <wakojun@gmail.com> */
+/* Very basic print functions, intended to be used with usb_debug_only.c
+ * http://www.pjrc.com/teensy/
+ * Copyright (c) 2008 PJRC.COM, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "util.h"
+#include "sendchar.h"
+#include "progmem.h"
+
+void print_set_sendchar(sendchar_func_t func);
+
+#ifndef NO_PRINT
+# if __has_include_next("_print.h")
+# include_next "_print.h" /* Include the platforms print.h */
+# else
+// Fall back to lib/printf
+# include "printf.h" // lib/printf/printf.h
+
+// Create user & normal print defines
+# define print(s) printf(s)
+# define println(s) printf(s "\r\n")
+# define xprintf printf
+# define uprint(s) printf(s)
+# define uprintln(s) printf(s "\r\n")
+# define uprintf printf
+
+# endif /* __has_include_next("_print.h") */
+#else /* NO_PRINT */
+# undef xprintf
+// Remove print defines
+# define print(s)
+# define println(s)
+# define xprintf(fmt, ...)
+# define uprintf(fmt, ...)
+# define uprint(s)
+# define uprintln(s)
+
+#endif /* NO_PRINT */
+
+#ifdef USER_PRINT
+// Remove normal print defines
+# undef print
+# undef println
+# undef xprintf
+# define print(s)
+# define println(s)
+# define xprintf(fmt, ...)
+#endif
+
+#define print_dec(i) xprintf("%u", i)
+#define print_decs(i) xprintf("%d", i)
+/* hex */
+#define print_hex4(i) xprintf("%X", i)
+#define print_hex8(i) xprintf("%02X", i)
+#define print_hex16(i) xprintf("%04X", i)
+#define print_hex32(i) xprintf("%08lX", i)
+/* binary */
+#define print_bin4(i) xprintf("%04b", i)
+#define print_bin8(i) xprintf("%08b", i)
+#define print_bin16(i) xprintf("%016b", i)
+#define print_bin32(i) xprintf("%032lb", i)
+#define print_bin_reverse8(i) xprintf("%08b", bitrev(i))
+#define print_bin_reverse16(i) xprintf("%016b", bitrev16(i))
+#define print_bin_reverse32(i) xprintf("%032lb", bitrev32(i))
+/* print value utility */
+#define print_val_dec(v) xprintf(#v ": %u\n", v)
+#define print_val_decs(v) xprintf(#v ": %d\n", v)
+#define print_val_hex8(v) xprintf(#v ": %X\n", v)
+#define print_val_hex16(v) xprintf(#v ": %02X\n", v)
+#define print_val_hex32(v) xprintf(#v ": %04lX\n", v)
+#define print_val_bin8(v) xprintf(#v ": %08b\n", v)
+#define print_val_bin16(v) xprintf(#v ": %016b\n", v)
+#define print_val_bin32(v) xprintf(#v ": %032lb\n", v)
+#define print_val_bin_reverse8(v) xprintf(#v ": %08b\n", bitrev(v))
+#define print_val_bin_reverse16(v) xprintf(#v ": %016b\n", bitrev16(v))
+#define print_val_bin_reverse32(v) xprintf(#v ": %032lb\n", bitrev32(v))
+
+// User print disables the normal print messages in the body of QMK/TMK code and
+// is meant as a lightweight alternative to NOPRINT. Use it when you only want to do
+// a spot of debugging but lack flash resources for allowing all of the codebase to
+// print (and store their wasteful strings).
+//
+// !!! DO NOT USE USER PRINT CALLS IN THE BODY OF QMK/TMK !!!
+
+/* decimal */
+#define uprint_dec(i) uprintf("%u", i)
+#define uprint_decs(i) uprintf("%d", i)
+/* hex */
+#define uprint_hex4(i) uprintf("%X", i)
+#define uprint_hex8(i) uprintf("%02X", i)
+#define uprint_hex16(i) uprintf("%04X", i)
+#define uprint_hex32(i) uprintf("%08lX", i)
+/* binary */
+#define uprint_bin4(i) uprintf("%04b", i)
+#define uprint_bin8(i) uprintf("%08b", i)
+#define uprint_bin16(i) uprintf("%016b", i)
+#define uprint_bin32(i) uprintf("%032lb", i)
+#define uprint_bin_reverse8(i) uprintf("%08b", bitrev(i))
+#define uprint_bin_reverse16(i) uprintf("%016b", bitrev16(i))
+#define uprint_bin_reverse32(i) uprintf("%032lb", bitrev32(i))
+/* print value utility */
+#define uprint_val_dec(v) uprintf(#v ": %u\n", v)
+#define uprint_val_decs(v) uprintf(#v ": %d\n", v)
+#define uprint_val_hex8(v) uprintf(#v ": %X\n", v)
+#define uprint_val_hex16(v) uprintf(#v ": %02X\n", v)
+#define uprint_val_hex32(v) uprintf(#v ": %04lX\n", v)
+#define uprint_val_bin8(v) uprintf(#v ": %08b\n", v)
+#define uprint_val_bin16(v) uprintf(#v ": %016b\n", v)
+#define uprint_val_bin32(v) uprintf(#v ": %032lb\n", v)
+#define uprint_val_bin_reverse8(v) uprintf(#v ": %08b\n", bitrev(v))
+#define uprint_val_bin_reverse16(v) uprintf(#v ": %016b\n", bitrev16(v))
+#define uprint_val_bin_reverse32(v) uprintf(#v ": %032lb\n", bitrev32(v))
diff --git a/quantum/logging/print.mk b/quantum/logging/print.mk
new file mode 100644
index 0000000000..67c004192d
--- /dev/null
+++ b/quantum/logging/print.mk
@@ -0,0 +1,9 @@
+PRINTF_PATH = $(LIB_PATH)/printf
+
+VPATH += $(PRINTF_PATH)
+SRC += $(PRINTF_PATH)/printf.c
+QUANTUM_SRC +=$(QUANTUM_DIR)/logging/print.c
+OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_FLOAT
+OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_EXPONENTIAL
+OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_LONG_LONG
+OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_PTRDIFF_T
diff --git a/quantum/logging/sendchar.c b/quantum/logging/sendchar.c
new file mode 100644
index 0000000000..9422382f6f
--- /dev/null
+++ b/quantum/logging/sendchar.c
@@ -0,0 +1,20 @@
+/*
+Copyright 2011 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 "sendchar.h"
+
+/* default noop "null" implementation */
+__attribute__((weak)) int8_t sendchar(uint8_t c) { return 0; }
diff --git a/quantum/logging/sendchar.h b/quantum/logging/sendchar.h
new file mode 100644
index 0000000000..edcddaa6bb
--- /dev/null
+++ b/quantum/logging/sendchar.h
@@ -0,0 +1,33 @@
+/*
+Copyright 2011 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/>.
+*/
+
+#pragma once
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int8_t (*sendchar_func_t)(uint8_t c);
+
+/* transmit a character. return 0 on success, -1 on error. */
+int8_t sendchar(uint8_t c);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/quantum/main.c b/quantum/main.c
new file mode 100644
index 0000000000..2cbcd73d8f
--- /dev/null
+++ b/quantum/main.c
@@ -0,0 +1,41 @@
+/* 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 3 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 "keyboard.h"
+
+void platform_setup(void);
+
+void protocol_setup(void);
+void protocol_init(void);
+void protocol_task(void);
+
+/** \brief Main
+ *
+ * FIXME: Needs doc
+ */
+int main(void) __attribute__((weak));
+int main(void) {
+ platform_setup();
+ protocol_setup();
+
+ protocol_init();
+
+ /* Main loop */
+ while (true) {
+ protocol_task();
+ housekeeping_task();
+ }
+}
diff --git a/quantum/matrix.c b/quantum/matrix.c
index 34d6af2e6d..33586c431b 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -16,22 +16,65 @@ 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"
+
+# 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 +86,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 +109,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 +120,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 +178,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 +293,78 @@ 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
+// Fallback implementation for keyboards not using the standard split_util.c
+__attribute__((weak)) bool transport_master_if_connected(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ transport_master(master_matrix, slave_matrix);
+ return true; // Treat the transport as always connected
+}
+
+bool matrix_post_scan(void) {
bool changed = false;
+ if (is_keyboard_master()) {
+ matrix_row_t slave_matrix[ROWS_PER_HAND] = {0};
+ if (transport_master_if_connected(matrix + thisHand, slave_matrix)) {
+ for (int i = 0; i < ROWS_PER_HAND; ++i) {
+ if (matrix[thatHand + i] != slave_matrix[i]) {
+ matrix[thatHand + i] = slave_matrix[i];
+ changed = true;
+ }
+ }
+ } else {
+ // reset other half if disconnected
+ for (int i = 0; i < ROWS_PER_HAND; ++i) {
+ matrix[thatHand + i] = 0;
+ slave_matrix[i] = 0;
+ }
+
+ 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..f7eaeec8ac 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
@@ -268,7 +252,12 @@ ifneq ($(findstring STM32F401, $(MCU)),)
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
- MCU_LDSCRIPT ?= STM32F401xC
+ ifeq ($(strip $(BOOTLOADER)), tinyuf2)
+ MCU_LDSCRIPT ?= STM32F401xC_tinyuf2
+ FIRMWARE_FORMAT ?= uf2
+ else
+ MCU_LDSCRIPT ?= STM32F401xC
+ endif
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
@@ -280,9 +269,37 @@ 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
+
+ifneq ($(findstring STM32F407, $(MCU)),)
+ # 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 = STM32F4xx
+
+ # Linker script to use
+ # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
+ # or <keyboard_dir>/ld/
+ MCU_LDSCRIPT ?= STM32F407xE
+
+ # Startup code to use
+ # - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
+ MCU_STARTUP ?= stm32f4xx
+
+ # Board: it should exist either in <chibios>/os/hal/boards/,
+ # <keyboard_dir>/boards/, or drivers/boards/
+ BOARD ?= GENERIC_STM32_F407XE
+
+ USE_FPU ?= yes
# UF2 settings
UF2_FAMILY ?= STM32F4
@@ -321,10 +338,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 +370,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 +400,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 +432,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 +466,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..e8661839c7 100644
--- a/quantum/process_keycode/process_combo.c
+++ b/quantum/process_keycode/process_combo.c
@@ -16,114 +16,449 @@
#include "print.h"
#include "process_combo.h"
+#include "action_tapping.h"
-#ifndef COMBO_VARIABLE_LEN
-__attribute__((weak)) combo_t key_combos[COMBO_COUNT] = {};
+
+#ifdef COMBO_COUNT
+__attribute__((weak)) combo_t key_combos[COMBO_COUNT];
+uint16_t COMBO_LEN = COMBO_COUNT;
#else
extern combo_t key_combos[];
-extern int COMBO_LEN;
+extern uint16_t COMBO_LEN;
#endif
__attribute__((weak)) void process_combo_event(uint16_t combo_index, bool pressed) {}
-static uint16_t timer = 0;
-static uint16_t current_combo_index = 0;
-static bool drop_buffer = false;
-static bool is_active = false;
-static bool b_combo_enable = true; // defaults to enabled
+#ifdef COMBO_MUST_HOLD_PER_COMBO
+__attribute__((weak)) bool get_combo_must_hold(uint16_t index, combo_t *combo) { return false; }
+#endif
+
+#ifdef COMBO_MUST_TAP_PER_COMBO
+__attribute__((weak)) bool get_combo_must_tap(uint16_t index, combo_t *combo) { return false; }
+#endif
+
+#ifdef COMBO_TERM_PER_COMBO
+__attribute__((weak)) uint16_t get_combo_term(uint16_t index, combo_t *combo) { return COMBO_TERM; }
+#endif
+
+#ifdef COMBO_PROCESS_KEY_RELEASE
+__attribute__((weak)) bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) { return false; }
+#endif
-static uint8_t buffer_size = 0;
-#ifdef COMBO_ALLOW_ACTION_KEYS
-static keyrecord_t key_buffer[MAX_COMBO_LENGTH];
+#ifndef COMBO_NO_TIMER
+static uint16_t timer = 0;
+#endif
+static bool b_combo_enable = true; // defaults to enabled
+static uint16_t longest_term = 0;
+
+typedef struct {
+ keyrecord_t record;
+ uint16_t combo_index;
+ uint16_t keycode;
+} queued_record_t;
+static uint8_t key_buffer_size = 0;
+static queued_record_t key_buffer[COMBO_KEY_BUFFER_LENGTH];
+
+typedef struct {
+ uint16_t combo_index;
+} queued_combo_t;
+static uint8_t combo_buffer_write= 0;
+static uint8_t combo_buffer_read = 0;
+static queued_combo_t combo_buffer[COMBO_BUFFER_LENGTH];
+
+#define INCREMENT_MOD(i) i = (i + 1) % COMBO_BUFFER_LENGTH
+
+#define COMBO_KEY_POS ((keypos_t){.col=254, .row=254})
+
+
+#ifndef EXTRA_SHORT_COMBOS
+/* flags are their own elements in combo_t struct. */
+# define COMBO_ACTIVE(combo) (combo->active)
+# define COMBO_DISABLED(combo) (combo->disabled)
+# define COMBO_STATE(combo) (combo->state)
+
+# define ACTIVATE_COMBO(combo) do {combo->active = true;}while(0)
+# define DEACTIVATE_COMBO(combo) do {combo->active = false;}while(0)
+# define DISABLE_COMBO(combo) do {combo->disabled = true;}while(0)
+# define RESET_COMBO_STATE(combo) do { \
+ combo->disabled = false; \
+ combo->state = 0; \
+}while(0)
#else
-static uint16_t key_buffer[MAX_COMBO_LENGTH];
+/* flags are at the two high bits of state. */
+# define COMBO_ACTIVE(combo) (combo->state & 0x80)
+# define COMBO_DISABLED(combo) (combo->state & 0x40)
+# define COMBO_STATE(combo) (combo->state & 0x3F)
+
+# define ACTIVATE_COMBO(combo) do {combo->state |= 0x80;}while(0)
+# define DEACTIVATE_COMBO(combo) do {combo->state &= ~0x80;}while(0)
+# define DISABLE_COMBO(combo) do {combo->state |= 0x40;}while(0)
+# define RESET_COMBO_STATE(combo) do {combo->state &= ~0x7F;}while(0)
#endif
-static inline void send_combo(uint16_t action, bool pressed) {
- if (action) {
- if (pressed) {
- register_code16(action);
- } else {
- unregister_code16(action);
- }
+static inline void release_combo(uint16_t combo_index, combo_t *combo) {
+ if (combo->keycode) {
+ keyrecord_t record = {
+ .event = {
+ .key = COMBO_KEY_POS,
+ .time = timer_read()|1,
+ .pressed = false,
+ },
+ .keycode = combo->keycode,
+ };
+#ifndef NO_ACTION_TAPPING
+ action_tapping_process(record);
+#else
+ process_record(&record);
+#endif
} else {
- process_combo_event(current_combo_index, pressed);
+ process_combo_event(combo_index, false);
}
+ DEACTIVATE_COMBO(combo);
+}
+
+static inline bool _get_combo_must_hold(uint16_t combo_index, combo_t *combo) {
+#ifdef COMBO_NO_TIMER
+ return false;
+#elif defined(COMBO_MUST_HOLD_PER_COMBO)
+ return get_combo_must_hold(combo_index, combo);
+#elif defined(COMBO_MUST_HOLD_MODS)
+ return (KEYCODE_IS_MOD(combo->keycode) ||
+ (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX));
+#endif
+ return false;
+}
+
+static inline uint16_t _get_wait_time(uint16_t combo_index, combo_t *combo ) {
+ if (_get_combo_must_hold(combo_index, combo)
+#ifdef COMBO_MUST_TAP_PER_COMBO
+ || get_combo_must_tap(combo_index, combo)
+#endif
+ ) {
+ if (longest_term < COMBO_HOLD_TERM) {
+ return COMBO_HOLD_TERM;
+ }
+ }
+
+ return longest_term;
+}
+
+static inline uint16_t _get_combo_term(uint16_t combo_index, combo_t *combo) {
+
+#if defined(COMBO_TERM_PER_COMBO)
+ return get_combo_term(combo_index, combo);
+#endif
+
+ return COMBO_TERM;
}
-static inline void dump_key_buffer(bool emit) {
- if (buffer_size == 0) {
+void clear_combos(void) {
+ uint16_t index = 0;
+ longest_term = 0;
+ for (index = 0; index < COMBO_LEN; ++index) {
+ combo_t *combo = &key_combos[index];
+ if (!COMBO_ACTIVE(combo)) {
+ RESET_COMBO_STATE(combo);
+ }
+ }
+}
+
+static inline void dump_key_buffer(void) {
+ /* First call start from 0 index; recursive calls need to start from i+1 index */
+ static uint8_t key_buffer_next = 0;
+
+ if (key_buffer_size == 0) {
return;
}
- if (emit) {
- for (uint8_t i = 0; i < buffer_size; i++) {
-#ifdef COMBO_ALLOW_ACTION_KEYS
- const action_t action = store_or_get_action(key_buffer[i].event.pressed, key_buffer[i].event.key);
- process_action(&(key_buffer[i]), action);
+ for (uint8_t key_buffer_i = key_buffer_next; key_buffer_i < key_buffer_size; key_buffer_i++) {
+ key_buffer_next = key_buffer_i + 1;
+
+ queued_record_t *qrecord = &key_buffer[key_buffer_i];
+ keyrecord_t *record = &qrecord->record;
+
+ if (IS_NOEVENT(record->event)) {
+ continue;
+ }
+
+ if (!record->keycode && qrecord->combo_index != (uint16_t)-1) {
+ process_combo_event(qrecord->combo_index, true);
+ } else {
+#ifndef NO_ACTION_TAPPING
+ action_tapping_process(*record);
#else
- register_code16(key_buffer[i]);
- send_keyboard_report();
+ process_record(record);
#endif
}
+ record->event.time = 0;
}
- buffer_size = 0;
+ key_buffer_next = key_buffer_size = 0;
}
-#define ALL_COMBO_KEYS_ARE_DOWN (((1 << count) - 1) == combo->state)
-#define KEY_STATE_DOWN(key) \
- do { \
- combo->state |= (1 << key); \
+#define NO_COMBO_KEYS_ARE_DOWN (0 == COMBO_STATE(combo))
+#define ALL_COMBO_KEYS_ARE_DOWN(state, key_count) (((1 << key_count) - 1) == state)
+#define ONLY_ONE_KEY_IS_DOWN(state) !(state & (state - 1))
+#define KEY_NOT_YET_RELEASED(state, key_index) ((1 << key_index) & state)
+#define KEY_STATE_DOWN(state, key_index) \
+ do { \
+ state |= (1 << key_index); \
} while (0)
-#define KEY_STATE_UP(key) \
- do { \
- combo->state &= ~(1 << key); \
+#define KEY_STATE_UP(state, key_index) \
+ do { \
+ state &= ~(1 << key_index); \
} while (0)
-static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record) {
- uint8_t count = 0;
- uint16_t index = -1;
- /* Find index of keycode and number of combo keys */
- for (const uint16_t *keys = combo->keys;; ++count) {
- uint16_t key = pgm_read_word(&keys[count]);
- if (keycode == key) index = count;
+static inline void _find_key_index_and_count(const uint16_t *keys, uint16_t keycode, uint16_t *key_index, uint8_t *key_count) {
+ while (true) {
+ uint16_t key = pgm_read_word(&keys[*key_count]);
+ if (keycode == key) *key_index = *key_count;
if (COMBO_END == key) break;
+ (*key_count)++;
+ }
+}
+
+void drop_combo_from_buffer(uint16_t combo_index) {
+ /* Mark a combo as processed from the buffer. If the buffer is in the
+ * beginning of the buffer, drop it. */
+ uint8_t i = combo_buffer_read;
+ while (i != combo_buffer_write) {
+ queued_combo_t *qcombo = &combo_buffer[i];
+
+ if (qcombo->combo_index == combo_index) {
+ combo_t *combo = &key_combos[combo_index];
+ DISABLE_COMBO(combo);
+
+ if (i == combo_buffer_read) {
+ INCREMENT_MOD(combo_buffer_read);
+ }
+ break;
+ }
+ INCREMENT_MOD(i);
}
+}
+
+void apply_combo(uint16_t combo_index, combo_t *combo) {
+ /* Apply combo's result keycode to the last chord key of the combo and
+ * disable the other keys. */
- /* Continue processing if not a combo key */
- if (-1 == (int8_t)index) return false;
+ if (COMBO_DISABLED(combo)) { return; }
- bool is_combo_active = is_active;
+ // state to check against so we find the last key of the combo from the buffer
+#if defined(EXTRA_EXTRA_LONG_COMBOS)
+ uint32_t state = 0;
+#elif defined(EXTRA_LONG_COMBOS)
+ uint16_t state = 0;
+#else
+ uint8_t state = 0;
+#endif
+
+ for (uint8_t key_buffer_i = 0; key_buffer_i < key_buffer_size; key_buffer_i++) {
+
+ queued_record_t *qrecord = &key_buffer[key_buffer_i];
+ keyrecord_t *record = &qrecord->record;
+ uint16_t keycode = qrecord->keycode;
+
+ uint8_t key_count = 0;
+ uint16_t key_index = -1;
+ _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);
+
+ if (-1 == (int16_t)key_index) {
+ // key not part of this combo
+ continue;
+ }
- if (record->event.pressed) {
- KEY_STATE_DOWN(index);
+ KEY_STATE_DOWN(state, key_index);
+ if (ALL_COMBO_KEYS_ARE_DOWN(state, key_count)) {
+ // this in the end executes the combo when the key_buffer is dumped.
+ record->keycode = combo->keycode;
+ record->event.key = COMBO_KEY_POS;
- if (is_combo_active) {
- if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was pressed */
- send_combo(combo->keycode, true);
- drop_buffer = true;
+ qrecord->combo_index = combo_index;
+ ACTIVATE_COMBO(combo);
+
+ break;
+ } else {
+ // key was part of the combo but not the last one, "disable" it
+ // by making it a TICK event.
+ record->event.time = 0;
+ }
+
+ }
+ drop_combo_from_buffer(combo_index);
+}
+
+static inline void apply_combos(void) {
+ // Apply all buffered normal combos.
+ for (uint8_t i = combo_buffer_read;
+ i != combo_buffer_write;
+ INCREMENT_MOD(i)) {
+
+ queued_combo_t *buffered_combo = &combo_buffer[i];
+ combo_t *combo = &key_combos[buffered_combo->combo_index];
+
+#ifdef COMBO_MUST_TAP_PER_COMBO
+ if (get_combo_must_tap(buffered_combo->combo_index, combo)) {
+ // Tap-only combos are applied on key release only, so let's drop 'em here.
+ drop_combo_from_buffer(buffered_combo->combo_index);
+ continue;
+ }
+#endif
+ apply_combo(buffered_combo->combo_index, combo);
+ }
+ dump_key_buffer();
+ clear_combos();
+}
+
+combo_t* overlaps(combo_t *combo1, combo_t *combo2) {
+ /* Checks if the combos overlap and returns the combo that should be
+ * dropped from the combo buffer.
+ * The combo that has less keys will be dropped. If they have the same
+ * amount of keys, drop combo1. */
+
+ uint8_t idx1 = 0, idx2 = 0;
+ uint16_t key1, key2;
+ bool overlaps = false;
+
+ while ((key1 = pgm_read_word(&combo1->keys[idx1])) != COMBO_END) {
+ idx2 = 0;
+ while ((key2 = pgm_read_word(&combo2->keys[idx2])) != COMBO_END) {
+ if (key1 == key2) overlaps = true;
+ idx2 += 1;
+ }
+ idx1 += 1;
+ }
+
+ if (!overlaps) return NULL;
+ if (idx2 < idx1) return combo2;
+ return combo1;
+}
+
+static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record, uint16_t combo_index) {
+ uint8_t key_count = 0;
+ uint16_t key_index = -1;
+ _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);
+
+ /* Continue processing if key isn't part of current combo. */
+ if (-1 == (int16_t)key_index) {
+ return false;
+ }
+
+ bool key_is_part_of_combo = !COMBO_DISABLED(combo) && is_combo_enabled();
+
+ if (record->event.pressed && key_is_part_of_combo) {
+ uint16_t time = _get_combo_term(combo_index, combo);
+ if (!COMBO_ACTIVE(combo)) {
+ KEY_STATE_DOWN(combo->state, key_index);
+ if (longest_term < time) {
+ longest_term = time;
}
}
+ if (ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) {
+ /* Combo was fully pressed */
+ /* Buffer the combo so we can fire it after COMBO_TERM */
+
+#ifndef COMBO_NO_TIMER
+ /* Don't buffer this combo if its combo term has passed. */
+ if (timer && timer_elapsed(timer) > time) {
+ DISABLE_COMBO(combo);
+ return true;
+ } else
+#endif
+ {
+
+ // disable readied combos that overlap with this combo
+ combo_t *drop = NULL;
+ for (uint8_t combo_buffer_i = combo_buffer_read;
+ combo_buffer_i != combo_buffer_write;
+ INCREMENT_MOD(combo_buffer_i)) {
+
+ queued_combo_t *qcombo = &combo_buffer[combo_buffer_i];
+ combo_t *buffered_combo = &key_combos[qcombo->combo_index];
+
+ if ((drop = overlaps(buffered_combo, combo))) {
+ DISABLE_COMBO(drop);
+ if (drop == combo) {
+ // stop checking for overlaps if dropped combo was current combo.
+ break;
+ } else if (combo_buffer_i == combo_buffer_read && drop == buffered_combo) {
+ /* Drop the disabled buffered combo from the buffer if
+ * it is in the beginning of the buffer. */
+ INCREMENT_MOD(combo_buffer_read);
+ }
+ }
+
+ }
+
+ if (drop != combo) {
+ // save this combo to buffer
+ combo_buffer[combo_buffer_write] = (queued_combo_t){
+ .combo_index=combo_index,
+ };
+ INCREMENT_MOD(combo_buffer_write);
+
+ // get possible longer waiting time for tap-/hold-only combos.
+ longest_term = _get_wait_time(combo_index, combo);
+ }
+ } // if timer elapsed end
+
+ }
} else {
- if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */
- send_combo(combo->keycode, false);
+ // chord releases
+ if (!COMBO_ACTIVE(combo) && ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) {
+ /* First key quickly released */
+ if (COMBO_DISABLED(combo) || _get_combo_must_hold(combo_index, combo)) {
+ // combo wasn't tappable, disable it and drop it from buffer.
+ drop_combo_from_buffer(combo_index);
+ key_is_part_of_combo = false;
+ }
+#ifdef COMBO_MUST_TAP_PER_COMBO
+ else if (get_combo_must_tap(combo_index, combo)) {
+ // immediately apply tap-only combo
+ apply_combo(combo_index, combo);
+ apply_combos(); // also apply other prepared combos and dump key buffer
+# ifdef COMBO_PROCESS_KEY_RELEASE
+ if (process_combo_key_release(combo_index, combo, key_index, keycode)) {
+ release_combo(combo_index, combo);
+ }
+# endif
+ }
+#endif
+ } else if (COMBO_ACTIVE(combo)
+ && ONLY_ONE_KEY_IS_DOWN(COMBO_STATE(combo))
+ && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)
+ ) {
+ /* last key released */
+ release_combo(combo_index, combo);
+ key_is_part_of_combo = true;
+
+#ifdef COMBO_PROCESS_KEY_RELEASE
+ process_combo_key_release(combo_index, combo, key_index, keycode);
+#endif
+ } else if (COMBO_ACTIVE(combo)
+ && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)
+ ) {
+ /* first or middle key released */
+ key_is_part_of_combo = true;
+
+#ifdef COMBO_PROCESS_KEY_RELEASE
+ if (process_combo_key_release(combo_index, combo, key_index, keycode)) {
+ release_combo(combo_index, combo);
+ }
+#endif
} else {
- /* continue processing without immediately returning */
- is_combo_active = false;
+ /* The released key was part of an incomplete combo */
+ key_is_part_of_combo = false;
}
- KEY_STATE_UP(index);
+ KEY_STATE_UP(combo->state, key_index);
}
- return is_combo_active;
+ return key_is_part_of_combo;
}
-#define NO_COMBO_KEYS_ARE_DOWN (0 == combo->state)
-
bool process_combo(uint16_t keycode, keyrecord_t *record) {
bool is_combo_key = false;
- drop_buffer = false;
bool no_combo_keys_pressed = true;
if (keycode == CMB_ON && record->event.pressed) {
@@ -141,65 +476,82 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
return true;
}
- if (!is_combo_enabled()) {
- return true;
- }
-#ifndef COMBO_VARIABLE_LEN
- for (current_combo_index = 0; current_combo_index < COMBO_COUNT; ++current_combo_index) {
-#else
- for (current_combo_index = 0; current_combo_index < COMBO_LEN; ++current_combo_index) {
+#ifdef COMBO_ONLY_FROM_LAYER
+ /* Only check keycodes from one layer. */
+ keycode = keymap_key_to_keycode(COMBO_ONLY_FROM_LAYER, record->event.key);
#endif
- combo_t *combo = &key_combos[current_combo_index];
- is_combo_key |= process_single_combo(combo, keycode, record);
- no_combo_keys_pressed = no_combo_keys_pressed && NO_COMBO_KEYS_ARE_DOWN;
+
+ for (uint16_t idx = 0; idx < COMBO_LEN; ++idx) {
+ combo_t *combo = &key_combos[idx];
+ is_combo_key |= process_single_combo(combo, keycode, record, idx);
+ no_combo_keys_pressed = no_combo_keys_pressed && (NO_COMBO_KEYS_ARE_DOWN || COMBO_ACTIVE(combo) || COMBO_DISABLED(combo));
}
- if (drop_buffer) {
- /* buffer is only dropped when we complete a combo, so we refresh the timer
- * here */
- timer = timer_read();
- dump_key_buffer(false);
- } else if (!is_combo_key) {
- /* if no combos claim the key we need to emit the keybuffer */
- dump_key_buffer(true);
-
- // reset state if there are no combo keys pressed at all
- if (no_combo_keys_pressed) {
- timer = 0;
- is_active = true;
- }
- } else if (record->event.pressed && is_active) {
- /* otherwise the key is consumed and placed in the buffer */
+ if (record->event.pressed && is_combo_key) {
+#ifndef COMBO_NO_TIMER
+# ifdef COMBO_STRICT_TIMER
+ if (!timer) {
+ // timer is set only on the first key
+ timer = timer_read();
+ }
+# else
timer = timer_read();
+# endif
+#endif
- if (buffer_size < MAX_COMBO_LENGTH) {
-#ifdef COMBO_ALLOW_ACTION_KEYS
- key_buffer[buffer_size++] = *record;
-#else
- key_buffer[buffer_size++] = keycode;
+ if (key_buffer_size < COMBO_KEY_BUFFER_LENGTH) {
+ key_buffer[key_buffer_size++] = (queued_record_t){
+ .record = *record,
+ .keycode = keycode,
+ .combo_index = -1, // this will be set when applying combos
+ };
+ }
+ } else {
+ if (combo_buffer_read != combo_buffer_write) {
+ // some combo is prepared
+ apply_combos();
+ } else {
+ // reset state if there are no combo keys pressed at all
+ dump_key_buffer();
+#ifndef COMBO_NO_TIMER
+ timer = 0;
#endif
+ clear_combos();
}
}
-
return !is_combo_key;
}
-void matrix_scan_combo(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
- */
- is_active = false;
- dump_key_buffer(true);
+void combo_task(void) {
+ if (!b_combo_enable) {
+ return;
}
+
+#ifndef COMBO_NO_TIMER
+ if (timer && timer_elapsed(timer) > longest_term) {
+ if (combo_buffer_read != combo_buffer_write) {
+ apply_combos();
+ longest_term = 0;
+ timer = 0;
+ } else {
+ dump_key_buffer();
+ timer = 0;
+ clear_combos();
+ }
+ }
+#endif
}
void combo_enable(void) { b_combo_enable = true; }
void combo_disable(void) {
- b_combo_enable = is_active = false;
+#ifndef COMBO_NO_TIMER
timer = 0;
- dump_key_buffer(true);
+#endif
+ b_combo_enable = false;
+ combo_buffer_read = combo_buffer_write;
+ clear_combos();
+ dump_key_buffer();
}
void combo_toggle(void) {
diff --git a/quantum/process_keycode/process_combo.h b/quantum/process_keycode/process_combo.h
index e51a2f1f4e..43c36d79e6 100644
--- a/quantum/process_keycode/process_combo.h
+++ b/quantum/process_keycode/process_combo.h
@@ -20,23 +20,38 @@
#include "quantum.h"
#include <stdint.h>
-#ifdef EXTRA_EXTRA_LONG_COMBOS
+#ifdef EXTRA_SHORT_COMBOS
+# define MAX_COMBO_LENGTH 6
+#elif defined(EXTRA_EXTRA_LONG_COMBOS)
# define MAX_COMBO_LENGTH 32
-#elif EXTRA_LONG_COMBOS
+#elif defined(EXTRA_LONG_COMBOS)
# define MAX_COMBO_LENGTH 16
#else
# define MAX_COMBO_LENGTH 8
#endif
+#ifndef COMBO_KEY_BUFFER_LENGTH
+# define COMBO_KEY_BUFFER_LENGTH MAX_COMBO_LENGTH
+#endif
+#ifndef COMBO_BUFFER_LENGTH
+# define COMBO_BUFFER_LENGTH 4
+#endif
+
typedef struct {
const uint16_t *keys;
uint16_t keycode;
-#ifdef EXTRA_EXTRA_LONG_COMBOS
+#ifdef EXTRA_SHORT_COMBOS
+ uint8_t state;
+#else
+ bool disabled;
+ bool active;
+# if defined(EXTRA_EXTRA_LONG_COMBOS)
uint32_t state;
-#elif EXTRA_LONG_COMBOS
+# elif defined(EXTRA_LONG_COMBOS)
uint16_t state;
-#else
+# else
uint8_t state;
+# endif
#endif
} combo_t;
@@ -46,15 +61,18 @@ typedef struct {
{ .keys = &(ck)[0] }
#define COMBO_END 0
-#ifndef COMBO_COUNT
-# define COMBO_COUNT 0
-#endif
#ifndef COMBO_TERM
-# define COMBO_TERM TAPPING_TERM
+# define COMBO_TERM 50
#endif
+#ifndef COMBO_HOLD_TERM
+# define COMBO_HOLD_TERM TAPPING_TERM
+#endif
+
+/* check if keycode is only modifiers */
+#define KEYCODE_IS_MOD(code) (IS_MOD(code) || (code >= QK_MODS && code <= QK_MODS_MAX && !(code & QK_BASIC_MAX)))
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..09b2725079
--- /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.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_magic.c b/quantum/process_keycode/process_magic.c
index 44dd5f0579..01f2fb9289 100644
--- a/quantum/process_keycode/process_magic.c
+++ b/quantum/process_keycode/process_magic.c
@@ -164,6 +164,9 @@ bool process_magic(uint16_t keycode, keyrecord_t *record) {
case MAGIC_EE_HANDS_RIGHT:
eeconfig_update_handedness(false);
break;
+ case MAGIC_TOGGLE_GUI:
+ keymap_config.no_gui = !keymap_config.no_gui;
+ break;
}
eeconfig_update_keymap(keymap_config.raw);
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..69853cd5c4 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);
@@ -162,7 +161,7 @@ bool process_rgb(const uint16_t keycode, const keyrecord_t *record) {
#if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES) && defined(RGBLIGHT_EFFECT_BREATHING)
handleKeycodeRGBMode(RGBLIGHT_MODE_BREATHING, RGBLIGHT_MODE_BREATHING_end);
#endif
-#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && !defined(DISABLE_RGB_MATRIX_BREATHING)
+#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && defined(ENABLE_RGB_MATRIX_BREATHING)
rgb_matrix_mode(RGB_MATRIX_BREATHING);
#endif
return false;
@@ -170,7 +169,7 @@ bool process_rgb(const uint16_t keycode, const keyrecord_t *record) {
#if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES) && defined(RGBLIGHT_EFFECT_RAINBOW_MOOD)
handleKeycodeRGBMode(RGBLIGHT_MODE_RAINBOW_MOOD, RGBLIGHT_MODE_RAINBOW_MOOD_end);
#endif
-#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && !defined(DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT)
+#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && defined(ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT)
rgb_matrix_mode(RGB_MATRIX_CYCLE_LEFT_RIGHT);
#endif
return false;
@@ -178,7 +177,7 @@ bool process_rgb(const uint16_t keycode, const keyrecord_t *record) {
#if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES) && defined(RGBLIGHT_EFFECT_RAINBOW_SWIRL)
handleKeycodeRGBMode(RGBLIGHT_MODE_RAINBOW_SWIRL, RGBLIGHT_MODE_RAINBOW_SWIRL_end);
#endif
-#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && !defined(DISABLE_RGB_MATRIX_CYCLE_PINWHEEL)
+#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && defined(ENABLE_RGB_MATRIX_CYCLE_PINWHEEL)
rgb_matrix_mode(RGB_MATRIX_CYCLE_PINWHEEL);
#endif
return false;
diff --git a/quantum/process_keycode/process_steno.c b/quantum/process_keycode/process_steno.c
index 57e279f211..a964aead35 100644
--- a/quantum/process_keycode/process_steno.c
+++ b/quantum/process_keycode/process_steno.c
@@ -65,6 +65,12 @@ static steno_mode_t mode;
static const uint8_t boltmap[64] PROGMEM = {TXB_NUL, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_S_L, TXB_S_L, TXB_T_L, TXB_K_L, TXB_P_L, TXB_W_L, TXB_H_L, TXB_R_L, TXB_A_L, TXB_O_L, TXB_STR, TXB_STR, TXB_NUL, TXB_NUL, TXB_NUL, TXB_STR, TXB_STR, TXB_E_R, TXB_U_R, TXB_F_R, TXB_R_R, TXB_P_R, TXB_B_R, TXB_L_R, TXB_G_R, TXB_T_R, TXB_S_R, TXB_D_R, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_Z_R};
+#ifdef STENO_COMBINEDMAP
+/* Used to look up when pressing the middle row key to combine two consonant or vowel keys */
+static const uint16_t combinedmap_first[] PROGMEM = {STN_S1, STN_TL, STN_PL, STN_HL, STN_FR, STN_PR, STN_LR, STN_TR, STN_DR, STN_A, STN_E};
+static const uint16_t combinedmap_second[] PROGMEM = {STN_S2, STN_KL, STN_WL, STN_RL, STN_RR, STN_BR, STN_GR, STN_SR, STN_ZR, STN_O, STN_U};
+#endif
+
static void steno_clear_state(void) {
memset(state, 0, sizeof(state));
memset(chord, 0, sizeof(chord));
@@ -167,6 +173,15 @@ bool process_steno(uint16_t keycode, keyrecord_t *record) {
}
return false;
+#ifdef STENO_COMBINEDMAP
+ case QK_STENO_COMB ... QK_STENO_COMB_MAX:
+ {
+ uint8_t result;
+ result = process_steno(combinedmap_first[keycode-QK_STENO_COMB], record);
+ result &= process_steno(combinedmap_second[keycode-QK_STENO_COMB], record);
+ return result;
+ }
+#endif
case STN__MIN ... STN__MAX:
if (!process_steno_user(keycode, record)) {
return false;
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..00426c3973 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -49,65 +49,7 @@ 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)) {
- switch (code) {
- case QK_MODS ... QK_MODS_MAX:
- break;
- default:
- return;
- }
-
- uint8_t mods_to_send = 0;
-
- if (code & QK_RMODS_MIN) { // Right mod flag is set
- if (code & QK_LCTL) mods_to_send |= MOD_BIT(KC_RCTL);
- if (code & QK_LSFT) mods_to_send |= MOD_BIT(KC_RSFT);
- if (code & QK_LALT) mods_to_send |= MOD_BIT(KC_RALT);
- if (code & QK_LGUI) mods_to_send |= MOD_BIT(KC_RGUI);
- } else {
- if (code & QK_LCTL) mods_to_send |= MOD_BIT(KC_LCTL);
- if (code & QK_LSFT) mods_to_send |= MOD_BIT(KC_LSFT);
- if (code & QK_LALT) mods_to_send |= MOD_BIT(KC_LALT);
- if (code & QK_LGUI) mods_to_send |= MOD_BIT(KC_LGUI);
- }
-
- f(mods_to_send);
-}
-
-void register_code16(uint16_t code) {
- if (IS_MOD(code) || code == KC_NO) {
- do_code16(code, register_mods);
- } else {
- do_code16(code, register_weak_mods);
- }
- register_code(code);
-}
-
-void unregister_code16(uint16_t code) {
- unregister_code(code);
- if (IS_MOD(code) || code == KC_NO) {
- do_code16(code, unregister_mods);
- } else {
- do_code16(code, unregister_weak_mods);
- }
-}
-
-void tap_code16(uint16_t code) {
- register_code16(code);
-#if TAP_CODE_DELAY > 0
- wait_ms(TAP_CODE_DELAY);
#endif
- unregister_code16(code);
-}
__attribute__((weak)) bool process_action_kb(keyrecord_t *record) { return true; }
@@ -143,31 +85,16 @@ void reset_keyboard(void) {
bootloader_jump();
}
-/* Convert record into usable keycode via the contained event. */
-uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache) { return get_event_keycode(record->event, update_layer_cache); }
-
-/* Convert event into usable keycode. Checks the layer cache to ensure that it
- * retains the correct keycode after a layer change, if the key is still pressed.
- * "update_layer_cache" is to ensure that it only updates the layer cache when
- * appropriate, otherwise, it will update it and cause layer tap (and other keys)
- * from triggering properly.
- */
-uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache) {
-#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
- /* TODO: Use store_or_get_action() or a similar function. */
- if (!disable_action_cache) {
- uint8_t layer;
-
- if (event.pressed && update_layer_cache) {
- layer = layer_switch_get_layer(event.key);
- update_source_layers_cache(event.key, layer);
- } else {
- layer = read_source_layers_cache(event.key);
- }
- return keymap_key_to_keycode(layer, event.key);
- } else
+/* Get keycode, and then process pre tapping functionality */
+bool pre_process_record_quantum(keyrecord_t *record) {
+ if (!(
+#ifdef COMBO_ENABLE
+ process_combo(get_record_keycode(record, true), record) &&
#endif
- return keymap_key_to_keycode(layer_switch_get_layer(event.key), event.key);
+ true)) {
+ return false;
+ }
+ return true; // continue processing
}
/* Get keycode, and then call keyboard function */
@@ -217,10 +144,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 +170,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
@@ -252,9 +182,6 @@ bool process_record_quantum(keyrecord_t *record) {
#ifdef LEADER_ENABLE
process_leader(keycode, record) &&
#endif
-#ifdef COMBO_ENABLE
- process_combo(keycode, record) &&
-#endif
#ifdef PRINTING_ENABLE
process_printer(keycode, record) &&
#endif
@@ -340,13 +267,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);
}
@@ -354,10 +281,7 @@ void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3) { layer_st
void matrix_init_quantum() {
magic();
-#if defined(LED_NUM_LOCK_PIN) || defined(LED_CAPS_LOCK_PIN) || defined(LED_SCROLL_LOCK_PIN) || defined(LED_COMPOSE_PIN) || defined(LED_KANA_PIN)
- // TODO: remove calls to led_init_ports from keyboards and remove ifdef
led_init_ports();
-#endif
#ifdef BACKLIGHT_ENABLE
backlight_init_ports();
#endif
@@ -384,7 +308,7 @@ void matrix_init_quantum() {
}
void matrix_scan_quantum() {
-#if defined(AUDIO_ENABLE)
+#if defined(AUDIO_ENABLE) && defined(AUDIO_INIT_DELAY)
// There are some tasks that need to be run a little bit
// after keyboard startup, or else they will not work correctly
// because of interaction with the USB device state, which
@@ -405,19 +329,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 d87d14041d..b409edef31 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
+#ifdef OLED_ENABLE
# include "oled_driver.h"
#endif
+#ifdef ST7565_ENABLE
+# include "st7565.h"
+#endif
+
#ifdef DIP_SWITCH_ENABLE
# include "dip_switch.h"
#endif
@@ -203,27 +212,17 @@ 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);
-bool process_record_kb(uint16_t keycode, keyrecord_t *record);
-bool process_record_user(uint16_t keycode, keyrecord_t *record);
-void post_process_record_kb(uint16_t keycode, keyrecord_t *record);
-void post_process_record_user(uint16_t keycode, keyrecord_t *record);
+bool process_action_kb(keyrecord_t *record);
+bool process_record_kb(uint16_t keycode, keyrecord_t *record);
+bool process_record_user(uint16_t keycode, keyrecord_t *record);
+void post_process_record_kb(uint16_t keycode, keyrecord_t *record);
+void post_process_record_user(uint16_t keycode, keyrecord_t *record);
void reset_keyboard(void);
void startup_user(void);
void shutdown_user(void);
-void register_code16(uint16_t code);
-void unregister_code16(uint16_t code);
-void tap_code16(uint16_t code);
-
void led_set_user(uint8_t usb_led);
void led_set_kb(uint8_t usb_led);
bool led_update_user(led_t led_state);
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
index c361dd670e..ef4b0f457b 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -66,6 +66,8 @@ enum quantum_keycodes {
QK_STENO = 0x5A00,
QK_STENO_BOLT = 0x5A30,
QK_STENO_GEMINI = 0x5A31,
+ QK_STENO_COMB = 0x5A32,
+ QK_STENO_COMB_MAX = 0x5A3C,
QK_STENO_MAX = 0x5A3F,
// 0x5C00 - 0x5FFF are reserved, see below
QK_MOD_TAP = 0x6000,
@@ -514,6 +516,14 @@ enum quantum_keycodes {
// RGB underglow/matrix (continued)
RGB_MODE_TWINKLE,
+ // Key Overrides
+ KEY_OVERRIDE_TOGGLE,
+ KEY_OVERRIDE_ON,
+ KEY_OVERRIDE_OFF,
+
+ // Additional magic key
+ MAGIC_TOGGLE_GUI,
+
// Start of custom keycode range for keyboards and keymaps - always leave at the end
SAFE_RANGE
};
@@ -687,6 +697,7 @@ enum quantum_keycodes {
#define GUI_OFF MAGIC_NO_GUI
#define GUI_ON MAGIC_UNNO_GUI
+#define GUI_TOG MAGIC_TOGGLE_GUI
#define GE_SWAP MAGIC_SWAP_GRAVE_ESC
#define GE_NORM MAGIC_UNSWAP_GRAVE_ESC
diff --git a/quantum/rgb_matrix_animations/alpha_mods_anim.h b/quantum/rgb_matrix/animations/alpha_mods_anim.h
index 426d88ef35..3f2c9b799a 100644
--- a/quantum/rgb_matrix_animations/alpha_mods_anim.h
+++ b/quantum/rgb_matrix/animations/alpha_mods_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
+#ifdef ENABLE_RGB_MATRIX_ALPHAS_MODS
RGB_MATRIX_EFFECT(ALPHAS_MODS)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/breathing_anim.h b/quantum/rgb_matrix/animations/breathing_anim.h
index 340bd93e5d..a00ccb83a2 100644
--- a/quantum/rgb_matrix_animations/breathing_anim.h
+++ b/quantum/rgb_matrix/animations/breathing_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_BREATHING
+#ifdef ENABLE_RGB_MATRIX_BREATHING
RGB_MATRIX_EFFECT(BREATHING)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/colorband_pinwheel_sat_anim.h b/quantum/rgb_matrix/animations/colorband_pinwheel_sat_anim.h
index 3df3cfda7d..ac95789228 100644
--- a/quantum/rgb_matrix_animations/colorband_pinwheel_sat_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_pinwheel_sat_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_BAND_PINWHEEL_SAT
+#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT
RGB_MATRIX_EFFECT(BAND_PINWHEEL_SAT)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/colorband_pinwheel_val_anim.h b/quantum/rgb_matrix/animations/colorband_pinwheel_val_anim.h
index 7d80074fd5..f7fe4b76e9 100644
--- a/quantum/rgb_matrix_animations/colorband_pinwheel_val_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_pinwheel_val_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_BAND_PINWHEEL_VAL
+#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL
RGB_MATRIX_EFFECT(BAND_PINWHEEL_VAL)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/colorband_sat_anim.h b/quantum/rgb_matrix/animations/colorband_sat_anim.h
index 35b830af6b..96a6cbf5cd 100644
--- a/quantum/rgb_matrix_animations/colorband_sat_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_sat_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_BAND_SAT
+#ifdef ENABLE_RGB_MATRIX_BAND_SAT
RGB_MATRIX_EFFECT(BAND_SAT)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/colorband_spiral_sat_anim.h b/quantum/rgb_matrix/animations/colorband_spiral_sat_anim.h
index 048157aa1b..52f6040d81 100644
--- a/quantum/rgb_matrix_animations/colorband_spiral_sat_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_spiral_sat_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_BAND_SPIRAL_SAT
+#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT
RGB_MATRIX_EFFECT(BAND_SPIRAL_SAT)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/colorband_spiral_val_anim.h b/quantum/rgb_matrix/animations/colorband_spiral_val_anim.h
index bff2da1616..d5752e27fb 100644
--- a/quantum/rgb_matrix_animations/colorband_spiral_val_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_spiral_val_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_BAND_SPIRAL_VAL
+#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL
RGB_MATRIX_EFFECT(BAND_SPIRAL_VAL)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/colorband_val_anim.h b/quantum/rgb_matrix/animations/colorband_val_anim.h
index f1aaf1d067..32bc6f52a5 100644
--- a/quantum/rgb_matrix_animations/colorband_val_anim.h
+++ b/quantum/rgb_matrix/animations/colorband_val_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_BAND_VAL
+#ifdef ENABLE_RGB_MATRIX_BAND_VAL
RGB_MATRIX_EFFECT(BAND_VAL)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/cycle_all_anim.h b/quantum/rgb_matrix/animations/cycle_all_anim.h
index faf8598a39..20af94b6ba 100644
--- a/quantum/rgb_matrix_animations/cycle_all_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_all_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
+#ifdef ENABLE_RGB_MATRIX_CYCLE_ALL
RGB_MATRIX_EFFECT(CYCLE_ALL)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/cycle_left_right_anim.h b/quantum/rgb_matrix/animations/cycle_left_right_anim.h
index cf911eb937..7f84f4bd59 100644
--- a/quantum/rgb_matrix_animations/cycle_left_right_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_left_right_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
+#ifdef ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
RGB_MATRIX_EFFECT(CYCLE_LEFT_RIGHT)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/cycle_out_in_anim.h b/quantum/rgb_matrix/animations/cycle_out_in_anim.h
index d66acd4b2b..2277f16a2f 100644
--- a/quantum/rgb_matrix_animations/cycle_out_in_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_out_in_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_CYCLE_OUT_IN
+#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN
RGB_MATRIX_EFFECT(CYCLE_OUT_IN)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/cycle_out_in_dual_anim.h b/quantum/rgb_matrix/animations/cycle_out_in_dual_anim.h
index fe8396140f..981c0afd05 100644
--- a/quantum/rgb_matrix_animations/cycle_out_in_dual_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_out_in_dual_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL
+#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL
RGB_MATRIX_EFFECT(CYCLE_OUT_IN_DUAL)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/cycle_pinwheel_anim.h b/quantum/rgb_matrix/animations/cycle_pinwheel_anim.h
index 7799887099..1a7db4a4c7 100644
--- a/quantum/rgb_matrix_animations/cycle_pinwheel_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_pinwheel_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_CYCLE_PINWHEEL
+#ifdef ENABLE_RGB_MATRIX_CYCLE_PINWHEEL
RGB_MATRIX_EFFECT(CYCLE_PINWHEEL)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/cycle_spiral_anim.h b/quantum/rgb_matrix/animations/cycle_spiral_anim.h
index 80cfb0dbc7..245cdc237f 100644
--- a/quantum/rgb_matrix_animations/cycle_spiral_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_spiral_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_CYCLE_SPIRAL
+#ifdef ENABLE_RGB_MATRIX_CYCLE_SPIRAL
RGB_MATRIX_EFFECT(CYCLE_SPIRAL)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/cycle_up_down_anim.h b/quantum/rgb_matrix/animations/cycle_up_down_anim.h
index 5016f739d6..c9b5a54133 100644
--- a/quantum/rgb_matrix_animations/cycle_up_down_anim.h
+++ b/quantum/rgb_matrix/animations/cycle_up_down_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
+#ifdef ENABLE_RGB_MATRIX_CYCLE_UP_DOWN
RGB_MATRIX_EFFECT(CYCLE_UP_DOWN)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/digital_rain_anim.h b/quantum/rgb_matrix/animations/digital_rain_anim.h
index 1de45f8e8d..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..5c06080a26 100644
--- a/quantum/rgb_matrix_animations/dual_beacon_anim.h
+++ b/quantum/rgb_matrix/animations/dual_beacon_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
+#ifdef ENABLE_RGB_MATRIX_DUAL_BEACON
RGB_MATRIX_EFFECT(DUAL_BEACON)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/gradient_left_right_anim.h b/quantum/rgb_matrix/animations/gradient_left_right_anim.h
index 53dfd04e2c..b4f2752ff7 100644
--- a/quantum/rgb_matrix_animations/gradient_left_right_anim.h
+++ b/quantum/rgb_matrix/animations/gradient_left_right_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT
+#ifdef ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT
RGB_MATRIX_EFFECT(GRADIENT_LEFT_RIGHT)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/gradient_up_down_anim.h b/quantum/rgb_matrix/animations/gradient_up_down_anim.h
index 7e0d2898cf..3fd45cf99b 100644
--- a/quantum/rgb_matrix_animations/gradient_up_down_anim.h
+++ b/quantum/rgb_matrix/animations/gradient_up_down_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
+#ifdef ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN
RGB_MATRIX_EFFECT(GRADIENT_UP_DOWN)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/hue_breathing_anim.h b/quantum/rgb_matrix/animations/hue_breathing_anim.h
index 54dea958af..6d974b8c39 100644
--- a/quantum/rgb_matrix_animations/hue_breathing_anim.h
+++ b/quantum/rgb_matrix/animations/hue_breathing_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_HUE_BREATHING
+#ifdef ENABLE_RGB_MATRIX_HUE_BREATHING
RGB_MATRIX_EFFECT(HUE_BREATHING)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/hue_pendulum_anim.h b/quantum/rgb_matrix/animations/hue_pendulum_anim.h
index 2d8d36174f..a6e1c1074d 100644
--- a/quantum/rgb_matrix_animations/hue_pendulum_anim.h
+++ b/quantum/rgb_matrix/animations/hue_pendulum_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_HUE_PENDULUM
+#ifdef ENABLE_RGB_MATRIX_HUE_PENDULUM
RGB_MATRIX_EFFECT(HUE_PENDULUM)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/hue_wave_anim.h b/quantum/rgb_matrix/animations/hue_wave_anim.h
index fd9026fc90..b1c72b7336 100644
--- a/quantum/rgb_matrix_animations/hue_wave_anim.h
+++ b/quantum/rgb_matrix/animations/hue_wave_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_HUE_WAVE
+#ifdef ENABLE_RGB_MATRIX_HUE_WAVE
RGB_MATRIX_EFFECT(HUE_WAVE)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h b/quantum/rgb_matrix/animations/jellybean_raindrops_anim.h
index 9493b38508..7d8eafffb9 100644
--- a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h
+++ b/quantum/rgb_matrix/animations/jellybean_raindrops_anim.h
@@ -1,10 +1,10 @@
-#ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
+#ifdef ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
RGB_MATRIX_EFFECT(JELLYBEAN_RAINDROPS)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
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..b54d997f63 100644
--- a/quantum/rgb_matrix_animations/rainbow_beacon_anim.h
+++ b/quantum/rgb_matrix/animations/rainbow_beacon_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
+#ifdef ENABLE_RGB_MATRIX_RAINBOW_BEACON
RGB_MATRIX_EFFECT(RAINBOW_BEACON)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h b/quantum/rgb_matrix/animations/rainbow_moving_chevron_anim.h
index e51e7b2516..50db922014 100644
--- a/quantum/rgb_matrix_animations/rainbow_moving_chevron_anim.h
+++ b/quantum/rgb_matrix/animations/rainbow_moving_chevron_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+#ifdef ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
RGB_MATRIX_EFFECT(RAINBOW_MOVING_CHEVRON)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h b/quantum/rgb_matrix/animations/rainbow_pinwheels_anim.h
index 1cd4ed2acf..3299f15df1 100644
--- a/quantum/rgb_matrix_animations/rainbow_pinwheels_anim.h
+++ b/quantum/rgb_matrix/animations/rainbow_pinwheels_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
+#ifdef ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS
RGB_MATRIX_EFFECT(RAINBOW_PINWHEELS)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix_animations/raindrops_anim.h b/quantum/rgb_matrix/animations/raindrops_anim.h
index 38359cdca7..c01688e2c7 100644
--- a/quantum/rgb_matrix_animations/raindrops_anim.h
+++ b/quantum/rgb_matrix/animations/raindrops_anim.h
@@ -1,4 +1,4 @@
-#ifndef DISABLE_RGB_MATRIX_RAINDROPS
+#ifdef ENABLE_RGB_MATRIX_RAINDROPS
RGB_MATRIX_EFFECT(RAINDROPS)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
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..28f040109d 100644
--- a/quantum/rgb_matrix_animations/typing_heatmap_anim.h
+++ b/quantum/rgb_matrix/animations/typing_heatmap_anim.h
@@ -1,4 +1,4 @@
-#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(DISABLE_RGB_MATRIX_TYPING_HEATMAP)
+#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_RGB_MATRIX_TYPING_HEATMAP)
RGB_MATRIX_EFFECT(TYPING_HEATMAP)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c
index ab8dbd849b..8f00b40877 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
@@ -93,7 +92,7 @@ __attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv
#endif
#if !defined(RGB_MATRIX_STARTUP_MODE)
-# ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
+# ifdef ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
# define RGB_MATRIX_STARTUP_MODE RGB_MATRIX_CYCLE_LEFT_RIGHT
# else
// fallback to solid colors if RGB_MATRIX_CYCLE_LEFT_RIGHT is disabled in userspace
@@ -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++) {
@@ -243,11 +243,11 @@ void process_rgb_matrix(uint8_t row, uint8_t col, bool pressed) {
}
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
-#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(DISABLE_RGB_MATRIX_TYPING_HEATMAP)
+#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_RGB_MATRIX_TYPING_HEATMAP)
if (rgb_matrix_config.mode == RGB_MATRIX_TYPING_HEATMAP) {
process_rgb_matrix_typing_heatmap(row, col);
}
-#endif // defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(DISABLE_RGB_MATRIX_TYPING_HEATMAP)
+#endif // defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_RGB_MATRIX_TYPING_HEATMAP)
}
void rgb_matrix_test(void) {
@@ -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)
@@ -499,8 +501,9 @@ void rgb_matrix_init(void) {
void rgb_matrix_set_suspend_state(bool state) {
#ifdef RGB_DISABLE_WHEN_USB_SUSPENDED
- if (state) {
- rgb_matrix_set_color_all(0, 0, 0); // turn off all LEDs when suspending
+ if (state && !suspend_state) { // only run if turning off, and only once
+ rgb_task_render(0); // turn off all LEDs when suspending
+ rgb_task_flush(0); // and actually flash led state to LEDs
}
suspend_state = state;
#endif
@@ -511,9 +514,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 +522,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 +532,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 +554,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 +583,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 +620,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..f53e011c1b 100644
--- a/quantum/rgb_matrix.h
+++ b/quantum/rgb_matrix/rgb_matrix.h
@@ -23,7 +23,7 @@
#include "rgb_matrix_types.h"
#include "color.h"
#include "quantum.h"
-#include "rgblight_list.h"
+#include "rgb_matrix_legacy_enables.h"
#ifdef IS31FL3731
# include "is31fl3731.h"
@@ -33,6 +33,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 +72,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/rgb_matrix_legacy_enables.h b/quantum/rgb_matrix/rgb_matrix_legacy_enables.h
new file mode 100644
index 0000000000..398858ebc8
--- /dev/null
+++ b/quantum/rgb_matrix/rgb_matrix_legacy_enables.h
@@ -0,0 +1,153 @@
+/* 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/>.
+ */
+
+// to-do: remove this
+
+#pragma once
+
+#ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
+# define ENABLE_RGB_MATRIX_ALPHAS_MODS
+#endif
+#ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
+# define ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN
+#endif
+#ifndef DISABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT
+# define ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT
+#endif
+#ifndef DISABLE_RGB_MATRIX_BREATHING
+# define ENABLE_RGB_MATRIX_BREATHING
+#endif
+#ifndef DISABLE_RGB_MATRIX_BAND_SAT
+# define ENABLE_RGB_MATRIX_BAND_SAT
+#endif
+#ifndef DISABLE_RGB_MATRIX_BAND_VAL
+# define ENABLE_RGB_MATRIX_BAND_VAL
+#endif
+#ifndef DISABLE_RGB_MATRIX_BAND_PINWHEEL_SAT
+# define ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT
+#endif
+#ifndef DISABLE_RGB_MATRIX_BAND_PINWHEEL_VAL
+# define ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL
+#endif
+#ifndef DISABLE_RGB_MATRIX_BAND_SPIRAL_SAT
+# define ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT
+#endif
+#ifndef DISABLE_RGB_MATRIX_BAND_SPIRAL_VAL
+# define ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL
+#endif
+#ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
+# define ENABLE_RGB_MATRIX_CYCLE_ALL
+#endif
+#ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
+# define ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
+#endif
+#ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
+# define ENABLE_RGB_MATRIX_CYCLE_UP_DOWN
+#endif
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+# define ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+#endif
+#ifndef DISABLE_RGB_MATRIX_CYCLE_OUT_IN
+# define ENABLE_RGB_MATRIX_CYCLE_OUT_IN
+#endif
+#ifndef DISABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL
+# define ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL
+#endif
+#ifndef DISABLE_RGB_MATRIX_CYCLE_PINWHEEL
+# define ENABLE_RGB_MATRIX_CYCLE_PINWHEEL
+#endif
+#ifndef DISABLE_RGB_MATRIX_CYCLE_SPIRAL
+# define ENABLE_RGB_MATRIX_CYCLE_SPIRAL
+#endif
+#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
+# define ENABLE_RGB_MATRIX_DUAL_BEACON
+#endif
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
+# define ENABLE_RGB_MATRIX_RAINBOW_BEACON
+#endif
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
+# define ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS
+#endif
+#ifndef DISABLE_RGB_MATRIX_RAINDROPS
+# define ENABLE_RGB_MATRIX_RAINDROPS
+#endif
+#ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
+# define ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
+#endif
+#ifndef DISABLE_RGB_MATRIX_HUE_BREATHING
+# define ENABLE_RGB_MATRIX_HUE_BREATHING
+#endif
+#ifndef DISABLE_RGB_MATRIX_HUE_PENDULUM
+# define ENABLE_RGB_MATRIX_HUE_PENDULUM
+#endif
+#ifndef DISABLE_RGB_MATRIX_HUE_WAVE
+# define ENABLE_RGB_MATRIX_HUE_WAVE
+#endif
+#ifndef DISABLE_RGB_MATRIX_PIXEL_RAIN
+# define ENABLE_RGB_MATRIX_PIXEL_RAIN
+#endif
+#ifndef DISABLE_RGB_MATRIX_PIXEL_FLOW
+# define ENABLE_RGB_MATRIX_PIXEL_FLOW
+#endif
+#ifndef DISABLE_RGB_MATRIX_PIXEL_FRACTAL
+# define ENABLE_RGB_MATRIX_PIXEL_FRACTAL
+#endif
+#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS)
+# ifndef DISABLE_RGB_MATRIX_TYPING_HEATMAP
+# define ENABLE_RGB_MATRIX_TYPING_HEATMAP
+# endif
+# ifndef DISABLE_RGB_MATRIX_DIGITAL_RAIN
+# define ENABLE_RGB_MATRIX_DIGITAL_RAIN
+# endif
+#endif
+#if defined(RGB_MATRIX_KEYREACTIVE_ENABLED)
+# ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
+# define ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
+# endif
+# ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
+# define ENABLE_RGB_MATRIX_SOLID_REACTIVE
+# endif
+# ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE
+# define ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE
+# endif
+# ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE
+# define ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE
+# endif
+# ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS
+# define ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS
+# endif
+# ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS
+# define ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS
+# endif
+# ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS
+# define ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS
+# endif
+# ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS
+# define ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS
+# endif
+# ifndef DISABLE_RGB_MATRIX_SPLASH
+# define ENABLE_RGB_MATRIX_SPLASH
+# endif
+# ifndef DISABLE_RGB_MATRIX_MULTISPLASH
+# define ENABLE_RGB_MATRIX_MULTISPLASH
+# endif
+# ifndef DISABLE_RGB_MATRIX_SOLID_SPLASH
+# define ENABLE_RGB_MATRIX_SOLID_SPLASH
+# endif
+# ifndef DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
+# define ENABLE_RGB_MATRIX_SOLID_MULTISPLASH
+# endif
+#endif
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..35f0a9d181 100644
--- a/quantum/split_common/split_util.c
+++ b/quantum/split_common/split_util.c
@@ -39,6 +39,21 @@
# define SPLIT_USB_TIMEOUT_POLL 10
#endif
+// Max number of consecutive failed communications (one per scan cycle) before the communication is seen as disconnected.
+// Set to 0 to disable the disconnection check altogether.
+#ifndef SPLIT_MAX_CONNECTION_ERRORS
+# define SPLIT_MAX_CONNECTION_ERRORS 10
+#endif // SPLIT_MAX_CONNECTION_ERRORS
+
+// How long (in milliseconds) to block all connection attempts after the communication has been flagged as disconnected.
+// One communication attempt will be allowed everytime this amount of time has passed since the last attempt. If that attempt succeeds, the communication is seen as working again.
+// Set to 0 to disable communication throttling while disconnected
+#ifndef SPLIT_CONNECTION_CHECK_TIMEOUT
+# define SPLIT_CONNECTION_CHECK_TIMEOUT 500
+#endif // SPLIT_CONNECTION_CHECK_TIMEOUT
+
+static uint8_t connection_errors = 0;
+
volatile bool isLeftHand = true;
#if defined(SPLIT_USB_DETECT)
@@ -77,7 +92,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 +121,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();
}
}
@@ -138,3 +157,39 @@ void split_post_init(void) {
transport_slave_init();
}
}
+
+bool is_transport_connected(void) { return connection_errors < SPLIT_MAX_CONNECTION_ERRORS; }
+
+bool transport_master_if_connected(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+#if SPLIT_MAX_CONNECTION_ERRORS > 0 && SPLIT_CONNECTION_CHECK_TIMEOUT > 0
+ // Throttle transaction attempts if target doesn't seem to be connected
+ // Without this, a solo half becomes unusable due to constant read timeouts
+ static uint16_t connection_check_timer = 0;
+ const bool is_disconnected = !is_transport_connected();
+ if (is_disconnected && timer_elapsed(connection_check_timer) < SPLIT_CONNECTION_CHECK_TIMEOUT) {
+ return false;
+ }
+#endif // SPLIT_MAX_CONNECTION_ERRORS > 0 && SPLIT_CONNECTION_CHECK_TIMEOUT > 0
+
+ __attribute__((unused)) bool okay = transport_master(master_matrix, slave_matrix);
+#if SPLIT_MAX_CONNECTION_ERRORS > 0
+ if (!okay) {
+ if (connection_errors < UINT8_MAX) {
+ connection_errors++;
+ }
+# if SPLIT_CONNECTION_CHECK_TIMEOUT > 0
+ bool connected = is_transport_connected();
+ if (!connected) {
+ connection_check_timer = timer_read();
+ dprintln("Target disconnected, throttling connection attempts");
+ }
+ return connected;
+ } else if (is_disconnected) {
+ dprintln("Target connected");
+# endif // SPLIT_CONNECTION_CHECK_TIMEOUT > 0
+ }
+
+ connection_errors = 0;
+#endif // SPLIT_MAX_CONNECTION_ERRORS > 0
+ return true;
+}
diff --git a/quantum/split_common/split_util.h b/quantum/split_common/split_util.h
index a4c12519e0..ef72043bb7 100644
--- a/quantum/split_common/split_util.h
+++ b/quantum/split_common/split_util.h
@@ -5,8 +5,13 @@
#include <stdio.h>
#include <stdlib.h>
+#include "matrix.h"
+
extern volatile bool isLeftHand;
void matrix_master_OLED_init(void);
void split_pre_init(void);
void split_post_init(void);
+
+bool transport_master_if_connected(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);
+bool is_transport_connected(void);
diff --git a/quantum/split_common/transaction_id_define.h b/quantum/split_common/transaction_id_define.h
new file mode 100644
index 0000000000..535bc21aea
--- /dev/null
+++ b/quantum/split_common/transaction_id_define.h
@@ -0,0 +1,102 @@
+/* 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(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
+ PUT_OLED,
+#endif // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
+
+#if defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
+ PUT_ST7565,
+#endif // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_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..fd676f0729
--- /dev/null
+++ b/quantum/split_common/transactions.c
@@ -0,0 +1,723 @@
+/* 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 "split_util.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
+
+static bool transaction_handler_master(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[])) {
+ int num_retries = is_transport_connected() ? 10 : 1;
+ for (int iter = 1; iter <= num_retries; ++iter) {
+ if (iter > 1) {
+ for (int i = 0; i < iter * iter; ++i) {
+ wait_us(10);
+ }
+ }
+ bool this_okay = true;
+ ATOMIC_BLOCK_FORCEON { this_okay = handler(master_matrix, slave_matrix); };
+ if (this_okay) return true;
+ }
+ dprintf("Failed to execute %s\n", prefix);
+ return false;
+}
+
+#define TRANSACTION_HANDLER_MASTER(prefix) \
+ do { \
+ if (!transaction_handler_master(master_matrix, slave_matrix, #prefix, &prefix##_handlers_master)) return false; \
+ } while (0)
+
+#define TRANSACTION_HANDLER_SLAVE(prefix) \
+ do { \
+ ATOMIC_BLOCK_FORCEON { prefix##_handlers_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)
+#define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(slave_matrix)
+#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)
+# define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(master_matrix)
+# 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)
+# define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder)
+# 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)
+# define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE(sync_timer)
+# 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)
+# define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(layer_state)
+# 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)
+# define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(led_state)
+# 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)
+# define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods)
+# 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)
+# define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight)
+# 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)
+# define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight)
+# 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)
+# define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix)
+# 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)
+# define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix)
+# 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)
+# define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE(wpm)
+# 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)
+
+////////////////////////////////////////////////////
+// OLED
+
+#if defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
+
+static bool oled_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ bool current_oled_state = is_oled_on();
+ return send_if_condition(PUT_OLED, &last_update, (current_oled_state != split_shmem->current_oled_state), &current_oled_state, sizeof(current_oled_state));
+}
+
+static void oled_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ if (split_shmem->current_oled_state) {
+ oled_on();
+ } else {
+ oled_off();
+ }
+}
+
+# define TRANSACTIONS_OLED_MASTER() TRANSACTION_HANDLER_MASTER(oled)
+# define TRANSACTIONS_OLED_SLAVE() TRANSACTION_HANDLER_SLAVE(oled)
+# define TRANSACTIONS_OLED_REGISTRATIONS [PUT_OLED] = trans_initiator2target_initializer(current_oled_state),
+
+#else // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
+
+# define TRANSACTIONS_OLED_MASTER()
+# define TRANSACTIONS_OLED_SLAVE()
+# define TRANSACTIONS_OLED_REGISTRATIONS
+
+#endif // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
+
+////////////////////////////////////////////////////
+// ST7565
+
+#if defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
+
+static bool st7565_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ static uint32_t last_update = 0;
+ bool current_st7565_state = st7565_is_on();
+ return send_if_condition(PUT_ST7565, &last_update, (current_st7565_state != split_shmem->current_st7565_state), &current_st7565_state, sizeof(current_st7565_state));
+}
+
+static void st7565_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+ if (split_shmem->current_st7565_state) {
+ st7565_on();
+ } else {
+ st7565_off();
+ }
+}
+
+# define TRANSACTIONS_ST7565_MASTER() TRANSACTION_HANDLER_MASTER(st7565)
+# define TRANSACTIONS_ST7565_SLAVE() TRANSACTION_HANDLER_SLAVE(st7565)
+# define TRANSACTIONS_ST7565_REGISTRATIONS [PUT_ST7565] = trans_initiator2target_initializer(current_st7565_state),
+
+#else // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
+
+# define TRANSACTIONS_ST7565_MASTER()
+# define TRANSACTIONS_ST7565_SLAVE()
+# define TRANSACTIONS_ST7565_REGISTRATIONS
+
+#endif // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_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
+ TRANSACTIONS_OLED_REGISTRATIONS
+ TRANSACTIONS_ST7565_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[]) {
+ 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();
+ TRANSACTIONS_OLED_MASTER();
+ TRANSACTIONS_ST7565_MASTER();
+ return true;
+}
+
+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();
+ TRANSACTIONS_OLED_SLAVE();
+ TRANSACTIONS_ST7565_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 transaction attempts while transport is disconnected
+ if (!is_transport_connected()) {
+ return false;
+ }
+ // 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..53610d6f8e
--- /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) (((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..bcc0261417 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); }
diff --git a/quantum/split_common/transport.h b/quantum/split_common/transport.h
index a9f66301bf..1d4f6ed0cd 100644
--- a/quantum/split_common/transport.h
+++ b/quantum/split_common/transport.h
@@ -1,10 +1,183 @@
+/* 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(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
+ uint8_t current_oled_state;
+#endif // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)
+
+#if defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
+ uint8_t current_st7565_state;
+#endif // ST7565_ENABLE(OLED_ENABLE) && defined(SPLIT_ST7565_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;
diff --git a/quantum/template/avr/rules.mk b/quantum/template/avr/rules.mk
index 5c0d8f307c..62fbb34348 100644
--- a/quantum/template/avr/rules.mk
+++ b/quantum/template/avr/rules.mk
@@ -7,7 +7,7 @@ BOOTLOADER = atmel-dfu
# Build Options
# change yes to no to disable
#
-BOOTMAGIC_ENABLE = lite # Virtual DIP switch configuration
+BOOTMAGIC_ENABLE = yes # Enable Bootmagic Lite
MOUSEKEY_ENABLE = yes # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = no # Console for debug
diff --git a/quantum/template/ps2avrgb/rules.mk b/quantum/template/ps2avrgb/rules.mk
index 1b61e9534d..6c0b7cc231 100644
--- a/quantum/template/ps2avrgb/rules.mk
+++ b/quantum/template/ps2avrgb/rules.mk
@@ -7,7 +7,7 @@ BOOTLOADER = bootloadHID
# Build Options
# change yes to no to disable
#
-BOOTMAGIC_ENABLE = lite # Virtual DIP switch configuration
+BOOTMAGIC_ENABLE = yes # Enable Bootmagic Lite
MOUSEKEY_ENABLE = yes # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = no # Console for debug
diff --git a/quantum/via.c b/quantum/via.c
index c89b663b9b..1b2dbcf08d 100644
--- a/quantum/via.c
+++ b/quantum/via.c
@@ -83,16 +83,6 @@ void via_eeprom_set_valid(bool valid) {
eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 2, valid ? magic2 : 0xFF);
}
-// Flag QMK and VIA/keyboard level EEPROM as invalid.
-// Used in bootmagic_lite() and VIA command handler.
-// Keyboard level code should not need to call this.
-void via_eeprom_reset(void) {
- // Set the VIA specific EEPROM state as invalid.
- via_eeprom_set_valid(false);
- // Set the TMK/QMK EEPROM state as invalid.
- eeconfig_disable();
-}
-
// Override this at the keyboard code level to check
// VIA's EEPROM valid state and reset to defaults as needed.
// Used by keyboards that store their own state in EEPROM,
@@ -109,19 +99,24 @@ void via_init(void) {
// If the EEPROM has the magic, the data is good.
// OK to load from EEPROM.
- if (via_eeprom_is_valid()) {
- } else {
- // This resets the layout options
- via_set_layout_options(VIA_EEPROM_LAYOUT_OPTIONS_DEFAULT);
- // This resets the keymaps in EEPROM to what is in flash.
- dynamic_keymap_reset();
- // This resets the macros in EEPROM to nothing.
- dynamic_keymap_macro_reset();
- // Save the magic number last, in case saving was interrupted
- via_eeprom_set_valid(true);
+ if (!via_eeprom_is_valid()) {
+ eeconfig_init_via();
}
}
+void eeconfig_init_via(void) {
+ // set the magic number to false, in case this gets interrupted
+ via_eeprom_set_valid(false);
+ // This resets the layout options
+ via_set_layout_options(VIA_EEPROM_LAYOUT_OPTIONS_DEFAULT);
+ // This resets the keymaps in EEPROM to what is in flash.
+ dynamic_keymap_reset();
+ // This resets the macros in EEPROM to nothing.
+ dynamic_keymap_macro_reset();
+ // Save the magic number last, in case saving was interrupted
+ via_eeprom_set_valid(true);
+}
+
// This is generalized so the layout options EEPROM usage can be
// variable, between 1 and 4 bytes.
uint32_t via_get_layout_options(void) {
@@ -329,6 +324,13 @@ void raw_hid_receive(uint8_t *data, uint8_t length) {
#endif
break;
}
+#ifdef VIA_EEPROM_ALLOW_RESET
+ case id_eeprom_reset: {
+ via_eeprom_set_valid(false);
+ eeconfig_init_via();
+ break;
+ }
+#endif
case id_dynamic_keymap_macro_get_count: {
command_data[0] = dynamic_keymap_macro_get_count();
break;
diff --git a/quantum/via.h b/quantum/via.h
index d0510fcabd..3db318a454 100644
--- a/quantum/via.h
+++ b/quantum/via.h
@@ -16,7 +16,7 @@
#pragma once
-#include "tmk_core/common/eeconfig.h" // for EECONFIG_SIZE
+#include "eeconfig.h" // for EECONFIG_SIZE
// Keyboard level code can change where VIA stores the magic.
// The magic is the build date YYMMDD encoded as BCD in 3 bytes,
@@ -152,12 +152,8 @@ bool via_eeprom_is_valid(void);
// Keyboard level code (eg. via_init_kb()) should not call this
void via_eeprom_set_valid(bool valid);
-// Flag QMK and VIA/keyboard level EEPROM as invalid.
-// Used in bootmagic_lite() and VIA command handler.
-// Keyboard level code should not need to call this.
-void via_eeprom_reset(void);
-
// Called by QMK core to initialize dynamic keymaps etc.
+void eeconfig_init_via(void);
void via_init(void);
// Used by VIA to store and retrieve the layout options.