Skip to content

Commit

Permalink
Attempt at simplifying interface.
Browse files Browse the repository at this point in the history
  • Loading branch information
getreuer committed Jan 22, 2025
1 parent 45f573f commit 8eaf67d
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 45 deletions.
14 changes: 9 additions & 5 deletions docs/unit_testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,22 @@ Use the result of `get_keycode_string()` immediately. Subsequent invocations reu
Many common QMK keycodes are recognized by `get_keycode_string()`, but not all. These include some common basic keycodes, layer switch keycodes, mod-taps, one-shot keycodes, tap dance keycodes, and Unicode keycodes. As a fallback, an unrecognized keycode is written as a hex number.
Optionally, `keycode_string_names_user` may be defined to add names for additional keycodes. For example, supposing keymap.c defines `MYMACRO1` and `MYMACRO2` as custom keycodes, the following adds their names:
Optionally, names for additional keycodes may be defined. To do this, define `KEYCODE_STRING_NAMES_USER` in config.h:
```c
const keycode_string_name_t *keycode_string_names_user =
(keycode_string_name_t []){
#define KEYCODE_STRING_NAMES_USER
```

Then define `keycode_string_names_user` with the names for the additional keycodes. For example, supposing keymap.c defines `MYMACRO1` and `MYMACRO2` as custom keycodes, the following adds their names:

```c
const keycode_string_name_t keycode_string_names_user[] = {
KEYCODE_STRING_NAME(MYMACRO1),
KEYCODE_STRING_NAME(MYMACRO2),
KEYCODE_STRING_NAMES_END // End of table sentinel.
};
```

Similarly, `keycode_string_names_kb` may be defined to add names at the keyboard level.
Similarly at the keyboard level, names for additional keycodes may be added by defining `KEYCODE_STRING_NAMES_KB` in config.h and defining keycode names in `keycode_string_names_kb`.

# Tracing Variables {#tracing-variables}

Expand Down
43 changes: 27 additions & 16 deletions quantum/keycode_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
#include <string.h>
#include "bitwise.h"
#include "keycode.h"
#include "keymap_introspection.h"
#include "progmem.h"
#include "quantum_keycodes.h"
#include "util.h"

typedef int_fast8_t index_t;

Expand Down Expand Up @@ -89,16 +91,16 @@ static const keycode_string_name_t keycode_names[] = {
KEYCODE_STRING_NAME(QK_LLCK),
#endif // LAYER_LOCK_ENABLE
KEYCODE_STRING_NAME(DB_TOGG),
KEYCODE_STRING_NAMES_END
};
// clang-format on

__attribute__((weak)) const keycode_string_name_t empty_table[] = {KEYCODE_STRING_NAMES_END};
static const keycode_string_name_t* keycode_names_get(uint16_t i) {
if (i >= ARRAY_SIZE(keycode_names)) {
return NULL;
}
return &keycode_names[i];
}

/** Users can override this to define names of additional keycodes. */
__attribute__((weak)) const keycode_string_name_t* keycode_string_names_user = empty_table;
/** Keyboard vendors can override this to define names of additional keycodes. */
__attribute__((weak)) const keycode_string_name_t* keycode_string_names_kb = empty_table;
/** Names of the 4 mods on each hand. */
static const char* mod_names[4] = {PSTR("CTL"), PSTR("SFT"), PSTR("ALT"), PSTR("GUI")};
/** Internal buffer for holding a stringified keycode. */
Expand All @@ -107,17 +109,21 @@ static char buffer[32];
static index_t buffer_len;

/**
* @brief Finds the name of a keycode in `table` or returns NULL.
* @brief Finds the name of a keycode in table or returns NULL.
*
* The last entry of the table must be `KEYCODE_STRING_NAMES_END`.
* The function arg `table_get(i)` is called as `table_get(0)`, `table_get(1)`,
* `table_get(2)`, etc., until it returns NULL or once the keycode is found.
*
* @param table A table of keycode_string_name_t to be searched.
* @param table_get Function to get the table entries.
* @return Name string for the keycode, or NULL if not found.
*/
static const char* find_keycode_name(const keycode_string_name_t* table, uint16_t keycode) {
for (; table->keycode; ++table) {
if (table->keycode == keycode) {
return table->name;
static const char* find_keycode_name(const keycode_string_name_t* (*table_get)(uint16_t), uint16_t keycode) {
for (uint16_t i = 0;; ++i) {
const keycode_string_name_t* entry = table_get(i);
if (entry == NULL) {
break;
} else if (entry->keycode == keycode) {
return entry->name;
}
}
return NULL;
Expand Down Expand Up @@ -206,19 +212,24 @@ static void append_unary_keycode(const char* name, const char* param) {

/** Stringifies `keycode` and appends it to `buffer`. */
static void append_keycode(uint16_t keycode) {
const char* keycode_name;
#ifdef KEYCODE_STRING_NAMES_USER
// In case there is overlap among tables, search `keycode_string_names_user`
// first so that it takes precedence.
const char* keycode_name = find_keycode_name(keycode_string_names_user, keycode);
keycode_name = find_keycode_name(keycode_string_names_user_get, keycode);
if (keycode_name) {
append_P(keycode_name);
return;
}
keycode_name = find_keycode_name(keycode_string_names_kb, keycode);
#endif // KEYCODE_STRING_NAMES_USER
#ifdef KEYCODE_STRING_NAMES_KB
keycode_name = find_keycode_name(keycode_string_names_kb_get, keycode);
if (keycode_name) {
append_P(keycode_name);
return;
}
keycode_name = find_keycode_name(keycode_names, keycode);
#endif // KEYCODE_STRING_NAMES_KB
keycode_name = find_keycode_name(keycode_names_get, keycode);
if (keycode_name) {
append_P(keycode_name);
return;
Expand Down
13 changes: 6 additions & 7 deletions quantum/keycode_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@
const char* get_keycode_string(uint16_t keycode);

/** Defines a human-readable name for a keycode. */
typedef struct {
typedef struct keycode_string_name_t {
uint16_t keycode;
const char* name;
} keycode_string_name_t;
// typedef struct keycode_string_name_t keycode_string_name_t;

/**
* @brief Names for additional keycodes for `get_keycode_string()`.
Expand All @@ -90,16 +91,14 @@ typedef struct {
* The above defines names for `MYMACRO1` and `MYMACRO2`, and overrides
* `KC_EXLM` to format as "KC_EXLM" instead of the default "S(KC_1)".
*/
extern const keycode_string_name_t* keycode_string_names_user;
// extern const keycode_string_name_t* keycode_string_names_user;
/** Same as `keycode_string_names_user`, but for use at the keyboard level. */
extern const keycode_string_name_t* keycode_string_names_kb;
// extern const keycode_string_name_t* keycode_string_names_kb;

/** Helper to define a keycode_string_name_t. */
# define KEYCODE_STRING_NAME(kc) \
{ (kc), PSTR(#kc) }
# define KEYCODE_STRING_NAME(kc) {(kc), PSTR(#kc)}
/** Makes end-of-table sentinel for a table of keycode_string_name_t. */
# define KEYCODE_STRING_NAMES_END \
{ 0, NULL }
# define KEYCODE_STRING_NAMES_END {0, NULL}

#else

Expand Down
33 changes: 33 additions & 0 deletions quantum/keymap_introspection.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,36 @@ __attribute__((weak)) const key_override_t* key_override_get(uint16_t key_overri
}

#endif // defined(KEY_OVERRIDE_ENABLE)

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Keycode String

#if defined(KEYCODE_STRING_ENABLE)

# if defined(KEYCODE_STRING_NAMES_USER)
const keycode_string_name_t* keycode_string_names_user_get_raw(uint16_t i) {
if (i >= ARRAY_SIZE(keycode_string_names_user)) {
return NULL;
}
return (const keycode_string_name_t*)&keycode_string_names_user[i];
}

__attribute__((weak)) const keycode_string_name_t* keycode_string_names_user_get(uint16_t i) {
return keycode_string_names_user_get_raw(i);
}
# endif // defined(KEYCODE_STRING_NAMES_USER)

# if defined(KEYCODE_STRING_NAMES_KB)
const keycode_string_name_t* keycode_string_names_kb_get_raw(uint16_t i) {
if (i >= ARRAY_SIZE(keycode_string_names_kb)) {
return NULL;
}
return (const keycode_string_name_t*)&keycode_string_names_kb[i];
}

__attribute__((weak)) const keycode_string_name_t* keycode_string_names_kb_get(uint16_t i) {
return keycode_string_names_kb_get_raw(i);
}
# endif // defined(KEYCODE_STRING_NAMES_KB)

#endif // defined(KEYCODE_STRING_ENABLE)
24 changes: 24 additions & 0 deletions quantum/keymap_introspection.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,27 @@ const key_override_t* key_override_get_raw(uint16_t key_override_idx);
const key_override_t* key_override_get(uint16_t key_override_idx);

#endif // defined(KEY_OVERRIDE_ENABLE)

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Keycode String

#if defined(KEYCODE_STRING_ENABLE)

struct keycode_string_name_t;
typedef struct keycode_string_name_t keycode_string_name_t;

# if defined(KEYCODE_STRING_NAMES_USER)
// Get the user-level keycode names, stored in firmware.
const keycode_string_name_t* keycode_string_names_user_get_raw(uint16_t i);
// Get the user-level keycode names, potentially stored dynamically.
const keycode_string_name_t* keycode_string_names_user_get(uint16_t i);
# endif // defined(KEYCODE_STRING_NAMES_USER)

# if defined(KEYCODE_STRING_NAMES_KB)
// Get the kb-level keycode names, stored in firmware.
const keycode_string_name_t* keycode_string_names_kb_get_raw(uint16_t i);
// Get the kb-level keycode names, potentially stored dynamically.
const keycode_string_name_t* keycode_string_names_kb_get(uint16_t i);
# endif // defined(KEYCODE_STRING_NAMES_KB)

#endif // defined(KEYCODE_STRING_ENABLE)
4 changes: 4 additions & 0 deletions tests/keycode_string/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@
#pragma once

#include "test_common.h"

#define KEYCODE_STRING_NAMES_USER
#define KEYCODE_STRING_NAMES_KB

1 change: 1 addition & 0 deletions tests/keycode_string/test.mk
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
KEYCODE_STRING_ENABLE = yes
EXTRAKEY_ENABLE = yes
MOUSEKEY_ENABLE = yes
INTROSPECTION_KEYMAP_C = test_keymap.c
18 changes: 1 addition & 17 deletions tests/keycode_string/test_keycode_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,7 @@
#include <iostream>

#include "test_common.hpp"

enum {
MYMACRO1 = SAFE_RANGE,
MYMACRO2,
};

// clang-format off
extern "C" const keycode_string_name_t *keycode_string_names_kb = (keycode_string_name_t []){
KEYCODE_STRING_NAME(MYMACRO1),
KEYCODE_STRING_NAMES_END // End of table sentinel.
};
extern "C" const keycode_string_name_t *keycode_string_names_user = (keycode_string_name_t []){
KEYCODE_STRING_NAME(MYMACRO2),
KEYCODE_STRING_NAME(KC_EXLM),
KEYCODE_STRING_NAMES_END // End of table sentinel.
};
// clang-format on
#include "test_keymap.h"

class KeycodeStringTest : public TestFixture {};

Expand Down
27 changes: 27 additions & 0 deletions tests/keycode_string/test_keymap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "test_keymap.h"
#include "quantum.h"

// clang-format off
const keycode_string_name_t keycode_string_names_kb[] = {
KEYCODE_STRING_NAME(MYMACRO1),
};

const keycode_string_name_t keycode_string_names_user[] = {
KEYCODE_STRING_NAME(MYMACRO2),
KEYCODE_STRING_NAME(KC_EXLM),
};
// clang-format on
20 changes: 20 additions & 0 deletions tests/keycode_string/test_keymap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

enum {
MYMACRO1 = SAFE_RANGE,
MYMACRO2,
};

0 comments on commit 8eaf67d

Please sign in to comment.