Annotation of src/usr.bin/tmux/input-keys.c, Revision 1.72
1.72 ! nicm 1: /* $OpenBSD: input-keys.c,v 1.71 2020/04/07 13:38:30 nicm Exp $ */
1.1 nicm 2:
3: /*
1.53 nicm 4: * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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 <stdint.h>
22: #include <stdlib.h>
23: #include <string.h>
24:
25: #include "tmux.h"
26:
1.8 nicm 27: /*
28: * This file is rather misleadingly named, it contains the code which takes a
29: * key code and translates it into something suitable to be sent to the
30: * application running in a pane (similar to input.c does in the other
31: * direction with output).
32: */
33:
1.56 nicm 34: static void input_key_mouse(struct window_pane *, struct mouse_event *);
1.42 nicm 35:
1.72 ! nicm 36: /* Entry in the key tree. */
! 37: struct input_key_entry {
! 38: key_code key;
! 39: const char *data;
! 40:
! 41: RB_ENTRY(input_key_entry) entry;
1.1 nicm 42: };
1.72 ! nicm 43: RB_HEAD(input_key_tree, input_key_entry);
! 44:
! 45: /* Tree of input keys. */
! 46: static int input_key_cmp(struct input_key_entry *,
! 47: struct input_key_entry *);
! 48: RB_GENERATE_STATIC(input_key_tree, input_key_entry, entry, input_key_cmp);
! 49: struct input_key_tree input_key_tree = RB_INITIALIZER(&input_key_tree);
1.1 nicm 50:
1.72 ! nicm 51: /* List of default keys, the tree is built from this. */
! 52: static struct input_key_entry input_key_defaults[] = {
1.61 nicm 53: /* Paste keys. */
1.72 ! nicm 54: { .key = KEYC_PASTE_START,
! 55: .data = "\033[200~"
! 56: },
! 57: { .key = KEYC_PASTE_END,
! 58: .data = "\033[201~"
! 59: },
1.3 nicm 60:
1.1 nicm 61: /* Function keys. */
1.72 ! nicm 62: { .key = KEYC_F1,
! 63: .data = "\033OP"
! 64: },
! 65: { .key = KEYC_F2,
! 66: .data = "\033OQ"
! 67: },
! 68: { .key = KEYC_F3,
! 69: .data = "\033OR"
! 70: },
! 71: { .key = KEYC_F4,
! 72: .data = "\033OS"
! 73: },
! 74: { .key = KEYC_F5,
! 75: .data = "\033[15~"
! 76: },
! 77: { .key = KEYC_F6,
! 78: .data = "\033[17~"
! 79: },
! 80: { .key = KEYC_F7,
! 81: .data = "\033[18~"
! 82: },
! 83: { .key = KEYC_F8,
! 84: .data = "\033[19~"
! 85: },
! 86: { .key = KEYC_F9,
! 87: .data = "\033[20~"
! 88: },
! 89: { .key = KEYC_F10,
! 90: .data = "\033[21~"
! 91: },
! 92: { .key = KEYC_F11,
! 93: .data = "\033[23~"
! 94: },
! 95: { .key = KEYC_F12,
! 96: .data = "\033[24~"
! 97: },
! 98: { .key = KEYC_F1|KEYC_SHIFT,
! 99: .data = "\033[25~"
! 100: },
! 101: { .key = KEYC_F2|KEYC_SHIFT,
! 102: .data = "\033[26~"
! 103: },
! 104: { .key = KEYC_F3|KEYC_SHIFT,
! 105: .data = "\033[28~"
! 106: },
! 107: { .key = KEYC_F4|KEYC_SHIFT,
! 108: .data = "\033[29~"
! 109: },
! 110: { .key = KEYC_F5|KEYC_SHIFT,
! 111: .data = "\033[31~"
! 112: },
! 113: { .key = KEYC_F6|KEYC_SHIFT,
! 114: .data = "\033[32~"
! 115: },
! 116: { .key = KEYC_F7|KEYC_SHIFT,
! 117: .data = "\033[33~"
! 118: },
! 119: { .key = KEYC_F8|KEYC_SHIFT,
! 120: .data = "\033[34~"
! 121: },
! 122: { .key = KEYC_IC,
! 123: .data = "\033[2~"
! 124: },
! 125: { .key = KEYC_DC,
! 126: .data = "\033[3~"
! 127: },
! 128: { .key = KEYC_HOME,
! 129: .data = "\033[1~"
! 130: },
! 131: { .key = KEYC_END,
! 132: .data = "\033[4~"
! 133: },
! 134: { .key = KEYC_NPAGE,
! 135: .data = "\033[6~"
! 136: },
! 137: { .key = KEYC_PPAGE,
! 138: .data = "\033[5~"
! 139: },
! 140: { .key = KEYC_BTAB,
! 141: .data = "\033[Z"
! 142: },
! 143:
! 144: /* Arrow keys. */
! 145: { .key = KEYC_UP|KEYC_CURSOR,
! 146: .data = "\033OA"
! 147: },
! 148: { .key = KEYC_DOWN|KEYC_CURSOR,
! 149: .data = "\033OB"
! 150: },
! 151: { .key = KEYC_RIGHT|KEYC_CURSOR,
! 152: .data = "\033OC"
! 153: },
! 154: { .key = KEYC_LEFT|KEYC_CURSOR,
! 155: .data = "\033OD"
! 156: },
! 157: { .key = KEYC_UP,
! 158: .data = "\033[A"
! 159: },
! 160: { .key = KEYC_DOWN,
! 161: .data = "\033[B"
! 162: },
! 163: { .key = KEYC_RIGHT,
! 164: .data = "\033[C"
! 165: },
! 166: { .key = KEYC_LEFT,
! 167: .data = "\033[D"
! 168: },
! 169:
! 170: /* Keypad keys. */
! 171: { .key = KEYC_KP_SLASH|KEYC_KEYPAD,
! 172: .data = "\033Oo"
! 173: },
! 174: { .key = KEYC_KP_STAR|KEYC_KEYPAD,
! 175: .data = "\033Oj"
! 176: },
! 177: { .key = KEYC_KP_MINUS|KEYC_KEYPAD,
! 178: .data = "\033Om"
! 179: },
! 180: { .key = KEYC_KP_SEVEN|KEYC_KEYPAD,
! 181: .data = "\033Ow"
! 182: },
! 183: { .key = KEYC_KP_EIGHT|KEYC_KEYPAD,
! 184: .data = "\033Ox"
! 185: },
! 186: { .key = KEYC_KP_NINE|KEYC_KEYPAD,
! 187: .data = "\033Oy"
! 188: },
! 189: { .key = KEYC_KP_PLUS|KEYC_KEYPAD,
! 190: .data = "\033Ok"
! 191: },
! 192: { .key = KEYC_KP_FOUR|KEYC_KEYPAD,
! 193: .data = "\033Ot"
! 194: },
! 195: { .key = KEYC_KP_FIVE|KEYC_KEYPAD,
! 196: .data = "\033Ou"
! 197: },
! 198: { .key = KEYC_KP_SIX|KEYC_KEYPAD,
! 199: .data = "\033Ov"
! 200: },
! 201: { .key = KEYC_KP_ONE|KEYC_KEYPAD,
! 202: .data = "\033Oq"
! 203: },
! 204: { .key = KEYC_KP_TWO|KEYC_KEYPAD,
! 205: .data = "\033Or"
! 206: },
! 207: { .key = KEYC_KP_THREE|KEYC_KEYPAD,
! 208: .data = "\033Os"
! 209: },
! 210: { .key = KEYC_KP_ENTER|KEYC_KEYPAD,
! 211: .data = "\033OM"
! 212: },
! 213: { .key = KEYC_KP_ZERO|KEYC_KEYPAD,
! 214: .data = "\033Op"
! 215: },
! 216: { .key = KEYC_KP_PERIOD|KEYC_KEYPAD,
! 217: .data = "\033On"
! 218: },
! 219: { .key = KEYC_KP_SLASH,
! 220: .data = "/"
! 221: },
! 222: { .key = KEYC_KP_STAR,
! 223: .data = "*"
! 224: },
! 225: { .key = KEYC_KP_MINUS,
! 226: .data = "-"
! 227: },
! 228: { .key = KEYC_KP_SEVEN,
! 229: .data = "7"
! 230: },
! 231: { .key = KEYC_KP_EIGHT,
! 232: .data = "8"
! 233: },
! 234: { .key = KEYC_KP_NINE,
! 235: .data = "9"
! 236: },
! 237: { .key = KEYC_KP_PLUS,
! 238: .data = "+"
! 239: },
! 240: { .key = KEYC_KP_FOUR,
! 241: .data = "4"
! 242: },
! 243: { .key = KEYC_KP_FIVE,
! 244: .data = "5"
! 245: },
! 246: { .key = KEYC_KP_SIX,
! 247: .data = "6"
! 248: },
! 249: { .key = KEYC_KP_ONE,
! 250: .data = "1"
! 251: },
! 252: { .key = KEYC_KP_TWO,
! 253: .data = "2"
! 254: },
! 255: { .key = KEYC_KP_THREE,
! 256: .data = "3"
! 257: },
! 258: { .key = KEYC_KP_ENTER,
! 259: .data = "\n"
! 260: },
! 261: { .key = KEYC_KP_ZERO,
! 262: .data = "0"
! 263: },
! 264: { .key = KEYC_KP_PERIOD,
! 265: .data = "."
! 266: },
! 267:
! 268: /* Keys with an embedded modifier. */
! 269: { .key = KEYC_F1|KEYC_XTERM,
! 270: .data = "\033[1;_P"
! 271: },
! 272: { .key = KEYC_F2|KEYC_XTERM,
! 273: .data = "\033[1;_Q"
! 274: },
! 275: { .key = KEYC_F3|KEYC_XTERM,
! 276: .data = "\033[1;_R"
! 277: },
! 278: { .key = KEYC_F4|KEYC_XTERM,
! 279: .data = "\033[1;_S"
! 280: },
! 281: { .key = KEYC_F5|KEYC_XTERM,
! 282: .data = "\033[15;_~"
! 283: },
! 284: { .key = KEYC_F6|KEYC_XTERM,
! 285: .data = "\033[17;_~"
! 286: },
! 287: { .key = KEYC_F7|KEYC_XTERM,
! 288: .data = "\033[18;_~"
! 289: },
! 290: { .key = KEYC_F8|KEYC_XTERM,
! 291: .data = "\033[19;_~"
! 292: },
! 293: { .key = KEYC_F9|KEYC_XTERM,
! 294: .data = "\033[20;_~"
! 295: },
! 296: { .key = KEYC_F10|KEYC_XTERM,
! 297: .data = "\033[21;_~"
! 298: },
! 299: { .key = KEYC_F11|KEYC_XTERM,
! 300: .data = "\033[23;_~"
! 301: },
! 302: { .key = KEYC_F12|KEYC_XTERM,
! 303: .data = "\033[24;_~"
! 304: },
! 305: { .key = KEYC_UP|KEYC_XTERM,
! 306: .data = "\033[1;_A"
! 307: },
! 308: { .key = KEYC_DOWN|KEYC_XTERM,
! 309: .data = "\033[1;_B"
! 310: },
! 311: { .key = KEYC_RIGHT|KEYC_XTERM,
! 312: .data = "\033[1;_C"
! 313: },
! 314: { .key = KEYC_LEFT|KEYC_XTERM,
! 315: .data = "\033[1;_D"
! 316: },
! 317: { .key = KEYC_HOME|KEYC_XTERM,
! 318: .data = "\033[1;_H"
! 319: },
! 320: { .key = KEYC_END|KEYC_XTERM,
! 321: .data = "\033[1;_F"
! 322: },
! 323: { .key = KEYC_PPAGE|KEYC_XTERM,
! 324: .data = "\033[5;_~"
! 325: },
! 326: { .key = KEYC_NPAGE|KEYC_XTERM,
! 327: .data = "\033[6;_~"
! 328: },
! 329: { .key = KEYC_IC|KEYC_XTERM,
! 330: .data = "\033[2;_~"
! 331: },
! 332: { .key = KEYC_DC|KEYC_XTERM,
! 333: .data = "\033[3;_~" }
! 334: };
! 335: static const key_code input_key_modifiers[] = {
! 336: 0,
! 337: 0,
! 338: KEYC_SHIFT|KEYC_XTERM,
! 339: KEYC_ESCAPE|KEYC_XTERM,
! 340: KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM,
! 341: KEYC_CTRL|KEYC_XTERM,
! 342: KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM,
! 343: KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM,
! 344: KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM
! 345: };
1.7 nicm 346:
1.72 ! nicm 347: /* Input key comparison function. */
! 348: static int
! 349: input_key_cmp(struct input_key_entry *ike1, struct input_key_entry *ike2)
! 350: {
! 351: if (ike1->key < ike2->key)
! 352: return (-1);
! 353: if (ike1->key > ike2->key)
! 354: return (1);
! 355: return (0);
! 356: }
1.1 nicm 357:
1.54 nicm 358: /* Split a character into two UTF-8 bytes. */
359: static size_t
1.72 ! nicm 360: input_key_split2(u_int c, u_char *dst)
1.54 nicm 361: {
362: if (c > 0x7f) {
363: dst[0] = (c >> 6) | 0xc0;
364: dst[1] = (c & 0x3f) | 0x80;
365: return (2);
366: }
367: dst[0] = c;
368: return (1);
369: }
370:
1.72 ! nicm 371: /* Build input key tree. */
! 372: void
! 373: input_key_build(void)
! 374: {
! 375: struct input_key_entry *ike, *new;
! 376: u_int i, j;
! 377: char *data;
! 378:
! 379: for (i = 0; i < nitems(input_key_defaults); i++) {
! 380: ike = &input_key_defaults[i];
! 381: if (~ike->key & KEYC_XTERM) {
! 382: RB_INSERT(input_key_tree, &input_key_tree, ike);
! 383: continue;
! 384: }
! 385:
! 386: for (j = 2; j < nitems(input_key_modifiers); j++) {
! 387: data = xstrdup(ike->data);
! 388: data[strcspn(data, "_")] = '0' + j;
! 389:
! 390: new = xcalloc(1, sizeof *new);
! 391: new->key = ike->key|input_key_modifiers[j];
! 392: new->data = data;
! 393: RB_INSERT(input_key_tree, &input_key_tree, new);
! 394: }
! 395: }
! 396:
! 397: RB_FOREACH(ike, input_key_tree, &input_key_tree) {
! 398: log_debug("%s: 0x%llx (%s) is %s", __func__, ike->key,
! 399: key_string_lookup_key(ike->key), ike->data);
! 400: }
! 401: }
! 402:
1.69 nicm 403: /* Translate a key code into an output key sequence for a pane. */
404: int
405: input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m)
406: {
1.72 ! nicm 407: if (log_get_level() != 0) {
! 408: log_debug("writing key 0x%llx (%s) to %%%u", key,
! 409: key_string_lookup_key(key), wp->id);
! 410: }
1.69 nicm 411:
412: if (KEYC_IS_MOUSE(key)) {
413: if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id)
414: input_key_mouse(wp, m);
415: return (0);
416: }
1.72 ! nicm 417: return (input_key(wp->screen, wp->event, key));
1.69 nicm 418: }
419:
1.8 nicm 420: /* Translate a key code into an output key sequence. */
1.67 nicm 421: int
1.72 ! nicm 422: input_key(struct screen *s, struct bufferevent *bev, key_code key)
1.1 nicm 423: {
1.72 ! nicm 424: struct input_key_entry *ike, entry;
! 425: size_t datalen;
! 426: key_code justkey, newkey;
! 427: struct utf8_data ud;
1.1 nicm 428:
1.69 nicm 429: /* Mouse keys need a pane. */
430: if (KEYC_IS_MOUSE(key))
1.67 nicm 431: return (0);
1.64 nicm 432:
433: /* Literal keys go as themselves (can't be more than eight bits). */
434: if (key & KEYC_LITERAL) {
435: ud.data[0] = (u_char)key;
1.69 nicm 436: bufferevent_write(bev, &ud.data[0], 1);
1.67 nicm 437: return (0);
1.65 nicm 438: }
439:
440: /* Is this backspace? */
441: if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
1.66 nicm 442: newkey = options_get_number(global_options, "backspace");
443: if (newkey >= 0x7f)
444: newkey = '\177';
445: key = newkey|(key & KEYC_MASK_MOD);
1.42 nicm 446: }
1.1 nicm 447:
1.8 nicm 448: /*
449: * If this is a normal 7-bit key, just send it, with a leading escape
1.46 nicm 450: * if necessary. If it is a UTF-8 key, split it and send it.
1.8 nicm 451: */
1.62 nicm 452: justkey = (key & ~(KEYC_XTERM|KEYC_ESCAPE));
1.52 nicm 453: if (justkey <= 0x7f) {
1.2 nicm 454: if (key & KEYC_ESCAPE)
1.69 nicm 455: bufferevent_write(bev, "\033", 1);
1.47 nicm 456: ud.data[0] = justkey;
1.69 nicm 457: bufferevent_write(bev, &ud.data[0], 1);
1.67 nicm 458: return (0);
1.46 nicm 459: }
1.52 nicm 460: if (justkey > 0x7f && justkey < KEYC_BASE) {
1.48 nicm 461: if (utf8_split(justkey, &ud) != UTF8_DONE)
1.67 nicm 462: return (-1);
1.46 nicm 463: if (key & KEYC_ESCAPE)
1.69 nicm 464: bufferevent_write(bev, "\033", 1);
465: bufferevent_write(bev, ud.data, ud.size);
1.67 nicm 466: return (0);
1.12 nicm 467: }
468:
1.18 nicm 469: /*
1.72 ! nicm 470: * Look up in the tree. If not in application keypad or cursor mode,
! 471: * remove the flags from the key.
1.12 nicm 472: */
1.72 ! nicm 473: if (~s->mode & MODE_KKEYPAD)
! 474: key &= ~KEYC_KEYPAD;
! 475: if (~s->mode & MODE_KCURSOR)
! 476: key &= ~KEYC_CURSOR;
! 477: entry.key = key;
! 478: if ((ike = RB_FIND(input_key_tree, &input_key_tree, &entry)) == NULL) {
1.46 nicm 479: log_debug("key 0x%llx missing", key);
1.67 nicm 480: return (-1);
1.1 nicm 481: }
1.72 ! nicm 482: datalen = strlen(ike->data);
1.46 nicm 483: log_debug("found key 0x%llx: \"%s\"", key, ike->data);
1.1 nicm 484:
1.9 nicm 485: /* Prefix a \033 for escape. */
1.2 nicm 486: if (key & KEYC_ESCAPE)
1.69 nicm 487: bufferevent_write(bev, "\033", 1);
1.72 ! nicm 488: bufferevent_write(bev, ike->data, datalen);
1.67 nicm 489: return (0);
1.1 nicm 490: }
491:
1.70 nicm 492: /* Get mouse event string. */
493: int
494: input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y,
495: const char **rbuf, size_t *rlen)
1.1 nicm 496: {
1.70 nicm 497: static char buf[40];
1.59 nicm 498: size_t len;
1.42 nicm 499:
1.70 nicm 500: *rbuf = NULL;
501: *rlen = 0;
1.24 nicm 502:
1.59 nicm 503: /* If this pane is not in button or all mode, discard motion events. */
1.68 nicm 504: if (MOUSE_DRAG(m->b) && (s->mode & MOTION_MOUSE_MODES) == 0)
1.71 nicm 505: return (0);
506: if ((s->mode & ALL_MOUSE_MODES) == 0)
1.70 nicm 507: return (0);
1.59 nicm 508:
509: /*
510: * If this event is a release event and not in all mode, discard it.
511: * In SGR mode we can tell absolutely because a release is normally
512: * shown by the last character. Without SGR, we check if the last
513: * buttons was also a release.
514: */
515: if (m->sgr_type != ' ') {
516: if (MOUSE_DRAG(m->sgr_b) &&
517: MOUSE_BUTTONS(m->sgr_b) == 3 &&
1.68 nicm 518: (~s->mode & MODE_MOUSE_ALL))
1.70 nicm 519: return (0);
1.59 nicm 520: } else {
521: if (MOUSE_DRAG(m->b) &&
522: MOUSE_BUTTONS(m->b) == 3 &&
523: MOUSE_BUTTONS(m->lb) == 3 &&
1.68 nicm 524: (~s->mode & MODE_MOUSE_ALL))
1.70 nicm 525: return (0);
1.59 nicm 526: }
1.42 nicm 527:
528: /*
529: * Use the SGR (1006) extension only if the application requested it
530: * and the underlying terminal also sent the event in this format (this
531: * is because an old style mouse release event cannot be converted into
532: * the new SGR format, since the released button is unknown). Otherwise
533: * pretend that tmux doesn't speak this extension, and fall back to the
1.51 nicm 534: * UTF-8 (1005) extension if the application requested, or to the
1.42 nicm 535: * legacy format.
536: */
1.59 nicm 537: if (m->sgr_type != ' ' && (s->mode & MODE_MOUSE_SGR)) {
1.42 nicm 538: len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c",
539: m->sgr_b, x + 1, y + 1, m->sgr_type);
1.59 nicm 540: } else if (s->mode & MODE_MOUSE_UTF8) {
1.55 nicm 541: if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33)
1.70 nicm 542: return (0);
1.51 nicm 543: len = xsnprintf(buf, sizeof buf, "\033[M");
1.72 ! nicm 544: len += input_key_split2(m->b + 32, &buf[len]);
! 545: len += input_key_split2(x + 33, &buf[len]);
! 546: len += input_key_split2(y + 33, &buf[len]);
1.42 nicm 547: } else {
548: if (m->b > 223)
1.70 nicm 549: return (0);
1.42 nicm 550: len = xsnprintf(buf, sizeof buf, "\033[M");
551: buf[len++] = m->b + 32;
552: buf[len++] = x + 33;
553: buf[len++] = y + 33;
1.1 nicm 554: }
1.70 nicm 555:
556: *rbuf = buf;
557: *rlen = len;
558: return (1);
559: }
560:
561: /* Translate mouse and output. */
562: static void
563: input_key_mouse(struct window_pane *wp, struct mouse_event *m)
564: {
565: struct screen *s = wp->screen;
566: u_int x, y;
567: const char *buf;
568: size_t len;
569:
570: /* Ignore events if no mouse mode or the pane is not visible. */
571: if (m->ignore || (s->mode & ALL_MOUSE_MODES) == 0)
572: return;
573: if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
574: return;
575: if (!window_pane_visible(wp))
576: return;
577: if (!input_key_get_mouse(s, m, x, y, &buf, &len))
578: return;
1.44 nicm 579: log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id);
1.42 nicm 580: bufferevent_write(wp->event, buf, len);
1.1 nicm 581: }