dte test coverage


Directory: ./
File: src/terminal/key.c
Date: 2025-09-07 23:01:39
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 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