qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

keycode_string.c (23052B)


      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 }