diff options
Diffstat (limited to 'quantum')
46 files changed, 1345 insertions, 339 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..fa20c675c3 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c @@ -16,6 +16,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.  */  #include <stdint.h> +#include "quantum.h"  #include "keyboard.h"  #include "matrix.h"  #include "keymap.h" @@ -349,6 +350,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 +390,17 @@ void switch_events(uint8_t row, uint8_t col, bool pressed) {  #endif  } -/** \brief Keyboard task: Do keyboard routine jobs +/** \brief Perform scan of keyboard matrix   * - * Do routine keyboard jobs: - * - * * 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,10 +447,94 @@ void keyboard_task(void) {  MATRIX_LOOP_END: -#ifdef DEBUG_MATRIX_SCAN_RATE      matrix_scan_perf_task(); +    return matrix_changed; +} + +/** \brief Tasks previously located in matrix_scan_quantum + * + * TODO: rationalise against keyboard_task and current split role + */ +void quantum_task(void) { +#ifdef SPLIT_KEYBOARD +    // some tasks should only run on master +    if (!is_keyboard_master()) return; +#endif + +#if defined(AUDIO_ENABLE) && defined(AUDIO_INIT_DELAY) +    // There are some tasks that need to be run a little bit +    // after keyboard startup, or else they will not work correctly +    // because of interaction with the USB device state, which +    // may still be in flux... +    // +    // At the moment the only feature that needs this is the +    // startup song. +    static bool     delayed_tasks_run  = false; +    static uint16_t delayed_task_timer = 0; +    if (!delayed_tasks_run) { +        if (!delayed_task_timer) { +            delayed_task_timer = timer_read(); +        } else if (timer_elapsed(delayed_task_timer) > 300) { +            audio_startup(); +            delayed_tasks_run = true; +        } +    } +#endif + +#if defined(AUDIO_ENABLE) && !defined(NO_MUSIC_MODE) +    music_task(); +#endif + +#ifdef KEY_OVERRIDE_ENABLE +    key_override_task(); +#endif + +#ifdef SEQUENCER_ENABLE +    sequencer_task();  #endif +#ifdef TAP_DANCE_ENABLE +    tap_dance_task(); +#endif + +#ifdef COMBO_ENABLE +    combo_task(); +#endif + +#ifdef WPM_ENABLE +    decay_wpm(); +#endif + +#ifdef HAPTIC_ENABLE +    haptic_task(); +#endif + +#ifdef DIP_SWITCH_ENABLE +    dip_switch_read(false); +#endif + +#ifdef AUTO_SHIFT_ENABLE +    autoshift_matrix_scan(); +#endif +} + +/** \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; + +    quantum_task(); +  #if defined(RGBLIGHT_ENABLE)      rgblight_task();  #endif @@ -453,7 +553,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 +616,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_grave_esc.c b/quantum/process_keycode/process_grave_esc.c index 41c50f5cb8..ddf027391d 100644 --- a/quantum/process_keycode/process_grave_esc.c +++ b/quantum/process_keycode/process_grave_esc.c @@ -15,13 +15,13 @@   */  #include "process_grave_esc.h" -/* true if the last press of GRAVE_ESC was shifted (i.e. GUI or SHIFT were pressed), false otherwise. +/* true if the last press of QK_GRAVE_ESCAPE was shifted (i.e. GUI or SHIFT were pressed), false otherwise.   * Used to ensure that the correct keycode is released if the key is released.   */  static bool grave_esc_was_shifted = false;  bool process_grave_esc(uint16_t keycode, keyrecord_t *record) { -    if (keycode == GRAVE_ESC) { +    if (keycode == QK_GRAVE_ESCAPE) {          const uint8_t mods    = get_mods();          uint8_t       shifted = mods & MOD_MASK_SG; 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..f12b1a9526 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -47,10 +47,6 @@ float default_layer_songs[][16][2] = DEFAULT_LAYER_SONGS;  #    endif  #endif -#ifdef AUTO_SHIFT_ENABLE -#    include "process_auto_shift.h" -#endif -  uint8_t extract_mod_bits(uint16_t code) {      switch (code) {          case QK_MODS ... QK_MODS_MAX: @@ -76,9 +72,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 +83,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 +92,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 +259,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 @@ -306,12 +302,12 @@ bool process_record_quantum(keyrecord_t *record) {      if (record->event.pressed) {          switch (keycode) {  #ifndef NO_RESET -            case RESET: +            case QK_BOOTLOADER:                  reset_keyboard();                  return false;  #endif  #ifndef NO_DEBUG -            case DEBUG: +            case QK_DEBUG_TOGGLE:                  debug_enable ^= 1;                  if (debug_enable) {                      print("DEBUG: enabled.\n"); @@ -320,7 +316,7 @@ bool process_record_quantum(keyrecord_t *record) {                  }  #endif                  return false; -            case EEPROM_RESET: +            case QK_CLEAR_EEPROM:                  eeconfig_init();                  return false;  #ifdef VELOCIKEY_ENABLE @@ -387,7 +383,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 @@ -400,73 +396,8 @@ void matrix_init_quantum() {      matrix_init_kb();  } -void matrix_scan_quantum() { -#if defined(AUDIO_ENABLE) && defined(AUDIO_INIT_DELAY) -    // There are some tasks that need to be run a little bit -    // after keyboard startup, or else they will not work correctly -    // because of interaction with the USB device state, which -    // may still be in flux... -    // -    // At the moment the only feature that needs this is the -    // startup song. -    static bool     delayed_tasks_run  = false; -    static uint16_t delayed_task_timer = 0; -    if (!delayed_tasks_run) { -        if (!delayed_task_timer) { -            delayed_task_timer = timer_read(); -        } else if (timer_elapsed(delayed_task_timer) > 300) { -            audio_startup(); -            delayed_tasks_run = true; -        } -    } -#endif - -#if defined(AUDIO_ENABLE) && !defined(NO_MUSIC_MODE) -    music_task(); -#endif - -#ifdef KEY_OVERRIDE_ENABLE -    key_override_task(); -#endif - -#ifdef SEQUENCER_ENABLE -    sequencer_task(); -#endif - -#ifdef TAP_DANCE_ENABLE -    tap_dance_task(); -#endif - -#ifdef COMBO_ENABLE -    combo_task(); -#endif - -#ifdef LED_MATRIX_ENABLE -    led_matrix_task(); -#endif - -#ifdef WPM_ENABLE -    decay_wpm(); -#endif - -#ifdef HAPTIC_ENABLE -    haptic_task(); -#endif - -#ifdef DIP_SWITCH_ENABLE -    dip_switch_read(false); -#endif - -#ifdef AUTO_SHIFT_ENABLE -    autoshift_matrix_scan(); -#endif - -    matrix_scan_kb(); -} - -#ifdef HD44780_ENABLED -#    include "hd44780.h" -#endif +// TODO: remove legacy api +void matrix_scan_quantum() { matrix_scan_kb(); }  //------------------------------------------------------------------------------  // Override these functions in your keymap file to play different tunes on 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..15ea0276a7 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -80,8 +80,8 @@ enum quantum_keycodes {      QK_UNICODEMAP_PAIR_MAX = 0xFFFF,      // Loose keycodes - to be used directly -    RESET = 0x5C00, -    DEBUG,  // 5C01 +    QK_BOOTLOADER = 0x5C00, +    QK_DEBUG_TOGGLE,  // 5C01      // Magic      MAGIC_SWAP_CONTROL_CAPSLOCK,       // 5C02 @@ -106,7 +106,7 @@ enum quantum_keycodes {      MAGIC_TOGGLE_ALT_GUI,              // 5C15      // Grave Escape -    GRAVE_ESC,  // 5C16 +    QK_GRAVE_ESCAPE,  // 5C16      // Auto Shift      KC_ASUP,   // 5C17 @@ -379,7 +379,7 @@ enum quantum_keycodes {      OUT_USB,   // 5CDE      // Clear EEPROM -    EEPROM_RESET,  // 5CDF +    QK_CLEAR_EEPROM,  // 5CDF      // Unicode      UNICODE_MODE_FORWARD,  // 5CE0 @@ -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  }; @@ -714,9 +716,11 @@ enum quantum_keycodes {  #define MACROTAP(kc) (QK_MACRO | (FUNC_TAP << 8) | (kc))  #define MACRODOWN(...) (record->event.pressed ? MACRO(__VA_ARGS__) : MACRO_NONE) -#define KC_GESC GRAVE_ESC +#define QK_GESC QK_GRAVE_ESCAPE -#define EEP_RST EEPROM_RESET +#define QK_BOOT QK_BOOTLOADER +#define DB_TOGG QK_DEBUG_TOGGLE +#define EE_CLR QK_CLEAR_EEPROM  // Audio Clicky aliases  #define CK_TOGG CLICKY_TOGGLE @@ -749,6 +753,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 @@ -961,3 +966,5 @@ enum quantum_keycodes {  #define PB_32 PROGRAMMABLE_BUTTON_32  #define PROGRAMMABLE_BUTTON_MIN PROGRAMMABLE_BUTTON_1  #define PROGRAMMABLE_BUTTON_MAX PROGRAMMABLE_BUTTON_32 + +#include "quantum_keycodes_legacy.h" diff --git a/quantum/quantum_keycodes_legacy.h b/quantum/quantum_keycodes_legacy.h new file mode 100644 index 0000000000..ed9455ee74 --- /dev/null +++ b/quantum/quantum_keycodes_legacy.h @@ -0,0 +1,13 @@ +#pragma once + +// clang-format off + +// Deprecated Quantum keycodes + +#define RESET        QK_BOOTLOADER +#define DEBUG        QK_DEBUG_TOGGLE +#define GRAVE_ESC    QK_GRAVE_ESCAPE +#define EEPROM_RESET QK_CLEAR_EEPROM + +#define KC_GESC QK_GRAVE_ESCAPE +#define EEP_RST QK_CLEAR_EEPROM diff --git a/quantum/rgb_matrix/animations/pixel_fractal_anim.h b/quantum/rgb_matrix/animations/pixel_fractal_anim.h index 8e25ec402c..35187b92de 100644 --- a/quantum/rgb_matrix/animations/pixel_fractal_anim.h +++ b/quantum/rgb_matrix/animations/pixel_fractal_anim.h @@ -31,6 +31,10 @@ static bool PIXEL_FRACTAL(effect_params_t* params) {      inline uint32_t interval(void) { return 3000 / scale16by8(qadd8(rgb_matrix_config.speed, 16), 16); } +    if (params->init) { +        rgb_matrix_set_color_all(0, 0, 0); +    } +      RGB rgb = rgb_matrix_hsv_to_rgb(rgb_matrix_config.hsv);      for (uint8_t h = 0; h < MATRIX_ROWS; ++h) {          for (uint8_t l = 0; l < MID_COL - 1; ++l) {  // Light and move left columns outwards 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/via_ensure_keycode.h b/quantum/via_ensure_keycode.h index 1aba0cdd2a..75f816b560 100644 --- a/quantum/via_ensure_keycode.h +++ b/quantum/via_ensure_keycode.h @@ -244,12 +244,12 @@ _Static_assert(KC_LT                  == 0x0236, "");  _Static_assert(KC_GT                  == 0x0237, "");  _Static_assert(KC_QUES                == 0x0238, ""); -_Static_assert(RESET                  == 0x5C00, ""); -_Static_assert(DEBUG                  == 0x5C01, ""); +_Static_assert(QK_BOOTLOADER          == 0x5C00, ""); +_Static_assert(QK_DEBUG_TOGGLE        == 0x5C01, "");  _Static_assert(MAGIC_TOGGLE_NKRO      == 0x5C14, ""); -_Static_assert(KC_GESC                == 0x5C16, ""); +_Static_assert(QK_GRAVE_ESCAPE        == 0x5C16, "");  _Static_assert(AU_ON                  == 0x5C1D, "");  _Static_assert(AU_OFF                 == 0x5C1E, ""); 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); | 
