diff options
Diffstat (limited to 'quantum')
67 files changed, 1534 insertions, 882 deletions
diff --git a/quantum/action.c b/quantum/action.c index 5e81efb671..d932c01688 100644 --- a/quantum/action.c +++ b/quantum/action.c @@ -23,7 +23,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "led.h" #include "action_layer.h" #include "action_tapping.h" -#include "action_macro.h" #include "action_util.h" #include "action.h" #include "wait.h" @@ -634,12 +633,7 @@ void process_action(keyrecord_t *record, action_t action) { break; # endif #endif - /* Extentions */ -#ifndef NO_ACTION_MACRO - case ACT_MACRO: - action_macro_play(action_get_macro(record, action.func.id, action.func.opt)); - break; -#endif + #ifdef SWAP_HANDS_ENABLE case ACT_SWAP_HANDS: switch (action.swap.code) { @@ -713,11 +707,6 @@ void process_action(keyrecord_t *record, action_t action) { # endif } #endif -#ifndef NO_ACTION_FUNCTION - case ACT_FUNCTION: - action_function(record, action.func.id, action.func.opt); - break; -#endif default: break; } @@ -794,7 +783,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 +879,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 +944,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 +956,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 +973,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 +984,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 +995,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(); @@ -1041,7 +1030,6 @@ void clear_keyboard_but_mods_and_keys() { host_consumer_send(0); #endif clear_weak_mods(); - clear_macro_mods(); send_keyboard_report(); #ifdef MOUSEKEY_ENABLE mousekey_clear(); @@ -1104,12 +1092,6 @@ bool is_tap_action(action_t action) { return true; } return false; - case ACT_MACRO: - case ACT_FUNCTION: - if (action.func.opt & FUNC_TAP) { - return true; - } - return false; } return false; } @@ -1166,12 +1148,6 @@ void debug_action(action_t action) { case ACT_LAYER_TAP_EXT: dprint("ACT_LAYER_TAP_EXT"); break; - case ACT_MACRO: - dprint("ACT_MACRO"); - break; - case ACT_FUNCTION: - dprint("ACT_FUNCTION"); - break; case ACT_SWAP_HANDS: dprint("ACT_SWAP_HANDS"); break; diff --git a/quantum/action.h b/quantum/action.h index b562f18c5b..39b5da63b8 100644 --- a/quantum/action.h +++ b/quantum/action.h @@ -19,25 +19,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include <stdint.h> #include <stdbool.h> +#include "progmem.h" #include "keyboard.h" #include "keycode.h" #include "action_code.h" -#include "action_macro.h" #ifdef __cplusplus extern "C" { #endif -/* Disable macro and function features when LTO is enabled, since they break */ -#ifdef LTO_ENABLE -# ifndef NO_ACTION_MACRO -# define NO_ACTION_MACRO -# endif -# ifndef NO_ACTION_FUNCTION -# define NO_ACTION_FUNCTION -# endif -#endif - #ifndef TAP_CODE_DELAY # define TAP_CODE_DELAY 0 #endif @@ -72,12 +62,6 @@ void action_exec(keyevent_t event); action_t action_for_key(uint8_t layer, keypos_t key); action_t action_for_keycode(uint16_t keycode); -/* macro */ -const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt); - -/* user defined special function */ -void action_function(keyrecord_t *record, uint8_t id, uint8_t opt); - /* keyboard-specific key event (pre)processing */ bool process_record_quantum(keyrecord_t *record); diff --git a/quantum/action_code.h b/quantum/action_code.h index eb18c36ae8..20b3e459d2 100644 --- a/quantum/action_code.h +++ b/quantum/action_code.h @@ -79,19 +79,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. * 101E|LLLL|1111 0100 One Shot Layer (0xF4) [TAP] * 101E|LLLL|1111 xxxx Reserved (0xF5-FF) * ELLLL: layer 0-31(E: extra bit for layer 16-31) - * - * Extensions(11xx) - * ---------------- - * ACT_MACRO(1100): - * 1100|opt | id(8) Macro play? - * 1100|1111| id(8) Macro record? - * - * 1101|xxxx xxxx xxxx (reserved) - * 1110|xxxx xxxx xxxx (reserved) - * - * ACT_FUNCTION(1111): - * 1111| address(12) Function? - * 1111|opt | id(8) Function? */ enum action_kind_id { /* Key Actions */ @@ -111,9 +98,6 @@ enum action_kind_id { ACT_LAYER_MODS = 0b1001, ACT_LAYER_TAP = 0b1010, /* Layer 0-15 */ ACT_LAYER_TAP_EXT = 0b1011, /* Layer 16-31 */ - /* Extensions */ - ACT_MACRO = 0b1100, - ACT_FUNCTION = 0b1111 }; /** \brief Action Code Struct @@ -164,11 +148,6 @@ typedef union { uint8_t page : 2; uint8_t kind : 4; } usage; - struct action_function { - uint8_t id : 8; - uint8_t opt : 4; - uint8_t kind : 4; - } func; struct action_swap { uint8_t code : 8; uint8_t opt : 4; @@ -275,17 +254,6 @@ enum layer_param_tap_op { #define ACTION_DEFAULT_LAYER_BIT_XOR(part, bits) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), 0) #define ACTION_DEFAULT_LAYER_BIT_SET(part, bits) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), 0) -/* Macro */ -#define ACTION_MACRO(id) ACTION(ACT_MACRO, (id)) -#define ACTION_MACRO_TAP(id) ACTION(ACT_MACRO, FUNC_TAP << 8 | (id)) -#define ACTION_MACRO_OPT(id, opt) ACTION(ACT_MACRO, (opt) << 8 | (id)) -/* Function */ -enum function_opts { - FUNC_TAP = 0x8, /* indciates function is tappable */ -}; -#define ACTION_FUNCTION(id) ACTION(ACT_FUNCTION, (id)) -#define ACTION_FUNCTION_TAP(id) ACTION(ACT_FUNCTION, FUNC_TAP << 8 | (id)) -#define ACTION_FUNCTION_OPT(id, opt) ACTION(ACT_FUNCTION, (opt) << 8 | (id)) /* OneHand Support */ enum swap_hands_param_tap_op { OP_SH_TOGGLE = 0xF0, diff --git a/quantum/action_macro.c b/quantum/action_macro.c deleted file mode 100644 index 92228c0ba8..0000000000 --- a/quantum/action_macro.c +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright 2013 Jun Wako <wakojun@gmail.com> - -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 "action.h" -#include "action_util.h" -#include "action_macro.h" -#include "wait.h" - -#ifdef DEBUG_ACTION -# include "debug.h" -#else -# include "nodebug.h" -#endif - -#ifndef NO_ACTION_MACRO - -# define MACRO_READ() (macro = MACRO_GET(macro_p++)) -/** \brief Action Macro Play - * - * FIXME: Needs doc - */ -void action_macro_play(const macro_t *macro_p) { - macro_t macro = END; - uint8_t interval = 0; - - if (!macro_p) return; - while (true) { - switch (MACRO_READ()) { - case KEY_DOWN: - MACRO_READ(); - dprintf("KEY_DOWN(%02X)\n", macro); - if (IS_MOD(macro)) { - add_macro_mods(MOD_BIT(macro)); - send_keyboard_report(); - } else { - register_code(macro); - } - break; - case KEY_UP: - MACRO_READ(); - dprintf("KEY_UP(%02X)\n", macro); - if (IS_MOD(macro)) { - del_macro_mods(MOD_BIT(macro)); - send_keyboard_report(); - } else { - unregister_code(macro); - } - break; - case WAIT: - MACRO_READ(); - dprintf("WAIT(%u)\n", macro); - { - uint8_t ms = macro; - while (ms--) wait_ms(1); - } - break; - case INTERVAL: - interval = MACRO_READ(); - dprintf("INTERVAL(%u)\n", interval); - break; - case 0x04 ... 0x73: - dprintf("DOWN(%02X)\n", macro); - register_code(macro); - break; - case 0x84 ... 0xF3: - dprintf("UP(%02X)\n", macro); - unregister_code(macro & 0x7F); - break; - case END: - default: - return; - } - // interval - { - uint8_t ms = interval; - while (ms--) wait_ms(1); - } - } -} -#endif diff --git a/quantum/action_macro.h b/quantum/action_macro.h deleted file mode 100644 index 685e2c6ffc..0000000000 --- a/quantum/action_macro.h +++ /dev/null @@ -1,123 +0,0 @@ -/* -Copyright 2013 Jun Wako <wakojun@gmail.com> - -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/>. -*/ - -#pragma once - -#include <stdint.h> -#include "progmem.h" - -typedef uint8_t macro_t; - -#define MACRO_NONE (macro_t *)0 -#define MACRO(...) \ - ({ \ - static const macro_t __m[] PROGMEM = {__VA_ARGS__}; \ - &__m[0]; \ - }) -#define MACRO_GET(p) pgm_read_byte(p) - -// Sends press when the macro key is pressed, release when release, or tap_macro when the key has been tapped -#define MACRO_TAP_HOLD(record, press, release, tap_macro) (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? (press) : MACRO_NONE) : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (tap_macro) : (release))) - -// Holds down the modifier mod when the macro key is held, or sends macro instead when tapped -#define MACRO_TAP_HOLD_MOD(record, macro, mod) MACRO_TAP_HOLD(record, (MACRO(D(mod), END)), MACRO(U(mod), END), macro) - -// Holds down the modifier mod when the macro key is held, or pressed a shifted key when tapped (eg: shift+3 for #) -#define MACRO_TAP_SHFT_KEY_HOLD_MOD(record, key, mod) MACRO_TAP_HOLD_MOD(record, (MACRO(I(10), D(LSFT), T(key), U(LSFT), END)), mod) - -// Momentary switch layer when held, sends macro if tapped -#define MACRO_TAP_HOLD_LAYER(record, macro, layer) \ - (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? ({ \ - layer_on((layer)); \ - MACRO_NONE; \ - }) \ - : MACRO_NONE) \ - : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (macro) : ({ \ - layer_off((layer)); \ - MACRO_NONE; \ - }))) - -// Momentary switch layer when held, presses a shifted key when tapped (eg: shift+3 for #) -#define MACRO_TAP_SHFT_KEY_HOLD_LAYER(record, key, layer) MACRO_TAP_HOLD_LAYER(record, MACRO(I(10), D(LSFT), T(key), U(LSFT), END), layer) - -#ifndef NO_ACTION_MACRO -void action_macro_play(const macro_t *macro_p); -#else -# define action_macro_play(macro) -#endif - -/* Macro commands - * code(0x04-73) // key down(1byte) - * code(0x04-73) | 0x80 // key up(1byte) - * { KEY_DOWN, code(0x04-0xff) } // key down(2bytes) - * { KEY_UP, code(0x04-0xff) } // key up(2bytes) - * WAIT // wait milli-seconds - * INTERVAL // set interval between macro commands - * END // stop macro execution - * - * Ideas(Not implemented): - * modifiers - * system usage - * consumer usage - * unicode usage - * function call - * conditionals - * loop - */ -enum macro_command_id { - /* 0x00 - 0x03 */ - END = 0x00, - KEY_DOWN, - KEY_UP, - - /* 0x04 - 0x73 (reserved for keycode down) */ - - /* 0x74 - 0x83 */ - WAIT = 0x74, - INTERVAL, - - /* 0x84 - 0xf3 (reserved for keycode up) */ - - /* 0xf4 - 0xff */ -}; - -/* TODO: keycode:0x04-0x73 can be handled by 1byte command else 2bytes are needed - * if keycode between 0x04 and 0x73 - * keycode / (keycode|0x80) - * else - * {KEY_DOWN, keycode} / {KEY_UP, keycode} - */ -#define DOWN(key) KEY_DOWN, (key) -#define UP(key) KEY_UP, (key) -#define TYPE(key) DOWN(key), UP(key) -#define WAIT(ms) WAIT, (ms) -#define INTERVAL(ms) INTERVAL, (ms) - -/* key down */ -#define D(key) DOWN(KC_##key) -/* key up */ -#define U(key) UP(KC_##key) -/* key type */ -#define T(key) TYPE(KC_##key) -/* wait */ -#define W(ms) WAIT(ms) -/* interval */ -#define I(ms) INTERVAL(ms) - -/* for backward comaptibility */ -#define MD(key) DOWN(KC_##key) -#define MU(key) UP(KC_##key) diff --git a/quantum/action_util.c b/quantum/action_util.c index 78e02aec18..9eb2a6d30d 100644 --- a/quantum/action_util.c +++ b/quantum/action_util.c @@ -21,12 +21,12 @@ 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; -static uint8_t real_mods = 0; -static uint8_t weak_mods = 0; -static uint8_t macro_mods = 0; +static uint8_t real_mods = 0; +static uint8_t weak_mods = 0; #ifdef KEY_OVERRIDE_ENABLE static uint8_t weak_override_mods = 0; static uint8_t suppressed_mods = 0; @@ -223,7 +223,6 @@ bool is_oneshot_enabled(void) { return keymap_config.oneshot_disable; } void send_keyboard_report(void) { keyboard_report->mods = real_mods; keyboard_report->mods |= weak_mods; - keyboard_report->mods |= macro_mods; #ifndef NO_ACTION_ONESHOT if (oneshot_mods) { @@ -247,7 +246,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 @@ -318,33 +323,6 @@ void set_suppressed_override_mods(uint8_t mods) { suppressed_mods = mods; } void clear_suppressed_override_mods(void) { suppressed_mods = 0; } #endif -/* macro modifier */ -/** \brief get macro mods - * - * FIXME: needs doc - */ -uint8_t get_macro_mods(void) { return macro_mods; } -/** \brief add macro mods - * - * FIXME: needs doc - */ -void add_macro_mods(uint8_t mods) { macro_mods |= mods; } -/** \brief del macro mods - * - * FIXME: needs doc - */ -void del_macro_mods(uint8_t mods) { macro_mods &= ~mods; } -/** \brief set macro mods - * - * FIXME: needs doc - */ -void set_macro_mods(uint8_t mods) { macro_mods = mods; } -/** \brief clear macro mods - * - * FIXME: needs doc - */ -void clear_macro_mods(void) { macro_mods = 0; } - #ifndef NO_ACTION_ONESHOT /** \brief get oneshot mods * diff --git a/quantum/action_util.h b/quantum/action_util.h index f2b3897ae5..bfd0a6cf95 100644 --- a/quantum/action_util.h +++ b/quantum/action_util.h @@ -49,13 +49,6 @@ void del_weak_mods(uint8_t mods); void set_weak_mods(uint8_t mods); void clear_weak_mods(void); -/* macro modifier */ -uint8_t get_macro_mods(void); -void add_macro_mods(uint8_t mods); -void del_macro_mods(uint8_t mods); -void set_macro_mods(uint8_t mods); -void clear_macro_mods(void); - /* oneshot modifier */ uint8_t get_oneshot_mods(void); void add_oneshot_mods(uint8_t 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/color.h b/quantum/color.h index e2cfc46927..135ad623b5 100644 --- a/quantum/color.h +++ b/quantum/color.h @@ -60,7 +60,7 @@ #define HSV_GOLDENROD 30, 218, 218 #define HSV_GREEN 85, 255, 255 #define HSV_MAGENTA 213, 255, 255 -#define HSV_ORANGE 28, 255, 255 +#define HSV_ORANGE 21, 255, 255 #define HSV_PINK 234, 128, 255 #define HSV_PURPLE 191, 255, 255 #define HSV_RED 0, 255, 255 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/dynamic_keymap.c b/quantum/dynamic_keymap.c index 19a6bea59a..8f1f39bc0f 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -29,24 +29,22 @@ # define DYNAMIC_KEYMAP_MACRO_COUNT 16 #endif -// This is the default EEPROM max address to use for dynamic keymaps. -// The default is the ATmega32u4 EEPROM max address. -// Explicitly override it if the keyboard uses a microcontroller with -// more EEPROM *and* it makes sense to increase it. +#ifndef TOTAL_EEPROM_BYTE_COUNT +# error Unknown total EEPROM size. Cannot derive maximum for dynamic keymaps. +#endif + #ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR -# if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) -# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 2047 -# elif defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) -# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 4095 -# elif defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATtiny85__) -# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 511 -# else -# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 1023 -# endif +# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR (TOTAL_EEPROM_BYTE_COUNT - 1) +#endif + +#if DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > (TOTAL_EEPROM_BYTE_COUNT - 1) +# pragma message STR(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) " > " STR((TOTAL_EEPROM_BYTE_COUNT - 1)) +# error DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is configured to use more space than what is available for the selected EEPROM driver #endif // Due to usage of uint16_t check for max 65535 #if DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > 65535 +# pragma message STR(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) " > 65535" # error DYNAMIC_KEYMAP_EEPROM_MAX_ADDR must be less than 65536 #endif @@ -71,6 +69,7 @@ // or DYNAMIC_KEYMAP_EEPROM_MAX_ADDR to increase it, *only if* the microcontroller has // more than the default. #if DYNAMIC_KEYMAP_EEPROM_MAX_ADDR - DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR < 100 +# pragma message STR(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR - DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR) " < 100" # error Dynamic keymaps are configured to use more EEPROM than is available. #endif diff --git a/quantum/keyboard.c b/quantum/keyboard.c index 3bca05aab7..6bb2800910 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c @@ -16,9 +16,11 @@ 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" +#include "magic.h" #include "host.h" #include "led.h" #include "keycode.h" @@ -100,6 +102,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #ifdef SLEEP_LED_ENABLE # include "sleep_led.h" #endif +#ifdef SPLIT_KEYBOARD +# include "split_util.h" +#endif +#ifdef BLUETOOTH_ENABLE +# include "outputselect.h" +#endif static uint32_t last_input_modification_time = 0; uint32_t last_input_activity_time(void) { return last_input_modification_time; } @@ -290,6 +298,36 @@ void housekeeping_task(void) { housekeeping_task_user(); } +/** \brief Init tasks previously located in matrix_init_quantum + * + * TODO: rationalise against keyboard_init and current split role + */ +void quantum_init(void) { + magic(); + led_init_ports(); +#ifdef BACKLIGHT_ENABLE + backlight_init_ports(); +#endif +#ifdef AUDIO_ENABLE + audio_init(); +#endif +#ifdef LED_MATRIX_ENABLE + led_matrix_init(); +#endif +#ifdef RGB_MATRIX_ENABLE + rgb_matrix_init(); +#endif +#if defined(UNICODE_COMMON_ENABLE) + unicode_input_mode_init(); +#endif +#ifdef HAPTIC_ENABLE + haptic_init(); +#endif +#if defined(BLUETOOTH_ENABLE) && defined(OUTPUT_AUTO_ENABLE) + set_output(OUTPUT_AUTO); +#endif +} + /** \brief keyboard_init * * FIXME: needs doc @@ -300,7 +338,11 @@ void keyboard_init(void) { #ifdef VIA_ENABLE via_init(); #endif +#ifdef SPLIT_KEYBOARD + split_pre_init(); +#endif matrix_init(); + quantum_init(); #if defined(CRC_ENABLE) crc_init(); #endif @@ -341,6 +383,9 @@ void keyboard_init(void) { #ifdef VIRTSER_ENABLE virtser_init(); #endif +#ifdef SPLIT_KEYBOARD + split_post_init(); +#endif #if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE) debug_enable = true; @@ -363,28 +408,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 +465,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 +571,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 +634,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/keyboard.h b/quantum/keyboard.h index 08f4e84f94..9b0fb3cef8 100644 --- a/quantum/keyboard.h +++ b/quantum/keyboard.h @@ -58,8 +58,6 @@ void keyboard_setup(void); void keyboard_init(void); /* it runs repeatedly in main loop */ void keyboard_task(void); -/* it runs when host LED status is updated */ -void keyboard_set_leds(uint8_t leds); /* it runs whenever code has to behave differently on a slave */ bool is_keyboard_master(void); /* it runs whenever code has to behave differently on left vs right split */ diff --git a/quantum/keycode.h b/quantum/keycode.h index 38a29b439b..a932550635 100644 --- a/quantum/keycode.h +++ b/quantum/keycode.h @@ -35,8 +35,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #define IS_SYSTEM(code) (KC_PWR <= (code) && (code) <= KC_WAKE) #define IS_CONSUMER(code) (KC_MUTE <= (code) && (code) <= KC_BRID) -#define IS_FN(code) (KC_FN0 <= (code) && (code) <= KC_FN31) - #define IS_MOUSEKEY(code) (KC_MS_UP <= (code) && (code) <= KC_MS_ACCEL2) #define IS_MOUSEKEY_MOVE(code) (KC_MS_UP <= (code) && (code) <= KC_MS_RIGHT) #define IS_MOUSEKEY_BUTTON(code) (KC_MS_BTN1 <= (code) && (code) <= KC_MS_BTN8) @@ -62,11 +60,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #define MOD_MASK_SAG (MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI) #define MOD_MASK_CSAG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI) -#define FN_BIT(code) (1 << FN_INDEX(code)) -#define FN_INDEX(code) ((code)-KC_FN0) -#define FN_MIN KC_FN0 -#define FN_MAX KC_FN31 - // clang-format off /* @@ -509,41 +502,7 @@ enum internal_special_keycodes { KC_MEDIA_FAST_FORWARD, KC_MEDIA_REWIND, KC_BRIGHTNESS_UP, - KC_BRIGHTNESS_DOWN, - - /* Fn keys */ - KC_FN0 = 0xC0, - KC_FN1, - KC_FN2, - KC_FN3, - KC_FN4, - KC_FN5, - KC_FN6, - KC_FN7, - KC_FN8, - KC_FN9, - KC_FN10, - KC_FN11, - KC_FN12, - KC_FN13, - KC_FN14, - KC_FN15, - KC_FN16, // 0xD0 - KC_FN17, - KC_FN18, - KC_FN19, - KC_FN20, - KC_FN21, - KC_FN22, - KC_FN23, - KC_FN24, - KC_FN25, - KC_FN26, - KC_FN27, - KC_FN28, - KC_FN29, - KC_FN30, - KC_FN31 + KC_BRIGHTNESS_DOWN }; enum mouse_keys { diff --git a/quantum/keymap.h b/quantum/keymap.h index 191e813977..2ee2e1b576 100644 --- a/quantum/keymap.h +++ b/quantum/keymap.h @@ -27,7 +27,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. # include <ch.h> #endif #include "keycode.h" -#include "action_macro.h" #include "report.h" #include "host.h" // #include "print.h" @@ -35,9 +34,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "keycode_config.h" // ChibiOS uses RESET in its FlagStatus enumeration -// Therefore define it as QK_RESET here, to avoid name collision +// Therefore define it as QK_BOOTLOADER here, to avoid name collision #if defined(PROTOCOL_CHIBIOS) -# define RESET QK_RESET +# define RESET QK_BOOTLOADER #endif // Gross hack, remove me and change RESET keycode to QK_BOOT #if defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1287__) @@ -49,8 +48,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. // translates key to keycode uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key); -// translates function id to action -uint16_t keymap_function_id_to_action(uint16_t function_id); - extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; -extern const uint16_t fn_actions[]; diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c index 5007f15f11..cd67f71a8f 100644 --- a/quantum/keymap_common.c +++ b/quantum/keymap_common.c @@ -20,7 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "keycode.h" #include "action_layer.h" #include "action.h" -#include "action_macro.h" #include "debug.h" #include "quantum.h" @@ -80,24 +79,6 @@ action_t action_for_keycode(uint16_t keycode) { // Split it up action.code = ACTION_MODS_KEY(keycode >> 8, keycode & 0xFF); // adds modifier to key break; -#ifndef NO_ACTION_FUNCTION - case KC_FN0 ... KC_FN31: - action.code = keymap_function_id_to_action(FN_INDEX(keycode)); - break; - case QK_FUNCTION ... QK_FUNCTION_MAX:; - // Is a shortcut for function action_layer, pull last 12bits - // This means we have 4,096 FN macros at our disposal - action.code = keymap_function_id_to_action((int)keycode & 0xFFF); - break; -#endif -#ifndef NO_ACTION_MACRO - case QK_MACRO ... QK_MACRO_MAX: - if (keycode & 0x800) // tap macros have upper bit set - action.code = ACTION_MACRO_TAP(keycode & 0xFF); - else - action.code = ACTION_MACRO(keycode & 0xFF); - break; -#endif #ifndef NO_ACTION_LAYER case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: action.code = ACTION_LAYER_TAP_KEY((keycode >> 0x8) & 0xF, keycode & 0xFF); @@ -165,30 +146,8 @@ action_t action_for_keycode(uint16_t keycode) { return action; } -__attribute__((weak)) const uint16_t PROGMEM fn_actions[] = { - -}; - -/* Macro */ -__attribute__((weak)) const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { return MACRO_NONE; } - -/* Function */ -__attribute__((weak)) void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) {} - // translates key to keycode __attribute__((weak)) uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) { // Read entire word (16bits) return pgm_read_word(&keymaps[(layer)][(key.row)][(key.col)]); } - -// translates function id to action -__attribute__((weak)) uint16_t keymap_function_id_to_action(uint16_t function_id) { -// The compiler sees the empty (weak) fn_actions and generates a warning -// This function should not be called in that case, so the warning is too strict -// If this function is called however, the keymap should have overridden fn_actions, and then the compile -// is comparing against the wrong array -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" - return pgm_read_word(&fn_actions[function_id]); -#pragma GCC diagnostic pop -} diff --git a/quantum/led.c b/quantum/led.c index 8f0eccf55d..7b3f877208 100644 --- a/quantum/led.c +++ b/quantum/led.c @@ -13,21 +13,26 @@ * 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 "quantum.h" +#include "led.h" +#include "host.h" +#include "debug.h" +#include "gpio.h" -#ifdef BACKLIGHT_ENABLE -# include "backlight.h" +#ifdef BACKLIGHT_CAPS_LOCK +# ifdef BACKLIGHT_ENABLE +# include "backlight.h" extern backlight_config_t backlight_config; -#else -// Cannot use BACKLIGHT_CAPS_LOCK without backlight being enabled -# undef BACKLIGHT_CAPS_LOCK +# else +# pragma message "Cannot use BACKLIGHT_CAPS_LOCK without backlight being enabled" +# undef BACKLIGHT_CAPS_LOCK +# endif #endif #ifndef LED_PIN_ON_STATE # define LED_PIN_ON_STATE 1 #endif -#if defined(BACKLIGHT_CAPS_LOCK) +#ifdef BACKLIGHT_CAPS_LOCK /** \brief Caps Lock indicator using backlight (for keyboards without dedicated LED) */ static void handle_backlight_caps_lock(led_t led_state) { @@ -135,3 +140,41 @@ __attribute__((weak)) void led_set(uint8_t usb_led) { led_set_kb(usb_led); led_update_kb((led_t)usb_led); } + +/** \brief Trigger behaviour on transition to suspend + */ +void led_suspend(void) { + uint8_t leds_off = 0; +#ifdef BACKLIGHT_CAPS_LOCK + if (is_backlight_enabled()) { + // Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off + leds_off |= (1 << USB_LED_CAPS_LOCK); + } +#endif + led_set(leds_off); +} + +/** \brief Trigger behaviour on transition from suspend + */ +void led_wakeup(void) { led_set(host_keyboard_leds()); } + +/** \brief set host led state + * + * Only sets state if change detected + */ +void led_task(void) { + static uint8_t last_led_status = 0; + + // update LED + uint8_t led_status = host_keyboard_leds(); + if (last_led_status != led_status) { + last_led_status = led_status; + + if (debug_keyboard) { + debug("led_task: "); + debug_hex8(led_status); + debug("\n"); + } + led_set(led_status); + } +} diff --git a/quantum/led.h b/quantum/led.h index 0fe38ea035..934d25312c 100644 --- a/quantum/led.h +++ b/quantum/led.h @@ -49,6 +49,18 @@ void led_set(uint8_t usb_led); void led_init_ports(void); +void led_suspend(void); + +void led_wakeup(void); + +void led_task(void); + +/* Callbacks */ +void led_set_user(uint8_t usb_led); +void led_set_kb(uint8_t usb_led); +bool led_update_user(led_t led_state); +bool led_update_kb(led_t led_state); + #ifdef __cplusplus } #endif 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..1dd9655e57 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 { @@ -256,8 +252,6 @@ __attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[] void matrix_init(void) { #ifdef SPLIT_KEYBOARD - split_pre_init(); - // Set pinout for right half if pinout for that half is defined if (!isLeftHand) { # ifdef DIRECT_PINS_RIGHT @@ -296,10 +290,6 @@ void matrix_init(void) { debounce_init(ROWS_PER_HAND); matrix_init_quantum(); - -#ifdef SPLIT_KEYBOARD - split_post_init(); -#endif } #ifdef SPLIT_KEYBOARD @@ -308,35 +298,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..d67aaf508c 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,57 @@ 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 + thisHand = isLeftHand ? 0 : (ROWS_PER_HAND); + thatHand = ROWS_PER_HAND - thisHand; +#endif + matrix_init_custom(); // initialize matrix state: all keys off @@ -104,7 +151,7 @@ __attribute__((weak)) void matrix_init(void) { matrix[i] = 0; } - debounce_init(MATRIX_ROWS); + debounce_init(ROWS_PER_HAND); matrix_init_quantum(); } @@ -112,9 +159,14 @@ __attribute__((weak)) void matrix_init(void) { __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/mousekey.h b/quantum/mousekey.h index 56c91b5f1b..03da5f282a 100644 --- a/quantum/mousekey.h +++ b/quantum/mousekey.h @@ -37,9 +37,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. # ifndef MOUSEKEY_MOVE_DELTA # ifndef MK_KINETIC_SPEED -# define MOUSEKEY_MOVE_DELTA 5 +# define MOUSEKEY_MOVE_DELTA 8 # else -# define MOUSEKEY_MOVE_DELTA 25 +# define MOUSEKEY_MOVE_DELTA 5 # endif # endif # ifndef MOUSEKEY_WHEEL_DELTA @@ -47,29 +47,29 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. # endif # ifndef MOUSEKEY_DELAY # ifndef MK_KINETIC_SPEED -# define MOUSEKEY_DELAY 300 +# define MOUSEKEY_DELAY 10 # else -# define MOUSEKEY_DELAY 8 +# define MOUSEKEY_DELAY 5 # endif # endif # ifndef MOUSEKEY_INTERVAL # ifndef MK_KINETIC_SPEED -# define MOUSEKEY_INTERVAL 50 +# define MOUSEKEY_INTERVAL 20 # else -# define MOUSEKEY_INTERVAL 8 +# define MOUSEKEY_INTERVAL 10 # endif # endif # ifndef MOUSEKEY_MAX_SPEED # define MOUSEKEY_MAX_SPEED 10 # endif # ifndef MOUSEKEY_TIME_TO_MAX -# define MOUSEKEY_TIME_TO_MAX 20 +# define MOUSEKEY_TIME_TO_MAX 30 # endif # ifndef MOUSEKEY_WHEEL_DELAY -# define MOUSEKEY_WHEEL_DELAY 300 +# define MOUSEKEY_WHEEL_DELAY 10 # endif # ifndef MOUSEKEY_WHEEL_INTERVAL -# define MOUSEKEY_WHEEL_INTERVAL 100 +# define MOUSEKEY_WHEEL_INTERVAL 80 # endif # ifndef MOUSEKEY_WHEEL_MAX_SPEED # define MOUSEKEY_WHEEL_MAX_SPEED 8 diff --git a/quantum/pointing_device.c b/quantum/pointing_device.c index 2fefdb67b6..cce292e0bf 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,295 @@ __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 (POINTING_DEVICE_TASK_THROTTLE_MS > 0) + static uint32_t last_exec = 0; + if (timer_elapsed32(last_exec) < POINTING_DEVICE_TASK_THROTTLE_MS) { + return; + } + last_exec = timer_read32(); +#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..8394c20952 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,19 @@ 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_TASK_THROTTLE_MS) +# define POINTING_DEVICE_TASK_THROTTLE_MS 1 +# endif +# 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..21fd737ab7 100644 --- a/quantum/process_keycode/process_combo.c +++ b/quantum/process_keycode/process_combo.c @@ -17,6 +17,7 @@ #include "print.h" #include "process_combo.h" #include "action_tapping.h" +#include "action.h" #ifdef COMBO_COUNT __attribute__((weak)) combo_t key_combos[COMBO_COUNT]; @@ -40,10 +41,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 @@ -185,6 +194,9 @@ void clear_combos(void) { static inline void dump_key_buffer(void) { /* First call start from 0 index; recursive calls need to start from i+1 index */ static uint8_t key_buffer_next = 0; +#if TAP_CODE_DELAY > 0 + bool delay_done = false; +#endif if (key_buffer_size == 0) { return; @@ -210,6 +222,15 @@ static inline void dump_key_buffer(void) { #endif } record->event.time = 0; + clear_weak_mods(); + +#if TAP_CODE_DELAY > 0 + // only delay once and for a non-tapping key + if (!delay_done && !is_tap_record(record)) { + delay_done = true; + wait_ms(TAP_CODE_DELAY); + } +#endif } key_buffer_next = key_buffer_size = 0; @@ -350,6 +371,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 +403,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_haptic.c b/quantum/process_keycode/process_haptic.c index 85b2ffcddd..0f07f9ac75 100644 --- a/quantum/process_keycode/process_haptic.c +++ b/quantum/process_keycode/process_haptic.c @@ -35,9 +35,6 @@ __attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t case QK_MOMENTARY ... QK_MOMENTARY_MAX: case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: #endif -#ifdef NO_HAPTIC_FN - case KC_FN0 ... KC_FN31: -#endif #ifdef NO_HAPTIC_ALPHA case KC_A ... KC_Z: #endif 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..fb6220efa2 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -15,7 +15,6 @@ */ #include "quantum.h" -#include "magic.h" #ifdef BLUETOOTH_ENABLE # include "outputselect.h" @@ -47,10 +46,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 +71,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 +82,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,11 +91,13 @@ 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); -#endif + if (code == KC_CAPS_LOCK) { + wait_ms(TAP_HOLD_CAPS_DELAY); + } else if (TAP_CODE_DELAY > 0) { + wait_ms(TAP_CODE_DELAY); + } unregister_code16(code); } @@ -263,7 +260,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 +303,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 +317,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 @@ -372,101 +369,9 @@ layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_ void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3) { layer_state_set(update_tri_layer_state(layer_state, layer1, layer2, layer3)); } -void matrix_init_quantum() { - magic(); - led_init_ports(); -#ifdef BACKLIGHT_ENABLE - backlight_init_ports(); -#endif -#ifdef AUDIO_ENABLE - audio_init(); -#endif -#ifdef LED_MATRIX_ENABLE - led_matrix_init(); -#endif -#ifdef RGB_MATRIX_ENABLE - rgb_matrix_init(); -#endif -#if defined(UNICODE_ENABLE) || defined(UNICODEMAP_ENABLE) || defined(UCIS_ENABLE) - unicode_input_mode_init(); -#endif -#ifdef HAPTIC_ENABLE - haptic_init(); -#endif -#if defined(BLUETOOTH_ENABLE) && defined(OUTPUT_AUTO_ENABLE) - set_output(OUTPUT_AUTO); -#endif - - 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_init_quantum() { matrix_init_kb(); } +void matrix_scan_quantum() { matrix_scan_kb(); } //------------------------------------------------------------------------------ // Override these functions in your keymap file to play different tunes on @@ -476,17 +381,6 @@ __attribute__((weak)) void startup_user() {} __attribute__((weak)) void shutdown_user() {} -/** \brief Run keyboard level Power down - * - * FIXME: needs doc - */ -__attribute__((weak)) void suspend_power_down_user(void) {} -/** \brief Run keyboard level Power down - * - * FIXME: needs doc - */ -__attribute__((weak)) void suspend_power_down_kb(void) { suspend_power_down_user(); } - void suspend_power_down_quantum(void) { #ifndef NO_SUSPEND_POWER_DOWN // Turn off backlight @@ -502,14 +396,7 @@ void suspend_power_down_quantum(void) { # endif // Turn off LED indicators - uint8_t leds_off = 0; -# if defined(BACKLIGHT_CAPS_LOCK) && defined(BACKLIGHT_ENABLE) - if (is_backlight_enabled()) { - // Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off - leds_off |= (1 << USB_LED_CAPS_LOCK); - } -# endif - led_set(leds_off); + led_suspend(); // Turn off audio # ifdef AUDIO_ENABLE @@ -541,18 +428,6 @@ void suspend_power_down_quantum(void) { #endif } -/** \brief run user level code immediately after wakeup - * - * FIXME: needs doc - */ -__attribute__((weak)) void suspend_wakeup_init_user(void) {} - -/** \brief run keyboard level code immediately after wakeup - * - * FIXME: needs doc - */ -__attribute__((weak)) void suspend_wakeup_init_kb(void) { suspend_wakeup_init_user(); } - __attribute__((weak)) void suspend_wakeup_init_quantum(void) { // Turn on backlight #ifdef BACKLIGHT_ENABLE @@ -560,7 +435,7 @@ __attribute__((weak)) void suspend_wakeup_init_quantum(void) { #endif // Restore LED indicators - led_set(host_keyboard_leds()); + led_wakeup(); // Wake up underglow #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) diff --git a/quantum/quantum.h b/quantum/quantum.h index 6927884e2f..020e455941 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 @@ -245,11 +249,6 @@ void register_code16(uint16_t code); void unregister_code16(uint16_t code); void tap_code16(uint16_t code); -void led_set_user(uint8_t usb_led); -void led_set_kb(uint8_t usb_led); -bool led_update_user(led_t led_state); -bool led_update_kb(led_t led_state); - const char *get_numeric_str(char *buf, size_t buf_len, uint32_t curr_num, char curr_pad); const char *get_u8_str(uint8_t curr_num, char curr_pad); const char *get_u16_str(uint16_t curr_num, char curr_pad); diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index e4d0167aac..b5b3566786 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -37,10 +37,6 @@ enum quantum_keycodes { QK_RALT = 0x1400, QK_RGUI = 0x1800, QK_MODS_MAX = 0x1FFF, - QK_FUNCTION = 0x2000, - QK_FUNCTION_MAX = 0x2FFF, - QK_MACRO = 0x3000, - QK_MACRO_MAX = 0x3FFF, QK_LAYER_TAP = 0x4000, QK_LAYER_TAP_MAX = 0x4FFF, QK_TO = 0x5000, @@ -80,8 +76,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 +102,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 +375,7 @@ enum quantum_keycodes { OUT_USB, // 5CDE // Clear EEPROM - EEPROM_RESET, // 5CDF + QK_CLEAR_EEPROM, // 5CDF // Unicode UNICODE_MODE_FORWARD, // 5CE0 @@ -597,6 +593,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 }; @@ -708,15 +706,11 @@ enum quantum_keycodes { #define A(kc) LALT(kc) #define G(kc) LGUI(kc) -// Deprecated - do not use -#define F(kc) (QK_FUNCTION | (kc)) -#define M(kc) (QK_MACRO | (kc)) -#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 +743,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 +956,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_flow_anim.h b/quantum/rgb_matrix/animations/pixel_flow_anim.h new file mode 100644 index 0000000000..312c87628c --- /dev/null +++ b/quantum/rgb_matrix/animations/pixel_flow_anim.h @@ -0,0 +1,49 @@ +// Copyright 2022 @filterpaper +// SPDX-License-Identifier: GPL-2.0+ + +#ifdef ENABLE_RGB_MATRIX_PIXEL_FLOW +RGB_MATRIX_EFFECT(PIXEL_FLOW) +# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS + +static bool PIXEL_FLOW(effect_params_t* params) { + // LED state array + static RGB led[DRIVER_LED_TOTAL]; + + static uint32_t wait_timer = 0; + if (wait_timer > g_rgb_timer) { + return false; + } + + inline uint32_t interval(void) { return 3000 / scale16by8(qadd8(rgb_matrix_config.speed, 16), 16); } + + if (params->init) { + // Clear LEDs and fill the state array + rgb_matrix_set_color_all(0, 0, 0); + for (uint8_t j = 0; j < DRIVER_LED_TOTAL; ++j) { + led[j] = (random8() & 2) ? (RGB){0, 0, 0} : hsv_to_rgb((HSV){random8(), qadd8(random8() >> 1, 127), rgb_matrix_config.hsv.v}); + } + } + + RGB_MATRIX_USE_LIMITS(led_min, led_max); + // Light LEDs based on state array + for (uint8_t i = led_min; i < led_max; ++i) { + RGB_MATRIX_TEST_LED_FLAGS(); + rgb_matrix_set_color(i, led[i].r, led[i].g, led[i].b); + } + + if (!rgb_matrix_check_finished_leds(led_max)) { + // Shift LED state forward + for (uint8_t j = 0; j < led_max - 1; ++j) { + led[j] = led[j + 1]; + } + // Fill last LED + led[led_max - 1] = (random8() & 2) ? (RGB){0, 0, 0} : hsv_to_rgb((HSV){random8(), qadd8(random8() >> 1, 127), rgb_matrix_config.hsv.v}); + // Set pulse timer + wait_timer = g_rgb_timer + interval(); + } + + return rgb_matrix_check_finished_leds(led_max); +} + +# endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS +#endif // ENABLE_RGB_MATRIX_PIXEL_FLOW 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/rgb_matrix/animations/rgb_matrix_effects.inc b/quantum/rgb_matrix/animations/rgb_matrix_effects.inc index 27ce347235..ac7bac428d 100644 --- a/quantum/rgb_matrix/animations/rgb_matrix_effects.inc +++ b/quantum/rgb_matrix/animations/rgb_matrix_effects.inc @@ -27,6 +27,7 @@ #include "hue_pendulum_anim.h" #include "hue_wave_anim.h" #include "pixel_rain_anim.h" +#include "pixel_flow_anim.h" #include "pixel_fractal_anim.h" #include "typing_heatmap_anim.h" #include "digital_rain_anim.h" diff --git a/quantum/rgb_matrix/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c index 51e9554e2d..5a4556f097 100644 --- a/quantum/rgb_matrix/rgb_matrix.c +++ b/quantum/rgb_matrix/rgb_matrix.c @@ -164,6 +164,16 @@ void eeconfig_debug_rgb_matrix(void) { dprintf("rgb_matrix_config.flags = %d\n", rgb_matrix_config.flags); } +void rgb_matrix_reload_from_eeprom(void) { + rgb_matrix_disable_noeeprom(); + /* Reset back to what we have in eeprom */ + eeconfig_init_rgb_matrix(); + eeconfig_debug_rgb_matrix(); // display current eeprom values + if (rgb_matrix_config.enable) { + rgb_matrix_mode_noeeprom(rgb_matrix_config.mode); + } +} + __attribute__((weak)) uint8_t rgb_matrix_map_row_column_to_led_kb(uint8_t row, uint8_t column, uint8_t *led_i) { return 0; } uint8_t rgb_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i) { diff --git a/quantum/rgb_matrix/rgb_matrix.h b/quantum/rgb_matrix/rgb_matrix.h index a804d99abc..3cd77857be 100644 --- a/quantum/rgb_matrix/rgb_matrix.h +++ b/quantum/rgb_matrix/rgb_matrix.h @@ -136,6 +136,8 @@ void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max); void rgb_matrix_init(void); +void rgb_matrix_reload_from_eeprom(void); + void rgb_matrix_set_suspend_state(bool state); bool rgb_matrix_get_suspend_state(void); void rgb_matrix_toggle(void); @@ -182,6 +184,7 @@ void rgb_matrix_set_flags(led_flags_t flags); #ifndef RGBLIGHT_ENABLE # define eeconfig_update_rgblight_current eeconfig_update_rgb_matrix +# define rgblight_reload_from_eeprom rgb_matrix_reload_from_eeprom # define rgblight_toggle rgb_matrix_toggle # define rgblight_toggle_noeeprom rgb_matrix_toggle_noeeprom # define rgblight_enable rgb_matrix_enable 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..4b059a2b8a 100644 --- a/quantum/split_common/transactions.c +++ b/quantum/split_common/transactions.c @@ -35,11 +35,11 @@ #define sizeof_member(type, member) sizeof(((type *)NULL)->member) #define trans_initiator2target_initializer_cb(member, cb) \ - { &dummy, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb } + { sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb } #define trans_initiator2target_initializer(member) trans_initiator2target_initializer_cb(member, NULL) #define trans_target2initiator_initializer_cb(member, cb) \ - { &dummy, 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb } + { 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb } #define trans_target2initiator_initializer(member) trans_target2initiator_initializer_cb(member, NULL) #define transport_write(id, data, length) transport_execute_transaction(id, data, length, NULL, 0) @@ -579,11 +579,88 @@ 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; +# if (POINTING_DEVICE_TASK_THROTTLE_MS > 0) + 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] = { // Set defaults - [0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {NULL, 0, 0, 0, 0, 0}, + [0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {0, 0, 0, 0, 0}, #ifdef USE_I2C [I2C_EXECUTE_CALLBACK] = trans_initiator2target_initializer(transaction_id), @@ -604,6 +681,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 +707,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 +726,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/transactions.h b/quantum/split_common/transactions.h index 53610d6f8e..e38ec79ce9 100644 --- a/quantum/split_common/transactions.h +++ b/quantum/split_common/transactions.h @@ -27,7 +27,6 @@ typedef void (*slave_callback_t)(uint8_t initiator2target_buffer_size, const voi // Split transaction Descriptor typedef struct _split_transaction_desc_t { - uint8_t * status; uint8_t initiator2target_buffer_size; uint16_t initiator2target_offset; uint8_t target2initiator_buffer_size; diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c index bcc0261417..060ba8a927 100644 --- a/quantum/split_common/transport.c +++ b/quantum/split_common/transport.c @@ -99,7 +99,7 @@ bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len); } - if (soft_serial_transaction(id) != TRANSACTION_END) { + if (!soft_serial_transaction(id)) { return false; } 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); |