1 /* $OpenBSD: tty-keys.c,v 1.5 2009/10/11 07:01:10 nicm Exp $ */ 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> 23 #include <termios.h> 24 #include <unistd.h> 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(char *, size_t, size_t *, struct mouse_event *); 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 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 }, 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; 184 tty_keys_add(tty, tmp + 1, tke->key | KEYC_CTRL, 0); 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, struct mouse_event *mouse) 235 { 236 struct tty_key *tk; 237 struct timeval tv; 238 char *buf; 239 size_t len, size; 240 cc_t bspace; 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); 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; 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(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 failed"); 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); 304 *key = buffer_read8(tty->in) | KEYC_ESCAPE; 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); 313 *key = tk->key | KEYC_ESCAPE; 314 goto found; 315 } 316 } 317 318 /* If the timer hasn't expired, keep waiting. */ 319 if (gettimeofday(&tv, NULL) != 0) 320 fatal("gettimeofday failed"); 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(char *buf, size_t len, size_t *size, struct mouse_event *m) 335 { 336 /* 337 * Mouse sequences are \033[M followed by three characters indicating 338 * buttons, X and Y, all based at 32 with 1,1 top-left. 339 */ 340 341 log_debug("mouse input is: %.*s", (int) len, buf); 342 if (len != 6 || memcmp(buf, "\033[M", 3) != 0) 343 return (KEYC_NONE); 344 *size = 6; 345 346 m->b = buf[3]; 347 m->x = buf[4]; 348 m->y = buf[5]; 349 if (m->b < 32 || m->x < 33 || m->y < 33) 350 return (KEYC_NONE); 351 m->b -= 32; 352 m->x -= 33; 353 m->y -= 33; 354 return (KEYC_MOUSE); 355 } 356 357 int 358 tty_keys_parse_xterm(struct tty *tty, char *buf, size_t len, size_t *size) 359 { 360 struct tty_key *tk; 361 char tmp[5]; 362 size_t tmplen; 363 int key; 364 365 /* 366 * xterm sequences with modifier keys are of the form: 367 * 368 * ^[[1;xD becomes ^[[D 369 * ^[[5;x~ becomes ^[[5~ 370 * 371 * This function is a bit of a hack. Need to figure out what exact 372 * format and meaning xterm outputs and fix it. XXX 373 */ 374 375 log_debug("xterm input is: %.*s", (int) len, buf); 376 if (len != 6 || memcmp(buf, "\033[1;", 4) != 0) 377 return (KEYC_NONE); 378 *size = 6; 379 380 tmplen = 0; 381 tmp[tmplen++] = '['; 382 if (buf[5] == '~') { 383 tmp[tmplen++] = buf[2]; 384 tmp[tmplen++] = '~'; 385 } else 386 tmp[tmplen++] = buf[5]; 387 log_debug("xterm output is: %.*s", (int) tmplen, tmp); 388 389 tk = tty_keys_find(tty, tmp, tmplen, size); 390 if (tk == NULL) 391 return (KEYC_NONE); 392 key = tk->key; 393 394 switch (buf[4]) { 395 case '8': 396 key |= KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL; 397 break; 398 case '7': 399 key |= KEYC_ESCAPE|KEYC_CTRL; 400 break; 401 case '6': 402 key |= KEYC_SHIFT|KEYC_CTRL; 403 break; 404 case '5': 405 key |= KEYC_CTRL; 406 break; 407 case '4': 408 key |= KEYC_SHIFT|KEYC_ESCAPE; 409 break; 410 case '3': 411 key |= KEYC_ESCAPE; 412 break; 413 case '2': 414 key |= KEYC_SHIFT; 415 break; 416 } 417 418 *size = 6; 419 return (key); 420 } 421