Annotation of src/usr.bin/tmux/tty-keys.c, Revision 1.3
1.3 ! nicm 1: /* $OpenBSD: tty-keys.c,v 1.2 2009/07/21 17:57:29 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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: #include <sys/time.h>
21:
22: #include <string.h>
1.3 ! nicm 23: #include <termios.h>
! 24: #include <unistd.h>
1.1 nicm 25:
26: #include "tmux.h"
27:
28: void tty_keys_add(struct tty *, const char *, int, int);
29: int tty_keys_parse_xterm(struct tty *, char *, size_t, size_t *);
30: int tty_keys_parse_mouse(struct tty *, char *, size_t, size_t *, u_char *);
31:
32: struct tty_key_ent {
33: enum tty_code_code code;
34: const char *string;
35:
36: int key;
37: int flags;
38: };
39:
40: struct tty_key_ent tty_keys[] = {
41: /* Function keys. */
42: { TTYC_KF1, NULL, KEYC_F1, TTYKEY_CTRL },
43: { TTYC_KF2, NULL, KEYC_F2, TTYKEY_CTRL },
44: { TTYC_KF3, NULL, KEYC_F3, TTYKEY_CTRL },
45: { TTYC_KF4, NULL, KEYC_F4, TTYKEY_CTRL },
46: { TTYC_KF5, NULL, KEYC_F5, TTYKEY_CTRL },
47: { TTYC_KF6, NULL, KEYC_F6, TTYKEY_CTRL },
48: { TTYC_KF7, NULL, KEYC_F7, TTYKEY_CTRL },
49: { TTYC_KF8, NULL, KEYC_F8, TTYKEY_CTRL },
50: { TTYC_KF9, NULL, KEYC_F9, TTYKEY_CTRL },
51: { TTYC_KF10, NULL, KEYC_F10, TTYKEY_CTRL },
52: { TTYC_KF11, NULL, KEYC_F11, TTYKEY_CTRL },
53: { TTYC_KF12, NULL, KEYC_F12, TTYKEY_CTRL },
54: { TTYC_KF13, NULL, KEYC_F13, TTYKEY_CTRL },
55: { TTYC_KF14, NULL, KEYC_F14, TTYKEY_CTRL },
56: { TTYC_KF15, NULL, KEYC_F15, TTYKEY_CTRL },
57: { TTYC_KF16, NULL, KEYC_F16, TTYKEY_CTRL },
58: { TTYC_KF17, NULL, KEYC_F17, TTYKEY_CTRL },
59: { TTYC_KF18, NULL, KEYC_F18, TTYKEY_CTRL },
60: { TTYC_KF19, NULL, KEYC_F19, TTYKEY_CTRL },
61: { TTYC_KF20, NULL, KEYC_F20, TTYKEY_CTRL },
62: { TTYC_KICH1, NULL, KEYC_IC, TTYKEY_CTRL },
63: { TTYC_KDCH1, NULL, KEYC_DC, TTYKEY_CTRL },
64: { TTYC_KHOME, NULL, KEYC_HOME, TTYKEY_CTRL },
65: { TTYC_KEND, NULL, KEYC_END, TTYKEY_CTRL },
66: { TTYC_KNP, NULL, KEYC_NPAGE, TTYKEY_CTRL },
67: { TTYC_KPP, NULL, KEYC_PPAGE, TTYKEY_CTRL },
68: { TTYC_KCBT, NULL, KEYC_BTAB, TTYKEY_CTRL },
69:
70: /* Arrow keys. */
71: { 0, "\033OA", KEYC_UP, TTYKEY_RAW },
72: { 0, "\033OB", KEYC_DOWN, TTYKEY_RAW },
73: { 0, "\033OC", KEYC_RIGHT, TTYKEY_RAW },
74: { 0, "\033OD", KEYC_LEFT, TTYKEY_RAW },
75:
76: { 0, "\033[A", KEYC_UP, TTYKEY_RAW },
77: { 0, "\033[B", KEYC_DOWN, TTYKEY_RAW },
78: { 0, "\033[C", KEYC_RIGHT, TTYKEY_RAW },
79: { 0, "\033[D", KEYC_LEFT, TTYKEY_RAW },
80:
1.2 nicm 81: { 0, "\033Oa", KEYC_UP | KEYC_CTRL, TTYKEY_RAW },
82: { 0, "\033Ob", KEYC_DOWN | KEYC_CTRL, TTYKEY_RAW },
83: { 0, "\033Oc", KEYC_RIGHT | KEYC_CTRL, TTYKEY_RAW },
84: { 0, "\033Od", KEYC_LEFT | KEYC_CTRL, TTYKEY_RAW },
85: { 0, "\033[a", KEYC_UP | KEYC_SHIFT, TTYKEY_RAW },
86: { 0, "\033[b", KEYC_DOWN | KEYC_SHIFT, TTYKEY_RAW },
87: { 0, "\033[c", KEYC_RIGHT | KEYC_SHIFT, TTYKEY_RAW },
88: { 0, "\033[d", KEYC_LEFT | KEYC_SHIFT, TTYKEY_RAW },
1.1 nicm 89:
90: { TTYC_KCUU1, NULL, KEYC_UP, TTYKEY_CTRL },
91: { TTYC_KCUD1, NULL, KEYC_DOWN, TTYKEY_CTRL },
92: { TTYC_KCUB1, NULL, KEYC_LEFT, TTYKEY_CTRL },
93: { TTYC_KCUF1, NULL, KEYC_RIGHT, TTYKEY_CTRL },
94:
95: /*
96: * Numeric keypad. termcap and terminfo are totally confusing for this.
97: * There are definitions for some keypad keys and for function keys,
98: * but these seem to now be used for the real function keys rather than
99: * for the keypad keys in application mode (which is different from
100: * what it says in the termcap file). So, we just hardcode the vt100
101: * escape sequences here and always put the terminal into keypad_xmit
102: * mode. Translation of numbers mode/applications mode is done in
103: * input-keys.c.
104: */
105: { 0, "\033Oo", KEYC_KP0_1, TTYKEY_RAW },
106: { 0, "\033Oj", KEYC_KP0_2, TTYKEY_RAW },
107: { 0, "\033Om", KEYC_KP0_3, TTYKEY_RAW },
108: { 0, "\033Ow", KEYC_KP1_0, TTYKEY_RAW },
109: { 0, "\033Ox", KEYC_KP1_1, TTYKEY_RAW },
110: { 0, "\033Oy", KEYC_KP1_2, TTYKEY_RAW },
111: { 0, "\033Ok", KEYC_KP1_3, TTYKEY_RAW },
112: { 0, "\033Ot", KEYC_KP2_0, TTYKEY_RAW },
113: { 0, "\033Ou", KEYC_KP2_1, TTYKEY_RAW },
114: { 0, "\033Ov", KEYC_KP2_2, TTYKEY_RAW },
115: { 0, "\033Oq", KEYC_KP3_0, TTYKEY_RAW },
116: { 0, "\033Or", KEYC_KP3_1, TTYKEY_RAW },
117: { 0, "\033Os", KEYC_KP3_2, TTYKEY_RAW },
118: { 0, "\033OM", KEYC_KP3_3, TTYKEY_RAW },
119: { 0, "\033Op", KEYC_KP4_0, TTYKEY_RAW },
120: { 0, "\033On", KEYC_KP4_2, TTYKEY_RAW },
121: };
122:
123: RB_GENERATE(tty_keys, tty_key, entry, tty_keys_cmp);
124:
125: struct tty_key *tty_keys_find(struct tty *, char *, size_t, size_t *);
126:
127: int
128: tty_keys_cmp(struct tty_key *k1, struct tty_key *k2)
129: {
130: return (strcmp(k1->string, k2->string));
131: }
132:
133: void
134: tty_keys_add(struct tty *tty, const char *s, int key, int flags)
135: {
136: struct tty_key *tk, *tl;
137:
138: tk = xmalloc(sizeof *tk);
139: tk->string = xstrdup(s);
140: tk->key = key;
141: tk->flags = flags;
142:
143: if ((tl = RB_INSERT(tty_keys, &tty->ktree, tk)) != NULL) {
144: xfree(tk->string);
145: xfree(tk);
146: log_debug("key exists: %s (old %x, new %x)", s, tl->key, key);
147: return;
148: }
149:
150: if (strlen(tk->string) > tty->ksize)
151: tty->ksize = strlen(tk->string);
152: log_debug("new key %x: size now %zu (%s)", key, tty->ksize, tk->string);
153: }
154:
155: void
156: tty_keys_init(struct tty *tty)
157: {
158: struct tty_key_ent *tke;
159: u_int i;
160: const char *s;
161: char tmp[64];
162:
163: RB_INIT(&tty->ktree);
164:
165: tty->ksize = 0;
166: for (i = 0; i < nitems(tty_keys); i++) {
167: tke = &tty_keys[i];
168:
169: if (tke->flags & TTYKEY_RAW)
170: s = tke->string;
171: else {
172: if (!tty_term_has(tty->term, tke->code))
173: continue;
174: s = tty_term_string(tty->term, tke->code);
175: if (s[0] != '\033' || s[1] == '\0')
176: continue;
177: }
178:
179: tty_keys_add(tty, s + 1, tke->key, tke->flags);
180: if (tke->flags & TTYKEY_CTRL) {
181: if (strlcpy(tmp, s, sizeof tmp) >= sizeof tmp)
182: continue;
183: tmp[strlen(tmp) - 1] ^= 0x20;
1.2 nicm 184: tty_keys_add(tty, tmp + 1, tke->key | KEYC_CTRL, 0);
1.1 nicm 185: }
186: }
187: }
188:
189: void
190: tty_keys_free(struct tty *tty)
191: {
192: struct tty_key *tk;
193:
194: while (!RB_EMPTY(&tty->ktree)) {
195: tk = RB_ROOT(&tty->ktree);
196: RB_REMOVE(tty_keys, &tty->ktree, tk);
197: xfree(tk->string);
198: xfree(tk);
199: }
200: }
201:
202: struct tty_key *
203: tty_keys_find(struct tty *tty, char *buf, size_t len, size_t *size)
204: {
205: struct tty_key *tk, tl;
206: char *s;
207:
208: if (len == 0)
209: return (NULL);
210:
211: s = xmalloc(tty->ksize + 1);
212: for (*size = tty->ksize; (*size) > 0; (*size)--) {
213: if ((*size) > len)
214: continue;
215: memcpy(s, buf, *size);
216: s[*size] = '\0';
217:
218: log_debug2("looking for key: %s", s);
219:
220: tl.string = s;
221: tk = RB_FIND(tty_keys, &tty->ktree, &tl);
222: if (tk != NULL) {
223: log_debug2("got key: 0x%x", tk->key);
224: xfree(s);
225: return (tk);
226: }
227: }
228: xfree(s);
229:
230: return (NULL);
231: }
232:
233: int
234: tty_keys_next(struct tty *tty, int *key, u_char *mouse)
235: {
236: struct tty_key *tk;
237: struct timeval tv;
238: char *buf;
239: size_t len, size;
1.3 ! nicm 240: cc_t bspace;
1.1 nicm 241:
242: buf = BUFFER_OUT(tty->in);
243: len = BUFFER_USED(tty->in);
244: if (len == 0)
245: return (1);
246: log_debug("keys are %zu (%.*s)", len, (int) len, buf);
247:
248: /* If a normal key, return it. */
249: if (*buf != '\033') {
250: *key = buffer_read8(tty->in);
1.3 ! nicm 251:
! 252: /*
! 253: * Check for backspace key using termios VERASE - the terminfo
! 254: * kbs entry is extremely unreliable, so cannot be safely
! 255: * used. termios should have a better idea.
! 256: */
! 257: bspace = tty->tio.c_cc[VERASE];
! 258: if (bspace != _POSIX_VDISABLE && *key == bspace)
! 259: *key = KEYC_BSPACE;
1.1 nicm 260: goto found;
261: }
262:
263: /* Look for matching key string and return if found. */
264: tk = tty_keys_find(tty, buf + 1, len - 1, &size);
265: if (tk != NULL) {
266: buffer_remove(tty->in, size + 1);
267: *key = tk->key;
268: goto found;
269: }
270:
271: /* Not found. Is this a mouse key press? */
272: *key = tty_keys_parse_mouse(tty, buf, len, &size, mouse);
273: if (*key != KEYC_NONE) {
274: buffer_remove(tty->in, size);
275: goto found;
276: }
277:
278: /* Not found. Try to parse xterm-type arguments. */
279: *key = tty_keys_parse_xterm(tty, buf, len, &size);
280: if (*key != KEYC_NONE) {
281: buffer_remove(tty->in, size);
282: goto found;
283: }
284:
285: /* Escape but no key string. If the timer isn't started, start it. */
286: if (!(tty->flags & TTY_ESCAPE)) {
287: tv.tv_sec = 0;
288: tv.tv_usec = ESCAPE_PERIOD * 1000L;
289: if (gettimeofday(&tty->key_timer, NULL) != 0)
290: fatal("gettimeofday");
291: timeradd(&tty->key_timer, &tv, &tty->key_timer);
292:
293: tty->flags |= TTY_ESCAPE;
294: return (1);
295: }
296:
297: /* Skip the escape. */
298: buf++;
299: len--;
300:
301: /* Is there a normal key following? */
302: if (len != 0 && *buf != '\033') {
303: buffer_remove(tty->in, 1);
1.2 nicm 304: *key = buffer_read8(tty->in) | KEYC_ESCAPE;
1.1 nicm 305: goto found;
306: }
307:
308: /* Or a key string? */
309: if (len > 1) {
310: tk = tty_keys_find(tty, buf + 1, len - 1, &size);
311: if (tk != NULL) {
312: buffer_remove(tty->in, size + 2);
1.2 nicm 313: *key = tk->key | KEYC_ESCAPE;
1.1 nicm 314: goto found;
315: }
316: }
317:
318: /* If the timer hasn't expired, keep waiting. */
319: if (gettimeofday(&tv, NULL) != 0)
320: fatal("gettimeofday");
321: if (timercmp(&tty->key_timer, &tv, >))
322: return (1);
323:
324: /* Give up and return the escape. */
325: buffer_remove(tty->in, 1);
326: *key = '\033';
327:
328: found:
329: tty->flags &= ~TTY_ESCAPE;
330: return (0);
331: }
332:
333: int
334: tty_keys_parse_mouse(
335: unused struct tty *tty, char *buf, size_t len, size_t *size, u_char *mouse)
336: {
337: /*
338: * Mouse sequences are \033[M followed by three characters indicating
339: * buttons, X and Y, all based at 32 with 1,1 top-left.
340: */
341:
342: log_debug("mouse input is: %.*s", (int) len, buf);
343: if (len != 6 || memcmp(buf, "\033[M", 3) != 0)
344: return (KEYC_NONE);
345: *size = 6;
346:
347: if (buf[3] < 32 || buf[4] < 33 || buf[5] < 33)
348: return (KEYC_NONE);
349:
350: mouse[0] = buf[3] - 32;
351: mouse[1] = buf[4] - 33;
352: mouse[2] = buf[5] - 33;
353: return (KEYC_MOUSE);
354: }
355:
356: int
357: tty_keys_parse_xterm(struct tty *tty, char *buf, size_t len, size_t *size)
358: {
359: struct tty_key *tk;
360: char tmp[5];
361: size_t tmplen;
362: int key;
363:
364: /*
365: * xterm sequences with modifier keys are of the form:
366: *
367: * ^[[1;xD becomes ^[[D
368: * ^[[5;x~ becomes ^[[5~
369: *
370: * This function is a bit of a hack. Need to figure out what exact
371: * format and meaning xterm outputs and fix it. XXX
372: */
373:
374: log_debug("xterm input is: %.*s", (int) len, buf);
375: if (len != 6 || memcmp(buf, "\033[1;", 4) != 0)
376: return (KEYC_NONE);
377: *size = 6;
378:
379: tmplen = 0;
380: tmp[tmplen++] = '[';
381: if (buf[5] == '~') {
382: tmp[tmplen++] = buf[2];
383: tmp[tmplen++] = '~';
384: } else
385: tmp[tmplen++] = buf[5];
386: log_debug("xterm output is: %.*s", (int) tmplen, tmp);
387:
388: tk = tty_keys_find(tty, tmp, tmplen, size);
389: if (tk == NULL)
390: return (KEYC_NONE);
391: key = tk->key;
392:
393: switch (buf[4]) {
394: case '8':
1.2 nicm 395: key |= KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL;
1.1 nicm 396: break;
397: case '7':
1.2 nicm 398: key |= KEYC_ESCAPE|KEYC_CTRL;
1.1 nicm 399: break;
400: case '6':
1.2 nicm 401: key |= KEYC_SHIFT|KEYC_CTRL;
1.1 nicm 402: break;
403: case '5':
1.2 nicm 404: key |= KEYC_CTRL;
1.1 nicm 405: break;
406: case '4':
1.2 nicm 407: key |= KEYC_SHIFT|KEYC_ESCAPE;
1.1 nicm 408: break;
409: case '3':
1.2 nicm 410: key |= KEYC_ESCAPE;
1.1 nicm 411: break;
412: case '2':
1.2 nicm 413: key |= KEYC_SHIFT;
1.1 nicm 414: break;
415: }
416:
417: *size = 6;
418: return (key);
419: }