1 /* $OpenBSD: input-keys.c,v 1.73 2020/05/16 16:33:16 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 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 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 34 static void input_key_mouse(struct window_pane *, struct mouse_event *); 35 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; 42 }; 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); 50 51 /* List of default keys, the tree is built from this. */ 52 static struct input_key_entry input_key_defaults[] = { 53 /* Paste keys. */ 54 { .key = KEYC_PASTE_START, 55 .data = "\033[200~" 56 }, 57 { .key = KEYC_PASTE_END, 58 .data = "\033[201~" 59 }, 60 61 /* Function keys. */ 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_META|KEYC_XTERM, 340 KEYC_SHIFT|KEYC_META|KEYC_XTERM, 341 KEYC_CTRL|KEYC_XTERM, 342 KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM, 343 KEYC_META|KEYC_CTRL|KEYC_XTERM, 344 KEYC_SHIFT|KEYC_META|KEYC_CTRL|KEYC_XTERM 345 }; 346 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 } 357 358 /* Split a character into two UTF-8 bytes. */ 359 static size_t 360 input_key_split2(u_int c, u_char *dst) 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 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 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 { 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 } 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 } 417 return (input_key(wp->screen, wp->event, key)); 418 } 419 420 /* Translate a key code into an output key sequence. */ 421 int 422 input_key(struct screen *s, struct bufferevent *bev, key_code key) 423 { 424 struct input_key_entry *ike, entry; 425 size_t datalen; 426 key_code justkey, newkey; 427 struct utf8_data ud; 428 429 /* Mouse keys need a pane. */ 430 if (KEYC_IS_MOUSE(key)) 431 return (0); 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; 436 bufferevent_write(bev, &ud.data[0], 1); 437 return (0); 438 } 439 440 /* Is this backspace? */ 441 if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) { 442 newkey = options_get_number(global_options, "backspace"); 443 if (newkey >= 0x7f) 444 newkey = '\177'; 445 key = newkey|(key & KEYC_MASK_MOD); 446 } 447 448 /* 449 * If this is a normal 7-bit key, just send it, with a leading escape 450 * if necessary. If it is a UTF-8 key, split it and send it. 451 */ 452 justkey = (key & ~(KEYC_XTERM|KEYC_META)); 453 if (justkey <= 0x7f) { 454 if (key & KEYC_META) 455 bufferevent_write(bev, "\033", 1); 456 ud.data[0] = justkey; 457 bufferevent_write(bev, &ud.data[0], 1); 458 return (0); 459 } 460 if (justkey > 0x7f && justkey < KEYC_BASE) { 461 if (utf8_split(justkey, &ud) != UTF8_DONE) 462 return (-1); 463 if (key & KEYC_META) 464 bufferevent_write(bev, "\033", 1); 465 bufferevent_write(bev, ud.data, ud.size); 466 return (0); 467 } 468 469 /* 470 * Look up in the tree. If not in application keypad or cursor mode, 471 * remove the flags from the key. 472 */ 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) { 479 log_debug("key 0x%llx missing", key); 480 return (-1); 481 } 482 datalen = strlen(ike->data); 483 log_debug("found key 0x%llx: \"%s\"", key, ike->data); 484 485 /* Prefix a \033 for escape. */ 486 if (key & KEYC_META) 487 bufferevent_write(bev, "\033", 1); 488 bufferevent_write(bev, ike->data, datalen); 489 return (0); 490 } 491 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) 496 { 497 static char buf[40]; 498 size_t len; 499 500 *rbuf = NULL; 501 *rlen = 0; 502 503 /* If this pane is not in button or all mode, discard motion events. */ 504 if (MOUSE_DRAG(m->b) && (s->mode & MOTION_MOUSE_MODES) == 0) 505 return (0); 506 if ((s->mode & ALL_MOUSE_MODES) == 0) 507 return (0); 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 && 518 (~s->mode & MODE_MOUSE_ALL)) 519 return (0); 520 } else { 521 if (MOUSE_DRAG(m->b) && 522 MOUSE_BUTTONS(m->b) == 3 && 523 MOUSE_BUTTONS(m->lb) == 3 && 524 (~s->mode & MODE_MOUSE_ALL)) 525 return (0); 526 } 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 534 * UTF-8 (1005) extension if the application requested, or to the 535 * legacy format. 536 */ 537 if (m->sgr_type != ' ' && (s->mode & MODE_MOUSE_SGR)) { 538 len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", 539 m->sgr_b, x + 1, y + 1, m->sgr_type); 540 } else if (s->mode & MODE_MOUSE_UTF8) { 541 if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) 542 return (0); 543 len = xsnprintf(buf, sizeof buf, "\033[M"); 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]); 547 } else { 548 if (m->b > 223) 549 return (0); 550 len = xsnprintf(buf, sizeof buf, "\033[M"); 551 buf[len++] = m->b + 32; 552 buf[len++] = x + 33; 553 buf[len++] = y + 33; 554 } 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; 579 log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id); 580 bufferevent_write(wp->event, buf, len); 581 } 582