src/terminal/key.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include <string.h> | ||
| 2 | #include "key.h" | ||
| 3 | #include "util/array.h" | ||
| 4 | #include "util/ascii.h" | ||
| 5 | #include "util/debug.h" | ||
| 6 | #include "util/numtostr.h" | ||
| 7 | #include "util/str-util.h" | ||
| 8 | #include "util/unicode.h" | ||
| 9 | #include "util/utf8.h" | ||
| 10 | |||
| 11 | #define K(key) [key - KEY_SPECIAL_MIN] | ||
| 12 | |||
| 13 | static const char special_names[][10] = { | ||
| 14 | K(KEY_ESCAPE) = "escape", | ||
| 15 | K(KEY_BACKSPACE) = "backspace", | ||
| 16 | K(KEY_INSERT) = "insert", | ||
| 17 | K(KEY_DELETE) = "delete", | ||
| 18 | K(KEY_HOME) = "home", | ||
| 19 | K(KEY_END) = "end", | ||
| 20 | K(KEY_PAGE_UP) = "pgup", | ||
| 21 | K(KEY_PAGE_DOWN) = "pgdown", | ||
| 22 | K(KEY_UP) = "up", | ||
| 23 | K(KEY_DOWN) = "down", | ||
| 24 | K(KEY_RIGHT) = "right", | ||
| 25 | K(KEY_LEFT) = "left", | ||
| 26 | K(KEY_BEGIN) = "begin", | ||
| 27 | K(KEY_SCROLL_LOCK) = "scrlock", | ||
| 28 | K(KEY_PRINT_SCREEN) = "print", | ||
| 29 | K(KEY_PAUSE) = "pause", | ||
| 30 | K(KEY_MENU) = "menu", | ||
| 31 | K(KEY_F1) = "F1", | ||
| 32 | K(KEY_F2) = "F2", | ||
| 33 | K(KEY_F3) = "F3", | ||
| 34 | K(KEY_F4) = "F4", | ||
| 35 | K(KEY_F5) = "F5", | ||
| 36 | K(KEY_F6) = "F6", | ||
| 37 | K(KEY_F7) = "F7", | ||
| 38 | K(KEY_F8) = "F8", | ||
| 39 | K(KEY_F9) = "F9", | ||
| 40 | K(KEY_F10) = "F10", | ||
| 41 | K(KEY_F11) = "F11", | ||
| 42 | K(KEY_F12) = "F12", | ||
| 43 | K(KEY_F13) = "F13", | ||
| 44 | K(KEY_F14) = "F14", | ||
| 45 | K(KEY_F15) = "F15", | ||
| 46 | K(KEY_F16) = "F16", | ||
| 47 | K(KEY_F17) = "F17", | ||
| 48 | K(KEY_F18) = "F18", | ||
| 49 | K(KEY_F19) = "F19", | ||
| 50 | K(KEY_F20) = "F20", | ||
| 51 | K(KEY_F21) = "F21", | ||
| 52 | K(KEY_F22) = "F22", | ||
| 53 | K(KEY_F23) = "F23", | ||
| 54 | K(KEY_F24) = "F24", | ||
| 55 | K(KEY_F25) = "F25", | ||
| 56 | K(KEY_F26) = "F26", | ||
| 57 | K(KEY_F27) = "F27", | ||
| 58 | K(KEY_F28) = "F28", | ||
| 59 | K(KEY_F29) = "F29", | ||
| 60 | K(KEY_F30) = "F30", | ||
| 61 | K(KEY_F31) = "F31", | ||
| 62 | K(KEY_F32) = "F32", | ||
| 63 | K(KEY_F33) = "F33", | ||
| 64 | K(KEY_F34) = "F34", | ||
| 65 | K(KEY_F35) = "F35", | ||
| 66 | }; | ||
| 67 | |||
| 68 | static const struct { | ||
| 69 | char name[8]; | ||
| 70 | KeyCode key; | ||
| 71 | } other_keys[] = { | ||
| 72 | {"tab", KEY_TAB}, | ||
| 73 | {"enter", KEY_ENTER}, | ||
| 74 | {"space", KEY_SPACE}, | ||
| 75 | }; | ||
| 76 | |||
| 77 | 24 | UNITTEST { | |
| 78 | 24 | static_assert(ARRAYLEN(special_names) == NR_SPECIAL_KEYS); | |
| 79 | 24 | CHECK_STRING_ARRAY(special_names); | |
| 80 | 24 | CHECK_STRUCT_ARRAY(other_keys, name); | |
| 81 | |||
| 82 | 24 | static_assert(KEY_UNICODE_MAX == UNICODE_MAX_VALID_CODEPOINT); | |
| 83 | 24 | static_assert(KEY_MASK + 1 == 1u << 21); | |
| 84 | 24 | static_assert((KEY_MASK & MOD_MASK) == 0); | |
| 85 | 24 | static_assert(((KEY_MASK | MOD_MASK) & KEYCODE_QUERY_REPLY_BIT) == 0); | |
| 86 | 24 | static_assert((KEY_MASK & KEY_SPECIAL_MIN) == KEY_SPECIAL_MIN); | |
| 87 | 24 | static_assert((KEY_MASK & KEY_SPECIAL_MAX) == KEY_SPECIAL_MAX); | |
| 88 | 24 | static_assert((KEY_MASK & KEY_IGNORE) == KEY_IGNORE); | |
| 89 | 24 | static_assert((KEY_MASK & KEYCODE_DETECTED_PASTE) == KEYCODE_DETECTED_PASTE); | |
| 90 | 24 | static_assert((KEY_MASK & KEYCODE_BRACKETED_PASTE) == KEYCODE_BRACKETED_PASTE); | |
| 91 | 24 | } | |
| 92 | |||
| 93 | // https://www.gnu.org/software/emacs/manual/html_node/efaq/Binding-combinations-of-modifiers-and-function-keys.html | ||
| 94 | 3687 | static KeyCode modifier_from_char(char c) | |
| 95 | { | ||
| 96 |
6/6✓ Branch 2 → 3 taken 776 times.
✓ Branch 2 → 4 taken 513 times.
✓ Branch 2 → 5 taken 20 times.
✓ Branch 2 → 6 taken 6 times.
✓ Branch 2 → 7 taken 1750 times.
✓ Branch 2 → 8 taken 622 times.
|
3687 | switch (c) { |
| 97 | case 'C': return MOD_CTRL; | ||
| 98 | 776 | case 'M': return MOD_META; | |
| 99 | 513 | case 'S': return MOD_SHIFT; | |
| 100 | 20 | case 's': return MOD_SUPER; | |
| 101 | 6 | case 'H': return MOD_HYPER; | |
| 102 | } | ||
| 103 | 1750 | return 0; | |
| 104 | } | ||
| 105 | |||
| 106 | 1774 | static size_t parse_modifiers(const char *str, KeyCode *modifiersp) | |
| 107 | { | ||
| 108 |
3/4✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 5 taken 1772 times.
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 5 not taken.
|
1774 | if (str[0] == '^' && str[1] != '\0') { |
| 109 | 2 | *modifiersp = MOD_CTRL; | |
| 110 | 2 | return 1; | |
| 111 | } | ||
| 112 | |||
| 113 | KeyCode modifiers = 0; | ||
| 114 | size_t i = 0; | ||
| 115 | |||
| 116 | 5602 | while (1) { | |
| 117 | 3687 | KeyCode tmp = modifier_from_char(str[i]); | |
| 118 |
6/6✓ Branch 6 → 7 taken 1937 times.
✓ Branch 6 → 10 taken 1750 times.
✓ Branch 7 → 8 taken 1917 times.
✓ Branch 7 → 10 taken 20 times.
✓ Branch 8 → 9 taken 1915 times.
✓ Branch 8 → 10 taken 2 times.
|
3687 | if (tmp == 0 || str[i + 1] != '-' || modifiers & tmp) { |
| 119 | break; | ||
| 120 | } | ||
| 121 | 1915 | modifiers |= tmp; | |
| 122 | 1915 | i += 2; | |
| 123 | } | ||
| 124 | |||
| 125 | 1772 | *modifiersp = modifiers; | |
| 126 | 1772 | return i; | |
| 127 | } | ||
| 128 | |||
| 129 | 1774 | KeyCode keycode_from_str(const char *str) | |
| 130 | { | ||
| 131 | 1774 | KeyCode modifiers; | |
| 132 | 1774 | str += parse_modifiers(str, &modifiers); | |
| 133 | 1774 | size_t len = strlen(str); | |
| 134 | 1774 | size_t pos = 0; | |
| 135 | 1774 | KeyCode ch = u_get_char(str, len, &pos); | |
| 136 | |||
| 137 |
3/4✓ Branch 4 → 5 taken 1774 times.
✗ Branch 4 → 17 not taken.
✓ Branch 5 → 6 taken 814 times.
✓ Branch 5 → 17 taken 960 times.
|
1774 | if (u_is_unicode(ch) && pos == len) { |
| 138 |
2/2✓ Branch 6 → 7 taken 806 times.
✓ Branch 6 → 24 taken 8 times.
|
814 | if (unlikely(u_is_cntrl(ch))) { |
| 139 | return KEY_NONE; | ||
| 140 | } | ||
| 141 |
2/2✓ Branch 7 → 8 taken 10 times.
✓ Branch 7 → 12 taken 796 times.
|
806 | if (u_is_ascii_upper(ch)) { |
| 142 |
2/2✓ Branch 8 → 9 taken 8 times.
✓ Branch 8 → 10 taken 2 times.
|
10 | if (modifiers & MOD_CTRL) { |
| 143 | // Convert C-A to C-a | ||
| 144 | 8 | ch = ascii_tolower(ch); | |
| 145 |
2/2✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 1 time.
|
2 | } else if (modifiers & MOD_META) { |
| 146 | // Convert M-A to M-S-a | ||
| 147 | 1 | modifiers |= MOD_SHIFT; | |
| 148 | 1 | ch = ascii_tolower(ch); | |
| 149 | } | ||
| 150 | } | ||
| 151 | 806 | return modifiers | ch; | |
| 152 | } | ||
| 153 | |||
| 154 |
2/2✓ Branch 18 → 13 taken 2779 times.
✓ Branch 18 → 23 taken 895 times.
|
3674 | for (size_t i = 0; i < ARRAYLEN(other_keys); i++) { |
| 155 |
2/2✓ Branch 14 → 15 taken 65 times.
✓ Branch 14 → 16 taken 2714 times.
|
2779 | if (ascii_streq_icase(str, other_keys[i].name)) { |
| 156 | 65 | return modifiers | other_keys[i].key; | |
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 |
2/2✓ Branch 23 → 19 taken 9174 times.
✓ Branch 23 → 24 taken 9 times.
|
9183 | for (size_t i = 0; i < ARRAYLEN(special_names); i++) { |
| 161 |
2/2✓ Branch 20 → 21 taken 886 times.
✓ Branch 20 → 22 taken 8288 times.
|
9174 | if (ascii_streq_icase(str, special_names[i])) { |
| 162 | 886 | return modifiers | (KEY_SPECIAL_MIN + i); | |
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | return KEY_NONE; | ||
| 167 | } | ||
| 168 | |||
| 169 | 292 | static const char *lookup_other_key(KeyCode key) | |
| 170 | { | ||
| 171 |
2/2✓ Branch 6 → 3 taken 849 times.
✓ Branch 6 → 7 taken 272 times.
|
1121 | for (size_t i = 0; i < ARRAYLEN(other_keys); i++) { |
| 172 |
2/2✓ Branch 3 → 4 taken 20 times.
✓ Branch 3 → 5 taken 829 times.
|
849 | if (key == other_keys[i].key) { |
| 173 | 20 | return other_keys[i].name; | |
| 174 | } | ||
| 175 | } | ||
| 176 | return NULL; | ||
| 177 | } | ||
| 178 | |||
| 179 | 593 | static size_t modifiers_to_str(KeyCode k, char *buf) | |
| 180 | { | ||
| 181 | 593 | static const struct { | |
| 182 | char str[2]; | ||
| 183 | KeyCode code; | ||
| 184 | } mods[] = { | ||
| 185 | {"C-", MOD_CTRL}, | ||
| 186 | {"M-", MOD_META}, | ||
| 187 | {"S-", MOD_SHIFT}, | ||
| 188 | {"s-", MOD_SUPER}, | ||
| 189 | {"H-", MOD_HYPER}, | ||
| 190 | }; | ||
| 191 | |||
| 192 | 593 | size_t pos = 0; | |
| 193 |
2/2✓ Branch 7 → 3 taken 2965 times.
✓ Branch 7 → 8 taken 593 times.
|
3558 | for (size_t i = 0; i < ARRAYLEN(mods); i++) { |
| 194 |
2/2✓ Branch 3 → 4 taken 651 times.
✓ Branch 3 → 6 taken 2314 times.
|
2965 | if (k & mods[i].code) { |
| 195 | 651 | pos += copystrn(buf + pos, mods[i].str, sizeof(mods[0].str)); | |
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | 593 | return pos; | |
| 200 | } | ||
| 201 | |||
| 202 | // Writes the string representation of `k` into `buf` (which must | ||
| 203 | // have at least `KEYCODE_STR_BUFSIZE` bytes available) and returns | ||
| 204 | // the length of the written string. | ||
| 205 | 597 | size_t keycode_to_str(KeyCode k, char buf[static KEYCODE_STR_BUFSIZE]) | |
| 206 | { | ||
| 207 | 597 | KeyCode key = keycode_get_key(k); | |
| 208 | 597 | KeyCode mask = MOD_MASK | KEY_MASK; | |
| 209 |
4/4✓ Branch 2 → 3 taken 596 times.
✓ Branch 2 → 6 taken 1 time.
✓ Branch 3 → 4 taken 593 times.
✓ Branch 3 → 6 taken 3 times.
|
597 | bool is_key_combo = (k & mask) == k && key <= KEY_SPECIAL_MAX; |
| 210 | |||
| 211 | 597 | if (unlikely(!is_key_combo)) { | |
| 212 | 4 | size_t n = (k & KEYCODE_QUERY_REPLY_BIT) | |
| 213 | 1 | ? copyliteral(buf, "QUERY REPLY; 0x") | |
| 214 |
2/2✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 3 times.
|
4 | : copyliteral(buf, "INVALID; 0x") |
| 215 | ; | ||
| 216 | 4 | return n + buf_umax_to_hex_str(k, buf + n, 8); | |
| 217 | } | ||
| 218 | |||
| 219 | 593 | const char *name; | |
| 220 | 593 | size_t pos = modifiers_to_str(k, buf); | |
| 221 | |||
| 222 |
2/2✓ Branch 5 → 11 taken 301 times.
✓ Branch 5 → 12 taken 292 times.
|
593 | if (key >= KEY_SPECIAL_MIN) { |
| 223 | 301 | name = special_names[key - KEY_SPECIAL_MIN]; | |
| 224 | } else { | ||
| 225 | 292 | name = lookup_other_key(key); | |
| 226 |
2/2✓ Branch 12 → 13 taken 272 times.
✓ Branch 12 → 15 taken 20 times.
|
292 | if (!name) { |
| 227 | 272 | pos += u_set_char(buf + pos, key); | |
| 228 | 272 | buf[pos] = '\0'; | |
| 229 | 272 | return pos; | |
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | 321 | BUG_ON(name[0] == '\0'); | |
| 234 | 321 | char *end = memccpy(buf + pos, name, '\0', KEYCODE_STR_BUFSIZE - pos); | |
| 235 | 321 | BUG_ON(!end); | |
| 236 | 321 | BUG_ON(end <= buf); | |
| 237 | 321 | return (size_t)(end - buf) - 1; | |
| 238 | } | ||
| 239 |