1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2007 Nicholas Marriott <[email protected]> |
5 | * |
6 | * Permission to use, copy, modify, and distribute this software for any |
7 | * purpose with or without fee is hereby granted, provided that the above |
8 | * copyright notice and this permission notice appear in all copies. |
9 | * |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ |
18 | |
19 | #include <sys/types.h> |
20 | |
21 | #include <string.h> |
22 | |
23 | #include "tmux.h" |
24 | |
25 | static key_code key_string_search_table(const char *); |
26 | static key_code key_string_get_modifiers(const char **); |
27 | |
28 | static const struct { |
29 | const char *string; |
30 | key_code key; |
31 | } key_string_table[] = { |
32 | /* Function keys. */ |
33 | { "F1" , KEYC_F1 }, |
34 | { "F2" , KEYC_F2 }, |
35 | { "F3" , KEYC_F3 }, |
36 | { "F4" , KEYC_F4 }, |
37 | { "F5" , KEYC_F5 }, |
38 | { "F6" , KEYC_F6 }, |
39 | { "F7" , KEYC_F7 }, |
40 | { "F8" , KEYC_F8 }, |
41 | { "F9" , KEYC_F9 }, |
42 | { "F10" , KEYC_F10 }, |
43 | { "F11" , KEYC_F11 }, |
44 | { "F12" , KEYC_F12 }, |
45 | { "IC" , KEYC_IC }, |
46 | { "Insert" , KEYC_IC }, |
47 | { "DC" , KEYC_DC }, |
48 | { "Delete" , KEYC_DC }, |
49 | { "Home" , KEYC_HOME }, |
50 | { "End" , KEYC_END }, |
51 | { "NPage" , KEYC_NPAGE }, |
52 | { "PageDown" , KEYC_NPAGE }, |
53 | { "PgDn" , KEYC_NPAGE }, |
54 | { "PPage" , KEYC_PPAGE }, |
55 | { "PageUp" , KEYC_PPAGE }, |
56 | { "PgUp" , KEYC_PPAGE }, |
57 | { "Tab" , '\011' }, |
58 | { "BTab" , KEYC_BTAB }, |
59 | { "Space" , ' ' }, |
60 | { "BSpace" , KEYC_BSPACE }, |
61 | { "Enter" , '\r' }, |
62 | { "Escape" , '\033' }, |
63 | |
64 | /* Arrow keys. */ |
65 | { "Up" , KEYC_UP }, |
66 | { "Down" , KEYC_DOWN }, |
67 | { "Left" , KEYC_LEFT }, |
68 | { "Right" , KEYC_RIGHT }, |
69 | |
70 | /* Numeric keypad. */ |
71 | { "KP/" , KEYC_KP_SLASH }, |
72 | { "KP*" , KEYC_KP_STAR }, |
73 | { "KP-" , KEYC_KP_MINUS }, |
74 | { "KP7" , KEYC_KP_SEVEN }, |
75 | { "KP8" , KEYC_KP_EIGHT }, |
76 | { "KP9" , KEYC_KP_NINE }, |
77 | { "KP+" , KEYC_KP_PLUS }, |
78 | { "KP4" , KEYC_KP_FOUR }, |
79 | { "KP5" , KEYC_KP_FIVE }, |
80 | { "KP6" , KEYC_KP_SIX }, |
81 | { "KP1" , KEYC_KP_ONE }, |
82 | { "KP2" , KEYC_KP_TWO }, |
83 | { "KP3" , KEYC_KP_THREE }, |
84 | { "KPEnter" , KEYC_KP_ENTER }, |
85 | { "KP0" , KEYC_KP_ZERO }, |
86 | { "KP." , KEYC_KP_PERIOD }, |
87 | |
88 | /* Mouse keys. */ |
89 | KEYC_MOUSE_STRING(MOUSEDOWN1, MouseDown1), |
90 | KEYC_MOUSE_STRING(MOUSEDOWN2, MouseDown2), |
91 | KEYC_MOUSE_STRING(MOUSEDOWN3, MouseDown3), |
92 | KEYC_MOUSE_STRING(MOUSEUP1, MouseUp1), |
93 | KEYC_MOUSE_STRING(MOUSEUP2, MouseUp2), |
94 | KEYC_MOUSE_STRING(MOUSEUP3, MouseUp3), |
95 | KEYC_MOUSE_STRING(MOUSEDRAG1, MouseDrag1), |
96 | KEYC_MOUSE_STRING(MOUSEDRAG2, MouseDrag2), |
97 | KEYC_MOUSE_STRING(MOUSEDRAG3, MouseDrag3), |
98 | KEYC_MOUSE_STRING(MOUSEDRAGEND1, MouseDragEnd1), |
99 | KEYC_MOUSE_STRING(MOUSEDRAGEND2, MouseDragEnd2), |
100 | KEYC_MOUSE_STRING(MOUSEDRAGEND3, MouseDragEnd3), |
101 | KEYC_MOUSE_STRING(WHEELUP, WheelUp), |
102 | KEYC_MOUSE_STRING(WHEELDOWN, WheelDown), |
103 | KEYC_MOUSE_STRING(DOUBLECLICK1, DoubleClick1), |
104 | KEYC_MOUSE_STRING(DOUBLECLICK2, DoubleClick2), |
105 | KEYC_MOUSE_STRING(DOUBLECLICK3, DoubleClick3), |
106 | KEYC_MOUSE_STRING(TRIPLECLICK1, TripleClick1), |
107 | KEYC_MOUSE_STRING(TRIPLECLICK2, TripleClick2), |
108 | KEYC_MOUSE_STRING(TRIPLECLICK3, TripleClick3), |
109 | }; |
110 | |
111 | /* Find key string in table. */ |
112 | static key_code |
113 | key_string_search_table(const char *string) |
114 | { |
115 | u_int i, user; |
116 | |
117 | for (i = 0; i < nitems(key_string_table); i++) { |
118 | if (strcasecmp(string, key_string_table[i].string) == 0) |
119 | return (key_string_table[i].key); |
120 | } |
121 | |
122 | if (sscanf(string, "User%u" , &user) == 1 && user < KEYC_NUSER) |
123 | return (KEYC_USER + user); |
124 | |
125 | return (KEYC_UNKNOWN); |
126 | } |
127 | |
128 | /* Find modifiers. */ |
129 | static key_code |
130 | key_string_get_modifiers(const char **string) |
131 | { |
132 | key_code modifiers; |
133 | |
134 | modifiers = 0; |
135 | while (((*string)[0] != '\0') && (*string)[1] == '-') { |
136 | switch ((*string)[0]) { |
137 | case 'C': |
138 | case 'c': |
139 | modifiers |= KEYC_CTRL; |
140 | break; |
141 | case 'M': |
142 | case 'm': |
143 | modifiers |= KEYC_ESCAPE; |
144 | break; |
145 | case 'S': |
146 | case 's': |
147 | modifiers |= KEYC_SHIFT; |
148 | break; |
149 | default: |
150 | *string = NULL; |
151 | return (0); |
152 | } |
153 | *string += 2; |
154 | } |
155 | return (modifiers); |
156 | } |
157 | |
158 | /* Lookup a string and convert to a key value. */ |
159 | key_code |
160 | key_string_lookup_string(const char *string) |
161 | { |
162 | static const char *other = "!#()+,-.0123456789:;<=>'\r\t" ; |
163 | key_code key; |
164 | u_int u; |
165 | key_code modifiers; |
166 | struct utf8_data ud; |
167 | u_int i; |
168 | enum utf8_state more; |
169 | wchar_t wc; |
170 | |
171 | /* Is this no key or any key? */ |
172 | if (strcasecmp(string, "None" ) == 0) |
173 | return (KEYC_NONE); |
174 | if (strcasecmp(string, "Any" ) == 0) |
175 | return (KEYC_ANY); |
176 | |
177 | /* Is this a hexadecimal value? */ |
178 | if (string[0] == '0' && string[1] == 'x') { |
179 | if (sscanf(string + 2, "%x" , &u) != 1) |
180 | return (KEYC_UNKNOWN); |
181 | if (u > 0x1fffff) |
182 | return (KEYC_UNKNOWN); |
183 | return (u); |
184 | } |
185 | |
186 | /* Check for modifiers. */ |
187 | modifiers = 0; |
188 | if (string[0] == '^' && string[1] != '\0') { |
189 | modifiers |= KEYC_CTRL; |
190 | string++; |
191 | } |
192 | modifiers |= key_string_get_modifiers(&string); |
193 | if (string == NULL || string[0] == '\0') |
194 | return (KEYC_UNKNOWN); |
195 | |
196 | /* Is this a standard ASCII key? */ |
197 | if (string[1] == '\0' && (u_char)string[0] <= 127) { |
198 | key = (u_char)string[0]; |
199 | if (key < 32) |
200 | return (KEYC_UNKNOWN); |
201 | } else { |
202 | /* Try as a UTF-8 key. */ |
203 | if ((more = utf8_open(&ud, (u_char)*string)) == UTF8_MORE) { |
204 | if (strlen(string) != ud.size) |
205 | return (KEYC_UNKNOWN); |
206 | for (i = 1; i < ud.size; i++) |
207 | more = utf8_append(&ud, (u_char)string[i]); |
208 | if (more != UTF8_DONE) |
209 | return (KEYC_UNKNOWN); |
210 | if (utf8_combine(&ud, &wc) != UTF8_DONE) |
211 | return (KEYC_UNKNOWN); |
212 | return (wc | modifiers); |
213 | } |
214 | |
215 | /* Otherwise look the key up in the table. */ |
216 | key = key_string_search_table(string); |
217 | if (key == KEYC_UNKNOWN) |
218 | return (KEYC_UNKNOWN); |
219 | } |
220 | |
221 | /* Convert the standard control keys. */ |
222 | if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && !strchr(other, key)) { |
223 | if (key >= 97 && key <= 122) |
224 | key -= 96; |
225 | else if (key >= 64 && key <= 95) |
226 | key -= 64; |
227 | else if (key == 32) |
228 | key = 0; |
229 | else if (key == '?') |
230 | key = 127; |
231 | else if (key == 63) |
232 | key = KEYC_BSPACE; |
233 | else |
234 | return (KEYC_UNKNOWN); |
235 | modifiers &= ~KEYC_CTRL; |
236 | } |
237 | |
238 | return (key | modifiers); |
239 | } |
240 | |
241 | /* Convert a key code into string format, with prefix if necessary. */ |
242 | const char * |
243 | key_string_lookup_key(key_code key) |
244 | { |
245 | static char out[32]; |
246 | char tmp[8]; |
247 | const char *s; |
248 | u_int i; |
249 | struct utf8_data ud; |
250 | size_t off; |
251 | |
252 | *out = '\0'; |
253 | |
254 | /* Literal keys are themselves. */ |
255 | if (key & KEYC_LITERAL) { |
256 | snprintf(out, sizeof out, "%c" , (int)(key & 0xff)); |
257 | return (out); |
258 | } |
259 | |
260 | /* Display C-@ as C-Space. */ |
261 | if ((key & KEYC_MASK_KEY) == 0) |
262 | key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD); |
263 | |
264 | /* Fill in the modifiers. */ |
265 | if (key & KEYC_CTRL) |
266 | strlcat(out, "C-" , sizeof out); |
267 | if (key & KEYC_ESCAPE) |
268 | strlcat(out, "M-" , sizeof out); |
269 | if (key & KEYC_SHIFT) |
270 | strlcat(out, "S-" , sizeof out); |
271 | key &= KEYC_MASK_KEY; |
272 | |
273 | /* Handle no key. */ |
274 | if (key == KEYC_NONE) |
275 | return ("None" ); |
276 | |
277 | /* Handle special keys. */ |
278 | if (key == KEYC_UNKNOWN) { |
279 | s = "Unknown" ; |
280 | goto append; |
281 | } |
282 | if (key == KEYC_ANY) { |
283 | s = "Any" ; |
284 | goto append; |
285 | } |
286 | if (key == KEYC_FOCUS_IN) { |
287 | s = "FocusIn" ; |
288 | goto append; |
289 | } |
290 | if (key == KEYC_FOCUS_OUT) { |
291 | s = "FocusOut" ; |
292 | goto append; |
293 | } |
294 | if (key == KEYC_PASTE_START) { |
295 | s = "PasteStart" ; |
296 | goto append; |
297 | } |
298 | if (key == KEYC_PASTE_END) { |
299 | s = "PasteEnd" ; |
300 | goto append; |
301 | } |
302 | if (key == KEYC_MOUSE) { |
303 | s = "Mouse" ; |
304 | goto append; |
305 | } |
306 | if (key == KEYC_DRAGGING) { |
307 | s = "Dragging" ; |
308 | goto append; |
309 | } |
310 | if (key == KEYC_MOUSEMOVE_PANE) { |
311 | s = "MouseMovePane" ; |
312 | goto append; |
313 | } |
314 | if (key == KEYC_MOUSEMOVE_STATUS) { |
315 | s = "MouseMoveStatus" ; |
316 | goto append; |
317 | } |
318 | if (key == KEYC_MOUSEMOVE_STATUS_LEFT) { |
319 | s = "MouseMoveStatusLeft" ; |
320 | goto append; |
321 | } |
322 | if (key == KEYC_MOUSEMOVE_STATUS_RIGHT) { |
323 | s = "MouseMoveStatusRight" ; |
324 | goto append; |
325 | } |
326 | if (key == KEYC_MOUSEMOVE_BORDER) { |
327 | s = "MouseMoveBorder" ; |
328 | goto append; |
329 | } |
330 | if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) { |
331 | snprintf(tmp, sizeof tmp, "User%u" , (u_int)(key - KEYC_USER)); |
332 | strlcat(out, tmp, sizeof out); |
333 | return (out); |
334 | } |
335 | |
336 | /* Try the key against the string table. */ |
337 | for (i = 0; i < nitems(key_string_table); i++) { |
338 | if (key == key_string_table[i].key) |
339 | break; |
340 | } |
341 | if (i != nitems(key_string_table)) { |
342 | strlcat(out, key_string_table[i].string, sizeof out); |
343 | return (out); |
344 | } |
345 | |
346 | /* Is this a UTF-8 key? */ |
347 | if (key > 127 && key < KEYC_BASE) { |
348 | if (utf8_split(key, &ud) == UTF8_DONE) { |
349 | off = strlen(out); |
350 | memcpy(out + off, ud.data, ud.size); |
351 | out[off + ud.size] = '\0'; |
352 | return (out); |
353 | } |
354 | } |
355 | |
356 | /* Invalid keys are errors. */ |
357 | if (key > 255) { |
358 | snprintf(out, sizeof out, "Invalid#%llx" , key); |
359 | return (out); |
360 | } |
361 | |
362 | /* Check for standard or control key. */ |
363 | if (key <= 32) { |
364 | if (key == 0 || key > 26) |
365 | xsnprintf(tmp, sizeof tmp, "C-%c" , (int)(64 + key)); |
366 | else |
367 | xsnprintf(tmp, sizeof tmp, "C-%c" , (int)(96 + key)); |
368 | } else if (key >= 32 && key <= 126) { |
369 | tmp[0] = key; |
370 | tmp[1] = '\0'; |
371 | } else if (key == 127) |
372 | xsnprintf(tmp, sizeof tmp, "C-?" ); |
373 | else if (key >= 128) |
374 | xsnprintf(tmp, sizeof tmp, "\\%llo" , key); |
375 | |
376 | strlcat(out, tmp, sizeof out); |
377 | return (out); |
378 | |
379 | append: |
380 | strlcat(out, s, sizeof out); |
381 | return (out); |
382 | } |
383 | |