Attempt at simplifying interface.

This commit is contained in:
Pascal Getreuer 2025-01-21 23:55:16 -08:00
parent 45f573f335
commit 8eaf67de76
10 changed files with 152 additions and 45 deletions

View File

@ -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. 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 ```c
const keycode_string_name_t *keycode_string_names_user = #define KEYCODE_STRING_NAMES_USER
(keycode_string_name_t []){ ```
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(MYMACRO1),
KEYCODE_STRING_NAME(MYMACRO2), 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} # Tracing Variables {#tracing-variables}

View File

@ -17,8 +17,10 @@
#include <string.h> #include <string.h>
#include "bitwise.h" #include "bitwise.h"
#include "keycode.h" #include "keycode.h"
#include "keymap_introspection.h"
#include "progmem.h" #include "progmem.h"
#include "quantum_keycodes.h" #include "quantum_keycodes.h"
#include "util.h"
typedef int_fast8_t index_t; typedef int_fast8_t index_t;
@ -89,16 +91,16 @@ static const keycode_string_name_t keycode_names[] = {
KEYCODE_STRING_NAME(QK_LLCK), KEYCODE_STRING_NAME(QK_LLCK),
#endif // LAYER_LOCK_ENABLE #endif // LAYER_LOCK_ENABLE
KEYCODE_STRING_NAME(DB_TOGG), KEYCODE_STRING_NAME(DB_TOGG),
KEYCODE_STRING_NAMES_END
}; };
// clang-format on // 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. */ /** Names of the 4 mods on each hand. */
static const char* mod_names[4] = {PSTR("CTL"), PSTR("SFT"), PSTR("ALT"), PSTR("GUI")}; static const char* mod_names[4] = {PSTR("CTL"), PSTR("SFT"), PSTR("ALT"), PSTR("GUI")};
/** Internal buffer for holding a stringified keycode. */ /** Internal buffer for holding a stringified keycode. */
@ -107,17 +109,21 @@ static char buffer[32];
static index_t buffer_len; 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. * @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) { static const char* find_keycode_name(const keycode_string_name_t* (*table_get)(uint16_t), uint16_t keycode) {
for (; table->keycode; ++table) { for (uint16_t i = 0;; ++i) {
if (table->keycode == keycode) { const keycode_string_name_t* entry = table_get(i);
return table->name; if (entry == NULL) {
break;
} else if (entry->keycode == keycode) {
return entry->name;
} }
} }
return NULL; return NULL;
@ -206,19 +212,24 @@ static void append_unary_keycode(const char* name, const char* param) {
/** Stringifies `keycode` and appends it to `buffer`. */ /** Stringifies `keycode` and appends it to `buffer`. */
static void append_keycode(uint16_t keycode) { 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` // In case there is overlap among tables, search `keycode_string_names_user`
// first so that it takes precedence. // 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) { if (keycode_name) {
append_P(keycode_name); append_P(keycode_name);
return; 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) { if (keycode_name) {
append_P(keycode_name); append_P(keycode_name);
return; 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) { if (keycode_name) {
append_P(keycode_name); append_P(keycode_name);
return; return;

View File

@ -65,10 +65,11 @@
const char* get_keycode_string(uint16_t keycode); const char* get_keycode_string(uint16_t keycode);
/** Defines a human-readable name for a keycode. */ /** Defines a human-readable name for a keycode. */
typedef struct { typedef struct keycode_string_name_t {
uint16_t keycode; uint16_t keycode;
const char* name; const char* name;
} keycode_string_name_t; } keycode_string_name_t;
// typedef struct keycode_string_name_t keycode_string_name_t;
/** /**
* @brief Names for additional keycodes for `get_keycode_string()`. * @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 * The above defines names for `MYMACRO1` and `MYMACRO2`, and overrides
* `KC_EXLM` to format as "KC_EXLM" instead of the default "S(KC_1)". * `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. */ /** 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. */ /** Helper to define a keycode_string_name_t. */
# define KEYCODE_STRING_NAME(kc) \ # define KEYCODE_STRING_NAME(kc) {(kc), PSTR(#kc)}
{ (kc), PSTR(#kc) }
/** Makes end-of-table sentinel for a table of keycode_string_name_t. */ /** Makes end-of-table sentinel for a table of keycode_string_name_t. */
# define KEYCODE_STRING_NAMES_END \ # define KEYCODE_STRING_NAMES_END {0, NULL}
{ 0, NULL }
#else #else

View File

@ -171,3 +171,36 @@ __attribute__((weak)) const key_override_t* key_override_get(uint16_t key_overri
} }
#endif // defined(KEY_OVERRIDE_ENABLE) #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)

View File

@ -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); const key_override_t* key_override_get(uint16_t key_override_idx);
#endif // defined(KEY_OVERRIDE_ENABLE) #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)

View File

@ -17,3 +17,7 @@
#pragma once #pragma once
#include "test_common.h" #include "test_common.h"
#define KEYCODE_STRING_NAMES_USER
#define KEYCODE_STRING_NAMES_KB

View File

@ -15,3 +15,4 @@
KEYCODE_STRING_ENABLE = yes KEYCODE_STRING_ENABLE = yes
EXTRAKEY_ENABLE = yes EXTRAKEY_ENABLE = yes
MOUSEKEY_ENABLE = yes MOUSEKEY_ENABLE = yes
INTROSPECTION_KEYMAP_C = test_keymap.c

View File

@ -15,23 +15,7 @@
#include <iostream> #include <iostream>
#include "test_common.hpp" #include "test_common.hpp"
#include "test_keymap.h"
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
class KeycodeStringTest : public TestFixture {}; class KeycodeStringTest : public TestFixture {};

View File

@ -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

View File

@ -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,
};