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 0 (2→3) taken 2 times.
✓ Branch 1 (2→4) taken 1758 times.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (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 0 (6→7) taken 775 times.
✓ Branch 1 (6→8) taken 511 times.
✓ Branch 2 (6→9) taken 20 times.
✓ Branch 3 (6→10) taken 6 times.
✓ Branch 4 (6→11) taken 1736 times.
✓ Branch 5 (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 0 (12→13) taken 1912 times.
✓ Branch 1 (12→14) taken 20 times.
✓ Branch 2 (13→14) taken 2 times.
✓ Branch 3 (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 0 (4→5) taken 1760 times.
✗ Branch 1 (4→17) not taken.
✓ Branch 2 (5→6) taken 814 times.
✓ Branch 3 (5→17) taken 946 times.
|
1760 | if (u_is_unicode(ch) && pos == len) { |
135 |
2/2✓ Branch 0 (6→7) taken 806 times.
✓ Branch 1 (6→24) taken 8 times.
|
814 | if (unlikely(u_is_cntrl(ch))) { |
136 | return KEY_NONE; | ||
137 | } | ||
138 |
2/2✓ Branch 0 (7→8) taken 10 times.
✓ Branch 1 (7→12) taken 796 times.
|
806 | if (u_is_ascii_upper(ch)) { |
139 |
2/2✓ Branch 0 (8→9) taken 8 times.
✓ Branch 1 (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 0 (10→11) taken 1 times.
✓ Branch 1 (10→12) taken 1 times.
|
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 0 (18→13) taken 2737 times.
✓ Branch 1 (18→23) taken 881 times.
|
3618 | for (size_t i = 0; i < ARRAYLEN(other_keys); i++) { |
152 |
2/2✓ Branch 0 (14→15) taken 65 times.
✓ Branch 1 (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 0 (23→19) taken 8402 times.
✓ Branch 1 (23→24) taken 9 times.
|
8411 | for (size_t i = 0; i < ARRAYLEN(special_names); i++) { |
158 |
2/2✓ Branch 0 (20→21) taken 872 times.
✓ Branch 1 (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 0 (6→3) taken 849 times.
✓ Branch 1 (6→7) taken 272 times.
|
1121 | for (size_t i = 0; i < ARRAYLEN(other_keys); i++) { |
169 |
2/2✓ Branch 0 (3→4) taken 20 times.
✓ Branch 1 (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 0 (2→3) taken 594 times.
✓ Branch 1 (2→4) taken 1 times.
✓ Branch 2 (3→4) taken 3 times.
✓ Branch 3 (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 0 (4→5) taken 1 times.
✓ Branch 1 (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 0 (12→9) taken 2955 times.
✓ Branch 1 (12→13) taken 591 times.
|
3546 | for (size_t i = 0; i < ARRAYLEN(mods); i++) { |
206 |
2/2✓ Branch 0 (9→10) taken 649 times.
✓ Branch 1 (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 0 (13→14) taken 299 times.
✓ Branch 1 (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 0 (15→16) taken 272 times.
✓ Branch 1 (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 |