diff options
| author | Pascal Getreuer <50221757+getreuer@users.noreply.github.com> | 2025-03-19 12:45:56 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-03-19 20:45:56 +0100 |
| commit | 3484f0a0dffd264ae3bef70ce2a8961489c16f50 (patch) | |
| tree | 6c9611c3510dc970fd586ee7bc61d9e0265bfd2d /quantum/keycode_string.c | |
| parent | 40a446a0ec1f2f7cb8c52538bfefb1b26c951dd4 (diff) | |
[Core] get_keycode_string(): function to format keycodes as strings, for more readable debug logging. (#24787)
* keycode_string(): Format keycodes as strings.
This adds the `keycode_string()` function described in
https://getreuer.info/posts/keyboards/keycode-string/index.html
as a core feature.
* Fix formatting.
* keycode_string review revisions.
* Rename keycode_string() -> get_keycode_string() for consistency with
existing string utils like get_u8_str().
* Revise custom keycode names with separate _user and _kb tables.
* Correct indent in builddefs/generic_features.mk.
Co-authored-by: Ryan <fauxpark@gmail.com>
* Add KC_NUHS, KC_NUBS, and KC_CAPS.
* Fix linking error with custom names.
* Attempt at simplifying interface.
* Formatting fix.
* Several fixes and revisions.
* Don't use PSTR in KEYCODE_STRING_NAME, since this fails to build on
AVR. Store custom names in RAM.
* Revise the internal table of common keycode names to use its own
storage representation, still in PROGMEM, and now more efficiently
stored flat in 8 bytes per entry.
* Support Swap Hands keycodes and a few other keycodes.
* Revert "Formatting fix."
This reverts commit 2a2771068c7ee545ffac4103aa07e847a9ec3816.
* Revert "Attempt at simplifying interface."
This reverts commit 8eaf67de76e75bc92d106a8b0decc893fbc65fa5.
* Simplify custom names API by sigprof's suggestion.
* Support more keycodes.
* Add QK_LOCK keycode.
* Add Secure keycodes.
* Add Joystick keycodes.
* Add Programmable Button keycodes.
* Add macro MC_ keycodes.
* For remaining keys in known code ranges, stringify them as
"QK_<feature>+<number>". For instance, "QK_MIDI+7".
* Bug fix and a few improvements.
* Fix missing right-hand bit when displaying 5-bit mods numerically.
* Support KC_HYPR, KC_MEH, HYPR_T(kc), MEH_T(kc).
* Exclude one-shot keycodes when NO_ACTION_ONESHOT is defined.
---------
Co-authored-by: Ryan <fauxpark@gmail.com>
Diffstat (limited to 'quantum/keycode_string.c')
| -rw-r--r-- | quantum/keycode_string.c | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/quantum/keycode_string.c b/quantum/keycode_string.c new file mode 100644 index 0000000000..18044f2ef6 --- /dev/null +++ b/quantum/keycode_string.c | |||
| @@ -0,0 +1,564 @@ | |||
| 1 | // Copyright 2024-2025 Google LLC | ||
| 2 | // | ||
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | // you may not use this file except in compliance with the License. | ||
| 5 | // You may obtain a copy of the License at | ||
| 6 | // | ||
| 7 | // https://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | // | ||
| 9 | // Unless required by applicable law or agreed to in writing, software | ||
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | // See the License for the specific language governing permissions and | ||
| 13 | // limitations under the License. | ||
| 14 | |||
| 15 | #include "keycode_string.h" | ||
| 16 | |||
| 17 | #include <string.h> | ||
| 18 | #include "bitwise.h" | ||
| 19 | #include "keycode.h" | ||
| 20 | #include "progmem.h" | ||
| 21 | #include "quantum_keycodes.h" | ||
| 22 | #include "util.h" | ||
| 23 | |||
| 24 | typedef int_fast8_t index_t; | ||
| 25 | |||
| 26 | // clang-format off | ||
| 27 | /** Packs a 7-char keycode name, ignoring the third char, as 3 words. */ | ||
| 28 | #define KEYCODE_NAME7(c0, c1, unused_c2, c3, c4, c5, c6) \ | ||
| 29 | ((uint16_t)c0) | (((uint16_t)c1) << 8), \ | ||
| 30 | ((uint16_t)c3) | (((uint16_t)c4) << 8), \ | ||
| 31 | ((uint16_t)c5) | (((uint16_t)c6) << 8) | ||
| 32 | |||
| 33 | /** | ||
| 34 | * @brief Names of some common keycodes. | ||
| 35 | * | ||
| 36 | * Each (keycode, name) entry is stored flat in 8 bytes in PROGMEM. Names in | ||
| 37 | * this table must be at most 7 chars long and have an underscore '_' for the | ||
| 38 | * third char. This underscore is assumed and not actually stored. | ||
| 39 | * | ||
| 40 | * To save memory, feature-specific key entries are ifdef'd to include them only | ||
| 41 | * when their feature is enabled. | ||
| 42 | */ | ||
| 43 | static const uint16_t common_names[] PROGMEM = { | ||
| 44 | KC_TRNS, KEYCODE_NAME7('K', 'C', '_', 'T', 'R', 'N', 'S'), | ||
| 45 | KC_ENT , KEYCODE_NAME7('K', 'C', '_', 'E', 'N', 'T', 0 ), | ||
| 46 | KC_ESC , KEYCODE_NAME7('K', 'C', '_', 'E', 'S', 'C', 0 ), | ||
| 47 | KC_BSPC, KEYCODE_NAME7('K', 'C', '_', 'B', 'S', 'P', 'C'), | ||
| 48 | KC_TAB , KEYCODE_NAME7('K', 'C', '_', 'T', 'A', 'B', 0 ), | ||
| 49 | KC_SPC , KEYCODE_NAME7('K', 'C', '_', 'S', 'P', 'C', 0 ), | ||
| 50 | KC_MINS, KEYCODE_NAME7('K', 'C', '_', 'M', 'I', 'N', 'S'), | ||
| 51 | KC_EQL , KEYCODE_NAME7('K', 'C', '_', 'E', 'Q', 'L', 0 ), | ||
| 52 | KC_LBRC, KEYCODE_NAME7('K', 'C', '_', 'L', 'B', 'R', 'C'), | ||
| 53 | KC_RBRC, KEYCODE_NAME7('K', 'C', '_', 'R', 'B', 'R', 'C'), | ||
| 54 | KC_BSLS, KEYCODE_NAME7('K', 'C', '_', 'B', 'S', 'L', 'S'), | ||
| 55 | KC_NUHS, KEYCODE_NAME7('K', 'C', '_', 'N', 'U', 'H', 'S'), | ||
| 56 | KC_SCLN, KEYCODE_NAME7('K', 'C', '_', 'S', 'C', 'L', 'N'), | ||
| 57 | KC_QUOT, KEYCODE_NAME7('K', 'C', '_', 'Q', 'U', 'O', 'T'), | ||
| 58 | KC_GRV , KEYCODE_NAME7('K', 'C', '_', 'G', 'R', 'V', 0 ), | ||
| 59 | KC_COMM, KEYCODE_NAME7('K', 'C', '_', 'C', 'O', 'M', 'M'), | ||
| 60 | KC_DOT , KEYCODE_NAME7('K', 'C', '_', 'D', 'O', 'T', 0 ), | ||
| 61 | KC_SLSH, KEYCODE_NAME7('K', 'C', '_', 'S', 'L', 'S', 'H'), | ||
| 62 | KC_CAPS, KEYCODE_NAME7('K', 'C', '_', 'C', 'A', 'P', 'S'), | ||
| 63 | KC_PSCR, KEYCODE_NAME7('K', 'C', '_', 'P', 'S', 'C', 'R'), | ||
| 64 | KC_PAUS, KEYCODE_NAME7('K', 'C', '_', 'P', 'A', 'U', 'S'), | ||
| 65 | KC_INS , KEYCODE_NAME7('K', 'C', '_', 'I', 'N', 'S', 0 ), | ||
| 66 | KC_HOME, KEYCODE_NAME7('K', 'C', '_', 'H', 'O', 'M', 'E'), | ||
| 67 | KC_PGUP, KEYCODE_NAME7('K', 'C', '_', 'P', 'G', 'U', 'P'), | ||
| 68 | KC_DEL , KEYCODE_NAME7('K', 'C', '_', 'D', 'E', 'L', 0 ), | ||
| 69 | KC_END , KEYCODE_NAME7('K', 'C', '_', 'E', 'N', 'D', 0 ), | ||
| 70 | KC_PGDN, KEYCODE_NAME7('K', 'C', '_', 'P', 'G', 'D', 'N'), | ||
| 71 | KC_RGHT, KEYCODE_NAME7('K', 'C', '_', 'R', 'G', 'H', 'T'), | ||
| 72 | KC_LEFT, KEYCODE_NAME7('K', 'C', '_', 'L', 'E', 'F', 'T'), | ||
| 73 | KC_DOWN, KEYCODE_NAME7('K', 'C', '_', 'D', 'O', 'W', 'N'), | ||
| 74 | KC_UP , KEYCODE_NAME7('K', 'C', '_', 'U', 'P', 0 , 0 ), | ||
| 75 | KC_NUBS, KEYCODE_NAME7('K', 'C', '_', 'N', 'U', 'B', 'S'), | ||
| 76 | KC_HYPR, KEYCODE_NAME7('K', 'C', '_', 'H', 'Y', 'P', 'R'), | ||
| 77 | KC_MEH , KEYCODE_NAME7('K', 'C', '_', 'M', 'E', 'H', 0 ), | ||
| 78 | #ifdef EXTRAKEY_ENABLE | ||
| 79 | KC_WHOM, KEYCODE_NAME7('K', 'C', '_', 'W', 'H', 'O', 'M'), | ||
| 80 | KC_WBAK, KEYCODE_NAME7('K', 'C', '_', 'W', 'B', 'A', 'K'), | ||
| 81 | KC_WFWD, KEYCODE_NAME7('K', 'C', '_', 'W', 'F', 'W', 'D'), | ||
| 82 | KC_WSTP, KEYCODE_NAME7('K', 'C', '_', 'W', 'S', 'T', 'P'), | ||
| 83 | KC_WREF, KEYCODE_NAME7('K', 'C', '_', 'W', 'R', 'E', 'F'), | ||
| 84 | KC_MNXT, KEYCODE_NAME7('K', 'C', '_', 'M', 'N', 'X', 'T'), | ||
| 85 | KC_MPRV, KEYCODE_NAME7('K', 'C', '_', 'M', 'P', 'R', 'V'), | ||
| 86 | KC_MPLY, KEYCODE_NAME7('K', 'C', '_', 'M', 'P', 'L', 'Y'), | ||
| 87 | KC_MUTE, KEYCODE_NAME7('K', 'C', '_', 'M', 'U', 'T', 'E'), | ||
| 88 | KC_VOLU, KEYCODE_NAME7('K', 'C', '_', 'V', 'O', 'L', 'U'), | ||
| 89 | KC_VOLD, KEYCODE_NAME7('K', 'C', '_', 'V', 'O', 'L', 'D'), | ||
| 90 | #endif // EXTRAKEY_ENABLE | ||
| 91 | #ifdef MOUSEKEY_ENABLE | ||
| 92 | MS_LEFT, KEYCODE_NAME7('M', 'S', '_', 'L', 'E', 'F', 'T'), | ||
| 93 | MS_RGHT, KEYCODE_NAME7('M', 'S', '_', 'R', 'G', 'H', 'T'), | ||
| 94 | MS_UP , KEYCODE_NAME7('M', 'S', '_', 'U', 'P', 0 , 0 ), | ||
| 95 | MS_DOWN, KEYCODE_NAME7('M', 'S', '_', 'D', 'O', 'W', 'N'), | ||
| 96 | MS_WHLL, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'L'), | ||
| 97 | MS_WHLR, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'R'), | ||
| 98 | MS_WHLU, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'U'), | ||
| 99 | MS_WHLD, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'D'), | ||
| 100 | #endif // MOUSEKEY_ENABLE | ||
| 101 | #ifdef SWAP_HANDS_ENABLE | ||
| 102 | SH_ON , KEYCODE_NAME7('S', 'H', '_', 'O', 'N', 0 , 0 ), | ||
| 103 | SH_OFF , KEYCODE_NAME7('S', 'H', '_', 'O', 'F', 'F', 0 ), | ||
| 104 | SH_MON , KEYCODE_NAME7('S', 'H', '_', 'M', 'O', 'N', 0 ), | ||
| 105 | SH_MOFF, KEYCODE_NAME7('S', 'H', '_', 'M', 'O', 'F', 'F'), | ||
| 106 | SH_TOGG, KEYCODE_NAME7('S', 'H', '_', 'T', 'O', 'G', 'G'), | ||
| 107 | SH_TT , KEYCODE_NAME7('S', 'H', '_', 'T', 'T', 0 , 0 ), | ||
| 108 | # if !defined(NO_ACTION_ONESHOT) | ||
| 109 | SH_OS , KEYCODE_NAME7('S', 'H', '_', 'O', 'S', 0 , 0 ), | ||
| 110 | # endif // !defined(NO_ACTION_ONESHOT) | ||
| 111 | #endif // SWAP_HANDS_ENABLE | ||
| 112 | #ifdef LEADER_ENABLE | ||
| 113 | QK_LEAD, KEYCODE_NAME7('Q', 'K', '_', 'L', 'E', 'A', 'D'), | ||
| 114 | #endif // LEADER_ENABLE | ||
| 115 | #ifdef KEY_LOCK_ENABLE | ||
| 116 | QK_LOCK, KEYCODE_NAME7('Q', 'K', '_', 'L', 'O', 'C', 'K'), | ||
| 117 | #endif // KEY_LOCK_ENABLE | ||
| 118 | #ifdef TRI_LAYER_ENABLE | ||
| 119 | TL_LOWR, KEYCODE_NAME7('T', 'L', '_', 'L', 'O', 'W', 'R'), | ||
| 120 | TL_UPPR, KEYCODE_NAME7('T', 'L', '_', 'U', 'P', 'P', 'R'), | ||
| 121 | #endif // TRI_LAYER_ENABLE | ||
| 122 | #ifdef GRAVE_ESC_ENABLE | ||
| 123 | QK_GESC, KEYCODE_NAME7('Q', 'K', '_', 'G', 'E', 'S', 'C'), | ||
| 124 | #endif // GRAVE_ESC_ENABLE | ||
| 125 | #ifdef CAPS_WORD_ENABLE | ||
| 126 | CW_TOGG, KEYCODE_NAME7('C', 'W', '_', 'T', 'O', 'G', 'G'), | ||
| 127 | #endif // CAPS_WORD_ENABLE | ||
| 128 | #ifdef SECURE_ENABLE | ||
| 129 | SE_LOCK, KEYCODE_NAME7('S', 'E', '_', 'L', 'O', 'C', 'K'), | ||
| 130 | SE_UNLK, KEYCODE_NAME7('S', 'E', '_', 'U', 'N', 'L', 'K'), | ||
| 131 | SE_TOGG, KEYCODE_NAME7('S', 'E', '_', 'T', 'O', 'G', 'G'), | ||
| 132 | SE_REQ , KEYCODE_NAME7('S', 'E', '_', 'R', 'E', 'Q', 0 ), | ||
| 133 | #endif // SECURE_ENABLE | ||
| 134 | #ifdef LAYER_LOCK_ENABLE | ||
| 135 | QK_LLCK, KEYCODE_NAME7('Q', 'K', '_', 'L', 'L', 'C', 'K'), | ||
| 136 | #endif // LAYER_LOCK_ENABLE | ||
| 137 | EE_CLR , KEYCODE_NAME7('E', 'E', '_', 'C', 'L', 'R', 0 ), | ||
| 138 | QK_BOOT, KEYCODE_NAME7('Q', 'K', '_', 'B', 'O', 'O', 'T'), | ||
| 139 | DB_TOGG, KEYCODE_NAME7('D', 'B', '_', 'T', 'O', 'G', 'G'), | ||
| 140 | }; | ||
| 141 | // clang-format on | ||
| 142 | |||
| 143 | /** Users can override this to define names of additional keycodes. */ | ||
| 144 | __attribute__((weak)) const keycode_string_name_t* keycode_string_names_data_user = NULL; | ||
| 145 | __attribute__((weak)) uint16_t keycode_string_names_size_user = 0; | ||
| 146 | /** Keyboard vendors can override this to define names of additional keycodes. */ | ||
| 147 | __attribute__((weak)) const keycode_string_name_t* keycode_string_names_data_kb = NULL; | ||
| 148 | __attribute__((weak)) uint16_t keycode_string_names_size_kb = 0; | ||
| 149 | /** Names of the 4 mods on each hand. */ | ||
| 150 | static const char mod_names[] PROGMEM = "CTL\0SFT\0ALT\0GUI"; | ||
| 151 | /** Internal buffer for holding a stringified keycode. */ | ||
| 152 | static char buffer[32]; | ||
| 153 | #define BUFFER_MAX_LEN (sizeof(buffer) - 1) | ||
| 154 | static index_t buffer_len; | ||
| 155 | |||
| 156 | /** Finds the name of a keycode in `common_names` or returns NULL. */ | ||
| 157 | static const char* search_common_names(uint16_t keycode) { | ||
| 158 | static uint8_t buffer[8]; | ||
| 159 | |||
| 160 | for (int_fast16_t offset = 0; offset < ARRAY_SIZE(common_names); offset += 4) { | ||
| 161 | if (keycode == pgm_read_word(common_names + offset)) { | ||
| 162 | const uint16_t w0 = pgm_read_word(common_names + offset + 1); | ||
| 163 | const uint16_t w1 = pgm_read_word(common_names + offset + 2); | ||
| 164 | const uint16_t w2 = pgm_read_word(common_names + offset + 3); | ||
| 165 | buffer[0] = (uint8_t)w0; | ||
| 166 | buffer[1] = (uint8_t)(w0 >> 8); | ||
| 167 | buffer[2] = '_'; | ||
| 168 | buffer[3] = (uint8_t)w1; | ||
| 169 | buffer[4] = (uint8_t)(w1 >> 8); | ||
| 170 | buffer[5] = (uint8_t)w2; | ||
| 171 | buffer[6] = (uint8_t)(w2 >> 8); | ||
| 172 | buffer[7] = 0; | ||
| 173 | return (const char*)buffer; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | return NULL; | ||
| 178 | } | ||
| 179 | |||
| 180 | /** | ||
| 181 | * @brief Finds the name of a keycode in table or returns NULL. | ||
| 182 | * | ||
| 183 | * @param data Pointer to table to be searched. | ||
| 184 | * @param size Numer of entries in the table. | ||
| 185 | * @return Name string for the keycode, or NULL if not found. | ||
| 186 | */ | ||
| 187 | static const char* search_table(const keycode_string_name_t* data, uint16_t size, uint16_t keycode) { | ||
| 188 | if (data != NULL) { | ||
| 189 | for (uint16_t i = 0; i < size; ++i) { | ||
| 190 | if (data[i].keycode == keycode) { | ||
| 191 | return data[i].name; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | return NULL; | ||
| 196 | } | ||
| 197 | |||
| 198 | /** Formats `number` in `base`, either 10 or 16. */ | ||
| 199 | static char* number_string(uint16_t number, int8_t base) { | ||
| 200 | static char result[7]; | ||
| 201 | result[sizeof(result) - 1] = '\0'; | ||
| 202 | index_t i = sizeof(result) - 1; | ||
| 203 | do { | ||
| 204 | const uint8_t digit = number % base; | ||
| 205 | number /= base; | ||
| 206 | result[--i] = (digit < 10) ? (char)(digit + UINT8_C('0')) : (char)(digit + (UINT8_C('A') - 10)); | ||
| 207 | } while (number > 0 && i > 0); | ||
| 208 | |||
| 209 | if (base == 16 && i >= 2) { | ||
| 210 | result[--i] = 'x'; | ||
| 211 | result[--i] = '0'; | ||
| 212 | } | ||
| 213 | return result + i; | ||
| 214 | } | ||
| 215 | |||
| 216 | /** Appends `str` to `buffer`, truncating if the result would overflow. */ | ||
| 217 | static void append(const char* str) { | ||
| 218 | char* dest = buffer + buffer_len; | ||
| 219 | index_t i; | ||
| 220 | for (i = 0; buffer_len + i < BUFFER_MAX_LEN && str[i]; ++i) { | ||
| 221 | dest[i] = str[i]; | ||
| 222 | } | ||
| 223 | buffer_len += i; | ||
| 224 | buffer[buffer_len] = '\0'; | ||
| 225 | } | ||
| 226 | |||
| 227 | /** Same as append(), but where `str` is a PROGMEM string. */ | ||
| 228 | static void append_P(const char* str) { | ||
| 229 | char* dest = buffer + buffer_len; | ||
| 230 | index_t i; | ||
| 231 | for (i = 0; buffer_len + i < BUFFER_MAX_LEN; ++i) { | ||
| 232 | const char c = pgm_read_byte(&str[i]); | ||
| 233 | if (c == '\0') { | ||
| 234 | break; | ||
| 235 | } | ||
| 236 | dest[i] = c; | ||
| 237 | } | ||
| 238 | buffer_len += i; | ||
| 239 | buffer[buffer_len] = '\0'; | ||
| 240 | } | ||
| 241 | |||
| 242 | /** Appends a single char to `buffer` if there is space. */ | ||
| 243 | static void append_char(char c) { | ||
| 244 | if (buffer_len < BUFFER_MAX_LEN) { | ||
| 245 | buffer[buffer_len] = c; | ||
| 246 | buffer[++buffer_len] = '\0'; | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | /** Formats `number` in `base`, either 10 or 16, and appends it to `buffer`. */ | ||
| 251 | static void append_number(uint16_t number, int8_t base) { | ||
| 252 | append(number_string(number, base)); | ||
| 253 | } | ||
| 254 | |||
| 255 | /** Stringifies 5-bit mods and appends it to `buffer`. */ | ||
| 256 | static void append_5_bit_mods(uint8_t mods) { | ||
| 257 | const bool is_rhs = mods > 15; | ||
| 258 | const uint8_t csag = mods & 15; | ||
| 259 | if (csag != 0 && (csag & (csag - 1)) == 0) { // One mod is set. | ||
| 260 | append_P(PSTR("MOD_")); | ||
| 261 | append_char(is_rhs ? 'R' : 'L'); | ||
| 262 | append_P(&mod_names[4 * biton(csag)]); | ||
| 263 | } else { // Fallback: write the mod as a hex value. | ||
| 264 | append_number(mods, 16); | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | /** | ||
| 269 | * @brief Writes a keycode of the format `name` + "(" + `param` + ")". | ||
| 270 | * @note `name` is a PROGMEM string, `param` is not. | ||
| 271 | */ | ||
| 272 | static void append_unary_keycode(const char* name, const char* param) { | ||
| 273 | append_P(name); | ||
| 274 | append_char('('); | ||
| 275 | append(param); | ||
| 276 | append_char(')'); | ||
| 277 | } | ||
| 278 | |||
| 279 | /** | ||
| 280 | * @brief Writes a keycode of the format `name` + `number`. | ||
| 281 | * @note `name` is a PROGMEM string. | ||
| 282 | */ | ||
| 283 | static void append_numbered_keycode(const char* name, uint16_t number) { | ||
| 284 | append_P(name); | ||
| 285 | append_number(number, 10); | ||
| 286 | } | ||
| 287 | |||
| 288 | /** Stringifies `keycode` and appends it to `buffer`. */ | ||
| 289 | static void append_keycode(uint16_t keycode) { | ||
| 290 | // In case there is overlap among tables, search `keycode_string_names_user` | ||
| 291 | // first so that it takes precedence. | ||
| 292 | const char* keycode_name = search_table(keycode_string_names_data_user, keycode_string_names_size_user, keycode); | ||
| 293 | if (keycode_name) { | ||
| 294 | append(keycode_name); | ||
| 295 | return; | ||
| 296 | } | ||
| 297 | keycode_name = search_table(keycode_string_names_data_kb, keycode_string_names_size_kb, keycode); | ||
| 298 | if (keycode_name) { | ||
| 299 | append(keycode_name); | ||
| 300 | return; | ||
| 301 | } | ||
| 302 | keycode_name = search_common_names(keycode); | ||
| 303 | if (keycode_name) { | ||
| 304 | append(keycode_name); | ||
| 305 | return; | ||
| 306 | } | ||
| 307 | |||
| 308 | if (keycode <= 255) { // Basic keycodes. | ||
| 309 | switch (keycode) { | ||
| 310 | // Modifiers KC_LSFT, KC_RCTL, etc. | ||
| 311 | case MODIFIER_KEYCODE_RANGE: { | ||
| 312 | const uint8_t i = keycode - KC_LCTL; | ||
| 313 | const bool is_rhs = i > 3; | ||
| 314 | append_P(PSTR("KC_")); | ||
| 315 | append_char(is_rhs ? 'R' : 'L'); | ||
| 316 | append_P(&mod_names[4 * (i & 3)]); | ||
| 317 | } | ||
| 318 | return; | ||
| 319 | |||
| 320 | // Letters A-Z. | ||
| 321 | case KC_A ... KC_Z: | ||
| 322 | append_P(PSTR("KC_")); | ||
| 323 | append_char((char)(keycode + (UINT8_C('A') - KC_A))); | ||
| 324 | return; | ||
| 325 | |||
| 326 | // Digits 0-9 (NOTE: Unlike the ASCII order, KC_0 comes *after* KC_9.) | ||
| 327 | case KC_1 ... KC_0: | ||
| 328 | append_numbered_keycode(PSTR("KC_"), (keycode - (KC_1 - 1)) % 10); | ||
| 329 | return; | ||
| 330 | |||
| 331 | // Keypad digits. | ||
| 332 | case KC_KP_1 ... KC_KP_0: | ||
| 333 | append_numbered_keycode(PSTR("KC_KP_"), (keycode - (KC_KP_1 - 1)) % 10); | ||
| 334 | return; | ||
| 335 | |||
| 336 | // Function keys. F1-F12 and F13-F24 are coded in separate ranges. | ||
| 337 | case KC_F1 ... KC_F12: | ||
| 338 | append_numbered_keycode(PSTR("KC_F"), keycode - (KC_F1 - 1)); | ||
| 339 | return; | ||
| 340 | |||
| 341 | case KC_F13 ... KC_F24: | ||
| 342 | append_numbered_keycode(PSTR("KC_F"), keycode - (KC_F13 - 13)); | ||
| 343 | return; | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | // clang-format off | ||
| 348 | switch (keycode) { | ||
| 349 | // A modified keycode, like S(KC_1) for Shift + 1 = !. This implementation | ||
| 350 | // only covers modified keycodes where one modifier is applied, e.g. a | ||
| 351 | // Ctrl + Shift + kc or Hyper + kc keycode is not formatted. | ||
| 352 | case QK_MODS ... QK_MODS_MAX: { | ||
| 353 | uint8_t mods = QK_MODS_GET_MODS(keycode); | ||
| 354 | const bool is_rhs = mods > 15; | ||
| 355 | mods &= 15; | ||
| 356 | if (mods != 0 && (mods & (mods - 1)) == 0) { // One mod is set. | ||
| 357 | const char* name = &mod_names[4 * biton(mods)]; | ||
| 358 | if (is_rhs) { | ||
| 359 | append_char('R'); | ||
| 360 | append_P(name); | ||
| 361 | } else { | ||
| 362 | append_char(pgm_read_byte(&name[0])); | ||
| 363 | } | ||
| 364 | append_char('('); | ||
| 365 | append_keycode(QK_MODS_GET_BASIC_KEYCODE(keycode)); | ||
| 366 | append_char(')'); | ||
| 367 | return; | ||
| 368 | } | ||
| 369 | } break; | ||
| 370 | |||
| 371 | #if !defined(NO_ACTION_ONESHOT) | ||
| 372 | case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: // One-shot mod OSM(mod) key. | ||
| 373 | append_P(PSTR("OSM(")); | ||
| 374 | append_5_bit_mods(QK_ONE_SHOT_MOD_GET_MODS(keycode)); | ||
| 375 | append_char(')'); | ||
| 376 | return; | ||
| 377 | #endif // !defined(NO_ACTION_ONESHOT) | ||
| 378 | |||
| 379 | // Various layer switch keys. | ||
| 380 | case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: // Layer-tap LT(layer,kc) key. | ||
| 381 | append_P(PSTR("LT(")); | ||
| 382 | append_number(QK_LAYER_TAP_GET_LAYER(keycode), 10); | ||
| 383 | append_char(','); | ||
| 384 | append_keycode(QK_LAYER_TAP_GET_TAP_KEYCODE(keycode)); | ||
| 385 | append_char(')'); | ||
| 386 | return; | ||
| 387 | |||
| 388 | case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: // LM(layer,mod) key. | ||
| 389 | append_P(PSTR("LM(")); | ||
| 390 | append_number(QK_LAYER_MOD_GET_LAYER(keycode), 10); | ||
| 391 | append_char(','); | ||
| 392 | append_5_bit_mods(QK_LAYER_MOD_GET_MODS(keycode)); | ||
| 393 | append_char(')'); | ||
| 394 | return; | ||
| 395 | |||
| 396 | case QK_TO ... QK_TO_MAX: // TO(layer) key. | ||
| 397 | append_unary_keycode(PSTR("TO"), number_string(QK_TO_GET_LAYER(keycode), 10)); | ||
| 398 | return; | ||
| 399 | |||
| 400 | case QK_MOMENTARY ... QK_MOMENTARY_MAX: // MO(layer) key. | ||
| 401 | append_unary_keycode(PSTR("MO"), number_string(QK_MOMENTARY_GET_LAYER(keycode), 10)); | ||
| 402 | return; | ||
| 403 | |||
| 404 | case QK_DEF_LAYER ... QK_DEF_LAYER_MAX: // DF(layer) key. | ||
| 405 | append_unary_keycode(PSTR("DF"), number_string(QK_DEF_LAYER_GET_LAYER(keycode), 10)); | ||
| 406 | return; | ||
| 407 | |||
| 408 | case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX: // TG(layer) key. | ||
| 409 | append_unary_keycode(PSTR("TG"), number_string(QK_TOGGLE_LAYER_GET_LAYER(keycode), 10)); | ||
| 410 | return; | ||
| 411 | |||
| 412 | #if !defined(NO_ACTION_ONESHOT) | ||
| 413 | case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX: // OSL(layer) key. | ||
| 414 | append_unary_keycode(PSTR("OSL"), number_string(QK_ONE_SHOT_LAYER_GET_LAYER(keycode), 10)); | ||
| 415 | return; | ||
| 416 | #endif // !defined(NO_ACTION_ONESHOT) | ||
| 417 | |||
| 418 | case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: // TT(layer) key. | ||
| 419 | append_unary_keycode(PSTR("TT"), number_string(QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode), 10)); | ||
| 420 | return; | ||
| 421 | |||
| 422 | case QK_PERSISTENT_DEF_LAYER ... QK_PERSISTENT_DEF_LAYER_MAX: // PDF(layer) key. | ||
| 423 | append_unary_keycode(PSTR("PDF"), number_string(QK_PERSISTENT_DEF_LAYER_GET_LAYER(keycode), 10)); | ||
| 424 | return; | ||
| 425 | |||
| 426 | // Mod-tap MT(mod,kc) key. This implementation formats the MT keys where | ||
| 427 | // one modifier is applied. For MT keys with multiple modifiers, the mod | ||
| 428 | // arg is written numerically as a hex code. | ||
| 429 | case QK_MOD_TAP ... QK_MOD_TAP_MAX: { | ||
| 430 | uint8_t mods = QK_MOD_TAP_GET_MODS(keycode); | ||
| 431 | const bool is_rhs = mods > 15; | ||
| 432 | const uint8_t csag = mods & 15; | ||
| 433 | if (csag != 0 && (csag & (csag - 1)) == 0) { // One mod is set. | ||
| 434 | append_char(is_rhs ? 'R' : 'L'); | ||
| 435 | append_P(&mod_names[4 * biton(csag)]); | ||
| 436 | append_P(PSTR("_T(")); | ||
| 437 | } else if (mods == MOD_HYPR) { | ||
| 438 | append_P(PSTR("HYPR_T(")); | ||
| 439 | } else if (mods == MOD_MEH) { | ||
| 440 | append_P(PSTR("MEH_T(")); | ||
| 441 | } else { | ||
| 442 | append_P(PSTR("MT(")); | ||
| 443 | append_number(mods, 16); | ||
| 444 | append_char(','); | ||
| 445 | } | ||
| 446 | append_keycode(QK_MOD_TAP_GET_TAP_KEYCODE(keycode)); | ||
| 447 | append_char(')'); | ||
| 448 | } return; | ||
| 449 | |||
| 450 | case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: // Tap dance TD(i) key. | ||
| 451 | append_unary_keycode(PSTR("TD"), number_string(QK_TAP_DANCE_GET_INDEX(keycode), 10)); | ||
| 452 | return; | ||
| 453 | |||
| 454 | #ifdef UNICODE_ENABLE | ||
| 455 | case QK_UNICODE ... QK_UNICODE_MAX: // Unicode UC(codepoint) key. | ||
| 456 | append_unary_keycode(PSTR("UC"), number_string(QK_UNICODE_GET_CODE_POINT(keycode), 16)); | ||
| 457 | return; | ||
| 458 | #elif defined(UNICODEMAP_ENABLE) | ||
| 459 | case QK_UNICODEMAP ... QK_UNICODEMAP_MAX: // Unicode Map UM(i) key. | ||
| 460 | append_unary_keycode(PSTR("UM"), number_string(QK_UNICODEMAP_GET_INDEX(keycode), 10)); | ||
| 461 | return; | ||
| 462 | |||
| 463 | case QK_UNICODEMAP_PAIR ... QK_UNICODEMAP_PAIR_MAX: { // UP(i,j) key. | ||
| 464 | const uint8_t i = QK_UNICODEMAP_PAIR_GET_UNSHIFTED_INDEX(keycode); | ||
| 465 | const uint8_t j = QK_UNICODEMAP_PAIR_GET_SHIFTED_INDEX(keycode); | ||
| 466 | append_P(PSTR("UP(")); | ||
| 467 | append_number(i, 10); | ||
| 468 | append_char(','); | ||
| 469 | append_number(j, 10); | ||
| 470 | append_char(')'); | ||
| 471 | } return; | ||
| 472 | #endif | ||
| 473 | #ifdef MOUSEKEY_ENABLE | ||
| 474 | case MS_BTN1 ... MS_BTN8: // Mouse button keycode. | ||
| 475 | append_numbered_keycode(PSTR("MS_BTN"), keycode - (MS_BTN1 - 1)); | ||
| 476 | return; | ||
| 477 | #endif // MOUSEKEY_ENABLE | ||
| 478 | #ifdef SWAP_HANDS_ENABLE | ||
| 479 | case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: // Swap Hands SH_T(kc) key. | ||
| 480 | if (!IS_SWAP_HANDS_KEYCODE(keycode)) { | ||
| 481 | append_P(PSTR("SH_T(")); | ||
| 482 | append_keycode(QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode)); | ||
| 483 | append_char(')'); | ||
| 484 | return; | ||
| 485 | } | ||
| 486 | break; | ||
| 487 | #endif // SWAP_HANDS_ENABLE | ||
| 488 | #ifdef JOYSTICK_ENABLE | ||
| 489 | case JOYSTICK_KEYCODE_RANGE: // Joystick JS_ key. | ||
| 490 | append_numbered_keycode(PSTR("JS_"), keycode - JS_0); | ||
| 491 | return; | ||
| 492 | #endif // JOYSTICK_ENABLE | ||
| 493 | #ifdef PROGRAMMABLE_BUTTON_ENABLE | ||
| 494 | case PROGRAMMABLE_BUTTON_KEYCODE_RANGE: // Programmable button PB_ key. | ||
| 495 | append_numbered_keycode(PSTR("PB_"), keycode - (PB_1 - 1)); | ||
| 496 | return; | ||
| 497 | #endif // PROGRAMMABLE_BUTTON_ENABLE | ||
| 498 | |||
| 499 | case MACRO_KEYCODE_RANGE: // Macro range MC_ keycode. | ||
| 500 | append_numbered_keycode(PSTR("MC_"), keycode - MC_0); | ||
| 501 | return; | ||
| 502 | |||
| 503 | case KB_KEYCODE_RANGE: // Keyboard range keycode. | ||
| 504 | append_numbered_keycode(PSTR("QK_KB_"), keycode - QK_KB_0); | ||
| 505 | return; | ||
| 506 | |||
| 507 | case USER_KEYCODE_RANGE: // User range keycode. | ||
| 508 | append_numbered_keycode(PSTR("QK_USER_"), keycode - QK_USER_0); | ||
| 509 | return; | ||
| 510 | |||
| 511 | // It would take a nontrivial amount of string data to cover some | ||
| 512 | // feature-specific keycodes, such as those for MIDI and lighting. As a | ||
| 513 | // fallback while still providing some information, we stringify | ||
| 514 | // remaining keys in known code ranges as "QK_<feature>+<number>". | ||
| 515 | #ifdef MAGIC_ENABLE | ||
| 516 | case MAGIC_KEYCODE_RANGE: | ||
| 517 | append_numbered_keycode(PSTR("QK_MAGIC+"), keycode - QK_MAGIC); | ||
| 518 | return; | ||
| 519 | #endif // MAGIC_ENABLE | ||
| 520 | #ifdef MIDI_ENABLE | ||
| 521 | case MIDI_KEYCODE_RANGE: | ||
| 522 | append_numbered_keycode(PSTR("QK_MIDI+"), keycode - QK_MIDI); | ||
| 523 | return; | ||
| 524 | #endif // MIDI_ENABLE | ||
| 525 | #ifdef SEQUENCER_ENABLE | ||
| 526 | case SEQUENCER_KEYCODE_RANGE: | ||
| 527 | append_numbered_keycode(PSTR("QK_SEQUENCER+"), keycode - QK_SEQUENCER); | ||
| 528 | return; | ||
| 529 | #endif // SEQUENCER_ENABLE | ||
| 530 | #ifdef AUDIO_ENABLE | ||
| 531 | case AUDIO_KEYCODE_RANGE: | ||
| 532 | append_numbered_keycode(PSTR("QK_AUDIO+"), keycode - QK_AUDIO); | ||
| 533 | return; | ||
| 534 | #endif // AUDIO_ENABLE | ||
| 535 | #if defined(BACKLIGHT_ENABLE) || defined(LED_MATRIX_ENABLE) || defined(RGBLIGHT_ENABLED) || defined(RGB_MATRIX_ENABLE) // Lighting-related features. | ||
| 536 | case QK_LIGHTING ... QK_LIGHTING_MAX: | ||
| 537 | append_numbered_keycode(PSTR("QK_LIGHTING+"), keycode - QK_LIGHTING); | ||
| 538 | return; | ||
| 539 | #endif // defined(BACKLIGHT_ENABLE) || defined(LED_MATRIX_ENABLE) || defined(RGBLIGHT_ENABLED) || defined(RGB_MATRIX_ENABLE) | ||
| 540 | #ifdef STENO_ENABLE | ||
| 541 | case STENO_KEYCODE_RANGE: | ||
| 542 | append_numbered_keycode(PSTR("QK_STENO+"), keycode - QK_STENO); | ||
| 543 | return; | ||
| 544 | #endif // AUDIO_ENABLE | ||
| 545 | #ifdef BLUETOOTH_ENABLE | ||
| 546 | case CONNECTION_KEYCODE_RANGE: | ||
| 547 | append_numbered_keycode(PSTR("QK_CONNECTION+"), keycode - QK_CONNECTION); | ||
| 548 | return; | ||
| 549 | #endif // BLUETOOTH_ENABLE | ||
| 550 | case QUANTUM_KEYCODE_RANGE: | ||
| 551 | append_numbered_keycode(PSTR("QK_QUANTUM+"), keycode - QK_QUANTUM); | ||
| 552 | return; | ||
| 553 | } | ||
| 554 | // clang-format on | ||
| 555 | |||
| 556 | append_number(keycode, 16); // Fallback: write keycode as hex value. | ||
| 557 | } | ||
| 558 | |||
| 559 | const char* get_keycode_string(uint16_t keycode) { | ||
| 560 | buffer_len = 0; | ||
| 561 | buffer[0] = '\0'; | ||
| 562 | append_keycode(keycode); | ||
| 563 | return buffer; | ||
| 564 | } | ||
