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