summaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
Diffstat (limited to 'quantum')
-rw-r--r--quantum/action.c16
-rw-r--r--quantum/action_util.c9
-rw-r--r--quantum/audio/audio.c3
-rw-r--r--quantum/debounce.h2
-rw-r--r--quantum/debounce/asym_eager_defer_pk.c1
-rw-r--r--quantum/debounce/none.c2
-rw-r--r--quantum/debounce/sym_defer_g.c2
-rw-r--r--quantum/debounce/sym_defer_pk.c1
-rw-r--r--quantum/debounce/sym_defer_pr.c72
-rw-r--r--quantum/debounce/sym_eager_pk.c1
-rw-r--r--quantum/debounce/sym_eager_pr.c1
-rw-r--r--quantum/debounce/tests/rules.mk5
-rw-r--r--quantum/debounce/tests/sym_defer_pr_tests.cpp238
-rw-r--r--quantum/debounce/tests/testlist.mk1
-rw-r--r--quantum/deferred_exec.c85
-rw-r--r--quantum/deferred_exec.h119
-rw-r--r--quantum/keyboard.c83
-rw-r--r--quantum/led_matrix/led_matrix.h4
-rw-r--r--quantum/matrix.c35
-rw-r--r--quantum/matrix.h3
-rw-r--r--quantum/matrix_common.c80
-rw-r--r--quantum/pointing_device.c389
-rw-r--r--quantum/pointing_device.h16
-rw-r--r--quantum/pointing_device_drivers.c65
-rw-r--r--quantum/process_keycode/process_audio.c1
-rw-r--r--quantum/process_keycode/process_audio.h1
-rw-r--r--quantum/process_keycode/process_combo.c39
-rw-r--r--quantum/process_keycode/process_key_lock.c5
-rw-r--r--quantum/process_keycode/process_key_lock.h1
-rw-r--r--quantum/process_keycode/process_magic.c4
-rw-r--r--quantum/process_keycode/process_printer.c5
-rw-r--r--quantum/process_keycode/process_printer.h2
-rw-r--r--quantum/process_keycode/process_rgb.c6
-rw-r--r--quantum/quantum.c12
-rw-r--r--quantum/quantum.h4
-rw-r--r--quantum/quantum_keycodes.h3
-rw-r--r--quantum/split_common/split_util.h1
-rw-r--r--quantum/split_common/transaction_id_define.h6
-rw-r--r--quantum/split_common/transactions.c81
-rw-r--r--quantum/split_common/transport.h13
-rw-r--r--quantum/wpm.c75
-rw-r--r--quantum/wpm.h2
42 files changed, 1239 insertions, 255 deletions
diff --git a/quantum/action.c b/quantum/action.c
index 5e81efb671..ea2310a4d9 100644
--- a/quantum/action.c
+++ b/quantum/action.c
@@ -794,7 +794,7 @@ void process_action(keyrecord_t *record, action_t action) {
*
* FIXME: Needs documentation.
*/
-void register_code(uint8_t code) {
+__attribute__((weak)) void register_code(uint8_t code) {
if (code == KC_NO) {
return;
}
@@ -890,7 +890,7 @@ void register_code(uint8_t code) {
*
* FIXME: Needs documentation.
*/
-void unregister_code(uint8_t code) {
+__attribute__((weak)) void unregister_code(uint8_t code) {
if (code == KC_NO) {
return;
}
@@ -955,7 +955,7 @@ void unregister_code(uint8_t code) {
* \param code The basic keycode to tap.
* \param delay The amount of time in milliseconds to leave the keycode registered, before unregistering it.
*/
-void tap_code_delay(uint8_t code, uint16_t delay) {
+__attribute__((weak)) void tap_code_delay(uint8_t code, uint16_t delay) {
register_code(code);
for (uint16_t i = delay; i > 0; i--) {
wait_ms(1);
@@ -967,13 +967,13 @@ void tap_code_delay(uint8_t code, uint16_t delay) {
*
* \param code The basic keycode to tap. If `code` is `KC_CAPS_LOCK`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined.
*/
-void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS_LOCK ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); }
+__attribute__((weak)) void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS_LOCK ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); }
/** \brief Adds the given physically pressed modifiers and sends a keyboard report immediately.
*
* \param mods A bitfield of modifiers to register.
*/
-void register_mods(uint8_t mods) {
+__attribute__((weak)) void register_mods(uint8_t mods) {
if (mods) {
add_mods(mods);
send_keyboard_report();
@@ -984,7 +984,7 @@ void register_mods(uint8_t mods) {
*
* \param mods A bitfield of modifiers to unregister.
*/
-void unregister_mods(uint8_t mods) {
+__attribute__((weak)) void unregister_mods(uint8_t mods) {
if (mods) {
del_mods(mods);
send_keyboard_report();
@@ -995,7 +995,7 @@ void unregister_mods(uint8_t mods) {
*
* \param mods A bitfield of modifiers to register.
*/
-void register_weak_mods(uint8_t mods) {
+__attribute__((weak)) void register_weak_mods(uint8_t mods) {
if (mods) {
add_weak_mods(mods);
send_keyboard_report();
@@ -1006,7 +1006,7 @@ void register_weak_mods(uint8_t mods) {
*
* \param mods A bitfield of modifiers to unregister.
*/
-void unregister_weak_mods(uint8_t mods) {
+__attribute__((weak)) void unregister_weak_mods(uint8_t mods) {
if (mods) {
del_weak_mods(mods);
send_keyboard_report();
diff --git a/quantum/action_util.c b/quantum/action_util.c
index 78e02aec18..7e30593fb1 100644
--- a/quantum/action_util.c
+++ b/quantum/action_util.c
@@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "action_layer.h"
#include "timer.h"
#include "keycode_config.h"
+#include <string.h>
extern keymap_config_t keymap_config;
@@ -247,7 +248,13 @@ void send_keyboard_report(void) {
keyboard_report->mods |= weak_override_mods;
#endif
- host_keyboard_send(keyboard_report);
+ static report_keyboard_t last_report;
+
+ /* Only send the report if there are changes to propagate to the host. */
+ if (memcmp(keyboard_report, &last_report, sizeof(report_keyboard_t)) != 0) {
+ memcpy(&last_report, keyboard_report, sizeof(report_keyboard_t));
+ host_keyboard_send(keyboard_report);
+ }
}
/** \brief Get mods
diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c
index 49bb309e80..b3d6389dd5 100644
--- a/quantum/audio/audio.c
+++ b/quantum/audio/audio.c
@@ -160,6 +160,8 @@ void audio_toggle(void) {
eeconfig_update_audio(audio_config.raw);
if (audio_config.enable) {
audio_on_user();
+ } else {
+ audio_off_user();
}
}
@@ -172,6 +174,7 @@ void audio_on(void) {
void audio_off(void) {
PLAY_SONG(audio_off_song);
+ audio_off_user();
wait_ms(100);
audio_stop_all();
audio_config.enable = 0;
diff --git a/quantum/debounce.h b/quantum/debounce.h
index 5043868289..3532d9cd7b 100644
--- a/quantum/debounce.h
+++ b/quantum/debounce.h
@@ -6,8 +6,6 @@
// changed is true if raw has changed since the last call
void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);
-bool debounce_active(void);
-
void debounce_init(uint8_t num_rows);
void debounce_free(void);
diff --git a/quantum/debounce/asym_eager_defer_pk.c b/quantum/debounce/asym_eager_defer_pk.c
index 81f39383c4..b1eb4a2b7b 100644
--- a/quantum/debounce/asym_eager_defer_pk.c
+++ b/quantum/debounce/asym_eager_defer_pk.c
@@ -165,7 +165,6 @@ static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], ui
}
}
-bool debounce_active(void) { return true; }
#else
# include "none.c"
#endif
diff --git a/quantum/debounce/none.c b/quantum/debounce/none.c
index b03892bc5b..8a85cc04a8 100644
--- a/quantum/debounce/none.c
+++ b/quantum/debounce/none.c
@@ -26,6 +26,4 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
}
}
-bool debounce_active(void) { return false; }
-
void debounce_free(void) {}
diff --git a/quantum/debounce/sym_defer_g.c b/quantum/debounce/sym_defer_g.c
index 9155eb914c..8cac1c37f9 100644
--- a/quantum/debounce/sym_defer_g.c
+++ b/quantum/debounce/sym_defer_g.c
@@ -44,8 +44,6 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
}
}
-bool debounce_active(void) { return debouncing; }
-
void debounce_free(void) {}
#else // no debouncing.
# include "none.c"
diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c
index 1b698ba347..9dee29e28e 100644
--- a/quantum/debounce/sym_defer_pk.c
+++ b/quantum/debounce/sym_defer_pk.c
@@ -134,7 +134,6 @@ static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], u
}
}
-bool debounce_active(void) { return true; }
#else
# include "none.c"
#endif
diff --git a/quantum/debounce/sym_defer_pr.c b/quantum/debounce/sym_defer_pr.c
new file mode 100644
index 0000000000..8b33acc6a2
--- /dev/null
+++ b/quantum/debounce/sym_defer_pr.c
@@ -0,0 +1,72 @@
+/*
+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.
+*/
+
+#include "matrix.h"
+#include "timer.h"
+#include "quantum.h"
+#include <stdlib.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;
+
+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));
+
+ last_time = timer_read();
+}
+
+void debounce_free(void) {
+ free(countdowns);
+ countdowns = NULL;
+ free(last_raw);
+ last_raw = NULL;
+}
+
+void 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;
+
+ 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[row] = raw_row;
+ *countdown = 0;
+ }
+ }
+}
+
+bool debounce_active(void) { return true; }
diff --git a/quantum/debounce/sym_eager_pk.c b/quantum/debounce/sym_eager_pk.c
index 9da000ea9a..deec463649 100644
--- a/quantum/debounce/sym_eager_pk.c
+++ b/quantum/debounce/sym_eager_pk.c
@@ -140,7 +140,6 @@ static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], ui
}
}
-bool debounce_active(void) { return true; }
#else
# include "none.c"
#endif
diff --git a/quantum/debounce/sym_eager_pr.c b/quantum/debounce/sym_eager_pr.c
index eda92a263b..29b0cabefb 100644
--- a/quantum/debounce/sym_eager_pr.c
+++ b/quantum/debounce/sym_eager_pr.c
@@ -132,7 +132,6 @@ static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], ui
}
}
-bool debounce_active(void) { return true; }
#else
# include "none.c"
#endif
diff --git a/quantum/debounce/tests/rules.mk b/quantum/debounce/tests/rules.mk
index e908dd6f67..8318b1c668 100644
--- a/quantum/debounce/tests/rules.mk
+++ b/quantum/debounce/tests/rules.mk
@@ -28,6 +28,11 @@ debounce_sym_defer_pk_SRC := $(DEBOUNCE_COMMON_SRC) \
$(QUANTUM_PATH)/debounce/sym_defer_pk.c \
$(QUANTUM_PATH)/debounce/tests/sym_defer_pk_tests.cpp
+debounce_sym_defer_pr_DEFS := $(DEBOUNCE_COMMON_DEFS)
+debounce_sym_defer_pr_SRC := $(DEBOUNCE_COMMON_SRC) \
+ $(QUANTUM_PATH)/debounce/sym_defer_pr.c \
+ $(QUANTUM_PATH)/debounce/tests/sym_defer_pr_tests.cpp
+
debounce_sym_eager_pk_DEFS := $(DEBOUNCE_COMMON_DEFS)
debounce_sym_eager_pk_SRC := $(DEBOUNCE_COMMON_SRC) \
$(QUANTUM_PATH)/debounce/sym_eager_pk.c \
diff --git a/quantum/debounce/tests/sym_defer_pr_tests.cpp b/quantum/debounce/tests/sym_defer_pr_tests.cpp
new file mode 100644
index 0000000000..417e1f4ca2
--- /dev/null
+++ b/quantum/debounce/tests/sym_defer_pr_tests.cpp
@@ -0,0 +1,238 @@
+/* Copyright 2021 Simon Arlott
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtest/gtest.h"
+
+#include "debounce_test_common.h"
+
+TEST_F(DebounceTest, OneKeyShort1) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ /* 0ms delay (fast scan rate) */
+ {5, {{0, 1, UP}}, {}},
+
+ {10, {}, {{0, 1, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort2) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ /* 1ms delay */
+ {6, {{0, 1, UP}}, {}},
+
+ {11, {}, {{0, 1, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyShort3) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ /* 2ms delay */
+ {7, {{0, 1, UP}}, {}},
+
+ {12, {}, {{0, 1, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyTooQuick1) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ /* Release key exactly on the debounce time */
+ {5, {{0, 1, UP}}, {}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyTooQuick2) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+ {6, {{0, 1, UP}}, {}},
+
+ /* Press key exactly on the debounce time */
+ {11, {{0, 1, DOWN}}, {}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing1) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {1, {{0, 1, UP}}, {}},
+ {2, {{0, 1, DOWN}}, {}},
+ {3, {{0, 1, UP}}, {}},
+ {4, {{0, 1, DOWN}}, {}},
+ {5, {{0, 1, UP}}, {}},
+ {6, {{0, 1, DOWN}}, {}},
+ {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyBouncing2) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {5, {}, {{0, 1, DOWN}}},
+ {6, {{0, 1, UP}}, {}},
+ {7, {{0, 1, DOWN}}, {}},
+ {8, {{0, 1, UP}}, {}},
+ {9, {{0, 1, DOWN}}, {}},
+ {10, {{0, 1, UP}}, {}},
+ {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyLong) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}}},
+
+ {25, {{0, 1, UP}}, {}},
+
+ {30, {}, {{0, 1, UP}}},
+
+ {50, {{0, 1, DOWN}}, {}},
+
+ {55, {}, {{0, 1, DOWN}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysShort) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {1, {{0, 2, DOWN}}, {}},
+
+ {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
+
+ {7, {{0, 1, UP}}, {}},
+ {8, {{0, 2, UP}}, {}},
+
+ {13, {}, {{0, 1, UP}, {0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysSimultaneous1) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},
+
+ {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
+ {6, {{0, 1, UP}, {0, 2, UP}}, {}},
+
+ {11, {}, {{0, 1, UP}, {0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, TwoKeysSimultaneous2) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+ {1, {{0, 2, DOWN}}, {}},
+
+ {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
+ {7, {{0, 2, UP}}, {}},
+ {9, {{0, 1, UP}}, {}},
+
+ // Debouncing loses the specific ordering -- both events report simultaneously.
+ {14, {}, {{0, 1, UP}, {0, 2, UP}}},
+ });
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan1) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Processing is very late */
+ {300, {}, {{0, 1, DOWN}}},
+ /* Immediately release key */
+ {300, {{0, 1, UP}}, {}},
+
+ {305, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan2) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Processing is very late */
+ {300, {}, {{0, 1, DOWN}}},
+ /* Release key after 1ms */
+ {301, {{0, 1, UP}}, {}},
+
+ {306, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan3) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Release key before debounce expires */
+ {300, {{0, 1, UP}}, {}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
+
+TEST_F(DebounceTest, OneKeyDelayedScan4) {
+ addEvents({
+ /* Time, Inputs, Outputs */
+ {0, {{0, 1, DOWN}}, {}},
+
+ /* Processing is a bit late */
+ {50, {}, {{0, 1, DOWN}}},
+ /* Release key after 1ms */
+ {51, {{0, 1, UP}}, {}},
+
+ {56, {}, {{0, 1, UP}}},
+ });
+ time_jumps_ = true;
+ runEvents();
+}
diff --git a/quantum/debounce/tests/testlist.mk b/quantum/debounce/tests/testlist.mk
index c54c45aa63..f7bd520698 100644
--- a/quantum/debounce/tests/testlist.mk
+++ b/quantum/debounce/tests/testlist.mk
@@ -1,6 +1,7 @@
TEST_LIST += \
debounce_sym_defer_g \
debounce_sym_defer_pk \
+ debounce_sym_defer_pr \
debounce_sym_eager_pk \
debounce_sym_eager_pr \
debounce_asym_eager_defer_pk
diff --git a/quantum/deferred_exec.c b/quantum/deferred_exec.c
index 5b0a5b1425..a64b451df2 100644
--- a/quantum/deferred_exec.c
+++ b/quantum/deferred_exec.c
@@ -9,32 +9,27 @@
# define MAX_DEFERRED_EXECUTORS 8
#endif
-typedef struct deferred_executor_t {
- deferred_token token;
- uint32_t trigger_time;
- deferred_exec_callback callback;
- void * cb_arg;
-} deferred_executor_t;
-
-static deferred_token current_token = 0;
-static uint32_t last_deferred_exec_check = 0;
-static deferred_executor_t executors[MAX_DEFERRED_EXECUTORS] = {0};
-
-static inline bool token_can_be_used(deferred_token token) {
+//------------------------------------
+// Helpers
+//
+
+static deferred_token current_token = 0;
+
+static inline bool token_can_be_used(deferred_executor_t *table, size_t table_count, deferred_token token) {
if (token == INVALID_DEFERRED_TOKEN) {
return false;
}
- for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
- if (executors[i].token == token) {
+ for (int i = 0; i < table_count; ++i) {
+ if (table[i].token == token) {
return false;
}
}
return true;
}
-static inline deferred_token allocate_token(void) {
+static inline deferred_token allocate_token(deferred_executor_t *table, size_t table_count) {
deferred_token first = ++current_token;
- while (!token_can_be_used(current_token)) {
+ while (!token_can_be_used(table, table_count, current_token)) {
++current_token;
if (current_token == first) {
// If we've looped back around to the first, everything is already allocated (yikes!). Need to exit with a failure.
@@ -44,18 +39,22 @@ static inline deferred_token allocate_token(void) {
return current_token;
}
-deferred_token defer_exec(uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg) {
- // Ignore queueing if it's a zero-time delay, or invalid callback
- if (delay_ms == 0 || !callback) {
+//------------------------------------
+// Advanced API: used when a custom-allocated table is used, primarily for core code.
+//
+
+deferred_token defer_exec_advanced(deferred_executor_t *table, size_t table_count, uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg) {
+ // Ignore queueing if the table isn't valid, it's a zero-time delay, or the token is not valid
+ if (!table || table_count == 0 || delay_ms == 0 || !callback) {
return INVALID_DEFERRED_TOKEN;
}
// Find an unused slot and claim it
- for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
- deferred_executor_t *entry = &executors[i];
+ for (int i = 0; i < table_count; ++i) {
+ deferred_executor_t *entry = &table[i];
if (entry->token == INVALID_DEFERRED_TOKEN) {
// Work out the new token value, dropping out if none were available
- deferred_token token = allocate_token();
+ deferred_token token = allocate_token(table, table_count);
if (token == INVALID_DEFERRED_TOKEN) {
return false;
}
@@ -73,15 +72,15 @@ deferred_token defer_exec(uint32_t delay_ms, deferred_exec_callback callback, vo
return INVALID_DEFERRED_TOKEN;
}
-bool extend_deferred_exec(deferred_token token, uint32_t delay_ms) {
- // Ignore queueing if it's a zero-time delay, or the token is not valid
- if (delay_ms == 0 || token == INVALID_DEFERRED_TOKEN) {
+bool extend_deferred_exec_advanced(deferred_executor_t *table, size_t table_count, deferred_token token, uint32_t delay_ms) {
+ // Ignore queueing if the table isn't valid, it's a zero-time delay, or the token is not valid
+ if (!table || table_count == 0 || delay_ms == 0 || token == INVALID_DEFERRED_TOKEN) {
return false;
}
// Find the entry corresponding to the token
- for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
- deferred_executor_t *entry = &executors[i];
+ for (int i = 0; i < table_count; ++i) {
+ deferred_executor_t *entry = &table[i];
if (entry->token == token) {
// Found it, extend the delay
entry->trigger_time = timer_read32() + delay_ms;
@@ -93,15 +92,15 @@ bool extend_deferred_exec(deferred_token token, uint32_t delay_ms) {
return false;
}
-bool cancel_deferred_exec(deferred_token token) {
- // Ignore request if the token is not valid
- if (token == INVALID_DEFERRED_TOKEN) {
+bool cancel_deferred_exec_advanced(deferred_executor_t *table, size_t table_count, deferred_token token) {
+ // Ignore request if the table/token are not valid
+ if (!table || table_count == 0 || token == INVALID_DEFERRED_TOKEN) {
return false;
}
// Find the entry corresponding to the token
- for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
- deferred_executor_t *entry = &executors[i];
+ for (int i = 0; i < table_count; ++i) {
+ deferred_executor_t *entry = &table[i];
if (entry->token == token) {
// Found it, cancel and clear the table entry
entry->token = INVALID_DEFERRED_TOKEN;
@@ -116,16 +115,16 @@ bool cancel_deferred_exec(deferred_token token) {
return false;
}
-void deferred_exec_task(void) {
+void deferred_exec_advanced_task(deferred_executor_t *table, size_t table_count, uint32_t *last_execution_time) {
uint32_t now = timer_read32();
// Throttle only once per millisecond
- if (((int32_t)TIMER_DIFF_32(now, last_deferred_exec_check)) > 0) {
- last_deferred_exec_check = now;
+ if (((int32_t)TIMER_DIFF_32(now, (*last_execution_time))) > 0) {
+ *last_execution_time = now;
// Run through each of the executors
- for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
- deferred_executor_t *entry = &executors[i];
+ for (int i = 0; i < table_count; ++i) {
+ deferred_executor_t *entry = &table[i];
// Check if we're supposed to execute this entry
if (entry->token != INVALID_DEFERRED_TOKEN && ((int32_t)TIMER_DIFF_32(entry->trigger_time, now)) <= 0) {
@@ -150,3 +149,15 @@ void deferred_exec_task(void) {
}
}
}
+
+//------------------------------------
+// Basic API: used by user-mode code, guaranteed to not collide with core deferred execution
+//
+
+static uint32_t last_deferred_exec_check = 0;
+static deferred_executor_t basic_executors[MAX_DEFERRED_EXECUTORS] = {0};
+
+deferred_token defer_exec(uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg) { return defer_exec_advanced(basic_executors, MAX_DEFERRED_EXECUTORS, delay_ms, callback, cb_arg); }
+bool extend_deferred_exec(deferred_token token, uint32_t delay_ms) { return extend_deferred_exec_advanced(basic_executors, MAX_DEFERRED_EXECUTORS, token, delay_ms); }
+bool cancel_deferred_exec(deferred_token token) { return cancel_deferred_exec_advanced(basic_executors, MAX_DEFERRED_EXECUTORS, token); }
+void deferred_exec_task(void) { deferred_exec_advanced_task(basic_executors, MAX_DEFERRED_EXECUTORS, &last_deferred_exec_check); }
diff --git a/quantum/deferred_exec.h b/quantum/deferred_exec.h
index f80d353169..97ef0f6c0e 100644
--- a/quantum/deferred_exec.h
+++ b/quantum/deferred_exec.h
@@ -5,34 +5,117 @@
#include <stdbool.h>
#include <stdint.h>
+#include <stdlib.h>
-// A token that can be used to cancel an existing deferred execution.
+//------------------------------------
+// Common
+//------------------------------------
+
+/**
+ * @typedef A token that can be used to cancel or extend an existing deferred execution.
+ */
typedef uint8_t deferred_token;
+
+/**
+ * @def The constant used to denote an invalid deferred execution token.
+ */
#define INVALID_DEFERRED_TOKEN 0
-// Callback to execute.
-// -- Parameter trigger_time: the intended trigger time to execute the callback -- equivalent time-space as timer_read32()
-// cb_arg: the callback argument specified when enqueueing the deferred executor
-// -- Return value: Non-zero re-queues the callback to execute after the returned number of milliseconds. Zero cancels repeated execution.
+/**
+ * @typedef Callback to execute.
+ * @param trigger_time[in] the intended trigger time to execute the callback -- equivalent time-space as timer_read32()
+ * @param cb_arg[in] the callback argument specified when enqueueing the deferred executor
+ * @return non-zero re-queues the callback to execute after the returned number of milliseconds. Zero cancels repeated execution.
+ */
typedef uint32_t (*deferred_exec_callback)(uint32_t trigger_time, void *cb_arg);
-// Configures the supplied deferred executor to be executed after the required number of milliseconds.
-// -- Parameter delay_ms: the number of milliseconds before executing the callback
-// -- callback: the executor to invoke
-// -- cb_arg: the argument to pass to the executor, may be NULL if unused by the executor
-// -- Return value: a token usable for cancellation, or INVALID_DEFERRED_TOKEN if an error occurred
+//------------------------------------
+// Basic API: used by user-mode code, guaranteed to not collide with core deferred execution
+//------------------------------------
+
+/**
+ * Configures the supplied deferred executor to be executed after the required number of milliseconds.
+ *
+ * @param delay_ms[in] the number of milliseconds before executing the callback
+ * @param callback[in] the executor to invoke
+ * @param cb_arg[in] the argument to pass to the executor, may be NULL if unused by the executor
+ * @return a token usable for extension/cancellation, or INVALID_DEFERRED_TOKEN if an error occurred
+ */
deferred_token defer_exec(uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg);
-// Allows for extending the timeframe before an existing deferred execution is invoked.
-// -- Parameter token: the returned value from defer_exec for the deferred execution you wish to extend.
-// -- delay_ms: the new delay (with respect to the current time)
-// -- Return value: if the token was found, and the delay was extended
+/**
+ * Allows for extending the timeframe before an existing deferred execution is invoked.
+ *
+ * @param token[in] the returned value from defer_exec for the deferred execution you wish to extend
+ * @param delay_ms[in] the number of milliseconds before executing the callback
+ * @return true if the token was extended successfully, otherwise false
+ */
bool extend_deferred_exec(deferred_token token, uint32_t delay_ms);
-// Allows for cancellation of an existing deferred execution.
-// -- Parameter token: the returned value from defer_exec for the deferred execution you wish to cancel.
-// -- Return value: if the token was found, and the executor was cancelled
+/**
+ * Allows for cancellation of an existing deferred execution.
+ *
+ * @param token[in] the returned value from defer_exec for the deferred execution you wish to cancel
+ * @return true if the token was cancelled successfully, otherwise false
+ */
bool cancel_deferred_exec(deferred_token token);
-// Forward declaration for the main loop in order to execute any deferred executors. Should not be invoked by keyboard/user code.
+/**
+ * Forward declaration for the main loop in order to execute any deferred executors. Should not be invoked by keyboard/user code.
+ */
void deferred_exec_task(void);
+
+//------------------------------------
+// Advanced API: used when a custom-allocated table is used, primarily for core code.
+//------------------------------------
+
+/**
+ * @struct Structure for containing self-hosted deferred executor tables.
+ * @brief Core-side code can use this to create their own tables without impacting on the use of users' ability to add deferred execution.
+ * Code outside deferred_exec.c should not worry about internals of this struct, and should just allocate the required number in an array.
+ */
+typedef struct deferred_executor_t {
+ deferred_token token;
+ uint32_t trigger_time;
+ deferred_exec_callback callback;
+ void * cb_arg;
+} deferred_executor_t;
+
+/**
+ * Configures the supplied deferred executor to be executed after the required number of milliseconds.
+ *
+ * @param table[in] the custom table used for storage
+ * @param table_count[in] the number of available items in the table
+ * @param delay_ms[in] the number of milliseconds before executing the callback
+ * @param callback[in] the executor to invoke
+ * @param cb_arg[in] the argument to pass to the executor, may be NULL if unused by the executor
+ * @return a token usable for extension/cancellation, or INVALID_DEFERRED_TOKEN if an error occurred
+ */
+deferred_token defer_exec_advanced(deferred_executor_t *table, size_t table_count, uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg);
+
+/**
+ * Allows for extending the timeframe before an existing deferred execution is invoked.
+ *
+ * @param token[in] the returned value from defer_exec for the deferred execution you wish to extend
+ * @param delay_ms[in] the number of milliseconds before executing the callback
+ * @return true if the token was extended successfully, otherwise false
+ */
+bool extend_deferred_exec_advanced(deferred_executor_t *table, size_t table_count, deferred_token token, uint32_t delay_ms);
+
+/**
+ * Allows for cancellation of an existing deferred execution.
+ *
+ * @param token[in] the returned value from defer_exec for the deferred execution you wish to cancel
+ * @return true if the token was cancelled successfully, otherwise false
+ */
+bool cancel_deferred_exec_advanced(deferred_executor_t *table, size_t table_count, deferred_token token);
+
+/**
+ * Forward declaration for the main loop in order to execute any custom table deferred executors. Should not be invoked by keyboard/user code.
+ * Needed for any custom-allocated deferred execution tables. Any core tasks should add appropriate invocation to quantum/main.c.
+ *
+ * @param table[in] the custom table used for storage
+ * @param table_count[in] the number of available items in the table
+ * @param last_execution_time[in,out] the last execution time -- this will be checked first to determine if execution is needed, and updated if execution occurred
+ */
+void deferred_exec_advanced_task(deferred_executor_t *table, size_t table_count, uint32_t *last_execution_time);
diff --git a/quantum/keyboard.c b/quantum/keyboard.c
index 3bca05aab7..67f7381f74 100644
--- a/quantum/keyboard.c
+++ b/quantum/keyboard.c
@@ -349,6 +349,32 @@ void keyboard_init(void) {
keyboard_post_init_kb(); /* Always keep this last */
}
+/** \brief keyboard set leds
+ *
+ * FIXME: needs doc
+ */
+void keyboard_set_leds(uint8_t leds) {
+ if (debug_keyboard) {
+ debug("keyboard_set_led: ");
+ debug_hex8(leds);
+ debug("\n");
+ }
+ led_set(leds);
+}
+
+/** \brief set host led state
+ *
+ * Only sets state if change detected
+ */
+void led_task(void) {
+ static uint8_t led_status = 0;
+ // update LED
+ if (led_status != host_keyboard_leds()) {
+ led_status = host_keyboard_leds();
+ keyboard_set_leds(led_status);
+ }
+}
+
/** \brief key_event_task
*
* This function is responsible for calling into other systems when they need to respond to electrical switch press events.
@@ -363,28 +389,17 @@ void switch_events(uint8_t row, uint8_t col, bool pressed) {
#endif
}
-/** \brief Keyboard task: Do keyboard routine jobs
- *
- * Do routine keyboard jobs:
+/** \brief Perform scan of keyboard matrix
*
- * * scan matrix
- * * handle mouse movements
- * * handle midi commands
- * * light LEDs
- *
- * This is repeatedly called as fast as possible.
+ * Any detected changes in state are sent out as part of the processing
*/
-void keyboard_task(void) {
+bool matrix_scan_task(void) {
static matrix_row_t matrix_prev[MATRIX_ROWS];
- static uint8_t led_status = 0;
matrix_row_t matrix_row = 0;
matrix_row_t matrix_change = 0;
#ifdef QMK_KEYS_PER_SCAN
uint8_t keys_processed = 0;
#endif
-#ifdef ENCODER_ENABLE
- bool encoders_changed = false;
-#endif
uint8_t matrix_changed = matrix_scan();
if (matrix_changed) last_matrix_activity_trigger();
@@ -431,9 +446,24 @@ void keyboard_task(void) {
MATRIX_LOOP_END:
-#ifdef DEBUG_MATRIX_SCAN_RATE
matrix_scan_perf_task();
-#endif
+ return matrix_changed;
+}
+
+/** \brief Keyboard task: Do keyboard routine jobs
+ *
+ * Do routine keyboard jobs:
+ *
+ * * scan matrix
+ * * handle mouse movements
+ * * handle midi commands
+ * * light LEDs
+ *
+ * This is repeatedly called as fast as possible.
+ */
+void keyboard_task(void) {
+ bool matrix_changed = matrix_scan_task();
+ (void)matrix_changed;
#if defined(RGBLIGHT_ENABLE)
rgblight_task();
@@ -453,7 +483,7 @@ MATRIX_LOOP_END:
#endif
#ifdef ENCODER_ENABLE
- encoders_changed = encoder_read();
+ bool encoders_changed = encoder_read();
if (encoders_changed) last_encoder_activity_trigger();
#endif
@@ -516,22 +546,5 @@ MATRIX_LOOP_END:
programmable_button_send();
#endif
- // update LED
- if (led_status != host_keyboard_leds()) {
- led_status = host_keyboard_leds();
- keyboard_set_leds(led_status);
- }
-}
-
-/** \brief keyboard set leds
- *
- * FIXME: needs doc
- */
-void keyboard_set_leds(uint8_t leds) {
- if (debug_keyboard) {
- debug("keyboard_set_led: ");
- debug_hex8(leds);
- debug("\n");
- }
- led_set(leds);
+ led_task();
}
diff --git a/quantum/led_matrix/led_matrix.h b/quantum/led_matrix/led_matrix.h
index e42be64661..cb7526d6e0 100644
--- a/quantum/led_matrix/led_matrix.h
+++ b/quantum/led_matrix/led_matrix.h
@@ -2,6 +2,7 @@
* Copyright 2017 Jack Humbert
* Copyright 2018 Yiancar
* Copyright 2019 Clueboard
+ * Copyright 2021 Leo Deng
*
* 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
@@ -27,6 +28,9 @@
#ifdef IS31FL3731
# include "is31fl3731-simple.h"
#endif
+#ifdef IS31FL3733
+# include "is31fl3733-simple.h"
+#endif
#ifndef LED_MATRIX_LED_FLUSH_LIMIT
# define LED_MATRIX_LED_FLUSH_LIMIT 16
diff --git a/quantum/matrix.c b/quantum/matrix.c
index 483d518ecc..a58cc752fb 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -63,17 +63,13 @@ extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values
#ifdef SPLIT_KEYBOARD
// row offsets for each hand
-uint8_t thisHand, thatHand;
+extern uint8_t thisHand, thatHand;
#endif
// user-defined overridable functions
__attribute__((weak)) void matrix_init_pins(void);
__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter);
-#ifdef SPLIT_KEYBOARD
-__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); }
-__attribute__((weak)) void matrix_slave_scan_user(void) {}
-#endif
static inline void setPinOutput_writeLow(pin_t pin) {
ATOMIC_BLOCK_FORCEON {
@@ -308,35 +304,6 @@ __attribute__((weak)) bool transport_master_if_connected(matrix_row_t master_mat
transport_master(master_matrix, slave_matrix);
return true; // Treat the transport as always connected
}
-
-bool matrix_post_scan(void) {
- bool changed = false;
- if (is_keyboard_master()) {
- static bool last_connected = false;
- matrix_row_t slave_matrix[ROWS_PER_HAND] = {0};
- if (transport_master_if_connected(matrix + thisHand, slave_matrix)) {
- changed = memcmp(matrix + thatHand, slave_matrix, sizeof(slave_matrix)) != 0;
-
- last_connected = true;
- } else if (last_connected) {
- // reset other half when disconnected
- memset(slave_matrix, 0, sizeof(slave_matrix));
- changed = true;
-
- last_connected = false;
- }
-
- if (changed) memcpy(matrix + thatHand, slave_matrix, sizeof(slave_matrix));
-
- matrix_scan_quantum();
- } else {
- transport_slave(matrix + thatHand, matrix + thisHand);
-
- matrix_slave_scan_kb();
- }
-
- return changed;
-}
#endif
uint8_t matrix_scan(void) {
diff --git a/quantum/matrix.h b/quantum/matrix.h
index 5c696622fc..d968efeb0f 100644
--- a/quantum/matrix.h
+++ b/quantum/matrix.h
@@ -46,8 +46,6 @@ void matrix_setup(void);
void matrix_init(void);
/* scan all key states on matrix */
uint8_t matrix_scan(void);
-/* whether modified from previous scan. used after matrix_scan. */
-bool matrix_is_modified(void) __attribute__((deprecated));
/* whether a switch is on */
bool matrix_is_on(uint8_t row, uint8_t col);
/* matrix state on row */
@@ -75,6 +73,7 @@ void matrix_init_user(void);
void matrix_scan_user(void);
#ifdef SPLIT_KEYBOARD
+bool matrix_post_scan(void);
void matrix_slave_scan_kb(void);
void matrix_slave_scan_user(void);
#endif
diff --git a/quantum/matrix_common.c b/quantum/matrix_common.c
index 66c89970b1..98b83ddcf6 100644
--- a/quantum/matrix_common.c
+++ b/quantum/matrix_common.c
@@ -4,6 +4,15 @@
#include "wait.h"
#include "print.h"
#include "debug.h"
+#ifdef SPLIT_KEYBOARD
+# include "split_common/split_util.h"
+# include "split_common/transactions.h"
+# include <string.h>
+
+# define ROWS_PER_HAND (MATRIX_ROWS / 2)
+#else
+# define ROWS_PER_HAND (MATRIX_ROWS)
+#endif
#ifndef MATRIX_IO_DELAY
# define MATRIX_IO_DELAY 30
@@ -13,6 +22,11 @@
matrix_row_t raw_matrix[MATRIX_ROWS];
matrix_row_t matrix[MATRIX_ROWS];
+#ifdef SPLIT_KEYBOARD
+// row offsets for each hand
+uint8_t thisHand, thatHand;
+#endif
+
#ifdef MATRIX_MASKED
extern const matrix_row_t matrix_mask[];
#endif
@@ -45,12 +59,6 @@ inline matrix_row_t matrix_get_row(uint8_t row) {
#endif
}
-// Deprecated.
-bool matrix_is_modified(void) {
- if (debounce_active()) return false;
- return true;
-}
-
#if (MATRIX_COLS <= 8)
# define print_matrix_header() print("\nr/c 01234567\n")
# define print_matrix_row(row) print_bin_reverse8(matrix_get_row(row))
@@ -84,18 +92,59 @@ uint8_t matrix_key_count(void) {
return count;
}
+#ifdef SPLIT_KEYBOARD
+bool matrix_post_scan(void) {
+ bool changed = false;
+ if (is_keyboard_master()) {
+ static bool last_connected = false;
+ matrix_row_t slave_matrix[ROWS_PER_HAND] = {0};
+ if (transport_master_if_connected(matrix + thisHand, slave_matrix)) {
+ changed = memcmp(matrix + thatHand, slave_matrix, sizeof(slave_matrix)) != 0;
+
+ last_connected = true;
+ } else if (last_connected) {
+ // reset other half when disconnected
+ memset(slave_matrix, 0, sizeof(slave_matrix));
+ changed = true;
+
+ last_connected = false;
+ }
+
+ if (changed) memcpy(matrix + thatHand, slave_matrix, sizeof(slave_matrix));
+
+ matrix_scan_quantum();
+ } else {
+ transport_slave(matrix + thatHand, matrix + thisHand);
+
+ matrix_slave_scan_kb();
+ }
+
+ return changed;
+}
+#endif
+
/* `matrix_io_delay ()` exists for backwards compatibility. From now on, use matrix_output_unselect_delay(). */
__attribute__((weak)) void matrix_io_delay(void) { wait_us(MATRIX_IO_DELAY); }
-
__attribute__((weak)) void matrix_output_select_delay(void) { waitInputPinDelay(); }
__attribute__((weak)) void matrix_output_unselect_delay(uint8_t line, bool key_pressed) { matrix_io_delay(); }
// CUSTOM MATRIX 'LITE'
__attribute__((weak)) void matrix_init_custom(void) {}
-
__attribute__((weak)) bool matrix_scan_custom(matrix_row_t current_matrix[]) { return true; }
+#ifdef SPLIT_KEYBOARD
+__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); }
+__attribute__((weak)) void matrix_slave_scan_user(void) {}
+#endif
+
__attribute__((weak)) void matrix_init(void) {
+#ifdef SPLIT_KEYBOARD
+ split_pre_init();
+
+ thisHand = isLeftHand ? 0 : (ROWS_PER_HAND);
+ thatHand = ROWS_PER_HAND - thisHand;
+#endif
+
matrix_init_custom();
// initialize matrix state: all keys off
@@ -104,17 +153,26 @@ __attribute__((weak)) void matrix_init(void) {
matrix[i] = 0;
}
- debounce_init(MATRIX_ROWS);
+ debounce_init(ROWS_PER_HAND);
matrix_init_quantum();
+
+#ifdef SPLIT_KEYBOARD
+ split_post_init();
+#endif
}
__attribute__((weak)) uint8_t matrix_scan(void) {
bool changed = matrix_scan_custom(raw_matrix);
- debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
-
+#ifdef SPLIT_KEYBOARD
+ debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed);
+ changed = (changed || matrix_post_scan());
+#else
+ debounce(raw_matrix, matrix, ROWS_PER_HAND, changed);
matrix_scan_quantum();
+#endif
+
return changed;
}
diff --git a/quantum/pointing_device.c b/quantum/pointing_device.c
index 2fefdb67b6..23d93fa15f 100644
--- a/quantum/pointing_device.c
+++ b/quantum/pointing_device.c
@@ -18,24 +18,105 @@
#include "pointing_device.h"
#include <string.h>
+#include "timer.h"
#ifdef MOUSEKEY_ENABLE
# include "mousekey.h"
#endif
#if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1
# error More than one rotation selected. This is not supported.
#endif
+#if defined(SPLIT_POINTING_ENABLE)
+# include "transactions.h"
+# include "keyboard.h"
-static report_mouse_t mouseReport = {};
+report_mouse_t shared_mouse_report = {};
+uint16_t shared_cpi = 0;
+
+/**
+ * @brief Sets the shared mouse report used be pointing device task
+ *
+ * NOTE : Only available when using SPLIT_POINTING_ENABLE
+ *
+ * @param[in] new_mouse_report report_mouse_t
+ */
+void pointing_device_set_shared_report(report_mouse_t new_mouse_report) { shared_mouse_report = new_mouse_report; }
+
+/**
+ * @brief Gets current pointing device CPI if supported
+ *
+ * Gets current cpi of the shared report and returns it as uint16_t
+ *
+ * NOTE : Only available when using SPLIT_POINTING_ENABLE
+ *
+ * @return cpi value as uint16_t
+ */
+uint16_t pointing_device_get_shared_cpi(void) { return shared_cpi; }
+
+# if defined(POINTING_DEVICE_LEFT)
+# define POINTING_DEVICE_THIS_SIDE is_keyboard_left()
+# elif defined(POINTING_DEVICE_RIGHT)
+# define POINTING_DEVICE_THIS_SIDE !is_keyboard_left()
+# elif defined(POINTING_DEVICE_COMBINED)
+# define POINTING_DEVICE_THIS_SIDE true
+# endif
+
+#endif // defined(SPLIT_POINTING_ENABLE)
+
+static report_mouse_t local_mouse_report = {};
extern const pointing_device_driver_t pointing_device_driver;
+/**
+ * @brief Compares 2 mouse reports for difference and returns result
+ *
+ * @param[in] new report_mouse_t
+ * @param[in] old report_mouse_t
+ * @return bool result
+ */
__attribute__((weak)) bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old) { return memcmp(&new, &old, sizeof(new)); }
-__attribute__((weak)) void pointing_device_init_kb(void) {}
-__attribute__((weak)) void pointing_device_init_user(void) {}
+/**
+ * @brief Keyboard level code pointing device initialisation
+ *
+ */
+__attribute__((weak)) void pointing_device_init_kb(void) {}
+
+/**
+ * @brief User level code pointing device initialisation
+ *
+ */
+__attribute__((weak)) void pointing_device_init_user(void) {}
+
+/**
+ * @brief Weak function allowing for keyboard level mouse report modification
+ *
+ * Takes report_mouse_t struct allowing modification at keyboard level then returns report_mouse_t.
+ *
+ * @param[in] mouse_report report_mouse_t
+ * @return report_mouse_t
+ */
__attribute__((weak)) report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { return pointing_device_task_user(mouse_report); }
+
+/**
+ * @brief Weak function allowing for user level mouse report modification
+ *
+ * Takes report_mouse_t struct allowing modification at user level then returns report_mouse_t.
+ *
+ * @param[in] mouse_report report_mouse_t
+ * @return report_mouse_t
+ */
__attribute__((weak)) report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { return mouse_report; }
+/**
+ * @brief Handles pointing device buttons
+ *
+ * Returns modified button bitmask using bool pressed and selected pointing_device_buttons_t button in uint8_t buttons bitmask.
+ *
+ * @param buttons[in] uint8_t bitmask
+ * @param pressed[in] bool
+ * @param button[in] pointing_device_buttons_t value
+ * @return Modified uint8_t bitmask buttons
+ */
__attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button) {
if (pressed) {
buttons |= 1 << (button);
@@ -45,7 +126,17 @@ __attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bo
return buttons;
}
+/**
+ * @brief Initialises pointing device
+ *
+ * Initialises pointing device, perform driver init and optional keyboard/user level code.
+ */
__attribute__((weak)) void pointing_device_init(void) {
+#if defined(SPLIT_POINTING_ENABLE)
+ if (!(POINTING_DEVICE_THIS_SIDE)) {
+ return;
+ }
+#endif
pointing_device_driver.init();
#ifdef POINTING_DEVICE_MOTION_PIN
setPinInputHigh(POINTING_DEVICE_MOTION_PIN);
@@ -54,67 +145,299 @@ __attribute__((weak)) void pointing_device_init(void) {
pointing_device_init_user();
}
+/**
+ * @brief Sends processed mouse report to host
+ *
+ * This sends the mouse report generated by pointing_device_task if changed since the last report. Once send zeros mouse report except buttons.
+ *
+ */
__attribute__((weak)) void pointing_device_send(void) {
static report_mouse_t old_report = {};
// If you need to do other things, like debugging, this is the place to do it.
- if (has_mouse_report_changed(mouseReport, old_report)) {
- host_mouse_send(&mouseReport);
+ if (has_mouse_report_changed(local_mouse_report, old_report)) {
+ host_mouse_send(&local_mouse_report);
}
// send it and 0 it out except for buttons, so those stay until they are explicity over-ridden using update_pointing_device
- mouseReport.x = 0;
- mouseReport.y = 0;
- mouseReport.v = 0;
- mouseReport.h = 0;
+ local_mouse_report.x = 0;
+ local_mouse_report.y = 0;
+ local_mouse_report.v = 0;
+ local_mouse_report.h = 0;
- memcpy(&old_report, &mouseReport, sizeof(mouseReport));
+ memcpy(&old_report, &local_mouse_report, sizeof(local_mouse_report));
}
-__attribute__((weak)) void pointing_device_task(void) {
- // Gather report info
-#ifdef POINTING_DEVICE_MOTION_PIN
- if (!readPin(POINTING_DEVICE_MOTION_PIN))
-#endif
- mouseReport = pointing_device_driver.get_report(mouseReport);
-
- // Support rotation of the sensor data
+/**
+ * @brief Adjust mouse report by any optional common pointing configuration defines
+ *
+ * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines.
+ *
+ * @param mouse_report[in] takes a report_mouse_t to be adjusted
+ * @return report_mouse_t with adjusted values
+ */
+report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report) {
+ // Support rotation of the sensor data
#if defined(POINTING_DEVICE_ROTATION_90) || defined(POINTING_DEVICE_ROTATION_180) || defined(POINTING_DEVICE_ROTATION_270)
- int8_t x = mouseReport.x, y = mouseReport.y;
+ int8_t x = mouse_report.x, y = mouse_report.y;
# if defined(POINTING_DEVICE_ROTATION_90)
- mouseReport.x = y;
- mouseReport.y = -x;
+ mouse_report.x = y;
+ mouse_report.y = -x;
# elif defined(POINTING_DEVICE_ROTATION_180)
- mouseReport.x = -x;
- mouseReport.y = -y;
+ mouse_report.x = -x;
+ mouse_report.y = -y;
# elif defined(POINTING_DEVICE_ROTATION_270)
- mouseReport.x = -y;
- mouseReport.y = x;
+ mouse_report.x = -y;
+ mouse_report.y = x;
# else
# error "How the heck did you get here?!"
# endif
#endif
// Support Inverting the X and Y Axises
#if defined(POINTING_DEVICE_INVERT_X)
- mouseReport.x = -mouseReport.x;
+ mouse_report.x = -mouse_report.x;
#endif
#if defined(POINTING_DEVICE_INVERT_Y)
- mouseReport.y = -mouseReport.y;
+ mouse_report.y = -mouse_report.y;
+#endif
+ return mouse_report;
+}
+
+/**
+ * @brief Retrieves and processes pointing device data.
+ *
+ * This function is part of the keyboard loop and retrieves the mouse report from the pointing device driver.
+ * It applies any optional configuration e.g. rotation or axis inversion and then initiates a send.
+ *
+ */
+__attribute__((weak)) void pointing_device_task(void) {
+#if defined(SPLIT_POINTING_ENABLE)
+ // Don't poll the target side pointing device.
+ if (!is_keyboard_master()) {
+ return;
+ };
+#endif
+
+#if defined(POINTING_DEVICE_TASK_THROTTLE_MS)
+ static uint32_t last_exec = 0;
+ if (timer_elapsed32(last_exec) < POINTING_DEVICE_TASK_THROTTLE_MS) {
+ return;
+ }
+ last_exec = timer_read32();
+#else
+# if defined(SPLIT_POINTING_ENABLE)
+# pragma message("It's recommended you enable a throttle when sharing pointing devices.")
+# endif
+#endif
+
+ // Gather report info
+#ifdef POINTING_DEVICE_MOTION_PIN
+# if defined(SPLIT_POINTING_ENABLE)
+# error POINTING_DEVICE_MOTION_PIN not supported when sharing the pointing device report between sides.
+# endif
+ if (!readPin(POINTING_DEVICE_MOTION_PIN))
#endif
+#if defined(SPLIT_POINTING_ENABLE)
+# if defined(POINTING_DEVICE_COMBINED)
+ static uint8_t old_buttons = 0;
+ local_mouse_report.buttons = old_buttons;
+ local_mouse_report = pointing_device_driver.get_report(local_mouse_report);
+ old_buttons = local_mouse_report.buttons;
+# elif defined(POINTING_DEVICE_LEFT) || defined(POINTING_DEVICE_RIGHT)
+ local_mouse_report = POINTING_DEVICE_THIS_SIDE ? pointing_device_driver.get_report(local_mouse_report) : shared_mouse_report;
+# else
+# error "You need to define the side(s) the pointing device is on. POINTING_DEVICE_COMBINED / POINTING_DEVICE_LEFT / POINTING_DEVICE_RIGHT"
+# endif
+#else
+ local_mouse_report = pointing_device_driver.get_report(local_mouse_report);
+#endif // defined(SPLIT_POINTING_ENABLE)
+
// allow kb to intercept and modify report
- mouseReport = pointing_device_task_kb(mouseReport);
+#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)
+ if (is_keyboard_left()) {
+ local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report);
+ shared_mouse_report = pointing_device_adjust_by_defines_right(shared_mouse_report);
+ } else {
+ local_mouse_report = pointing_device_adjust_by_defines_right(local_mouse_report);
+ shared_mouse_report = pointing_device_adjust_by_defines(shared_mouse_report);
+ }
+ local_mouse_report = is_keyboard_left() ? pointing_device_task_combined_kb(local_mouse_report, shared_mouse_report) : pointing_device_task_combined_kb(shared_mouse_report, local_mouse_report);
+#else
+ local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report);
+ local_mouse_report = pointing_device_task_kb(local_mouse_report);
+#endif
// combine with mouse report to ensure that the combined is sent correctly
#ifdef MOUSEKEY_ENABLE
report_mouse_t mousekey_report = mousekey_get_report();
- mouseReport.buttons = mouseReport.buttons | mousekey_report.buttons;
+ local_mouse_report.buttons = local_mouse_report.buttons | mousekey_report.buttons;
#endif
pointing_device_send();
}
-report_mouse_t pointing_device_get_report(void) { return mouseReport; }
+/**
+ * @brief Gets current mouse report used by pointing device task
+ *
+ * @return report_mouse_t
+ */
+report_mouse_t pointing_device_get_report(void) { return local_mouse_report; }
+
+/**
+ * @brief Sets mouse report used be pointing device task
+ *
+ * @param[in] new_mouse_report
+ */
+void pointing_device_set_report(report_mouse_t new_mouse_report) { local_mouse_report = new_mouse_report; }
+
+/**
+ * @brief Gets current pointing device CPI if supported
+ *
+ * Gets current cpi from pointing device driver if supported and returns it as uint16_t
+ *
+ * @return cpi value as uint16_t
+ */
+uint16_t pointing_device_get_cpi(void) {
+#if defined(SPLIT_POINTING_ENABLE)
+ return POINTING_DEVICE_THIS_SIDE ? pointing_device_driver.get_cpi() : shared_cpi;
+#else
+ return pointing_device_driver.get_cpi();
+#endif
+}
-void pointing_device_set_report(report_mouse_t newMouseReport) { mouseReport = newMouseReport; }
+/**
+ * @brief Set pointing device CPI if supported
+ *
+ * Takes a uint16_t value to set pointing device cpi if supported by driver.
+ *
+ * @param[in] cpi uint16_t value.
+ */
+void pointing_device_set_cpi(uint16_t cpi) {
+#if defined(SPLIT_POINTING_ENABLE)
+ if (POINTING_DEVICE_THIS_SIDE) {
+ pointing_device_driver.set_cpi(cpi);
+ } else {
+ shared_cpi = cpi;
+ }
+#else
+ pointing_device_driver.set_cpi(cpi);
+#endif
+}
-uint16_t pointing_device_get_cpi(void) { return pointing_device_driver.get_cpi(); }
+#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)
+/**
+ * @brief Set pointing device CPI if supported
+ *
+ * Takes a bool and uint16_t and allows setting cpi for a single side when using 2 pointing devices with a split keyboard.
+ *
+ * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
+ *
+ * @param[in] left true = left, false = right.
+ * @param[in] cpi uint16_t value.
+ */
+void pointing_device_set_cpi_on_side(bool left, uint16_t cpi) {
+ bool local = (is_keyboard_left() & left) ? true : false;
+ if (local) {
+ pointing_device_driver.set_cpi(cpi);
+ } else {
+ shared_cpi = cpi;
+ }
+}
-void pointing_device_set_cpi(uint16_t cpi) { pointing_device_driver.set_cpi(cpi); }
+/**
+ * @brief clamps int16_t to int8_t
+ *
+ * @param[in] int16_t value
+ * @return int8_t clamped value
+ */
+static inline int8_t pointing_device_movement_clamp(int16_t value) {
+ if (value < INT8_MIN) {
+ return INT8_MIN;
+ } else if (value > INT8_MAX) {
+ return INT8_MAX;
+ } else {
+ return value;
+ }
+}
+
+/**
+ * @brief combines 2 mouse reports and returns 2
+ *
+ * Combines 2 report_mouse_t structs, clamping movement values to int8_t and ignores report_id then returns the resulting report_mouse_t struct.
+ *
+ * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
+ *
+ * @param[in] left_report left report_mouse_t
+ * @param[in] right_report right report_mouse_t
+ * @return combined report_mouse_t of left_report and right_report
+ */
+report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report) {
+ left_report.x = pointing_device_movement_clamp((int16_t)left_report.x + right_report.x);
+ left_report.y = pointing_device_movement_clamp((int16_t)left_report.y + right_report.y);
+ left_report.h = pointing_device_movement_clamp((int16_t)left_report.h + right_report.h);
+ left_report.v = pointing_device_movement_clamp((int16_t)left_report.v + right_report.v);
+ left_report.buttons |= right_report.buttons;
+ return left_report;
+}
+
+/**
+ * @brief Adjust mouse report by any optional right pointing configuration defines
+ *
+ * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines.
+ *
+ * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
+ *
+ * @param[in] mouse_report report_mouse_t to be adjusted
+ * @return report_mouse_t with adjusted values
+ */
+report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report) {
+ // Support rotation of the sensor data
+# if defined(POINTING_DEVICE_ROTATION_90_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT) || defined(POINTING_DEVICE_ROTATION_RIGHT)
+ int8_t x = mouse_report.x, y = mouse_report.y;
+# if defined(POINTING_DEVICE_ROTATION_90_RIGHT)
+ mouse_report.x = y;
+ mouse_report.y = -x;
+# elif defined(POINTING_DEVICE_ROTATION_180_RIGHT)
+ mouse_report.x = -x;
+ mouse_report.y = -y;
+# elif defined(POINTING_DEVICE_ROTATION_270_RIGHT)
+ mouse_report.x = -y;
+ mouse_report.y = x;
+# else
+# error "How the heck did you get here?!"
+# endif
+# endif
+ // Support Inverting the X and Y Axises
+# if defined(POINTING_DEVICE_INVERT_X_RIGHT)
+ mouse_report.x = -mouse_report.x;
+# endif
+# if defined(POINTING_DEVICE_INVERT_Y_RIGHT)
+ mouse_report.y = -mouse_report.y;
+# endif
+ return mouse_report;
+}
+
+/**
+ * @brief Weak function allowing for keyboard level mouse report modification
+ *
+ * Takes 2 report_mouse_t structs allowing individual modification of sides at keyboard level then returns pointing_device_task_combined_user.
+ *
+ * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
+ *
+ * @param[in] left_report report_mouse_t
+ * @param[in] right_report report_mouse_t
+ * @return pointing_device_task_combined_user(left_report, right_report) by default
+ */
+__attribute__((weak)) report_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report) { return pointing_device_task_combined_user(left_report, right_report); }
+
+/**
+ * @brief Weak function allowing for user level mouse report modification
+ *
+ * Takes 2 report_mouse_t structs allowing individual modification of sides at user level then returns pointing_device_combine_reports.
+ *
+ * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED
+ *
+ * @param[in] left_report report_mouse_t
+ * @param[in] right_report report_mouse_t
+ * @return pointing_device_combine_reports(left_report, right_report) by default
+ */
+__attribute__((weak)) report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report) { return pointing_device_combine_reports(left_report, right_report); }
+#endif
diff --git a/quantum/pointing_device.h b/quantum/pointing_device.h
index 5106c26660..6ff267e491 100644
--- a/quantum/pointing_device.h
+++ b/quantum/pointing_device.h
@@ -47,6 +47,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#elif defined(POINTING_DEVICE_DRIVER_pmw3360)
# include "spi_master.h"
# include "drivers/sensors/pmw3360.h"
+#elif defined(POINTING_DEVICE_DRIVER_pmw3389)
+# include "spi_master.h"
+# include "drivers/sensors/pmw3389.h"
#else
void pointing_device_driver_init(void);
report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report);
@@ -86,3 +89,16 @@ void pointing_device_init_user(void);
report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report);
report_mouse_t pointing_device_task_user(report_mouse_t mouse_report);
uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button);
+report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report);
+
+#if defined(SPLIT_POINTING_ENABLE)
+void pointing_device_set_shared_report(report_mouse_t report);
+uint16_t pointing_device_get_shared_cpi(void);
+# if defined(POINTING_DEVICE_COMBINED)
+void pointing_device_set_cpi_on_side(bool left, uint16_t cpi);
+report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report);
+report_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report);
+report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report);
+report_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report);
+# endif // defined(POINTING_DEVICE_COMBINED)
+#endif // defined(SPLIT_POINTING_ENABLE)
diff --git a/quantum/pointing_device_drivers.c b/quantum/pointing_device_drivers.c
index 9ad5e76ba6..4333bbb095 100644
--- a/quantum/pointing_device_drivers.c
+++ b/quantum/pointing_device_drivers.c
@@ -165,14 +165,13 @@ const pointing_device_driver_t pointing_device_driver = {
// clang-format on
#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball)
-report_mouse_t pimorono_trackball_get_report(report_mouse_t mouse_report) {
- static fast_timer_t throttle = 0;
- static uint16_t debounce = 0;
- static uint8_t error_count = 0;
- pimoroni_data_t pimoroni_data = {0};
- static int16_t x_offset = 0, y_offset = 0;
-
- if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT && timer_elapsed_fast(throttle) >= PIMORONI_TRACKBALL_INTERVAL_MS) {
+report_mouse_t pimoroni_trackball_get_report(report_mouse_t mouse_report) {
+ static uint16_t debounce = 0;
+ static uint8_t error_count = 0;
+ pimoroni_data_t pimoroni_data = {0};
+ static int16_t x_offset = 0, y_offset = 0;
+
+ if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT) {
i2c_status_t status = read_pimoroni_trackball(&pimoroni_data);
if (status == I2C_STATUS_SUCCESS) {
@@ -195,22 +194,20 @@ report_mouse_t pimorono_trackball_get_report(report_mouse_t mouse_report) {
} else {
error_count++;
}
- throttle = timer_read_fast();
}
return mouse_report;
}
// clang-format off
const pointing_device_driver_t pointing_device_driver = {
- .init = pimironi_trackball_device_init,
- .get_report = pimorono_trackball_get_report,
- .set_cpi = NULL,
- .get_cpi = NULL
+ .init = pimoroni_trackball_device_init,
+ .get_report = pimoroni_trackball_get_report,
+ .set_cpi = pimoroni_trackball_set_cpi,
+ .get_cpi = pimoroni_trackball_get_cpi
};
// clang-format on
#elif defined(POINTING_DEVICE_DRIVER_pmw3360)
-
-static void init(void) { pmw3360_init(); }
+static void pmw3360_device_init(void) { pmw3360_init(); }
report_mouse_t pmw3360_get_report(report_mouse_t mouse_report) {
report_pmw3360_t data = pmw3360_read_burst();
@@ -239,12 +236,48 @@ report_mouse_t pmw3360_get_report(report_mouse_t mouse_report) {
// clang-format off
const pointing_device_driver_t pointing_device_driver = {
- .init = init,
+ .init = pmw3360_device_init,
.get_report = pmw3360_get_report,
.set_cpi = pmw3360_set_cpi,
.get_cpi = pmw3360_get_cpi
};
// clang-format on
+#elif defined(POINTING_DEVICE_DRIVER_pmw3389)
+static void pmw3389_device_init(void) { pmw3389_init(); }
+
+report_mouse_t pmw3389_get_report(report_mouse_t mouse_report) {
+ report_pmw3389_t data = pmw3389_read_burst();
+ static uint16_t MotionStart = 0; // Timer for accel, 0 is resting state
+
+ if (data.isOnSurface && data.isMotion) {
+ // Reset timer if stopped moving
+ if (!data.isMotion) {
+ if (MotionStart != 0) MotionStart = 0;
+ return mouse_report;
+ }
+
+ // Set timer if new motion
+ if ((MotionStart == 0) && data.isMotion) {
+# ifdef CONSOLE_ENABLE
+ if (debug_mouse) dprintf("Starting motion.\n");
+# endif
+ MotionStart = timer_read();
+ }
+ mouse_report.x = constrain_hid(data.dx);
+ mouse_report.y = constrain_hid(data.dy);
+ }
+
+ return mouse_report;
+}
+
+// clang-format off
+const pointing_device_driver_t pointing_device_driver = {
+ .init = pmw3389_device_init,
+ .get_report = pmw3389_get_report,
+ .set_cpi = pmw3389_set_cpi,
+ .get_cpi = pmw3389_get_cpi
+};
+// clang-format on
#else
__attribute__((weak)) void pointing_device_driver_init(void) {}
__attribute__((weak)) report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) { return mouse_report; }
diff --git a/quantum/process_keycode/process_audio.c b/quantum/process_keycode/process_audio.c
index 3b5fa8490b..23664721e8 100644
--- a/quantum/process_keycode/process_audio.c
+++ b/quantum/process_keycode/process_audio.c
@@ -57,3 +57,4 @@ void process_audio_noteoff(uint8_t note) { stop_note(compute_freq_for_midi_note(
void process_audio_all_notes_off(void) { stop_all_notes(); }
__attribute__((weak)) void audio_on_user() {}
+__attribute__((weak)) void audio_off_user() {}
diff --git a/quantum/process_keycode/process_audio.h b/quantum/process_keycode/process_audio.h
index d89a834ea8..42cfab4af2 100644
--- a/quantum/process_keycode/process_audio.h
+++ b/quantum/process_keycode/process_audio.h
@@ -8,3 +8,4 @@ void process_audio_noteoff(uint8_t note);
void process_audio_all_notes_off(void);
void audio_on_user(void);
+void audio_off_user(void);
diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
index a050161edf..8040ede528 100644
--- a/quantum/process_keycode/process_combo.c
+++ b/quantum/process_keycode/process_combo.c
@@ -40,10 +40,18 @@ __attribute__((weak)) bool get_combo_must_tap(uint16_t index, combo_t *combo) {
__attribute__((weak)) uint16_t get_combo_term(uint16_t index, combo_t *combo) { return COMBO_TERM; }
#endif
+#ifdef COMBO_MUST_PRESS_IN_ORDER_PER_COMBO
+__attribute__((weak)) bool get_combo_must_press_in_order(uint16_t combo_index, combo_t *combo) { return true; }
+#endif
+
#ifdef COMBO_PROCESS_KEY_RELEASE
__attribute__((weak)) bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) { return false; }
#endif
+#ifdef COMBO_SHOULD_TRIGGER
+__attribute__((weak)) bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode, keyrecord_t *record) { return true; }
+#endif
+
#ifndef COMBO_NO_TIMER
static uint16_t timer = 0;
#endif
@@ -350,6 +358,28 @@ combo_t *overlaps(combo_t *combo1, combo_t *combo2) {
return combo1;
}
+#if defined(COMBO_MUST_PRESS_IN_ORDER) || defined(COMBO_MUST_PRESS_IN_ORDER_PER_COMBO)
+static bool keys_pressed_in_order(uint16_t combo_index, combo_t *combo, uint16_t key_index, uint16_t keycode, keyrecord_t *record) {
+# ifdef COMBO_MUST_PRESS_IN_ORDER_PER_COMBO
+ if (!get_combo_must_press_in_order(combo_index, combo)) {
+ return true;
+ }
+# endif
+ if (
+ // The `state` bit for the key being pressed.
+ (1 << key_index) ==
+ // The *next* combo key's bit.
+ (COMBO_STATE(combo) + 1)
+ // E.g. two keys already pressed: `state == 11`.
+ // Next possible `state` is `111`.
+ // So the needed bit is `100` which we get with `11 + 1`.
+ ) {
+ return true;
+ }
+ return false;
+}
+#endif
+
static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record, uint16_t combo_index) {
uint8_t key_count = 0;
uint16_t key_index = -1;
@@ -360,7 +390,14 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
return false;
}
- bool key_is_part_of_combo = !COMBO_DISABLED(combo) && is_combo_enabled();
+ bool key_is_part_of_combo = (!COMBO_DISABLED(combo) && is_combo_enabled()
+#if defined(COMBO_MUST_PRESS_IN_ORDER) || defined(COMBO_MUST_PRESS_IN_ORDER_PER_COMBO)
+ && keys_pressed_in_order(combo_index, combo, key_index, keycode, record)
+#endif
+#ifdef COMBO_SHOULD_TRIGGER
+ && combo_should_trigger(combo_index, combo, keycode, record)
+#endif
+ );
if (record->event.pressed && key_is_part_of_combo) {
uint16_t time = _get_combo_term(combo_index, combo);
diff --git a/quantum/process_keycode/process_key_lock.c b/quantum/process_keycode/process_key_lock.c
index 4bd58f0c1e..941a2c5780 100644
--- a/quantum/process_keycode/process_key_lock.c
+++ b/quantum/process_keycode/process_key_lock.c
@@ -56,6 +56,11 @@ static inline uint16_t translate_keycode(uint16_t keycode) {
}
}
+void cancel_key_lock(void) {
+ watching = false;
+ UNSET_KEY_STATE(0x0);
+}
+
bool process_key_lock(uint16_t *keycode, keyrecord_t *record) {
// We start by categorizing the keypress event. In the event of a down
// event, there are several possibilities:
diff --git a/quantum/process_keycode/process_key_lock.h b/quantum/process_keycode/process_key_lock.h
index baa0b39077..5159b0ba02 100644
--- a/quantum/process_keycode/process_key_lock.h
+++ b/quantum/process_keycode/process_key_lock.h
@@ -18,4 +18,5 @@
#include "quantum.h"
+void cancel_key_lock(void);
bool process_key_lock(uint16_t *keycode, keyrecord_t *record);
diff --git a/quantum/process_keycode/process_magic.c b/quantum/process_keycode/process_magic.c
index d5cff4f12a..6332be647c 100644
--- a/quantum/process_keycode/process_magic.c
+++ b/quantum/process_keycode/process_magic.c
@@ -44,6 +44,7 @@ bool process_magic(uint16_t keycode, keyrecord_t *record) {
case MAGIC_SWAP_CONTROL_CAPSLOCK ... MAGIC_TOGGLE_ALT_GUI:
case MAGIC_SWAP_LCTL_LGUI ... MAGIC_EE_HANDS_RIGHT:
case MAGIC_TOGGLE_GUI:
+ case MAGIC_TOGGLE_CONTROL_CAPSLOCK:
/* keymap config */
keymap_config.raw = eeconfig_read_keymap();
switch (keycode) {
@@ -168,6 +169,9 @@ bool process_magic(uint16_t keycode, keyrecord_t *record) {
case MAGIC_TOGGLE_GUI:
keymap_config.no_gui = !keymap_config.no_gui;
break;
+ case MAGIC_TOGGLE_CONTROL_CAPSLOCK:
+ keymap_config.swap_control_capslock = !keymap_config.swap_control_capslock;
+ break;
}
eeconfig_update_keymap(keymap_config.raw);
diff --git a/quantum/process_keycode/process_printer.c b/quantum/process_keycode/process_printer.c
index 82528cc680..0801f078ef 100644
--- a/quantum/process_keycode/process_printer.c
+++ b/quantum/process_keycode/process_printer.c
@@ -16,13 +16,14 @@
#include "process_printer.h"
#include "action_util.h"
+#include "uart.h"
bool printing_enabled = false;
uint8_t character_shift = 0;
void enable_printing(void) {
printing_enabled = true;
- serial_init();
+ uart_init(19200);
}
void disable_printing(void) { printing_enabled = false; }
@@ -35,7 +36,7 @@ uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0
void print_char(char c) {
USB_Disable();
- serial_send(c);
+ uart_write(c);
USB_Init();
}
diff --git a/quantum/process_keycode/process_printer.h b/quantum/process_keycode/process_printer.h
index 3c6d06ff94..6f4d09f333 100644
--- a/quantum/process_keycode/process_printer.h
+++ b/quantum/process_keycode/process_printer.h
@@ -18,6 +18,4 @@
#include "quantum.h"
-#include "protocol/serial.h"
-
bool process_printer(uint16_t keycode, keyrecord_t *record);
diff --git a/quantum/process_keycode/process_rgb.c b/quantum/process_keycode/process_rgb.c
index 69853cd5c4..c805bd615d 100644
--- a/quantum/process_keycode/process_rgb.c
+++ b/quantum/process_keycode/process_rgb.c
@@ -51,12 +51,8 @@ static void __attribute__((noinline, unused)) handleKeycodeRGBMode(const uint8_t
* Handle keycodes for both rgblight and rgbmatrix
*/
bool process_rgb(const uint16_t keycode, const keyrecord_t *record) {
-#ifndef SPLIT_KEYBOARD
- if (record->event.pressed) {
-#else
- // Split keyboards need to trigger on key-up for edge-case issue
+ // need to trigger on key-up for edge-case issue
if (!record->event.pressed) {
-#endif
#if (defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES)) || (defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES))
uint8_t shifted = get_mods() & MOD_MASK_SHIFT;
#endif
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 35b6351e9d..c106f795d0 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -76,9 +76,9 @@ uint8_t extract_mod_bits(uint16_t code) {
return mods_to_send;
}
-static void do_code16(uint16_t code, void (*f)(uint8_t)) { f(extract_mod_bits(code)); }
+void do_code16(uint16_t code, void (*f)(uint8_t)) { f(extract_mod_bits(code)); }
-void register_code16(uint16_t code) {
+__attribute__((weak)) void register_code16(uint16_t code) {
if (IS_MOD(code) || code == KC_NO) {
do_code16(code, register_mods);
} else {
@@ -87,7 +87,7 @@ void register_code16(uint16_t code) {
register_code(code);
}
-void unregister_code16(uint16_t code) {
+__attribute__((weak)) void unregister_code16(uint16_t code) {
unregister_code(code);
if (IS_MOD(code) || code == KC_NO) {
do_code16(code, unregister_mods);
@@ -96,7 +96,7 @@ void unregister_code16(uint16_t code) {
}
}
-void tap_code16(uint16_t code) {
+__attribute__((weak)) void tap_code16(uint16_t code) {
register_code16(code);
#if TAP_CODE_DELAY > 0
wait_ms(TAP_CODE_DELAY);
@@ -263,7 +263,7 @@ bool process_record_quantum(keyrecord_t *record) {
#ifdef TAP_DANCE_ENABLE
process_tap_dance(keycode, record) &&
#endif
-#if defined(UNICODE_ENABLE) || defined(UNICODEMAP_ENABLE) || defined(UCIS_ENABLE)
+#if defined(UNICODE_COMMON_ENABLE)
process_unicode_common(keycode, record) &&
#endif
#ifdef LEADER_ENABLE
@@ -387,7 +387,7 @@ void matrix_init_quantum() {
#ifdef RGB_MATRIX_ENABLE
rgb_matrix_init();
#endif
-#if defined(UNICODE_ENABLE) || defined(UNICODEMAP_ENABLE) || defined(UCIS_ENABLE)
+#if defined(UNICODE_COMMON_ENABLE)
unicode_input_mode_init();
#endif
#ifdef HAPTIC_ENABLE
diff --git a/quantum/quantum.h b/quantum/quantum.h
index 6927884e2f..5d3a665887 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -109,6 +109,10 @@ extern layer_state_t layer_state;
# include "process_unicodemap.h"
#endif
+#ifdef UNICODE_COMMON_ENABLE
+# include "process_unicode_common.h"
+#endif
+
#ifdef KEY_OVERRIDE_ENABLE
# include "process_key_override.h"
#endif
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
index e4d0167aac..3950a3bcae 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -597,6 +597,8 @@ enum quantum_keycodes {
MACRO_30,
MACRO_31,
+ MAGIC_TOGGLE_CONTROL_CAPSLOCK,
+
// Start of custom keycode range for keyboards and keymaps - always leave at the end
SAFE_RANGE
};
@@ -749,6 +751,7 @@ enum quantum_keycodes {
#define CL_NORM MAGIC_UNSWAP_CONTROL_CAPSLOCK
#define CL_CTRL MAGIC_CAPSLOCK_TO_CONTROL
#define CL_CAPS MAGIC_UNCAPSLOCK_TO_CONTROL
+#define CL_TOGG MAGIC_TOGGLE_CONTROL_CAPSLOCK
#define LCG_SWP MAGIC_SWAP_LCTL_LGUI
#define LCG_NRM MAGIC_UNSWAP_LCTL_LGUI
diff --git a/quantum/split_common/split_util.h b/quantum/split_common/split_util.h
index ef72043bb7..c7eabea233 100644
--- a/quantum/split_common/split_util.h
+++ b/quantum/split_common/split_util.h
@@ -2,7 +2,6 @@
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
#include "matrix.h"
diff --git a/quantum/split_common/transaction_id_define.h b/quantum/split_common/transaction_id_define.h
index 535bc21aea..aa71c3621e 100644
--- a/quantum/split_common/transaction_id_define.h
+++ b/quantum/split_common/transaction_id_define.h
@@ -78,6 +78,12 @@ enum serial_transaction_id {
PUT_ST7565,
#endif // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
+#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+ GET_POINTING_CHECKSUM,
+ GET_POINTING_DATA,
+ PUT_POINTING_CPI,
+#endif // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
PUT_RPC_INFO,
PUT_RPC_REQ_DATA,
diff --git a/quantum/split_common/transactions.c b/quantum/split_common/transactions.c
index 3ff87710e7..81f9836382 100644
--- a/quantum/split_common/transactions.c
+++ b/quantum/split_common/transactions.c
@@ -579,6 +579,84 @@ static void st7565_handlers_slave(matrix_row_t master_matrix[], matrix_row_t sla
#endif // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)
////////////////////////////////////////////////////
+// POINTING
+
+#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
+static bool pointing_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+# if defined(POINTING_DEVICE_LEFT)
+ if (is_keyboard_left()) {
+ return true;
+ }
+# elif defined(POINTING_DEVICE_RIGHT)
+ if (!is_keyboard_left()) {
+ return true;
+ }
+# endif
+ static uint32_t last_update = 0;
+ static uint16_t last_cpi = 0;
+ report_mouse_t temp_state;
+ uint16_t temp_cpi;
+ bool okay = read_if_checksum_mismatch(GET_POINTING_CHECKSUM, GET_POINTING_DATA, &last_update, &temp_state, &split_shmem->pointing.report, sizeof(temp_state));
+ if (okay) pointing_device_set_shared_report(temp_state);
+ temp_cpi = pointing_device_get_shared_cpi();
+ if (temp_cpi && memcmp(&last_cpi, &temp_cpi, sizeof(temp_cpi)) != 0) {
+ memcpy(&split_shmem->pointing.cpi, &temp_cpi, sizeof(temp_cpi));
+ okay = transport_write(PUT_POINTING_CPI, &split_shmem->pointing.cpi, sizeof(split_shmem->pointing.cpi));
+ if (okay) {
+ last_cpi = temp_cpi;
+ }
+ }
+ return okay;
+}
+
+extern const pointing_device_driver_t pointing_device_driver;
+
+static void pointing_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
+# if defined(POINTING_DEVICE_LEFT)
+ if (!is_keyboard_left()) {
+ return;
+ }
+# elif defined(POINTING_DEVICE_RIGHT)
+ if (is_keyboard_left()) {
+ return;
+ }
+# endif
+ report_mouse_t temp_report;
+ uint16_t temp_cpi;
+# ifdef POINTING_DEVICE_TASK_THROTTLE_MS
+ static uint32_t last_exec = 0;
+ if (timer_elapsed32(last_exec) < POINTING_DEVICE_TASK_THROTTLE_MS) {
+ return;
+ }
+ last_exec = timer_read32();
+# endif
+ temp_cpi = !pointing_device_driver.get_cpi ? 0 : pointing_device_driver.get_cpi(); // check for NULL
+ if (split_shmem->pointing.cpi && memcmp(&split_shmem->pointing.cpi, &temp_cpi, sizeof(temp_cpi)) != 0) {
+ if (pointing_device_driver.set_cpi) {
+ pointing_device_driver.set_cpi(split_shmem->pointing.cpi);
+ }
+ }
+ memset(&temp_report, 0, sizeof(temp_report));
+ temp_report = pointing_device_driver.get_report(temp_report);
+ memcpy(&split_shmem->pointing.report, &temp_report, sizeof(temp_report));
+ // Now update the checksum given that the pointing has been written to
+ split_shmem->pointing.checksum = crc8(&temp_report, sizeof(temp_report));
+}
+
+# define TRANSACTIONS_POINTING_MASTER() TRANSACTION_HANDLER_MASTER(pointing)
+# define TRANSACTIONS_POINTING_SLAVE() TRANSACTION_HANDLER_SLAVE(pointing)
+# define TRANSACTIONS_POINTING_REGISTRATIONS [GET_POINTING_CHECKSUM] = trans_target2initiator_initializer(pointing.checksum), [GET_POINTING_DATA] = trans_target2initiator_initializer(pointing.report), [PUT_POINTING_CPI] = trans_initiator2target_initializer(pointing.cpi),
+
+#else // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
+# define TRANSACTIONS_POINTING_MASTER()
+# define TRANSACTIONS_POINTING_SLAVE()
+# define TRANSACTIONS_POINTING_REGISTRATIONS
+
+#endif // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
+////////////////////////////////////////////////////
uint8_t dummy;
split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = {
@@ -604,6 +682,7 @@ split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = {
TRANSACTIONS_WPM_REGISTRATIONS
TRANSACTIONS_OLED_REGISTRATIONS
TRANSACTIONS_ST7565_REGISTRATIONS
+ TRANSACTIONS_POINTING_REGISTRATIONS
// clang-format on
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
@@ -629,6 +708,7 @@ bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix
TRANSACTIONS_WPM_MASTER();
TRANSACTIONS_OLED_MASTER();
TRANSACTIONS_ST7565_MASTER();
+ TRANSACTIONS_POINTING_MASTER();
return true;
}
@@ -647,6 +727,7 @@ void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[
TRANSACTIONS_WPM_SLAVE();
TRANSACTIONS_OLED_SLAVE();
TRANSACTIONS_ST7565_SLAVE();
+ TRANSACTIONS_POINTING_SLAVE();
}
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
diff --git a/quantum/split_common/transport.h b/quantum/split_common/transport.h
index 1d4f6ed0cd..31b804908b 100644
--- a/quantum/split_common/transport.h
+++ b/quantum/split_common/transport.h
@@ -106,6 +106,15 @@ typedef struct _split_mods_sync_t {
} split_mods_sync_t;
#endif // SPLIT_MODS_ENABLE
+#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+# include "pointing_device.h"
+typedef struct _split_slave_pointing_sync_t {
+ uint8_t checksum;
+ report_mouse_t report;
+ uint16_t cpi;
+} split_slave_pointing_sync_t;
+#endif // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
typedef struct _rpc_sync_info_t {
int8_t transaction_id;
@@ -173,6 +182,10 @@ typedef struct _split_shared_memory_t {
uint8_t current_st7565_state;
#endif // ST7565_ENABLE(OLED_ENABLE) && defined(SPLIT_ST7565_ENABLE)
+#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+ split_slave_pointing_sync_t pointing;
+#endif // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)
+
#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)
rpc_sync_info_t rpc_info;
uint8_t rpc_m2s_buffer[RPC_M2S_BUFFER_SIZE];
diff --git a/quantum/wpm.c b/quantum/wpm.c
index 925e2c416e..62d4128b8e 100644
--- a/quantum/wpm.c
+++ b/quantum/wpm.c
@@ -22,33 +22,37 @@
// WPM Stuff
static uint8_t current_wpm = 0;
static uint32_t wpm_timer = 0;
-#ifndef WPM_UNFILTERED
-static uint32_t smoothing_timer = 0;
-#endif
/* The WPM calculation works by specifying a certain number of 'periods' inside
* a ring buffer, and we count the number of keypresses which occur in each of
* those periods. Then to calculate WPM, we add up all of the keypresses in
* the whole ring buffer, divide by the number of keypresses in a 'word', and
- * then adjust for how much time is captured by our ring buffer. Right now
- * the ring buffer is hardcoded below to be six half-second periods, accounting
- * for a total WPM sampling period of up to three seconds of typing.
+ * then adjust for how much time is captured by our ring buffer. The size
+ * of the ring buffer can be configured using the keymap configuration
+ * value `WPM_SAMPLE_PERIODS`.
*
- * Whenever our WPM drops to absolute zero due to no typing occurring within
- * any contiguous three seconds, we reset and start measuring fresh,
- * which lets our WPM immediately reach the correct value even before a full
- * three second sampling buffer has been filled.
*/
#define MAX_PERIODS (WPM_SAMPLE_PERIODS)
#define PERIOD_DURATION (1000 * WPM_SAMPLE_SECONDS / MAX_PERIODS)
-#define LATENCY (100)
-static int8_t period_presses[MAX_PERIODS] = {0};
+
+static int16_t period_presses[MAX_PERIODS] = {0};
static uint8_t current_period = 0;
static uint8_t periods = 1;
#if !defined(WPM_UNFILTERED)
-static uint8_t prev_wpm = 0;
-static uint8_t next_wpm = 0;
+/* LATENCY is used as part of filtering, and controls how quickly the reported
+ * WPM trails behind our actual instantaneous measured WPM value, and is
+ * defined in milliseconds. So for LATENCY == 100, the displayed WPM is
+ * smoothed out over periods of 0.1 seconds. This results in a nice,
+ * smoothly-moving reported WPM value which nevertheless is never more than
+ * 0.1 seconds behind the typist's actual current WPM.
+ *
+ * LATENCY is not used if WPM_UNFILTERED is defined.
+ */
+# define LATENCY (100)
+static uint32_t smoothing_timer = 0;
+static uint8_t prev_wpm = 0;
+static uint8_t next_wpm = 0;
#endif
void set_current_wpm(uint8_t new_wpm) { current_wpm = new_wpm; }
@@ -71,7 +75,7 @@ __attribute__((weak)) bool wpm_keycode_user(uint16_t keycode) {
return false;
}
-#ifdef WPM_ALLOW_COUNT_REGRESSION
+#if defined(WPM_ALLOW_COUNT_REGRESSION)
__attribute__((weak)) uint8_t wpm_regress_count(uint16_t keycode) {
bool weak_modded = (keycode >= QK_LCTL && keycode < QK_LSFT) || (keycode >= QK_RCTL && keycode < QK_RSFT);
@@ -95,12 +99,12 @@ __attribute__((weak)) uint8_t wpm_regress_count(uint16_t keycode) {
// Outside 'raw' mode we smooth results over time.
void update_wpm(uint16_t keycode) {
- if (wpm_keycode(keycode)) {
+ if (wpm_keycode(keycode) && period_presses[current_period] < INT16_MAX) {
period_presses[current_period]++;
}
-#ifdef WPM_ALLOW_COUNT_REGRESSION
+#if defined(WPM_ALLOW_COUNT_REGRESSION)
uint8_t regress = wpm_regress_count(keycode);
- if (regress) {
+ if (regress && period_presses[current_period] > INT16_MIN) {
period_presses[current_period]--;
}
#endif
@@ -116,32 +120,41 @@ void decay_wpm(void) {
}
int32_t elapsed = timer_elapsed32(wpm_timer);
uint32_t duration = (((periods)*PERIOD_DURATION) + elapsed);
- uint32_t wpm_now = (60000 * presses) / (duration * WPM_ESTIMATED_WORD_SIZE);
- wpm_now = (wpm_now > 240) ? 240 : wpm_now;
+ int32_t wpm_now = (60000 * presses) / (duration * WPM_ESTIMATED_WORD_SIZE);
+
+ if (wpm_now < 0) // set some reasonable WPM measurement limits
+ wpm_now = 0;
+ if (wpm_now > 240) wpm_now = 240;
if (elapsed > PERIOD_DURATION) {
current_period = (current_period + 1) % MAX_PERIODS;
period_presses[current_period] = 0;
periods = (periods < MAX_PERIODS - 1) ? periods + 1 : MAX_PERIODS - 1;
elapsed = 0;
- /* if (wpm_timer == 0) { */
- wpm_timer = timer_read32();
- /* } else { */
- /* wpm_timer += PERIOD_DURATION; */
- /* } */
+ wpm_timer = timer_read32();
}
if (presses < 2) // don't guess high WPM based on a single keypress.
wpm_now = 0;
-#if defined WPM_LAUNCH_CONTROL
+#if defined(WPM_LAUNCH_CONTROL)
+ /*
+ * If the `WPM_LAUNCH_CONTROL` option is enabled, then whenever our WPM
+ * drops to absolute zero due to no typing occurring within our sample
+ * ring buffer, we reset and start measuring fresh, which lets our WPM
+ * immediately reach the correct value even before a full sampling buffer
+ * has been filled.
+ */
if (presses == 0) {
- current_period = 0;
- periods = 0;
- wpm_now = 0;
+ current_period = 0;
+ periods = 0;
+ wpm_now = 0;
+ period_presses[0] = 0;
}
#endif // WPM_LAUNCH_CONTROL
-#ifndef WPM_UNFILTERED
+#if defined(WPM_UNFILTERED)
+ current_wpm = wpm_now;
+#else
int32_t latency = timer_elapsed32(smoothing_timer);
if (latency > LATENCY) {
smoothing_timer = timer_read32();
@@ -150,7 +163,5 @@ void decay_wpm(void) {
}
current_wpm = prev_wpm + (latency * ((int)next_wpm - (int)prev_wpm) / LATENCY);
-#else
- current_wpm = wpm_now;
#endif
}
diff --git a/quantum/wpm.h b/quantum/wpm.h
index c8e7d26684..305d75b450 100644
--- a/quantum/wpm.h
+++ b/quantum/wpm.h
@@ -26,7 +26,7 @@
# define WPM_SAMPLE_SECONDS 5
#endif
#ifndef WPM_SAMPLE_PERIODS
-# define WPM_SAMPLE_PERIODS 50
+# define WPM_SAMPLE_PERIODS 25
#endif
bool wpm_keycode(uint16_t keycode);