Merge pull request #3264 from fredizzimo/multimatrix

Add multi-matrix support and make the layer cache more efficient
This commit is contained in:
Jack Humbert 2018-07-21 15:34:10 -04:00 committed by GitHub
commit 47c27bb6b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 550 additions and 147 deletions

View File

@ -37,15 +37,15 @@
void qwiic_keyboard_write_keymap(uint8_t * pointer);
void qwiic_keyboard_read_keymap(uint8_t * pointer);
bool qwiic_keyboard_master = false;
bool qwiic_keyboard_connected = false;
uint8_t qwiic_keyboard_handshake_message[QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE] = {0};
uint8_t qwiic_keyboard_matrix_message[QWIIC_KEYBOARD_ROWS] = {0};
twi2c_message_received qwiic_keyboard_message_received_ptr = qwiic_keyboard_message_received;
static bool qwiic_keyboard_master = false;
static bool qwiic_keyboard_connected = false;
static uint8_t qwiic_keyboard_handshake_message[QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE] = {0};
static uint8_t qwiic_keyboard_matrix_message[QWIIC_KEYBOARD_ROWS] = {0};
static qwiic_matrix_t matrix_prev[QWIIC_KEYBOARD_ROWS] = {0};
static twi2c_message_received qwiic_keyboard_message_received_ptr = qwiic_keyboard_message_received;
uint16_t qwiic_keyboard_keymap[QWIIC_KEYBOARD_LAYERS][QWIIC_KEYBOARD_ROWS][QWIIC_KEYBOARD_COLS] = {0};
uint8_t qwiic_keyboard_listening_address = QWIIC_KEYBOARD_LISTENING_ADDRESS_START;
uint8_t qwiic_keyboard_processing_slave = false;
static uint16_t qwiic_keyboard_keymap[QWIIC_KEYBOARD_LAYERS][QWIIC_KEYBOARD_ROWS][QWIIC_KEYBOARD_COLS] = {{{0}}};
static uint8_t qwiic_keyboard_listening_address = QWIIC_KEYBOARD_LISTENING_ADDRESS_START;
void qwiic_keyboard_init(void) {
twi2c_init();
@ -69,50 +69,10 @@ void qwiic_keyboard_task(void) {
if (qwiic_keyboard_master) {
if (qwiic_keyboard_connected) {
// send empty message, expecting matrix info
if (MSG_OK == twi2c_transmit_receive(qwiic_keyboard_listening_address,
if (MSG_OK != twi2c_transmit_receive(qwiic_keyboard_listening_address,
command, 1,
qwiic_keyboard_matrix_message, QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE
)) {
// majority of this is pulled from keyboard.c:keyboard_task()
static qwiic_matrix_t matrix_prev[QWIIC_KEYBOARD_ROWS];
qwiic_matrix_t matrix_row = 0;
qwiic_matrix_t matrix_change = 0;
#ifdef QMK_KEYS_PER_SCAN
uint8_t keys_processed = 0;
#endif
qwiic_keyboard_processing_slave = true;
for (uint8_t r = 0; r < QWIIC_KEYBOARD_ROWS; r++) {
matrix_row = qwiic_keyboard_matrix_message[r];
matrix_change = matrix_row ^ matrix_prev[r];
if (matrix_change) {
for (uint8_t c = 0; c < QWIIC_KEYBOARD_COLS; c++) {
if (matrix_change & ((qwiic_matrix_t)1<<c)) {
action_exec((keyevent_t){
.key = (keypos_t){ .row = r, .col = c },
.pressed = (matrix_row & ((qwiic_matrix_t)1<<c)),
.time = (timer_read() | 1) /* time should not be 0 */
});
// record a processed key
matrix_prev[r] ^= ((qwiic_matrix_t)1<<c);
#ifdef QMK_KEYS_PER_SCAN
// only jump out if we have processed "enough" keys.
if (++keys_processed >= QMK_KEYS_PER_SCAN)
#endif
// process a key per task call
goto QWIIC_MATRIX_LOOP_END;
}
}
}
}
// call with pseudo tick event when no real key event.
#ifdef QMK_KEYS_PER_SCAN
// we can get here with some keys processed now.
if (!keys_processed)
#endif
action_exec(TICK);
QWIIC_MATRIX_LOOP_END:
qwiic_keyboard_processing_slave = false;
} else {
// disconnect
// qwiic_keyboard_connected = false;
}
@ -190,13 +150,40 @@ bool is_keyboard_master(void) {
}
// overwrite the built-in function
uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) {
if (qwiic_keyboard_processing_slave) {
// trick the built-in handling to accept our replacement keymap
return qwiic_keyboard_keymap[(layer)][(key.row)][(key.col)];
//return KC_A;
} else {
uint16_t keymap_key_to_keycode(uint8_t layer, keymatrix_t key) {
if (key.matrix == 0) {
// Read entire word (16bits)
return pgm_read_word(&keymaps[(layer)][(key.row)][(key.col)]);
return pgm_read_word(&keymaps[(layer)][(key.pos.row)][(key.pos.col)]);
} else {
return qwiic_keyboard_keymap[(layer)][(key.pos.row)][(key.pos.col)];
}
}
uint8_t multimatrix_get_num_matrices(void) {
return qwiic_keyboard_connected ? 1 : 0;
}
uint8_t multimatrix_get_num_cols(uint8_t matrix) {
return QWIIC_KEYBOARD_COLS;
}
uint8_t multimatrix_get_num_rows(uint8_t matrix) {
return QWIIC_KEYBOARD_ROWS;
}
uint32_t multimatrix_get_row(uint8_t matrix, uint8_t row) {
return qwiic_keyboard_matrix_message[row];
}
uint32_t multimatrix_get_row_cache(uint8_t matrix, uint8_t row) {
return matrix_prev[row];
}
void multimatrix_set_row_cache(uint8_t matrix, uint8_t row, uint32_t value) {
matrix_prev[row] = value;
}
uint8_t* multimatrix_get_source_layers_cache(uint8_t matrix) {
static uint8_t source_layers_cache[(QWIIC_KEYBOARD_ROWS * QWIIC_KEYBOARD_COLS * MAX_LAYER_BITS + 7) / 8] = {0};
return source_layers_cache;
}

View File

@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/* translates key to keycode */
uint8_t keymap_key_to_keycode(uint8_t layer, keypos_t key)
uint8_t keymap_key_to_keycode(uint8_t layer, keymatrix_t key)
{
return pgm_read_byte(&keymaps[(layer)][(key.row)][(key.col)]);
}

View File

@ -44,7 +44,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "quantum_keycodes.h"
// translates key to keycode
uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key);
uint16_t keymap_key_to_keycode(uint8_t layer, keymatrix_t key);
// translates function id to action
uint16_t keymap_function_id_to_action( uint16_t function_id );

View File

@ -38,7 +38,7 @@ extern keymap_config_t keymap_config;
#include <inttypes.h>
/* converts key to action */
action_t action_for_key(uint8_t layer, keypos_t key)
action_t action_for_key(uint8_t layer, keymatrix_t key)
{
// 16bit keycodes - important
uint16_t keycode = keymap_key_to_keycode(layer, key);
@ -184,10 +184,12 @@ 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)
uint16_t keymap_key_to_keycode(uint8_t layer, keymatrix_t key)
{
// Read entire word (16bits)
return pgm_read_word(&keymaps[(layer)][(key.row)][(key.col)]);
// The default implmention does not have multiple matrix support
// and therfore it ignores the matrix field of the key
return pgm_read_word(&keymaps[(layer)][(key.pos.row)][(key.pos.col)]);
}
// translates function id to action

View File

@ -198,22 +198,23 @@ bool process_music(uint16_t keycode, keyrecord_t *record) {
}
uint8_t note = 36;
// Note: Multimatrix support is missing from this (it's probablly better to define it using keycodes)
#ifdef MUSIC_MAP
if (music_mode == MUSIC_MODE_CHROMATIC) {
note = music_starting_note + music_offset + 36 + music_map[record->event.key.row][record->event.key.col];
note = music_starting_note + music_offset + 36 + music_map[record->event.key.pos.row][record->event.key.pos.col];
} else {
uint8_t position = music_map[record->event.key.row][record->event.key.col];
uint8_t position = music_map[record->event.key.pos.row][record->event.key.pos.col];
note = music_starting_note + music_offset + 36 + SCALE[position % 12] + (position / 12)*12;
}
#else
if (music_mode == MUSIC_MODE_CHROMATIC)
note = (music_starting_note + record->event.key.col + music_offset - 3)+12*(MATRIX_ROWS - record->event.key.row);
note = (music_starting_note + record->event.key.pos.col + music_offset - 3)+12*(MATRIX_ROWS - record->event.key.pos.row);
else if (music_mode == MUSIC_MODE_GUITAR)
note = (music_starting_note + record->event.key.col + music_offset + 32)+5*(MATRIX_ROWS - record->event.key.row);
note = (music_starting_note + record->event.key.pos.col + music_offset + 32)+5*(MATRIX_ROWS - record->event.key.pos.row);
else if (music_mode == MUSIC_MODE_VIOLIN)
note = (music_starting_note + record->event.key.col + music_offset + 32)+7*(MATRIX_ROWS - record->event.key.row);
note = (music_starting_note + record->event.key.pos.col + music_offset + 32)+7*(MATRIX_ROWS - record->event.key.pos.row);
else if (music_mode == MUSIC_MODE_MAJOR)
note = (music_starting_note + SCALE[record->event.key.col + music_offset] - 3)+12*(MATRIX_ROWS - record->event.key.row);
note = (music_starting_note + SCALE[record->event.key.pos.col + music_offset] - 3)+12*(MATRIX_ROWS - record->event.key.pos.row);
else
note = music_starting_note;
#endif

View File

@ -190,7 +190,7 @@ static bool grave_esc_was_shifted = false;
bool process_record_quantum(keyrecord_t *record) {
/* This gets the keycode from the key pressed */
keypos_t key = record->event.key;
keymatrix_t key = record->event.key;
uint16_t keycode;
#if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS)

View File

@ -119,7 +119,8 @@ void rgb_matrix_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) {
bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) {
if ( record->event.pressed ) {
uint8_t led[8], led_count;
map_row_column_to_led(record->event.key.row, record->event.key.col, led, &led_count);
// TODO: Support multi-matrix keyboards
map_row_column_to_led(record->event.key.pos.row, record->event.key.pos.col, led, &led_count);
if (led_count > 0) {
for (uint8_t i = LED_HITS_TO_REMEMBER; i > 1; i--) {
g_last_led_hit[i - 1] = g_last_led_hit[i - 2];

View File

@ -0,0 +1,21 @@
/* Copyright 2018 Fred Sundvik
*
* 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
#define MATRIX_ROWS 2
#define MATRIX_COLS 2
#define PREVENT_STUCK_MODIFIERS

View File

@ -0,0 +1,24 @@
/* Copyright 2018 Fred Sundvik
*
* 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 "quantum.h"
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = {
{KC_A, KC_B},
{KC_C, KC_D},
}
};

View File

@ -0,0 +1,16 @@
# Copyright 2018 Fred Sundvik
#
# 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/>.
CUSTOM_MATRIX=yes

View File

@ -0,0 +1,142 @@
/* Copyright 2018 Fred Sundvik
*
* 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 "test_common.hpp"
#if MAX_LAYER_BITS != 5
#error "Tese tests assume that the MAX_LAYER_BITS is equal to 5"
// If this is changed, change the constants below
#endif
#if MATRIX_COLS != 2 || MATRIX_ROWS !=2
#error "These tests assume that the second row starts after the second column"
#endif
namespace
{
constexpr uint8_t max_layer_value = 0b11111;
constexpr uint8_t min_layer_value = 0;
constexpr uint8_t alternating_starting_with_1 = 0b10101;
constexpr uint8_t alternating_starting_with_0 = 0b01010;
uint8_t read_cache(uint8_t col, uint8_t row) {
keymatrix_t key;
key.pos.col = col;
key.pos.row = row;
key.matrix = 0;
return read_source_layers_cache(key);
}
void write_cache(uint8_t col, uint8_t row, uint8_t value) {
keymatrix_t key;
key.pos.col = col;
key.pos.row = row;
key.matrix = 0;
return update_source_layers_cache(key, value);
}
void fill_cache() {
for (int i=0; i < MATRIX_ROWS; i++) {
for (int j=0; j < MATRIX_COLS; j++) {
write_cache(j, i, max_layer_value);
}
}
}
void clear_cache() {
for (int i=0; i < MATRIX_ROWS; i++) {
for (int j=0; j < MATRIX_COLS; j++) {
write_cache(j, i, min_layer_value);
}
}
}
}
class LayerCache : public testing::Test
{
public:
LayerCache()
{
clear_cache();
}
};
TEST_F(LayerCache, LayerCacheIsInitializedToZero) {
for (int i=0; i < MATRIX_ROWS; i++) {
for (int j=0; j < MATRIX_COLS; j++) {
EXPECT_EQ(read_cache(j, i), min_layer_value);
}
}
}
TEST_F(LayerCache, FillAndClearCache) {
fill_cache();
clear_cache();
for (int i=0; i < MATRIX_ROWS; i++) {
for (int j=0; j < MATRIX_COLS; j++) {
EXPECT_EQ(read_cache(j, i), min_layer_value);
}
}
}
TEST_F(LayerCache, WriteAndReadFirstPosMaximumValue) {
write_cache(0, 0, max_layer_value);
EXPECT_EQ(read_cache(0, 0), max_layer_value);
// The second position should not be updated
EXPECT_EQ(read_cache(1, 0), min_layer_value);
EXPECT_EQ(read_cache(0, 1), min_layer_value);
}
TEST_F(LayerCache, WriteAndReadSecondPosMaximumValue) {
write_cache(1, 0, max_layer_value);
EXPECT_EQ(read_cache(1, 0), max_layer_value);
// The surrounding positions should not be updated
EXPECT_EQ(read_cache(0, 0), min_layer_value);
EXPECT_EQ(read_cache(0, 1), min_layer_value);
}
TEST_F(LayerCache, WriteAndReadFirstPosAlternatingBitsStartingWith1) {
write_cache(0, 0, alternating_starting_with_1);
EXPECT_EQ(read_cache(0, 0), alternating_starting_with_1);
// The second position should not be updated
EXPECT_EQ(read_cache(1, 0), min_layer_value);
EXPECT_EQ(read_cache(0, 1), min_layer_value);
}
TEST_F(LayerCache, WriteAndReadSecondPosAlternatingBitsStartingWith1) {
write_cache(1, 0, alternating_starting_with_1);
EXPECT_EQ(read_cache(1, 0), alternating_starting_with_1);
// The surrounding positions should not be updated
EXPECT_EQ(read_cache(0, 0), min_layer_value);
EXPECT_EQ(read_cache(0, 1), min_layer_value);
}
TEST_F(LayerCache, WriteAndReadFirstPosAlternatingBitsStartingWith0) {
write_cache(0, 0, alternating_starting_with_0);
EXPECT_EQ(read_cache(0, 0), alternating_starting_with_0);
// The second position should not be updated
EXPECT_EQ(read_cache(1, 0), min_layer_value);
EXPECT_EQ(read_cache(0, 1), min_layer_value);
}
TEST_F(LayerCache, WriteAndReadSecondPosAlternatingBitsStartingWith0) {
write_cache(1, 0, alternating_starting_with_0);
EXPECT_EQ(read_cache(1, 0), alternating_starting_with_0);
// The surrounding positions should not be updated
EXPECT_EQ(read_cache(0, 0), min_layer_value);
EXPECT_EQ(read_cache(0, 1), min_layer_value);
}

View File

@ -106,13 +106,14 @@ bool swap_held = false;
void process_hand_swap(keyevent_t *event) {
static swap_state_row_t swap_state[MATRIX_ROWS];
keypos_t pos = event->key;
// TODO: Properly support multimatrices, currenty this only works for single-matrix keyboards
keypos_t pos = event->key.pos;
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 = hand_swap_config[pos.row][pos.col];
event->key.pos = hand_swap_config[pos.row][pos.col];
swap_state[pos.row] |= col_bit;
} else {
swap_state[pos.row] &= ~(col_bit);
@ -892,7 +893,7 @@ void clear_keyboard_but_mods(void)
*
* FIXME: Needs documentation.
*/
bool is_tap_key(keypos_t key)
bool is_tap_key(keymatrix_t key)
{
action_t action = layer_switch_get_action(key);

View File

@ -50,7 +50,7 @@ typedef struct {
void action_exec(keyevent_t event);
/* action for key */
action_t action_for_key(uint8_t layer, keypos_t key);
action_t action_for_key(uint8_t layer, keymatrix_t key);
/* macro */
const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt);
@ -94,7 +94,7 @@ void unregister_mods(uint8_t mods);
void clear_keyboard(void);
void clear_keyboard_but_mods(void);
void layer_switch(uint8_t new_layer);
bool is_tap_key(keypos_t key);
bool is_tap_key(keymatrix_t key);
#ifndef NO_ACTION_TAPPING
void process_record_tap_hint(keyrecord_t *record);

View File

@ -220,37 +220,102 @@ void layer_debug(void)
#endif
#if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS)
uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS] = {{0}};
static const uint8_t layer_cache_mask = (1u << MAX_LAYER_BITS) - 1;
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;
/** \brief Get the pointer to the source layer cache for a connected matrix
*
* Implement this if you support multiple matrices, see qwiic_keyboard.c for an example
* NOTE: The matrix index 0 is the first remote matrix, the function is not called for the master
*/
__attribute__((weak))
uint8_t* multimatrix_get_source_layers_cache(uint8_t matrix) {
return 0;
}
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);
static uint8_t* get_source_layers_cache(keymatrix_t key) {
if (key.matrix == 0) {
static uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS * MAX_LAYER_BITS + 7) / 8] = {0};
return source_layers_cache;
} else {
return multimatrix_get_source_layers_cache(key.matrix - 1);
}
}
uint8_t read_source_layers_cache(keypos_t key)
void update_source_layers_cache(keymatrix_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;
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;
const uint8_t num_cols = keyboard_get_num_cols(key.matrix);
const uint8_t num_rows = keyboard_get_num_rows(key.matrix);
const uint16_t num_cache_bytes = get_source_layers_cache_size(num_cols, num_rows);
uint8_t* cache = get_source_layers_cache(key);
const uint16_t key_number = key.pos.col + (key.pos.row * num_cols);
const uint32_t bit_number = key_number * MAX_LAYER_BITS;
const uint16_t byte_number = bit_number / 8;
if (byte_number >= num_cache_bytes) {
return;
}
const uint8_t bit_position = bit_number % 8;
int8_t shift = 16 - MAX_LAYER_BITS - bit_position;
return layer;
if (shift > 8 ) {
// We need to write only one byte
shift -= 8;
const uint8_t mask = layer_cache_mask << shift;
const uint8_t shifted_layer = layer << shift;
cache[byte_number] = (shifted_layer & mask) | (cache[byte_number] & (~mask));
} else {
if (byte_number + 1 >= num_cache_bytes) {
return;
}
// We need to write two bytes
uint16_t value = layer;
uint16_t mask = layer_cache_mask;
value <<= shift;
mask <<= shift;
uint16_t masked_value = value & mask;
uint16_t inverse_mask = ~mask;
// This could potentially be done with a single write, but then we have to assume the endian
cache[byte_number + 1] = masked_value | (cache[byte_number + 1] & (inverse_mask));
masked_value >>= 8;
inverse_mask >>= 8;
cache[byte_number] = masked_value | (cache[byte_number] & (inverse_mask));
}
}
uint8_t read_source_layers_cache(keymatrix_t key)
{
const uint8_t num_cols = keyboard_get_num_cols(key.matrix);
const uint8_t num_rows = keyboard_get_num_rows(key.matrix);
const uint16_t num_cache_bytes = get_source_layers_cache_size(num_cols, num_rows);
uint8_t* cache = get_source_layers_cache(key);
const uint16_t key_number = key.pos.col + (key.pos.row * num_cols);
const uint32_t bit_number = key_number * MAX_LAYER_BITS;
const uint16_t byte_number = bit_number / 8;
if (byte_number >= num_cache_bytes) {
return 0;
}
const uint8_t bit_position = bit_number % 8;
int8_t shift = 16 - MAX_LAYER_BITS - bit_position;
if (shift > 8 ) {
// We need to read only one byte
shift -= 8;
return (cache[byte_number] >> shift) & layer_cache_mask;
} else {
if (byte_number + 1 >= num_cache_bytes) {
return 0;
}
// Otherwise read two bytes
// This could potentially be done with a single read, but then we have to assume the endian
uint16_t value = cache[byte_number] << 8 | cache[byte_number + 1];
return (value >> shift) & layer_cache_mask;
}
}
uint8_t get_source_layers_cache_size(uint8_t num_cols, uint8_t num_rows) {
return (num_rows * num_cols * MAX_LAYER_BITS + 7) / 8;
}
#endif
@ -261,7 +326,7 @@ uint8_t read_source_layers_cache(keypos_t key)
* when the layer is switched after the down event but before the up
* event as they may get stuck otherwise.
*/
action_t store_or_get_action(bool pressed, keypos_t key)
action_t store_or_get_action(bool pressed, keymatrix_t key)
{
#if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS)
if (disable_action_cache) {
@ -288,7 +353,7 @@ action_t store_or_get_action(bool pressed, keypos_t key)
*
* FIXME: Needs docs
*/
int8_t layer_switch_get_layer(keypos_t key)
int8_t layer_switch_get_layer(keymatrix_t key)
{
#ifndef NO_ACTION_LAYER
action_t action;
@ -315,7 +380,7 @@ int8_t layer_switch_get_layer(keypos_t key)
*
* FIXME: Needs docs
*/
action_t layer_switch_get_action(keypos_t key)
action_t layer_switch_get_action(keymatrix_t key)
{
return action_for_key(layer_switch_get_layer(key), key);
}

View File

@ -91,15 +91,17 @@ uint32_t layer_state_set_kb(uint32_t state);
#if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS)
/* The number of bits needed to represent the layer number: log2(32). */
#define MAX_LAYER_BITS 5
void update_source_layers_cache(keypos_t key, uint8_t layer);
uint8_t read_source_layers_cache(keypos_t key);
void update_source_layers_cache(keymatrix_t key, uint8_t layer);
uint8_t read_source_layers_cache(keymatrix_t key);
uint8_t get_source_layers_cache_size(uint8_t num_cols, uint8_t num_rows);
#endif
action_t store_or_get_action(bool pressed, keypos_t key);
action_t store_or_get_action(bool pressed, keymatrix_t key);
/* return the topmost non-transparent layer currently associated with key */
int8_t layer_switch_get_layer(keypos_t key);
int8_t layer_switch_get_layer(keymatrix_t key);
/* return action depending on current layer status */
action_t layer_switch_get_action(keypos_t key);
action_t layer_switch_get_action(keymatrix_t key);
#endif

View File

@ -116,7 +116,7 @@ static bool scan_keycode(uint8_t keycode)
matrix_row_t matrix_row = matrix_get_row(r);
for (uint8_t c = 0; c < MATRIX_COLS; c++) {
if (matrix_row & ((matrix_row_t)1<<c)) {
if (keycode == keymap_key_to_keycode(0, (keypos_t){ .row = r, .col = c })) {
if (keycode == keymap_key_to_keycode(0, (keymatrix_t){ (keypos_t){.row = r, .col = c }, .matrix=0 })) {
return true;
}
}

View File

@ -145,6 +145,132 @@ bool is_keyboard_master(void) {
return true;
}
/** \brief Get the number of currently connected matrices
*
* Implement this if you support multiple matrices, see qwiic_keyboard.c for an example
* NOTE: It should return the number of additional matrices, the master is always implied.
*/
__attribute__((weak))
uint8_t multimatrix_get_num_matrices(void) {
return 0;
}
/** \brief Get the number of columns of a connected matrix
*
* Implement this if you support multiple matrices, see qwiic_keyboard.c for an example
* NOTE: The matrix index 0 is the first remote matrix, the function is not called for the master
*/
__attribute__((weak))
uint8_t multimatrix_get_num_cols(uint8_t matrix) {
return 0;
}
/** \brief Get the number of rows of a connected matrix
*
* Implement this if you support multiple matrices, see qwiic_keyboard.c for an example
* NOTE: The matrix index 0 is the first remote matrix, the function is not called for the master
*/
__attribute__((weak))
uint8_t multimatrix_get_num_rows(uint8_t matrix) {
return 0;
}
/** \brief Get the row status of a connected matrix
*
* Implement this if you support multiple matrices, see qwiic_keyboard.c for an example
* NOTE: The matrix index 0 is the first remote matrix, the function is not called for the master
*/
__attribute__((weak))
uint32_t multimatrix_get_row(uint8_t matrix, uint8_t row) {
return 0;
}
/** \brief Get the row cache of a connected matrix
*
* Implement this if you support multiple matrices, see qwiic_keyboard.c for an example
* NOTE: The matrix index 0 is the first remote matrix, the function is not called for the master
*/
__attribute__((weak))
uint32_t multimatrix_get_row_cache(uint8_t matrix, uint8_t row) {
return 0;
}
/** \brief Set the row cache of a connected matrix
*
* Implement this if you support multiple matrices, see qwiic_keyboard.c for an example
* NOTE: The matrix index 0 is the first remote matrix, the function is not called for the master
*/
__attribute__((weak))
void multimatrix_set_row_cache(uint8_t matrix, uint8_t row, uint32_t value) {
}
/** \brief Get the number of currently connected matrices
*
* For normal keyboards this usually returns 1, but for multi-matrix keyboards this will
* return the total number of connected keyboards/modules including the master
*/
uint8_t keyboard_get_num_matrices(void) {
return 1 + multimatrix_get_num_matrices();
}
/** \brief Get the number of columns of a connected matrix
*
* Specify the matrix index to query connected multi-matrix keyboards/modules
* 0 is always the master
*/
uint8_t keyboard_get_num_cols(uint8_t matrix) {
if (matrix == 0) {
return MATRIX_COLS;
} else {
return multimatrix_get_num_cols(matrix - 1);
}
}
/** \brief Get the number of rows of a connected matrix
*
* Specify the matrix index to query connected multi-matrix keyboards/modules
* 0 is always the master
*/
uint8_t keyboard_get_num_rows(uint8_t matrix) {
if (matrix == 0) {
return MATRIX_ROWS;
} else {
return multimatrix_get_num_rows(matrix - 1);
}
}
/** \brief Get the row status of a connected matrix
*
* Specify the matrix index to query connected multi-matrix keyboards/modules
* 0 is always the master
*/
uint32_t keyboard_get_row(uint8_t matrix, uint8_t row) {
if (matrix == 0) {
return matrix_get_row(row);
} else {
return multimatrix_get_row(matrix - 1, row);
}
}
static matrix_row_t matrix_prev[MATRIX_ROWS];
static uint32_t get_row_cache(uint8_t matrix, uint8_t row) {
if (matrix == 0) {
return matrix_prev[row];
} else {
return multimatrix_get_row_cache(matrix - 1, row);
}
}
static void set_row_cache(uint8_t matrix, uint8_t row, uint32_t value) {
if (matrix == 0) {
matrix_prev[row] = value;
} else {
return multimatrix_set_row_cache(matrix - 1, row, value);
}
}
/** \brief keyboard_init
*
* FIXME: needs doc
@ -203,24 +329,25 @@ void keyboard_init(void) {
*/
void keyboard_task(void)
{
static matrix_row_t matrix_prev[MATRIX_ROWS];
#ifdef MATRIX_HAS_GHOST
// static matrix_row_t matrix_ghost[MATRIX_ROWS];
#endif
static uint8_t led_status = 0;
matrix_row_t matrix_row = 0;
matrix_row_t matrix_change = 0;
uint32_t matrix_row = 0;
uint32_t matrix_change = 0;
#ifdef QMK_KEYS_PER_SCAN
uint8_t keys_processed = 0;
#endif
matrix_scan();
if (is_keyboard_master()) {
for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
matrix_row = matrix_get_row(r);
matrix_change = matrix_row ^ matrix_prev[r];
for (uint8_t m = 0; m < keyboard_get_num_matrices(); m++) {
uint8_t num_cols = keyboard_get_num_cols(m);
uint8_t num_rows = keyboard_get_num_rows(m);
for (uint8_t r = 0; r < num_rows; r++) {
matrix_row = keyboard_get_row(m, r);
uint32_t row_cache = get_row_cache(m, r);
matrix_change = matrix_row ^ row_cache;
if (matrix_change) {
#ifdef MATRIX_HAS_GHOST
//NOTE: The we support ghosting only for the main matrix, since it's only useful for old keyboards without diodes
if (has_ghost_in_row(r, matrix_row)) {
/* Keep track of whether ghosted status has changed for
* debugging. But don't update matrix_prev until un-ghosted, or
@ -235,15 +362,17 @@ void keyboard_task(void)
//matrix_ghost[r] = matrix_row;
#endif
if (debug_matrix) matrix_print();
for (uint8_t c = 0; c < MATRIX_COLS; c++) {
if (matrix_change & ((matrix_row_t)1<<c)) {
for (uint8_t c = 0; c < num_cols; c++) {
if (matrix_change & (uint32_t)(1u<<c)) {
action_exec((keyevent_t){
.key = (keypos_t){ .row = r, .col = c },
.pressed = (matrix_row & ((matrix_row_t)1<<c)),
// The main matrix is always 0
.key = (keymatrix_t){.pos = (keypos_t){ .row = r, .col = c }, .matrix = m},
.pressed = (matrix_row & ((uint32_t)1u<<c)),
.time = (timer_read() | 1) /* time should not be 0 */
});
// record a processed key
matrix_prev[r] ^= ((matrix_row_t)1<<c);
row_cache ^= (uint32_t)(1u<<c);
set_row_cache(m, r, row_cache);
#ifdef QMK_KEYS_PER_SCAN
// only jump out if we have processed "enough" keys.
if (++keys_processed >= QMK_KEYS_PER_SCAN)
@ -255,6 +384,7 @@ void keyboard_task(void)
}
}
}
}
// call with pseudo tick event when no real key event.
#ifdef QMK_KEYS_PER_SCAN
// we can get here with some keys processed now.

View File

@ -32,27 +32,33 @@ typedef struct {
uint8_t row;
} keypos_t;
/* the key and matrix position */
typedef struct {
keypos_t pos;
uint8_t matrix;
} keymatrix_t;
/* key event */
typedef struct {
keypos_t key;
keymatrix_t key;
bool pressed;
uint16_t time;
} keyevent_t;
/* equivalent test of keypos_t */
#define KEYEQ(keya, keyb) ((keya).row == (keyb).row && (keya).col == (keyb).col)
/* equivalent test of keymatrix_t */
#define KEYEQ(keya, keyb) (keya.pos.row == keyb.pos.row && keya.pos.col == keyb.pos.col && keya.matrix == keyb.matrix)
/* 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); }
static inline bool IS_NOEVENT(keyevent_t event) { return event.time == 0 || (event.key.pos.row == 255 && event.key.pos.col == 255); }
static inline bool IS_PRESSED(keyevent_t event) { return (!IS_NOEVENT(event) && event.pressed); }
static inline bool IS_RELEASED(keyevent_t event) { return (!IS_NOEVENT(event) && !event.pressed); }
/* Tick event */
#define TICK (keyevent_t){ \
.key = (keypos_t){ .row = 255, .col = 255 }, \
.key = (keymatrix_t){ .pos = (keypos_t){.row = 255, .col = 255}, .matrix = 0 }, \
.pressed = false, \
.time = (timer_read() | 1) \
}
@ -66,6 +72,11 @@ void keyboard_task(void);
/* it runs when host LED status is updated */
void keyboard_set_leds(uint8_t leds);
uint8_t keyboard_get_num_matrices(void);
uint8_t keyboard_get_num_cols(uint8_t matrix);
uint8_t keyboard_get_num_rows(uint8_t matrix);
uint32_t keyboard_get_row(uint8_t matrix, uint8_t row);
#ifdef __cplusplus
}
#endif