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 | }; | ||
52 | |||
53 | static const struct { | ||
54 | char name[8]; | ||
55 | KeyCode key; | ||
56 | } other_keys[] = { | ||
57 | {"tab", KEY_TAB}, | ||
58 | {"enter", KEY_ENTER}, | ||
59 | {"space", KEY_SPACE}, | ||
60 | }; | ||
61 | |||
62 | 22 | UNITTEST { | |
63 | 22 | static_assert(ARRAYLEN(special_names) == NR_SPECIAL_KEYS); | |
64 | 22 | CHECK_STRING_ARRAY(special_names); | |
65 | 22 | CHECK_STRUCT_ARRAY(other_keys, name); | |
66 | |||
67 | 22 | static_assert(KEY_UNICODE_MAX == UNICODE_MAX_VALID_CODEPOINT); | |
68 | 22 | static_assert(KEY_MASK + 1 == 1u << 21); | |
69 | 22 | static_assert((KEY_MASK & MOD_MASK) == 0); | |
70 | 22 | static_assert(((KEY_MASK | MOD_MASK) & KEYCODE_QUERY_REPLY_BIT) == 0); | |
71 | 22 | static_assert((KEY_MASK & KEY_SPECIAL_MIN) == KEY_SPECIAL_MIN); | |
72 | 22 | static_assert((KEY_MASK & KEY_SPECIAL_MAX) == KEY_SPECIAL_MAX); | |
73 | 22 | static_assert((KEY_MASK & KEY_IGNORE) == KEY_IGNORE); | |
74 | 22 | static_assert((KEY_MASK & KEYCODE_DETECTED_PASTE) == KEYCODE_DETECTED_PASTE); | |
75 | 22 | static_assert((KEY_MASK & KEYCODE_BRACKETED_PASTE) == KEYCODE_BRACKETED_PASTE); | |
76 | 22 | } | |
77 | |||
78 | 1260 | static size_t parse_modifiers(const char *str, KeyCode *modifiersp) | |
79 | { | ||
80 |
3/4✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→4) taken 1258 times.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 2 times.
|
1260 | if (str[0] == '^' && str[1] != '\0') { |
81 | 2 | *modifiersp = MOD_CTRL; | |
82 | 2 | return 1; | |
83 | } | ||
84 | |||
85 | KeyCode modifiers = 0; | ||
86 | size_t i = 0; | ||
87 | |||
88 | // https://www.gnu.org/software/emacs/manual/html_node/efaq/Binding-combinations-of-modifiers-and-function-keys.html | ||
89 | 3888 | while (1) { | |
90 | 2573 | KeyCode tmp; | |
91 |
6/6✓ Branch 0 (6→7) taken 550 times.
✓ Branch 1 (6→8) taken 322 times.
✓ Branch 2 (6→9) taken 18 times.
✓ Branch 3 (6→10) taken 6 times.
✓ Branch 4 (6→11) taken 1238 times.
✓ Branch 5 (6→12) taken 439 times.
|
2573 | switch (str[i]) { |
92 | case 'C': | ||
93 | tmp = MOD_CTRL; | ||
94 | break; | ||
95 | 550 | case 'M': | |
96 | 550 | tmp = MOD_META; | |
97 | 550 | break; | |
98 | 322 | case 'S': | |
99 | 322 | tmp = MOD_SHIFT; | |
100 | 322 | break; | |
101 | 18 | case 's': | |
102 | 18 | tmp = MOD_SUPER; | |
103 | 18 | break; | |
104 | 6 | case 'H': | |
105 | 6 | tmp = MOD_HYPER; | |
106 | 6 | break; | |
107 | 1238 | default: | |
108 | 1238 | goto end; | |
109 | } | ||
110 |
4/4✓ Branch 0 (12→13) taken 1317 times.
✓ Branch 1 (12→14) taken 18 times.
✓ Branch 2 (13→14) taken 2 times.
✓ Branch 3 (13→15) taken 1315 times.
|
1335 | if (str[i + 1] != '-' || modifiers & tmp) { |
111 | 20 | goto end; | |
112 | } | ||
113 | 1315 | modifiers |= tmp; | |
114 | 1315 | i += 2; | |
115 | } | ||
116 | |||
117 | 1258 | end: | |
118 | 1258 | *modifiersp = modifiers; | |
119 | 1258 | return i; | |
120 | } | ||
121 | |||
122 | 1260 | KeyCode parse_key_string(const char *str) | |
123 | { | ||
124 | 1260 | KeyCode modifiers; | |
125 | 1260 | str += parse_modifiers(str, &modifiers); | |
126 | 1260 | size_t len = strlen(str); | |
127 | 1260 | size_t pos = 0; | |
128 | 1260 | KeyCode ch = u_get_char(str, len, &pos); | |
129 | |||
130 |
3/4✓ Branch 0 (4→5) taken 1260 times.
✗ Branch 1 (4→17) not taken.
✓ Branch 2 (5→6) taken 614 times.
✓ Branch 3 (5→17) taken 646 times.
|
1260 | if (u_is_unicode(ch) && pos == len) { |
131 |
2/2✓ Branch 0 (6→7) taken 606 times.
✓ Branch 1 (6→24) taken 8 times.
|
614 | if (unlikely(u_is_cntrl(ch))) { |
132 | return KEY_NONE; | ||
133 | } | ||
134 |
2/2✓ Branch 0 (7→8) taken 10 times.
✓ Branch 1 (7→12) taken 596 times.
|
606 | if (u_is_ascii_upper(ch)) { |
135 |
2/2✓ Branch 0 (8→9) taken 8 times.
✓ Branch 1 (8→10) taken 2 times.
|
10 | if (modifiers & MOD_CTRL) { |
136 | // Convert C-A to C-a | ||
137 | 8 | ch = ascii_tolower(ch); | |
138 |
2/2✓ Branch 0 (10→11) taken 1 times.
✓ Branch 1 (10→12) taken 1 times.
|
2 | } else if (modifiers & MOD_META) { |
139 | // Convert M-A to M-S-a | ||
140 | 1 | modifiers |= MOD_SHIFT; | |
141 | 1 | ch = ascii_tolower(ch); | |
142 | } | ||
143 | } | ||
144 | 606 | return modifiers | ch; | |
145 | } | ||
146 | |||
147 |
2/2✓ Branch 0 (18→13) taken 1857 times.
✓ Branch 1 (18→23) taken 593 times.
|
2450 | for (size_t i = 0; i < ARRAYLEN(other_keys); i++) { |
148 |
2/2✓ Branch 0 (14→15) taken 53 times.
✓ Branch 1 (14→16) taken 1804 times.
|
1857 | if (ascii_streq_icase(str, other_keys[i].name)) { |
149 | 53 | return modifiers | other_keys[i].key; | |
150 | } | ||
151 | } | ||
152 | |||
153 |
2/2✓ Branch 0 (23→19) taken 5861 times.
✓ Branch 1 (23→24) taken 9 times.
|
5870 | for (size_t i = 0; i < ARRAYLEN(special_names); i++) { |
154 |
2/2✓ Branch 0 (20→21) taken 584 times.
✓ Branch 1 (20→22) taken 5277 times.
|
5861 | if (ascii_streq_icase(str, special_names[i])) { |
155 | 584 | return modifiers | (KEY_SPECIAL_MIN + i); | |
156 | } | ||
157 | } | ||
158 | |||
159 | return KEY_NONE; | ||
160 | } | ||
161 | |||
162 | 280 | static const char *lookup_other_key(KeyCode key) | |
163 | { | ||
164 |
2/2✓ Branch 0 (6→3) taken 813 times.
✓ Branch 1 (6→7) taken 260 times.
|
1073 | for (size_t i = 0; i < ARRAYLEN(other_keys); i++) { |
165 |
2/2✓ Branch 0 (3→4) taken 20 times.
✓ Branch 1 (3→5) taken 793 times.
|
813 | if (key == other_keys[i].key) { |
166 | 20 | return other_keys[i].name; | |
167 | } | ||
168 | } | ||
169 | return NULL; | ||
170 | } | ||
171 | |||
172 | // Writes the string representation of `k` into `buf` (which must | ||
173 | // have at least `KEYCODE_STR_BUFSIZE` bytes available) and returns | ||
174 | // the length of the written string. | ||
175 | 536 | size_t keycode_to_string(KeyCode k, char buf[KEYCODE_STR_BUFSIZE]) | |
176 | { | ||
177 | 536 | static const struct { | |
178 | char prefix; | ||
179 | KeyCode code; | ||
180 | } mods[] = { | ||
181 | {'C', MOD_CTRL}, | ||
182 | {'M', MOD_META}, | ||
183 | {'S', MOD_SHIFT}, | ||
184 | {'s', MOD_SUPER}, | ||
185 | {'H', MOD_HYPER}, | ||
186 | }; | ||
187 | |||
188 | 536 | KeyCode key = keycode_get_key(k); | |
189 | 536 | KeyCode mask = MOD_MASK | KEY_MASK; | |
190 |
4/4✓ Branch 0 (2→3) taken 535 times.
✓ Branch 1 (2→4) taken 1 times.
✓ Branch 2 (3→4) taken 3 times.
✓ Branch 3 (3→12) taken 532 times.
|
536 | bool is_key_combo = (k & mask) == k && key <= KEY_SPECIAL_MAX; |
191 | |||
192 | 4 | if (unlikely(!is_key_combo)) { | |
193 | 4 | size_t n = (k & KEYCODE_QUERY_REPLY_BIT) | |
194 | 1 | ? copyliteral(buf, "QUERY REPLY; 0x") | |
195 |
2/2✓ Branch 0 (4→5) taken 1 times.
✓ Branch 1 (4→6) taken 3 times.
|
4 | : copyliteral(buf, "INVALID; 0x") |
196 | ; | ||
197 | 4 | return n + buf_umax_to_hex_str(k, buf + n, 8); | |
198 | } | ||
199 | |||
200 | size_t pos = 0; | ||
201 |
2/2✓ Branch 0 (12→9) taken 2660 times.
✓ Branch 1 (12→13) taken 532 times.
|
3192 | for (size_t i = 0; i < ARRAYLEN(mods); i++) { |
202 |
2/2✓ Branch 0 (9→10) taken 569 times.
✓ Branch 1 (9→11) taken 2091 times.
|
2660 | if (k & mods[i].code) { |
203 | 569 | buf[pos++] = mods[i].prefix; | |
204 | 569 | buf[pos++] = '-'; | |
205 | } | ||
206 | } | ||
207 | |||
208 | 532 | const char *name; | |
209 |
2/2✓ Branch 0 (13→14) taken 252 times.
✓ Branch 1 (13→15) taken 280 times.
|
532 | if (key >= KEY_SPECIAL_MIN) { |
210 | 252 | name = special_names[key - KEY_SPECIAL_MIN]; | |
211 | } else { | ||
212 | 280 | name = lookup_other_key(key); | |
213 |
2/2✓ Branch 0 (15→16) taken 260 times.
✓ Branch 1 (15→18) taken 20 times.
|
280 | if (!name) { |
214 | 260 | pos += u_set_char(buf + pos, key); | |
215 | 260 | buf[pos] = '\0'; | |
216 | 260 | return pos; | |
217 | } | ||
218 | } | ||
219 | |||
220 | 272 | BUG_ON(name[0] == '\0'); | |
221 | 272 | char *end = memccpy(buf + pos, name, '\0', KEYCODE_STR_BUFSIZE - pos); | |
222 | 272 | BUG_ON(!end); | |
223 | 272 | BUG_ON(end <= buf); | |
224 | 272 | return (size_t)(end - buf) - 1; | |
225 | } | ||
226 |