| 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 KEY(keyname) [KEY_ ## keyname - KEY_SPECIAL_MIN] | ||
| 12 | |||
| 13 | static const char special_names[][10] = { | ||
| 14 | KEY(ESCAPE) = "escape", | ||
| 15 | KEY(BACKSPACE) = "backspace", | ||
| 16 | KEY(INSERT) = "insert", | ||
| 17 | KEY(DELETE) = "delete", | ||
| 18 | KEY(HOME) = "home", | ||
| 19 | KEY(END) = "end", | ||
| 20 | KEY(PAGE_UP) = "pgup", | ||
| 21 | KEY(PAGE_DOWN) = "pgdown", | ||
| 22 | KEY(UP) = "up", | ||
| 23 | KEY(DOWN) = "down", | ||
| 24 | KEY(RIGHT) = "right", | ||
| 25 | KEY(LEFT) = "left", | ||
| 26 | KEY(BEGIN) = "begin", | ||
| 27 | KEY(SCROLL_LOCK) = "scrlock", | ||
| 28 | KEY(PRINT_SCREEN) = "print", | ||
| 29 | KEY(PAUSE) = "pause", | ||
| 30 | KEY(MENU) = "menu", | ||
| 31 | KEY(F1) = "F1", | ||
| 32 | KEY(F2) = "F2", | ||
| 33 | KEY(F3) = "F3", | ||
| 34 | KEY(F4) = "F4", | ||
| 35 | KEY(F5) = "F5", | ||
| 36 | KEY(F6) = "F6", | ||
| 37 | KEY(F7) = "F7", | ||
| 38 | KEY(F8) = "F8", | ||
| 39 | KEY(F9) = "F9", | ||
| 40 | KEY(F10) = "F10", | ||
| 41 | KEY(F11) = "F11", | ||
| 42 | KEY(F12) = "F12", | ||
| 43 | KEY(F13) = "F13", | ||
| 44 | KEY(F14) = "F14", | ||
| 45 | KEY(F15) = "F15", | ||
| 46 | KEY(F16) = "F16", | ||
| 47 | KEY(F17) = "F17", | ||
| 48 | KEY(F18) = "F18", | ||
| 49 | KEY(F19) = "F19", | ||
| 50 | KEY(F20) = "F20", | ||
| 51 | KEY(F21) = "F21", | ||
| 52 | KEY(F22) = "F22", | ||
| 53 | KEY(F23) = "F23", | ||
| 54 | KEY(F24) = "F24", | ||
| 55 | }; | ||
| 56 | |||
| 57 | static const struct { | ||
| 58 | char name[8]; | ||
| 59 | KeyCode key; | ||
| 60 | } other_keys[] = { | ||
| 61 | {"tab", KEY_TAB}, | ||
| 62 | {"enter", KEY_ENTER}, | ||
| 63 | {"space", KEY_SPACE}, | ||
| 64 | }; | ||
| 65 | |||
| 66 | 24 | UNITTEST { | |
| 67 | 24 | static_assert(ARRAYLEN(special_names) == NR_SPECIAL_KEYS); | |
| 68 | 24 | CHECK_STRING_ARRAY(special_names); | |
| 69 | 24 | CHECK_STRUCT_ARRAY(other_keys, name); | |
| 70 | |||
| 71 | 24 | static_assert(KEY_UNICODE_MAX == UNICODE_MAX_VALID_CODEPOINT); | |
| 72 | 24 | static_assert(KEY_MASK + 1 == 1u << 21); | |
| 73 | 24 | static_assert((KEY_MASK & MOD_MASK) == 0); | |
| 74 | 24 | static_assert(((KEY_MASK | MOD_MASK) & KEYCODE_QUERY_REPLY_BIT) == 0); | |
| 75 | 24 | static_assert((KEY_MASK & KEY_SPECIAL_MIN) == KEY_SPECIAL_MIN); | |
| 76 | 24 | static_assert((KEY_MASK & KEY_SPECIAL_MAX) == KEY_SPECIAL_MAX); | |
| 77 | 24 | static_assert((KEY_MASK & KEY_IGNORE) == KEY_IGNORE); | |
| 78 | 24 | static_assert((KEY_MASK & KEYCODE_DETECTED_PASTE) == KEYCODE_DETECTED_PASTE); | |
| 79 | 24 | static_assert((KEY_MASK & KEYCODE_BRACKETED_PASTE) == KEYCODE_BRACKETED_PASTE); | |
| 80 | 24 | } | |
| 81 | |||
| 82 | 1760 | static size_t parse_modifiers(const char *str, KeyCode *modifiersp) | |
| 83 | { | ||
| 84 |
3/4✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 4 taken 1758 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
|
1760 | if (str[0] == '^' && str[1] != '\0') { |
| 85 | 2 | *modifiersp = MOD_CTRL; | |
| 86 | 2 | return 1; | |
| 87 | } | ||
| 88 | |||
| 89 | KeyCode modifiers = 0; | ||
| 90 | size_t i = 0; | ||
| 91 | |||
| 92 | // https://www.gnu.org/software/emacs/manual/html_node/efaq/Binding-combinations-of-modifiers-and-function-keys.html | ||
| 93 | 5578 | while (1) { | |
| 94 | 3668 | KeyCode tmp; | |
| 95 |
6/6✓ Branch 6 → 7 taken 775 times.
✓ Branch 6 → 8 taken 511 times.
✓ Branch 6 → 9 taken 20 times.
✓ Branch 6 → 10 taken 6 times.
✓ Branch 6 → 11 taken 1736 times.
✓ Branch 6 → 12 taken 620 times.
|
3668 | switch (str[i]) { |
| 96 | case 'C': | ||
| 97 | tmp = MOD_CTRL; | ||
| 98 | break; | ||
| 99 | 775 | case 'M': | |
| 100 | 775 | tmp = MOD_META; | |
| 101 | 775 | break; | |
| 102 | 511 | case 'S': | |
| 103 | 511 | tmp = MOD_SHIFT; | |
| 104 | 511 | break; | |
| 105 | 20 | case 's': | |
| 106 | 20 | tmp = MOD_SUPER; | |
| 107 | 20 | break; | |
| 108 | 6 | case 'H': | |
| 109 | 6 | tmp = MOD_HYPER; | |
| 110 | 6 | break; | |
| 111 | 1736 | default: | |
| 112 | 1736 | goto end; | |
| 113 | } | ||
| 114 |
4/4✓ Branch 12 → 13 taken 1912 times.
✓ Branch 12 → 14 taken 20 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 1910 times.
|
1932 | if (str[i + 1] != '-' || modifiers & tmp) { |
| 115 | 22 | goto end; | |
| 116 | } | ||
| 117 | 1910 | modifiers |= tmp; | |
| 118 | 1910 | i += 2; | |
| 119 | } | ||
| 120 | |||
| 121 | 1758 | end: | |
| 122 | 1758 | *modifiersp = modifiers; | |
| 123 | 1758 | return i; | |
| 124 | } | ||
| 125 | |||
| 126 | 1760 | KeyCode keycode_from_str(const char *str) | |
| 127 | { | ||
| 128 | 1760 | KeyCode modifiers; | |
| 129 | 1760 | str += parse_modifiers(str, &modifiers); | |
| 130 | 1760 | size_t len = strlen(str); | |
| 131 | 1760 | size_t pos = 0; | |
| 132 | 1760 | KeyCode ch = u_get_char(str, len, &pos); | |
| 133 | |||
| 134 |
3/4✓ Branch 4 → 5 taken 1760 times.
✗ Branch 4 → 17 not taken.
✓ Branch 5 → 6 taken 814 times.
✓ Branch 5 → 17 taken 946 times.
|
1760 | if (u_is_unicode(ch) && pos == len) { |
| 135 |
2/2✓ Branch 6 → 7 taken 806 times.
✓ Branch 6 → 24 taken 8 times.
|
814 | if (unlikely(u_is_cntrl(ch))) { |
| 136 | return KEY_NONE; | ||
| 137 | } | ||
| 138 |
2/2✓ Branch 7 → 8 taken 10 times.
✓ Branch 7 → 12 taken 796 times.
|
806 | if (u_is_ascii_upper(ch)) { |
| 139 |
2/2✓ Branch 8 → 9 taken 8 times.
✓ Branch 8 → 10 taken 2 times.
|
10 | if (modifiers & MOD_CTRL) { |
| 140 | // Convert C-A to C-a | ||
| 141 | 8 | ch = ascii_tolower(ch); | |
| 142 |
2/2✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 1 time.
|
2 | } else if (modifiers & MOD_META) { |
| 143 | // Convert M-A to M-S-a | ||
| 144 | 1 | modifiers |= MOD_SHIFT; | |
| 145 | 1 | ch = ascii_tolower(ch); | |
| 146 | } | ||
| 147 | } | ||
| 148 | 806 | return modifiers | ch; | |
| 149 | } | ||
| 150 | |||
| 151 |
2/2✓ Branch 18 → 13 taken 2737 times.
✓ Branch 18 → 23 taken 881 times.
|
3618 | for (size_t i = 0; i < ARRAYLEN(other_keys); i++) { |
| 152 |
2/2✓ Branch 14 → 15 taken 65 times.
✓ Branch 14 → 16 taken 2672 times.
|
2737 | if (ascii_streq_icase(str, other_keys[i].name)) { |
| 153 | 65 | return modifiers | other_keys[i].key; | |
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 |
2/2✓ Branch 23 → 19 taken 8402 times.
✓ Branch 23 → 24 taken 9 times.
|
8411 | for (size_t i = 0; i < ARRAYLEN(special_names); i++) { |
| 158 |
2/2✓ Branch 20 → 21 taken 872 times.
✓ Branch 20 → 22 taken 7530 times.
|
8402 | if (ascii_streq_icase(str, special_names[i])) { |
| 159 | 872 | return modifiers | (KEY_SPECIAL_MIN + i); | |
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | return KEY_NONE; | ||
| 164 | } | ||
| 165 | |||
| 166 | 292 | static const char *lookup_other_key(KeyCode key) | |
| 167 | { | ||
| 168 |
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++) { |
| 169 |
2/2✓ Branch 3 → 4 taken 20 times.
✓ Branch 3 → 5 taken 829 times.
|
849 | if (key == other_keys[i].key) { |
| 170 | 20 | return other_keys[i].name; | |
| 171 | } | ||
| 172 | } | ||
| 173 | return NULL; | ||
| 174 | } | ||
| 175 | |||
| 176 | // Writes the string representation of `k` into `buf` (which must | ||
| 177 | // have at least `KEYCODE_STR_BUFSIZE` bytes available) and returns | ||
| 178 | // the length of the written string. | ||
| 179 | 595 | size_t keycode_to_str(KeyCode k, char buf[static KEYCODE_STR_BUFSIZE]) | |
| 180 | { | ||
| 181 | 595 | static const struct { | |
| 182 | char prefix; | ||
| 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 | 595 | KeyCode key = keycode_get_key(k); | |
| 193 | 595 | KeyCode mask = MOD_MASK | KEY_MASK; | |
| 194 |
4/4✓ Branch 2 → 3 taken 594 times.
✓ Branch 2 → 4 taken 1 time.
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 12 taken 591 times.
|
595 | bool is_key_combo = (k & mask) == k && key <= KEY_SPECIAL_MAX; |
| 195 | |||
| 196 | 4 | if (unlikely(!is_key_combo)) { | |
| 197 | 4 | size_t n = (k & KEYCODE_QUERY_REPLY_BIT) | |
| 198 | 1 | ? copyliteral(buf, "QUERY REPLY; 0x") | |
| 199 |
2/2✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 3 times.
|
4 | : copyliteral(buf, "INVALID; 0x") |
| 200 | ; | ||
| 201 | 4 | return n + buf_umax_to_hex_str(k, buf + n, 8); | |
| 202 | } | ||
| 203 | |||
| 204 | size_t pos = 0; | ||
| 205 |
2/2✓ Branch 12 → 9 taken 2955 times.
✓ Branch 12 → 13 taken 591 times.
|
3546 | for (size_t i = 0; i < ARRAYLEN(mods); i++) { |
| 206 |
2/2✓ Branch 9 → 10 taken 649 times.
✓ Branch 9 → 11 taken 2306 times.
|
2955 | if (k & mods[i].code) { |
| 207 | 649 | buf[pos++] = mods[i].prefix; | |
| 208 | 649 | buf[pos++] = '-'; | |
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | 591 | const char *name; | |
| 213 |
2/2✓ Branch 13 → 14 taken 299 times.
✓ Branch 13 → 15 taken 292 times.
|
591 | if (key >= KEY_SPECIAL_MIN) { |
| 214 | 299 | name = special_names[key - KEY_SPECIAL_MIN]; | |
| 215 | } else { | ||
| 216 | 292 | name = lookup_other_key(key); | |
| 217 |
2/2✓ Branch 15 → 16 taken 272 times.
✓ Branch 15 → 18 taken 20 times.
|
292 | if (!name) { |
| 218 | 272 | pos += u_set_char(buf + pos, key); | |
| 219 | 272 | buf[pos] = '\0'; | |
| 220 | 272 | return pos; | |
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | 319 | BUG_ON(name[0] == '\0'); | |
| 225 | 319 | char *end = memccpy(buf + pos, name, '\0', KEYCODE_STR_BUFSIZE - pos); | |
| 226 | 319 | BUG_ON(!end); | |
| 227 | 319 | BUG_ON(end <= buf); | |
| 228 | 319 | return (size_t)(end - buf) - 1; | |
| 229 | } | ||
| 230 |