keyboard stuff
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
24typedef 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 */
43static 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. */
150static const char mod_names[] PROGMEM = "CTL\0SFT\0ALT\0GUI";
151/** Internal buffer for holding a stringified keycode. */
152static char buffer[32];
153#define BUFFER_MAX_LEN (sizeof(buffer) - 1)
154static index_t buffer_len;
155
156/** Finds the name of a keycode in `common_names` or returns NULL. */
157static 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 */
187static 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. */
199static 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. */
217static 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. */
228static 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. */
243static 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`. */
251static 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`. */
256static 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 */
272static 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 */
283static 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`. */
289static 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
559const char* get_keycode_string(uint16_t keycode) {
560 buffer_len = 0;
561 buffer[0] = '\0';
562 append_keycode(keycode);
563 return buffer;
564}