diff --git a/docs/unit_testing.md b/docs/unit_testing.md index a404fc22473..3d0bce19e70 100644 --- a/docs/unit_testing.md +++ b/docs/unit_testing.md @@ -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} diff --git a/quantum/keycode_string.c b/quantum/keycode_string.c index 0ba497dbcbb..ef2cedccf7c 100644 --- a/quantum/keycode_string.c +++ b/quantum/keycode_string.c @@ -17,8 +17,10 @@ #include #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; @@ -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. */ @@ -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; @@ -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; diff --git a/quantum/keycode_string.h b/quantum/keycode_string.h index 5f4d7c2866c..1b4280a9a6c 100644 --- a/quantum/keycode_string.h +++ b/quantum/keycode_string.h @@ -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()`. @@ -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 diff --git a/quantum/keymap_introspection.c b/quantum/keymap_introspection.c index 236b54ce980..bc28e41bbd3 100644 --- a/quantum/keymap_introspection.c +++ b/quantum/keymap_introspection.c @@ -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) diff --git a/quantum/keymap_introspection.h b/quantum/keymap_introspection.h index 719825c674d..897061d2ee0 100644 --- a/quantum/keymap_introspection.h +++ b/quantum/keymap_introspection.h @@ -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) diff --git a/tests/keycode_string/config.h b/tests/keycode_string/config.h index 7fc76d7c2e7..a0a647108a2 100644 --- a/tests/keycode_string/config.h +++ b/tests/keycode_string/config.h @@ -17,3 +17,7 @@ #pragma once #include "test_common.h" + +#define KEYCODE_STRING_NAMES_USER +#define KEYCODE_STRING_NAMES_KB + diff --git a/tests/keycode_string/test.mk b/tests/keycode_string/test.mk index 23c1f04e2bc..e8524ef3101 100644 --- a/tests/keycode_string/test.mk +++ b/tests/keycode_string/test.mk @@ -15,3 +15,4 @@ KEYCODE_STRING_ENABLE = yes EXTRAKEY_ENABLE = yes MOUSEKEY_ENABLE = yes +INTROSPECTION_KEYMAP_C = test_keymap.c diff --git a/tests/keycode_string/test_keycode_string.cpp b/tests/keycode_string/test_keycode_string.cpp index ac90d7854cf..b718899b1d0 100644 --- a/tests/keycode_string/test_keycode_string.cpp +++ b/tests/keycode_string/test_keycode_string.cpp @@ -15,23 +15,7 @@ #include #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 {}; diff --git a/tests/keycode_string/test_keymap.c b/tests/keycode_string/test_keymap.c new file mode 100644 index 00000000000..841a97f9862 --- /dev/null +++ b/tests/keycode_string/test_keymap.c @@ -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 diff --git a/tests/keycode_string/test_keymap.h b/tests/keycode_string/test_keymap.h new file mode 100644 index 00000000000..d50f0f64e30 --- /dev/null +++ b/tests/keycode_string/test_keymap.h @@ -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, +};