dte test coverage


Directory: ./
File: src/terminal/key.c
Date: 2025-05-08 15:05:54
Exec Total Coverage
Lines: 94 94 100.0%
Functions: 5 5 100.0%
Branches: 50 52 96.2%

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