aboutsummaryrefslogtreecommitdiffstats
path: root/quantum
diff options
context:
space:
mode:
Diffstat (limited to 'quantum')
-rw-r--r--quantum/action.c5
-rw-r--r--quantum/action.h2
-rw-r--r--quantum/action_tapping.c178
-rw-r--r--quantum/action_tapping.h40
-rw-r--r--quantum/action_util.c52
-rw-r--r--quantum/action_util.h21
-rw-r--r--quantum/debounce.h7
-rw-r--r--quantum/debounce/asym_eager_defer_pk.c167
-rw-r--r--quantum/debounce/none.c8
-rw-r--r--quantum/debounce/sym_defer_g.c39
-rw-r--r--quantum/debounce/sym_defer_pk.c151
-rw-r--r--quantum/debounce/sym_defer_pr.c156
-rw-r--r--quantum/debounce/sym_eager_pk.c127
-rw-r--r--quantum/debounce/sym_eager_pr.c138
-rw-r--r--quantum/debounce/tests/debounce_test_common.cpp6
-rw-r--r--quantum/keyboard.c12
-rw-r--r--quantum/keycode_config.c11
-rw-r--r--quantum/keycodes.h22
-rw-r--r--quantum/led_matrix/led_matrix.c82
-rw-r--r--quantum/led_matrix/led_matrix.h4
-rw-r--r--quantum/matrix.c6
-rw-r--r--quantum/matrix_common.c6
-rw-r--r--quantum/mousekey.c3
-rw-r--r--quantum/painter/qff.h10
-rw-r--r--quantum/painter/qgf.c2
-rw-r--r--quantum/painter/qgf.h16
-rw-r--r--quantum/painter/qp_draw_core.c22
-rw-r--r--quantum/painter/qp_draw_ellipse.c2
-rw-r--r--quantum/painter/qp_internal.h8
-rw-r--r--quantum/painter/qp_internal_formats.h17
-rw-r--r--quantum/painter/rules.mk3
-rw-r--r--quantum/pointing_device/pointing_device.h4
-rw-r--r--quantum/process_keycode/process_combo.c4
-rw-r--r--quantum/process_keycode/process_dynamic_macro.c23
-rw-r--r--quantum/process_keycode/process_led_matrix.c6
-rw-r--r--quantum/process_keycode/process_rgb_matrix.c14
-rw-r--r--quantum/process_keycode/process_tap_dance.c122
-rw-r--r--quantum/process_keycode/process_tap_dance.h11
-rw-r--r--quantum/quantum.c4
-rw-r--r--quantum/rgb_matrix/animations/pixel_rain_anim.h25
-rw-r--r--quantum/rgb_matrix/animations/raindrops_anim.h9
-rw-r--r--quantum/rgb_matrix/rgb_matrix.c82
-rw-r--r--quantum/rgb_matrix/rgb_matrix.h4
43 files changed, 1057 insertions, 574 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) {
if (IS_NOEVENT(record->event)) {
return;
}
+#ifdef SPECULATIVE_HOLD
+ if (record->event.pressed) {
+ speculative_key_settled(record);
+ }
+#endif // SPECULATIVE_HOLD
#ifdef FLOW_TAP_TERM
flow_tap_update_last_event(record);
#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" {
/* tapping count and state */
typedef struct {
bool interrupted : 1;
- bool reserved2 : 1;
+ bool speculated : 1;
bool reserved1 : 1;
bool reserved0 : 1;
uint8_t count : 4;
diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c
index 9e48fc9d6c..6baf7721ad 100644
--- a/quantum/action_tapping.c
+++ b/quantum/action_tapping.c
@@ -6,8 +6,10 @@
#include "action_tapping.h"
#include "action_util.h"
#include "keycode.h"
+#include "keycode_config.h"
#include "quantum_keycodes.h"
#include "timer.h"
+#include "wait.h"
#ifndef NO_ACTION_TAPPING
@@ -51,6 +53,21 @@ __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *re
}
# endif
+# ifdef SPECULATIVE_HOLD
+typedef struct {
+ keypos_t key;
+ uint8_t mods;
+} speculative_key_t;
+# define SPECULATIVE_KEYS_SIZE 8
+static speculative_key_t speculative_keys[SPECULATIVE_KEYS_SIZE] = {};
+static uint8_t num_speculative_keys = 0;
+static uint8_t prev_speculative_mods = 0;
+static uint8_t speculative_mods = 0;
+
+/** Handler to be called on incoming press events. */
+static void speculative_key_press(keyrecord_t *record);
+# endif // SPECULATIVE_HOLD
+
# if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
# define REGISTERED_TAPS_SIZE 8
// 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);
* FIXME: Needs doc
*/
void action_tapping_process(keyrecord_t record) {
+# ifdef SPECULATIVE_HOLD
+ prev_speculative_mods = speculative_mods;
+ if (record.event.pressed) {
+ speculative_key_press(&record);
+ }
+# endif // SPECULATIVE_HOLD
+
if (process_tapping(&record)) {
if (IS_EVENT(record.event)) {
ac_dprintf("processed: ");
@@ -145,6 +169,12 @@ void action_tapping_process(keyrecord_t record) {
}
}
+# ifdef SPECULATIVE_HOLD
+ if (speculative_mods != prev_speculative_mods) {
+ send_keyboard_report();
+ }
+# endif // SPECULATIVE_HOLD
+
// process waiting_buffer
if (IS_EVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) {
ac_dprintf("---- action_exec: process waiting_buffer -----\n");
@@ -708,6 +738,147 @@ void waiting_buffer_scan_tap(void) {
}
}
+# ifdef SPECULATIVE_HOLD
+static void debug_speculative_keys(void) {
+ ac_dprintf("mods = { ");
+ for (int8_t i = 0; i < num_speculative_keys; ++i) {
+ ac_dprintf("%02X ", speculative_keys[i].mods);
+ }
+ ac_dprintf("}, keys = { ");
+ for (int8_t i = 0; i < num_speculative_keys; ++i) {
+ ac_dprintf("%02X%02X ", speculative_keys[i].key.row, speculative_keys[i].key.col);
+ }
+ ac_dprintf("}\n");
+}
+
+// Find key in speculative_keys. Returns num_speculative_keys if not found.
+static int8_t speculative_keys_find(keypos_t key) {
+ uint8_t i;
+ for (i = 0; i < num_speculative_keys; ++i) {
+ if (KEYEQ(speculative_keys[i].key, key)) {
+ break;
+ }
+ }
+ return i;
+}
+
+static void speculative_key_press(keyrecord_t *record) {
+ if (num_speculative_keys >= SPECULATIVE_KEYS_SIZE) { // Overflow!
+ ac_dprintf("SPECULATIVE KEYS OVERFLOW: IGNORING EVENT\n");
+ return; // Don't trigger: speculative_keys is full.
+ }
+ if (speculative_keys_find(record->event.key) < num_speculative_keys) {
+ return; // Don't trigger: key is already in speculative_keys.
+ }
+
+ const uint16_t keycode = get_record_keycode(record, false);
+ if (!IS_QK_MOD_TAP(keycode)) {
+ return; // Don't trigger: not a mod-tap key.
+ }
+
+ uint8_t mods = mod_config(QK_MOD_TAP_GET_MODS(keycode));
+ if ((mods & 0x10) != 0) { // Unpack 5-bit mods to 8-bit representation.
+ mods <<= 4;
+ }
+ if ((~(get_mods() | speculative_mods) & mods) == 0) {
+ return; // Don't trigger: mods are already active.
+ }
+
+ // Don't do Speculative Hold when there are non-speculated buffered events,
+ // since that could result in sending keys out of order.
+ for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
+ if (!waiting_buffer[i].tap.speculated) {
+ return;
+ }
+ }
+
+ if (get_speculative_hold(keycode, record)) {
+ record->tap.speculated = true;
+ speculative_mods |= mods;
+ // Remember the keypos and mods associated with this key.
+ speculative_keys[num_speculative_keys] = (speculative_key_t){
+ .key = record->event.key,
+ .mods = mods,
+ };
+ ++num_speculative_keys;
+
+ ac_dprintf("Speculative Hold: ");
+ debug_speculative_keys();
+ }
+}
+
+uint8_t get_speculative_mods(void) {
+ return speculative_mods;
+}
+
+__attribute__((weak)) bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) {
+ const uint8_t mods = mod_config(QK_MOD_TAP_GET_MODS(keycode));
+ return (mods & (MOD_LCTL | MOD_LSFT)) == (mods & (MOD_HYPR));
+}
+
+void speculative_key_settled(keyrecord_t *record) {
+ if (num_speculative_keys == 0) {
+ return; // Early return when there are no active speculative keys.
+ }
+
+ uint8_t i = speculative_keys_find(record->event.key);
+
+ const uint16_t keycode = get_record_keycode(record, false);
+ if (IS_QK_MOD_TAP(keycode) && record->tap.count == 0) { // MT hold press.
+ if (i < num_speculative_keys) {
+ --num_speculative_keys;
+ const uint8_t cleared_mods = speculative_keys[i].mods;
+
+ if (num_speculative_keys) {
+ speculative_mods &= ~cleared_mods;
+ // Don't call send_keyboard_report() here; allow default
+ // handling to reapply the mod before the next report.
+
+ // Remove the ith entry from speculative_keys.
+ for (uint8_t j = i; j < num_speculative_keys; ++j) {
+ speculative_keys[j] = speculative_keys[j + 1];
+ }
+ } else {
+ speculative_mods = 0;
+ }
+
+ ac_dprintf("Speculative Hold: settled %02x, ", cleared_mods);
+ debug_speculative_keys();
+ }
+ } else { // Tap press event; cancel speculatively-held mod.
+ if (i >= num_speculative_keys) {
+ i = 0;
+ }
+
+ // Clear mods for the ith key and all keys that follow.
+ uint8_t cleared_mods = 0;
+ for (uint8_t j = i; j < num_speculative_keys; ++j) {
+ cleared_mods |= speculative_keys[j].mods;
+ }
+
+ num_speculative_keys = i; // Remove ith and following entries.
+
+ if ((prev_speculative_mods & cleared_mods) != 0) {
+# ifdef DUMMY_MOD_NEUTRALIZER_KEYCODE
+ neutralize_flashing_modifiers(get_mods() | prev_speculative_mods);
+# endif // DUMMY_MOD_NEUTRALIZER_KEYCODE
+ }
+
+ if (num_speculative_keys) {
+ speculative_mods &= ~cleared_mods;
+ } else {
+ speculative_mods = 0;
+ }
+
+ send_keyboard_report();
+ wait_ms(TAP_CODE_DELAY);
+
+ ac_dprintf("Speculative Hold: canceled %02x, ", cleared_mods);
+ debug_speculative_keys();
+ }
+}
+# endif // SPECULATIVE_HOLD
+
# if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
static void registered_taps_add(keypos_t key) {
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)
return false;
}
+// Checks both flow_tap_expired flag and elapsed time to determine
+// if the key is within the flow tap term.
+bool within_flow_tap_term(uint16_t keycode, keyrecord_t *record) {
+ uint16_t term = get_flow_tap_term(keycode, record, flow_tap_prev_keycode);
+ return !flow_tap_expired && TIMER_DIFF_16(record->event.time, flow_tap_prev_time) <= term;
+}
+
// By default, enable Flow Tap for the keys in the main alphas area and Space.
// This should work reasonably even if the layout is remapped on the host to an
// 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);
bool get_retro_tapping(uint16_t keycode, keyrecord_t *record);
bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record);
+#ifdef SPECULATIVE_HOLD
+/** Gets the currently active speculative mods. */
+uint8_t get_speculative_mods(void);
+
+/**
+ * Callback to say if a mod-tap key may be speculatively held.
+ *
+ * By default, speculative hold is enabled for mod-tap keys where the mod is
+ * Ctrl, Shift, and Ctrl+Shift for either hand.
+ *
+ * @param keycode Keycode of the mod-tap key.
+ * @param record Record associated with the mod-tap press event.
+ * @return True if the mod-tap key may be speculatively held.
+ */
+bool get_speculative_hold(uint16_t keycode, keyrecord_t *record);
+
+/**
+ * Handler to be called on press events after tap-holds are settled.
+ *
+ * This function is to be called in process_record() in action.c, that is, just
+ * after tap-hold events are settled as either tapped or held. When `record`
+ * corresponds to a speculatively-held key, the speculative mod is cleared.
+ *
+ * @param record Record associated with the mod-tap press event.
+ */
+void speculative_key_settled(keyrecord_t *record);
+#else
+# define get_speculative_mods() 0
+#endif // SPECULATIVE_HOLD
+
#ifdef CHORDAL_HOLD
/**
* 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_
/** Updates the Flow Tap last key and timer. */
void flow_tap_update_last_event(keyrecord_t *record);
+
+/**
+ * Checks if the pressed key is within the flow tap term.
+ * Can be used to avoid triggering combos or other actions within the flow tap term.
+ *
+ * @param keycode The keycode of the pressed key.
+ * @param record The keyrecord of the pressed key.
+ * @return True if the pressed key is within the flow tap term; false otherwise.
+ */
+bool within_flow_tap_term(uint16_t keycode, keyrecord_t *record);
#endif // FLOW_TAP_TERM
#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/>.
#include "debug.h"
#include "action_util.h"
#include "action_layer.h"
+#include "action_tapping.h"
#include "timer.h"
#include "keycode_config.h"
#include <string.h>
@@ -46,9 +47,20 @@ extern inline void clear_keys(void);
#ifndef NO_ACTION_ONESHOT
static uint8_t oneshot_mods = 0;
static uint8_t oneshot_locked_mods = 0;
-uint8_t get_oneshot_locked_mods(void) {
+/**
+ * @brief Retrieve current state of locked oneshot modifiers.
+ *
+ * @return Current state of the locked oneshot modifier keys as a bitmask.
+ */
+uint8_t get_oneshot_locked_mods(void) {
return oneshot_locked_mods;
}
+/**
+ * Same as \ref get_oneshot_locked_mods but returns \ref mod_t for convenience.
+ */
+mod_t get_oneshot_locked_mod_state(void) {
+ return (mod_t)get_oneshot_locked_mods();
+}
void add_oneshot_locked_mods(uint8_t mods) {
if ((oneshot_locked_mods & mods) != mods) {
oneshot_locked_mods |= mods;
@@ -273,6 +285,10 @@ static uint8_t get_mods_for_report(void) {
}
#endif
+#ifdef SPECULATIVE_HOLD
+ mods |= get_speculative_mods();
+#endif
+
#ifdef KEY_OVERRIDE_ENABLE
// These need to be last to be able to properly control key overrides
mods &= ~suppressed_mods;
@@ -326,13 +342,20 @@ void send_keyboard_report(void) {
send_6kro_report();
}
-/** \brief Get mods
+/**
+ * @brief Retrieve current state of modifiers.
*
- * FIXME: needs doc
+ * @return Current state of the modifier keys as a bitmask.
*/
uint8_t get_mods(void) {
return real_mods;
}
+/**
+ * Same as \ref get_mods but returns \ref mod_t for convenience.
+ */
+mod_t get_mod_state(void) {
+ return (mod_t)get_mods();
+}
/** \brief add mods
*
* FIXME: needs doc
@@ -362,13 +385,20 @@ void clear_mods(void) {
real_mods = 0;
}
-/** \brief get weak mods
+/**
+ * @brief Retrieve current state of weak modifiers.
*
- * FIXME: needs doc
+ * @return Current state of the weak modifier keys as a bitmask.
*/
uint8_t get_weak_mods(void) {
return weak_mods;
}
+/**
+ * Same as \ref get_weak_mods but returns \ref mod_t for convenience.
+ */
+mod_t get_weak_mod_state(void) {
+ return (mod_t)get_weak_mods();
+}
/** \brief add weak mods
*
* FIXME: needs doc
@@ -423,14 +453,22 @@ void clear_suppressed_override_mods(void) {
#endif
#ifndef NO_ACTION_ONESHOT
-/** \brief get oneshot mods
+/**
+ * @brief Retrieve current state of oneshot modifiers.
*
- * FIXME: needs doc
+ * @return Current state of the oneshot modifier keys as a bitmask.
*/
uint8_t get_oneshot_mods(void) {
return oneshot_mods;
}
+/**
+ * Same as \ref get_oneshot_mods but returns \ref mod_t for convenience.
+ */
+mod_t get_oneshot_mod_state(void) {
+ return (mod_t)get_oneshot_mods();
+}
+
void add_oneshot_mods(uint8_t mods) {
if ((oneshot_mods & mods) != mods) {
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
diff --git a/quantum/action_util.h b/quantum/action_util.h
index 12192ebe37..f3178bba6e 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/>.
#pragma once
#include <stdint.h>
+
+#include "compiler_support.h"
#include "report.h"
#include "modifiers.h"
@@ -25,6 +27,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
extern "C" {
#endif
+typedef union {
+ uint8_t raw;
+ struct {
+ bool left_ctrl : 1;
+ bool left_shift : 1;
+ bool left_alt : 1;
+ bool left_gui : 1;
+ bool right_ctrl : 1;
+ bool right_shift : 1;
+ bool right_alt : 1;
+ bool right_gui : 1;
+ };
+} PACKED mod_t;
+STATIC_ASSERT(sizeof(mod_t) == sizeof(uint8_t), "Invalid size for 'mod_t'");
+
extern report_keyboard_t *keyboard_report;
#ifdef NKRO_ENABLE
extern report_nkro_t *nkro_report;
@@ -47,6 +64,7 @@ inline void clear_keys(void) {
/* modifier */
uint8_t get_mods(void);
+mod_t get_mod_state(void);
void add_mods(uint8_t mods);
void del_mods(uint8_t mods);
void set_mods(uint8_t mods);
@@ -54,6 +72,7 @@ void clear_mods(void);
/* weak modifier */
uint8_t get_weak_mods(void);
+mod_t get_weak_mod_state(void);
void add_weak_mods(uint8_t mods);
void del_weak_mods(uint8_t mods);
void set_weak_mods(uint8_t mods);
@@ -61,6 +80,7 @@ void clear_weak_mods(void);
/* oneshot modifier */
uint8_t get_oneshot_mods(void);
+mod_t get_oneshot_mod_state(void);
void add_oneshot_mods(uint8_t mods);
void del_oneshot_mods(uint8_t mods);
void set_oneshot_mods(uint8_t mods);
@@ -68,6 +88,7 @@ void clear_oneshot_mods(void);
bool has_oneshot_mods_timed_out(void);
uint8_t get_oneshot_locked_mods(void);
+mod_t get_oneshot_locked_mod_state(void);
void add_oneshot_locked_mods(uint8_t mods);
void set_oneshot_locked_mods(uint8_t mods);
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 @@
*
* @param raw The current key state
* @param cooked The debounced key state
- * @param num_rows Number of rows to debounce
* @param changed True if raw has changed since the last call
* @return true Cooked has new keychanges after debouncing
* @return false Cooked is the same as before
*/
-bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);
+bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed);
-void debounce_init(uint8_t num_rows);
-
-void debounce_free(void);
+void debounce_init(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 @@
-/*
- * Copyright 2017 Alex Ong <the.onga@gmail.com>
- * Copyright 2020 Andrei Purdea <andrei@purdea.ro>
- * Copyright 2021 Simon Arlott
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
-Asymetric per-key algorithm. After pressing a key, it immediately changes state,
-with no further inputs accepted until DEBOUNCE milliseconds have occurred. After
-releasing a key, that state is pushed after no changes occur for DEBOUNCE milliseconds.
-*/
+// Copyright 2017 Alex Ong <the.onga@gmail.com>
+// Copyright 2020 Andrei Purdea <andrei@purdea.ro>
+// Copyright 2021 Simon Arlott
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Asymetric per-key algorithm. After pressing a key, it immediately changes state,
+// with no further inputs accepted until DEBOUNCE milliseconds have occurred. After
+// releasing a key, that state is pushed after no changes occur for DEBOUNCE milliseconds.
#include "debounce.h"
#include "timer.h"
-#include <stdlib.h>
-
-#ifdef PROTOCOL_CHIBIOS
-# if CH_CFG_USE_MEMCORE == FALSE
-# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
-# endif
-#endif
+#include "util.h"
#ifndef DEBOUNCE
# define DEBOUNCE 5
@@ -43,44 +21,29 @@ releasing a key, that state is pushed after no changes occur for DEBOUNCE millis
# define DEBOUNCE 127
#endif
-#define ROW_SHIFTER ((matrix_row_t)1)
+#define DEBOUNCE_ELAPSED 0
+#if DEBOUNCE > 0
typedef struct {
bool pressed : 1;
uint8_t time : 7;
} debounce_counter_t;
-#if DEBOUNCE > 0
-static debounce_counter_t *debounce_counters;
-static fast_timer_t last_time;
-static bool counters_need_update;
-static bool matrix_need_update;
-static bool cooked_changed;
-
-# define DEBOUNCE_ELAPSED 0
-
-static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
-static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
-
-// we use num_rows rather than MATRIX_ROWS to support split keyboards
-void debounce_init(uint8_t num_rows) {
- debounce_counters = malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));
- int i = 0;
- for (uint8_t r = 0; r < num_rows; r++) {
- for (uint8_t c = 0; c < MATRIX_COLS; c++) {
- debounce_counters[i++].time = DEBOUNCE_ELAPSED;
- }
- }
-}
+// Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards
+static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED};
+static bool counters_need_update;
+static bool matrix_need_update;
+static bool cooked_changed;
-void debounce_free(void) {
- free(debounce_counters);
- debounce_counters = NULL;
-}
+static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time);
+static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]);
-bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
- bool updated_last = false;
- cooked_changed = false;
+void debounce_init(void) {}
+
+bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
+ static fast_timer_t last_time;
+ bool updated_last = false;
+ cooked_changed = false;
if (counters_need_update) {
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
last_time = now;
updated_last = true;
- if (elapsed_time > UINT8_MAX) {
- elapsed_time = UINT8_MAX;
- }
if (elapsed_time > 0) {
- update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time);
+ // Update debounce counters with elapsed timer clamped to 127 (maximum debounce)
+ update_debounce_counters_and_transfer_if_expired(raw, cooked, MIN(elapsed_time, 127));
}
}
@@ -102,74 +63,96 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
last_time = timer_read_fast();
}
- transfer_matrix_values(raw, cooked, num_rows);
+ transfer_matrix_values(raw, cooked);
}
return cooked_changed;
}
-static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) {
- debounce_counter_t *debounce_pointer = debounce_counters;
-
+/**
+ * @brief Processes per-key debounce counters and updates the debounced matrix state.
+ *
+ * This function iterates through each key in the matrix and updates its debounce counter
+ * based on the elapsed time. If the debounce period has expired, the debounced state is
+ * updated accordingly for key-down (eager) and key-up (defer) events.
+ *
+ * @param raw The current raw key state matrix.
+ * @param cooked The debounced key state matrix to be updated.
+ * @param elapsed_time The time elapsed since the last debounce update, in milliseconds.
+ */
+static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time) {
counters_need_update = false;
matrix_need_update = false;
- for (uint8_t row = 0; row < num_rows; row++) {
+ for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
+ uint16_t row_offset = row * MATRIX_COLS;
+
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
- matrix_row_t col_mask = (ROW_SHIFTER << col);
+ uint16_t index = row_offset + col;
- if (debounce_pointer->time != DEBOUNCE_ELAPSED) {
- if (debounce_pointer->time <= elapsed_time) {
- debounce_pointer->time = DEBOUNCE_ELAPSED;
+ if (debounce_counters[index].time != DEBOUNCE_ELAPSED) {
+ if (debounce_counters[index].time <= elapsed_time) {
+ debounce_counters[index].time = DEBOUNCE_ELAPSED;
- if (debounce_pointer->pressed) {
+ if (debounce_counters[index].pressed) {
// key-down: eager
matrix_need_update = true;
} else {
// key-up: defer
+ matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col);
matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask);
cooked_changed |= cooked_next ^ cooked[row];
cooked[row] = cooked_next;
}
} else {
- debounce_pointer->time -= elapsed_time;
+ debounce_counters[index].time -= elapsed_time;
counters_need_update = true;
}
}
- debounce_pointer++;
}
}
}
-static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
- debounce_counter_t *debounce_pointer = debounce_counters;
-
+/**
+ * @brief Applies debounced changes to the matrix state based on per-key counters.
+ *
+ * This function compares the raw and cooked key state matrices to detect changes.
+ * For each key, it updates the debounce counter and the debounced state according
+ * to the debounce algorithm. Key-down events are handled eagerly, while key-up
+ * events are deferred until the debounce period has elapsed.
+ *
+ * @param raw The current raw key state matrix.
+ * @param cooked The debounced key state matrix to be updated.
+ */
+static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]) {
matrix_need_update = false;
- for (uint8_t row = 0; row < num_rows; row++) {
- matrix_row_t delta = raw[row] ^ cooked[row];
+ for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
+ uint16_t row_offset = row * MATRIX_COLS;
+ matrix_row_t delta = raw[row] ^ cooked[row];
+
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
- matrix_row_t col_mask = (ROW_SHIFTER << col);
+ uint16_t index = row_offset + col;
+ matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col);
if (delta & col_mask) {
- if (debounce_pointer->time == DEBOUNCE_ELAPSED) {
- debounce_pointer->pressed = (raw[row] & col_mask);
- debounce_pointer->time = DEBOUNCE;
- counters_need_update = true;
+ if (debounce_counters[index].time == DEBOUNCE_ELAPSED) {
+ debounce_counters[index].pressed = (raw[row] & col_mask);
+ debounce_counters[index].time = DEBOUNCE;
+ counters_need_update = true;
- if (debounce_pointer->pressed) {
+ if (debounce_counters[index].pressed) {
// key-down: eager
cooked[row] ^= col_mask;
cooked_changed = true;
}
}
- } else if (debounce_pointer->time != DEBOUNCE_ELAPSED) {
- if (!debounce_pointer->pressed) {
+ } else if (debounce_counters[index].time != DEBOUNCE_ELAPSED) {
+ if (!debounce_counters[index].pressed) {
// key-up: defer
- debounce_pointer->time = DEBOUNCE_ELAPSED;
+ debounce_counters[index].time = DEBOUNCE_ELAPSED;
}
}
- debounce_pointer++;
}
}
}
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 @@
#include "debounce.h"
#include <string.h>
-void debounce_init(uint8_t num_rows) {}
+void debounce_init(void) {}
-bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
+bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
bool cooked_changed = false;
if (changed) {
- size_t matrix_size = num_rows * sizeof(matrix_row_t);
+ size_t matrix_size = MATRIX_ROWS_PER_HAND * sizeof(matrix_row_t);
if (memcmp(cooked, raw, matrix_size) != 0) {
memcpy(cooked, raw, matrix_size);
cooked_changed = true;
@@ -32,5 +32,3 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
return cooked_changed;
}
-
-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 @@
-/*
-Copyright 2017 Alex Ong<the.onga@gmail.com>
-Copyright 2021 Simon Arlott
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-/*
-Basic global debounce algorithm. Used in 99% of keyboards at time of implementation
-When no state changes have occured for DEBOUNCE milliseconds, we push the state.
-*/
+// Copyright 2017 Alex Ong<the.onga@gmail.com>
+// Copyright 2021 Simon Arlott
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Basic global debounce algorithm. Used in 99% of keyboards at time of implementation
+// When no state changes have occured for DEBOUNCE milliseconds, we push the state.
+
#include "debounce.h"
#include "timer.h"
#include <string.h>
@@ -31,19 +19,19 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
#endif
#if DEBOUNCE > 0
-static bool debouncing = false;
-static fast_timer_t debouncing_time;
-void debounce_init(uint8_t num_rows) {}
+void debounce_init(void) {}
-bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
- bool cooked_changed = false;
+bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
+ static fast_timer_t debouncing_time;
+ static bool debouncing = false;
+ bool cooked_changed = false;
if (changed) {
debouncing = true;
debouncing_time = timer_read_fast();
} else if (debouncing && timer_elapsed_fast(debouncing_time) >= DEBOUNCE) {
- size_t matrix_size = num_rows * sizeof(matrix_row_t);
+ size_t matrix_size = MATRIX_ROWS_PER_HAND * sizeof(matrix_row_t);
if (memcmp(cooked, raw, matrix_size) != 0) {
memcpy(cooked, raw, matrix_size);
cooked_changed = true;
@@ -54,7 +42,6 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
return cooked_changed;
}
-void debounce_free(void) {}
#else // no debouncing.
# include "none.c"
#endif
diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c
index 156535a373..b910571219 100644
--- a/quantum/debounce/sym_defer_pk.c
+++ b/quantum/debounce/sym_defer_pk.c
@@ -1,33 +1,14 @@
-/*
-Copyright 2017 Alex Ong<the.onga@gmail.com>
-Copyright 2020 Andrei Purdea<andrei@purdea.ro>
-Copyright 2021 Simon Arlott
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-/*
-Basic symmetric per-key algorithm. Uses an 8-bit counter per key.
-When no state changes have occured for DEBOUNCE milliseconds, we push the state.
-*/
+// Copyright 2017 Alex Ong<the.onga@gmail.com>
+// Copyright 2020 Andrei Purdea<andrei@purdea.ro>
+// Copyright 2021 Simon Arlott
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Basic symmetric per-key algorithm. Uses an 8-bit counter per key.
+// When no state changes have occured for DEBOUNCE milliseconds, we push the state.
#include "debounce.h"
#include "timer.h"
-#include <stdlib.h>
-
-#ifdef PROTOCOL_CHIBIOS
-# if CH_CFG_USE_MEMCORE == FALSE
-# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
-# endif
-#endif
+#include "util.h"
#ifndef DEBOUNCE
# define DEBOUNCE 5
@@ -39,40 +20,24 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
# define DEBOUNCE UINT8_MAX
#endif
-#define ROW_SHIFTER ((matrix_row_t)1)
+#define DEBOUNCE_ELAPSED 0
+#if DEBOUNCE > 0
typedef uint8_t debounce_counter_t;
+// Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards
+static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED};
+static bool counters_need_update;
+static bool cooked_changed;
-#if DEBOUNCE > 0
-static debounce_counter_t *debounce_counters;
-static fast_timer_t last_time;
-static bool counters_need_update;
-static bool cooked_changed;
-
-# define DEBOUNCE_ELAPSED 0
-
-static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
-static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
-
-// we use num_rows rather than MATRIX_ROWS to support split keyboards
-void debounce_init(uint8_t num_rows) {
- debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));
- int i = 0;
- for (uint8_t r = 0; r < num_rows; r++) {
- for (uint8_t c = 0; c < MATRIX_COLS; c++) {
- debounce_counters[i++] = DEBOUNCE_ELAPSED;
- }
- }
-}
+static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time);
+static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]);
-void debounce_free(void) {
- free(debounce_counters);
- debounce_counters = NULL;
-}
+void debounce_init(void) {}
-bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
- bool updated_last = false;
- cooked_changed = false;
+bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
+ static fast_timer_t last_time;
+ bool updated_last = false;
+ cooked_changed = false;
if (counters_need_update) {
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
last_time = now;
updated_last = true;
- if (elapsed_time > UINT8_MAX) {
- elapsed_time = UINT8_MAX;
- }
if (elapsed_time > 0) {
- update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time);
+ // Update debounce counters with elapsed timer clamped to UINT8_MAX
+ update_debounce_counters_and_transfer_if_expired(raw, cooked, MIN(elapsed_time, UINT8_MAX));
}
}
@@ -94,47 +57,73 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
last_time = timer_read_fast();
}
- start_debounce_counters(raw, cooked, num_rows);
+ start_debounce_counters(raw, cooked);
}
return cooked_changed;
}
-static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) {
- counters_need_update = false;
- debounce_counter_t *debounce_pointer = debounce_counters;
- for (uint8_t row = 0; row < num_rows; row++) {
+/**
+ * @brief Updates debounce counters and transfers debounced key states if the debounce period has expired.
+ *
+ * Iterates through each key in the matrix and checks its debounce counter. If the debounce period has expired
+ * for a key, the debounced state is updated to match the raw state. Otherwise, the debounce counter is decremented
+ * by the elapsed time and marked for further updates.
+ *
+ * @param raw The current raw key state matrix.
+ * @param cooked The debounced key state matrix to be updated.
+ * @param elapsed_time The time elapsed since the last debounce update, in milliseconds.
+ */
+static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time) {
+ counters_need_update = false;
+ for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
+ uint16_t row_offset = row * MATRIX_COLS;
+
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
- if (*debounce_pointer != DEBOUNCE_ELAPSED) {
- if (*debounce_pointer <= elapsed_time) {
- *debounce_pointer = DEBOUNCE_ELAPSED;
- matrix_row_t cooked_next = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col));
+ uint16_t index = row_offset + col;
+
+ if (debounce_counters[index] != DEBOUNCE_ELAPSED) {
+ if (debounce_counters[index] <= elapsed_time) {
+ debounce_counters[index] = DEBOUNCE_ELAPSED;
+ matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col);
+ matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask);
cooked_changed |= cooked[row] ^ cooked_next;
cooked[row] = cooked_next;
} else {
- *debounce_pointer -= elapsed_time;
+ debounce_counters[index] -= elapsed_time;
counters_need_update = true;
}
}
- debounce_pointer++;
}
}
}
-static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
- debounce_counter_t *debounce_pointer = debounce_counters;
- for (uint8_t row = 0; row < num_rows; row++) {
- matrix_row_t delta = raw[row] ^ cooked[row];
+/**
+ * @brief Initializes debounce counters for keys with changed states.
+ *
+ * For each key in the matrix, this function checks if the raw state differs from the debounced state.
+ * If a change is detected and the debounce counter has elapsed, the counter is set to the debounce period
+ * and marked for update. Otherwise, the counter is cleared.
+ *
+ * @param raw The current raw key state matrix.
+ * @param cooked The debounced key state matrix.
+ */
+static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]) {
+ for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
+ uint16_t row_offset = row * MATRIX_COLS;
+ matrix_row_t delta = raw[row] ^ cooked[row];
+
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
- if (delta & (ROW_SHIFTER << col)) {
- if (*debounce_pointer == DEBOUNCE_ELAPSED) {
- *debounce_pointer = DEBOUNCE;
- counters_need_update = true;
+ uint16_t index = row_offset + col;
+
+ if (delta & (MATRIX_ROW_SHIFTER << col)) {
+ if (debounce_counters[index] == DEBOUNCE_ELAPSED) {
+ debounce_counters[index] = DEBOUNCE;
+ counters_need_update = true;
}
} else {
- *debounce_pointer = DEBOUNCE_ELAPSED;
+ debounce_counters[index] = DEBOUNCE_ELAPSED;
}
- debounce_pointer++;
}
}
}
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 @@
-/*
-Copyright 2021 Chad Austin <chad@chadaustin.me>
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-/*
-Symmetric per-row debounce algorithm. Changes only apply when
-DEBOUNCE milliseconds have elapsed since the last change.
-*/
+// Copyright 2017 Alex Ong<the.onga@gmail.com>
+// Copyright 2020 Andrei Purdea<andrei@purdea.ro>
+// Copyright 2021 Simon Arlott
+// Copyright @filterpaper
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Basic symmetric per-row algorithm. Uses an 8-bit counter per row.
+// When no state changes have occured for DEBOUNCE milliseconds, we push the state.
#include "debounce.h"
#include "timer.h"
-#include <stdlib.h>
+#include "util.h"
#ifndef DEBOUNCE
# define DEBOUNCE 5
#endif
-static uint16_t last_time;
-// [row] milliseconds until key's state is considered debounced.
-static uint8_t* countdowns;
-// [row]
-static matrix_row_t* last_raw;
+// Maximum debounce: 255ms
+#if DEBOUNCE > UINT8_MAX
+# undef DEBOUNCE
+# define DEBOUNCE UINT8_MAX
+#endif
-void debounce_init(uint8_t num_rows) {
- countdowns = (uint8_t*)calloc(num_rows, sizeof(uint8_t));
- last_raw = (matrix_row_t*)calloc(num_rows, sizeof(matrix_row_t));
+#define DEBOUNCE_ELAPSED 0
- last_time = timer_read();
-}
+#if DEBOUNCE > 0
+typedef uint8_t debounce_counter_t;
+// Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards
+static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND] = {DEBOUNCE_ELAPSED};
+static bool counters_need_update;
+static bool cooked_changed;
-void debounce_free(void) {
- free(countdowns);
- countdowns = NULL;
- free(last_raw);
- last_raw = NULL;
-}
+static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time);
+static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]);
+
+void debounce_init(void) {}
+
+bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
+ static fast_timer_t last_time;
+ bool updated_last = false;
+ cooked_changed = false;
-bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
- uint16_t now = timer_read();
- uint16_t elapsed16 = TIMER_DIFF_16(now, last_time);
- last_time = now;
- uint8_t elapsed = (elapsed16 > 255) ? 255 : elapsed16;
- bool cooked_changed = false;
-
- uint8_t* countdown = countdowns;
-
- for (uint8_t row = 0; row < num_rows; ++row, ++countdown) {
- matrix_row_t raw_row = raw[row];
-
- if (raw_row != last_raw[row]) {
- *countdown = DEBOUNCE;
- last_raw[row] = raw_row;
- } else if (*countdown > elapsed) {
- *countdown -= elapsed;
- } else if (*countdown) {
- cooked_changed |= cooked[row] ^ raw_row;
- cooked[row] = raw_row;
- *countdown = 0;
+ if (counters_need_update) {
+ fast_timer_t now = timer_read_fast();
+ fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
+
+ last_time = now;
+ updated_last = true;
+
+ if (elapsed_time > 0) {
+ // Update debounce counters with elapsed timer clamped to UINT8_MAX
+ update_debounce_counters_and_transfer_if_expired(raw, cooked, MIN(elapsed_time, UINT8_MAX));
}
}
+ if (changed) {
+ if (!updated_last) {
+ last_time = timer_read_fast();
+ }
+
+ start_debounce_counters(raw, cooked);
+ }
+
return cooked_changed;
}
-bool debounce_active(void) {
- return true;
+/**
+ * @brief Updates debounce counters and transfers debounced row states if the debounce period has expired.
+ *
+ * Iterates through each row in the matrix and checks its debounce counter. If the debounce period has expired
+ * for a row, the debounced state is updated to match the raw state. Otherwise, the debounce counter is decremented
+ * by the elapsed time and marked for further updates.
+ *
+ * @param raw The current raw key state matrix.
+ * @param cooked The debounced key state matrix to be updated.
+ * @param elapsed_time The time elapsed since the last debounce update, in milliseconds.
+ */
+static inline void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t elapsed_time) {
+ counters_need_update = false;
+ for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
+ if (debounce_counters[row] != DEBOUNCE_ELAPSED) {
+ if (debounce_counters[row] <= elapsed_time) {
+ debounce_counters[row] = DEBOUNCE_ELAPSED;
+ cooked_changed |= cooked[row] ^ raw[row];
+ cooked[row] = raw[row];
+ } else {
+ debounce_counters[row] -= elapsed_time;
+ counters_need_update = true;
+ }
+ }
+ }
}
+
+/**
+ * @brief Initializes debounce counters for rows with changed states.
+ *
+ * For each row in the matrix, this function checks if the raw state differs from the debounced state.
+ * If a change is detected and the debounce counter has elapsed, the counter is set to the debounce period
+ * and marked for update. Otherwise, the counter is cleared.
+ *
+ * @param raw The current raw key state matrix.
+ * @param cooked The debounced key state matrix.
+ */
+static inline void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[]) {
+ for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
+ if (raw[row] != cooked[row]) {
+ debounce_counters[row] = DEBOUNCE;
+ counters_need_update = true;
+ } else {
+ debounce_counters[row] = DEBOUNCE_ELAPSED;
+ }
+ }
+}
+
+#else
+# include "none.c"
+#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.
#include "debounce.h"
#include "timer.h"
-#include <stdlib.h>
-
-#ifdef PROTOCOL_CHIBIOS
-# if CH_CFG_USE_MEMCORE == FALSE
-# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
-# endif
-#endif
+#include "util.h"
#ifndef DEBOUNCE
# define DEBOUNCE 5
@@ -39,41 +33,25 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
# define DEBOUNCE UINT8_MAX
#endif
-#define ROW_SHIFTER ((matrix_row_t)1)
+#define DEBOUNCE_ELAPSED 0
+#if DEBOUNCE > 0
typedef uint8_t debounce_counter_t;
+// Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards
+static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND * MATRIX_COLS] = {DEBOUNCE_ELAPSED};
+static bool counters_need_update;
+static bool matrix_need_update;
+static bool cooked_changed;
-#if DEBOUNCE > 0
-static debounce_counter_t *debounce_counters;
-static fast_timer_t last_time;
-static bool counters_need_update;
-static bool matrix_need_update;
-static bool cooked_changed;
-
-# define DEBOUNCE_ELAPSED 0
-
-static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
-static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
-
-// we use num_rows rather than MATRIX_ROWS to support split keyboards
-void debounce_init(uint8_t num_rows) {
- debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));
- int i = 0;
- for (uint8_t r = 0; r < num_rows; r++) {
- for (uint8_t c = 0; c < MATRIX_COLS; c++) {
- debounce_counters[i++] = DEBOUNCE_ELAPSED;
- }
- }
-}
+static inline void update_debounce_counters(uint8_t elapsed_time);
+static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]);
-void debounce_free(void) {
- free(debounce_counters);
- debounce_counters = NULL;
-}
+void debounce_init(void) {}
-bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
- bool updated_last = false;
- cooked_changed = false;
+bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
+ static fast_timer_t last_time;
+ bool updated_last = false;
+ cooked_changed = false;
if (counters_need_update) {
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
last_time = now;
updated_last = true;
- if (elapsed_time > UINT8_MAX) {
- elapsed_time = UINT8_MAX;
- }
if (elapsed_time > 0) {
- update_debounce_counters(num_rows, elapsed_time);
+ // Update debounce counters with elapsed timer clamped to UINT8_MAX
+ update_debounce_counters(MIN(elapsed_time, UINT8_MAX));
}
}
@@ -95,51 +71,74 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
last_time = timer_read_fast();
}
- transfer_matrix_values(raw, cooked, num_rows);
+ transfer_matrix_values(raw, cooked);
}
return cooked_changed;
}
-// If the current time is > debounce counter, set the counter to enable input.
-static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {
- counters_need_update = false;
- matrix_need_update = false;
- debounce_counter_t *debounce_pointer = debounce_counters;
- for (uint8_t row = 0; row < num_rows; row++) {
+/**
+ * @brief Updates per-key debounce counters and determines if matrix needs updating.
+ *
+ * Iterates through each key in the matrix and checks its debounce counter. If the debounce
+ * period has elapsed, the counter is reset and the matrix is marked for update. Otherwise,
+ * the counter is decremented by the elapsed time and marked for further updates if needed.
+ *
+ * @param elapsed_time The time elapsed since the last debounce update, in milliseconds.
+ */
+static inline void update_debounce_counters(uint8_t elapsed_time) {
+ counters_need_update = false;
+ matrix_need_update = false;
+
+ for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
+ uint16_t row_offset = row * MATRIX_COLS;
+
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
- if (*debounce_pointer != DEBOUNCE_ELAPSED) {
- if (*debounce_pointer <= elapsed_time) {
- *debounce_pointer = DEBOUNCE_ELAPSED;
- matrix_need_update = true;
+ uint16_t index = row_offset + col;
+
+ if (debounce_counters[index] != DEBOUNCE_ELAPSED) {
+ if (debounce_counters[index] <= elapsed_time) {
+ debounce_counters[index] = DEBOUNCE_ELAPSED;
+ matrix_need_update = true;
} else {
- *debounce_pointer -= elapsed_time;
+ debounce_counters[index] -= elapsed_time;
counters_need_update = true;
}
}
- debounce_pointer++;
}
}
}
-// upload from raw_matrix to final matrix;
-static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
- matrix_need_update = false;
- debounce_counter_t *debounce_pointer = debounce_counters;
- for (uint8_t row = 0; row < num_rows; row++) {
+/**
+ * @brief Transfers debounced key states from the raw matrix to the cooked matrix.
+ *
+ * For each key in the matrix, this function checks if its state has changed and if its
+ * debounce counter has elapsed. If so, the debounce counter is reset, the cooked matrix
+ * is updated to reflect the new state, and the matrix is marked for further updates.
+ *
+ * @param raw The current raw key state matrix.
+ * @param cooked The debounced key state matrix to be updated.
+ */
+static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]) {
+ matrix_need_update = false;
+
+ for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
+ uint16_t row_offset = row * MATRIX_COLS;
matrix_row_t delta = raw[row] ^ cooked[row];
matrix_row_t existing_row = cooked[row];
+
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
- matrix_row_t col_mask = (ROW_SHIFTER << col);
+ uint16_t index = row_offset + col;
+
+ matrix_row_t col_mask = (MATRIX_ROW_SHIFTER << col);
if (delta & col_mask) {
- if (*debounce_pointer == DEBOUNCE_ELAPSED) {
- *debounce_pointer = DEBOUNCE;
- counters_need_update = true;
+ if (debounce_counters[index] == DEBOUNCE_ELAPSED) {
+ debounce_counters[index] = DEBOUNCE;
+ counters_need_update = true;
existing_row ^= col_mask; // flip the bit.
cooked_changed = true;
}
}
- debounce_pointer++;
}
cooked[row] = existing_row;
}
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 @@
-/*
-Copyright 2019 Alex Ong<the.onga@gmail.com>
-Copyright 2021 Simon Arlott
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-/*
-Basic per-row algorithm. Uses an 8-bit counter per row.
-After pressing a key, it immediately changes state, and sets a counter.
-No further inputs are accepted until DEBOUNCE milliseconds have occurred.
-*/
+// Copyright 2017 Alex Ong<the.onga@gmail.com>
+// Copyright 2021 Simon Arlott
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Basic per-row algorithm. Uses an 8-bit counter per key.
+// After pressing a key, it immediately changes state, and sets a counter.
+// No further inputs are accepted until DEBOUNCE milliseconds have occurred.
#include "debounce.h"
#include "timer.h"
-#include <stdlib.h>
-
-#ifdef PROTOCOL_CHIBIOS
-# if CH_CFG_USE_MEMCORE == FALSE
-# error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.
-# endif
-#endif
+#include "util.h"
#ifndef DEBOUNCE
# define DEBOUNCE 5
@@ -39,37 +20,25 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred.
# define DEBOUNCE UINT8_MAX
#endif
-typedef uint8_t debounce_counter_t;
+#define DEBOUNCE_ELAPSED 0
#if DEBOUNCE > 0
-static bool matrix_need_update;
-
-static debounce_counter_t *debounce_counters;
-static fast_timer_t last_time;
-static bool counters_need_update;
-static bool cooked_changed;
-
-# define DEBOUNCE_ELAPSED 0
-
-static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
-static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
+typedef uint8_t debounce_counter_t;
+// Uses MATRIX_ROWS_PER_HAND instead of MATRIX_ROWS to support split keyboards
+static debounce_counter_t debounce_counters[MATRIX_ROWS_PER_HAND] = {DEBOUNCE_ELAPSED};
+static bool counters_need_update;
+static bool matrix_need_update;
+static bool cooked_changed;
-// we use num_rows rather than MATRIX_ROWS to support split keyboards
-void debounce_init(uint8_t num_rows) {
- debounce_counters = (debounce_counter_t *)malloc(num_rows * sizeof(debounce_counter_t));
- for (uint8_t r = 0; r < num_rows; r++) {
- debounce_counters[r] = DEBOUNCE_ELAPSED;
- }
-}
+static inline void update_debounce_counters(uint8_t elapsed_time);
+static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]);
-void debounce_free(void) {
- free(debounce_counters);
- debounce_counters = NULL;
-}
+void debounce_init(void) {}
-bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
- bool updated_last = false;
- cooked_changed = false;
+bool debounce(matrix_row_t raw[], matrix_row_t cooked[], bool changed) {
+ static fast_timer_t last_time;
+ bool updated_last = false;
+ cooked_changed = false;
if (counters_need_update) {
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
last_time = now;
updated_last = true;
- if (elapsed_time > UINT8_MAX) {
- elapsed_time = UINT8_MAX;
- }
if (elapsed_time > 0) {
- update_debounce_counters(num_rows, elapsed_time);
+ // Update debounce counters with elapsed timer clamped to UINT8_MAX
+ update_debounce_counters(MIN(elapsed_time, UINT8_MAX));
}
}
@@ -91,49 +58,64 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
last_time = timer_read_fast();
}
- transfer_matrix_values(raw, cooked, num_rows);
+ transfer_matrix_values(raw, cooked);
}
return cooked_changed;
}
-// If the current time is > debounce counter, set the counter to enable input.
-static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {
- counters_need_update = false;
- matrix_need_update = false;
- debounce_counter_t *debounce_pointer = debounce_counters;
- for (uint8_t row = 0; row < num_rows; row++) {
- if (*debounce_pointer != DEBOUNCE_ELAPSED) {
- if (*debounce_pointer <= elapsed_time) {
- *debounce_pointer = DEBOUNCE_ELAPSED;
- matrix_need_update = true;
+/**
+ * @brief Updates per-row debounce counters and determines if matrix needs updating.
+ *
+ * Iterates through each row in the matrix and checks its debounce counter. If the debounce
+ * period has elapsed, the counter is reset and the matrix is marked for update. Otherwise,
+ * the counter is decremented by the elapsed time and marked for further updates if needed.
+ *
+ * @param elapsed_time The time elapsed since the last debounce update, in milliseconds.
+ */
+static inline void update_debounce_counters(uint8_t elapsed_time) {
+ counters_need_update = false;
+ matrix_need_update = false;
+
+ for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
+ if (debounce_counters[row] != DEBOUNCE_ELAPSED) {
+ if (debounce_counters[row] <= elapsed_time) {
+ debounce_counters[row] = DEBOUNCE_ELAPSED;
+ matrix_need_update = true;
} else {
- *debounce_pointer -= elapsed_time;
+ debounce_counters[row] -= elapsed_time;
counters_need_update = true;
}
}
- debounce_pointer++;
}
}
-// upload from raw_matrix to final matrix;
-static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {
- matrix_need_update = false;
- debounce_counter_t *debounce_pointer = debounce_counters;
- for (uint8_t row = 0; row < num_rows; row++) {
+/**
+ * @brief Transfers debounced key states from the raw matrix to the cooked matrix.
+ *
+ * For each row in the matrix, this function checks if its state has changed and if its
+ * debounce counter has elapsed. If so, the debounce counter is reset, the cooked matrix
+ * is updated to reflect the new state, and the matrix is marked for further updates.
+ *
+ * @param raw The current raw key state matrix.
+ * @param cooked The debounced key state matrix
+ */
+static inline void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[]) {
+ matrix_need_update = false;
+
+ for (uint8_t row = 0; row < MATRIX_ROWS_PER_HAND; row++) {
matrix_row_t existing_row = cooked[row];
matrix_row_t raw_row = raw[row];
// determine new value basd on debounce pointer + raw value
if (existing_row != raw_row) {
- if (*debounce_pointer == DEBOUNCE_ELAPSED) {
- *debounce_pointer = DEBOUNCE;
+ if (debounce_counters[row] == DEBOUNCE_ELAPSED) {
+ debounce_counters[row] = DEBOUNCE;
cooked_changed |= cooked[row] ^ raw_row;
cooked[row] = raw_row;
counters_need_update = true;
}
}
- debounce_pointer++;
}
}
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() {
bool first = true;
/* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */
- debounce_init(MATRIX_ROWS);
+ debounce_init();
set_time(time_offset_);
simulate_async_tick(async_time_jumps_);
std::fill(std::begin(input_matrix_), std::end(input_matrix_), 0);
@@ -121,8 +121,6 @@ void DebounceTest::runEventsInternal() {
checkCookedMatrix(false, "debounce() modified cooked matrix");
advance_time(1);
}
-
- debounce_free();
}
void DebounceTest::runDebounce(bool changed) {
@@ -131,7 +129,7 @@ void DebounceTest::runDebounce(bool changed) {
reset_access_counter();
- bool cooked_changed = debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed);
+ bool cooked_changed = debounce(raw_matrix_, cooked_matrix_, changed);
if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) {
FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime() << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) << "\nraw_matrix:\n" << strMatrix(raw_matrix_);
diff --git a/quantum/keyboard.c b/quantum/keyboard.c
index 173c696e2d..e0c9373165 100644
--- a/quantum/keyboard.c
+++ b/quantum/keyboard.c
@@ -29,9 +29,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "debug.h"
#include "command.h"
#include "util.h"
+#include "host.h"
#include "sendchar.h"
#include "eeconfig.h"
#include "action_layer.h"
+#include "suspend.h"
#ifdef BOOTMAGIC_ENABLE
# include "bootmagic.h"
#endif
@@ -471,6 +473,7 @@ void keyboard_init(void) {
#ifdef CONNECTION_ENABLE
connection_init();
#endif
+ host_init();
led_init_ports();
#ifdef BACKLIGHT_ENABLE
backlight_init_ports();
@@ -561,6 +564,7 @@ void switch_events(uint8_t row, uint8_t col, bool pressed) {
#if defined(RGB_MATRIX_ENABLE)
rgb_matrix_handle_key_event(row, col, pressed);
#endif
+ wakeup_matrix_handle_key_event(row, col, pressed);
}
/**
@@ -576,6 +580,8 @@ static inline void generate_tick_event(void) {
}
}
+matrix_row_t matrix_previous[MATRIX_ROWS];
+
/**
* @brief This task scans the keyboards matrix and processes any key presses
* that occur.
@@ -589,8 +595,6 @@ static bool matrix_task(void) {
return false;
}
- static matrix_row_t matrix_previous[MATRIX_ROWS];
-
matrix_scan();
bool matrix_changed = false;
for (uint8_t row = 0; row < MATRIX_ROWS && !matrix_changed; row++) {
@@ -624,7 +628,7 @@ static bool matrix_task(void) {
if (row_changes & col_mask) {
const bool key_pressed = current_row & col_mask;
- if (process_keypress) {
+ if (process_keypress && !keypress_is_wakeup_key(row, col)) {
action_exec(MAKE_KEYEVENT(row, col, key_pressed));
}
@@ -699,6 +703,8 @@ void quantum_task(void) {
#ifdef LAYER_LOCK_ENABLE
layer_lock_task();
#endif
+
+ host_task();
}
/** \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..90b910e960 100644
--- a/quantum/keycode_config.c
+++ b/quantum/keycode_config.c
@@ -24,6 +24,7 @@ keymap_config_t keymap_config;
* and will return the corrected keycode, when appropriate.
*/
__attribute__((weak)) uint16_t keycode_config(uint16_t keycode) {
+#ifdef MAGIC_ENABLE
switch (keycode) {
case KC_CAPS_LOCK:
case KC_LOCKING_CAPS_LOCK:
@@ -115,6 +116,9 @@ __attribute__((weak)) uint16_t keycode_config(uint16_t keycode) {
default:
return keycode;
}
+#else
+ return keycode;
+#endif // MAGIC_ENABLE
}
/** \brief mod_config
@@ -124,6 +128,7 @@ __attribute__((weak)) uint16_t keycode_config(uint16_t keycode) {
*/
__attribute__((weak)) uint8_t mod_config(uint8_t mod) {
+#ifdef MAGIC_ENABLE
/**
* Note: This function is for the 5-bit packed mods, NOT the full 8-bit mods.
* More info about the mods can be seen in modifiers.h.
@@ -157,9 +162,11 @@ __attribute__((weak)) uint8_t mod_config(uint8_t mod) {
}
}
if (keymap_config.no_gui) {
- mod &= ~MOD_LGUI;
- mod &= ~MOD_RGUI;
+ if (mod & MOD_LGUI) {
+ mod &= ~MOD_RGUI;
+ }
}
+#endif // MAGIC_ENABLE
return mod;
}
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 @@
#pragma once
// clang-format off
-#define QMK_KEYCODES_VERSION "0.0.7"
-#define QMK_KEYCODES_VERSION_BCD 0x00000007
+#define QMK_KEYCODES_VERSION "0.0.8"
+#define QMK_KEYCODES_VERSION_BCD 0x00000008
#define QMK_KEYCODES_VERSION_MAJOR 0
#define QMK_KEYCODES_VERSION_MINOR 0
-#define QMK_KEYCODES_VERSION_PATCH 7
+#define QMK_KEYCODES_VERSION_PATCH 8
enum qk_keycode_ranges {
// Ranges
@@ -663,6 +663,8 @@ enum qk_keycode_defines {
QK_LED_MATRIX_BRIGHTNESS_DOWN = 0x7816,
QK_LED_MATRIX_SPEED_UP = 0x7817,
QK_LED_MATRIX_SPEED_DOWN = 0x7818,
+ QK_LED_MATRIX_FLAG_NEXT = 0x7819,
+ QK_LED_MATRIX_FLAG_PREVIOUS = 0x781A,
QK_UNDERGLOW_TOGGLE = 0x7820,
QK_UNDERGLOW_MODE_NEXT = 0x7821,
QK_UNDERGLOW_MODE_PREVIOUS = 0x7822,
@@ -697,6 +699,8 @@ enum qk_keycode_defines {
QK_RGB_MATRIX_VALUE_DOWN = 0x784A,
QK_RGB_MATRIX_SPEED_UP = 0x784B,
QK_RGB_MATRIX_SPEED_DOWN = 0x784C,
+ QK_RGB_MATRIX_FLAG_NEXT = 0x784D,
+ QK_RGB_MATRIX_FLAG_PREVIOUS = 0x784E,
QK_BOOTLOADER = 0x7C00,
QK_REBOOT = 0x7C01,
QK_DEBUG_TOGGLE = 0x7C02,
@@ -1352,6 +1356,8 @@ enum qk_keycode_defines {
LM_BRID = QK_LED_MATRIX_BRIGHTNESS_DOWN,
LM_SPDU = QK_LED_MATRIX_SPEED_UP,
LM_SPDD = QK_LED_MATRIX_SPEED_DOWN,
+ LM_FLGN = QK_LED_MATRIX_FLAG_NEXT,
+ LM_FLGP = QK_LED_MATRIX_FLAG_PREVIOUS,
UG_TOGG = QK_UNDERGLOW_TOGGLE,
UG_NEXT = QK_UNDERGLOW_MODE_NEXT,
UG_PREV = QK_UNDERGLOW_MODE_PREVIOUS,
@@ -1386,6 +1392,8 @@ enum qk_keycode_defines {
RM_VALD = QK_RGB_MATRIX_VALUE_DOWN,
RM_SPDU = QK_RGB_MATRIX_SPEED_UP,
RM_SPDD = QK_RGB_MATRIX_SPEED_DOWN,
+ RM_FLGN = QK_RGB_MATRIX_FLAG_NEXT,
+ RM_FLGP = QK_RGB_MATRIX_FLAG_PREVIOUS,
QK_BOOT = QK_BOOTLOADER,
QK_RBT = QK_REBOOT,
DB_TOGG = QK_DEBUG_TOGGLE,
@@ -1511,10 +1519,10 @@ enum qk_keycode_defines {
#define IS_MACRO_KEYCODE(code) ((code) >= QK_MACRO_0 && (code) <= QK_MACRO_31)
#define IS_CONNECTION_KEYCODE(code) ((code) >= QK_OUTPUT_AUTO && (code) <= QK_BLUETOOTH_PROFILE5)
#define IS_BACKLIGHT_KEYCODE(code) ((code) >= QK_BACKLIGHT_ON && (code) <= QK_BACKLIGHT_TOGGLE_BREATHING)
-#define IS_LED_MATRIX_KEYCODE(code) ((code) >= QK_LED_MATRIX_ON && (code) <= QK_LED_MATRIX_SPEED_DOWN)
+#define IS_LED_MATRIX_KEYCODE(code) ((code) >= QK_LED_MATRIX_ON && (code) <= QK_LED_MATRIX_FLAG_PREVIOUS)
#define IS_UNDERGLOW_KEYCODE(code) ((code) >= QK_UNDERGLOW_TOGGLE && (code) <= QK_UNDERGLOW_SPEED_DOWN)
#define IS_RGB_KEYCODE(code) ((code) >= RGB_MODE_PLAIN && (code) <= RGB_MODE_TWINKLE)
-#define IS_RGB_MATRIX_KEYCODE(code) ((code) >= QK_RGB_MATRIX_ON && (code) <= QK_RGB_MATRIX_SPEED_DOWN)
+#define IS_RGB_MATRIX_KEYCODE(code) ((code) >= QK_RGB_MATRIX_ON && (code) <= QK_RGB_MATRIX_FLAG_PREVIOUS)
#define IS_QUANTUM_KEYCODE(code) ((code) >= QK_BOOTLOADER && (code) <= QK_LAYER_LOCK)
#define IS_KB_KEYCODE(code) ((code) >= QK_KB_0 && (code) <= QK_KB_31)
#define IS_USER_KEYCODE(code) ((code) >= QK_USER_0 && (code) <= QK_USER_31)
@@ -1537,10 +1545,10 @@ enum qk_keycode_defines {
#define MACRO_KEYCODE_RANGE QK_MACRO_0 ... QK_MACRO_31
#define CONNECTION_KEYCODE_RANGE QK_OUTPUT_AUTO ... QK_BLUETOOTH_PROFILE5
#define BACKLIGHT_KEYCODE_RANGE QK_BACKLIGHT_ON ... QK_BACKLIGHT_TOGGLE_BREATHING
-#define LED_MATRIX_KEYCODE_RANGE QK_LED_MATRIX_ON ... QK_LED_MATRIX_SPEED_DOWN
+#define LED_MATRIX_KEYCODE_RANGE QK_LED_MATRIX_ON ... QK_LED_MATRIX_FLAG_PREVIOUS
#define UNDERGLOW_KEYCODE_RANGE QK_UNDERGLOW_TOGGLE ... QK_UNDERGLOW_SPEED_DOWN
#define RGB_KEYCODE_RANGE RGB_MODE_PLAIN ... RGB_MODE_TWINKLE
-#define RGB_MATRIX_KEYCODE_RANGE QK_RGB_MATRIX_ON ... QK_RGB_MATRIX_SPEED_DOWN
+#define RGB_MATRIX_KEYCODE_RANGE QK_RGB_MATRIX_ON ... QK_RGB_MATRIX_FLAG_PREVIOUS
#define QUANTUM_KEYCODE_RANGE QK_BOOTLOADER ... QK_LAYER_LOCK
#define KB_KEYCODE_RANGE QK_KB_0 ... QK_KB_31
#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..a751f08c75 100644
--- a/quantum/led_matrix/led_matrix.c
+++ b/quantum/led_matrix/led_matrix.c
@@ -70,12 +70,19 @@ uint8_t g_led_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}};
last_hit_t g_last_hit_tracker;
#endif // LED_MATRIX_KEYREACTIVE_ENABLED
+#ifndef LED_MATRIX_FLAG_STEPS
+# define LED_MATRIX_FLAG_STEPS {LED_FLAG_ALL, LED_FLAG_KEYLIGHT | LED_FLAG_MODIFIER, LED_FLAG_NONE}
+#endif
+static const uint8_t led_matrix_flag_steps[] = LED_MATRIX_FLAG_STEPS;
+#define LED_MATRIX_FLAG_STEPS_COUNT ARRAY_SIZE(led_matrix_flag_steps)
+
// internals
-static bool suspend_state = false;
-static uint8_t led_last_enable = UINT8_MAX;
-static uint8_t led_last_effect = UINT8_MAX;
-static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false};
-static led_task_states led_task_state = SYNCING;
+static bool suspend_state = false;
+static uint8_t led_last_enable = UINT8_MAX;
+static uint8_t led_last_effect = UINT8_MAX;
+static uint8_t led_current_effect = 0;
+static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false};
+static led_task_states led_task_state = SYNCING;
// double buffers
static uint32_t led_timer_buffer;
@@ -261,6 +268,17 @@ static void led_task_start(void) {
g_last_hit_tracker = last_hit_buffer;
#endif // LED_MATRIX_KEYREACTIVE_ENABLED
+ // Ideally we would also stop sending zeros to the LED driver PWM buffers
+ // while suspended and just do a software shutdown. This is a cheap hack for now.
+ bool suspend_backlight = suspend_state ||
+#if LED_MATRIX_TIMEOUT > 0
+ (last_input_activity_elapsed() > (uint32_t)LED_MATRIX_TIMEOUT) ||
+#endif // LED_MATRIX_TIMEOUT > 0
+ false;
+
+ // Set effect to be renedered
+ led_current_effect = suspend_backlight || !led_matrix_eeconfig.enable ? 0 : led_matrix_eeconfig.mode;
+
// next task
led_task_state = RENDERING;
}
@@ -342,15 +360,7 @@ static void led_task_flush(uint8_t effect) {
void led_matrix_task(void) {
led_task_timers();
- // Ideally we would also stop sending zeros to the LED driver PWM buffers
- // while suspended and just do a software shutdown. This is a cheap hack for now.
- bool suspend_backlight = suspend_state ||
-#if LED_MATRIX_TIMEOUT > 0
- (last_input_activity_elapsed() > (uint32_t)LED_MATRIX_TIMEOUT) ||
-#endif // LED_MATRIX_TIMEOUT > 0
- false;
-
- uint8_t effect = suspend_backlight || !led_matrix_eeconfig.enable ? 0 : led_matrix_eeconfig.mode;
+ uint8_t effect = led_current_effect;
switch (led_task_state) {
case STARTING:
@@ -661,6 +671,50 @@ void led_matrix_set_flags_noeeprom(led_flags_t flags) {
led_matrix_set_flags_eeprom_helper(flags, false);
}
+void led_matrix_flags_step_helper(bool write_to_eeprom) {
+ led_flags_t flags = led_matrix_get_flags();
+
+ uint8_t next = 0;
+ for (uint8_t i = 0; i < LED_MATRIX_FLAG_STEPS_COUNT; i++) {
+ if (led_matrix_flag_steps[i] == flags) {
+ next = i == LED_MATRIX_FLAG_STEPS_COUNT - 1 ? 0 : i + 1;
+ break;
+ }
+ }
+
+ led_matrix_set_flags_eeprom_helper(led_matrix_flag_steps[next], write_to_eeprom);
+}
+
+void led_matrix_flags_step_noeeprom(void) {
+ led_matrix_flags_step_helper(false);
+}
+
+void led_matrix_flags_step(void) {
+ led_matrix_flags_step_helper(true);
+}
+
+void led_matrix_flags_step_reverse_helper(bool write_to_eeprom) {
+ led_flags_t flags = led_matrix_get_flags();
+
+ uint8_t next = 0;
+ for (uint8_t i = 0; i < LED_MATRIX_FLAG_STEPS_COUNT; i++) {
+ if (led_matrix_flag_steps[i] == flags) {
+ next = i == 0 ? LED_MATRIX_FLAG_STEPS_COUNT - 1 : i - 1;
+ break;
+ }
+ }
+
+ led_matrix_set_flags_eeprom_helper(led_matrix_flag_steps[next], write_to_eeprom);
+}
+
+void led_matrix_flags_step_reverse_noeeprom(void) {
+ led_matrix_flags_step_reverse_helper(false);
+}
+
+void led_matrix_flags_step_reverse(void) {
+ led_matrix_flags_step_reverse_helper(true);
+}
+
// LED Matrix naming
#undef LED_MATRIX_EFFECT
#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);
led_flags_t led_matrix_get_flags(void);
void led_matrix_set_flags(led_flags_t flags);
void led_matrix_set_flags_noeeprom(led_flags_t flags);
+void led_matrix_flags_step_noeeprom(void);
+void led_matrix_flags_step(void);
+void led_matrix_flags_step_reverse_noeeprom(void);
+void led_matrix_flags_step_reverse(void);
#ifdef LED_MATRIX_MODE_NAME_ENABLE
const char *led_matrix_get_mode_name(uint8_t mode);
diff --git a/quantum/matrix.c b/quantum/matrix.c
index eee9c705ef..e99f754065 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -303,7 +303,7 @@ void matrix_init(void) {
memset(matrix, 0, sizeof(matrix));
memset(raw_matrix, 0, sizeof(raw_matrix));
- debounce_init(MATRIX_ROWS_PER_HAND);
+ debounce_init();
matrix_init_kb();
}
@@ -336,9 +336,9 @@ uint8_t matrix_scan(void) {
if (changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix));
#ifdef SPLIT_KEYBOARD
- changed = debounce(raw_matrix, matrix + thisHand, MATRIX_ROWS_PER_HAND, changed) | matrix_post_scan();
+ changed = debounce(raw_matrix, matrix + thisHand, changed) | matrix_post_scan();
#else
- changed = debounce(raw_matrix, matrix, MATRIX_ROWS_PER_HAND, changed);
+ changed = debounce(raw_matrix, matrix, changed);
matrix_scan_kb();
#endif
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) {
matrix[i] = 0;
}
- debounce_init(MATRIX_ROWS_PER_HAND);
+ debounce_init();
matrix_init_kb();
}
@@ -165,9 +165,9 @@ __attribute__((weak)) uint8_t matrix_scan(void) {
bool changed = matrix_scan_custom(raw_matrix);
#ifdef SPLIT_KEYBOARD
- changed = debounce(raw_matrix, matrix + thisHand, MATRIX_ROWS_PER_HAND, changed) | matrix_post_scan();
+ changed = debounce(raw_matrix, matrix + thisHand, changed) | matrix_post_scan();
#else
- changed = debounce(raw_matrix, matrix, MATRIX_ROWS_PER_HAND, changed);
+ changed = debounce(raw_matrix, matrix, changed);
matrix_scan_kb();
#endif
diff --git a/quantum/mousekey.c b/quantum/mousekey.c
index 885a5337ac..7d376ecf8c 100644
--- a/quantum/mousekey.c
+++ b/quantum/mousekey.c
@@ -386,7 +386,8 @@ void mousekey_task(void) {
void mousekey_on(uint8_t code) {
# ifdef MK_KINETIC_SPEED
- if (mouse_timer == 0) {
+ // Start kinetic timer when movement keycodes are pressed
+ if (mouse_timer == 0 && (IS_MOUSEKEY_MOVE(code) || IS_MOUSEKEY_WHEEL(code))) {
mouse_timer = timer_read();
}
# 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 @@
#define QFF_FONT_DESCRIPTOR_TYPEID 0x00
-typedef struct QP_PACKED qff_font_descriptor_v1_t {
+typedef struct PACKED qff_font_descriptor_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 20 }
uint32_t magic : 24; // constant, equal to 0x464651 ("QFF")
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)
#define QFF_GLYPH_OFFSET_BITS 18
#define QFF_GLYPH_OFFSET_MASK (((1 << QFF_GLYPH_OFFSET_BITS) - 1) << QFF_GLYPH_WIDTH_BITS)
-typedef struct QP_PACKED qff_ascii_glyph_v1_t {
+typedef struct PACKED qff_ascii_glyph_v1_t {
uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined
} qff_ascii_glyph_v1_t;
STATIC_ASSERT(sizeof(qff_ascii_glyph_v1_t) == 3, "qff_ascii_glyph_v1_t must be 3 bytes in v1 of QFF");
-typedef struct QP_PACKED qff_ascii_glyph_table_v1_t {
+typedef struct PACKED qff_ascii_glyph_table_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = 285 }
qff_ascii_glyph_v1_t glyph[95]; // 95 glyphs, 0x20..0x7E
} qff_ascii_glyph_table_v1_t;
@@ -69,14 +69,14 @@ STATIC_ASSERT(sizeof(qff_ascii_glyph_table_v1_t) == (sizeof(qgf_block_header_v1_
#define QFF_UNICODE_GLYPH_DESCRIPTOR_TYPEID 0x02
-typedef struct QP_PACKED qff_unicode_glyph_v1_t {
+typedef struct PACKED qff_unicode_glyph_v1_t {
uint32_t code_point : 24;
uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined
} qff_unicode_glyph_v1_t;
STATIC_ASSERT(sizeof(qff_unicode_glyph_v1_t) == 6, "qff_unicode_glyph_v1_t must be 6 bytes in v1 of QFF");
-typedef struct QP_PACKED qff_unicode_glyph_table_v1_t {
+typedef struct PACKED qff_unicode_glyph_table_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = (N * 6) }
qff_unicode_glyph_v1_t glyph[0]; // Extent of '0' signifies that this struct is immediately followed by the glyph data
} 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
bool qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette, bool *is_panel_native) {
// clang-format off
- static const struct QP_PACKED {
+ static const struct PACKED {
uint8_t bpp;
bool has_palette;
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 @@
/////////////////////////////////////////
// Common block header
-typedef struct QP_PACKED qgf_block_header_v1_t {
+typedef struct PACKED qgf_block_header_v1_t {
uint8_t type_id; // See each respective block type below.
uint8_t neg_type_id; // Negated type ID, used for detecting parsing errors.
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
#define QGF_GRAPHICS_DESCRIPTOR_TYPEID 0x00
-typedef struct QP_PACKED qgf_graphics_descriptor_v1_t {
+typedef struct PACKED qgf_graphics_descriptor_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 18 }
uint32_t magic : 24; // constant, equal to 0x464751 ("QGF")
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
#define QGF_FRAME_OFFSET_DESCRIPTOR_TYPEID 0x01
-typedef struct QP_PACKED qgf_frame_offsets_v1_t {
+typedef struct PACKED qgf_frame_offsets_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = (N * sizeof(uint32_t)) }
uint32_t offset[0]; // '0' signifies that this struct is immediately followed by the frame offsets
} qgf_frame_offsets_v1_t;
@@ -64,7 +64,7 @@ STATIC_ASSERT(sizeof(qgf_frame_offsets_v1_t) == sizeof(qgf_block_header_v1_t), "
#define QGF_FRAME_DESCRIPTOR_TYPEID 0x02
-typedef struct QP_PACKED qgf_frame_v1_t {
+typedef struct PACKED qgf_frame_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = 6 }
qp_image_format_t format : 8; // Frame format, see qp_internal_formats.h.
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
#define QGF_FRAME_PALETTE_DESCRIPTOR_TYPEID 0x03
-typedef struct QP_PACKED qgf_palette_entry_v1_t {
+typedef struct PACKED qgf_palette_entry_v1_t {
uint8_t h; // hue component: `[0,360)` degrees is mapped to `[0,255]` uint8_t.
uint8_t s; // saturation component: `[0,1]` is mapped to `[0,255]` uint8_t.
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 {
STATIC_ASSERT(sizeof(qgf_palette_entry_v1_t) == 3, "Palette entry is not 3 bytes in size");
-typedef struct QP_PACKED qgf_palette_v1_t {
+typedef struct PACKED qgf_palette_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x03, .neg_type_id = (~0x03), .length = (N * 3 * sizeof(uint8_t)) }
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
} qgf_palette_v1_t;
@@ -103,7 +103,7 @@ STATIC_ASSERT(sizeof(qgf_palette_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_pa
#define QGF_FRAME_DELTA_DESCRIPTOR_TYPEID 0x04
-typedef struct QP_PACKED qgf_delta_v1_t {
+typedef struct PACKED qgf_delta_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x04, .neg_type_id = (~0x04), .length = 8 }
uint16_t left; // The left pixel location to draw the delta image
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
#define QGF_FRAME_DATA_DESCRIPTOR_TYPEID 0x05
-typedef struct QP_PACKED qgf_data_v1_t {
+typedef struct PACKED qgf_data_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x05, .neg_type_id = (~0x05), .length = N }
uint8_t data[0]; // 0 signifies that this struct is immediately followed by the length of data specified in the header
} 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)
void qp_internal_fill_pixdata(painter_device_t device, uint32_t num_pixels, uint8_t hue, uint8_t sat, uint8_t val) {
painter_driver_t *driver = (painter_driver_t *)device;
uint32_t pixels_in_pixdata = qp_internal_num_pixels_in_buffer(device);
- num_pixels = QP_MIN(pixels_in_pixdata, num_pixels);
+ num_pixels = MIN(pixels_in_pixdata, num_pixels);
// Convert the color to native pixel format
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
uint32_t pixels_in_pixdata = qp_internal_num_pixels_in_buffer(device);
painter_driver_t *driver = (painter_driver_t *)device;
- uint16_t l = QP_MIN(left, right);
- uint16_t r = QP_MAX(left, right);
- uint16_t t = QP_MIN(top, bottom);
- uint16_t b = QP_MAX(top, bottom);
+ uint16_t l = MIN(left, right);
+ uint16_t r = MAX(left, right);
+ uint16_t t = MIN(top, bottom);
+ uint16_t b = MAX(top, bottom);
uint16_t w = r - l + 1;
uint16_t h = b - t + 1;
uint32_t remaining = w * h;
driver->driver_vtable->viewport(device, l, t, r, b);
while (remaining > 0) {
- uint32_t transmit = QP_MIN(remaining, pixels_in_pixdata);
+ uint32_t transmit = MIN(remaining, pixels_in_pixdata);
if (!driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, transmit)) {
return false;
}
@@ -260,10 +260,10 @@ bool qp_rect(painter_device_t device, uint16_t left, uint16_t top, uint16_t righ
}
// Cater for cases where people have submitted the coordinates backwards
- uint16_t l = QP_MIN(left, right);
- uint16_t r = QP_MAX(left, right);
- uint16_t t = QP_MIN(top, bottom);
- uint16_t b = QP_MAX(top, bottom);
+ uint16_t l = MIN(left, right);
+ uint16_t r = MAX(left, right);
+ uint16_t t = MIN(top, bottom);
+ uint16_t b = MAX(top, bottom);
uint16_t w = r - l + 1;
uint16_t h = b - t + 1;
@@ -281,7 +281,7 @@ bool qp_rect(painter_device_t device, uint16_t left, uint16_t top, uint16_t righ
ret = qp_internal_fillrect_helper_impl(device, l, t, r, b);
} else {
// Fill up the pixdata buffer with the required number of native pixels
- qp_internal_fill_pixdata(device, QP_MAX(w, h), hue, sat, val);
+ qp_internal_fill_pixdata(device, MAX(w, h), hue, sat, val);
// Draw 4x filled single-width rects to create an outline
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,
int16_t dx = 0;
int16_t dy = ((int16_t)sizey);
- qp_internal_fill_pixdata(device, QP_MAX(sizex, sizey), hue, sat, val);
+ qp_internal_fill_pixdata(device, MAX(sizex, sizey), hue, sat, val);
if (!qp_comms_start(device)) {
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 @@
#include "quantum.h"
#include "qp.h"
+#include "util.h" // PACKED/MIN/MAX
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Helpers
-// Mark certain types that there should be no padding bytes between members.
-#define QP_PACKED __attribute__((packed))
-
-// Min/max defines
-#define QP_MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
-#define QP_MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
-
#ifdef QUANTUM_PAINTER_DEBUG
# include <debug.h>
# 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 @@
#pragma once
+#include "color.h"
#include "compiler_support.h"
#include "qp_internal.h"
@@ -10,21 +11,13 @@
// Quantum Painter pixel formats
// Datatype containing a pixel's color. The internal member used is dependent on the external context.
-typedef union QP_PACKED qp_pixel_t {
+typedef union PACKED qp_pixel_t {
uint8_t mono;
uint8_t palette_idx;
- struct QP_PACKED {
- uint8_t h;
- uint8_t s;
- uint8_t v;
- } hsv888;
-
- struct QP_PACKED {
- uint8_t r;
- uint8_t g;
- uint8_t b;
- } rgb888;
+ hsv_t hsv888;
+
+ rgb_t rgb888;
uint16_t rgb565;
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)
SRC += \
$(DRIVER_PATH)/painter/generic/qp_surface_common.c \
$(DRIVER_PATH)/painter/generic/qp_surface_mono1bpp.c \
- $(DRIVER_PATH)/painter/generic/qp_surface_rgb565.c
+ $(DRIVER_PATH)/painter/generic/qp_surface_rgb565.c \
+ $(DRIVER_PATH)/painter/generic/qp_surface_rgb888.c
endif
# If dummy comms is needed, set up the required files
diff --git a/quantum/pointing_device/pointing_device.h b/quantum/pointing_device/pointing_device.h
index 5dfb9ce196..bb0e43af3c 100644
--- a/quantum/pointing_device/pointing_device.h
+++ b/quantum/pointing_device/pointing_device.h
@@ -39,6 +39,10 @@ typedef struct {
#elif defined(POINTING_DEVICE_DRIVER_pmw3320)
# include "drivers/sensors/pmw3320.h"
# define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW
+#elif defined(POINTING_DEVICE_DRIVER_paw3222)
+# include "spi_master.h"
+# include "drivers/sensors/paw3222.h"
+# define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW
#elif defined(POINTING_DEVICE_DRIVER_adns9800)
# include "spi_master.h"
# include "drivers/sensors/adns9800.h"
diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
index 3fdb732c41..dfab42e70d 100644
--- a/quantum/process_keycode/process_combo.c
+++ b/quantum/process_keycode/process_combo.c
@@ -347,6 +347,10 @@ void apply_combo(uint16_t combo_index, combo_t *combo) {
qrecord->combo_index = combo_index;
ACTIVATE_COMBO(combo);
+ if (key_count == 1) {
+ release_combo(combo_index, combo);
+ }
+
break;
} else {
// key was part of the combo but not the last one, "disable" it
diff --git a/quantum/process_keycode/process_dynamic_macro.c b/quantum/process_keycode/process_dynamic_macro.c
index f8e8256ac8..20e90c6e14 100644
--- a/quantum/process_keycode/process_dynamic_macro.c
+++ b/quantum/process_keycode/process_dynamic_macro.c
@@ -89,6 +89,11 @@ __attribute__((weak)) bool dynamic_macro_valid_key_user(uint16_t keycode, keyrec
#define DYNAMIC_MACRO_CURRENT_LENGTH(BEGIN, POINTER) ((int)(direction * ((POINTER) - (BEGIN))))
#define DYNAMIC_MACRO_CURRENT_CAPACITY(BEGIN, END2) ((int)(direction * ((END2) - (BEGIN)) + 1))
+#ifdef DYNAMIC_MACRO_KEEP_ORIGINAL_LAYER_STATE
+static layer_state_t dm1_layer_state;
+static layer_state_t dm2_layer_state;
+#endif
+
/**
* Start recording of the dynamic macro.
*
@@ -100,8 +105,16 @@ void dynamic_macro_record_start(keyrecord_t **macro_pointer, keyrecord_t *macro_
dynamic_macro_record_start_kb(direction);
- clear_keyboard();
+#ifdef DYNAMIC_MACRO_KEEP_ORIGINAL_LAYER_STATE
+ if (direction == 1) {
+ dm1_layer_state = layer_state;
+ } else if (direction == -1) {
+ dm2_layer_state = layer_state;
+ }
+#else
layer_clear();
+#endif
+ clear_keyboard();
*macro_pointer = macro_buffer;
}
@@ -118,7 +131,15 @@ void dynamic_macro_play(keyrecord_t *macro_buffer, keyrecord_t *macro_end, int8_
layer_state_t saved_layer_state = layer_state;
clear_keyboard();
+#ifdef DYNAMIC_MACRO_KEEP_ORIGINAL_LAYER_STATE
+ if (direction == 1) {
+ layer_state_set(dm1_layer_state);
+ } else if (direction == -1) {
+ layer_state_set(dm2_layer_state);
+ }
+#else
layer_clear();
+#endif
while (macro_buffer != macro_end) {
process_record(macro_buffer);
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) {
case QK_LED_MATRIX_SPEED_DOWN:
led_matrix_decrease_speed();
return false;
+ case QK_LED_MATRIX_FLAG_NEXT:
+ led_matrix_flags_step();
+ return false;
+ case QK_LED_MATRIX_FLAG_PREVIOUS:
+ led_matrix_flags_step_reverse();
+ return false;
}
}
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) {
rgb_matrix_decrease_speed();
}
return false;
+ case QK_RGB_MATRIX_FLAG_NEXT:
+ if (shifted) {
+ rgb_matrix_flags_step_reverse();
+ } else {
+ rgb_matrix_flags_step();
+ }
+ return false;
+ case QK_RGB_MATRIX_FLAG_PREVIOUS:
+ if (shifted) {
+ rgb_matrix_flags_step();
+ } else {
+ rgb_matrix_flags_step_reverse();
+ }
+ return false;
}
}
diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c
index 11df62763d..399f71a827 100644
--- a/quantum/process_keycode/process_tap_dance.c
+++ b/quantum/process_keycode/process_tap_dance.c
@@ -24,8 +24,46 @@
#include "keymap_introspection.h"
static uint16_t active_td;
+
+#ifndef TAP_DANCE_MAX_SIMULTANEOUS
+# define TAP_DANCE_MAX_SIMULTANEOUS 3
+#endif
+
+static tap_dance_state_t tap_dance_states[TAP_DANCE_MAX_SIMULTANEOUS];
+
static uint16_t last_tap_time;
+static tap_dance_state_t *tap_dance_get_or_allocate_state(uint8_t tap_dance_idx, bool allocate) {
+ uint8_t i;
+ if (tap_dance_idx >= tap_dance_count()) {
+ return NULL;
+ }
+ // Search for a state already used for this keycode
+ for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) {
+ if (tap_dance_states[i].in_use && tap_dance_states[i].index == tap_dance_idx) {
+ return &tap_dance_states[i];
+ }
+ }
+ // No existing state found; bail out if new state allocation is not allowed
+ if (!allocate) {
+ return NULL;
+ }
+ // Search for the first available state
+ for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) {
+ if (!tap_dance_states[i].in_use) {
+ tap_dance_states[i].index = tap_dance_idx;
+ tap_dance_states[i].in_use = true;
+ return &tap_dance_states[i];
+ }
+ }
+ // No states are available, tap dance won't happen
+ return NULL;
+}
+
+tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx) {
+ return tap_dance_get_or_allocate_state(tap_dance_idx, false);
+}
+
void tap_dance_pair_on_each_tap(tap_dance_state_t *state, void *user_data) {
tap_dance_pair_t *pair = (tap_dance_pair_t *)user_data;
@@ -86,58 +124,64 @@ static inline void _process_tap_dance_action_fn(tap_dance_state_t *state, void *
}
}
-static inline void process_tap_dance_action_on_each_tap(tap_dance_action_t *action) {
- action->state.count++;
- action->state.weak_mods = get_mods();
- action->state.weak_mods |= get_weak_mods();
+static inline void process_tap_dance_action_on_each_tap(tap_dance_action_t *action, tap_dance_state_t *state) {
+ state->count++;
+ state->weak_mods = get_mods();
+ state->weak_mods |= get_weak_mods();
#ifndef NO_ACTION_ONESHOT
- action->state.oneshot_mods = get_oneshot_mods();
+ state->oneshot_mods = get_oneshot_mods();
#endif
- _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_tap);
+ _process_tap_dance_action_fn(state, action->user_data, action->fn.on_each_tap);
}
-static inline void process_tap_dance_action_on_each_release(tap_dance_action_t *action) {
- _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_release);
+static inline void process_tap_dance_action_on_each_release(tap_dance_action_t *action, tap_dance_state_t *state) {
+ _process_tap_dance_action_fn(state, action->user_data, action->fn.on_each_release);
}
-static inline void process_tap_dance_action_on_reset(tap_dance_action_t *action) {
- _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_reset);
- del_weak_mods(action->state.weak_mods);
+static inline void process_tap_dance_action_on_reset(tap_dance_action_t *action, tap_dance_state_t *state) {
+ _process_tap_dance_action_fn(state, action->user_data, action->fn.on_reset);
+ del_weak_mods(state->weak_mods);
#ifndef NO_ACTION_ONESHOT
- del_mods(action->state.oneshot_mods);
+ del_mods(state->oneshot_mods);
#endif
send_keyboard_report();
- action->state = (const tap_dance_state_t){0};
+ // Clear the tap dance state and mark it as unused
+ memset(state, 0, sizeof(tap_dance_state_t));
}
-static inline void process_tap_dance_action_on_dance_finished(tap_dance_action_t *action) {
- if (!action->state.finished) {
- action->state.finished = true;
- add_weak_mods(action->state.weak_mods);
+static inline void process_tap_dance_action_on_dance_finished(tap_dance_action_t *action, tap_dance_state_t *state) {
+ if (!state->finished) {
+ state->finished = true;
+ add_weak_mods(state->weak_mods);
#ifndef NO_ACTION_ONESHOT
- add_mods(action->state.oneshot_mods);
+ add_mods(state->oneshot_mods);
#endif
send_keyboard_report();
- _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_dance_finished);
+ _process_tap_dance_action_fn(state, action->user_data, action->fn.on_dance_finished);
}
active_td = 0;
- if (!action->state.pressed) {
+ if (!state->pressed) {
// There will not be a key release event, so reset now.
- process_tap_dance_action_on_reset(action);
+ process_tap_dance_action_on_reset(action, state);
}
}
bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) {
tap_dance_action_t *action;
+ tap_dance_state_t *state;
if (!record->event.pressed) return false;
if (!active_td || keycode == active_td) return false;
- action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td));
- action->state.interrupted = true;
- action->state.interrupting_keycode = keycode;
- process_tap_dance_action_on_dance_finished(action);
+ action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td));
+ state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td));
+ if (state == NULL) {
+ return false;
+ }
+ state->interrupted = true;
+ state->interrupting_keycode = keycode;
+ process_tap_dance_action_on_dance_finished(action, state);
// Tap dance actions can leave some weak mods active (e.g., if the tap dance is mapped to a keycode with
// modifiers), but these weak mods should not affect the keypress which interrupted the tap dance.
@@ -151,8 +195,9 @@ bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) {
}
bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {
- int td_index;
+ uint8_t td_index;
tap_dance_action_t *action;
+ tap_dance_state_t *state;
switch (keycode) {
case QK_TAP_DANCE ... QK_TAP_DANCE_MAX:
@@ -161,16 +206,19 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {
return false;
}
action = tap_dance_get(td_index);
-
- action->state.pressed = record->event.pressed;
+ state = tap_dance_get_or_allocate_state(td_index, record->event.pressed);
+ if (state == NULL) {
+ return false;
+ }
+ state->pressed = record->event.pressed;
if (record->event.pressed) {
last_tap_time = timer_read();
- process_tap_dance_action_on_each_tap(action);
- active_td = action->state.finished ? 0 : keycode;
+ process_tap_dance_action_on_each_tap(action, state);
+ active_td = state->finished ? 0 : keycode;
} else {
- process_tap_dance_action_on_each_release(action);
- if (action->state.finished) {
- process_tap_dance_action_on_reset(action);
+ process_tap_dance_action_on_each_release(action, state);
+ if (state->finished) {
+ process_tap_dance_action_on_reset(action, state);
if (active_td == keycode) {
active_td = 0;
}
@@ -185,16 +233,18 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {
void tap_dance_task(void) {
tap_dance_action_t *action;
+ tap_dance_state_t *state;
if (!active_td || timer_elapsed(last_tap_time) <= GET_TAPPING_TERM(active_td, &(keyrecord_t){})) return;
action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td));
- if (!action->state.interrupted) {
- process_tap_dance_action_on_dance_finished(action);
+ state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td));
+ if (state != NULL && !state->interrupted) {
+ process_tap_dance_action_on_dance_finished(action, state);
}
}
void reset_tap_dance(tap_dance_state_t *state) {
active_td = 0;
- process_tap_dance_action_on_reset((tap_dance_action_t *)state);
+ process_tap_dance_action_on_reset(tap_dance_get(state->index), state);
}
diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h
index b64321da0c..bce93d611b 100644
--- a/quantum/process_keycode/process_tap_dance.h
+++ b/quantum/process_keycode/process_tap_dance.h
@@ -28,15 +28,16 @@ typedef struct {
#ifndef NO_ACTION_ONESHOT
uint8_t oneshot_mods;
#endif
- bool pressed : 1;
- bool finished : 1;
- bool interrupted : 1;
+ bool pressed : 1;
+ bool finished : 1;
+ bool interrupted : 1;
+ bool in_use : 1;
+ uint8_t index;
} tap_dance_state_t;
typedef void (*tap_dance_user_fn_t)(tap_dance_state_t *state, void *user_data);
typedef struct tap_dance_action_t {
- tap_dance_state_t state;
struct {
tap_dance_user_fn_t on_each_tap;
tap_dance_user_fn_t on_dance_finished;
@@ -98,6 +99,8 @@ typedef struct {
void reset_tap_dance(tap_dance_state_t *state);
+tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx);
+
/* To be used internally */
bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record);
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) {
*/
__attribute__((weak)) void tap_code16_delay(uint16_t code, uint16_t delay) {
register_code16(code);
- for (uint16_t i = delay; i > 0; i--) {
- wait_ms(1);
- }
+ wait_ms(delay);
unregister_code16(code);
}
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)
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
static bool PIXEL_RAIN(effect_params_t* params) {
- static fast_timer_t timer = 0;
- static uint16_t index = RGB_MATRIX_LED_COUNT + 1;
+ static uint8_t index = 0;
+ static uint32_t timer = 0;
- if ((params->iter == 0) && (timer_elapsed_fast(timer) > (320 - rgb_matrix_config.speed))) {
+ if (params->iter == 0 && params->init) {
index = random8_max(RGB_MATRIX_LED_COUNT);
- timer = timer_read_fast();
}
RGB_MATRIX_USE_LIMITS(led_min, led_max);
- if (led_min <= index && index < led_max && HAS_ANY_FLAGS(g_led_config.flags[index], params->flags)) {
- hsv_t hsv = (random8() & 2) ? (hsv_t){0, 0, 0} : (hsv_t){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v};
- rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);
- rgb_matrix_set_color(index, rgb.r, rgb.g, rgb.b);
- index = RGB_MATRIX_LED_COUNT + 1;
+ if (timer < g_rgb_timer) { // Execute when the delay period has elapsed
+ if (led_min <= index && index < led_max && HAS_ANY_FLAGS(g_led_config.flags[index], params->flags)) {
+ // Assign a random HSV color to hsv with 50% probability, otherwise assign zeroed hsv
+ hsv_t hsv = (random8() & 2) ? (hsv_t){0, 0, 0} : (hsv_t){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v};
+ rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);
+ rgb_matrix_set_color(index, rgb.r, rgb.g, rgb.b);
+ }
+ if (!rgb_matrix_check_finished_leds(led_max)) {
+ // In the final LED range, update the LED index and advance the timer for
+ // the next cycle, scaling the delay between 256–2048 ms based on speed.
+ index = random8_max(RGB_MATRIX_LED_COUNT);
+ timer = g_rgb_timer + (2048 - scale16by8(1792, rgb_matrix_config.speed));
+ }
}
return rgb_matrix_check_finished_leds(led_max);
}
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) {
hsv_t hsv = rgb_matrix_config.hsv;
// Take the shortest path between hues
- int16_t deltaH = ((hsv.h + 180) % 360 - hsv.h) / 4;
- if (deltaH > 127) {
- deltaH -= 256;
- } else if (deltaH < -127) {
- deltaH += 256;
- }
-
+ int8_t deltaH = (int8_t)((hsv.h + 128) - hsv.h) / 4;
hsv.h += (deltaH * random8_max(3));
+
rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
diff --git a/quantum/rgb_matrix/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c
index ab0aa17512..97ae6a3c76 100644
--- a/quantum/rgb_matrix/rgb_matrix.c
+++ b/quantum/rgb_matrix/rgb_matrix.c
@@ -72,12 +72,19 @@ uint8_t g_rgb_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}};
last_hit_t g_last_hit_tracker;
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
+#ifndef RGB_MATRIX_FLAG_STEPS
+# define RGB_MATRIX_FLAG_STEPS {LED_FLAG_ALL, LED_FLAG_KEYLIGHT | LED_FLAG_MODIFIER, LED_FLAG_UNDERGLOW, LED_FLAG_NONE}
+#endif
+static const uint8_t rgb_matrix_flag_steps[] = RGB_MATRIX_FLAG_STEPS;
+#define RGB_MATRIX_FLAG_STEPS_COUNT ARRAY_SIZE(rgb_matrix_flag_steps)
+
// internals
-static bool suspend_state = false;
-static uint8_t rgb_last_enable = UINT8_MAX;
-static uint8_t rgb_last_effect = UINT8_MAX;
-static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false};
-static rgb_task_states rgb_task_state = SYNCING;
+static bool suspend_state = false;
+static uint8_t rgb_last_enable = UINT8_MAX;
+static uint8_t rgb_last_effect = UINT8_MAX;
+static uint8_t rgb_current_effect = 0;
+static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false};
+static rgb_task_states rgb_task_state = SYNCING;
// double buffers
static uint32_t rgb_timer_buffer;
@@ -289,6 +296,17 @@ static void rgb_task_start(void) {
g_last_hit_tracker = last_hit_buffer;
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
+ // Ideally we would also stop sending zeros to the LED driver PWM buffers
+ // while suspended and just do a software shutdown. This is a cheap hack for now.
+ bool suspend_backlight = suspend_state ||
+#if RGB_MATRIX_TIMEOUT > 0
+ (last_input_activity_elapsed() > (uint32_t)RGB_MATRIX_TIMEOUT) ||
+#endif // RGB_MATRIX_TIMEOUT > 0
+ false;
+
+ // Set effect to be renedered
+ rgb_current_effect = suspend_backlight || !rgb_matrix_config.enable ? 0 : rgb_matrix_config.mode;
+
// next task
rgb_task_state = RENDERING;
}
@@ -377,15 +395,7 @@ static void rgb_task_flush(uint8_t effect) {
void rgb_matrix_task(void) {
rgb_task_timers();
- // Ideally we would also stop sending zeros to the LED driver PWM buffers
- // while suspended and just do a software shutdown. This is a cheap hack for now.
- bool suspend_backlight = suspend_state ||
-#if RGB_MATRIX_TIMEOUT > 0
- (last_input_activity_elapsed() > (uint32_t)RGB_MATRIX_TIMEOUT) ||
-#endif // RGB_MATRIX_TIMEOUT > 0
- false;
-
- uint8_t effect = suspend_backlight || !rgb_matrix_config.enable ? 0 : rgb_matrix_config.mode;
+ uint8_t effect = rgb_current_effect;
switch (rgb_task_state) {
case STARTING:
@@ -747,6 +757,50 @@ void rgb_matrix_set_flags_noeeprom(led_flags_t flags) {
rgb_matrix_set_flags_eeprom_helper(flags, false);
}
+void rgb_matrix_flags_step_helper(bool write_to_eeprom) {
+ led_flags_t flags = rgb_matrix_get_flags();
+
+ uint8_t next = 0;
+ for (uint8_t i = 0; i < RGB_MATRIX_FLAG_STEPS_COUNT; i++) {
+ if (rgb_matrix_flag_steps[i] == flags) {
+ next = i == RGB_MATRIX_FLAG_STEPS_COUNT - 1 ? 0 : i + 1;
+ break;
+ }
+ }
+
+ rgb_matrix_set_flags_eeprom_helper(rgb_matrix_flag_steps[next], write_to_eeprom);
+}
+
+void rgb_matrix_flags_step_noeeprom(void) {
+ rgb_matrix_flags_step_helper(false);
+}
+
+void rgb_matrix_flags_step(void) {
+ rgb_matrix_flags_step_helper(true);
+}
+
+void rgb_matrix_flags_step_reverse_helper(bool write_to_eeprom) {
+ led_flags_t flags = rgb_matrix_get_flags();
+
+ uint8_t next = 0;
+ for (uint8_t i = 0; i < RGB_MATRIX_FLAG_STEPS_COUNT; i++) {
+ if (rgb_matrix_flag_steps[i] == flags) {
+ next = i == 0 ? RGB_MATRIX_FLAG_STEPS_COUNT - 1 : i - 1;
+ break;
+ }
+ }
+
+ rgb_matrix_set_flags_eeprom_helper(rgb_matrix_flag_steps[next], write_to_eeprom);
+}
+
+void rgb_matrix_flags_step_reverse_noeeprom(void) {
+ rgb_matrix_flags_step_reverse_helper(false);
+}
+
+void rgb_matrix_flags_step_reverse(void) {
+ rgb_matrix_flags_step_reverse_helper(true);
+}
+
//----------------------------------------------------------
// RGB Matrix naming
#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);
led_flags_t rgb_matrix_get_flags(void);
void rgb_matrix_set_flags(led_flags_t flags);
void rgb_matrix_set_flags_noeeprom(led_flags_t flags);
+void rgb_matrix_flags_step_noeeprom(void);
+void rgb_matrix_flags_step(void);
+void rgb_matrix_flags_step_reverse_noeeprom(void);
+void rgb_matrix_flags_step_reverse(void);
void rgb_matrix_update_pwm_buffers(void);
#ifdef RGB_MATRIX_MODE_NAME_ENABLE