Skip to content

Commit

Permalink
fix(keymaps): fix keypresses that are not in the transform
Browse files Browse the repository at this point in the history
Before this change, if a matrix position was not present in the transform,
various incorrect behaviors would happen:

1) In some cases out-of-bounds accesses:

Note that the size of the`transform[]` array does not necessarily match
the size of the matrix. So for example if key position
(ZMK_MATRIX_COLS-1, ZMK_MATRIX_ROWS-1) is not present in the transform,
but ends up being pressed, then the array will be accessed beyond its
size, and any data could be returned.

2) In other cases the 0th position in the keymap will be used because
the `transform[]` array is initialized to all zeros.
  • Loading branch information
purdeaandrei authored Apr 10, 2023
1 parent ee9fcec commit 309359b
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 10 deletions.
2 changes: 1 addition & 1 deletion app/include/zmk/matrix_transform.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@

#pragma once

uint32_t zmk_matrix_transform_row_column_to_position(uint32_t row, uint32_t column);
int32_t zmk_matrix_transform_row_column_to_position(uint32_t row, uint32_t column);
9 changes: 8 additions & 1 deletion app/src/kscan.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,14 @@ void zmk_kscan_process_msgq(struct k_work *item) {

while (k_msgq_get(&zmk_kscan_msgq, &ev, K_NO_WAIT) == 0) {
bool pressed = (ev.state == ZMK_KSCAN_EVENT_STATE_PRESSED);
uint32_t position = zmk_matrix_transform_row_column_to_position(ev.row, ev.column);
int32_t position = zmk_matrix_transform_row_column_to_position(ev.row, ev.column);

if (position < 0) {
LOG_WRN("Not found in transform: row: %d, col: %d, pressed: %s", ev.row, ev.column,
(pressed ? "true" : "false"));
continue;
}

LOG_DBG("Row: %d, col: %d, position: %d, pressed: %s", ev.row, ev.column, position,
(pressed ? "true" : "false"));
ZMK_EVENT_RAISE(new_zmk_position_state_changed(
Expand Down
41 changes: 33 additions & 8 deletions app/src/matrix_transform.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,32 @@

#ifdef ZMK_KEYMAP_TRANSFORM_NODE

#define _TRANSFORM_ENTRY(i, _) \
/* the transform in the device tree is a list of (row,column) pairs that is
* indexed by by the keymap position of that key. We want to invert this in
* order to be able to quickly determine what keymap position a particular
* row,column pair is associated with, using a single lookup.
*
* We do this by creating the `transform` array at compile time, which is
* indexed by (row * ZMK_MATRIX_COLS) + column, and the value contains an
* encoded keymap index it is associated with. The keymap index is encoded
* by adding INDEX_OFFSET to it, because not all row,column pairs have an
* associated keymap index (some matrices are sparse), C globals are
* initialized to 0, and the keymap index of 0 is a valid index. We want to
* be able to detect the condition when an unassigned matrix position is
* pressed and we want to return an error.
*/

#define INDEX_OFFSET 1

#define TRANSFORM_ENTRY(i, _) \
[(KT_ROW(DT_PROP_BY_IDX(ZMK_KEYMAP_TRANSFORM_NODE, map, i)) * ZMK_MATRIX_COLS) + \
KT_COL(DT_PROP_BY_IDX(ZMK_KEYMAP_TRANSFORM_NODE, map, i))] = i
KT_COL(DT_PROP_BY_IDX(ZMK_KEYMAP_TRANSFORM_NODE, map, i))] = i + INDEX_OFFSET

static uint32_t transform[] = {LISTIFY(ZMK_KEYMAP_LEN, _TRANSFORM_ENTRY, (, ), 0)};
static uint32_t transform[] = {LISTIFY(ZMK_KEYMAP_LEN, TRANSFORM_ENTRY, (, ), 0)};

#endif

uint32_t zmk_matrix_transform_row_column_to_position(uint32_t row, uint32_t column) {
uint32_t matrix_index;

int32_t zmk_matrix_transform_row_column_to_position(uint32_t row, uint32_t column) {
#if DT_NODE_HAS_PROP(ZMK_KEYMAP_TRANSFORM_NODE, col_offset)
column += DT_PROP(ZMK_KEYMAP_TRANSFORM_NODE, col_offset);
#endif
Expand All @@ -30,10 +45,20 @@ uint32_t zmk_matrix_transform_row_column_to_position(uint32_t row, uint32_t colu
row += DT_PROP(ZMK_KEYMAP_TRANSFORM_NODE, row_offset);
#endif

matrix_index = (row * ZMK_MATRIX_COLS) + column;
const uint32_t matrix_index = (row * ZMK_MATRIX_COLS) + column;

#ifdef ZMK_KEYMAP_TRANSFORM_NODE
return transform[matrix_index];
if (matrix_index >= ARRAY_SIZE(transform)) {
return -EINVAL;
}

const uint32_t value = transform[matrix_index];

if (!value) {
return -EINVAL;
}

return value - INDEX_OFFSET;
#else
return matrix_index;
#endif /* ZMK_KEYMAP_TRANSFORM_NODE */
Expand Down

0 comments on commit 309359b

Please sign in to comment.