diff options
Diffstat (limited to 'quantum')
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 */ |
| 39 | typedef struct { | 39 | typedef 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 | ||
| 57 | typedef struct { | ||
| 58 | keypos_t key; | ||
| 59 | uint8_t mods; | ||
| 60 | } speculative_key_t; | ||
| 61 | # define SPECULATIVE_KEYS_SIZE 8 | ||
| 62 | static speculative_key_t speculative_keys[SPECULATIVE_KEYS_SIZE] = {}; | ||
| 63 | static uint8_t num_speculative_keys = 0; | ||
| 64 | static uint8_t prev_speculative_mods = 0; | ||
| 65 | static uint8_t speculative_mods = 0; | ||
| 66 | |||
| 67 | /** Handler to be called on incoming press events. */ | ||
| 68 | static 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 | */ |
| 131 | void action_tapping_process(keyrecord_t record) { | 148 | void 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 | ||
| 742 | static 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. | ||
| 755 | static 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 | |||
| 765 | static 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 | |||
| 810 | uint8_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 | |||
| 819 | void 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) |
| 712 | static void registered_taps_add(keypos_t key) { | 883 | static 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. | ||
| 1059 | bool 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); | |||
| 46 | bool get_retro_tapping(uint16_t keycode, keyrecord_t *record); | 46 | bool get_retro_tapping(uint16_t keycode, keyrecord_t *record); |
| 47 | bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record); | 47 | bool 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. */ | ||
| 51 | uint8_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 | */ | ||
| 63 | bool 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 | */ | ||
| 74 | void 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. */ |
| 171 | void flow_tap_update_last_event(keyrecord_t *record); | 201 | void 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 | */ | ||
| 211 | bool 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 |
| 47 | static uint8_t oneshot_mods = 0; | 48 | static uint8_t oneshot_mods = 0; |
| 48 | static uint8_t oneshot_locked_mods = 0; | 49 | static uint8_t oneshot_locked_mods = 0; |
| 49 | uint8_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 | */ | ||
| 55 | uint8_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 | */ | ||
| 61 | mod_t get_oneshot_locked_mod_state(void) { | ||
| 62 | return (mod_t)get_oneshot_locked_mods(); | ||
| 63 | } | ||
| 52 | void add_oneshot_locked_mods(uint8_t mods) { | 64 | void 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 | */ |
| 333 | uint8_t get_mods(void) { | 350 | uint8_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 | */ | ||
| 356 | mod_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 | */ |
| 369 | uint8_t get_weak_mods(void) { | 393 | uint8_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 | */ | ||
| 399 | mod_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 | */ |
| 430 | uint8_t get_oneshot_mods(void) { | 461 | uint8_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 | */ | ||
| 468 | mod_t get_oneshot_mod_state(void) { | ||
| 469 | return (mod_t)get_oneshot_mods(); | ||
| 470 | } | ||
| 471 | |||
| 434 | void add_oneshot_mods(uint8_t mods) { | 472 | void 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/>. | |||
| 25 | extern "C" { | 27 | extern "C" { |
| 26 | #endif | 28 | #endif |
| 27 | 29 | ||
| 30 | typedef 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; | ||
| 43 | STATIC_ASSERT(sizeof(mod_t) == sizeof(uint8_t), "Invalid size for 'mod_t'"); | ||
| 44 | |||
| 28 | extern report_keyboard_t *keyboard_report; | 45 | extern report_keyboard_t *keyboard_report; |
| 29 | #ifdef NKRO_ENABLE | 46 | #ifdef NKRO_ENABLE |
| 30 | extern report_nkro_t *nkro_report; | 47 | extern report_nkro_t *nkro_report; |
| @@ -47,6 +64,7 @@ inline void clear_keys(void) { | |||
| 47 | 64 | ||
| 48 | /* modifier */ | 65 | /* modifier */ |
| 49 | uint8_t get_mods(void); | 66 | uint8_t get_mods(void); |
| 67 | mod_t get_mod_state(void); | ||
| 50 | void add_mods(uint8_t mods); | 68 | void add_mods(uint8_t mods); |
| 51 | void del_mods(uint8_t mods); | 69 | void del_mods(uint8_t mods); |
| 52 | void set_mods(uint8_t mods); | 70 | void set_mods(uint8_t mods); |
| @@ -54,6 +72,7 @@ void clear_mods(void); | |||
| 54 | 72 | ||
| 55 | /* weak modifier */ | 73 | /* weak modifier */ |
| 56 | uint8_t get_weak_mods(void); | 74 | uint8_t get_weak_mods(void); |
| 75 | mod_t get_weak_mod_state(void); | ||
| 57 | void add_weak_mods(uint8_t mods); | 76 | void add_weak_mods(uint8_t mods); |
| 58 | void del_weak_mods(uint8_t mods); | 77 | void del_weak_mods(uint8_t mods); |
| 59 | void set_weak_mods(uint8_t mods); | 78 | void set_weak_mods(uint8_t mods); |
| @@ -61,6 +80,7 @@ void clear_weak_mods(void); | |||
| 61 | 80 | ||
| 62 | /* oneshot modifier */ | 81 | /* oneshot modifier */ |
| 63 | uint8_t get_oneshot_mods(void); | 82 | uint8_t get_oneshot_mods(void); |
| 83 | mod_t get_oneshot_mod_state(void); | ||
| 64 | void add_oneshot_mods(uint8_t mods); | 84 | void add_oneshot_mods(uint8_t mods); |
| 65 | void del_oneshot_mods(uint8_t mods); | 85 | void del_oneshot_mods(uint8_t mods); |
| 66 | void set_oneshot_mods(uint8_t mods); | 86 | void set_oneshot_mods(uint8_t mods); |
| @@ -68,6 +88,7 @@ void clear_oneshot_mods(void); | |||
| 68 | bool has_oneshot_mods_timed_out(void); | 88 | bool has_oneshot_mods_timed_out(void); |
| 69 | 89 | ||
| 70 | uint8_t get_oneshot_locked_mods(void); | 90 | uint8_t get_oneshot_locked_mods(void); |
| 91 | mod_t get_oneshot_locked_mod_state(void); | ||
| 71 | void add_oneshot_locked_mods(uint8_t mods); | 92 | void add_oneshot_locked_mods(uint8_t mods); |
| 72 | void set_oneshot_locked_mods(uint8_t mods); | 93 | void set_oneshot_locked_mods(uint8_t mods); |
| 73 | void clear_oneshot_locked_mods(void); | 94 | void 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 | */ |
| 17 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed); | 16 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed); |
| 18 | 17 | ||
| 19 | void debounce_init(uint8_t num_rows); | 18 | void debounce_init(void); |
| 20 | |||
| 21 | void 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 | /* | ||
| 21 | Asymetric per-key algorithm. After pressing a key, it immediately changes state, | ||
| 22 | with no further inputs accepted until DEBOUNCE milliseconds have occurred. After | ||
| 23 | releasing 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 | ||
| 48 | typedef struct { | 27 | typedef 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 |
| 54 | static debounce_counter_t *debounce_counters; | 33 | static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED}; |
| 55 | static fast_timer_t last_time; | 34 | static bool counters_need_update; |
| 56 | static bool counters_need_update; | 35 | static bool matrix_need_update; |
| 57 | static bool matrix_need_update; | 36 | static bool cooked_changed; |
| 58 | static bool cooked_changed; | ||
| 59 | |||
| 60 | # define DEBOUNCE_ELAPSED 0 | ||
| 61 | |||
| 62 | static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time); | ||
| 63 | static 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 | ||
| 66 | void 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 | ||
| 76 | void debounce_free(void) { | 38 | static 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); | 39 | static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]); |
| 78 | debounce_counters = NULL; | ||
| 79 | } | ||
| 80 | 40 | ||
| 81 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 41 | void debounce_init(void) {} |
| 82 | bool updated_last = false; | 42 | |
| 83 | cooked_changed = false; | 43 | bool 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 | ||
| 111 | static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) { | 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 | */ | ||
| 83 | static 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 | ||
| 144 | static 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 | */ | ||
| 127 | static 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 | ||
| 20 | void debounce_init(uint8_t num_rows) {} | 20 | void debounce_init(void) {} |
| 21 | 21 | ||
| 22 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 22 | bool 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 | |||
| 36 | void 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> |
| 2 | Copyright 2017 Alex Ong<the.onga@gmail.com> | 2 | // Copyright 2021 Simon Arlott |
| 3 | Copyright 2021 Simon Arlott | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | This program is free software: you can redistribute it and/or modify | 4 | // |
| 5 | it 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 |
| 6 | the 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 | |
| 8 | This program is distributed in the hope that it will be useful, | ||
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | GNU General Public License for more details. | ||
| 12 | You should have received a copy of the GNU General Public License | ||
| 13 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 14 | */ | ||
| 15 | |||
| 16 | /* | ||
| 17 | Basic global debounce algorithm. Used in 99% of keyboards at time of implementation | ||
| 18 | When 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 |
| 34 | static bool debouncing = false; | ||
| 35 | static fast_timer_t debouncing_time; | ||
| 36 | 22 | ||
| 37 | void debounce_init(uint8_t num_rows) {} | 23 | void debounce_init(void) {} |
| 38 | 24 | ||
| 39 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 25 | bool 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 | ||
| 57 | void 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> |
| 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 | This program is free software: you can redistribute it and/or modify | 5 | // |
| 6 | it 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. |
| 7 | the 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. | ||
| 9 | This program is distributed in the hope that it will be useful, | ||
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | GNU General Public License for more details. | ||
| 13 | You should have received a copy of the GNU General Public License | ||
| 14 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* | ||
| 18 | Basic symmetric per-key algorithm. Uses an 8-bit counter per key. | ||
| 19 | When 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 | ||
| 44 | typedef uint8_t debounce_counter_t; | 26 | typedef uint8_t debounce_counter_t; |
| 27 | // Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards | ||
| 28 | static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED}; | ||
| 29 | static bool counters_need_update; | ||
| 30 | static bool cooked_changed; | ||
| 45 | 31 | ||
| 46 | #if DEBOUNCE > 0 | 32 | static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time); |
| 47 | static debounce_counter_t *debounce_counters; | 33 | static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]); |
| 48 | static fast_timer_t last_time; | ||
| 49 | static bool counters_need_update; | ||
| 50 | static bool cooked_changed; | ||
| 51 | |||
| 52 | # define DEBOUNCE_ELAPSED 0 | ||
| 53 | |||
| 54 | static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time); | ||
| 55 | static 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 | ||
| 58 | void 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 | ||
| 68 | void debounce_free(void) { | 35 | void debounce_init(void) {} |
| 69 | free(debounce_counters); | ||
| 70 | debounce_counters = NULL; | ||
| 71 | } | ||
| 72 | 36 | ||
| 73 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 37 | bool 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 | ||
| 103 | static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) { | 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 | */ | ||
| 77 | static 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 | ||
| 124 | static 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 | */ | ||
| 111 | static 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> |
| 2 | Copyright 2021 Chad Austin <chad@chadaustin.me> | 2 | // Copyright 2020 Andrei Purdea<andrei@purdea.ro> |
| 3 | This program is free software: you can redistribute it and/or modify | 3 | // Copyright 2021 Simon Arlott |
| 4 | it under the terms of the GNU General Public License as published by | 4 | // Copyright @filterpaper |
| 5 | the 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 | // |
| 7 | This program is distributed in the hope that it will be useful, | 7 | // Basic symmetric per-row algorithm. Uses an 8-bit counter per row. |
| 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 8 | // When no state changes have occured for DEBOUNCE milliseconds, we push the state. |
| 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 10 | GNU General Public License for more details. | ||
| 11 | You should have received a copy of the GNU General Public License | ||
| 12 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 13 | */ | ||
| 14 | |||
| 15 | /* | ||
| 16 | Symmetric per-row debounce algorithm. Changes only apply when | ||
| 17 | DEBOUNCE 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 | ||
| 28 | static uint16_t last_time; | 18 | // Maximum debounce: 255ms |
| 29 | // [row] milliseconds until key's state is considered debounced. | 19 | #if DEBOUNCE > UINT8_MAX |
| 30 | static uint8_t* countdowns; | 20 | # undef DEBOUNCE |
| 31 | // [row] | 21 | # define DEBOUNCE UINT8_MAX |
| 32 | static matrix_row_t* last_raw; | 22 | #endif |
| 33 | 23 | ||
| 34 | void 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 | } | 27 | typedef uint8_t debounce_counter_t; |
| 28 | // Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards | ||
| 29 | static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND] = {DEBOUNCE_ELAPSED}; | ||
| 30 | static bool counters_need_update; | ||
| 31 | static bool cooked_changed; | ||
| 40 | 32 | ||
| 41 | void debounce_free(void) { | 33 | static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time); |
| 42 | free(countdowns); | 34 | static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]); |
| 43 | countdowns = NULL; | 35 | |
| 44 | free(last_raw); | 36 | void debounce_init(void) {} |
| 45 | last_raw = NULL; | 37 | |
| 46 | } | 38 | bool 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 | ||
| 48 | bool 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 | ||
| 75 | bool 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 | */ | ||
| 78 | static 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 | */ | ||
| 104 | static 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 | ||
| 44 | typedef uint8_t debounce_counter_t; | 39 | typedef uint8_t debounce_counter_t; |
| 40 | // Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards | ||
| 41 | static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED}; | ||
| 42 | static bool counters_need_update; | ||
| 43 | static bool matrix_need_update; | ||
| 44 | static bool cooked_changed; | ||
| 45 | 45 | ||
| 46 | #if DEBOUNCE > 0 | 46 | static inline void update_debounce_counters(uint8_t elapsed_time); |
| 47 | static debounce_counter_t *debounce_counters; | 47 | static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]); |
| 48 | static fast_timer_t last_time; | ||
| 49 | static bool counters_need_update; | ||
| 50 | static bool matrix_need_update; | ||
| 51 | static bool cooked_changed; | ||
| 52 | |||
| 53 | # define DEBOUNCE_ELAPSED 0 | ||
| 54 | |||
| 55 | static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); | ||
| 56 | static 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 | ||
| 59 | void 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 | ||
| 69 | void debounce_free(void) { | 49 | void debounce_init(void) {} |
| 70 | free(debounce_counters); | ||
| 71 | debounce_counters = NULL; | ||
| 72 | } | ||
| 73 | 50 | ||
| 74 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 51 | bool 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 | /** |
| 105 | static 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 | */ | ||
| 89 | static 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 | /** |
| 126 | static 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 | */ | ||
| 122 | static 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> |
| 2 | Copyright 2019 Alex Ong<the.onga@gmail.com> | 2 | // Copyright 2021 Simon Arlott |
| 3 | Copyright 2021 Simon Arlott | 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | This program is free software: you can redistribute it and/or modify | 4 | // |
| 5 | it under the terms of the GNU General Public License as published by | 5 | // Basic per-row algorithm. Uses an 8-bit counter per key. |
| 6 | the 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. |
| 8 | This program is distributed in the hope that it will be useful, | ||
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | GNU General Public License for more details. | ||
| 12 | You should have received a copy of the GNU General Public License | ||
| 13 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 14 | */ | ||
| 15 | |||
| 16 | /* | ||
| 17 | Basic per-row algorithm. Uses an 8-bit counter per row. | ||
| 18 | After pressing a key, it immediately changes state, and sets a counter. | ||
| 19 | No 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 | ||
| 42 | typedef uint8_t debounce_counter_t; | 23 | #define DEBOUNCE_ELAPSED 0 |
| 43 | 24 | ||
| 44 | #if DEBOUNCE > 0 | 25 | #if DEBOUNCE > 0 |
| 45 | static bool matrix_need_update; | 26 | typedef uint8_t debounce_counter_t; |
| 46 | 27 | // Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards | |
| 47 | static debounce_counter_t *debounce_counters; | 28 | static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND] = {DEBOUNCE_ELAPSED}; |
| 48 | static fast_timer_t last_time; | 29 | static bool counters_need_update; |
| 49 | static bool counters_need_update; | 30 | static bool matrix_need_update; |
| 50 | static bool cooked_changed; | 31 | static bool cooked_changed; |
| 51 | |||
| 52 | # define DEBOUNCE_ELAPSED 0 | ||
| 53 | |||
| 54 | static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); | ||
| 55 | static 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 | 33 | static inline void update_debounce_counters(uint8_t elapsed_time); |
| 58 | void debounce_init(uint8_t num_rows) { | 34 | static 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 | ||
| 65 | void debounce_free(void) { | 36 | void debounce_init(void) {} |
| 66 | free(debounce_counters); | ||
| 67 | debounce_counters = NULL; | ||
| 68 | } | ||
| 69 | 37 | ||
| 70 | bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { | 38 | bool 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 | /** |
| 101 | static 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; | 76 | static 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 | /** |
| 120 | static 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 | */ | ||
| 103 | static 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 | ||
| 128 | void DebounceTest::runDebounce(bool changed) { | 126 | void 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 | ||
| 35 | enum qk_keycode_ranges { | 35 | enum 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}}; | |||
| 70 | last_hit_t g_last_hit_tracker; | 70 | last_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 | ||
| 77 | static 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 |
| 74 | static bool suspend_state = false; | 81 | static bool suspend_state = false; |
| 75 | static uint8_t led_last_enable = UINT8_MAX; | 82 | static uint8_t led_last_enable = UINT8_MAX; |
| 76 | static uint8_t led_last_effect = UINT8_MAX; | 83 | static uint8_t led_last_effect = UINT8_MAX; |
| 77 | static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false}; | 84 | static uint8_t led_current_effect = 0; |
| 78 | static led_task_states led_task_state = SYNCING; | 85 | static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false}; |
| 86 | static led_task_states led_task_state = SYNCING; | ||
| 79 | 87 | ||
| 80 | // double buffers | 88 | // double buffers |
| 81 | static uint32_t led_timer_buffer; | 89 | static 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) { | |||
| 342 | void led_matrix_task(void) { | 361 | void 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 | ||
| 675 | void 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 | |||
| 689 | void led_matrix_flags_step_noeeprom(void) { | ||
| 690 | led_matrix_flags_step_helper(false); | ||
| 691 | } | ||
| 692 | |||
| 693 | void led_matrix_flags_step(void) { | ||
| 694 | led_matrix_flags_step_helper(true); | ||
| 695 | } | ||
| 696 | |||
| 697 | void 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 | |||
| 711 | void led_matrix_flags_step_reverse_noeeprom(void) { | ||
| 712 | led_matrix_flags_step_reverse_helper(false); | ||
| 713 | } | ||
| 714 | |||
| 715 | void 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); | |||
| 183 | led_flags_t led_matrix_get_flags(void); | 183 | led_flags_t led_matrix_get_flags(void); |
| 184 | void led_matrix_set_flags(led_flags_t flags); | 184 | void led_matrix_set_flags(led_flags_t flags); |
| 185 | void led_matrix_set_flags_noeeprom(led_flags_t flags); | 185 | void led_matrix_set_flags_noeeprom(led_flags_t flags); |
| 186 | void led_matrix_flags_step_noeeprom(void); | ||
| 187 | void led_matrix_flags_step(void); | ||
| 188 | void led_matrix_flags_step_reverse_noeeprom(void); | ||
| 189 | void led_matrix_flags_step_reverse(void); | ||
| 186 | 190 | ||
| 187 | #ifdef LED_MATRIX_MODE_NAME_ENABLE | 191 | #ifdef LED_MATRIX_MODE_NAME_ENABLE |
| 188 | const char *led_matrix_get_mode_name(uint8_t mode); | 192 | const 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 | ||
| 387 | void mousekey_on(uint8_t code) { | 387 | void 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 | ||
| 25 | typedef struct QP_PACKED qff_font_descriptor_v1_t { | 25 | typedef 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 | ||
| 54 | typedef struct QP_PACKED qff_ascii_glyph_v1_t { | 54 | typedef 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 | ||
| 58 | STATIC_ASSERT(sizeof(qff_ascii_glyph_v1_t) == 3, "qff_ascii_glyph_v1_t must be 3 bytes in v1 of QFF"); | 58 | STATIC_ASSERT(sizeof(qff_ascii_glyph_v1_t) == 3, "qff_ascii_glyph_v1_t must be 3 bytes in v1 of QFF"); |
| 59 | 59 | ||
| 60 | typedef struct QP_PACKED qff_ascii_glyph_table_v1_t { | 60 | typedef 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 | ||
| 72 | typedef struct QP_PACKED qff_unicode_glyph_v1_t { | 72 | typedef 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 | ||
| 77 | STATIC_ASSERT(sizeof(qff_unicode_glyph_v1_t) == 6, "qff_unicode_glyph_v1_t must be 6 bytes in v1 of QFF"); | 77 | STATIC_ASSERT(sizeof(qff_unicode_glyph_v1_t) == 6, "qff_unicode_glyph_v1_t must be 6 bytes in v1 of QFF"); |
| 78 | 78 | ||
| 79 | typedef struct QP_PACKED qff_unicode_glyph_table_v1_t { | 79 | typedef 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 | ||
| 27 | bool qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette, bool *is_panel_native) { | 27 | bool 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 | ||
| 22 | typedef struct QP_PACKED qgf_block_header_v1_t { | 22 | typedef 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 | ||
| 35 | typedef struct QP_PACKED qgf_graphics_descriptor_v1_t { | 35 | typedef 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 | ||
| 55 | typedef struct QP_PACKED qgf_frame_offsets_v1_t { | 55 | typedef 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 | ||
| 67 | typedef struct QP_PACKED qgf_frame_v1_t { | 67 | typedef 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 | ||
| 86 | typedef struct QP_PACKED qgf_palette_entry_v1_t { | 86 | typedef 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 | ||
| 92 | STATIC_ASSERT(sizeof(qgf_palette_entry_v1_t) == 3, "Palette entry is not 3 bytes in size"); | 92 | STATIC_ASSERT(sizeof(qgf_palette_entry_v1_t) == 3, "Palette entry is not 3 bytes in size"); |
| 93 | 93 | ||
| 94 | typedef struct QP_PACKED qgf_palette_v1_t { | 94 | typedef 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 | ||
| 106 | typedef struct QP_PACKED qgf_delta_v1_t { | 106 | typedef 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 | ||
| 121 | typedef struct QP_PACKED qgf_data_v1_t { | 121 | typedef 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) | |||
| 52 | void qp_internal_fill_pixdata(painter_device_t device, uint32_t num_pixels, uint8_t hue, uint8_t sat, uint8_t val) { | 52 | void 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. |
| 13 | typedef union QP_PACKED qp_pixel_t { | 14 | typedef 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 | ||
| 251 | endif | 252 | endif |
| 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 | ||
| 8 | static bool PIXEL_RAIN(effect_params_t* params) { | 8 | static 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}}; | |||
| 72 | last_hit_t g_last_hit_tracker; | 72 | last_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 | ||
| 79 | static 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 |
| 76 | static bool suspend_state = false; | 83 | static bool suspend_state = false; |
| 77 | static uint8_t rgb_last_enable = UINT8_MAX; | 84 | static uint8_t rgb_last_enable = UINT8_MAX; |
| 78 | static uint8_t rgb_last_effect = UINT8_MAX; | 85 | static uint8_t rgb_last_effect = UINT8_MAX; |
| 79 | static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false}; | 86 | static uint8_t rgb_current_effect = 0; |
| 80 | static rgb_task_states rgb_task_state = SYNCING; | 87 | static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false}; |
| 88 | static rgb_task_states rgb_task_state = SYNCING; | ||
| 81 | 89 | ||
| 82 | // double buffers | 90 | // double buffers |
| 83 | static uint32_t rgb_timer_buffer; | 91 | static 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) { | |||
| 377 | void rgb_matrix_task(void) { | 396 | void 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 | ||
| 761 | void 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 | |||
| 775 | void rgb_matrix_flags_step_noeeprom(void) { | ||
| 776 | rgb_matrix_flags_step_helper(false); | ||
| 777 | } | ||
| 778 | |||
| 779 | void rgb_matrix_flags_step(void) { | ||
| 780 | rgb_matrix_flags_step_helper(true); | ||
| 781 | } | ||
| 782 | |||
| 783 | void 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 | |||
| 797 | void rgb_matrix_flags_step_reverse_noeeprom(void) { | ||
| 798 | rgb_matrix_flags_step_reverse_helper(false); | ||
| 799 | } | ||
| 800 | |||
| 801 | void 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); | |||
| 218 | led_flags_t rgb_matrix_get_flags(void); | 218 | led_flags_t rgb_matrix_get_flags(void); |
| 219 | void rgb_matrix_set_flags(led_flags_t flags); | 219 | void rgb_matrix_set_flags(led_flags_t flags); |
| 220 | void rgb_matrix_set_flags_noeeprom(led_flags_t flags); | 220 | void rgb_matrix_set_flags_noeeprom(led_flags_t flags); |
| 221 | void rgb_matrix_flags_step_noeeprom(void); | ||
| 222 | void rgb_matrix_flags_step(void); | ||
| 223 | void rgb_matrix_flags_step_reverse_noeeprom(void); | ||
| 224 | void rgb_matrix_flags_step_reverse(void); | ||
| 221 | void rgb_matrix_update_pwm_buffers(void); | 225 | void rgb_matrix_update_pwm_buffers(void); |
| 222 | 226 | ||
| 223 | #ifdef RGB_MATRIX_MODE_NAME_ENABLE | 227 | #ifdef RGB_MATRIX_MODE_NAME_ENABLE |
