summaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
Diffstat (limited to 'quantum')
-rw-r--r--quantum/action.c5
-rw-r--r--quantum/action.h2
-rw-r--r--quantum/action_tapping.c178
-rw-r--r--quantum/action_tapping.h40
-rw-r--r--quantum/action_util.c52
-rw-r--r--quantum/action_util.h21
-rw-r--r--quantum/debounce.h7
-rw-r--r--quantum/debounce/asym_eager_defer_pk.c167
-rw-r--r--quantum/debounce/none.c8
-rw-r--r--quantum/debounce/sym_defer_g.c39
-rw-r--r--quantum/debounce/sym_defer_pk.c151
-rw-r--r--quantum/debounce/sym_defer_pr.c156
-rw-r--r--quantum/debounce/sym_eager_pk.c127
-rw-r--r--quantum/debounce/sym_eager_pr.c138
-rw-r--r--quantum/debounce/tests/debounce_test_common.cpp6
-rw-r--r--quantum/keyboard.c4
-rw-r--r--quantum/keycode_config.c6
-rw-r--r--quantum/keycodes.h22
-rw-r--r--quantum/led_matrix/led_matrix.c83
-rw-r--r--quantum/led_matrix/led_matrix.h4
-rw-r--r--quantum/matrix.c6
-rw-r--r--quantum/matrix_common.c6
-rw-r--r--quantum/mousekey.c3
-rw-r--r--quantum/painter/qff.h10
-rw-r--r--quantum/painter/qgf.c2
-rw-r--r--quantum/painter/qgf.h16
-rw-r--r--quantum/painter/qp_draw_core.c22
-rw-r--r--quantum/painter/qp_draw_ellipse.c2
-rw-r--r--quantum/painter/qp_internal.h8
-rw-r--r--quantum/painter/qp_internal_formats.h17
-rw-r--r--quantum/painter/rules.mk3
-rw-r--r--quantum/process_keycode/process_led_matrix.c6
-rw-r--r--quantum/process_keycode/process_rgb_matrix.c14
-rw-r--r--quantum/quantum.c4
-rw-r--r--quantum/rgb_matrix/animations/pixel_rain_anim.h25
-rw-r--r--quantum/rgb_matrix/animations/raindrops_anim.h9
-rw-r--r--quantum/rgb_matrix/rgb_matrix.c83
-rw-r--r--quantum/rgb_matrix/rgb_matrix.h4
38 files changed, 928 insertions, 528 deletions
diff --git a/quantum/action.c b/quantum/action.c
index dd82c9ec99..aacafbe2ff 100644
--- a/quantum/action.c
+++ b/quantum/action.c
@@ -281,6 +281,11 @@ void process_record(keyrecord_t *record) {
281 if (IS_NOEVENT(record->event)) { 281 if (IS_NOEVENT(record->event)) {
282 return; 282 return;
283 } 283 }
284#ifdef SPECULATIVE_HOLD
285 if (record->event.pressed) {
286 speculative_key_settled(record);
287 }
288#endif // SPECULATIVE_HOLD
284#ifdef FLOW_TAP_TERM 289#ifdef FLOW_TAP_TERM
285 flow_tap_update_last_event(record); 290 flow_tap_update_last_event(record);
286#endif // FLOW_TAP_TERM 291#endif // FLOW_TAP_TERM
diff --git a/quantum/action.h b/quantum/action.h
index 7616486c6d..a459c438c1 100644
--- a/quantum/action.h
+++ b/quantum/action.h
@@ -38,7 +38,7 @@ extern "C" {
38/* tapping count and state */ 38/* tapping count and state */
39typedef struct { 39typedef struct {
40 bool interrupted : 1; 40 bool interrupted : 1;
41 bool reserved2 : 1; 41 bool speculated : 1;
42 bool reserved1 : 1; 42 bool reserved1 : 1;
43 bool reserved0 : 1; 43 bool reserved0 : 1;
44 uint8_t count : 4; 44 uint8_t count : 4;
diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c
index b105cd60a9..5d43dd99ea 100644
--- a/quantum/action_tapping.c
+++ b/quantum/action_tapping.c
@@ -6,8 +6,10 @@
6#include "action_tapping.h" 6#include "action_tapping.h"
7#include "action_util.h" 7#include "action_util.h"
8#include "keycode.h" 8#include "keycode.h"
9#include "keycode_config.h"
9#include "quantum_keycodes.h" 10#include "quantum_keycodes.h"
10#include "timer.h" 11#include "timer.h"
12#include "wait.h"
11 13
12#ifndef NO_ACTION_TAPPING 14#ifndef NO_ACTION_TAPPING
13 15
@@ -51,6 +53,21 @@ __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *re
51} 53}
52# endif 54# endif
53 55
56# ifdef SPECULATIVE_HOLD
57typedef struct {
58 keypos_t key;
59 uint8_t mods;
60} speculative_key_t;
61# define SPECULATIVE_KEYS_SIZE 8
62static speculative_key_t speculative_keys[SPECULATIVE_KEYS_SIZE] = {};
63static uint8_t num_speculative_keys = 0;
64static uint8_t prev_speculative_mods = 0;
65static uint8_t speculative_mods = 0;
66
67/** Handler to be called on incoming press events. */
68static void speculative_key_press(keyrecord_t *record);
69# endif // SPECULATIVE_HOLD
70
54# if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM) 71# if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
55# define REGISTERED_TAPS_SIZE 8 72# define REGISTERED_TAPS_SIZE 8
56// Array of tap-hold keys that have been settled as tapped but not yet released. 73// Array of tap-hold keys that have been settled as tapped but not yet released.
@@ -129,6 +146,13 @@ static void debug_waiting_buffer(void);
129 * FIXME: Needs doc 146 * FIXME: Needs doc
130 */ 147 */
131void action_tapping_process(keyrecord_t record) { 148void action_tapping_process(keyrecord_t record) {
149# ifdef SPECULATIVE_HOLD
150 prev_speculative_mods = speculative_mods;
151 if (record.event.pressed) {
152 speculative_key_press(&record);
153 }
154# endif // SPECULATIVE_HOLD
155
132 if (process_tapping(&record)) { 156 if (process_tapping(&record)) {
133 if (IS_EVENT(record.event)) { 157 if (IS_EVENT(record.event)) {
134 ac_dprintf("processed: "); 158 ac_dprintf("processed: ");
@@ -145,6 +169,12 @@ void action_tapping_process(keyrecord_t record) {
145 } 169 }
146 } 170 }
147 171
172# ifdef SPECULATIVE_HOLD
173 if (speculative_mods != prev_speculative_mods) {
174 send_keyboard_report();
175 }
176# endif // SPECULATIVE_HOLD
177
148 // process waiting_buffer 178 // process waiting_buffer
149 if (IS_EVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) { 179 if (IS_EVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) {
150 ac_dprintf("---- action_exec: process waiting_buffer -----\n"); 180 ac_dprintf("---- action_exec: process waiting_buffer -----\n");
@@ -708,6 +738,147 @@ void waiting_buffer_scan_tap(void) {
708 } 738 }
709} 739}
710 740
741# ifdef SPECULATIVE_HOLD
742static void debug_speculative_keys(void) {
743 ac_dprintf("mods = { ");
744 for (int8_t i = 0; i < num_speculative_keys; ++i) {
745 ac_dprintf("%02X ", speculative_keys[i].mods);
746 }
747 ac_dprintf("}, keys = { ");
748 for (int8_t i = 0; i < num_speculative_keys; ++i) {
749 ac_dprintf("%02X%02X ", speculative_keys[i].key.row, speculative_keys[i].key.col);
750 }
751 ac_dprintf("}\n");
752}
753
754// Find key in speculative_keys. Returns num_speculative_keys if not found.
755static int8_t speculative_keys_find(keypos_t key) {
756 uint8_t i;
757 for (i = 0; i < num_speculative_keys; ++i) {
758 if (KEYEQ(speculative_keys[i].key, key)) {
759 break;
760 }
761 }
762 return i;
763}
764
765static void speculative_key_press(keyrecord_t *record) {
766 if (num_speculative_keys >= SPECULATIVE_KEYS_SIZE) { // Overflow!
767 ac_dprintf("SPECULATIVE KEYS OVERFLOW: IGNORING EVENT\n");
768 return; // Don't trigger: speculative_keys is full.
769 }
770 if (speculative_keys_find(record->event.key) < num_speculative_keys) {
771 return; // Don't trigger: key is already in speculative_keys.
772 }
773
774 const uint16_t keycode = get_record_keycode(record, false);
775 if (!IS_QK_MOD_TAP(keycode)) {
776 return; // Don't trigger: not a mod-tap key.
777 }
778
779 uint8_t mods = mod_config(QK_MOD_TAP_GET_MODS(keycode));
780 if ((mods & 0x10) != 0) { // Unpack 5-bit mods to 8-bit representation.
781 mods <<= 4;
782 }
783 if ((~(get_mods() | speculative_mods) & mods) == 0) {
784 return; // Don't trigger: mods are already active.
785 }
786
787 // Don't do Speculative Hold when there are non-speculated buffered events,
788 // since that could result in sending keys out of order.
789 for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
790 if (!waiting_buffer[i].tap.speculated) {
791 return;
792 }
793 }
794
795 if (get_speculative_hold(keycode, record)) {
796 record->tap.speculated = true;
797 speculative_mods |= mods;
798 // Remember the keypos and mods associated with this key.
799 speculative_keys[num_speculative_keys] = (speculative_key_t){
800 .key = record->event.key,
801 .mods = mods,
802 };
803 ++num_speculative_keys;
804
805 ac_dprintf("Speculative Hold: ");
806 debug_speculative_keys();
807 }
808}
809
810uint8_t get_speculative_mods(void) {
811 return speculative_mods;
812}
813
814__attribute__((weak)) bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) {
815 const uint8_t mods = mod_config(QK_MOD_TAP_GET_MODS(keycode));
816 return (mods & (MOD_LCTL | MOD_LSFT)) == mods;
817}
818
819void speculative_key_settled(keyrecord_t *record) {
820 if (num_speculative_keys == 0) {
821 return; // Early return when there are no active speculative keys.
822 }
823
824 uint8_t i = speculative_keys_find(record->event.key);
825
826 const uint16_t keycode = get_record_keycode(record, false);
827 if (IS_QK_MOD_TAP(keycode) && record->tap.count == 0) { // MT hold press.
828 if (i < num_speculative_keys) {
829 --num_speculative_keys;
830 const uint8_t cleared_mods = speculative_keys[i].mods;
831
832 if (num_speculative_keys) {
833 speculative_mods &= ~cleared_mods;
834 // Don't call send_keyboard_report() here; allow default
835 // handling to reapply the mod before the next report.
836
837 // Remove the ith entry from speculative_keys.
838 for (uint8_t j = i; j < num_speculative_keys; ++j) {
839 speculative_keys[j] = speculative_keys[j + 1];
840 }
841 } else {
842 speculative_mods = 0;
843 }
844
845 ac_dprintf("Speculative Hold: settled %02x, ", cleared_mods);
846 debug_speculative_keys();
847 }
848 } else { // Tap press event; cancel speculatively-held mod.
849 if (i >= num_speculative_keys) {
850 i = 0;
851 }
852
853 // Clear mods for the ith key and all keys that follow.
854 uint8_t cleared_mods = 0;
855 for (uint8_t j = i; j < num_speculative_keys; ++j) {
856 cleared_mods |= speculative_keys[j].mods;
857 }
858
859 num_speculative_keys = i; // Remove ith and following entries.
860
861 if ((prev_speculative_mods & cleared_mods) != 0) {
862# ifdef DUMMY_MOD_NEUTRALIZER_KEYCODE
863 neutralize_flashing_modifiers(get_mods() | prev_speculative_mods);
864# endif // DUMMY_MOD_NEUTRALIZER_KEYCODE
865 }
866
867 if (num_speculative_keys) {
868 speculative_mods &= ~cleared_mods;
869 } else {
870 speculative_mods = 0;
871 }
872
873 send_keyboard_report();
874 wait_ms(TAP_CODE_DELAY);
875
876 ac_dprintf("Speculative Hold: canceled %02x, ", cleared_mods);
877 debug_speculative_keys();
878 }
879}
880# endif // SPECULATIVE_HOLD
881
711# if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM) 882# if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
712static void registered_taps_add(keypos_t key) { 883static void registered_taps_add(keypos_t key) {
713 if (num_registered_taps >= REGISTERED_TAPS_SIZE) { 884 if (num_registered_taps >= REGISTERED_TAPS_SIZE) {
@@ -883,6 +1054,13 @@ static bool flow_tap_key_if_within_term(keyrecord_t *record, uint16_t prev_time)
883 return false; 1054 return false;
884} 1055}
885 1056
1057// Checks both flow_tap_expired flag and elapsed time to determine
1058// if the key is within the flow tap term.
1059bool within_flow_tap_term(uint16_t keycode, keyrecord_t *record) {
1060 uint16_t term = get_flow_tap_term(keycode, record, flow_tap_prev_keycode);
1061 return !flow_tap_expired && TIMER_DIFF_16(record->event.time, flow_tap_prev_time) <= term;
1062}
1063
886// By default, enable Flow Tap for the keys in the main alphas area and Space. 1064// By default, enable Flow Tap for the keys in the main alphas area and Space.
887// This should work reasonably even if the layout is remapped on the host to an 1065// This should work reasonably even if the layout is remapped on the host to an
888// alt layout or international layout (e.g. Dvorak or AZERTY), where these same 1066// alt layout or international layout (e.g. Dvorak or AZERTY), where these same
diff --git a/quantum/action_tapping.h b/quantum/action_tapping.h
index 0cf4aa1200..227e3330e1 100644
--- a/quantum/action_tapping.h
+++ b/quantum/action_tapping.h
@@ -46,6 +46,36 @@ bool get_permissive_hold(uint16_t keycode, keyrecord_t *record);
46bool get_retro_tapping(uint16_t keycode, keyrecord_t *record); 46bool get_retro_tapping(uint16_t keycode, keyrecord_t *record);
47bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record); 47bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record);
48 48
49#ifdef SPECULATIVE_HOLD
50/** Gets the currently active speculative mods. */
51uint8_t get_speculative_mods(void);
52
53/**
54 * Callback to say if a mod-tap key may be speculatively held.
55 *
56 * By default, speculative hold is enabled for mod-tap keys where the mod is
57 * Ctrl, Shift, and Ctrl+Shift for either hand.
58 *
59 * @param keycode Keycode of the mod-tap key.
60 * @param record Record associated with the mod-tap press event.
61 * @return True if the mod-tap key may be speculatively held.
62 */
63bool get_speculative_hold(uint16_t keycode, keyrecord_t *record);
64
65/**
66 * Handler to be called on press events after tap-holds are settled.
67 *
68 * This function is to be called in process_record() in action.c, that is, just
69 * after tap-hold events are settled as either tapped or held. When `record`
70 * corresponds to a speculatively-held key, the speculative mod is cleared.
71 *
72 * @param record Record associated with the mod-tap press event.
73 */
74void speculative_key_settled(keyrecord_t *record);
75#else
76# define get_speculative_mods() 0
77#endif // SPECULATIVE_HOLD
78
49#ifdef CHORDAL_HOLD 79#ifdef CHORDAL_HOLD
50/** 80/**
51 * Callback to say when a key chord before the tapping term may be held. 81 * Callback to say when a key chord before the tapping term may be held.
@@ -169,6 +199,16 @@ uint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t *record, uint16_t prev_
169 199
170/** Updates the Flow Tap last key and timer. */ 200/** Updates the Flow Tap last key and timer. */
171void flow_tap_update_last_event(keyrecord_t *record); 201void flow_tap_update_last_event(keyrecord_t *record);
202
203/**
204 * Checks if the pressed key is within the flow tap term.
205 * Can be used to avoid triggering combos or other actions within the flow tap term.
206 *
207 * @param keycode The keycode of the pressed key.
208 * @param record The keyrecord of the pressed key.
209 * @return True if the pressed key is within the flow tap term; false otherwise.
210 */
211bool within_flow_tap_term(uint16_t keycode, keyrecord_t *record);
172#endif // FLOW_TAP_TERM 212#endif // FLOW_TAP_TERM
173 213
174#ifdef DYNAMIC_TAPPING_TERM_ENABLE 214#ifdef DYNAMIC_TAPPING_TERM_ENABLE
diff --git a/quantum/action_util.c b/quantum/action_util.c
index e821e113ef..00cec24e3f 100644
--- a/quantum/action_util.c
+++ b/quantum/action_util.c
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
19#include "debug.h" 19#include "debug.h"
20#include "action_util.h" 20#include "action_util.h"
21#include "action_layer.h" 21#include "action_layer.h"
22#include "action_tapping.h"
22#include "timer.h" 23#include "timer.h"
23#include "keycode_config.h" 24#include "keycode_config.h"
24#include <string.h> 25#include <string.h>
@@ -46,9 +47,20 @@ extern inline void clear_keys(void);
46#ifndef NO_ACTION_ONESHOT 47#ifndef NO_ACTION_ONESHOT
47static uint8_t oneshot_mods = 0; 48static uint8_t oneshot_mods = 0;
48static uint8_t oneshot_locked_mods = 0; 49static uint8_t oneshot_locked_mods = 0;
49uint8_t get_oneshot_locked_mods(void) { 50/**
51 * @brief Retrieve current state of locked oneshot modifiers.
52 *
53 * @return Current state of the locked oneshot modifier keys as a bitmask.
54 */
55uint8_t get_oneshot_locked_mods(void) {
50 return oneshot_locked_mods; 56 return oneshot_locked_mods;
51} 57}
58/**
59 * Same as \ref get_oneshot_locked_mods but returns \ref mod_t for convenience.
60 */
61mod_t get_oneshot_locked_mod_state(void) {
62 return (mod_t)get_oneshot_locked_mods();
63}
52void add_oneshot_locked_mods(uint8_t mods) { 64void add_oneshot_locked_mods(uint8_t mods) {
53 if ((oneshot_locked_mods & mods) != mods) { 65 if ((oneshot_locked_mods & mods) != mods) {
54 oneshot_locked_mods |= mods; 66 oneshot_locked_mods |= mods;
@@ -273,6 +285,10 @@ static uint8_t get_mods_for_report(void) {
273 } 285 }
274#endif 286#endif
275 287
288#ifdef SPECULATIVE_HOLD
289 mods |= get_speculative_mods();
290#endif
291
276#ifdef KEY_OVERRIDE_ENABLE 292#ifdef KEY_OVERRIDE_ENABLE
277 // These need to be last to be able to properly control key overrides 293 // These need to be last to be able to properly control key overrides
278 mods &= ~suppressed_mods; 294 mods &= ~suppressed_mods;
@@ -326,13 +342,20 @@ void send_keyboard_report(void) {
326 send_6kro_report(); 342 send_6kro_report();
327} 343}
328 344
329/** \brief Get mods 345/**
346 * @brief Retrieve current state of modifiers.
330 * 347 *
331 * FIXME: needs doc 348 * @return Current state of the modifier keys as a bitmask.
332 */ 349 */
333uint8_t get_mods(void) { 350uint8_t get_mods(void) {
334 return real_mods; 351 return real_mods;
335} 352}
353/**
354 * Same as \ref get_mods but returns \ref mod_t for convenience.
355 */
356mod_t get_mod_state(void) {
357 return (mod_t)get_mods();
358}
336/** \brief add mods 359/** \brief add mods
337 * 360 *
338 * FIXME: needs doc 361 * FIXME: needs doc
@@ -362,13 +385,20 @@ void clear_mods(void) {
362 real_mods = 0; 385 real_mods = 0;
363} 386}
364 387
365/** \brief get weak mods 388/**
389 * @brief Retrieve current state of weak modifiers.
366 * 390 *
367 * FIXME: needs doc 391 * @return Current state of the weak modifier keys as a bitmask.
368 */ 392 */
369uint8_t get_weak_mods(void) { 393uint8_t get_weak_mods(void) {
370 return weak_mods; 394 return weak_mods;
371} 395}
396/**
397 * Same as \ref get_weak_mods but returns \ref mod_t for convenience.
398 */
399mod_t get_weak_mod_state(void) {
400 return (mod_t)get_weak_mods();
401}
372/** \brief add weak mods 402/** \brief add weak mods
373 * 403 *
374 * FIXME: needs doc 404 * FIXME: needs doc
@@ -423,14 +453,22 @@ void clear_suppressed_override_mods(void) {
423#endif 453#endif
424 454
425#ifndef NO_ACTION_ONESHOT 455#ifndef NO_ACTION_ONESHOT
426/** \brief get oneshot mods 456/**
457 * @brief Retrieve current state of oneshot modifiers.
427 * 458 *
428 * FIXME: needs doc 459 * @return Current state of the oneshot modifier keys as a bitmask.
429 */ 460 */
430uint8_t get_oneshot_mods(void) { 461uint8_t get_oneshot_mods(void) {
431 return oneshot_mods; 462 return oneshot_mods;
432} 463}
433 464
465/**
466 * Same as \ref get_oneshot_mods but returns \ref mod_t for convenience.
467 */
468mod_t get_oneshot_mod_state(void) {
469 return (mod_t)get_oneshot_mods();
470}
471
434void add_oneshot_mods(uint8_t mods) { 472void add_oneshot_mods(uint8_t mods) {
435 if ((oneshot_mods & mods) != mods) { 473 if ((oneshot_mods & mods) != mods) {
436# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) 474# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
diff --git a/quantum/action_util.h b/quantum/action_util.h
index d2ecb145be..8b1974cd32 100644
--- a/quantum/action_util.h
+++ b/quantum/action_util.h
@@ -18,6 +18,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
18#pragma once 18#pragma once
19 19
20#include <stdint.h> 20#include <stdint.h>
21
22#include "compiler_support.h"
21#include "report.h" 23#include "report.h"
22#include "modifiers.h" 24#include "modifiers.h"
23 25
@@ -25,6 +27,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
25extern "C" { 27extern "C" {
26#endif 28#endif
27 29
30typedef union {
31 uint8_t raw;
32 struct {
33 bool left_ctrl : 1;
34 bool left_shift : 1;
35 bool left_alt : 1;
36 bool left_gui : 1;
37 bool right_ctrl : 1;
38 bool right_shift : 1;
39 bool right_alt : 1;
40 bool right_gui : 1;
41 };
42} PACKED mod_t;
43STATIC_ASSERT(sizeof(mod_t) == sizeof(uint8_t), "Invalid size for 'mod_t'");
44
28extern report_keyboard_t *keyboard_report; 45extern report_keyboard_t *keyboard_report;
29#ifdef NKRO_ENABLE 46#ifdef NKRO_ENABLE
30extern report_nkro_t *nkro_report; 47extern report_nkro_t *nkro_report;
@@ -47,6 +64,7 @@ inline void clear_keys(void) {
47 64
48/* modifier */ 65/* modifier */
49uint8_t get_mods(void); 66uint8_t get_mods(void);
67mod_t get_mod_state(void);
50void add_mods(uint8_t mods); 68void add_mods(uint8_t mods);
51void del_mods(uint8_t mods); 69void del_mods(uint8_t mods);
52void set_mods(uint8_t mods); 70void set_mods(uint8_t mods);
@@ -54,6 +72,7 @@ void clear_mods(void);
54 72
55/* weak modifier */ 73/* weak modifier */
56uint8_t get_weak_mods(void); 74uint8_t get_weak_mods(void);
75mod_t get_weak_mod_state(void);
57void add_weak_mods(uint8_t mods); 76void add_weak_mods(uint8_t mods);
58void del_weak_mods(uint8_t mods); 77void del_weak_mods(uint8_t mods);
59void set_weak_mods(uint8_t mods); 78void set_weak_mods(uint8_t mods);
@@ -61,6 +80,7 @@ void clear_weak_mods(void);
61 80
62/* oneshot modifier */ 81/* oneshot modifier */
63uint8_t get_oneshot_mods(void); 82uint8_t get_oneshot_mods(void);
83mod_t get_oneshot_mod_state(void);
64void add_oneshot_mods(uint8_t mods); 84void add_oneshot_mods(uint8_t mods);
65void del_oneshot_mods(uint8_t mods); 85void del_oneshot_mods(uint8_t mods);
66void set_oneshot_mods(uint8_t mods); 86void set_oneshot_mods(uint8_t mods);
@@ -68,6 +88,7 @@ void clear_oneshot_mods(void);
68bool has_oneshot_mods_timed_out(void); 88bool has_oneshot_mods_timed_out(void);
69 89
70uint8_t get_oneshot_locked_mods(void); 90uint8_t get_oneshot_locked_mods(void);
91mod_t get_oneshot_locked_mod_state(void);
71void add_oneshot_locked_mods(uint8_t mods); 92void add_oneshot_locked_mods(uint8_t mods);
72void set_oneshot_locked_mods(uint8_t mods); 93void set_oneshot_locked_mods(uint8_t mods);
73void clear_oneshot_locked_mods(void); 94void clear_oneshot_locked_mods(void);
diff --git a/quantum/debounce.h b/quantum/debounce.h
index cea1f2b526..e26106cd3b 100644
--- a/quantum/debounce.h
+++ b/quantum/debounce.h
@@ -9,13 +9,10 @@
9 * 9 *
10 * @param raw The current key state 10 * @param raw The current key state
11 * @param cooked The debounced key state 11 * @param cooked The debounced key state
12 * @param num_rows Number of rows to debounce
13 * @param changed True if raw has changed since the last call 12 * @param changed True if raw has changed since the last call
14 * @return true Cooked has new keychanges after debouncing 13 * @return true Cooked has new keychanges after debouncing
15 * @return false Cooked is the same as before 14 * @return false Cooked is the same as before
16 */ 15 */
17bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed); 16bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed);
18 17
19void debounce_init(uint8_t num_rows); 18void debounce_init(void);
20
21void debounce_free(void);
diff --git a/quantum/debounce/asym_eager_defer_pk.c b/quantum/debounce/asym_eager_defer_pk.c
index b6fcdc3d4e..edd07eabc0 100644
--- a/quantum/debounce/asym_eager_defer_pk.c
+++ b/quantum/debounce/asym_eager_defer_pk.c
@@ -1,37 +1,15 @@
1/* 1// Copyright 2017 Alex Ong <the.onga@gmail.com>
2 * Copyright 2017 Alex Ong <the.onga@gmail.com> 2// Copyright 2020 Andrei Purdea <andrei@purdea.ro>
3 * Copyright 2020 Andrei Purdea <andrei@purdea.ro> 3// Copyright 2021 Simon Arlott
4 * Copyright 2021 Simon Arlott 4// SPDX-License-Identifier: GPL-2.0-or-later
5 * 5//
6 * This program is free software: you can redistribute it and/or modify 6// Asymetric per-key algorithm. After pressing a key, it immediately changes state,
7 * it under the terms of the GNU General Public License as published by 7// with no further inputs accepted until DEBOUNCE milliseconds have occurred. After
8 * the Free Software Foundation, either version 2 of the License, or 8// releasing a key, that state is pushed after no changes occur for DEBOUNCE milliseconds.
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/*
21Asymetric per-key algorithm. After pressing a key, it immediately changes state,
22with no further inputs accepted until DEBOUNCE milliseconds have occurred. After
23releasing a key, that state is pushed after no changes occur for DEBOUNCE milliseconds.
24*/
25 9
26#include "debounce.h" 10#include "debounce.h"
27#include "timer.h" 11#include "timer.h"
28#include <stdlib.h> 12#include "util.h"
29
30#ifdef PROTOCOL_CHIBIOS
31# if CH_CFG_USE_MEMCORE == FALSE
32# 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.
33# endif
34#endif
35 13
36#ifndef DEBOUNCE 14#ifndef DEBOUNCE
37# define DEBOUNCE 5 15# define DEBOUNCE 5
@@ -43,44 +21,29 @@ releasing a key, that state is pushed after no changes occur for DEBOUNCE millis
43# define DEBOUNCE 127 21# define DEBOUNCE 127
44#endif 22#endif
45 23
46#define ROW_SHIFTER ((matrix_row_t)1) 24#define DEBOUNCE_ELAPSED 0
47 25
26#if DEBOUNCE > 0
48typedef struct { 27typedef struct {
49 bool pressed : 1; 28 bool pressed : 1;
50 uint8_t time : 7; 29 uint8_t time : 7;
51} debounce_counter_t; 30} debounce_counter_t;
52 31
53#if DEBOUNCE > 0 32// Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards
54static debounce_counter_t *debounce_counters; 33static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED};
55static fast_timer_t last_time; 34static bool counters_need_update;
56static bool counters_need_update; 35static bool matrix_need_update;
57static bool matrix_need_update; 36static bool cooked_changed;
58static bool cooked_changed;
59
60# define DEBOUNCE_ELAPSED 0
61
62static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
63static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
64
65// we use num_rows rather than MATRIX_ROWS to support split keyboards
66void debounce_init(uint8_t num_rows) {
67 debounce_counters = malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));
68 int i = 0;
69 for (uint8_t r = 0; r < num_rows; r++) {
70 for (uint8_t c = 0; c < MATRIX_COLS; c++) {
71 debounce_counters[i++].time = DEBOUNCE_ELAPSED;
72 }
73 }
74}
75 37
76void debounce_free(void) { 38static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time);
77 free(debounce_counters); 39static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]);
78 debounce_counters = NULL;
79}
80 40
81bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { 41void debounce_init(void) {}
82 bool updated_last = false; 42
83 cooked_changed = false; 43bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
44 static fast_timer_t last_time;
45 bool updated_last = false;
46 cooked_changed = false;
84 47
85 if (counters_need_update) { 48 if (counters_need_update) {
86 fast_timer_t now = timer_read_fast(); 49 fast_timer_t now = timer_read_fast();
@@ -88,12 +51,10 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
88 51
89 last_time = now; 52 last_time = now;
90 updated_last = true; 53 updated_last = true;
91 if (elapsed_time > UINT8_MAX) {
92 elapsed_time = UINT8_MAX;
93 }
94 54
95 if (elapsed_time > 0) { 55 if (elapsed_time > 0) {
96 update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time); 56 // Update debounce counters with elapsed timer clamped to 127 (maximum debounce)
57 update_debounce_counters_and_transfer_if_expired(raw, cooked, MIN(elapsed_time, 127));
97 } 58 }
98 } 59 }
99 60
@@ -102,74 +63,96 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
102 last_time = timer_read_fast(); 63 last_time = timer_read_fast();
103 } 64 }
104 65
105 transfer_matrix_values(raw, cooked, num_rows); 66 transfer_matrix_values(raw, cooked);
106 } 67 }
107 68
108 return cooked_changed; 69 return cooked_changed;
109} 70}
110 71
111static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) { 72/**
112 debounce_counter_t *debounce_pointer = debounce_counters; 73 * @brief Processes per-key debounce counters and updates the debounced matrix state.
113 74 *
75 * This function iterates through each key in the matrix and updates its debounce counter
76 * based on the elapsed time. If the debounce period has expired, the debounced state is
77 * updated accordingly for key-down (eager) and key-up (defer) events.
78 *
79 * @param raw The current raw key state matrix.
80 * @param cooked The debounced key state matrix to be updated.
81 * @param elapsed_time The time elapsed since the last debounce update, in milliseconds.
82 */
83static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time) {
114 counters_need_update = false; 84 counters_need_update = false;
115 matrix_need_update = false; 85 matrix_need_update = false;
116 86
117 for (uint8_t row = 0; row < num_rows; row++) { 87 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
88 uint16_t row_offset = row * MATRIX_COLS;
89
118 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 90 for (uint8_t col = 0; col < MATRIX_COLS; col++) {
119 matrix_row_t col_mask = (ROW_SHIFTER << col); 91 uint16_t index = row_offset + col;
120 92
121 if (debounce_pointer->time != DEBOUNCE_ELAPSED) { 93 if (debounce_counters[index].time != DEBOUNCE_ELAPSED) {
122 if (debounce_pointer->time <= elapsed_time) { 94 if (debounce_counters[index].time <= elapsed_time) {
123 debounce_pointer->time = DEBOUNCE_ELAPSED; 95 debounce_counters[index].time = DEBOUNCE_ELAPSED;
124 96
125 if (debounce_pointer->pressed) { 97 if (debounce_counters[index].pressed) {
126 // key-down: eager 98 // key-down: eager
127 matrix_need_update = true; 99 matrix_need_update = true;
128 } else { 100 } else {
129 // key-up: defer 101 // key-up: defer
102 matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col);
130 matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask); 103 matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask);
131 cooked_changed |= cooked_next ^ cooked[row]; 104 cooked_changed |= cooked_next ^ cooked[row];
132 cooked[row] = cooked_next; 105 cooked[row] = cooked_next;
133 } 106 }
134 } else { 107 } else {
135 debounce_pointer->time -= elapsed_time; 108 debounce_counters[index].time -= elapsed_time;
136 counters_need_update = true; 109 counters_need_update = true;
137 } 110 }
138 } 111 }
139 debounce_pointer++;
140 } 112 }
141 } 113 }
142} 114}
143 115
144static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { 116/**
145 debounce_counter_t *debounce_pointer = debounce_counters; 117 * @brief Applies debounced changes to the matrix state based on per-key counters.
146 118 *
119 * This function compares the raw and cooked key state matrices to detect changes.
120 * For each key, it updates the debounce counter and the debounced state according
121 * to the debounce algorithm. Key-down events are handled eagerly, while key-up
122 * events are deferred until the debounce period has elapsed.
123 *
124 * @param raw The current raw key state matrix.
125 * @param cooked The debounced key state matrix to be updated.
126 */
127static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]) {
147 matrix_need_update = false; 128 matrix_need_update = false;
148 129
149 for (uint8_t row = 0; row < num_rows; row++) { 130 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
150 matrix_row_t delta = raw[row] ^ cooked[row]; 131 uint16_t row_offset = row * MATRIX_COLS;
132 matrix_row_t delta = raw[row] ^ cooked[row];
133
151 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 134 for (uint8_t col = 0; col < MATRIX_COLS; col++) {
152 matrix_row_t col_mask = (ROW_SHIFTER << col); 135 uint16_t index = row_offset + col;
136 matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col);
153 137
154 if (delta & col_mask) { 138 if (delta & col_mask) {
155 if (debounce_pointer->time == DEBOUNCE_ELAPSED) { 139 if (debounce_counters[index].time == DEBOUNCE_ELAPSED) {
156 debounce_pointer->pressed = (raw[row] & col_mask); 140 debounce_counters[index].pressed = (raw[row] & col_mask);
157 debounce_pointer->time = DEBOUNCE; 141 debounce_counters[index].time = DEBOUNCE;
158 counters_need_update = true; 142 counters_need_update = true;
159 143
160 if (debounce_pointer->pressed) { 144 if (debounce_counters[index].pressed) {
161 // key-down: eager 145 // key-down: eager
162 cooked[row] ^= col_mask; 146 cooked[row] ^= col_mask;
163 cooked_changed = true; 147 cooked_changed = true;
164 } 148 }
165 } 149 }
166 } else if (debounce_pointer->time != DEBOUNCE_ELAPSED) { 150 } else if (debounce_counters[index].time != DEBOUNCE_ELAPSED) {
167 if (!debounce_pointer->pressed) { 151 if (!debounce_counters[index].pressed) {
168 // key-up: defer 152 // key-up: defer
169 debounce_pointer->time = DEBOUNCE_ELAPSED; 153 debounce_counters[index].time = DEBOUNCE_ELAPSED;
170 } 154 }
171 } 155 }
172 debounce_pointer++;
173 } 156 }
174 } 157 }
175} 158}
diff --git a/quantum/debounce/none.c b/quantum/debounce/none.c
index 0a8ccfc4ee..e614f41a6b 100644
--- a/quantum/debounce/none.c
+++ b/quantum/debounce/none.c
@@ -17,13 +17,13 @@
17#include "debounce.h" 17#include "debounce.h"
18#include <string.h> 18#include <string.h>
19 19
20void debounce_init(uint8_t num_rows) {} 20void debounce_init(void) {}
21 21
22bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { 22bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
23 bool cooked_changed = false; 23 bool cooked_changed = false;
24 24
25 if (changed) { 25 if (changed) {
26 size_t matrix_size = num_rows * sizeof(matrix_row_t); 26 size_t matrix_size = MATRIX_ROWS_PER_HAND * sizeof(matrix_row_t);
27 if (memcmp(cooked, raw, matrix_size) != 0) { 27 if (memcmp(cooked, raw, matrix_size) != 0) {
28 memcpy(cooked, raw, matrix_size); 28 memcpy(cooked, raw, matrix_size);
29 cooked_changed = true; 29 cooked_changed = true;
@@ -32,5 +32,3 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
32 32
33 return cooked_changed; 33 return cooked_changed;
34} 34}
35
36void debounce_free(void) {}
diff --git a/quantum/debounce/sym_defer_g.c b/quantum/debounce/sym_defer_g.c
index d96758fab3..a60a131072 100644
--- a/quantum/debounce/sym_defer_g.c
+++ b/quantum/debounce/sym_defer_g.c
@@ -1,22 +1,10 @@
1/* 1// Copyright 2017 Alex Ong<the.onga@gmail.com>
2Copyright 2017 Alex Ong<the.onga@gmail.com> 2// Copyright 2021 Simon Arlott
3Copyright 2021 Simon Arlott 3// SPDX-License-Identifier: GPL-2.0-or-later
4This program is free software: you can redistribute it and/or modify 4//
5it under the terms of the GNU General Public License as published by 5// Basic global debounce algorithm. Used in 99% of keyboards at time of implementation
6the Free Software Foundation, either version 2 of the License, or 6// When no state changes have occured for DEBOUNCE milliseconds, we push the state.
7(at your option) any later version. 7
8This program is distributed in the hope that it will be useful,
9but WITHOUT ANY WARRANTY; without even the implied warranty of
10MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11GNU General Public License for more details.
12You should have received a copy of the GNU General Public License
13along with this program. If not, see <http://www.gnu.org/licenses/>.
14*/
15
16/*
17Basic global debounce algorithm. Used in 99% of keyboards at time of implementation
18When no state changes have occured for DEBOUNCE milliseconds, we push the state.
19*/
20#include "debounce.h" 8#include "debounce.h"
21#include "timer.h" 9#include "timer.h"
22#include <string.h> 10#include <string.h>
@@ -31,19 +19,19 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
31#endif 19#endif
32 20
33#if DEBOUNCE > 0 21#if DEBOUNCE > 0
34static bool debouncing = false;
35static fast_timer_t debouncing_time;
36 22
37void debounce_init(uint8_t num_rows) {} 23void debounce_init(void) {}
38 24
39bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { 25bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
40 bool cooked_changed = false; 26 static fast_timer_t debouncing_time;
27 static bool debouncing = false;
28 bool cooked_changed = false;
41 29
42 if (changed) { 30 if (changed) {
43 debouncing = true; 31 debouncing = true;
44 debouncing_time = timer_read_fast(); 32 debouncing_time = timer_read_fast();
45 } else if (debouncing && timer_elapsed_fast(debouncing_time) >= DEBOUNCE) { 33 } else if (debouncing && timer_elapsed_fast(debouncing_time) >= DEBOUNCE) {
46 size_t matrix_size = num_rows * sizeof(matrix_row_t); 34 size_t matrix_size = MATRIX_ROWS_PER_HAND * sizeof(matrix_row_t);
47 if (memcmp(cooked, raw, matrix_size) != 0) { 35 if (memcmp(cooked, raw, matrix_size) != 0) {
48 memcpy(cooked, raw, matrix_size); 36 memcpy(cooked, raw, matrix_size);
49 cooked_changed = true; 37 cooked_changed = true;
@@ -54,7 +42,6 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
54 return cooked_changed; 42 return cooked_changed;
55} 43}
56 44
57void debounce_free(void) {}
58#else // no debouncing. 45#else // no debouncing.
59# include "none.c" 46# include "none.c"
60#endif 47#endif
diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c
index 156535a373..b910571219 100644
--- a/quantum/debounce/sym_defer_pk.c
+++ b/quantum/debounce/sym_defer_pk.c
@@ -1,33 +1,14 @@
1/* 1// Copyright 2017 Alex Ong<the.onga@gmail.com>
2Copyright 2017 Alex Ong<the.onga@gmail.com> 2// Copyright 2020 Andrei Purdea<andrei@purdea.ro>
3Copyright 2020 Andrei Purdea<andrei@purdea.ro> 3// Copyright 2021 Simon Arlott
4Copyright 2021 Simon Arlott 4// SPDX-License-Identifier: GPL-2.0-or-later
5This program is free software: you can redistribute it and/or modify 5//
6it under the terms of the GNU General Public License as published by 6// Basic symmetric per-key algorithm. Uses an 8-bit counter per key.
7the Free Software Foundation, either version 2 of the License, or 7// When no state changes have occured for DEBOUNCE milliseconds, we push the state.
8(at your option) any later version.
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13You should have received a copy of the GNU General Public License
14along with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17/*
18Basic symmetric per-key algorithm. Uses an 8-bit counter per key.
19When no state changes have occured for DEBOUNCE milliseconds, we push the state.
20*/
21 8
22#include "debounce.h" 9#include "debounce.h"
23#include "timer.h" 10#include "timer.h"
24#include <stdlib.h> 11#include "util.h"
25
26#ifdef PROTOCOL_CHIBIOS
27# if CH_CFG_USE_MEMCORE == FALSE
28# 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.
29# endif
30#endif
31 12
32#ifndef DEBOUNCE 13#ifndef DEBOUNCE
33# define DEBOUNCE 5 14# define DEBOUNCE 5
@@ -39,40 +20,24 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
39# define DEBOUNCE UINT8_MAX 20# define DEBOUNCE UINT8_MAX
40#endif 21#endif
41 22
42#define ROW_SHIFTER ((matrix_row_t)1) 23#define DEBOUNCE_ELAPSED 0
43 24
25#if DEBOUNCE > 0
44typedef uint8_t debounce_counter_t; 26typedef uint8_t debounce_counter_t;
27// Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards
28static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED};
29static bool counters_need_update;
30static bool cooked_changed;
45 31
46#if DEBOUNCE > 0 32static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time);
47static debounce_counter_t *debounce_counters; 33static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]);
48static fast_timer_t last_time;
49static bool counters_need_update;
50static bool cooked_changed;
51
52# define DEBOUNCE_ELAPSED 0
53
54static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
55static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
56
57// we use num_rows rather than MATRIX_ROWS to support split keyboards
58void debounce_init(uint8_t num_rows) {
59 debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));
60 int i = 0;
61 for (uint8_t r = 0; r < num_rows; r++) {
62 for (uint8_t c = 0; c < MATRIX_COLS; c++) {
63 debounce_counters[i++] = DEBOUNCE_ELAPSED;
64 }
65 }
66}
67 34
68void debounce_free(void) { 35void debounce_init(void) {}
69 free(debounce_counters);
70 debounce_counters = NULL;
71}
72 36
73bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { 37bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
74 bool updated_last = false; 38 static fast_timer_t last_time;
75 cooked_changed = false; 39 bool updated_last = false;
40 cooked_changed = false;
76 41
77 if (counters_need_update) { 42 if (counters_need_update) {
78 fast_timer_t now = timer_read_fast(); 43 fast_timer_t now = timer_read_fast();
@@ -80,12 +45,10 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
80 45
81 last_time = now; 46 last_time = now;
82 updated_last = true; 47 updated_last = true;
83 if (elapsed_time > UINT8_MAX) {
84 elapsed_time = UINT8_MAX;
85 }
86 48
87 if (elapsed_time > 0) { 49 if (elapsed_time > 0) {
88 update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time); 50 // Update debounce counters with elapsed timer clamped to UINT8_MAX
51 update_debounce_counters_and_transfer_if_expired(raw, cooked, MIN(elapsed_time, UINT8_MAX));
89 } 52 }
90 } 53 }
91 54
@@ -94,47 +57,73 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
94 last_time = timer_read_fast(); 57 last_time = timer_read_fast();
95 } 58 }
96 59
97 start_debounce_counters(raw, cooked, num_rows); 60 start_debounce_counters(raw, cooked);
98 } 61 }
99 62
100 return cooked_changed; 63 return cooked_changed;
101} 64}
102 65
103static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) { 66/**
104 counters_need_update = false; 67 * @brief Updates debounce counters and transfers debounced key states if the debounce period has expired.
105 debounce_counter_t *debounce_pointer = debounce_counters; 68 *
106 for (uint8_t row = 0; row < num_rows; row++) { 69 * Iterates through each key in the matrix and checks its debounce counter. If the debounce period has expired
70 * for a key, the debounced state is updated to match the raw state. Otherwise, the debounce counter is decremented
71 * by the elapsed time and marked for further updates.
72 *
73 * @param raw The current raw key state matrix.
74 * @param cooked The debounced key state matrix to be updated.
75 * @param elapsed_time The time elapsed since the last debounce update, in milliseconds.
76 */
77static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time) {
78 counters_need_update = false;
79 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
80 uint16_t row_offset = row * MATRIX_COLS;
81
107 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 82 for (uint8_t col = 0; col < MATRIX_COLS; col++) {
108 if (*debounce_pointer != DEBOUNCE_ELAPSED) { 83 uint16_t index = row_offset + col;
109 if (*debounce_pointer <= elapsed_time) { 84
110 *debounce_pointer = DEBOUNCE_ELAPSED; 85 if (debounce_counters[index] != DEBOUNCE_ELAPSED) {
111 matrix_row_t cooked_next = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col)); 86 if (debounce_counters[index] <= elapsed_time) {
87 debounce_counters[index] = DEBOUNCE_ELAPSED;
88 matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col);
89 matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask);
112 cooked_changed |= cooked[row] ^ cooked_next; 90 cooked_changed |= cooked[row] ^ cooked_next;
113 cooked[row] = cooked_next; 91 cooked[row] = cooked_next;
114 } else { 92 } else {
115 *debounce_pointer -= elapsed_time; 93 debounce_counters[index] -= elapsed_time;
116 counters_need_update = true; 94 counters_need_update = true;
117 } 95 }
118 } 96 }
119 debounce_pointer++;
120 } 97 }
121 } 98 }
122} 99}
123 100
124static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { 101/**
125 debounce_counter_t *debounce_pointer = debounce_counters; 102 * @brief Initializes debounce counters for keys with changed states.
126 for (uint8_t row = 0; row < num_rows; row++) { 103 *
127 matrix_row_t delta = raw[row] ^ cooked[row]; 104 * For each key in the matrix, this function checks if the raw state differs from the debounced state.
105 * If a change is detected and the debounce counter has elapsed, the counter is set to the debounce period
106 * and marked for update. Otherwise, the counter is cleared.
107 *
108 * @param raw The current raw key state matrix.
109 * @param cooked The debounced key state matrix.
110 */
111static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]) {
112 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
113 uint16_t row_offset = row * MATRIX_COLS;
114 matrix_row_t delta = raw[row] ^ cooked[row];
115
128 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 116 for (uint8_t col = 0; col < MATRIX_COLS; col++) {
129 if (delta & (ROW_SHIFTER << col)) { 117 uint16_t index = row_offset + col;
130 if (*debounce_pointer == DEBOUNCE_ELAPSED) { 118
131 *debounce_pointer = DEBOUNCE; 119 if (delta & (MATRIX_ROW_SHIFTER << col)) {
132 counters_need_update = true; 120 if (debounce_counters[index] == DEBOUNCE_ELAPSED) {
121 debounce_counters[index] = DEBOUNCE;
122 counters_need_update = true;
133 } 123 }
134 } else { 124 } else {
135 *debounce_pointer = DEBOUNCE_ELAPSED; 125 debounce_counters[index] = DEBOUNCE_ELAPSED;
136 } 126 }
137 debounce_pointer++;
138 } 127 }
139 } 128 }
140} 129}
diff --git a/quantum/debounce/sym_defer_pr.c b/quantum/debounce/sym_defer_pr.c
index d6222af5b2..feaf55b08a 100644
--- a/quantum/debounce/sym_defer_pr.c
+++ b/quantum/debounce/sym_defer_pr.c
@@ -1,77 +1,117 @@
1/* 1// Copyright 2017 Alex Ong<the.onga@gmail.com>
2Copyright 2021 Chad Austin <chad@chadaustin.me> 2// Copyright 2020 Andrei Purdea<andrei@purdea.ro>
3This program is free software: you can redistribute it and/or modify 3// Copyright 2021 Simon Arlott
4it under the terms of the GNU General Public License as published by 4// Copyright @filterpaper
5the Free Software Foundation, either version 2 of the License, or 5// SPDX-License-Identifier: GPL-2.0-or-later
6(at your option) any later version. 6//
7This program is distributed in the hope that it will be useful, 7// Basic symmetric per-row algorithm. Uses an 8-bit counter per row.
8but WITHOUT ANY WARRANTY; without even the implied warranty of 8// When no state changes have occured for DEBOUNCE milliseconds, we push the state.
9MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10GNU General Public License for more details.
11You should have received a copy of the GNU General Public License
12along with this program. If not, see <http://www.gnu.org/licenses/>.
13*/
14
15/*
16Symmetric per-row debounce algorithm. Changes only apply when
17DEBOUNCE milliseconds have elapsed since the last change.
18*/
19 9
20#include "debounce.h" 10#include "debounce.h"
21#include "timer.h" 11#include "timer.h"
22#include <stdlib.h> 12#include "util.h"
23 13
24#ifndef DEBOUNCE 14#ifndef DEBOUNCE
25# define DEBOUNCE 5 15# define DEBOUNCE 5
26#endif 16#endif
27 17
28static uint16_t last_time; 18// Maximum debounce: 255ms
29// [row] milliseconds until key's state is considered debounced. 19#if DEBOUNCE > UINT8_MAX
30static uint8_t* countdowns; 20# undef DEBOUNCE
31// [row] 21# define DEBOUNCE UINT8_MAX
32static matrix_row_t* last_raw; 22#endif
33 23
34void debounce_init(uint8_t num_rows) { 24#define DEBOUNCE_ELAPSED 0
35 countdowns = (uint8_t*)calloc(num_rows, sizeof(uint8_t));
36 last_raw = (matrix_row_t*)calloc(num_rows, sizeof(matrix_row_t));
37 25
38 last_time = timer_read(); 26#if DEBOUNCE > 0
39} 27typedef uint8_t debounce_counter_t;
28// Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards
29static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND] = {DEBOUNCE_ELAPSED};
30static bool counters_need_update;
31static bool cooked_changed;
40 32
41void debounce_free(void) { 33static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time);
42 free(countdowns); 34static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]);
43 countdowns = NULL; 35
44 free(last_raw); 36void debounce_init(void) {}
45 last_raw = NULL; 37
46} 38bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
39 static fast_timer_t last_time;
40 bool updated_last = false;
41 cooked_changed = false;
47 42
48bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { 43 if (counters_need_update) {
49 uint16_t now = timer_read(); 44 fast_timer_t now = timer_read_fast();
50 uint16_t elapsed16 = TIMER_DIFF_16(now, last_time); 45 fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
51 last_time = now; 46
52 uint8_t elapsed = (elapsed16 > 255) ? 255 : elapsed16; 47 last_time = now;
53 bool cooked_changed = false; 48 updated_last = true;
54 49
55 uint8_t* countdown = countdowns; 50 if (elapsed_time > 0) {
56 51 // Update debounce counters with elapsed timer clamped to UINT8_MAX
57 for (uint8_t row = 0; row < num_rows; ++row, ++countdown) { 52 update_debounce_counters_and_transfer_if_expired(raw, cooked, MIN(elapsed_time, UINT8_MAX));
58 matrix_row_t raw_row = raw[row];
59
60 if (raw_row != last_raw[row]) {
61 *countdown = DEBOUNCE;
62 last_raw[row] = raw_row;
63 } else if (*countdown > elapsed) {
64 *countdown -= elapsed;
65 } else if (*countdown) {
66 cooked_changed |= cooked[row] ^ raw_row;
67 cooked[row] = raw_row;
68 *countdown = 0;
69 } 53 }
70 } 54 }
71 55
56 if (changed) {
57 if (!updated_last) {
58 last_time = timer_read_fast();
59 }
60
61 start_debounce_counters(raw, cooked);
62 }
63
72 return cooked_changed; 64 return cooked_changed;
73} 65}
74 66
75bool debounce_active(void) { 67/**
76 return true; 68 * @brief Updates debounce counters and transfers debounced row states if the debounce period has expired.
69 *
70 * Iterates through each row in the matrix and checks its debounce counter. If the debounce period has expired
71 * for a row, the debounced state is updated to match the raw state. Otherwise, the debounce counter is decremented
72 * by the elapsed time and marked for further updates.
73 *
74 * @param raw The current raw key state matrix.
75 * @param cooked The debounced key state matrix to be updated.
76 * @param elapsed_time The time elapsed since the last debounce update, in milliseconds.
77 */
78static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time) {
79 counters_need_update = false;
80 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
81 if (debounce_counters[row] != DEBOUNCE_ELAPSED) {
82 if (debounce_counters[row] <= elapsed_time) {
83 debounce_counters[row] = DEBOUNCE_ELAPSED;
84 cooked_changed |= cooked[row] ^ raw[row];
85 cooked[row] = raw[row];
86 } else {
87 debounce_counters[row] -= elapsed_time;
88 counters_need_update = true;
89 }
90 }
91 }
77} 92}
93
94/**
95 * @brief Initializes debounce counters for rows with changed states.
96 *
97 * For each row in the matrix, this function checks if the raw state differs from the debounced state.
98 * If a change is detected and the debounce counter has elapsed, the counter is set to the debounce period
99 * and marked for update. Otherwise, the counter is cleared.
100 *
101 * @param raw The current raw key state matrix.
102 * @param cooked The debounced key state matrix.
103 */
104static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]) {
105 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
106 if (raw[row] != cooked[row]) {
107 debounce_counters[row] = DEBOUNCE;
108 counters_need_update = true;
109 } else {
110 debounce_counters[row] = DEBOUNCE_ELAPSED;
111 }
112 }
113}
114
115#else
116# include "none.c"
117#endif
diff --git a/quantum/debounce/sym_eager_pk.c b/quantum/debounce/sym_eager_pk.c
index b359e79287..1f53330e9c 100644
--- a/quantum/debounce/sym_eager_pk.c
+++ b/quantum/debounce/sym_eager_pk.c
@@ -21,13 +21,7 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
21 21
22#include "debounce.h" 22#include "debounce.h"
23#include "timer.h" 23#include "timer.h"
24#include <stdlib.h> 24#include "util.h"
25
26#ifdef PROTOCOL_CHIBIOS
27# if CH_CFG_USE_MEMCORE == FALSE
28# 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.
29# endif
30#endif
31 25
32#ifndef DEBOUNCE 26#ifndef DEBOUNCE
33# define DEBOUNCE 5 27# define DEBOUNCE 5
@@ -39,41 +33,25 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
39# define DEBOUNCE UINT8_MAX 33# define DEBOUNCE UINT8_MAX
40#endif 34#endif
41 35
42#define ROW_SHIFTER ((matrix_row_t)1) 36#define DEBOUNCE_ELAPSED 0
43 37
38#if DEBOUNCE > 0
44typedef uint8_t debounce_counter_t; 39typedef uint8_t debounce_counter_t;
40// Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards
41static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED};
42static bool counters_need_update;
43static bool matrix_need_update;
44static bool cooked_changed;
45 45
46#if DEBOUNCE > 0 46static inline void update_debounce_counters(uint8_t elapsed_time);
47static debounce_counter_t *debounce_counters; 47static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]);
48static fast_timer_t last_time;
49static bool counters_need_update;
50static bool matrix_need_update;
51static bool cooked_changed;
52
53# define DEBOUNCE_ELAPSED 0
54
55static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
56static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
57
58// we use num_rows rather than MATRIX_ROWS to support split keyboards
59void debounce_init(uint8_t num_rows) {
60 debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));
61 int i = 0;
62 for (uint8_t r = 0; r < num_rows; r++) {
63 for (uint8_t c = 0; c < MATRIX_COLS; c++) {
64 debounce_counters[i++] = DEBOUNCE_ELAPSED;
65 }
66 }
67}
68 48
69void debounce_free(void) { 49void debounce_init(void) {}
70 free(debounce_counters);
71 debounce_counters = NULL;
72}
73 50
74bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { 51bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
75 bool updated_last = false; 52 static fast_timer_t last_time;
76 cooked_changed = false; 53 bool updated_last = false;
54 cooked_changed = false;
77 55
78 if (counters_need_update) { 56 if (counters_need_update) {
79 fast_timer_t now = timer_read_fast(); 57 fast_timer_t now = timer_read_fast();
@@ -81,12 +59,10 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
81 59
82 last_time = now; 60 last_time = now;
83 updated_last = true; 61 updated_last = true;
84 if (elapsed_time > UINT8_MAX) {
85 elapsed_time = UINT8_MAX;
86 }
87 62
88 if (elapsed_time > 0) { 63 if (elapsed_time > 0) {
89 update_debounce_counters(num_rows, elapsed_time); 64 // Update debounce counters with elapsed timer clamped to UINT8_MAX
65 update_debounce_counters(MIN(elapsed_time, UINT8_MAX));
90 } 66 }
91 } 67 }
92 68
@@ -95,51 +71,74 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
95 last_time = timer_read_fast(); 71 last_time = timer_read_fast();
96 } 72 }
97 73
98 transfer_matrix_values(raw, cooked, num_rows); 74 transfer_matrix_values(raw, cooked);
99 } 75 }
100 76
101 return cooked_changed; 77 return cooked_changed;
102} 78}
103 79
104// If the current time is > debounce counter, set the counter to enable input. 80/**
105static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) { 81 * @brief Updates per-key debounce counters and determines if matrix needs updating.
106 counters_need_update = false; 82 *
107 matrix_need_update = false; 83 * Iterates through each key in the matrix and checks its debounce counter. If the debounce
108 debounce_counter_t *debounce_pointer = debounce_counters; 84 * period has elapsed, the counter is reset and the matrix is marked for update. Otherwise,
109 for (uint8_t row = 0; row < num_rows; row++) { 85 * the counter is decremented by the elapsed time and marked for further updates if needed.
86 *
87 * @param elapsed_time The time elapsed since the last debounce update, in milliseconds.
88 */
89static inline void update_debounce_counters(uint8_t elapsed_time) {
90 counters_need_update = false;
91 matrix_need_update = false;
92
93 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
94 uint16_t row_offset = row * MATRIX_COLS;
95
110 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 96 for (uint8_t col = 0; col < MATRIX_COLS; col++) {
111 if (*debounce_pointer != DEBOUNCE_ELAPSED) { 97 uint16_t index = row_offset + col;
112 if (*debounce_pointer <= elapsed_time) { 98
113 *debounce_pointer = DEBOUNCE_ELAPSED; 99 if (debounce_counters[index] != DEBOUNCE_ELAPSED) {
114 matrix_need_update = true; 100 if (debounce_counters[index] <= elapsed_time) {
101 debounce_counters[index] = DEBOUNCE_ELAPSED;
102 matrix_need_update = true;
115 } else { 103 } else {
116 *debounce_pointer -= elapsed_time; 104 debounce_counters[index] -= elapsed_time;
117 counters_need_update = true; 105 counters_need_update = true;
118 } 106 }
119 } 107 }
120 debounce_pointer++;
121 } 108 }
122 } 109 }
123} 110}
124 111
125// upload from raw_matrix to final matrix; 112/**
126static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { 113 * @brief Transfers debounced key states from the raw matrix to the cooked matrix.
127 matrix_need_update = false; 114 *
128 debounce_counter_t *debounce_pointer = debounce_counters; 115 * For each key in the matrix, this function checks if its state has changed and if its
129 for (uint8_t row = 0; row < num_rows; row++) { 116 * debounce counter has elapsed. If so, the debounce counter is reset, the cooked matrix
117 * is updated to reflect the new state, and the matrix is marked for further updates.
118 *
119 * @param raw The current raw key state matrix.
120 * @param cooked The debounced key state matrix to be updated.
121 */
122static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]) {
123 matrix_need_update = false;
124
125 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
126 uint16_t row_offset = row * MATRIX_COLS;
130 matrix_row_t delta = raw[row] ^ cooked[row]; 127 matrix_row_t delta = raw[row] ^ cooked[row];
131 matrix_row_t existing_row = cooked[row]; 128 matrix_row_t existing_row = cooked[row];
129
132 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 130 for (uint8_t col = 0; col < MATRIX_COLS; col++) {
133 matrix_row_t col_mask = (ROW_SHIFTER << col); 131 uint16_t index = row_offset + col;
132
133 matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col);
134 if (delta & col_mask) { 134 if (delta & col_mask) {
135 if (*debounce_pointer == DEBOUNCE_ELAPSED) { 135 if (debounce_counters[index] == DEBOUNCE_ELAPSED) {
136 *debounce_pointer = DEBOUNCE; 136 debounce_counters[index] = DEBOUNCE;
137 counters_need_update = true; 137 counters_need_update = true;
138 existing_row ^= col_mask; // flip the bit. 138 existing_row ^= col_mask; // flip the bit.
139 cooked_changed = true; 139 cooked_changed = true;
140 } 140 }
141 } 141 }
142 debounce_pointer++;
143 } 142 }
144 cooked[row] = existing_row; 143 cooked[row] = existing_row;
145 } 144 }
diff --git a/quantum/debounce/sym_eager_pr.c b/quantum/debounce/sym_eager_pr.c
index 6cd9308aff..c929ff53dc 100644
--- a/quantum/debounce/sym_eager_pr.c
+++ b/quantum/debounce/sym_eager_pr.c
@@ -1,33 +1,14 @@
1/* 1// Copyright 2017 Alex Ong<the.onga@gmail.com>
2Copyright 2019 Alex Ong<the.onga@gmail.com> 2// Copyright 2021 Simon Arlott
3Copyright 2021 Simon Arlott 3// SPDX-License-Identifier: GPL-2.0-or-later
4This program is free software: you can redistribute it and/or modify 4//
5it under the terms of the GNU General Public License as published by 5// Basic per-row algorithm. Uses an 8-bit counter per key.
6the Free Software Foundation, either version 2 of the License, or 6// After pressing a key, it immediately changes state, and sets a counter.
7(at your option) any later version. 7// No further inputs are accepted until DEBOUNCE milliseconds have occurred.
8This program is distributed in the hope that it will be useful,
9but WITHOUT ANY WARRANTY; without even the implied warranty of
10MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11GNU General Public License for more details.
12You should have received a copy of the GNU General Public License
13along with this program. If not, see <http://www.gnu.org/licenses/>.
14*/
15
16/*
17Basic per-row algorithm. Uses an 8-bit counter per row.
18After pressing a key, it immediately changes state, and sets a counter.
19No further inputs are accepted until DEBOUNCE milliseconds have occurred.
20*/
21 8
22#include "debounce.h" 9#include "debounce.h"
23#include "timer.h" 10#include "timer.h"
24#include <stdlib.h> 11#include "util.h"
25
26#ifdef PROTOCOL_CHIBIOS
27# if CH_CFG_USE_MEMCORE == FALSE
28# 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.
29# endif
30#endif
31 12
32#ifndef DEBOUNCE 13#ifndef DEBOUNCE
33# define DEBOUNCE 5 14# define DEBOUNCE 5
@@ -39,37 +20,25 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
39# define DEBOUNCE UINT8_MAX 20# define DEBOUNCE UINT8_MAX
40#endif 21#endif
41 22
42typedef uint8_t debounce_counter_t; 23#define DEBOUNCE_ELAPSED 0
43 24
44#if DEBOUNCE > 0 25#if DEBOUNCE > 0
45static bool matrix_need_update; 26typedef uint8_t debounce_counter_t;
46 27// Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards
47static debounce_counter_t *debounce_counters; 28static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND] = {DEBOUNCE_ELAPSED};
48static fast_timer_t last_time; 29static bool counters_need_update;
49static bool counters_need_update; 30static bool matrix_need_update;
50static bool cooked_changed; 31static bool cooked_changed;
51
52# define DEBOUNCE_ELAPSED 0
53
54static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
55static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
56 32
57// we use num_rows rather than MATRIX_ROWS to support split keyboards 33static inline void update_debounce_counters(uint8_t elapsed_time);
58void debounce_init(uint8_t num_rows) { 34static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]);
59 debounce_counters = (debounce_counter_t *)malloc(num_rows * sizeof(debounce_counter_t));
60 for (uint8_t r = 0; r < num_rows; r++) {
61 debounce_counters[r] = DEBOUNCE_ELAPSED;
62 }
63}
64 35
65void debounce_free(void) { 36void debounce_init(void) {}
66 free(debounce_counters);
67 debounce_counters = NULL;
68}
69 37
70bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { 38bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
71 bool updated_last = false; 39 static fast_timer_t last_time;
72 cooked_changed = false; 40 bool updated_last = false;
41 cooked_changed = false;
73 42
74 if (counters_need_update) { 43 if (counters_need_update) {
75 fast_timer_t now = timer_read_fast(); 44 fast_timer_t now = timer_read_fast();
@@ -77,12 +46,10 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
77 46
78 last_time = now; 47 last_time = now;
79 updated_last = true; 48 updated_last = true;
80 if (elapsed_time > UINT8_MAX) {
81 elapsed_time = UINT8_MAX;
82 }
83 49
84 if (elapsed_time > 0) { 50 if (elapsed_time > 0) {
85 update_debounce_counters(num_rows, elapsed_time); 51 // Update debounce counters with elapsed timer clamped to UINT8_MAX
52 update_debounce_counters(MIN(elapsed_time, UINT8_MAX));
86 } 53 }
87 } 54 }
88 55
@@ -91,49 +58,64 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
91 last_time = timer_read_fast(); 58 last_time = timer_read_fast();
92 } 59 }
93 60
94 transfer_matrix_values(raw, cooked, num_rows); 61 transfer_matrix_values(raw, cooked);
95 } 62 }
96 63
97 return cooked_changed; 64 return cooked_changed;
98} 65}
99 66
100// If the current time is > debounce counter, set the counter to enable input. 67/**
101static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) { 68 * @brief Updates per-row debounce counters and determines if matrix needs updating.
102 counters_need_update = false; 69 *
103 matrix_need_update = false; 70 * Iterates through each row in the matrix and checks its debounce counter. If the debounce
104 debounce_counter_t *debounce_pointer = debounce_counters; 71 * period has elapsed, the counter is reset and the matrix is marked for update. Otherwise,
105 for (uint8_t row = 0; row < num_rows; row++) { 72 * the counter is decremented by the elapsed time and marked for further updates if needed.
106 if (*debounce_pointer != DEBOUNCE_ELAPSED) { 73 *
107 if (*debounce_pointer <= elapsed_time) { 74 * @param elapsed_time The time elapsed since the last debounce update, in milliseconds.
108 *debounce_pointer = DEBOUNCE_ELAPSED; 75 */
109 matrix_need_update = true; 76static inline void update_debounce_counters(uint8_t elapsed_time) {
77 counters_need_update = false;
78 matrix_need_update = false;
79
80 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
81 if (debounce_counters[row] != DEBOUNCE_ELAPSED) {
82 if (debounce_counters[row] <= elapsed_time) {
83 debounce_counters[row] = DEBOUNCE_ELAPSED;
84 matrix_need_update = true;
110 } else { 85 } else {
111 *debounce_pointer -= elapsed_time; 86 debounce_counters[row] -= elapsed_time;
112 counters_need_update = true; 87 counters_need_update = true;
113 } 88 }
114 } 89 }
115 debounce_pointer++;
116 } 90 }
117} 91}
118 92
119// upload from raw_matrix to final matrix; 93/**
120static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { 94 * @brief Transfers debounced key states from the raw matrix to the cooked matrix.
121 matrix_need_update = false; 95 *
122 debounce_counter_t *debounce_pointer = debounce_counters; 96 * For each row in the matrix, this function checks if its state has changed and if its
123 for (uint8_t row = 0; row < num_rows; row++) { 97 * debounce counter has elapsed. If so, the debounce counter is reset, the cooked matrix
98 * is updated to reflect the new state, and the matrix is marked for further updates.
99 *
100 * @param raw The current raw key state matrix.
101 * @param cooked The debounced key state matrix
102 */
103static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]) {
104 matrix_need_update = false;
105
106 for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
124 matrix_row_t existing_row = cooked[row]; 107 matrix_row_t existing_row = cooked[row];
125 matrix_row_t raw_row = raw[row]; 108 matrix_row_t raw_row = raw[row];
126 109
127 // determine new value basd on debounce pointer + raw value 110 // determine new value basd on debounce pointer + raw value
128 if (existing_row != raw_row) { 111 if (existing_row != raw_row) {
129 if (*debounce_pointer == DEBOUNCE_ELAPSED) { 112 if (debounce_counters[row] == DEBOUNCE_ELAPSED) {
130 *debounce_pointer = DEBOUNCE; 113 debounce_counters[row] = DEBOUNCE;
131 cooked_changed |= cooked[row] ^ raw_row; 114 cooked_changed |= cooked[row] ^ raw_row;
132 cooked[row] = raw_row; 115 cooked[row] = raw_row;
133 counters_need_update = true; 116 counters_need_update = true;
134 } 117 }
135 } 118 }
136 debounce_pointer++;
137 } 119 }
138} 120}
139 121
diff --git a/quantum/debounce/tests/debounce_test_common.cpp b/quantum/debounce/tests/debounce_test_common.cpp
index fd4b6f01a6..84b91f85e1 100644
--- a/quantum/debounce/tests/debounce_test_common.cpp
+++ b/quantum/debounce/tests/debounce_test_common.cpp
@@ -60,7 +60,7 @@ void DebounceTest::runEventsInternal() {
60 bool first = true; 60 bool first = true;
61 61
62 /* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */ 62 /* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */
63 debounce_init(MATRIX_ROWS); 63 debounce_init();
64 set_time(time_offset_); 64 set_time(time_offset_);
65 simulate_async_tick(async_time_jumps_); 65 simulate_async_tick(async_time_jumps_);
66 std::fill(std::begin(input_matrix_), std::end(input_matrix_), 0); 66 std::fill(std::begin(input_matrix_), std::end(input_matrix_), 0);
@@ -121,8 +121,6 @@ void DebounceTest::runEventsInternal() {
121 checkCookedMatrix(false, "debounce() modified cooked matrix"); 121 checkCookedMatrix(false, "debounce() modified cooked matrix");
122 advance_time(1); 122 advance_time(1);
123 } 123 }
124
125 debounce_free();
126} 124}
127 125
128void DebounceTest::runDebounce(bool changed) { 126void DebounceTest::runDebounce(bool changed) {
@@ -131,7 +129,7 @@ void DebounceTest::runDebounce(bool changed) {
131 129
132 reset_access_counter(); 130 reset_access_counter();
133 131
134 bool cooked_changed = debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed); 132 bool cooked_changed = debounce(raw_matrix_, cooked_matrix_, changed);
135 133
136 if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) { 134 if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) {
137 FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime() << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) << "\nraw_matrix:\n" << strMatrix(raw_matrix_); 135 FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime() << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) << "\nraw_matrix:\n" << strMatrix(raw_matrix_);
diff --git a/quantum/keyboard.c b/quantum/keyboard.c
index 173c696e2d..ce8c8efa68 100644
--- a/quantum/keyboard.c
+++ b/quantum/keyboard.c
@@ -29,6 +29,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
29#include "debug.h" 29#include "debug.h"
30#include "command.h" 30#include "command.h"
31#include "util.h" 31#include "util.h"
32#include "host.h"
32#include "sendchar.h" 33#include "sendchar.h"
33#include "eeconfig.h" 34#include "eeconfig.h"
34#include "action_layer.h" 35#include "action_layer.h"
@@ -471,6 +472,7 @@ void keyboard_init(void) {
471#ifdef CONNECTION_ENABLE 472#ifdef CONNECTION_ENABLE
472 connection_init(); 473 connection_init();
473#endif 474#endif
475 host_init();
474 led_init_ports(); 476 led_init_ports();
475#ifdef BACKLIGHT_ENABLE 477#ifdef BACKLIGHT_ENABLE
476 backlight_init_ports(); 478 backlight_init_ports();
@@ -699,6 +701,8 @@ void quantum_task(void) {
699#ifdef LAYER_LOCK_ENABLE 701#ifdef LAYER_LOCK_ENABLE
700 layer_lock_task(); 702 layer_lock_task();
701#endif 703#endif
704
705 host_task();
702} 706}
703 707
704/** \brief Main task that is repeatedly called as fast as possible. */ 708/** \brief Main task that is repeatedly called as fast as possible. */
diff --git a/quantum/keycode_config.c b/quantum/keycode_config.c
index cbfbcc8140..f5068902d5 100644
--- a/quantum/keycode_config.c
+++ b/quantum/keycode_config.c
@@ -24,6 +24,7 @@ keymap_config_t keymap_config;
24 * and will return the corrected keycode, when appropriate. 24 * and will return the corrected keycode, when appropriate.
25 */ 25 */
26__attribute__((weak)) uint16_t keycode_config(uint16_t keycode) { 26__attribute__((weak)) uint16_t keycode_config(uint16_t keycode) {
27#ifdef MAGIC_ENABLE
27 switch (keycode) { 28 switch (keycode) {
28 case KC_CAPS_LOCK: 29 case KC_CAPS_LOCK:
29 case KC_LOCKING_CAPS_LOCK: 30 case KC_LOCKING_CAPS_LOCK:
@@ -115,6 +116,9 @@ __attribute__((weak)) uint16_t keycode_config(uint16_t keycode) {
115 default: 116 default:
116 return keycode; 117 return keycode;
117 } 118 }
119#else
120 return keycode;
121#endif // MAGIC_ENABLE
118} 122}
119 123
120/** \brief mod_config 124/** \brief mod_config
@@ -124,6 +128,7 @@ __attribute__((weak)) uint16_t keycode_config(uint16_t keycode) {
124 */ 128 */
125 129
126__attribute__((weak)) uint8_t mod_config(uint8_t mod) { 130__attribute__((weak)) uint8_t mod_config(uint8_t mod) {
131#ifdef MAGIC_ENABLE
127 /** 132 /**
128 * Note: This function is for the 5-bit packed mods, NOT the full 8-bit mods. 133 * Note: This function is for the 5-bit packed mods, NOT the full 8-bit mods.
129 * More info about the mods can be seen in modifiers.h. 134 * More info about the mods can be seen in modifiers.h.
@@ -161,5 +166,6 @@ __attribute__((weak)) uint8_t mod_config(uint8_t mod) {
161 mod &= ~MOD_RGUI; 166 mod &= ~MOD_RGUI;
162 } 167 }
163 168
169#endif // MAGIC_ENABLE
164 return mod; 170 return mod;
165} 171}
diff --git a/quantum/keycodes.h b/quantum/keycodes.h
index 6a59aa376d..e5a64d9a71 100644
--- a/quantum/keycodes.h
+++ b/quantum/keycodes.h
@@ -26,11 +26,11 @@
26#pragma once 26#pragma once
27// clang-format off 27// clang-format off
28 28
29#define QMK_KEYCODES_VERSION "0.0.7" 29#define QMK_KEYCODES_VERSION "0.0.8"
30#define QMK_KEYCODES_VERSION_BCD 0x00000007 30#define QMK_KEYCODES_VERSION_BCD 0x00000008
31#define QMK_KEYCODES_VERSION_MAJOR 0 31#define QMK_KEYCODES_VERSION_MAJOR 0
32#define QMK_KEYCODES_VERSION_MINOR 0 32#define QMK_KEYCODES_VERSION_MINOR 0
33#define QMK_KEYCODES_VERSION_PATCH 7 33#define QMK_KEYCODES_VERSION_PATCH 8
34 34
35enum qk_keycode_ranges { 35enum qk_keycode_ranges {
36// Ranges 36// Ranges
@@ -663,6 +663,8 @@ enum qk_keycode_defines {
663 QK_LED_MATRIX_BRIGHTNESS_DOWN = 0x7816, 663 QK_LED_MATRIX_BRIGHTNESS_DOWN = 0x7816,
664 QK_LED_MATRIX_SPEED_UP = 0x7817, 664 QK_LED_MATRIX_SPEED_UP = 0x7817,
665 QK_LED_MATRIX_SPEED_DOWN = 0x7818, 665 QK_LED_MATRIX_SPEED_DOWN = 0x7818,
666 QK_LED_MATRIX_FLAG_NEXT = 0x7819,
667 QK_LED_MATRIX_FLAG_PREVIOUS = 0x781A,
666 QK_UNDERGLOW_TOGGLE = 0x7820, 668 QK_UNDERGLOW_TOGGLE = 0x7820,
667 QK_UNDERGLOW_MODE_NEXT = 0x7821, 669 QK_UNDERGLOW_MODE_NEXT = 0x7821,
668 QK_UNDERGLOW_MODE_PREVIOUS = 0x7822, 670 QK_UNDERGLOW_MODE_PREVIOUS = 0x7822,
@@ -697,6 +699,8 @@ enum qk_keycode_defines {
697 QK_RGB_MATRIX_VALUE_DOWN = 0x784A, 699 QK_RGB_MATRIX_VALUE_DOWN = 0x784A,
698 QK_RGB_MATRIX_SPEED_UP = 0x784B, 700 QK_RGB_MATRIX_SPEED_UP = 0x784B,
699 QK_RGB_MATRIX_SPEED_DOWN = 0x784C, 701 QK_RGB_MATRIX_SPEED_DOWN = 0x784C,
702 QK_RGB_MATRIX_FLAG_NEXT = 0x784D,
703 QK_RGB_MATRIX_FLAG_PREVIOUS = 0x784E,
700 QK_BOOTLOADER = 0x7C00, 704 QK_BOOTLOADER = 0x7C00,
701 QK_REBOOT = 0x7C01, 705 QK_REBOOT = 0x7C01,
702 QK_DEBUG_TOGGLE = 0x7C02, 706 QK_DEBUG_TOGGLE = 0x7C02,
@@ -1352,6 +1356,8 @@ enum qk_keycode_defines {
1352 LM_BRID = QK_LED_MATRIX_BRIGHTNESS_DOWN, 1356 LM_BRID = QK_LED_MATRIX_BRIGHTNESS_DOWN,
1353 LM_SPDU = QK_LED_MATRIX_SPEED_UP, 1357 LM_SPDU = QK_LED_MATRIX_SPEED_UP,
1354 LM_SPDD = QK_LED_MATRIX_SPEED_DOWN, 1358 LM_SPDD = QK_LED_MATRIX_SPEED_DOWN,
1359 LM_FLGN = QK_LED_MATRIX_FLAG_NEXT,
1360 LM_FLGP = QK_LED_MATRIX_FLAG_PREVIOUS,
1355 UG_TOGG = QK_UNDERGLOW_TOGGLE, 1361 UG_TOGG = QK_UNDERGLOW_TOGGLE,
1356 UG_NEXT = QK_UNDERGLOW_MODE_NEXT, 1362 UG_NEXT = QK_UNDERGLOW_MODE_NEXT,
1357 UG_PREV = QK_UNDERGLOW_MODE_PREVIOUS, 1363 UG_PREV = QK_UNDERGLOW_MODE_PREVIOUS,
@@ -1386,6 +1392,8 @@ enum qk_keycode_defines {
1386 RM_VALD = QK_RGB_MATRIX_VALUE_DOWN, 1392 RM_VALD = QK_RGB_MATRIX_VALUE_DOWN,
1387 RM_SPDU = QK_RGB_MATRIX_SPEED_UP, 1393 RM_SPDU = QK_RGB_MATRIX_SPEED_UP,
1388 RM_SPDD = QK_RGB_MATRIX_SPEED_DOWN, 1394 RM_SPDD = QK_RGB_MATRIX_SPEED_DOWN,
1395 RM_FLGN = QK_RGB_MATRIX_FLAG_NEXT,
1396 RM_FLGP = QK_RGB_MATRIX_FLAG_PREVIOUS,
1389 QK_BOOT = QK_BOOTLOADER, 1397 QK_BOOT = QK_BOOTLOADER,
1390 QK_RBT = QK_REBOOT, 1398 QK_RBT = QK_REBOOT,
1391 DB_TOGG = QK_DEBUG_TOGGLE, 1399 DB_TOGG = QK_DEBUG_TOGGLE,
@@ -1511,10 +1519,10 @@ enum qk_keycode_defines {
1511#define IS_MACRO_KEYCODE(code) ((code) >= QK_MACRO_0 && (code) <= QK_MACRO_31) 1519#define IS_MACRO_KEYCODE(code) ((code) >= QK_MACRO_0 && (code) <= QK_MACRO_31)
1512#define IS_CONNECTION_KEYCODE(code) ((code) >= QK_OUTPUT_AUTO && (code) <= QK_BLUETOOTH_PROFILE5) 1520#define IS_CONNECTION_KEYCODE(code) ((code) >= QK_OUTPUT_AUTO && (code) <= QK_BLUETOOTH_PROFILE5)
1513#define IS_BACKLIGHT_KEYCODE(code) ((code) >= QK_BACKLIGHT_ON && (code) <= QK_BACKLIGHT_TOGGLE_BREATHING) 1521#define IS_BACKLIGHT_KEYCODE(code) ((code) >= QK_BACKLIGHT_ON && (code) <= QK_BACKLIGHT_TOGGLE_BREATHING)
1514#define IS_LED_MATRIX_KEYCODE(code) ((code) >= QK_LED_MATRIX_ON && (code) <= QK_LED_MATRIX_SPEED_DOWN) 1522#define IS_LED_MATRIX_KEYCODE(code) ((code) >= QK_LED_MATRIX_ON && (code) <= QK_LED_MATRIX_FLAG_PREVIOUS)
1515#define IS_UNDERGLOW_KEYCODE(code) ((code) >= QK_UNDERGLOW_TOGGLE && (code) <= QK_UNDERGLOW_SPEED_DOWN) 1523#define IS_UNDERGLOW_KEYCODE(code) ((code) >= QK_UNDERGLOW_TOGGLE && (code) <= QK_UNDERGLOW_SPEED_DOWN)
1516#define IS_RGB_KEYCODE(code) ((code) >= RGB_MODE_PLAIN && (code) <= RGB_MODE_TWINKLE) 1524#define IS_RGB_KEYCODE(code) ((code) >= RGB_MODE_PLAIN && (code) <= RGB_MODE_TWINKLE)
1517#define IS_RGB_MATRIX_KEYCODE(code) ((code) >= QK_RGB_MATRIX_ON && (code) <= QK_RGB_MATRIX_SPEED_DOWN) 1525#define IS_RGB_MATRIX_KEYCODE(code) ((code) >= QK_RGB_MATRIX_ON && (code) <= QK_RGB_MATRIX_FLAG_PREVIOUS)
1518#define IS_QUANTUM_KEYCODE(code) ((code) >= QK_BOOTLOADER && (code) <= QK_LAYER_LOCK) 1526#define IS_QUANTUM_KEYCODE(code) ((code) >= QK_BOOTLOADER && (code) <= QK_LAYER_LOCK)
1519#define IS_KB_KEYCODE(code) ((code) >= QK_KB_0 && (code) <= QK_KB_31) 1527#define IS_KB_KEYCODE(code) ((code) >= QK_KB_0 && (code) <= QK_KB_31)
1520#define IS_USER_KEYCODE(code) ((code) >= QK_USER_0 && (code) <= QK_USER_31) 1528#define IS_USER_KEYCODE(code) ((code) >= QK_USER_0 && (code) <= QK_USER_31)
@@ -1537,10 +1545,10 @@ enum qk_keycode_defines {
1537#define MACRO_KEYCODE_RANGE QK_MACRO_0 ... QK_MACRO_31 1545#define MACRO_KEYCODE_RANGE QK_MACRO_0 ... QK_MACRO_31
1538#define CONNECTION_KEYCODE_RANGE QK_OUTPUT_AUTO ... QK_BLUETOOTH_PROFILE5 1546#define CONNECTION_KEYCODE_RANGE QK_OUTPUT_AUTO ... QK_BLUETOOTH_PROFILE5
1539#define BACKLIGHT_KEYCODE_RANGE QK_BACKLIGHT_ON ... QK_BACKLIGHT_TOGGLE_BREATHING 1547#define BACKLIGHT_KEYCODE_RANGE QK_BACKLIGHT_ON ... QK_BACKLIGHT_TOGGLE_BREATHING
1540#define LED_MATRIX_KEYCODE_RANGE QK_LED_MATRIX_ON ... QK_LED_MATRIX_SPEED_DOWN 1548#define LED_MATRIX_KEYCODE_RANGE QK_LED_MATRIX_ON ... QK_LED_MATRIX_FLAG_PREVIOUS
1541#define UNDERGLOW_KEYCODE_RANGE QK_UNDERGLOW_TOGGLE ... QK_UNDERGLOW_SPEED_DOWN 1549#define UNDERGLOW_KEYCODE_RANGE QK_UNDERGLOW_TOGGLE ... QK_UNDERGLOW_SPEED_DOWN
1542#define RGB_KEYCODE_RANGE RGB_MODE_PLAIN ... RGB_MODE_TWINKLE 1550#define RGB_KEYCODE_RANGE RGB_MODE_PLAIN ... RGB_MODE_TWINKLE
1543#define RGB_MATRIX_KEYCODE_RANGE QK_RGB_MATRIX_ON ... QK_RGB_MATRIX_SPEED_DOWN 1551#define RGB_MATRIX_KEYCODE_RANGE QK_RGB_MATRIX_ON ... QK_RGB_MATRIX_FLAG_PREVIOUS
1544#define QUANTUM_KEYCODE_RANGE QK_BOOTLOADER ... QK_LAYER_LOCK 1552#define QUANTUM_KEYCODE_RANGE QK_BOOTLOADER ... QK_LAYER_LOCK
1545#define KB_KEYCODE_RANGE QK_KB_0 ... QK_KB_31 1553#define KB_KEYCODE_RANGE QK_KB_0 ... QK_KB_31
1546#define USER_KEYCODE_RANGE QK_USER_0 ... QK_USER_31 1554#define USER_KEYCODE_RANGE QK_USER_0 ... QK_USER_31
diff --git a/quantum/led_matrix/led_matrix.c b/quantum/led_matrix/led_matrix.c
index 9c8004cc17..715d520d1c 100644
--- a/quantum/led_matrix/led_matrix.c
+++ b/quantum/led_matrix/led_matrix.c
@@ -70,12 +70,20 @@ uint8_t g_led_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}};
70last_hit_t g_last_hit_tracker; 70last_hit_t g_last_hit_tracker;
71#endif // LED_MATRIX_KEYREACTIVE_ENABLED 71#endif // LED_MATRIX_KEYREACTIVE_ENABLED
72 72
73#ifndef LED_MATRIX_FLAG_STEPS
74# define LED_MATRIX_FLAG_STEPS \
75 { LED_FLAG_ALL, LED_FLAG_KEYLIGHT | LED_FLAG_MODIFIER, LED_FLAG_NONE }
76#endif
77static const uint8_t led_matrix_flag_steps[] = LED_MATRIX_FLAG_STEPS;
78#define LED_MATRIX_FLAG_STEPS_COUNT ARRAY_SIZE(led_matrix_flag_steps)
79
73// internals 80// internals
74static bool suspend_state = false; 81static bool suspend_state = false;
75static uint8_t led_last_enable = UINT8_MAX; 82static uint8_t led_last_enable = UINT8_MAX;
76static uint8_t led_last_effect = UINT8_MAX; 83static uint8_t led_last_effect = UINT8_MAX;
77static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false}; 84static uint8_t led_current_effect = 0;
78static led_task_states led_task_state = SYNCING; 85static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false};
86static led_task_states led_task_state = SYNCING;
79 87
80// double buffers 88// double buffers
81static uint32_t led_timer_buffer; 89static uint32_t led_timer_buffer;
@@ -261,6 +269,17 @@ static void led_task_start(void) {
261 g_last_hit_tracker = last_hit_buffer; 269 g_last_hit_tracker = last_hit_buffer;
262#endif // LED_MATRIX_KEYREACTIVE_ENABLED 270#endif // LED_MATRIX_KEYREACTIVE_ENABLED
263 271
272 // Ideally we would also stop sending zeros to the LED driver PWM buffers
273 // while suspended and just do a software shutdown. This is a cheap hack for now.
274 bool suspend_backlight = suspend_state ||
275#if LED_MATRIX_TIMEOUT > 0
276 (last_input_activity_elapsed() > (uint32_t)LED_MATRIX_TIMEOUT) ||
277#endif // LED_MATRIX_TIMEOUT > 0
278 false;
279
280 // Set effect to be renedered
281 led_current_effect = suspend_backlight || !led_matrix_eeconfig.enable ? 0 : led_matrix_eeconfig.mode;
282
264 // next task 283 // next task
265 led_task_state = RENDERING; 284 led_task_state = RENDERING;
266} 285}
@@ -342,15 +361,7 @@ static void led_task_flush(uint8_t effect) {
342void led_matrix_task(void) { 361void led_matrix_task(void) {
343 led_task_timers(); 362 led_task_timers();
344 363
345 // Ideally we would also stop sending zeros to the LED driver PWM buffers 364 uint8_t effect = led_current_effect;
346 // while suspended and just do a software shutdown. This is a cheap hack for now.
347 bool suspend_backlight = suspend_state ||
348#if LED_MATRIX_TIMEOUT > 0
349 (last_input_activity_elapsed() > (uint32_t)LED_MATRIX_TIMEOUT) ||
350#endif // LED_MATRIX_TIMEOUT > 0
351 false;
352
353 uint8_t effect = suspend_backlight || !led_matrix_eeconfig.enable ? 0 : led_matrix_eeconfig.mode;
354 365
355 switch (led_task_state) { 366 switch (led_task_state) {
356 case STARTING: 367 case STARTING:
@@ -661,6 +672,50 @@ void led_matrix_set_flags_noeeprom(led_flags_t flags) {
661 led_matrix_set_flags_eeprom_helper(flags, false); 672 led_matrix_set_flags_eeprom_helper(flags, false);
662} 673}
663 674
675void led_matrix_flags_step_helper(bool write_to_eeprom) {
676 led_flags_t flags = led_matrix_get_flags();
677
678 uint8_t next = 0;
679 for (uint8_t i = 0; i < LED_MATRIX_FLAG_STEPS_COUNT; i++) {
680 if (led_matrix_flag_steps[i] == flags) {
681 next = i == LED_MATRIX_FLAG_STEPS_COUNT - 1 ? 0 : i + 1;
682 break;
683 }
684 }
685
686 led_matrix_set_flags_eeprom_helper(led_matrix_flag_steps[next], write_to_eeprom);
687}
688
689void led_matrix_flags_step_noeeprom(void) {
690 led_matrix_flags_step_helper(false);
691}
692
693void led_matrix_flags_step(void) {
694 led_matrix_flags_step_helper(true);
695}
696
697void led_matrix_flags_step_reverse_helper(bool write_to_eeprom) {
698 led_flags_t flags = led_matrix_get_flags();
699
700 uint8_t next = 0;
701 for (uint8_t i = 0; i < LED_MATRIX_FLAG_STEPS_COUNT; i++) {
702 if (led_matrix_flag_steps[i] == flags) {
703 next = i == 0 ? LED_MATRIX_FLAG_STEPS_COUNT - 1 : i - 1;
704 break;
705 }
706 }
707
708 led_matrix_set_flags_eeprom_helper(led_matrix_flag_steps[next], write_to_eeprom);
709}
710
711void led_matrix_flags_step_reverse_noeeprom(void) {
712 led_matrix_flags_step_reverse_helper(false);
713}
714
715void led_matrix_flags_step_reverse(void) {
716 led_matrix_flags_step_reverse_helper(true);
717}
718
664// LED Matrix naming 719// LED Matrix naming
665#undef LED_MATRIX_EFFECT 720#undef LED_MATRIX_EFFECT
666#ifdef LED_MATRIX_MODE_NAME_ENABLE 721#ifdef LED_MATRIX_MODE_NAME_ENABLE
diff --git a/quantum/led_matrix/led_matrix.h b/quantum/led_matrix/led_matrix.h
index 9a49515ab2..f484c700f4 100644
--- a/quantum/led_matrix/led_matrix.h
+++ b/quantum/led_matrix/led_matrix.h
@@ -183,6 +183,10 @@ void led_matrix_decrease_speed_noeeprom(void);
183led_flags_t led_matrix_get_flags(void); 183led_flags_t led_matrix_get_flags(void);
184void led_matrix_set_flags(led_flags_t flags); 184void led_matrix_set_flags(led_flags_t flags);
185void led_matrix_set_flags_noeeprom(led_flags_t flags); 185void led_matrix_set_flags_noeeprom(led_flags_t flags);
186void led_matrix_flags_step_noeeprom(void);
187void led_matrix_flags_step(void);
188void led_matrix_flags_step_reverse_noeeprom(void);
189void led_matrix_flags_step_reverse(void);
186 190
187#ifdef LED_MATRIX_MODE_NAME_ENABLE 191#ifdef LED_MATRIX_MODE_NAME_ENABLE
188const char *led_matrix_get_mode_name(uint8_t mode); 192const char *led_matrix_get_mode_name(uint8_t mode);
diff --git a/quantum/matrix.c b/quantum/matrix.c
index 167a70e5b6..2e7ea085f4 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -303,7 +303,7 @@ void matrix_init(void) {
303 memset(matrix, 0, sizeof(matrix)); 303 memset(matrix, 0, sizeof(matrix));
304 memset(raw_matrix, 0, sizeof(raw_matrix)); 304 memset(raw_matrix, 0, sizeof(raw_matrix));
305 305
306 debounce_init(MATRIX_ROWS_PER_HAND); 306 debounce_init();
307 307
308 matrix_init_kb(); 308 matrix_init_kb();
309} 309}
@@ -336,9 +336,9 @@ uint8_t matrix_scan(void) {
336 if (changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix)); 336 if (changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix));
337 337
338#ifdef SPLIT_KEYBOARD 338#ifdef SPLIT_KEYBOARD
339 changed = debounce(raw_matrix, matrix + thisHand, MATRIX_ROWS_PER_HAND, changed) | matrix_post_scan(); 339 changed = debounce(raw_matrix, matrix + thisHand, changed) | matrix_post_scan();
340#else 340#else
341 changed = debounce(raw_matrix, matrix, MATRIX_ROWS_PER_HAND, changed); 341 changed = debounce(raw_matrix, matrix, changed);
342 matrix_scan_kb(); 342 matrix_scan_kb();
343#endif 343#endif
344 return (uint8_t)changed; 344 return (uint8_t)changed;
diff --git a/quantum/matrix_common.c b/quantum/matrix_common.c
index b4a86fc483..26589f29a6 100644
--- a/quantum/matrix_common.c
+++ b/quantum/matrix_common.c
@@ -156,7 +156,7 @@ __attribute__((weak)) void matrix_init(void) {
156 matrix[i] = 0; 156 matrix[i] = 0;
157 } 157 }
158 158
159 debounce_init(MATRIX_ROWS_PER_HAND); 159 debounce_init();
160 160
161 matrix_init_kb(); 161 matrix_init_kb();
162} 162}
@@ -165,9 +165,9 @@ __attribute__((weak)) uint8_t matrix_scan(void) {
165 bool changed = matrix_scan_custom(raw_matrix); 165 bool changed = matrix_scan_custom(raw_matrix);
166 166
167#ifdef SPLIT_KEYBOARD 167#ifdef SPLIT_KEYBOARD
168 changed = debounce(raw_matrix, matrix + thisHand, MATRIX_ROWS_PER_HAND, changed) | matrix_post_scan(); 168 changed = debounce(raw_matrix, matrix + thisHand, changed) | matrix_post_scan();
169#else 169#else
170 changed = debounce(raw_matrix, matrix, MATRIX_ROWS_PER_HAND, changed); 170 changed = debounce(raw_matrix, matrix, changed);
171 matrix_scan_kb(); 171 matrix_scan_kb();
172#endif 172#endif
173 173
diff --git a/quantum/mousekey.c b/quantum/mousekey.c
index 9649943a0d..863c6baccc 100644
--- a/quantum/mousekey.c
+++ b/quantum/mousekey.c
@@ -386,7 +386,8 @@ void mousekey_task(void) {
386 386
387void mousekey_on(uint8_t code) { 387void mousekey_on(uint8_t code) {
388# ifdef MK_KINETIC_SPEED 388# ifdef MK_KINETIC_SPEED
389 if (mouse_timer == 0) { 389 // Start kinetic timer when movement keycodes are pressed
390 if (mouse_timer == 0 && (IS_MOUSEKEY_MOVE(code) || IS_MOUSEKEY_WHEEL(code))) {
390 mouse_timer = timer_read(); 391 mouse_timer = timer_read();
391 } 392 }
392# endif 393# endif
diff --git a/quantum/painter/qff.h b/quantum/painter/qff.h
index ed88508d73..f5c59b315b 100644
--- a/quantum/painter/qff.h
+++ b/quantum/painter/qff.h
@@ -22,7 +22,7 @@
22 22
23#define QFF_FONT_DESCRIPTOR_TYPEID 0x00 23#define QFF_FONT_DESCRIPTOR_TYPEID 0x00
24 24
25typedef struct QP_PACKED qff_font_descriptor_v1_t { 25typedef struct PACKED qff_font_descriptor_v1_t {
26 qgf_block_header_v1_t header; // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 20 } 26 qgf_block_header_v1_t header; // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 20 }
27 uint32_t magic : 24; // constant, equal to 0x464651 ("QFF") 27 uint32_t magic : 24; // constant, equal to 0x464651 ("QFF")
28 uint8_t qff_version; // constant, equal to 0x01 28 uint8_t qff_version; // constant, equal to 0x01
@@ -51,13 +51,13 @@ STATIC_ASSERT(sizeof(qff_font_descriptor_v1_t) == (sizeof(qgf_block_header_v1_t)
51#define QFF_GLYPH_OFFSET_BITS 18 51#define QFF_GLYPH_OFFSET_BITS 18
52#define QFF_GLYPH_OFFSET_MASK (((1 << QFF_GLYPH_OFFSET_BITS) - 1) << QFF_GLYPH_WIDTH_BITS) 52#define QFF_GLYPH_OFFSET_MASK (((1 << QFF_GLYPH_OFFSET_BITS) - 1) << QFF_GLYPH_WIDTH_BITS)
53 53
54typedef struct QP_PACKED qff_ascii_glyph_v1_t { 54typedef struct PACKED qff_ascii_glyph_v1_t {
55 uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined 55 uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined
56} qff_ascii_glyph_v1_t; 56} qff_ascii_glyph_v1_t;
57 57
58STATIC_ASSERT(sizeof(qff_ascii_glyph_v1_t) == 3, "qff_ascii_glyph_v1_t must be 3 bytes in v1 of QFF"); 58STATIC_ASSERT(sizeof(qff_ascii_glyph_v1_t) == 3, "qff_ascii_glyph_v1_t must be 3 bytes in v1 of QFF");
59 59
60typedef struct QP_PACKED qff_ascii_glyph_table_v1_t { 60typedef struct PACKED qff_ascii_glyph_table_v1_t {
61 qgf_block_header_v1_t header; // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = 285 } 61 qgf_block_header_v1_t header; // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = 285 }
62 qff_ascii_glyph_v1_t glyph[95]; // 95 glyphs, 0x20..0x7E 62 qff_ascii_glyph_v1_t glyph[95]; // 95 glyphs, 0x20..0x7E
63} qff_ascii_glyph_table_v1_t; 63} qff_ascii_glyph_table_v1_t;
@@ -69,14 +69,14 @@ STATIC_ASSERT(sizeof(qff_ascii_glyph_table_v1_t) == (sizeof(qgf_block_header_v1_
69 69
70#define QFF_UNICODE_GLYPH_DESCRIPTOR_TYPEID 0x02 70#define QFF_UNICODE_GLYPH_DESCRIPTOR_TYPEID 0x02
71 71
72typedef struct QP_PACKED qff_unicode_glyph_v1_t { 72typedef struct PACKED qff_unicode_glyph_v1_t {
73 uint32_t code_point : 24; 73 uint32_t code_point : 24;
74 uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined 74 uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined
75} qff_unicode_glyph_v1_t; 75} qff_unicode_glyph_v1_t;
76 76
77STATIC_ASSERT(sizeof(qff_unicode_glyph_v1_t) == 6, "qff_unicode_glyph_v1_t must be 6 bytes in v1 of QFF"); 77STATIC_ASSERT(sizeof(qff_unicode_glyph_v1_t) == 6, "qff_unicode_glyph_v1_t must be 6 bytes in v1 of QFF");
78 78
79typedef struct QP_PACKED qff_unicode_glyph_table_v1_t { 79typedef struct PACKED qff_unicode_glyph_table_v1_t {
80 qgf_block_header_v1_t header; // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = (N * 6) } 80 qgf_block_header_v1_t header; // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = (N * 6) }
81 qff_unicode_glyph_v1_t glyph[0]; // Extent of '0' signifies that this struct is immediately followed by the glyph data 81 qff_unicode_glyph_v1_t glyph[0]; // Extent of '0' signifies that this struct is immediately followed by the glyph data
82} qff_unicode_glyph_table_v1_t; 82} qff_unicode_glyph_table_v1_t;
diff --git a/quantum/painter/qgf.c b/quantum/painter/qgf.c
index 07c3f80314..e5a1895b74 100644
--- a/quantum/painter/qgf.c
+++ b/quantum/painter/qgf.c
@@ -26,7 +26,7 @@ bool qgf_validate_block_header(qgf_block_header_v1_t *desc, uint8_t expected_typ
26 26
27bool qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette, bool *is_panel_native) { 27bool qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette, bool *is_panel_native) {
28 // clang-format off 28 // clang-format off
29 static const struct QP_PACKED { 29 static const struct PACKED {
30 uint8_t bpp; 30 uint8_t bpp;
31 bool has_palette; 31 bool has_palette;
32 bool is_panel_native; 32 bool is_panel_native;
diff --git a/quantum/painter/qgf.h b/quantum/painter/qgf.h
index a1e245f15d..87a9124f27 100644
--- a/quantum/painter/qgf.h
+++ b/quantum/painter/qgf.h
@@ -19,7 +19,7 @@
19///////////////////////////////////////// 19/////////////////////////////////////////
20// Common block header 20// Common block header
21 21
22typedef struct QP_PACKED qgf_block_header_v1_t { 22typedef struct PACKED qgf_block_header_v1_t {
23 uint8_t type_id; // See each respective block type below. 23 uint8_t type_id; // See each respective block type below.
24 uint8_t neg_type_id; // Negated type ID, used for detecting parsing errors. 24 uint8_t neg_type_id; // Negated type ID, used for detecting parsing errors.
25 uint32_t length : 24; // 24-bit blob length, allowing for block sizes of a maximum of 16MB. 25 uint32_t length : 24; // 24-bit blob length, allowing for block sizes of a maximum of 16MB.
@@ -32,7 +32,7 @@ STATIC_ASSERT(sizeof(qgf_block_header_v1_t) == 5, "qgf_block_header_v1_t must be
32 32
33#define QGF_GRAPHICS_DESCRIPTOR_TYPEID 0x00 33#define QGF_GRAPHICS_DESCRIPTOR_TYPEID 0x00
34 34
35typedef struct QP_PACKED qgf_graphics_descriptor_v1_t { 35typedef struct PACKED qgf_graphics_descriptor_v1_t {
36 qgf_block_header_v1_t header; // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 18 } 36 qgf_block_header_v1_t header; // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 18 }
37 uint32_t magic : 24; // constant, equal to 0x464751 ("QGF") 37 uint32_t magic : 24; // constant, equal to 0x464751 ("QGF")
38 uint8_t qgf_version; // constant, equal to 0x01 38 uint8_t qgf_version; // constant, equal to 0x01
@@ -52,7 +52,7 @@ STATIC_ASSERT(sizeof(qgf_graphics_descriptor_v1_t) == (sizeof(qgf_block_header_v
52 52
53#define QGF_FRAME_OFFSET_DESCRIPTOR_TYPEID 0x01 53#define QGF_FRAME_OFFSET_DESCRIPTOR_TYPEID 0x01
54 54
55typedef struct QP_PACKED qgf_frame_offsets_v1_t { 55typedef struct PACKED qgf_frame_offsets_v1_t {
56 qgf_block_header_v1_t header; // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = (N * sizeof(uint32_t)) } 56 qgf_block_header_v1_t header; // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = (N * sizeof(uint32_t)) }
57 uint32_t offset[0]; // '0' signifies that this struct is immediately followed by the frame offsets 57 uint32_t offset[0]; // '0' signifies that this struct is immediately followed by the frame offsets
58} qgf_frame_offsets_v1_t; 58} qgf_frame_offsets_v1_t;
@@ -64,7 +64,7 @@ STATIC_ASSERT(sizeof(qgf_frame_offsets_v1_t) == sizeof(qgf_block_header_v1_t), "
64 64
65#define QGF_FRAME_DESCRIPTOR_TYPEID 0x02 65#define QGF_FRAME_DESCRIPTOR_TYPEID 0x02
66 66
67typedef struct QP_PACKED qgf_frame_v1_t { 67typedef struct PACKED qgf_frame_v1_t {
68 qgf_block_header_v1_t header; // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = 6 } 68 qgf_block_header_v1_t header; // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = 6 }
69 qp_image_format_t format : 8; // Frame format, see qp_internal_formats.h. 69 qp_image_format_t format : 8; // Frame format, see qp_internal_formats.h.
70 uint8_t flags; // Frame flags, see below. 70 uint8_t flags; // Frame flags, see below.
@@ -83,7 +83,7 @@ STATIC_ASSERT(sizeof(qgf_frame_v1_t) == (sizeof(qgf_block_header_v1_t) + 6), "qg
83 83
84#define QGF_FRAME_PALETTE_DESCRIPTOR_TYPEID 0x03 84#define QGF_FRAME_PALETTE_DESCRIPTOR_TYPEID 0x03
85 85
86typedef struct QP_PACKED qgf_palette_entry_v1_t { 86typedef struct PACKED qgf_palette_entry_v1_t {
87 uint8_t h; // hue component: `[0,360)` degrees is mapped to `[0,255]` uint8_t. 87 uint8_t h; // hue component: `[0,360)` degrees is mapped to `[0,255]` uint8_t.
88 uint8_t s; // saturation component: `[0,1]` is mapped to `[0,255]` uint8_t. 88 uint8_t s; // saturation component: `[0,1]` is mapped to `[0,255]` uint8_t.
89 uint8_t v; // value component: `[0,1]` is mapped to `[0,255]` uint8_t. 89 uint8_t v; // value component: `[0,1]` is mapped to `[0,255]` uint8_t.
@@ -91,7 +91,7 @@ typedef struct QP_PACKED qgf_palette_entry_v1_t {
91 91
92STATIC_ASSERT(sizeof(qgf_palette_entry_v1_t) == 3, "Palette entry is not 3 bytes in size"); 92STATIC_ASSERT(sizeof(qgf_palette_entry_v1_t) == 3, "Palette entry is not 3 bytes in size");
93 93
94typedef struct QP_PACKED qgf_palette_v1_t { 94typedef struct PACKED qgf_palette_v1_t {
95 qgf_block_header_v1_t header; // = { .type_id = 0x03, .neg_type_id = (~0x03), .length = (N * 3 * sizeof(uint8_t)) } 95 qgf_block_header_v1_t header; // = { .type_id = 0x03, .neg_type_id = (~0x03), .length = (N * 3 * sizeof(uint8_t)) }
96 qgf_palette_entry_v1_t hsv[0]; // N * hsv, where N is the number of palette entries depending on the frame format in the descriptor 96 qgf_palette_entry_v1_t hsv[0]; // N * hsv, where N is the number of palette entries depending on the frame format in the descriptor
97} qgf_palette_v1_t; 97} qgf_palette_v1_t;
@@ -103,7 +103,7 @@ STATIC_ASSERT(sizeof(qgf_palette_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_pa
103 103
104#define QGF_FRAME_DELTA_DESCRIPTOR_TYPEID 0x04 104#define QGF_FRAME_DELTA_DESCRIPTOR_TYPEID 0x04
105 105
106typedef struct QP_PACKED qgf_delta_v1_t { 106typedef struct PACKED qgf_delta_v1_t {
107 qgf_block_header_v1_t header; // = { .type_id = 0x04, .neg_type_id = (~0x04), .length = 8 } 107 qgf_block_header_v1_t header; // = { .type_id = 0x04, .neg_type_id = (~0x04), .length = 8 }
108 uint16_t left; // The left pixel location to draw the delta image 108 uint16_t left; // The left pixel location to draw the delta image
109 uint16_t top; // The top pixel location to draw the delta image 109 uint16_t top; // The top pixel location to draw the delta image
@@ -118,7 +118,7 @@ STATIC_ASSERT(sizeof(qgf_delta_v1_t) == (sizeof(qgf_block_header_v1_t) + 8), "qg
118 118
119#define QGF_FRAME_DATA_DESCRIPTOR_TYPEID 0x05 119#define QGF_FRAME_DATA_DESCRIPTOR_TYPEID 0x05
120 120
121typedef struct QP_PACKED qgf_data_v1_t { 121typedef struct PACKED qgf_data_v1_t {
122 qgf_block_header_v1_t header; // = { .type_id = 0x05, .neg_type_id = (~0x05), .length = N } 122 qgf_block_header_v1_t header; // = { .type_id = 0x05, .neg_type_id = (~0x05), .length = N }
123 uint8_t data[0]; // 0 signifies that this struct is immediately followed by the length of data specified in the header 123 uint8_t data[0]; // 0 signifies that this struct is immediately followed by the length of data specified in the header
124} qgf_data_v1_t; 124} qgf_data_v1_t;
diff --git a/quantum/painter/qp_draw_core.c b/quantum/painter/qp_draw_core.c
index 852abb19e8..3958045943 100644
--- a/quantum/painter/qp_draw_core.c
+++ b/quantum/painter/qp_draw_core.c
@@ -52,7 +52,7 @@ bool qp_internal_setpixel_impl(painter_device_t device, uint16_t x, uint16_t y)
52void qp_internal_fill_pixdata(painter_device_t device, uint32_t num_pixels, uint8_t hue, uint8_t sat, uint8_t val) { 52void qp_internal_fill_pixdata(painter_device_t device, uint32_t num_pixels, uint8_t hue, uint8_t sat, uint8_t val) {
53 painter_driver_t *driver = (painter_driver_t *)device; 53 painter_driver_t *driver = (painter_driver_t *)device;
54 uint32_t pixels_in_pixdata = qp_internal_num_pixels_in_buffer(device); 54 uint32_t pixels_in_pixdata = qp_internal_num_pixels_in_buffer(device);
55 num_pixels = QP_MIN(pixels_in_pixdata, num_pixels); 55 num_pixels = MIN(pixels_in_pixdata, num_pixels);
56 56
57 // Convert the color to native pixel format 57 // Convert the color to native pixel format
58 qp_pixel_t color = {.hsv888 = {.h = hue, .s = sat, .v = val}}; 58 qp_pixel_t color = {.hsv888 = {.h = hue, .s = sat, .v = val}};
@@ -232,17 +232,17 @@ bool qp_internal_fillrect_helper_impl(painter_device_t device, uint16_t left, ui
232 uint32_t pixels_in_pixdata = qp_internal_num_pixels_in_buffer(device); 232 uint32_t pixels_in_pixdata = qp_internal_num_pixels_in_buffer(device);
233 painter_driver_t *driver = (painter_driver_t *)device; 233 painter_driver_t *driver = (painter_driver_t *)device;
234 234
235 uint16_t l = QP_MIN(left, right); 235 uint16_t l = MIN(left, right);
236 uint16_t r = QP_MAX(left, right); 236 uint16_t r = MAX(left, right);
237 uint16_t t = QP_MIN(top, bottom); 237 uint16_t t = MIN(top, bottom);
238 uint16_t b = QP_MAX(top, bottom); 238 uint16_t b = MAX(top, bottom);
239 uint16_t w = r - l + 1; 239 uint16_t w = r - l + 1;
240 uint16_t h = b - t + 1; 240 uint16_t h = b - t + 1;
241 241
242 uint32_t remaining = w * h; 242 uint32_t remaining = w * h;
243 driver->driver_vtable->viewport(device, l, t, r, b); 243 driver->driver_vtable->viewport(device, l, t, r, b);
244 while (remaining > 0) { 244 while (remaining > 0) {
245 uint32_t transmit = QP_MIN(remaining, pixels_in_pixdata); 245 uint32_t transmit = MIN(remaining, pixels_in_pixdata);
246 if (!driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, transmit)) { 246 if (!driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, transmit)) {
247 return false; 247 return false;
248 } 248 }
@@ -260,10 +260,10 @@ bool qp_rect(painter_device_t device, uint16_t left, uint16_t top, uint16_t righ
260 } 260 }
261 261
262 // Cater for cases where people have submitted the coordinates backwards 262 // Cater for cases where people have submitted the coordinates backwards
263 uint16_t l = QP_MIN(left, right); 263 uint16_t l = MIN(left, right);
264 uint16_t r = QP_MAX(left, right); 264 uint16_t r = MAX(left, right);
265 uint16_t t = QP_MIN(top, bottom); 265 uint16_t t = MIN(top, bottom);
266 uint16_t b = QP_MAX(top, bottom); 266 uint16_t b = MAX(top, bottom);
267 uint16_t w = r - l + 1; 267 uint16_t w = r - l + 1;
268 uint16_t h = b - t + 1; 268 uint16_t h = b - t + 1;
269 269
@@ -281,7 +281,7 @@ bool qp_rect(painter_device_t device, uint16_t left, uint16_t top, uint16_t righ
281 ret = qp_internal_fillrect_helper_impl(device, l, t, r, b); 281 ret = qp_internal_fillrect_helper_impl(device, l, t, r, b);
282 } else { 282 } else {
283 // Fill up the pixdata buffer with the required number of native pixels 283 // Fill up the pixdata buffer with the required number of native pixels
284 qp_internal_fill_pixdata(device, QP_MAX(w, h), hue, sat, val); 284 qp_internal_fill_pixdata(device, MAX(w, h), hue, sat, val);
285 285
286 // Draw 4x filled single-width rects to create an outline 286 // Draw 4x filled single-width rects to create an outline
287 if (!qp_internal_fillrect_helper_impl(device, l, t, r, t) || !qp_internal_fillrect_helper_impl(device, l, b, r, b) || !qp_internal_fillrect_helper_impl(device, l, t + 1, l, b - 1) || !qp_internal_fillrect_helper_impl(device, r, t + 1, r, b - 1)) { 287 if (!qp_internal_fillrect_helper_impl(device, l, t, r, t) || !qp_internal_fillrect_helper_impl(device, l, b, r, b) || !qp_internal_fillrect_helper_impl(device, l, t + 1, l, b - 1) || !qp_internal_fillrect_helper_impl(device, r, t + 1, r, b - 1)) {
diff --git a/quantum/painter/qp_draw_ellipse.c b/quantum/painter/qp_draw_ellipse.c
index 9e77bca8b0..22f019d179 100644
--- a/quantum/painter/qp_draw_ellipse.c
+++ b/quantum/painter/qp_draw_ellipse.c
@@ -75,7 +75,7 @@ bool qp_ellipse(painter_device_t device, uint16_t x, uint16_t y, uint16_t sizex,
75 int16_t dx = 0; 75 int16_t dx = 0;
76 int16_t dy = ((int16_t)sizey); 76 int16_t dy = ((int16_t)sizey);
77 77
78 qp_internal_fill_pixdata(device, QP_MAX(sizex, sizey), hue, sat, val); 78 qp_internal_fill_pixdata(device, MAX(sizex, sizey), hue, sat, val);
79 79
80 if (!qp_comms_start(device)) { 80 if (!qp_comms_start(device)) {
81 qp_dprintf("qp_ellipse: fail (could not start comms)\n"); 81 qp_dprintf("qp_ellipse: fail (could not start comms)\n");
diff --git a/quantum/painter/qp_internal.h b/quantum/painter/qp_internal.h
index e7a6d113c5..52b1b5c69d 100644
--- a/quantum/painter/qp_internal.h
+++ b/quantum/painter/qp_internal.h
@@ -5,17 +5,11 @@
5 5
6#include "quantum.h" 6#include "quantum.h"
7#include "qp.h" 7#include "qp.h"
8#include "util.h" // PACKED/MIN/MAX
8 9
9//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 10////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10// Helpers 11// Helpers
11 12
12// Mark certain types that there should be no padding bytes between members.
13#define QP_PACKED __attribute__((packed))
14
15// Min/max defines
16#define QP_MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
17#define QP_MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
18
19#ifdef QUANTUM_PAINTER_DEBUG 13#ifdef QUANTUM_PAINTER_DEBUG
20# include <debug.h> 14# include <debug.h>
21# include <print.h> 15# include <print.h>
diff --git a/quantum/painter/qp_internal_formats.h b/quantum/painter/qp_internal_formats.h
index bd7105cab2..28cff0cd61 100644
--- a/quantum/painter/qp_internal_formats.h
+++ b/quantum/painter/qp_internal_formats.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "color.h"
6#include "compiler_support.h" 7#include "compiler_support.h"
7#include "qp_internal.h" 8#include "qp_internal.h"
8 9
@@ -10,21 +11,13 @@
10// Quantum Painter pixel formats 11// Quantum Painter pixel formats
11 12
12// Datatype containing a pixel's color. The internal member used is dependent on the external context. 13// Datatype containing a pixel's color. The internal member used is dependent on the external context.
13typedef union QP_PACKED qp_pixel_t { 14typedef union PACKED qp_pixel_t {
14 uint8_t mono; 15 uint8_t mono;
15 uint8_t palette_idx; 16 uint8_t palette_idx;
16 17
17 struct QP_PACKED { 18 hsv_t hsv888;
18 uint8_t h; 19
19 uint8_t s; 20 rgb_t rgb888;
20 uint8_t v;
21 } hsv888;
22
23 struct QP_PACKED {
24 uint8_t r;
25 uint8_t g;
26 uint8_t b;
27 } rgb888;
28 21
29 uint16_t rgb565; 22 uint16_t rgb565;
30 23
diff --git a/quantum/painter/rules.mk b/quantum/painter/rules.mk
index 10c2698092..2c5bc17eed 100644
--- a/quantum/painter/rules.mk
+++ b/quantum/painter/rules.mk
@@ -247,7 +247,8 @@ ifeq ($(strip $(QUANTUM_PAINTER_NEEDS_SURFACE)), yes)
247 SRC += \ 247 SRC += \
248 $(DRIVER_PATH)/painter/generic/qp_surface_common.c \ 248 $(DRIVER_PATH)/painter/generic/qp_surface_common.c \
249 $(DRIVER_PATH)/painter/generic/qp_surface_mono1bpp.c \ 249 $(DRIVER_PATH)/painter/generic/qp_surface_mono1bpp.c \
250 $(DRIVER_PATH)/painter/generic/qp_surface_rgb565.c 250 $(DRIVER_PATH)/painter/generic/qp_surface_rgb565.c \
251 $(DRIVER_PATH)/painter/generic/qp_surface_rgb888.c
251endif 252endif
252 253
253# If dummy comms is needed, set up the required files 254# If dummy comms is needed, set up the required files
diff --git a/quantum/process_keycode/process_led_matrix.c b/quantum/process_keycode/process_led_matrix.c
index 7f95bf1011..3342b33b92 100644
--- a/quantum/process_keycode/process_led_matrix.c
+++ b/quantum/process_keycode/process_led_matrix.c
@@ -40,6 +40,12 @@ bool process_led_matrix(uint16_t keycode, keyrecord_t *record) {
40 case QK_LED_MATRIX_SPEED_DOWN: 40 case QK_LED_MATRIX_SPEED_DOWN:
41 led_matrix_decrease_speed(); 41 led_matrix_decrease_speed();
42 return false; 42 return false;
43 case QK_LED_MATRIX_FLAG_NEXT:
44 led_matrix_flags_step();
45 return false;
46 case QK_LED_MATRIX_FLAG_PREVIOUS:
47 led_matrix_flags_step_reverse();
48 return false;
43 } 49 }
44 } 50 }
45 51
diff --git a/quantum/process_keycode/process_rgb_matrix.c b/quantum/process_keycode/process_rgb_matrix.c
index fd2aa1a0c7..c18212294d 100644
--- a/quantum/process_keycode/process_rgb_matrix.c
+++ b/quantum/process_keycode/process_rgb_matrix.c
@@ -94,6 +94,20 @@ bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) {
94 rgb_matrix_decrease_speed(); 94 rgb_matrix_decrease_speed();
95 } 95 }
96 return false; 96 return false;
97 case QK_RGB_MATRIX_FLAG_NEXT:
98 if (shifted) {
99 rgb_matrix_flags_step_reverse();
100 } else {
101 rgb_matrix_flags_step();
102 }
103 return false;
104 case QK_RGB_MATRIX_FLAG_PREVIOUS:
105 if (shifted) {
106 rgb_matrix_flags_step();
107 } else {
108 rgb_matrix_flags_step_reverse();
109 }
110 return false;
97 } 111 }
98 } 112 }
99 113
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 09e5fe1dac..128f8fb66d 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -157,9 +157,7 @@ __attribute__((weak)) void unregister_code16(uint16_t code) {
157 */ 157 */
158__attribute__((weak)) void tap_code16_delay(uint16_t code, uint16_t delay) { 158__attribute__((weak)) void tap_code16_delay(uint16_t code, uint16_t delay) {
159 register_code16(code); 159 register_code16(code);
160 for (uint16_t i = delay; i > 0; i--) { 160 wait_ms(delay);
161 wait_ms(1);
162 }
163 unregister_code16(code); 161 unregister_code16(code);
164} 162}
165 163
diff --git a/quantum/rgb_matrix/animations/pixel_rain_anim.h b/quantum/rgb_matrix/animations/pixel_rain_anim.h
index c0370831d8..f92a285b67 100644
--- a/quantum/rgb_matrix/animations/pixel_rain_anim.h
+++ b/quantum/rgb_matrix/animations/pixel_rain_anim.h
@@ -6,20 +6,27 @@ RGB_MATRIX_EFFECT(PIXEL_RAIN)
6# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS 6# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
7 7
8static bool PIXEL_RAIN(effect_params_t* params) { 8static bool PIXEL_RAIN(effect_params_t* params) {
9 static fast_timer_t timer = 0; 9 static uint8_t index = 0;
10 static uint16_t index = RGB_MATRIX_LED_COUNT + 1; 10 static uint32_t timer = 0;
11 11
12 if ((params->iter == 0) && (timer_elapsed_fast(timer) > (320 - rgb_matrix_config.speed))) { 12 if (params->iter == 0 && params->init) {
13 index = random8_max(RGB_MATRIX_LED_COUNT); 13 index = random8_max(RGB_MATRIX_LED_COUNT);
14 timer = timer_read_fast();
15 } 14 }
16 15
17 RGB_MATRIX_USE_LIMITS(led_min, led_max); 16 RGB_MATRIX_USE_LIMITS(led_min, led_max);
18 if (led_min <= index && index < led_max && HAS_ANY_FLAGS(g_led_config.flags[index], params->flags)) { 17 if (timer < g_rgb_timer) { // Execute when the delay period has elapsed
19 hsv_t hsv = (random8() & 2) ? (hsv_t){0, 0, 0} : (hsv_t){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v}; 18 if (led_min <= index && index < led_max && HAS_ANY_FLAGS(g_led_config.flags[index], params->flags)) {
20 rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv); 19 // Assign a random HSV color to hsv with 50% probability, otherwise assign zeroed hsv
21 rgb_matrix_set_color(index, rgb.r, rgb.g, rgb.b); 20 hsv_t hsv = (random8() & 2) ? (hsv_t){0, 0, 0} : (hsv_t){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v};
22 index = RGB_MATRIX_LED_COUNT + 1; 21 rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);
22 rgb_matrix_set_color(index, rgb.r, rgb.g, rgb.b);
23 }
24 if (!rgb_matrix_check_finished_leds(led_max)) {
25 // In the final LED range, update the LED index and advance the timer for
26 // the next cycle, scaling the delay between 256–2048 ms based on speed.
27 index = random8_max(RGB_MATRIX_LED_COUNT);
28 timer = g_rgb_timer + (2048 - scale16by8(1792, rgb_matrix_config.speed));
29 }
23 } 30 }
24 return rgb_matrix_check_finished_leds(led_max); 31 return rgb_matrix_check_finished_leds(led_max);
25} 32}
diff --git a/quantum/rgb_matrix/animations/raindrops_anim.h b/quantum/rgb_matrix/animations/raindrops_anim.h
index d4f79adb56..f3656a5c0d 100644
--- a/quantum/rgb_matrix/animations/raindrops_anim.h
+++ b/quantum/rgb_matrix/animations/raindrops_anim.h
@@ -7,14 +7,9 @@ static void raindrops_set_color(uint8_t i, effect_params_t* params) {
7 hsv_t hsv = rgb_matrix_config.hsv; 7 hsv_t hsv = rgb_matrix_config.hsv;
8 8
9 // Take the shortest path between hues 9 // Take the shortest path between hues
10 int16_t deltaH = ((hsv.h + 180) % 360 - hsv.h) / 4; 10 int8_t deltaH = (int8_t)((hsv.h + 128) - hsv.h) / 4;
11 if (deltaH > 127) {
12 deltaH -= 256;
13 } else if (deltaH < -127) {
14 deltaH += 256;
15 }
16
17 hsv.h += (deltaH * random8_max(3)); 11 hsv.h += (deltaH * random8_max(3));
12
18 rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv); 13 rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);
19 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 14 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
20} 15}
diff --git a/quantum/rgb_matrix/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c
index ab0aa17512..f517190e35 100644
--- a/quantum/rgb_matrix/rgb_matrix.c
+++ b/quantum/rgb_matrix/rgb_matrix.c
@@ -72,12 +72,20 @@ uint8_t g_rgb_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}};
72last_hit_t g_last_hit_tracker; 72last_hit_t g_last_hit_tracker;
73#endif // RGB_MATRIX_KEYREACTIVE_ENABLED 73#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
74 74
75#ifndef RGB_MATRIX_FLAG_STEPS
76# define RGB_MATRIX_FLAG_STEPS \
77 { LED_FLAG_ALL, LED_FLAG_KEYLIGHT | LED_FLAG_MODIFIER, LED_FLAG_UNDERGLOW, LED_FLAG_NONE }
78#endif
79static const uint8_t rgb_matrix_flag_steps[] = RGB_MATRIX_FLAG_STEPS;
80#define RGB_MATRIX_FLAG_STEPS_COUNT ARRAY_SIZE(rgb_matrix_flag_steps)
81
75// internals 82// internals
76static bool suspend_state = false; 83static bool suspend_state = false;
77static uint8_t rgb_last_enable = UINT8_MAX; 84static uint8_t rgb_last_enable = UINT8_MAX;
78static uint8_t rgb_last_effect = UINT8_MAX; 85static uint8_t rgb_last_effect = UINT8_MAX;
79static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false}; 86static uint8_t rgb_current_effect = 0;
80static rgb_task_states rgb_task_state = SYNCING; 87static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false};
88static rgb_task_states rgb_task_state = SYNCING;
81 89
82// double buffers 90// double buffers
83static uint32_t rgb_timer_buffer; 91static uint32_t rgb_timer_buffer;
@@ -289,6 +297,17 @@ static void rgb_task_start(void) {
289 g_last_hit_tracker = last_hit_buffer; 297 g_last_hit_tracker = last_hit_buffer;
290#endif // RGB_MATRIX_KEYREACTIVE_ENABLED 298#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
291 299
300 // Ideally we would also stop sending zeros to the LED driver PWM buffers
301 // while suspended and just do a software shutdown. This is a cheap hack for now.
302 bool suspend_backlight = suspend_state ||
303#if RGB_MATRIX_TIMEOUT > 0
304 (last_input_activity_elapsed() > (uint32_t)RGB_MATRIX_TIMEOUT) ||
305#endif // RGB_MATRIX_TIMEOUT > 0
306 false;
307
308 // Set effect to be renedered
309 rgb_current_effect = suspend_backlight || !rgb_matrix_config.enable ? 0 : rgb_matrix_config.mode;
310
292 // next task 311 // next task
293 rgb_task_state = RENDERING; 312 rgb_task_state = RENDERING;
294} 313}
@@ -377,15 +396,7 @@ static void rgb_task_flush(uint8_t effect) {
377void rgb_matrix_task(void) { 396void rgb_matrix_task(void) {
378 rgb_task_timers(); 397 rgb_task_timers();
379 398
380 // Ideally we would also stop sending zeros to the LED driver PWM buffers 399 uint8_t effect = rgb_current_effect;
381 // while suspended and just do a software shutdown. This is a cheap hack for now.
382 bool suspend_backlight = suspend_state ||
383#if RGB_MATRIX_TIMEOUT > 0
384 (last_input_activity_elapsed() > (uint32_t)RGB_MATRIX_TIMEOUT) ||
385#endif // RGB_MATRIX_TIMEOUT > 0
386 false;
387
388 uint8_t effect = suspend_backlight || !rgb_matrix_config.enable ? 0 : rgb_matrix_config.mode;
389 400
390 switch (rgb_task_state) { 401 switch (rgb_task_state) {
391 case STARTING: 402 case STARTING:
@@ -747,6 +758,50 @@ void rgb_matrix_set_flags_noeeprom(led_flags_t flags) {
747 rgb_matrix_set_flags_eeprom_helper(flags, false); 758 rgb_matrix_set_flags_eeprom_helper(flags, false);
748} 759}
749 760
761void rgb_matrix_flags_step_helper(bool write_to_eeprom) {
762 led_flags_t flags = rgb_matrix_get_flags();
763
764 uint8_t next = 0;
765 for (uint8_t i = 0; i < RGB_MATRIX_FLAG_STEPS_COUNT; i++) {
766 if (rgb_matrix_flag_steps[i] == flags) {
767 next = i == RGB_MATRIX_FLAG_STEPS_COUNT - 1 ? 0 : i + 1;
768 break;
769 }
770 }
771
772 rgb_matrix_set_flags_eeprom_helper(rgb_matrix_flag_steps[next], write_to_eeprom);
773}
774
775void rgb_matrix_flags_step_noeeprom(void) {
776 rgb_matrix_flags_step_helper(false);
777}
778
779void rgb_matrix_flags_step(void) {
780 rgb_matrix_flags_step_helper(true);
781}
782
783void rgb_matrix_flags_step_reverse_helper(bool write_to_eeprom) {
784 led_flags_t flags = rgb_matrix_get_flags();
785
786 uint8_t next = 0;
787 for (uint8_t i = 0; i < RGB_MATRIX_FLAG_STEPS_COUNT; i++) {
788 if (rgb_matrix_flag_steps[i] == flags) {
789 next = i == 0 ? RGB_MATRIX_FLAG_STEPS_COUNT - 1 : i - 1;
790 break;
791 }
792 }
793
794 rgb_matrix_set_flags_eeprom_helper(rgb_matrix_flag_steps[next], write_to_eeprom);
795}
796
797void rgb_matrix_flags_step_reverse_noeeprom(void) {
798 rgb_matrix_flags_step_reverse_helper(false);
799}
800
801void rgb_matrix_flags_step_reverse(void) {
802 rgb_matrix_flags_step_reverse_helper(true);
803}
804
750//---------------------------------------------------------- 805//----------------------------------------------------------
751// RGB Matrix naming 806// RGB Matrix naming
752#undef RGB_MATRIX_EFFECT 807#undef RGB_MATRIX_EFFECT
diff --git a/quantum/rgb_matrix/rgb_matrix.h b/quantum/rgb_matrix/rgb_matrix.h
index a91dded4a8..f800679b46 100644
--- a/quantum/rgb_matrix/rgb_matrix.h
+++ b/quantum/rgb_matrix/rgb_matrix.h
@@ -218,6 +218,10 @@ void rgb_matrix_decrease_speed_noeeprom(void);
218led_flags_t rgb_matrix_get_flags(void); 218led_flags_t rgb_matrix_get_flags(void);
219void rgb_matrix_set_flags(led_flags_t flags); 219void rgb_matrix_set_flags(led_flags_t flags);
220void rgb_matrix_set_flags_noeeprom(led_flags_t flags); 220void rgb_matrix_set_flags_noeeprom(led_flags_t flags);
221void rgb_matrix_flags_step_noeeprom(void);
222void rgb_matrix_flags_step(void);
223void rgb_matrix_flags_step_reverse_noeeprom(void);
224void rgb_matrix_flags_step_reverse(void);
221void rgb_matrix_update_pwm_buffers(void); 225void rgb_matrix_update_pwm_buffers(void);
222 226
223#ifdef RGB_MATRIX_MODE_NAME_ENABLE 227#ifdef RGB_MATRIX_MODE_NAME_ENABLE