Merge pull request #3264 from fredizzimo/multimatrix
Add multi-matrix support and make the layer cache more efficient
This commit is contained in:
commit
47c27bb6b9
@ -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;
|
||||
}
|
||||
|
@ -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)]);
|
||||
}
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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];
|
||||
|
21
tests/layer_cache/config.h
Normal file
21
tests/layer_cache/config.h
Normal 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
|
24
tests/layer_cache/keymap.c
Normal file
24
tests/layer_cache/keymap.c
Normal 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},
|
||||
}
|
||||
};
|
16
tests/layer_cache/rules.mk
Normal file
16
tests/layer_cache/rules.mk
Normal 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
|
142
tests/layer_cache/test_layer_cache.cpp
Normal file
142
tests/layer_cache/test_layer_cache.cpp
Normal 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);
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user