dte test coverage


Directory: ./
File: src/terminal/key.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 93 93 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 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