diff options
Diffstat (limited to 'quantum')
46 files changed, 1270 insertions, 308 deletions
| diff --git a/quantum/action.c b/quantum/action.c index 3efed443a3..ef059f0e2a 100644 --- a/quantum/action.c +++ b/quantum/action.c @@ -14,9 +14,18 @@ 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 <limits.h> + +#ifdef DEBUG_ACTION +#    include "debug.h" +#else +#    include "nodebug.h" +#endif +  #include "host.h"  #include "keycode.h"  #include "keyboard.h" +#include "keymap.h"  #include "mousekey.h"  #include "programmable_button.h"  #include "command.h" @@ -32,12 +41,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.  #    include "backlight.h"  #endif -#ifdef DEBUG_ACTION -#    include "debug.h" -#else -#    include "nodebug.h" -#endif -  #ifdef POINTING_DEVICE_ENABLE  #    include "pointing_device.h"  #endif @@ -89,6 +92,7 @@ void action_exec(keyevent_t event) {      }  #ifdef SWAP_HANDS_ENABLE +    // Swap hands handles both keys and encoders, if ENCODER_MAP_ENABLE is defined.      if (!IS_NOEVENT(event)) {          process_hand_swap(&event);      } @@ -97,7 +101,7 @@ void action_exec(keyevent_t event) {      keyrecord_t record = {.event = event};  #ifndef NO_ACTION_ONESHOT -    if (!keymap_config.oneshot_disable) { +    if (keymap_config.oneshot_enable) {  #    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))          if (has_oneshot_layer_timed_out()) {              clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); @@ -136,27 +140,65 @@ void action_exec(keyevent_t event) {  }  #ifdef SWAP_HANDS_ENABLE +extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS]; +#    ifdef ENCODER_MAP_ENABLE +extern const uint8_t PROGMEM encoder_hand_swap_config[NUM_ENCODERS]; +#    endif // ENCODER_MAP_ENABLE +  bool swap_hands = false;  bool swap_held  = false; +bool should_swap_hands(size_t index, uint8_t *swap_state, bool pressed) { +    size_t  array_index = index / (CHAR_BIT); +    size_t  bit_index   = index % (CHAR_BIT); +    uint8_t bit_val     = 1 << bit_index; +    bool    do_swap     = pressed ? swap_hands : swap_state[array_index] & bit_val; +    return do_swap; +} + +void set_swap_hands_state(size_t index, uint8_t *swap_state, bool on) { +    size_t  array_index = index / (CHAR_BIT); +    size_t  bit_index   = index % (CHAR_BIT); +    uint8_t bit_val     = 1 << bit_index; +    if (on) { +        swap_state[array_index] |= bit_val; +    } else { +        swap_state[array_index] &= ~bit_val; +    } +} +  /** \brief Process Hand Swap   *   * FIXME: Needs documentation.   */  void process_hand_swap(keyevent_t *event) { -    static swap_state_row_t swap_state[MATRIX_ROWS]; - -    keypos_t         pos     = event->key; -    swap_state_row_t col_bit = (swap_state_row_t)1 << pos.col; -    bool             do_swap = event->pressed ? swap_hands : swap_state[pos.row] & (col_bit); - -    if (do_swap) { -        event->key.row = pgm_read_byte(&hand_swap_config[pos.row][pos.col].row); -        event->key.col = pgm_read_byte(&hand_swap_config[pos.row][pos.col].col); -        swap_state[pos.row] |= col_bit; -    } else { -        swap_state[pos.row] &= ~(col_bit); +    keypos_t pos = event->key; +    if (pos.row < MATRIX_ROWS && pos.col < MATRIX_COLS) { +        static uint8_t matrix_swap_state[((MATRIX_ROWS * MATRIX_COLS) + (CHAR_BIT)-1) / (CHAR_BIT)]; +        size_t         index   = (size_t)(pos.row * MATRIX_COLS) + pos.col; +        bool           do_swap = should_swap_hands(index, matrix_swap_state, event->pressed); +        if (do_swap) { +            event->key.row = pgm_read_byte(&hand_swap_config[pos.row][pos.col].row); +            event->key.col = pgm_read_byte(&hand_swap_config[pos.row][pos.col].col); +            set_swap_hands_state(index, matrix_swap_state, true); +        } else { +            set_swap_hands_state(index, matrix_swap_state, false); +        } +    } +#    ifdef ENCODER_MAP_ENABLE +    else if (pos.row == KEYLOC_ENCODER_CW || pos.row == KEYLOC_ENCODER_CCW) { +        static uint8_t encoder_swap_state[((NUM_ENCODERS) + (CHAR_BIT)-1) / (CHAR_BIT)]; +        size_t         index   = pos.col; +        bool           do_swap = should_swap_hands(index, encoder_swap_state, event->pressed); +        if (do_swap) { +            event->key.row = pos.row; +            event->key.col = pgm_read_byte(&encoder_hand_swap_config[pos.col]); +            set_swap_hands_state(index, encoder_swap_state, true); +        } else { +            set_swap_hands_state(index, encoder_swap_state, false); +        }      } +#    endif // ENCODER_MAP_ENABLE  }  #endif @@ -216,7 +258,7 @@ void process_record(keyrecord_t *record) {      if (!process_record_quantum(record)) {  #ifndef NO_ACTION_ONESHOT -        if (is_oneshot_layer_active() && record->event.pressed && !keymap_config.oneshot_disable) { +        if (is_oneshot_layer_active() && record->event.pressed && keymap_config.oneshot_enable) {              clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);          }  #endif @@ -281,7 +323,7 @@ void process_action(keyrecord_t *record, action_t action) {  #    ifdef SWAP_HANDS_ENABLE          && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)  #    endif -        && !keymap_config.oneshot_disable) { +        && keymap_config.oneshot_enable) {          clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);          do_release_oneshot = !is_oneshot_layer_active();      } @@ -325,7 +367,7 @@ void process_action(keyrecord_t *record, action_t action) {  #    ifndef NO_ACTION_ONESHOT                  case MODS_ONESHOT:                      // Oneshot modifier -                    if (keymap_config.oneshot_disable) { +                    if (!keymap_config.oneshot_enable) {                          if (event.pressed) {                              if (mods) {                                  if (IS_MOD(action.key.code) || action.key.code == KC_NO) { @@ -571,7 +613,7 @@ void process_action(keyrecord_t *record, action_t action) {  #        ifndef NO_ACTION_ONESHOT                  case OP_ONESHOT:                      // Oneshot modifier -                    if (keymap_config.oneshot_disable) { +                    if (!keymap_config.oneshot_enable) {                          if (event.pressed) {                              layer_on(action.layer_tap.val);                          } else { diff --git a/quantum/action_layer.c b/quantum/action_layer.c index e20eedee40..473e0e948d 100644 --- a/quantum/action_layer.c +++ b/quantum/action_layer.c @@ -1,8 +1,5 @@ +#include <limits.h>  #include <stdint.h> -#include "keyboard.h" -#include "action.h" -#include "util.h" -#include "action_layer.h"  #ifdef DEBUG_ACTION  #    include "debug.h" @@ -10,6 +7,12 @@  #    include "nodebug.h"  #endif +#include "keyboard.h" +#include "keymap.h" +#include "action.h" +#include "util.h" +#include "action_layer.h" +  /** \brief Default Layer State   */  layer_state_t default_layer_state = 0; @@ -223,19 +226,20 @@ void layer_debug(void) {  /** \brief source layer cache   */ -uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS] = {{0}}; +uint8_t source_layers_cache[((MATRIX_ROWS * MATRIX_COLS) + (CHAR_BIT)-1) / (CHAR_BIT)][MAX_LAYER_BITS] = {{0}}; +#    ifdef ENCODER_MAP_ENABLE +uint8_t encoder_source_layers_cache[(NUM_ENCODERS + (CHAR_BIT)-1) / (CHAR_BIT)][MAX_LAYER_BITS] = {{0}}; +#    endif // ENCODER_MAP_ENABLE -/** \brief update source layers cache +/** \brief update source layers cache impl   * - * Updates the cached keys when changing layers + * Updates the supplied cache when changing layers   */ -void update_source_layers_cache(keypos_t key, uint8_t layer) { -    const uint8_t key_number  = key.col + (key.row * MATRIX_COLS); -    const uint8_t storage_row = key_number / 8; -    const uint8_t storage_bit = key_number % 8; - +void update_source_layers_cache_impl(uint8_t layer, uint16_t entry_number, uint8_t cache[][MAX_LAYER_BITS]) { +    const uint16_t storage_idx = entry_number / (CHAR_BIT); +    const uint8_t  storage_bit = entry_number % (CHAR_BIT);      for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { -        source_layers_cache[storage_row][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ source_layers_cache[storage_row][bit_number]) & (1U << storage_bit); +        cache[storage_idx][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ cache[storage_idx][bit_number]) & (1U << storage_bit);      }  } @@ -243,18 +247,52 @@ void update_source_layers_cache(keypos_t key, uint8_t layer) {   *   * reads the cached keys stored when the layer was changed   */ -uint8_t read_source_layers_cache(keypos_t key) { -    const uint8_t key_number  = key.col + (key.row * MATRIX_COLS); -    const uint8_t storage_row = key_number / 8; -    const uint8_t storage_bit = key_number % 8; -    uint8_t       layer       = 0; +uint8_t read_source_layers_cache_impl(uint16_t entry_number, uint8_t cache[][MAX_LAYER_BITS]) { +    const uint16_t storage_idx = entry_number / (CHAR_BIT); +    const uint8_t  storage_bit = entry_number % (CHAR_BIT); +    uint8_t        layer       = 0;      for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { -        layer |= ((source_layers_cache[storage_row][bit_number] & (1U << storage_bit)) != 0) << bit_number; +        layer |= ((cache[storage_idx][bit_number] & (1U << storage_bit)) != 0) << bit_number;      }      return layer;  } + +/** \brief update encoder source layers cache + * + * Updates the cached encoders when changing layers + */ +void update_source_layers_cache(keypos_t key, uint8_t layer) { +    if (key.row < MATRIX_ROWS && key.col < MATRIX_COLS) { +        const uint16_t entry_number = (uint16_t)(key.row * MATRIX_COLS) + key.col; +        update_source_layers_cache_impl(layer, entry_number, source_layers_cache); +    } +#    ifdef ENCODER_MAP_ENABLE +    else if (key.row == KEYLOC_ENCODER_CW || key.row == KEYLOC_ENCODER_CCW) { +        const uint16_t entry_number = key.col; +        update_source_layers_cache_impl(layer, entry_number, encoder_source_layers_cache); +    } +#    endif // ENCODER_MAP_ENABLE +} + +/** \brief read source layers cache + * + * reads the cached keys stored when the layer was changed + */ +uint8_t read_source_layers_cache(keypos_t key) { +    if (key.row < MATRIX_ROWS && key.col < MATRIX_COLS) { +        const uint16_t entry_number = (uint16_t)(key.row * MATRIX_COLS) + key.col; +        return read_source_layers_cache_impl(entry_number, source_layers_cache); +    } +#    ifdef ENCODER_MAP_ENABLE +    else if (key.row == KEYLOC_ENCODER_CW || key.row == KEYLOC_ENCODER_CCW) { +        const uint16_t entry_number = key.col; +        return read_source_layers_cache_impl(entry_number, encoder_source_layers_cache); +    } +#    endif // ENCODER_MAP_ENABLE +    return 0; +}  #endif  /** \brief Store or get action (FIXME: Needs better summary) diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c index 6f8b4f8c56..e436619428 100644 --- a/quantum/action_tapping.c +++ b/quantum/action_tapping.c @@ -1,10 +1,5 @@  #include <stdint.h>  #include <stdbool.h> -#include "action.h" -#include "action_layer.h" -#include "action_tapping.h" -#include "keycode.h" -#include "timer.h"  #ifdef DEBUG_ACTION  #    include "debug.h" @@ -12,6 +7,12 @@  #    include "nodebug.h"  #endif +#include "action.h" +#include "action_layer.h" +#include "action_tapping.h" +#include "keycode.h" +#include "timer.h" +  #ifndef NO_ACTION_TAPPING  #    define IS_TAPPING() !IS_NOEVENT(tapping_key.event) diff --git a/quantum/action_util.c b/quantum/action_util.c index 4ea0bf61fb..738410a4ac 100644 --- a/quantum/action_util.c +++ b/quantum/action_util.c @@ -155,7 +155,7 @@ void clear_oneshot_swaphands(void) {   * FIXME: needs doc   */  void set_oneshot_layer(uint8_t layer, uint8_t state) { -    if (!keymap_config.oneshot_disable) { +    if (keymap_config.oneshot_enable) {          oneshot_layer_data = layer << 3 | state;          layer_on(layer);  #    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) @@ -184,7 +184,7 @@ void reset_oneshot_layer(void) {  void clear_oneshot_layer_state(oneshot_fullfillment_t state) {      uint8_t start_state = oneshot_layer_data;      oneshot_layer_data &= ~state; -    if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) && !keymap_config.oneshot_disable) { +    if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) && keymap_config.oneshot_enable) {          layer_off(get_oneshot_layer());          reset_oneshot_layer();      } @@ -202,8 +202,8 @@ bool is_oneshot_layer_active(void) {   * FIXME: needs doc   */  void oneshot_set(bool active) { -    if (keymap_config.oneshot_disable != active) { -        keymap_config.oneshot_disable = active; +    if (keymap_config.oneshot_enable != active) { +        keymap_config.oneshot_enable = active;          eeconfig_update_keymap(keymap_config.raw);          clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);          dprintf("Oneshot: active: %d\n", active); @@ -215,7 +215,7 @@ void oneshot_set(bool active) {   * FIXME: needs doc   */  void oneshot_toggle(void) { -    oneshot_set(!keymap_config.oneshot_disable); +    oneshot_set(!keymap_config.oneshot_enable);  }  /** \brief enable oneshot @@ -235,7 +235,7 @@ void oneshot_disable(void) {  }  bool is_oneshot_enabled(void) { -    return keymap_config.oneshot_disable; +    return keymap_config.oneshot_enable;  }  #endif @@ -413,7 +413,7 @@ void del_oneshot_mods(uint8_t mods) {   * FIXME: needs doc   */  void set_oneshot_mods(uint8_t mods) { -    if (!keymap_config.oneshot_disable) { +    if (keymap_config.oneshot_enable) {          if (oneshot_mods != mods) {  #    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))              oneshot_time = timer_read(); diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index f070375ff3..fc1c55784d 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -21,6 +21,12 @@  #include "dynamic_keymap.h"  #include "via.h" // for default VIA_EEPROM_ADDR_END +#ifdef ENCODER_ENABLE +#    include "encoder.h" +#else +#    define NUM_ENCODERS 0 +#endif +  #ifndef DYNAMIC_KEYMAP_LAYER_COUNT  #    define DYNAMIC_KEYMAP_LAYER_COUNT 4  #endif @@ -58,20 +64,28 @@  #    endif  #endif -// Dynamic macro starts after dynamic keymaps -#ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR -#    define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2)) +// Dynamic encoders starts after dynamic keymaps +#ifndef DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR +#    define DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR (DYNAMIC_KEYMAP_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2))  #endif +// Dynamic macro starts after dynamic encoders, but only when using ENCODER_MAP +#ifdef ENCODER_MAP_ENABLE +#    ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR +#        define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * NUM_ENCODERS * 2 * 2)) +#    endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR +#else      // ENCODER_MAP_ENABLE +#    ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR +#        define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR) +#    endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR +#endif     // ENCODER_MAP_ENABLE +  // Sanity check that dynamic keymaps fit in available EEPROM  // If there's not 100 bytes available for macros, then something is wrong.  // The keyboard should override DYNAMIC_KEYMAP_LAYER_COUNT to reduce it,  // 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 +_Static_assert((DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) - (DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR) >= 100, "Dynamic keymaps are configured to use more EEPROM than is available.");  // Dynamic macros are stored after the keymaps and use what is available  // up to and including DYNAMIC_KEYMAP_EEPROM_MAX_ADDR. @@ -89,6 +103,7 @@ void *dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t c  }  uint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column) { +    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return KC_NO;      void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);      // Big endian, so we can read/write EEPROM directly from host if we want      uint16_t keycode = eeprom_read_byte(address) << 8; @@ -97,12 +112,36 @@ uint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column)  }  void dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode) { +    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return;      void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);      // Big endian, so we can read/write EEPROM directly from host if we want      eeprom_update_byte(address, (uint8_t)(keycode >> 8));      eeprom_update_byte(address + 1, (uint8_t)(keycode & 0xFF));  } +#ifdef ENCODER_MAP_ENABLE +void *dynamic_keymap_encoder_to_eeprom_address(uint8_t layer, uint8_t encoder_id) { +    return ((void *)DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR) + (layer * NUM_ENCODERS * 2 * 2) + (encoder_id * 2 * 2); +} + +uint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise) { +    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return KC_NO; +    void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id); +    // Big endian, so we can read/write EEPROM directly from host if we want +    uint16_t keycode = ((uint16_t)eeprom_read_byte(address + (clockwise ? 0 : 2))) << 8; +    keycode |= eeprom_read_byte(address + (clockwise ? 0 : 2) + 1); +    return keycode; +} + +void dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode) { +    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return; +    void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id); +    // Big endian, so we can read/write EEPROM directly from host if we want +    eeprom_update_byte(address + (clockwise ? 0 : 2), (uint8_t)(keycode >> 8)); +    eeprom_update_byte(address + (clockwise ? 0 : 2) + 1, (uint8_t)(keycode & 0xFF)); +} +#endif // ENCODER_MAP_ENABLE +  void dynamic_keymap_reset(void) {      // Reset the keymaps in EEPROM to what is in flash.      // All keyboards using dynamic keymaps should define a layout @@ -113,6 +152,12 @@ void dynamic_keymap_reset(void) {                  dynamic_keymap_set_keycode(layer, row, column, pgm_read_word(&keymaps[layer][row][column]));              }          } +#ifdef ENCODER_MAP_ENABLE +        for (int encoder = 0; encoder < NUM_ENCODERS; encoder++) { +            dynamic_keymap_set_encoder(layer, encoder, true, pgm_read_word(&encoder_map[layer][encoder][0])); +            dynamic_keymap_set_encoder(layer, encoder, false, pgm_read_word(&encoder_map[layer][encoder][1])); +        } +#endif // ENCODER_MAP_ENABLE      }  } @@ -148,9 +193,15 @@ void dynamic_keymap_set_buffer(uint16_t offset, uint16_t size, uint8_t *data) {  uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) {      if (layer < DYNAMIC_KEYMAP_LAYER_COUNT && key.row < MATRIX_ROWS && key.col < MATRIX_COLS) {          return dynamic_keymap_get_keycode(layer, key.row, key.col); -    } else { -        return KC_NO;      } +#ifdef ENCODER_MAP_ENABLE +    else if (layer < DYNAMIC_KEYMAP_LAYER_COUNT && key.row == KEYLOC_ENCODER_CW && key.col < NUM_ENCODERS) { +        return dynamic_keymap_get_encoder(layer, key.col, true); +    } else if (layer < DYNAMIC_KEYMAP_LAYER_COUNT && key.row == KEYLOC_ENCODER_CCW && key.col < NUM_ENCODERS) { +        return dynamic_keymap_get_encoder(layer, key.col, false); +    } +#endif // ENCODER_MAP_ENABLE +    return KC_NO;  }  uint8_t dynamic_keymap_macro_get_count(void) { diff --git a/quantum/dynamic_keymap.h b/quantum/dynamic_keymap.h index 55676172b6..459b48d07a 100644 --- a/quantum/dynamic_keymap.h +++ b/quantum/dynamic_keymap.h @@ -22,7 +22,11 @@ uint8_t  dynamic_keymap_get_layer_count(void);  void *   dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t column);  uint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column);  void     dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode); -void     dynamic_keymap_reset(void); +#ifdef ENCODER_MAP_ENABLE +uint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise); +void     dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode); +#endif // ENCODER_MAP_ENABLE +void dynamic_keymap_reset(void);  // These get/set the keycodes as stored in the EEPROM buffer  // Data is big-endian 16-bit values (the keycodes)  // Order is by layer/row/column diff --git a/quantum/eeconfig.c b/quantum/eeconfig.c index 14cd5887f4..0ff9996ca4 100644 --- a/quantum/eeconfig.c +++ b/quantum/eeconfig.c @@ -46,7 +46,7 @@ void eeconfig_init_quantum(void) {      eeprom_update_byte(EECONFIG_DEFAULT_LAYER, 0);      default_layer_state = 0;      eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, 0); -    eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, 0); +    eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, 0x4);      eeprom_update_byte(EECONFIG_MOUSEKEY_ACCEL, 0);      eeprom_update_byte(EECONFIG_BACKLIGHT, 0);      eeprom_update_byte(EECONFIG_AUDIO, 0xFF); // On by default diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h index f3cd1867ab..565a0dbe5b 100644 --- a/quantum/eeconfig.h +++ b/quantum/eeconfig.h @@ -21,7 +21,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.  #include <stdbool.h>  #ifndef EECONFIG_MAGIC_NUMBER -#    define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE9 // When changing, decrement this value to avoid future re-init issues +#    define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE8 // When changing, decrement this value to avoid future re-init issues  #endif  #define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF diff --git a/quantum/encoder.c b/quantum/encoder.c index 438c7d8564..105bed0147 100644 --- a/quantum/encoder.c +++ b/quantum/encoder.c @@ -23,6 +23,10 @@  // for memcpy  #include <string.h> +#ifndef ENCODER_MAP_KEY_DELAY +#    define ENCODER_MAP_KEY_DELAY 2 +#endif +  #if !defined(ENCODER_RESOLUTIONS) && !defined(ENCODER_RESOLUTION)  #    define ENCODER_RESOLUTION 4  #endif @@ -31,11 +35,13 @@  #    error "No encoder pads defined by ENCODERS_PAD_A and ENCODERS_PAD_B"  #endif -#define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t)) -static pin_t encoders_pad_a[] = ENCODERS_PAD_A; -static pin_t encoders_pad_b[] = ENCODERS_PAD_B; +extern volatile bool isLeftHand; + +static pin_t encoders_pad_a[NUM_ENCODERS_MAX_PER_SIDE] = ENCODERS_PAD_A; +static pin_t encoders_pad_b[NUM_ENCODERS_MAX_PER_SIDE] = ENCODERS_PAD_B; +  #ifdef ENCODER_RESOLUTIONS -static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS; +static uint8_t encoder_resolutions[NUM_ENCODERS] = ENCODER_RESOLUTIONS;  #endif  #ifndef ENCODER_DIRECTION_FLIP @@ -47,18 +53,20 @@ static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS;  #endif  static int8_t encoder_LUT[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0}; -static uint8_t encoder_state[NUMBER_OF_ENCODERS]  = {0}; -static int8_t  encoder_pulses[NUMBER_OF_ENCODERS] = {0}; +static uint8_t encoder_state[NUM_ENCODERS]  = {0}; +static int8_t  encoder_pulses[NUM_ENCODERS] = {0}; +// encoder counts +static uint8_t thisCount;  #ifdef SPLIT_KEYBOARD -// right half encoders come over as second set of encoders -static uint8_t encoder_value[NUMBER_OF_ENCODERS * 2] = {0}; -// row offsets for each hand +// encoder offsets for each hand  static uint8_t thisHand, thatHand; -#else -static uint8_t encoder_value[NUMBER_OF_ENCODERS] = {0}; +// encoder counts for each hand +static uint8_t thatCount;  #endif +static uint8_t encoder_value[NUM_ENCODERS] = {0}; +  __attribute__((weak)) void encoder_wait_pullup_charge(void) {      wait_us(100);  } @@ -72,46 +80,83 @@ __attribute__((weak)) bool encoder_update_kb(uint8_t index, bool clockwise) {  }  void encoder_init(void) { +#ifdef SPLIT_KEYBOARD +    thisHand  = isLeftHand ? 0 : NUM_ENCODERS_LEFT; +    thatHand  = NUM_ENCODERS_LEFT - thisHand; +    thisCount = isLeftHand ? NUM_ENCODERS_LEFT : NUM_ENCODERS_RIGHT; +    thatCount = isLeftHand ? NUM_ENCODERS_RIGHT : NUM_ENCODERS_LEFT; +#else // SPLIT_KEYBOARD +    thisCount                = NUM_ENCODERS; +#endif + +#ifdef ENCODER_TESTS +    // Annoying that we have to clear out values during initialisation here, but +    // because all the arrays are static locals, rerunning tests in the same +    // executable doesn't reset any of these. Kinda crappy having test-only code +    // here, but it's the simplest solution. +    memset(encoder_value, 0, sizeof(encoder_value)); +    memset(encoder_state, 0, sizeof(encoder_state)); +    memset(encoder_pulses, 0, sizeof(encoder_pulses)); +    static const pin_t encoders_pad_a_left[] = ENCODERS_PAD_A; +    static const pin_t encoders_pad_b_left[] = ENCODERS_PAD_B; +    for (uint8_t i = 0; i < thisCount; i++) { +        encoders_pad_a[i] = encoders_pad_a_left[i]; +        encoders_pad_b[i] = encoders_pad_b_left[i]; +    } +#endif +  #if defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT) +    // Re-initialise the pads if it's the right-hand side      if (!isLeftHand) { -        const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT; -        const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT; -#    if defined(ENCODER_RESOLUTIONS_RIGHT) -        const uint8_t encoder_resolutions_right[] = ENCODER_RESOLUTIONS_RIGHT; -#    endif -        for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { +        static const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT; +        static const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT; +        for (uint8_t i = 0; i < thisCount; i++) {              encoders_pad_a[i] = encoders_pad_a_right[i];              encoders_pad_b[i] = encoders_pad_b_right[i]; -#    if defined(ENCODER_RESOLUTIONS_RIGHT) -            encoder_resolutions[i] = encoder_resolutions_right[i]; -#    endif          }      } -#endif +#endif // defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT) -    for (int i = 0; i < NUMBER_OF_ENCODERS; i++) { +    // Encoder resolutions is handled purely master-side, so concatenate the two arrays +#if defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS) +#    if defined(ENCODER_RESOLUTIONS_RIGHT) +    static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS_RIGHT; +#    else  // defined(ENCODER_RESOLUTIONS_RIGHT) +    static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS; +#    endif // defined(ENCODER_RESOLUTIONS_RIGHT) +    for (uint8_t i = 0; i < NUM_ENCODERS_RIGHT; i++) { +        encoder_resolutions[NUM_ENCODERS_LEFT + i] = encoder_resolutions_right[i]; +    } +#endif // defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS) + +    for (uint8_t i = 0; i < thisCount; i++) {          setPinInputHigh(encoders_pad_a[i]);          setPinInputHigh(encoders_pad_b[i]);      }      encoder_wait_pullup_charge(); -    for (int i = 0; i < NUMBER_OF_ENCODERS; i++) { +    for (uint8_t i = 0; i < thisCount; i++) {          encoder_state[i] = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);      } +} -#ifdef SPLIT_KEYBOARD -    thisHand = isLeftHand ? 0 : NUMBER_OF_ENCODERS; -    thatHand = NUMBER_OF_ENCODERS - thisHand; -#endif +#ifdef ENCODER_MAP_ENABLE +static void encoder_exec_mapping(uint8_t index, bool clockwise) { +    // The delays below cater for Windows and its wonderful requirements. +    action_exec(clockwise ? ENCODER_CW_EVENT(index, true) : ENCODER_CCW_EVENT(index, true)); +    wait_ms(ENCODER_MAP_KEY_DELAY); +    action_exec(clockwise ? ENCODER_CW_EVENT(index, false) : ENCODER_CCW_EVENT(index, false)); +    wait_ms(ENCODER_MAP_KEY_DELAY);  } +#endif // ENCODER_MAP_ENABLE  static bool encoder_update(uint8_t index, uint8_t state) {      bool    changed = false;      uint8_t i       = index;  #ifdef ENCODER_RESOLUTIONS -    uint8_t resolution = encoder_resolutions[i]; +    const uint8_t resolution = encoder_resolutions[i];  #else -    uint8_t resolution = ENCODER_RESOLUTION; +    const uint8_t resolution = ENCODER_RESOLUTION;  #endif  #ifdef SPLIT_KEYBOARD @@ -121,12 +166,20 @@ static bool encoder_update(uint8_t index, uint8_t state) {      if (encoder_pulses[i] >= resolution) {          encoder_value[index]++;          changed = true; +#ifdef ENCODER_MAP_ENABLE +        encoder_exec_mapping(index, ENCODER_COUNTER_CLOCKWISE); +#else  // ENCODER_MAP_ENABLE          encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE); +#endif // ENCODER_MAP_ENABLE      }      if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise          encoder_value[index]--;          changed = true; +#ifdef ENCODER_MAP_ENABLE +        encoder_exec_mapping(index, ENCODER_CLOCKWISE); +#else  // ENCODER_MAP_ENABLE          encoder_update_kb(index, ENCODER_CLOCKWISE); +#endif // ENCODER_MAP_ENABLE      }      encoder_pulses[i] %= resolution;  #ifdef ENCODER_DEFAULT_POS @@ -139,10 +192,13 @@ static bool encoder_update(uint8_t index, uint8_t state) {  bool encoder_read(void) {      bool changed = false; -    for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { -        encoder_state[i] <<= 2; -        encoder_state[i] |= (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1); -        changed |= encoder_update(i, encoder_state[i]); +    for (uint8_t i = 0; i < thisCount; i++) { +        uint8_t new_status = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1); +        if ((encoder_state[i] & 0x3) != new_status) { +            encoder_state[i] <<= 2; +            encoder_state[i] |= new_status; +            changed |= encoder_update(i, encoder_state[i]); +        }      }      return changed;  } @@ -150,26 +206,34 @@ bool encoder_read(void) {  #ifdef SPLIT_KEYBOARD  void last_encoder_activity_trigger(void); -void encoder_state_raw(uint8_t* slave_state) { -    memcpy(slave_state, &encoder_value[thisHand], sizeof(uint8_t) * NUMBER_OF_ENCODERS); +void encoder_state_raw(uint8_t *slave_state) { +    memcpy(slave_state, &encoder_value[thisHand], sizeof(uint8_t) * thisCount);  } -void encoder_update_raw(uint8_t* slave_state) { +void encoder_update_raw(uint8_t *slave_state) {      bool changed = false; -    for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { -        uint8_t index = i + thatHand; -        int8_t  delta = slave_state[i] - encoder_value[index]; +    for (uint8_t i = 0; i < thatCount; i++) { // Note inverted logic -- we want the opposite side +        const uint8_t index = i + thatHand; +        int8_t        delta = slave_state[i] - encoder_value[index];          while (delta > 0) {              delta--;              encoder_value[index]++;              changed = true; +#    ifdef ENCODER_MAP_ENABLE +            encoder_exec_mapping(index, ENCODER_COUNTER_CLOCKWISE); +#    else  // ENCODER_MAP_ENABLE              encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE); +#    endif // ENCODER_MAP_ENABLE          }          while (delta < 0) {              delta++;              encoder_value[index]--;              changed = true; +#    ifdef ENCODER_MAP_ENABLE +            encoder_exec_mapping(index, ENCODER_CLOCKWISE); +#    else  // ENCODER_MAP_ENABLE              encoder_update_kb(index, ENCODER_CLOCKWISE); +#    endif // ENCODER_MAP_ENABLE          }      } diff --git a/quantum/encoder.h b/quantum/encoder.h index 25dc77721d..82f95b4931 100644 --- a/quantum/encoder.h +++ b/quantum/encoder.h @@ -18,6 +18,7 @@  #pragma once  #include "quantum.h" +#include "util.h"  void encoder_init(void);  bool encoder_read(void); @@ -26,6 +27,37 @@ bool encoder_update_kb(uint8_t index, bool clockwise);  bool encoder_update_user(uint8_t index, bool clockwise);  #ifdef SPLIT_KEYBOARD +  void encoder_state_raw(uint8_t* slave_state);  void encoder_update_raw(uint8_t* slave_state); -#endif + +#    if defined(ENCODERS_PAD_A_RIGHT) +#        define NUM_ENCODERS_LEFT (sizeof(((pin_t[])ENCODERS_PAD_A)) / sizeof(pin_t)) +#        define NUM_ENCODERS_RIGHT (sizeof(((pin_t[])ENCODERS_PAD_A_RIGHT)) / sizeof(pin_t)) +#    else +#        define NUM_ENCODERS_LEFT (sizeof(((pin_t[])ENCODERS_PAD_A)) / sizeof(pin_t)) +#        define NUM_ENCODERS_RIGHT NUM_ENCODERS_LEFT +#    endif +#    define NUM_ENCODERS (NUM_ENCODERS_LEFT + NUM_ENCODERS_RIGHT) + +#else // SPLIT_KEYBOARD + +#    define NUM_ENCODERS (sizeof(((pin_t[])ENCODERS_PAD_A)) / sizeof(pin_t)) +#    define NUM_ENCODERS_LEFT NUM_ENCODERS +#    define NUM_ENCODERS_RIGHT 0 + +#endif // SPLIT_KEYBOARD + +#ifndef NUM_ENCODERS +#    define NUM_ENCODERS 0 +#    define NUM_ENCODERS_LEFT 0 +#    define NUM_ENCODERS_RIGHT 0 +#endif // NUM_ENCODERS + +#define NUM_ENCODERS_MAX_PER_SIDE MAX(NUM_ENCODERS_LEFT, NUM_ENCODERS_RIGHT) + +#ifdef ENCODER_MAP_ENABLE +#    define ENCODER_CCW_CW(ccw, cw) \ +        { (cw), (ccw) } +extern const uint16_t encoder_map[][NUM_ENCODERS][2]; +#endif // ENCODER_MAP_ENABLE diff --git a/quantum/encoder/tests/config_mock.h b/quantum/encoder/tests/config_mock.h new file mode 100644 index 0000000000..703dcaf103 --- /dev/null +++ b/quantum/encoder/tests/config_mock.h @@ -0,0 +1,22 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#define MATRIX_ROWS 1 +#define MATRIX_COLS 1 + +/* Here, "pins" from 0 to 31 are allowed. */ +#define ENCODERS_PAD_A \ +    { 0 } +#define ENCODERS_PAD_B \ +    { 1 } + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mock.h" + +#ifdef __cplusplus +}; +#endif diff --git a/quantum/encoder/tests/config_mock_split_left_eq_right.h b/quantum/encoder/tests/config_mock_split_left_eq_right.h new file mode 100644 index 0000000000..c80ac4d519 --- /dev/null +++ b/quantum/encoder/tests/config_mock_split_left_eq_right.h @@ -0,0 +1,26 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#define MATRIX_ROWS 1 +#define MATRIX_COLS 1 + +/* Here, "pins" from 0 to 31 are allowed. */ +#define ENCODERS_PAD_A \ +    { 0, 2 } +#define ENCODERS_PAD_B \ +    { 1, 3 } +#define ENCODERS_PAD_A_RIGHT \ +    { 4, 6 } +#define ENCODERS_PAD_B_RIGHT \ +    { 5, 7 } + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mock_split.h" + +#ifdef __cplusplus +}; +#endif diff --git a/quantum/encoder/tests/config_mock_split_left_gt_right.h b/quantum/encoder/tests/config_mock_split_left_gt_right.h new file mode 100644 index 0000000000..91d5f3d605 --- /dev/null +++ b/quantum/encoder/tests/config_mock_split_left_gt_right.h @@ -0,0 +1,26 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#define MATRIX_ROWS 1 +#define MATRIX_COLS 1 + +/* Here, "pins" from 0 to 31 are allowed. */ +#define ENCODERS_PAD_A \ +    { 0, 2, 4 } +#define ENCODERS_PAD_B \ +    { 1, 3, 5 } +#define ENCODERS_PAD_A_RIGHT \ +    { 6, 8 } +#define ENCODERS_PAD_B_RIGHT \ +    { 7, 9 } + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mock_split.h" + +#ifdef __cplusplus +}; +#endif diff --git a/quantum/encoder/tests/config_mock_split_left_lt_right.h b/quantum/encoder/tests/config_mock_split_left_lt_right.h new file mode 100644 index 0000000000..4108a184a6 --- /dev/null +++ b/quantum/encoder/tests/config_mock_split_left_lt_right.h @@ -0,0 +1,26 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#define MATRIX_ROWS 1 +#define MATRIX_COLS 1 + +/* Here, "pins" from 0 to 31 are allowed. */ +#define ENCODERS_PAD_A \ +    { 0, 2 } +#define ENCODERS_PAD_B \ +    { 1, 3 } +#define ENCODERS_PAD_A_RIGHT \ +    { 4, 6, 8 } +#define ENCODERS_PAD_B_RIGHT \ +    { 5, 7, 9 } + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mock_split.h" + +#ifdef __cplusplus +}; +#endif diff --git a/quantum/encoder/tests/config_mock_split_no_left.h b/quantum/encoder/tests/config_mock_split_no_left.h new file mode 100644 index 0000000000..9db7fa7e41 --- /dev/null +++ b/quantum/encoder/tests/config_mock_split_no_left.h @@ -0,0 +1,26 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#define MATRIX_ROWS 1 +#define MATRIX_COLS 1 + +/* Here, "pins" from 0 to 31 are allowed. */ +#define ENCODERS_PAD_A \ +    {} +#define ENCODERS_PAD_B \ +    {} +#define ENCODERS_PAD_A_RIGHT \ +    { 0, 2 } +#define ENCODERS_PAD_B_RIGHT \ +    { 1, 3 } + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mock_split.h" + +#ifdef __cplusplus +}; +#endif diff --git a/quantum/encoder/tests/config_mock_split_no_right.h b/quantum/encoder/tests/config_mock_split_no_right.h new file mode 100644 index 0000000000..14f18015e6 --- /dev/null +++ b/quantum/encoder/tests/config_mock_split_no_right.h @@ -0,0 +1,26 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#define MATRIX_ROWS 1 +#define MATRIX_COLS 1 + +/* Here, "pins" from 0 to 31 are allowed. */ +#define ENCODERS_PAD_A \ +    { 0, 2 } +#define ENCODERS_PAD_B \ +    { 1, 3 } +#define ENCODERS_PAD_A_RIGHT \ +    {} +#define ENCODERS_PAD_B_RIGHT \ +    {} + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mock_split.h" + +#ifdef __cplusplus +}; +#endif diff --git a/quantum/encoder/tests/encoder_tests.cpp b/quantum/encoder/tests/encoder_tests.cpp index 1888fdab8d..b7c18aeec0 100644 --- a/quantum/encoder/tests/encoder_tests.cpp +++ b/quantum/encoder/tests/encoder_tests.cpp @@ -30,12 +30,12 @@ struct update {      bool   clockwise;  }; -uint8_t uidx = 0; +uint8_t updates_array_idx = 0;  update  updates[32];  bool encoder_update_kb(uint8_t index, bool clockwise) { -    updates[uidx % 32] = {index, clockwise}; -    uidx++; +    updates[updates_array_idx % 32] = {index, clockwise}; +    updates_array_idx++;      return true;  } @@ -47,15 +47,15 @@ bool setAndRead(pin_t pin, bool val) {  class EncoderTest : public ::testing::Test {};  TEST_F(EncoderTest, TestInit) { -    uidx = 0; +    updates_array_idx = 0;      encoder_init();      EXPECT_EQ(pinIsInputHigh[0], true);      EXPECT_EQ(pinIsInputHigh[1], true); -    EXPECT_EQ(uidx, 0); +    EXPECT_EQ(updates_array_idx, 0);  }  TEST_F(EncoderTest, TestOneClockwise) { -    uidx = 0; +    updates_array_idx = 0;      encoder_init();      // send 4 pulses. with resolution 4, that's one step and we should get 1 update.      setAndRead(0, false); @@ -63,26 +63,26 @@ TEST_F(EncoderTest, TestOneClockwise) {      setAndRead(0, true);      setAndRead(1, true); -    EXPECT_EQ(uidx, 1); +    EXPECT_EQ(updates_array_idx, 1);      EXPECT_EQ(updates[0].index, 0);      EXPECT_EQ(updates[0].clockwise, true);  }  TEST_F(EncoderTest, TestOneCounterClockwise) { -    uidx = 0; +    updates_array_idx = 0;      encoder_init();      setAndRead(1, false);      setAndRead(0, false);      setAndRead(1, true);      setAndRead(0, true); -    EXPECT_EQ(uidx, 1); +    EXPECT_EQ(updates_array_idx, 1);      EXPECT_EQ(updates[0].index, 0);      EXPECT_EQ(updates[0].clockwise, false);  }  TEST_F(EncoderTest, TestTwoClockwiseOneCC) { -    uidx = 0; +    updates_array_idx = 0;      encoder_init();      setAndRead(0, false);      setAndRead(1, false); @@ -97,7 +97,7 @@ TEST_F(EncoderTest, TestTwoClockwiseOneCC) {      setAndRead(1, true);      setAndRead(0, true); -    EXPECT_EQ(uidx, 3); +    EXPECT_EQ(updates_array_idx, 3);      EXPECT_EQ(updates[0].index, 0);      EXPECT_EQ(updates[0].clockwise, true);      EXPECT_EQ(updates[1].index, 0); @@ -107,38 +107,38 @@ TEST_F(EncoderTest, TestTwoClockwiseOneCC) {  }  TEST_F(EncoderTest, TestNoEarly) { -    uidx = 0; +    updates_array_idx = 0;      encoder_init();      // send 3 pulses. with resolution 4, that's not enough for a step.      setAndRead(0, false);      setAndRead(1, false);      setAndRead(0, true); -    EXPECT_EQ(uidx, 0); +    EXPECT_EQ(updates_array_idx, 0);      // now send last pulse      setAndRead(1, true); -    EXPECT_EQ(uidx, 1); +    EXPECT_EQ(updates_array_idx, 1);      EXPECT_EQ(updates[0].index, 0);      EXPECT_EQ(updates[0].clockwise, true);  }  TEST_F(EncoderTest, TestHalfway) { -    uidx = 0; +    updates_array_idx = 0;      encoder_init();      // go halfway      setAndRead(0, false);      setAndRead(1, false); -    EXPECT_EQ(uidx, 0); +    EXPECT_EQ(updates_array_idx, 0);      // back off      setAndRead(1, true);      setAndRead(0, true); -    EXPECT_EQ(uidx, 0); +    EXPECT_EQ(updates_array_idx, 0);      // go all the way      setAndRead(0, false);      setAndRead(1, false);      setAndRead(0, true);      setAndRead(1, true);      // should result in 1 update -    EXPECT_EQ(uidx, 1); +    EXPECT_EQ(updates_array_idx, 1);      EXPECT_EQ(updates[0].index, 0);      EXPECT_EQ(updates[0].clockwise, true);  } diff --git a/quantum/encoder/tests/encoder_tests_split_left_eq_right.cpp b/quantum/encoder/tests/encoder_tests_split_left_eq_right.cpp new file mode 100644 index 0000000000..916e47b185 --- /dev/null +++ b/quantum/encoder/tests/encoder_tests_split_left_eq_right.cpp @@ -0,0 +1,135 @@ +/* Copyright 2021 Balz Guenat + * + * 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 "gmock/gmock.h" +#include <vector> +#include <algorithm> +#include <stdio.h> + +extern "C" { +#include "encoder.h" +#include "encoder/tests/mock_split.h" +} + +struct update { +    int8_t index; +    bool   clockwise; +}; + +uint8_t updates_array_idx = 0; +update  updates[32]; + +bool isLeftHand; + +bool encoder_update_kb(uint8_t index, bool clockwise) { +    if (!isLeftHand) { +        // this method has no effect on slave half +        printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC"); +        return true; +    } +    updates[updates_array_idx % 32] = {index, clockwise}; +    updates_array_idx++; +    return true; +} + +bool setAndRead(pin_t pin, bool val) { +    setPin(pin, val); +    return encoder_read(); +} + +class EncoderSplitTestLeftEqRight : public ::testing::Test { +   protected: +    void SetUp() override { +        updates_array_idx = 0; +        for (int i = 0; i < 32; i++) { +            pinIsInputHigh[i] = 0; +            pins[i]           = 0; +        } +    } +}; + +TEST_F(EncoderSplitTestLeftEqRight, TestInitLeft) { +    isLeftHand = true; +    encoder_init(); +    EXPECT_EQ(pinIsInputHigh[0], true); +    EXPECT_EQ(pinIsInputHigh[1], true); +    EXPECT_EQ(pinIsInputHigh[2], true); +    EXPECT_EQ(pinIsInputHigh[3], true); +    EXPECT_EQ(pinIsInputHigh[4], false); +    EXPECT_EQ(pinIsInputHigh[5], false); +    EXPECT_EQ(pinIsInputHigh[6], false); +    EXPECT_EQ(pinIsInputHigh[7], false); +    EXPECT_EQ(updates_array_idx, 0); // no updates received +} + +TEST_F(EncoderSplitTestLeftEqRight, TestInitRight) { +    isLeftHand = false; +    encoder_init(); +    EXPECT_EQ(pinIsInputHigh[0], false); +    EXPECT_EQ(pinIsInputHigh[1], false); +    EXPECT_EQ(pinIsInputHigh[2], false); +    EXPECT_EQ(pinIsInputHigh[3], false); +    EXPECT_EQ(pinIsInputHigh[4], true); +    EXPECT_EQ(pinIsInputHigh[5], true); +    EXPECT_EQ(pinIsInputHigh[6], true); +    EXPECT_EQ(pinIsInputHigh[7], true); +    EXPECT_EQ(updates_array_idx, 0); // no updates received +} + +TEST_F(EncoderSplitTestLeftEqRight, TestOneClockwiseLeft) { +    isLeftHand = true; +    encoder_init(); +    // send 4 pulses. with resolution 4, that's one step and we should get 1 update. +    setAndRead(0, false); +    setAndRead(1, false); +    setAndRead(0, true); +    setAndRead(1, true); + +    EXPECT_EQ(updates_array_idx, 1); // one update received +    EXPECT_EQ(updates[0].index, 0); +    EXPECT_EQ(updates[0].clockwise, true); +} + +TEST_F(EncoderSplitTestLeftEqRight, TestOneClockwiseRightSent) { +    isLeftHand = false; +    encoder_init(); +    // send 4 pulses. with resolution 4, that's one step and we should get 1 update. +    setAndRead(6, false); +    setAndRead(7, false); +    setAndRead(6, true); +    setAndRead(7, true); + +    uint8_t slave_state[32] = {0}; +    encoder_state_raw(slave_state); + +    EXPECT_EQ(slave_state[0], 0); +    EXPECT_EQ(slave_state[1], 0xFF); +} + +TEST_F(EncoderSplitTestLeftEqRight, TestMultipleEncodersRightReceived) { +    isLeftHand = true; +    encoder_init(); + +    uint8_t slave_state[32] = {1, 0xFF}; // First right encoder is CCW, Second right encoder CW +    encoder_update_raw(slave_state); + +    EXPECT_EQ(updates_array_idx, 2); // two updates received, one for each changed item on the right side +    EXPECT_EQ(updates[0].index, 2); +    EXPECT_EQ(updates[0].clockwise, false); +    EXPECT_EQ(updates[1].index, 3); +    EXPECT_EQ(updates[1].clockwise, true); +} diff --git a/quantum/encoder/tests/encoder_tests_split_left_gt_right.cpp b/quantum/encoder/tests/encoder_tests_split_left_gt_right.cpp new file mode 100644 index 0000000000..7b64bb2981 --- /dev/null +++ b/quantum/encoder/tests/encoder_tests_split_left_gt_right.cpp @@ -0,0 +1,139 @@ +/* Copyright 2021 Balz Guenat + * + * 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 "gmock/gmock.h" +#include <vector> +#include <algorithm> +#include <stdio.h> + +extern "C" { +#include "encoder.h" +#include "encoder/tests/mock_split.h" +} + +struct update { +    int8_t index; +    bool   clockwise; +}; + +uint8_t updates_array_idx = 0; +update  updates[32]; + +bool isLeftHand; + +bool encoder_update_kb(uint8_t index, bool clockwise) { +    if (!isLeftHand) { +        // this method has no effect on slave half +        printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC"); +        return true; +    } +    updates[updates_array_idx % 32] = {index, clockwise}; +    updates_array_idx++; +    return true; +} + +bool setAndRead(pin_t pin, bool val) { +    setPin(pin, val); +    return encoder_read(); +} + +class EncoderSplitTestLeftGreaterThanRight : public ::testing::Test { +   protected: +    void SetUp() override { +        updates_array_idx = 0; +        for (int i = 0; i < 32; i++) { +            pinIsInputHigh[i] = 0; +            pins[i]           = 0; +        } +    } +}; + +TEST_F(EncoderSplitTestLeftGreaterThanRight, TestInitLeft) { +    isLeftHand = true; +    encoder_init(); +    EXPECT_EQ(pinIsInputHigh[0], true); +    EXPECT_EQ(pinIsInputHigh[1], true); +    EXPECT_EQ(pinIsInputHigh[2], true); +    EXPECT_EQ(pinIsInputHigh[3], true); +    EXPECT_EQ(pinIsInputHigh[4], true); +    EXPECT_EQ(pinIsInputHigh[5], true); +    EXPECT_EQ(pinIsInputHigh[6], false); +    EXPECT_EQ(pinIsInputHigh[7], false); +    EXPECT_EQ(pinIsInputHigh[8], false); +    EXPECT_EQ(pinIsInputHigh[9], false); +    EXPECT_EQ(updates_array_idx, 0); // no updates received +} + +TEST_F(EncoderSplitTestLeftGreaterThanRight, TestInitRight) { +    isLeftHand = false; +    encoder_init(); +    EXPECT_EQ(pinIsInputHigh[0], false); +    EXPECT_EQ(pinIsInputHigh[1], false); +    EXPECT_EQ(pinIsInputHigh[2], false); +    EXPECT_EQ(pinIsInputHigh[3], false); +    EXPECT_EQ(pinIsInputHigh[4], false); +    EXPECT_EQ(pinIsInputHigh[5], false); +    EXPECT_EQ(pinIsInputHigh[6], true); +    EXPECT_EQ(pinIsInputHigh[7], true); +    EXPECT_EQ(pinIsInputHigh[8], true); +    EXPECT_EQ(pinIsInputHigh[9], true); +    EXPECT_EQ(updates_array_idx, 0); // no updates received +} + +TEST_F(EncoderSplitTestLeftGreaterThanRight, TestOneClockwiseLeft) { +    isLeftHand = true; +    encoder_init(); +    // send 4 pulses. with resolution 4, that's one step and we should get 1 update. +    setAndRead(0, false); +    setAndRead(1, false); +    setAndRead(0, true); +    setAndRead(1, true); + +    EXPECT_EQ(updates_array_idx, 1); // one update received +    EXPECT_EQ(updates[0].index, 0); +    EXPECT_EQ(updates[0].clockwise, true); +} + +TEST_F(EncoderSplitTestLeftGreaterThanRight, TestOneClockwiseRightSent) { +    isLeftHand = false; +    encoder_init(); +    // send 4 pulses. with resolution 4, that's one step and we should get 1 update. +    setAndRead(6, false); +    setAndRead(7, false); +    setAndRead(6, true); +    setAndRead(7, true); + +    uint8_t slave_state[32] = {0}; +    encoder_state_raw(slave_state); + +    EXPECT_EQ(slave_state[0], 0xFF); +    EXPECT_EQ(slave_state[1], 0); +} + +TEST_F(EncoderSplitTestLeftGreaterThanRight, TestMultipleEncodersRightReceived) { +    isLeftHand = true; +    encoder_init(); + +    uint8_t slave_state[32] = {1, 0xFF}; // First right encoder is CCW, Second right encoder no change, third right encoder CW +    encoder_update_raw(slave_state); + +    EXPECT_EQ(updates_array_idx, 2); // two updates received, one for each changed item on the right side +    EXPECT_EQ(updates[0].index, 3); +    EXPECT_EQ(updates[0].clockwise, false); +    EXPECT_EQ(updates[1].index, 4); +    EXPECT_EQ(updates[1].clockwise, true); +} diff --git a/quantum/encoder/tests/encoder_tests_split_left_lt_right.cpp b/quantum/encoder/tests/encoder_tests_split_left_lt_right.cpp new file mode 100644 index 0000000000..a6519c5762 --- /dev/null +++ b/quantum/encoder/tests/encoder_tests_split_left_lt_right.cpp @@ -0,0 +1,139 @@ +/* Copyright 2021 Balz Guenat + * + * 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 "gmock/gmock.h" +#include <vector> +#include <algorithm> +#include <stdio.h> + +extern "C" { +#include "encoder.h" +#include "encoder/tests/mock_split.h" +} + +struct update { +    int8_t index; +    bool   clockwise; +}; + +uint8_t updates_array_idx = 0; +update  updates[32]; + +bool isLeftHand; + +bool encoder_update_kb(uint8_t index, bool clockwise) { +    if (!isLeftHand) { +        // this method has no effect on slave half +        printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC"); +        return true; +    } +    updates[updates_array_idx % 32] = {index, clockwise}; +    updates_array_idx++; +    return true; +} + +bool setAndRead(pin_t pin, bool val) { +    setPin(pin, val); +    return encoder_read(); +} + +class EncoderSplitTestLeftLessThanRight : public ::testing::Test { +   protected: +    void SetUp() override { +        updates_array_idx = 0; +        for (int i = 0; i < 32; i++) { +            pinIsInputHigh[i] = 0; +            pins[i]           = 0; +        } +    } +}; + +TEST_F(EncoderSplitTestLeftLessThanRight, TestInitLeft) { +    isLeftHand = true; +    encoder_init(); +    EXPECT_EQ(pinIsInputHigh[0], true); +    EXPECT_EQ(pinIsInputHigh[1], true); +    EXPECT_EQ(pinIsInputHigh[2], true); +    EXPECT_EQ(pinIsInputHigh[3], true); +    EXPECT_EQ(pinIsInputHigh[4], false); +    EXPECT_EQ(pinIsInputHigh[5], false); +    EXPECT_EQ(pinIsInputHigh[6], false); +    EXPECT_EQ(pinIsInputHigh[7], false); +    EXPECT_EQ(pinIsInputHigh[8], false); +    EXPECT_EQ(pinIsInputHigh[9], false); +    EXPECT_EQ(updates_array_idx, 0); // no updates received +} + +TEST_F(EncoderSplitTestLeftLessThanRight, TestInitRight) { +    isLeftHand = false; +    encoder_init(); +    EXPECT_EQ(pinIsInputHigh[0], false); +    EXPECT_EQ(pinIsInputHigh[1], false); +    EXPECT_EQ(pinIsInputHigh[2], false); +    EXPECT_EQ(pinIsInputHigh[3], false); +    EXPECT_EQ(pinIsInputHigh[4], true); +    EXPECT_EQ(pinIsInputHigh[5], true); +    EXPECT_EQ(pinIsInputHigh[6], true); +    EXPECT_EQ(pinIsInputHigh[7], true); +    EXPECT_EQ(pinIsInputHigh[8], true); +    EXPECT_EQ(pinIsInputHigh[9], true); +    EXPECT_EQ(updates_array_idx, 0); // no updates received +} + +TEST_F(EncoderSplitTestLeftLessThanRight, TestOneClockwiseLeft) { +    isLeftHand = true; +    encoder_init(); +    // send 4 pulses. with resolution 4, that's one step and we should get 1 update. +    setAndRead(0, false); +    setAndRead(1, false); +    setAndRead(0, true); +    setAndRead(1, true); + +    EXPECT_EQ(updates_array_idx, 1); // one update received +    EXPECT_EQ(updates[0].index, 0); +    EXPECT_EQ(updates[0].clockwise, true); +} + +TEST_F(EncoderSplitTestLeftLessThanRight, TestOneClockwiseRightSent) { +    isLeftHand = false; +    encoder_init(); +    // send 4 pulses. with resolution 4, that's one step and we should get 1 update. +    setAndRead(6, false); +    setAndRead(7, false); +    setAndRead(6, true); +    setAndRead(7, true); + +    uint8_t slave_state[32] = {0}; +    encoder_state_raw(slave_state); + +    EXPECT_EQ(slave_state[0], 0); +    EXPECT_EQ(slave_state[1], 0xFF); +} + +TEST_F(EncoderSplitTestLeftLessThanRight, TestMultipleEncodersRightReceived) { +    isLeftHand = true; +    encoder_init(); + +    uint8_t slave_state[32] = {1, 0, 0xFF}; // First right encoder is CCW, Second right encoder no change, third right encoder CW +    encoder_update_raw(slave_state); + +    EXPECT_EQ(updates_array_idx, 2); // two updates received, one for each changed item on the right side +    EXPECT_EQ(updates[0].index, 2); +    EXPECT_EQ(updates[0].clockwise, false); +    EXPECT_EQ(updates[1].index, 4); +    EXPECT_EQ(updates[1].clockwise, true); +} diff --git a/quantum/encoder/tests/encoder_tests_split.cpp b/quantum/encoder/tests/encoder_tests_split_no_left.cpp index 25e52c83f9..b6b2d7e2d1 100644 --- a/quantum/encoder/tests/encoder_tests_split.cpp +++ b/quantum/encoder/tests/encoder_tests_split_no_left.cpp @@ -30,7 +30,7 @@ struct update {      bool   clockwise;  }; -uint8_t uidx = 0; +uint8_t updates_array_idx = 0;  update  updates[32];  bool isLeftHand; @@ -41,8 +41,8 @@ bool encoder_update_kb(uint8_t index, bool clockwise) {          printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC");          return true;      } -    updates[uidx % 32] = {index, clockwise}; -    uidx++; +    updates[updates_array_idx % 32] = {index, clockwise}; +    updates_array_idx++;      return true;  } @@ -51,10 +51,10 @@ bool setAndRead(pin_t pin, bool val) {      return encoder_read();  } -class EncoderTest : public ::testing::Test { +class EncoderSplitTestNoLeft : public ::testing::Test {     protected:      void SetUp() override { -        uidx = 0; +        updates_array_idx = 0;          for (int i = 0; i < 32; i++) {              pinIsInputHigh[i] = 0;              pins[i]           = 0; @@ -62,27 +62,27 @@ class EncoderTest : public ::testing::Test {      }  }; -TEST_F(EncoderTest, TestInitLeft) { +TEST_F(EncoderSplitTestNoLeft, TestInitLeft) {      isLeftHand = true;      encoder_init(); -    EXPECT_EQ(pinIsInputHigh[0], true); -    EXPECT_EQ(pinIsInputHigh[1], true); +    EXPECT_EQ(pinIsInputHigh[0], false); +    EXPECT_EQ(pinIsInputHigh[1], false);      EXPECT_EQ(pinIsInputHigh[2], false);      EXPECT_EQ(pinIsInputHigh[3], false); -    EXPECT_EQ(uidx, 0); +    EXPECT_EQ(updates_array_idx, 0); // no updates received  } -TEST_F(EncoderTest, TestInitRight) { +TEST_F(EncoderSplitTestNoLeft, TestInitRight) {      isLeftHand = false;      encoder_init(); -    EXPECT_EQ(pinIsInputHigh[0], false); -    EXPECT_EQ(pinIsInputHigh[1], false); +    EXPECT_EQ(pinIsInputHigh[0], true); +    EXPECT_EQ(pinIsInputHigh[1], true);      EXPECT_EQ(pinIsInputHigh[2], true);      EXPECT_EQ(pinIsInputHigh[3], true); -    EXPECT_EQ(uidx, 0); +    EXPECT_EQ(updates_array_idx, 0); // no updates received  } -TEST_F(EncoderTest, TestOneClockwiseLeft) { +TEST_F(EncoderSplitTestNoLeft, TestOneClockwiseLeft) {      isLeftHand = true;      encoder_init();      // send 4 pulses. with resolution 4, that's one step and we should get 1 update. @@ -91,12 +91,10 @@ TEST_F(EncoderTest, TestOneClockwiseLeft) {      setAndRead(0, true);      setAndRead(1, true); -    EXPECT_EQ(uidx, 1); -    EXPECT_EQ(updates[0].index, 0); -    EXPECT_EQ(updates[0].clockwise, true); +    EXPECT_EQ(updates_array_idx, 0); // no updates received  } -TEST_F(EncoderTest, TestOneClockwiseRightSent) { +TEST_F(EncoderSplitTestNoLeft, TestOneClockwiseRightSent) {      isLeftHand = false;      encoder_init();      // send 4 pulses. with resolution 4, that's one step and we should get 1 update. @@ -105,39 +103,23 @@ TEST_F(EncoderTest, TestOneClockwiseRightSent) {      setAndRead(2, true);      setAndRead(3, true); -    uint8_t slave_state[2] = {0}; +    uint8_t slave_state[32] = {0};      encoder_state_raw(slave_state); -    EXPECT_EQ((int8_t)slave_state[0], -1); +    EXPECT_EQ(slave_state[0], 0); +    EXPECT_EQ(slave_state[1], 0xFF);  } -/* this test will not work after the previous test. - * this is due to encoder_value[1] already being set to -1 when simulating the right half. - * When we now receive this update acting as the left half, there is no change. - * This is hard to mock, as the static values inside encoder.c normally exist twice, once on each half, - * but here, they only exist once. - */ - -// TEST_F(EncoderTest, TestOneClockwiseRightReceived) { -//     isLeftHand = true; -//     encoder_init(); - -//     uint8_t slave_state[2] = {255, 0}; -//     encoder_update_raw(slave_state); - -//     EXPECT_EQ(uidx, 1); -//     EXPECT_EQ(updates[0].index, 1); -//     EXPECT_EQ(updates[0].clockwise, true); -// } - -TEST_F(EncoderTest, TestOneCounterClockwiseRightReceived) { +TEST_F(EncoderSplitTestNoLeft, TestMultipleEncodersRightReceived) {      isLeftHand = true;      encoder_init(); -    uint8_t slave_state[2] = {0, 0}; +    uint8_t slave_state[32] = {1, 0xFF}; // First right encoder is CCW, Second right encoder no change, third right encoder CW      encoder_update_raw(slave_state); -    EXPECT_EQ(uidx, 1); -    EXPECT_EQ(updates[0].index, 1); +    EXPECT_EQ(updates_array_idx, 2); // two updates received, one for each changed item on the right side +    EXPECT_EQ(updates[0].index, 0);      EXPECT_EQ(updates[0].clockwise, false); +    EXPECT_EQ(updates[1].index, 1); +    EXPECT_EQ(updates[1].clockwise, true);  } diff --git a/quantum/encoder/tests/encoder_tests_split_no_right.cpp b/quantum/encoder/tests/encoder_tests_split_no_right.cpp new file mode 100644 index 0000000000..fa0a7c18a8 --- /dev/null +++ b/quantum/encoder/tests/encoder_tests_split_no_right.cpp @@ -0,0 +1,118 @@ +/* Copyright 2021 Balz Guenat + * + * 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 "gmock/gmock.h" +#include <vector> +#include <algorithm> +#include <stdio.h> + +extern "C" { +#include "encoder.h" +#include "encoder/tests/mock_split.h" +} + +struct update { +    int8_t index; +    bool   clockwise; +}; + +uint8_t updates_array_idx = 0; +update  updates[32]; + +bool isLeftHand; + +bool encoder_update_kb(uint8_t index, bool clockwise) { +    if (!isLeftHand) { +        // this method has no effect on slave half +        printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC"); +        return true; +    } +    updates[updates_array_idx % 32] = {index, clockwise}; +    updates_array_idx++; +    return true; +} + +bool setAndRead(pin_t pin, bool val) { +    setPin(pin, val); +    return encoder_read(); +} + +class EncoderSplitTestNoRight : public ::testing::Test { +   protected: +    void SetUp() override { +        updates_array_idx = 0; +        for (int i = 0; i < 32; i++) { +            pinIsInputHigh[i] = 0; +            pins[i]           = 0; +        } +    } +}; + +TEST_F(EncoderSplitTestNoRight, TestInitLeft) { +    isLeftHand = true; +    encoder_init(); +    EXPECT_EQ(pinIsInputHigh[0], true); +    EXPECT_EQ(pinIsInputHigh[1], true); +    EXPECT_EQ(pinIsInputHigh[2], true); +    EXPECT_EQ(pinIsInputHigh[3], true); +    EXPECT_EQ(updates_array_idx, 0); // no updates received +} + +TEST_F(EncoderSplitTestNoRight, TestInitRight) { +    isLeftHand = false; +    encoder_init(); +    EXPECT_EQ(pinIsInputHigh[0], false); +    EXPECT_EQ(pinIsInputHigh[1], false); +    EXPECT_EQ(pinIsInputHigh[2], false); +    EXPECT_EQ(pinIsInputHigh[3], false); +    EXPECT_EQ(updates_array_idx, 0); // no updates received +} + +TEST_F(EncoderSplitTestNoRight, TestOneClockwiseLeft) { +    isLeftHand = true; +    encoder_init(); +    // send 4 pulses. with resolution 4, that's one step and we should get 1 update. +    setAndRead(0, false); +    setAndRead(1, false); +    setAndRead(0, true); +    setAndRead(1, true); + +    EXPECT_EQ(updates_array_idx, 1); // one updates received +    EXPECT_EQ(updates[0].index, 0); +    EXPECT_EQ(updates[0].clockwise, true); +} + +TEST_F(EncoderSplitTestNoRight, TestOneClockwiseRightSent) { +    isLeftHand = false; +    encoder_init(); + +    uint8_t slave_state[32] = {0xAA, 0xAA}; +    encoder_state_raw(slave_state); + +    EXPECT_EQ(slave_state[0], 0xAA); +    EXPECT_EQ(slave_state[1], 0xAA); +} + +TEST_F(EncoderSplitTestNoRight, TestMultipleEncodersRightReceived) { +    isLeftHand = true; +    encoder_init(); + +    uint8_t slave_state[32] = {1, 0xFF}; // These values would trigger updates if there were encoders on the other side +    encoder_update_raw(slave_state); + +    EXPECT_EQ(updates_array_idx, 0); // no updates received -- no right-hand encoders +} diff --git a/quantum/encoder/tests/mock.h b/quantum/encoder/tests/mock.h index dbc25a0846..80c336b5ef 100644 --- a/quantum/encoder/tests/mock.h +++ b/quantum/encoder/tests/mock.h @@ -19,12 +19,6 @@  #include <stdint.h>  #include <stdbool.h> -/* Here, "pins" from 0 to 31 are allowed. */ -#define ENCODERS_PAD_A \ -    { 0 } -#define ENCODERS_PAD_B \ -    { 1 } -  typedef uint8_t pin_t;  extern bool pins[]; diff --git a/quantum/encoder/tests/mock_split.h b/quantum/encoder/tests/mock_split.h index 0ae62652f9..2fc12f1830 100644 --- a/quantum/encoder/tests/mock_split.h +++ b/quantum/encoder/tests/mock_split.h @@ -20,20 +20,10 @@  #include <stdbool.h>  #define SPLIT_KEYBOARD -/* Here, "pins" from 0 to 31 are allowed. */ -#define ENCODERS_PAD_A \ -    { 0 } -#define ENCODERS_PAD_B \ -    { 1 } -#define ENCODERS_PAD_A_RIGHT \ -    { 2 } -#define ENCODERS_PAD_B_RIGHT \ -    { 3 } -  typedef uint8_t pin_t; -extern bool     isLeftHand; -void            encoder_state_raw(uint8_t* slave_state); -void            encoder_update_raw(uint8_t* slave_state); + +void encoder_state_raw(uint8_t* slave_state); +void encoder_update_raw(uint8_t* slave_state);  extern bool pins[];  extern bool pinIsInputHigh[]; diff --git a/quantum/encoder/tests/rules.mk b/quantum/encoder/tests/rules.mk index b826ce3aed..6a2611952c 100644 --- a/quantum/encoder/tests/rules.mk +++ b/quantum/encoder/tests/rules.mk @@ -1,13 +1,58 @@ -encoder_DEFS := -DENCODER_MOCK_SINGLE +encoder_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SINGLE +encoder_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock.h  encoder_SRC := \ +	platforms/test/timer.c \  	$(QUANTUM_PATH)/encoder/tests/mock.c \  	$(QUANTUM_PATH)/encoder/tests/encoder_tests.cpp \  	$(QUANTUM_PATH)/encoder.c -encoder_split_DEFS := -DENCODER_MOCK_SPLIT +encoder_split_left_eq_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT +encoder_split_left_eq_right_INC := $(QUANTUM_PATH)/split_common +encoder_split_left_eq_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_left_eq_right.h -encoder_split_SRC := \ +encoder_split_left_eq_right_SRC := \ +	platforms/test/timer.c \  	$(QUANTUM_PATH)/encoder/tests/mock_split.c \ -	$(QUANTUM_PATH)/encoder/tests/encoder_tests_split.cpp \ +	$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_left_eq_right.cpp \ +	$(QUANTUM_PATH)/encoder.c + +encoder_split_left_gt_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT +encoder_split_left_gt_right_INC := $(QUANTUM_PATH)/split_common +encoder_split_left_gt_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_left_gt_right.h + +encoder_split_left_gt_right_SRC := \ +	platforms/test/timer.c \ +	$(QUANTUM_PATH)/encoder/tests/mock_split.c \ +	$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_left_gt_right.cpp \ +	$(QUANTUM_PATH)/encoder.c + +encoder_split_left_lt_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT +encoder_split_left_lt_right_INC := $(QUANTUM_PATH)/split_common +encoder_split_left_lt_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_left_lt_right.h + +encoder_split_left_lt_right_SRC := \ +	platforms/test/timer.c \ +	$(QUANTUM_PATH)/encoder/tests/mock_split.c \ +	$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_left_lt_right.cpp \ +	$(QUANTUM_PATH)/encoder.c + +encoder_split_no_left_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT +encoder_split_no_left_INC := $(QUANTUM_PATH)/split_common +encoder_split_no_left_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_no_left.h + +encoder_split_no_left_SRC := \ +	platforms/test/timer.c \ +	$(QUANTUM_PATH)/encoder/tests/mock_split.c \ +	$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_no_left.cpp \ +	$(QUANTUM_PATH)/encoder.c + +encoder_split_no_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT +encoder_split_no_right_INC := $(QUANTUM_PATH)/split_common +encoder_split_no_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_no_right.h + +encoder_split_no_right_SRC := \ +	platforms/test/timer.c \ +	$(QUANTUM_PATH)/encoder/tests/mock_split.c \ +	$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_no_right.cpp \  	$(QUANTUM_PATH)/encoder.c diff --git a/quantum/encoder/tests/testlist.mk b/quantum/encoder/tests/testlist.mk index 1be9f4a054..6b2fd84d96 100644 --- a/quantum/encoder/tests/testlist.mk +++ b/quantum/encoder/tests/testlist.mk @@ -1,3 +1,7 @@  TEST_LIST += \  	encoder \ -	encoder_split +	encoder_split_left_eq_right \ +	encoder_split_left_gt_right \ +	encoder_split_left_lt_right \ +	encoder_split_no_left \ +	encoder_split_no_right diff --git a/quantum/joystick.c b/quantum/joystick.c index 7b87201aef..86b2c64036 100644 --- a/quantum/joystick.c +++ b/quantum/joystick.c @@ -1,13 +1,38 @@  #include "joystick.h" -joystick_t joystick_status = {.buttons = {0}, -                              .axes = -                                  { +// clang-format off +joystick_t joystick_status = { +    .buttons = {0}, +    .axes = {  #if JOYSTICK_AXES_COUNT > 0 -                                      0 +        0  #endif -                                  }, -                              .status = 0}; +    }, +    .status = 0 +}; +// clang-format on  // array defining the reading of analog values for each axis  __attribute__((weak)) joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = {}; + +// to be implemented in the hid protocol library +void send_joystick_packet(joystick_t *joystick); + +void joystick_flush(void) { +    if ((joystick_status.status & JS_UPDATED) > 0) { +        send_joystick_packet(&joystick_status); +        joystick_status.status &= ~JS_UPDATED; +    } +} + +void register_joystick_button(uint8_t button) { +    joystick_status.buttons[button / 8] |= 1 << (button % 8); +    joystick_status.status |= JS_UPDATED; +    joystick_flush(); +} + +void unregister_joystick_button(uint8_t button) { +    joystick_status.buttons[button / 8] &= ~(1 << (button % 8)); +    joystick_status.status |= JS_UPDATED; +    joystick_flush(); +} diff --git a/quantum/joystick.h b/quantum/joystick.h index 9156491aca..002df3a6d9 100644 --- a/quantum/joystick.h +++ b/quantum/joystick.h @@ -1,8 +1,7 @@  #pragma once -#include "quantum.h" -  #include <stdint.h> +#include "gpio.h"  #ifndef JOYSTICK_BUTTON_COUNT  #    define JOYSTICK_BUTTON_COUNT 8 @@ -58,5 +57,7 @@ typedef struct {  extern joystick_t joystick_status; -// to be implemented in the hid protocol library -void send_joystick_packet(joystick_t *joystick); +void joystick_flush(void); + +void register_joystick_button(uint8_t button); +void unregister_joystick_button(uint8_t button); diff --git a/quantum/keyboard.c b/quantum/keyboard.c index ce4f06ae69..fc8a2fe8e3 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c @@ -489,7 +489,7 @@ bool matrix_scan_task(void) {      // we can get here with some keys processed now.      if (!keys_processed)  #endif -        action_exec(TICK); +        action_exec(TICK_EVENT);  MATRIX_LOOP_END: diff --git a/quantum/keyboard.h b/quantum/keyboard.h index e122b38264..fe0736a515 100644 --- a/quantum/keyboard.h +++ b/quantum/keyboard.h @@ -40,25 +40,47 @@ typedef struct {  /* equivalent test of keypos_t */  #define KEYEQ(keya, keyb) ((keya).row == (keyb).row && (keya).col == (keyb).col) +/* special keypos_t entries */ +#define KEYLOC_TICK 255 +#define KEYLOC_COMBO 254 +#define KEYLOC_ENCODER_CW 253 +#define KEYLOC_ENCODER_CCW 252 +  /* Rules for No Event:   * 1) (time == 0) to handle (keyevent_t){} as empty event   * 2) Matrix(255, 255) to make TICK event available   */  static inline bool IS_NOEVENT(keyevent_t event) { -    return event.time == 0 || (event.key.row == 255 && event.key.col == 255); +    return event.time == 0 || (event.key.row == KEYLOC_TICK && event.key.col == KEYLOC_TICK); +} +static inline bool IS_KEYEVENT(keyevent_t event) { +    return event.key.row < MATRIX_ROWS && event.key.col < MATRIX_COLS; +} +static inline bool IS_COMBOEVENT(keyevent_t event) { +    return event.key.row == KEYLOC_COMBO; +} +static inline bool IS_ENCODEREVENT(keyevent_t event) { +    return event.key.row == KEYLOC_ENCODER_CW || event.key.row == KEYLOC_ENCODER_CCW;  }  static inline bool IS_PRESSED(keyevent_t event) { -    return (!IS_NOEVENT(event) && event.pressed); +    return !IS_NOEVENT(event) && event.pressed;  }  static inline bool IS_RELEASED(keyevent_t event) { -    return (!IS_NOEVENT(event) && !event.pressed); +    return !IS_NOEVENT(event) && !event.pressed;  } +/* Common keyevent object factory */ +#define MAKE_KEYPOS(row_num, col_num) ((keypos_t){.row = (row_num), .col = (col_num)}) +#define MAKE_KEYEVENT(row_num, col_num, press) ((keyevent_t){.key = MAKE_KEYPOS((row_num), (col_num)), .pressed = (press), .time = (timer_read() | 1)}) +  /* Tick event */ -#define TICK                                                                                    \ -    (keyevent_t) {                                                                              \ -        .key = (keypos_t){.row = 255, .col = 255}, .pressed = false, .time = (timer_read() | 1) \ -    } +#define TICK_EVENT MAKE_KEYEVENT(KEYLOC_TICK, KEYLOC_TICK, false) + +#ifdef ENCODER_MAP_ENABLE +/* Encoder events */ +#    define ENCODER_CW_EVENT(enc_id, press) MAKE_KEYEVENT(KEYLOC_ENCODER_CW, (enc_id), (press)) +#    define ENCODER_CCW_EVENT(enc_id, press) MAKE_KEYEVENT(KEYLOC_ENCODER_CCW, (enc_id), (press)) +#endif // ENCODER_MAP_ENABLE  /* it runs once at early stage of startup before keyboard_init. */  void keyboard_setup(void); diff --git a/quantum/keycode_config.h b/quantum/keycode_config.h index d7e334fdc8..a2cb025ed2 100644 --- a/quantum/keycode_config.h +++ b/quantum/keycode_config.h @@ -37,7 +37,7 @@ typedef union {          bool nkro : 1;          bool swap_lctl_lgui : 1;          bool swap_rctl_rgui : 1; -        bool oneshot_disable : 1; +        bool oneshot_enable : 1;      };  } keymap_config_t; diff --git a/quantum/keymap.h b/quantum/keymap.h index 2ee2e1b576..d64b271efb 100644 --- a/quantum/keymap.h +++ b/quantum/keymap.h @@ -32,6 +32,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.  // #include "print.h"  #include "debug.h"  #include "keycode_config.h" +#include "gpio.h" // for pin_t  // ChibiOS uses RESET in its FlagStatus enumeration  // Therefore define it as QK_BOOTLOADER here, to avoid name collision @@ -49,3 +50,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.  uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key);  extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; + +#ifdef ENCODER_MAP_ENABLE +// Ensure we have a forward declaration for the encoder map +#    include "encoder.h" +#endif diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c index a91b2a0b36..c1940f0fd3 100644 --- a/quantum/keymap_common.c +++ b/quantum/keymap_common.c @@ -148,6 +148,15 @@ action_t action_for_keycode(uint16_t keycode) {  // 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)]); +    if (key.row < MATRIX_ROWS && key.col < MATRIX_COLS) { +        return pgm_read_word(&keymaps[layer][key.row][key.col]); +    } +#ifdef ENCODER_MAP_ENABLE +    else if (key.row == KEYLOC_ENCODER_CW && key.col < NUM_ENCODERS) { +        return pgm_read_word(&encoder_map[layer][key.col][0]); +    } else if (key.row == KEYLOC_ENCODER_CCW && key.col < NUM_ENCODERS) { +        return pgm_read_word(&encoder_map[layer][key.col][1]); +    } +#endif // ENCODER_MAP_ENABLE +    return KC_NO;  } diff --git a/quantum/mousekey.c b/quantum/mousekey.c index 8bafbf977a..64d0e66682 100644 --- a/quantum/mousekey.c +++ b/quantum/mousekey.c @@ -16,6 +16,7 @@   */  #include <stdint.h> +#include <string.h>  #include "keycode.h"  #include "host.h"  #include "timer.h" @@ -209,7 +210,7 @@ static uint8_t wheel_unit(void) {  void mousekey_task(void) {      // report cursor and scroll movement independently -    report_mouse_t const tmpmr = mouse_report; +    report_mouse_t tmpmr = mouse_report;      mouse_report.x = 0;      mouse_report.y = 0; @@ -251,8 +252,10 @@ void mousekey_task(void) {          }      } -    if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send(); -    mouse_report = tmpmr; +    if (has_mouse_report_changed(&mouse_report, &tmpmr)) { +        mousekey_send(); +    } +    memcpy(&mouse_report, &tmpmr, sizeof(tmpmr));  }  void mousekey_on(uint8_t code) { @@ -340,11 +343,11 @@ uint16_t        w_intervals[mkspd_COUNT] = {MK_W_INTERVAL_UNMOD, MK_W_INTERVAL_0  void mousekey_task(void) {      // report cursor and scroll movement independently -    report_mouse_t const tmpmr = mouse_report; -    mouse_report.x             = 0; -    mouse_report.y             = 0; -    mouse_report.v             = 0; -    mouse_report.h             = 0; +    report_mouse_t tmpmr = mouse_report; +    mouse_report.x       = 0; +    mouse_report.y       = 0; +    mouse_report.v       = 0; +    mouse_report.h       = 0;      if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > c_intervals[mk_speed]) {          mouse_report.x = tmpmr.x; @@ -355,8 +358,10 @@ void mousekey_task(void) {          mouse_report.h = tmpmr.h;      } -    if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send(); -    mouse_report = tmpmr; +    if (has_mouse_report_changed(&mouse_report, &tmpmr)) { +        mousekey_send(); +    } +    memcpy(&mouse_report, &tmpmr, sizeof(tmpmr));  }  void adjust_speed(void) { diff --git a/quantum/pointing_device.c b/quantum/pointing_device.c index 47a0af45d2..a160647890 100644 --- a/quantum/pointing_device.c +++ b/quantum/pointing_device.c @@ -71,17 +71,6 @@ 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 report_mouse_t - * @param[in] old_report report_mouse_t - * @return bool result - */ -__attribute__((weak)) bool has_mouse_report_changed(report_mouse_t new_report, report_mouse_t old_report) { -    return memcmp(&new_report, &old_report, sizeof(new_report)); -} - -/**   * @brief Keyboard level code pointing device initialisation   *   */ @@ -165,7 +154,7 @@ __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(local_mouse_report, old_report)) { +    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 diff --git a/quantum/pointing_device.h b/quantum/pointing_device.h index a6bdbf120c..5c0eaeaf34 100644 --- a/quantum/pointing_device.h +++ b/quantum/pointing_device.h @@ -80,7 +80,6 @@ void           pointing_device_task(void);  void           pointing_device_send(void);  report_mouse_t pointing_device_get_report(void);  void           pointing_device_set_report(report_mouse_t mouse_report); -bool           has_mouse_report_changed(report_mouse_t new_report, report_mouse_t old_report);  uint16_t       pointing_device_get_cpi(void);  void           pointing_device_set_cpi(uint16_t cpi); diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c index efaf8fe0e9..d5a649adb3 100644 --- a/quantum/process_keycode/process_combo.c +++ b/quantum/process_keycode/process_combo.c @@ -88,8 +88,6 @@ static queued_combo_t combo_buffer[COMBO_BUFFER_LENGTH];  #define INCREMENT_MOD(i) i = (i + 1) % COMBO_BUFFER_LENGTH -#define COMBO_KEY_POS ((keypos_t){.col = 254, .row = 254}) -  #ifndef EXTRA_SHORT_COMBOS  /* flags are their own elements in combo_t struct. */  #    define COMBO_ACTIVE(combo) (combo->active) @@ -140,12 +138,7 @@ static queued_combo_t combo_buffer[COMBO_BUFFER_LENGTH];  static inline void release_combo(uint16_t combo_index, combo_t *combo) {      if (combo->keycode) {          keyrecord_t record = { -            .event = -                { -                    .key     = COMBO_KEY_POS, -                    .time    = timer_read() | 1, -                    .pressed = false, -                }, +            .event   = MAKE_KEYEVENT(KEYLOC_COMBO, KEYLOC_COMBO, false),              .keycode = combo->keycode,          };  #ifndef NO_ACTION_TAPPING @@ -325,7 +318,7 @@ void apply_combo(uint16_t combo_index, combo_t *combo) {          if (ALL_COMBO_KEYS_ARE_DOWN(state, key_count)) {              // this in the end executes the combo when the key_buffer is dumped.              record->keycode   = combo->keycode; -            record->event.key = COMBO_KEY_POS; +            record->event.key = MAKE_KEYPOS(KEYLOC_COMBO, KEYLOC_COMBO);              qrecord->combo_index = combo_index;              ACTIVATE_COMBO(combo); diff --git a/quantum/process_keycode/process_joystick.c b/quantum/process_keycode/process_joystick.c index 2fb092c573..8c3e71616f 100644 --- a/quantum/process_keycode/process_joystick.c +++ b/quantum/process_keycode/process_joystick.c @@ -6,41 +6,25 @@  #include <string.h>  #include <math.h> -bool process_joystick_buttons(uint16_t keycode, keyrecord_t *record); -  bool process_joystick(uint16_t keycode, keyrecord_t *record) { -    if (process_joystick_buttons(keycode, record) && (joystick_status.status & JS_UPDATED) > 0) { -        send_joystick_packet(&joystick_status); -        joystick_status.status &= ~JS_UPDATED; +    switch (keycode) { +        case JS_BUTTON0 ... JS_BUTTON_MAX: +            if (record->event.pressed) { +                register_joystick_button(keycode - JS_BUTTON0); +            } else { +                unregister_joystick_button(keycode - JS_BUTTON0); +            } +            return false;      } -      return true;  }  __attribute__((weak)) void joystick_task(void) { -    if (process_joystick_analogread() && (joystick_status.status & JS_UPDATED)) { -        send_joystick_packet(&joystick_status); -        joystick_status.status &= ~JS_UPDATED; +    if (process_joystick_analogread()) { +        joystick_flush();      }  } -bool process_joystick_buttons(uint16_t keycode, keyrecord_t *record) { -    if (keycode < JS_BUTTON0 || keycode > JS_BUTTON_MAX) { -        return true; -    } else { -        uint8_t button_idx = (keycode - JS_BUTTON0); -        if (record->event.pressed) { -            joystick_status.buttons[button_idx / 8] |= 1 << (button_idx % 8); -        } else { -            joystick_status.buttons[button_idx / 8] &= ~(1 << (button_idx % 8)); -        } - -        joystick_status.status |= JS_UPDATED; -    } - -    return true; -} -  uint16_t savePinState(pin_t pin) {  #ifdef __AVR__      uint8_t pinNumber = pin & 0xF; diff --git a/quantum/process_keycode/process_unicode_common.c b/quantum/process_keycode/process_unicode_common.c index 46b77e14ba..2606ea1f37 100644 --- a/quantum/process_keycode/process_unicode_common.c +++ b/quantum/process_keycode/process_unicode_common.c @@ -16,8 +16,6 @@  #include "process_unicode_common.h"  #include "eeprom.h" -#include <ctype.h> -#include <string.h>  unicode_config_t unicode_config;  uint8_t          unicode_saved_mods; @@ -231,37 +229,6 @@ void register_unicode(uint32_t code_point) {      unicode_input_finish();  } -// clang-format off - -void send_unicode_hex_string(const char *str) { -    if (!str) { -        return; -    } - -    while (*str) { -        // Find the next code point (token) in the string -        for (; *str == ' '; str++);    // Skip leading spaces -        size_t n = strcspn(str, " ");  // Length of the current token -        char code_point[n+1]; -        strncpy(code_point, str, n);   // Copy token into buffer -        code_point[n] = '\0';          // Make sure it's null-terminated - -        // Normalize the code point: make all hex digits lowercase -        for (char *p = code_point; *p; p++) { -            *p = tolower((unsigned char)*p); -        } - -        // Send the code point as a Unicode input string -        unicode_input_start(); -        send_string(code_point); -        unicode_input_finish(); - -        str += n;  // Move to the first ' ' (or '\0') after the current token -    } -} - -// clang-format on -  // Borrowed from https://nullprogram.com/blog/2017/10/06/  static const char *decode_utf8(const char *str, int32_t *code_point) {      const char *next; diff --git a/quantum/process_keycode/process_unicode_common.h b/quantum/process_keycode/process_unicode_common.h index 1a6607c757..8a4494c939 100644 --- a/quantum/process_keycode/process_unicode_common.h +++ b/quantum/process_keycode/process_unicode_common.h @@ -90,7 +90,6 @@ void register_hex(uint16_t hex);  void register_hex32(uint32_t hex);  void register_unicode(uint32_t code_point); -void send_unicode_hex_string(const char *str);  void send_unicode_string(const char *str);  bool process_unicode_common(uint16_t keycode, keyrecord_t *record); diff --git a/quantum/quantum.c b/quantum/quantum.c index ef6e5ac1df..d4e91ddd37 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -358,6 +358,26 @@ bool process_record_quantum(keyrecord_t *record) {                  oneshot_disable();                  break;  #endif +#ifdef ENABLE_COMPILE_KEYCODE +            case QK_MAKE: // Compiles the firmware, and adds the flash command based on keyboard bootloader +            { +#    ifdef NO_ACTION_ONESHOT +                const uint8_t temp_mod = mod_config(get_mods()); +#    else +                const uint8_t temp_mod = mod_config(get_mods() | get_oneshot_mods()); +                clear_oneshot_mods(); +#    endif +                clear_mods(); + +                SEND_STRING_DELAY("qmk", TAP_CODE_DELAY); +                if (temp_mod & MOD_MASK_SHIFT) { // if shift is held, flash rather than compile +                    SEND_STRING_DELAY(" flash ", TAP_CODE_DELAY); +                } else { +                    SEND_STRING_DELAY(" compile ", TAP_CODE_DELAY); +                } +                SEND_STRING_DELAY("-kb " QMK_KEYBOARD " -km " QMK_KEYMAP SS_TAP(X_ENTER), TAP_CODE_DELAY); +            } +#endif          }      } diff --git a/quantum/quantum.h b/quantum/quantum.h index 020e455941..f87e5f1916 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -200,6 +200,10 @@ extern layer_state_t layer_state;  #    include "dynamic_keymap.h"  #endif +#ifdef JOYSTICK_ENABLE +#    include "joystick.h" +#endif +  #ifdef VIA_ENABLE  #    include "via.h"  #endif diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index 2552c48165..dacfe5bdcd 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -595,6 +595,8 @@ enum quantum_keycodes {      MAGIC_TOGGLE_CONTROL_CAPSLOCK, +    QK_MAKE, +      // Start of custom keycode range for keyboards and keymaps - always leave at the end      SAFE_RANGE  }; diff --git a/quantum/split_common/transactions.c b/quantum/split_common/transactions.c index cffbccaeee..105bf918cb 100644 --- a/quantum/split_common/transactions.c +++ b/quantum/split_common/transactions.c @@ -180,7 +180,7 @@ static void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_ro  static bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {      static uint32_t last_update = 0; -    uint8_t         temp_state[NUMBER_OF_ENCODERS]; +    uint8_t         temp_state[NUM_ENCODERS_MAX_PER_SIDE];      bool okay = read_if_checksum_mismatch(GET_ENCODERS_CHECKSUM, GET_ENCODERS_DATA, &last_update, temp_state, split_shmem->encoders.state, sizeof(temp_state));      if (okay) encoder_update_raw(temp_state); @@ -188,7 +188,7 @@ static bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t s  }  static void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { -    uint8_t encoder_state[NUMBER_OF_ENCODERS]; +    uint8_t encoder_state[NUM_ENCODERS_MAX_PER_SIDE];      encoder_state_raw(encoder_state);      // Always prepare the encoder state for read.      memcpy(split_shmem->encoders.state, encoder_state, sizeof(encoder_state)); diff --git a/quantum/split_common/transport.h b/quantum/split_common/transport.h index 26bd136728..e62679990a 100644 --- a/quantum/split_common/transport.h +++ b/quantum/split_common/transport.h @@ -42,7 +42,6 @@ bool transport_execute_transaction(int8_t id, const void *initiator2target_buf,  #ifdef ENCODER_ENABLE  #    include "encoder.h" -#    define NUMBER_OF_ENCODERS (sizeof((pin_t[])ENCODERS_PAD_A) / sizeof(pin_t))  #endif // ENCODER_ENABLE  #ifdef BACKLIGHT_ENABLE @@ -67,7 +66,7 @@ typedef struct _split_master_matrix_sync_t {  #ifdef ENCODER_ENABLE  typedef struct _split_slave_encoder_sync_t {      uint8_t checksum; -    uint8_t state[NUMBER_OF_ENCODERS]; +    uint8_t state[NUM_ENCODERS_MAX_PER_SIDE];  } split_slave_encoder_sync_t;  #endif // ENCODER_ENABLE diff --git a/quantum/util.h b/quantum/util.h index bef3b9abe3..ab96ce4bde 100644 --- a/quantum/util.h +++ b/quantum/util.h @@ -24,3 +24,11 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.  // convert to string  #define STR(s) XSTR(s)  #define XSTR(s) #s + +#if !defined(MIN) +#    define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#if !defined(MAX) +#    define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif | 
