xref: /openbsd-src/usr.bin/tmux/input-keys.c (revision 2f106d387997d65d1eab0db72078a13e5d51f6cf)
1*2f106d38Snicm /* $OpenBSD: input-keys.c,v 1.107 2025/01/02 10:34:45 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm 
21311827fbSnicm #include <stdint.h>
22311827fbSnicm #include <stdlib.h>
23311827fbSnicm #include <string.h>
24311827fbSnicm 
25311827fbSnicm #include "tmux.h"
26311827fbSnicm 
279f8989e0Snicm /*
289f8989e0Snicm  * This file is rather misleadingly named, it contains the code which takes a
299f8989e0Snicm  * key code and translates it into something suitable to be sent to the
309f8989e0Snicm  * application running in a pane (similar to input.c does in the other
319f8989e0Snicm  * direction with output).
329f8989e0Snicm  */
339f8989e0Snicm 
34413e5e52Snicm static void	 input_key_mouse(struct window_pane *, struct mouse_event *);
35e048bb79Snicm 
369265d1acSnicm /* Entry in the key tree. */
379265d1acSnicm struct input_key_entry {
38885a4698Snicm 	key_code			 key;
39311827fbSnicm 	const char			*data;
40311827fbSnicm 
419265d1acSnicm 	RB_ENTRY(input_key_entry)	 entry;
42311827fbSnicm };
439265d1acSnicm RB_HEAD(input_key_tree, input_key_entry);
44311827fbSnicm 
459265d1acSnicm /* Tree of input keys. */
469265d1acSnicm static int	input_key_cmp(struct input_key_entry *,
479265d1acSnicm 		    struct input_key_entry *);
489265d1acSnicm RB_GENERATE_STATIC(input_key_tree, input_key_entry, entry, input_key_cmp);
499265d1acSnicm struct input_key_tree input_key_tree = RB_INITIALIZER(&input_key_tree);
509265d1acSnicm 
519265d1acSnicm /* List of default keys, the tree is built from this. */
529265d1acSnicm static struct input_key_entry input_key_defaults[] = {
53b99601f2Snicm 	/* Paste keys. */
549265d1acSnicm 	{ .key = KEYC_PASTE_START,
559265d1acSnicm 	  .data = "\033[200~"
569265d1acSnicm 	},
579265d1acSnicm 	{ .key = KEYC_PASTE_END,
589265d1acSnicm 	  .data = "\033[201~"
599265d1acSnicm 	},
60b99601f2Snicm 
61311827fbSnicm 	/* Function keys. */
629265d1acSnicm 	{ .key = KEYC_F1,
639265d1acSnicm 	  .data = "\033OP"
649265d1acSnicm 	},
659265d1acSnicm 	{ .key = KEYC_F2,
669265d1acSnicm 	  .data = "\033OQ"
679265d1acSnicm 	},
689265d1acSnicm 	{ .key = KEYC_F3,
699265d1acSnicm 	  .data = "\033OR"
709265d1acSnicm 	},
719265d1acSnicm 	{ .key = KEYC_F4,
729265d1acSnicm 	  .data = "\033OS"
739265d1acSnicm 	},
749265d1acSnicm 	{ .key = KEYC_F5,
759265d1acSnicm 	  .data = "\033[15~"
769265d1acSnicm 	},
779265d1acSnicm 	{ .key = KEYC_F6,
789265d1acSnicm 	  .data = "\033[17~"
799265d1acSnicm 	},
809265d1acSnicm 	{ .key = KEYC_F7,
819265d1acSnicm 	  .data = "\033[18~"
829265d1acSnicm 	},
839265d1acSnicm 	{ .key = KEYC_F8,
849265d1acSnicm 	  .data = "\033[19~"
859265d1acSnicm 	},
869265d1acSnicm 	{ .key = KEYC_F9,
879265d1acSnicm 	  .data = "\033[20~"
889265d1acSnicm 	},
899265d1acSnicm 	{ .key = KEYC_F10,
909265d1acSnicm 	  .data = "\033[21~"
919265d1acSnicm 	},
929265d1acSnicm 	{ .key = KEYC_F11,
939265d1acSnicm 	  .data = "\033[23~"
949265d1acSnicm 	},
959265d1acSnicm 	{ .key = KEYC_F12,
969265d1acSnicm 	  .data = "\033[24~"
979265d1acSnicm 	},
989265d1acSnicm 	{ .key = KEYC_IC,
999265d1acSnicm 	  .data = "\033[2~"
1009265d1acSnicm 	},
1019265d1acSnicm 	{ .key = KEYC_DC,
1029265d1acSnicm 	  .data = "\033[3~"
1039265d1acSnicm 	},
1049265d1acSnicm 	{ .key = KEYC_HOME,
1059265d1acSnicm 	  .data = "\033[1~"
1069265d1acSnicm 	},
1079265d1acSnicm 	{ .key = KEYC_END,
1089265d1acSnicm 	  .data = "\033[4~"
1099265d1acSnicm 	},
1109265d1acSnicm 	{ .key = KEYC_NPAGE,
1119265d1acSnicm 	  .data = "\033[6~"
1129265d1acSnicm 	},
1139265d1acSnicm 	{ .key = KEYC_PPAGE,
1149265d1acSnicm 	  .data = "\033[5~"
1159265d1acSnicm 	},
1169265d1acSnicm 	{ .key = KEYC_BTAB,
1179265d1acSnicm 	  .data = "\033[Z"
1189265d1acSnicm 	},
119311827fbSnicm 
1209265d1acSnicm 	/* Arrow keys. */
1219265d1acSnicm 	{ .key = KEYC_UP|KEYC_CURSOR,
1229265d1acSnicm 	  .data = "\033OA"
1239265d1acSnicm 	},
1249265d1acSnicm 	{ .key = KEYC_DOWN|KEYC_CURSOR,
1259265d1acSnicm 	  .data = "\033OB"
1269265d1acSnicm 	},
1279265d1acSnicm 	{ .key = KEYC_RIGHT|KEYC_CURSOR,
1289265d1acSnicm 	  .data = "\033OC"
1299265d1acSnicm 	},
1309265d1acSnicm 	{ .key = KEYC_LEFT|KEYC_CURSOR,
1319265d1acSnicm 	  .data = "\033OD"
1329265d1acSnicm 	},
1339265d1acSnicm 	{ .key = KEYC_UP,
1349265d1acSnicm 	  .data = "\033[A"
1359265d1acSnicm 	},
1369265d1acSnicm 	{ .key = KEYC_DOWN,
1379265d1acSnicm 	  .data = "\033[B"
1389265d1acSnicm 	},
1399265d1acSnicm 	{ .key = KEYC_RIGHT,
1409265d1acSnicm 	  .data = "\033[C"
1419265d1acSnicm 	},
1429265d1acSnicm 	{ .key = KEYC_LEFT,
1439265d1acSnicm 	  .data = "\033[D"
1449265d1acSnicm 	},
145ceb8a03dSnicm 
1469265d1acSnicm 	/* Keypad keys. */
1479265d1acSnicm 	{ .key = KEYC_KP_SLASH|KEYC_KEYPAD,
1489265d1acSnicm 	  .data = "\033Oo"
1499265d1acSnicm 	},
1509265d1acSnicm 	{ .key = KEYC_KP_STAR|KEYC_KEYPAD,
1519265d1acSnicm 	  .data = "\033Oj"
1529265d1acSnicm 	},
1539265d1acSnicm 	{ .key = KEYC_KP_MINUS|KEYC_KEYPAD,
1549265d1acSnicm 	  .data = "\033Om"
1559265d1acSnicm 	},
1569265d1acSnicm 	{ .key = KEYC_KP_SEVEN|KEYC_KEYPAD,
1579265d1acSnicm 	  .data = "\033Ow"
1589265d1acSnicm 	},
1599265d1acSnicm 	{ .key = KEYC_KP_EIGHT|KEYC_KEYPAD,
1609265d1acSnicm 	  .data = "\033Ox"
1619265d1acSnicm 	},
1629265d1acSnicm 	{ .key = KEYC_KP_NINE|KEYC_KEYPAD,
1639265d1acSnicm 	  .data = "\033Oy"
1649265d1acSnicm 	},
1659265d1acSnicm 	{ .key = KEYC_KP_PLUS|KEYC_KEYPAD,
1669265d1acSnicm 	  .data = "\033Ok"
1679265d1acSnicm 	},
1689265d1acSnicm 	{ .key = KEYC_KP_FOUR|KEYC_KEYPAD,
1699265d1acSnicm 	  .data = "\033Ot"
1709265d1acSnicm 	},
1719265d1acSnicm 	{ .key = KEYC_KP_FIVE|KEYC_KEYPAD,
1729265d1acSnicm 	  .data = "\033Ou"
1739265d1acSnicm 	},
1749265d1acSnicm 	{ .key = KEYC_KP_SIX|KEYC_KEYPAD,
1759265d1acSnicm 	  .data = "\033Ov"
1769265d1acSnicm 	},
1779265d1acSnicm 	{ .key = KEYC_KP_ONE|KEYC_KEYPAD,
1789265d1acSnicm 	  .data = "\033Oq"
1799265d1acSnicm 	},
1809265d1acSnicm 	{ .key = KEYC_KP_TWO|KEYC_KEYPAD,
1819265d1acSnicm 	  .data = "\033Or"
1829265d1acSnicm 	},
1839265d1acSnicm 	{ .key = KEYC_KP_THREE|KEYC_KEYPAD,
1849265d1acSnicm 	  .data = "\033Os"
1859265d1acSnicm 	},
1869265d1acSnicm 	{ .key = KEYC_KP_ENTER|KEYC_KEYPAD,
1879265d1acSnicm 	  .data = "\033OM"
1889265d1acSnicm 	},
1899265d1acSnicm 	{ .key = KEYC_KP_ZERO|KEYC_KEYPAD,
1909265d1acSnicm 	  .data = "\033Op"
1919265d1acSnicm 	},
1929265d1acSnicm 	{ .key = KEYC_KP_PERIOD|KEYC_KEYPAD,
1939265d1acSnicm 	  .data = "\033On"
1949265d1acSnicm 	},
1959265d1acSnicm 	{ .key = KEYC_KP_SLASH,
1969265d1acSnicm 	  .data = "/"
1979265d1acSnicm 	},
1989265d1acSnicm 	{ .key = KEYC_KP_STAR,
1999265d1acSnicm 	  .data = "*"
2009265d1acSnicm 	},
2019265d1acSnicm 	{ .key = KEYC_KP_MINUS,
2029265d1acSnicm 	  .data = "-"
2039265d1acSnicm 	},
2049265d1acSnicm 	{ .key = KEYC_KP_SEVEN,
2059265d1acSnicm 	  .data = "7"
2069265d1acSnicm 	},
2079265d1acSnicm 	{ .key = KEYC_KP_EIGHT,
2089265d1acSnicm 	  .data = "8"
2099265d1acSnicm 	},
2109265d1acSnicm 	{ .key = KEYC_KP_NINE,
2119265d1acSnicm 	  .data = "9"
2129265d1acSnicm 	},
2139265d1acSnicm 	{ .key = KEYC_KP_PLUS,
2149265d1acSnicm 	  .data = "+"
2159265d1acSnicm 	},
2169265d1acSnicm 	{ .key = KEYC_KP_FOUR,
2179265d1acSnicm 	  .data = "4"
2189265d1acSnicm 	},
2199265d1acSnicm 	{ .key = KEYC_KP_FIVE,
2209265d1acSnicm 	  .data = "5"
2219265d1acSnicm 	},
2229265d1acSnicm 	{ .key = KEYC_KP_SIX,
2239265d1acSnicm 	  .data = "6"
2249265d1acSnicm 	},
2259265d1acSnicm 	{ .key = KEYC_KP_ONE,
2269265d1acSnicm 	  .data = "1"
2279265d1acSnicm 	},
2289265d1acSnicm 	{ .key = KEYC_KP_TWO,
2299265d1acSnicm 	  .data = "2"
2309265d1acSnicm 	},
2319265d1acSnicm 	{ .key = KEYC_KP_THREE,
2329265d1acSnicm 	  .data = "3"
2339265d1acSnicm 	},
2349265d1acSnicm 	{ .key = KEYC_KP_ENTER,
2359265d1acSnicm 	  .data = "\n"
2369265d1acSnicm 	},
2379265d1acSnicm 	{ .key = KEYC_KP_ZERO,
2389265d1acSnicm 	  .data = "0"
2399265d1acSnicm 	},
2409265d1acSnicm 	{ .key = KEYC_KP_PERIOD,
2419265d1acSnicm 	  .data = "."
2429265d1acSnicm 	},
243311827fbSnicm 
2449265d1acSnicm 	/* Keys with an embedded modifier. */
2455416581eSnicm 	{ .key = KEYC_F1|KEYC_BUILD_MODIFIERS,
2469265d1acSnicm 	  .data = "\033[1;_P"
2479265d1acSnicm 	},
2485416581eSnicm 	{ .key = KEYC_F2|KEYC_BUILD_MODIFIERS,
2499265d1acSnicm 	  .data = "\033[1;_Q"
2509265d1acSnicm 	},
2515416581eSnicm 	{ .key = KEYC_F3|KEYC_BUILD_MODIFIERS,
2529265d1acSnicm 	  .data = "\033[1;_R"
2539265d1acSnicm 	},
2545416581eSnicm 	{ .key = KEYC_F4|KEYC_BUILD_MODIFIERS,
2559265d1acSnicm 	  .data = "\033[1;_S"
2569265d1acSnicm 	},
2575416581eSnicm 	{ .key = KEYC_F5|KEYC_BUILD_MODIFIERS,
2589265d1acSnicm 	  .data = "\033[15;_~"
2599265d1acSnicm 	},
2605416581eSnicm 	{ .key = KEYC_F6|KEYC_BUILD_MODIFIERS,
2619265d1acSnicm 	  .data = "\033[17;_~"
2629265d1acSnicm 	},
2635416581eSnicm 	{ .key = KEYC_F7|KEYC_BUILD_MODIFIERS,
2649265d1acSnicm 	  .data = "\033[18;_~"
2659265d1acSnicm 	},
2665416581eSnicm 	{ .key = KEYC_F8|KEYC_BUILD_MODIFIERS,
2679265d1acSnicm 	  .data = "\033[19;_~"
2689265d1acSnicm 	},
2695416581eSnicm 	{ .key = KEYC_F9|KEYC_BUILD_MODIFIERS,
2709265d1acSnicm 	  .data = "\033[20;_~"
2719265d1acSnicm 	},
2725416581eSnicm 	{ .key = KEYC_F10|KEYC_BUILD_MODIFIERS,
2739265d1acSnicm 	  .data = "\033[21;_~"
2749265d1acSnicm 	},
2755416581eSnicm 	{ .key = KEYC_F11|KEYC_BUILD_MODIFIERS,
2769265d1acSnicm 	  .data = "\033[23;_~"
2779265d1acSnicm 	},
2785416581eSnicm 	{ .key = KEYC_F12|KEYC_BUILD_MODIFIERS,
2799265d1acSnicm 	  .data = "\033[24;_~"
2809265d1acSnicm 	},
2815416581eSnicm 	{ .key = KEYC_UP|KEYC_BUILD_MODIFIERS,
2829265d1acSnicm 	  .data = "\033[1;_A"
2839265d1acSnicm 	},
2845416581eSnicm 	{ .key = KEYC_DOWN|KEYC_BUILD_MODIFIERS,
2859265d1acSnicm 	  .data = "\033[1;_B"
2869265d1acSnicm 	},
2875416581eSnicm 	{ .key = KEYC_RIGHT|KEYC_BUILD_MODIFIERS,
2889265d1acSnicm 	  .data = "\033[1;_C"
2899265d1acSnicm 	},
2905416581eSnicm 	{ .key = KEYC_LEFT|KEYC_BUILD_MODIFIERS,
2919265d1acSnicm 	  .data = "\033[1;_D"
2929265d1acSnicm 	},
2935416581eSnicm 	{ .key = KEYC_HOME|KEYC_BUILD_MODIFIERS,
2949265d1acSnicm 	  .data = "\033[1;_H"
2959265d1acSnicm 	},
2965416581eSnicm 	{ .key = KEYC_END|KEYC_BUILD_MODIFIERS,
2979265d1acSnicm 	  .data = "\033[1;_F"
2989265d1acSnicm 	},
2995416581eSnicm 	{ .key = KEYC_PPAGE|KEYC_BUILD_MODIFIERS,
3009265d1acSnicm 	  .data = "\033[5;_~"
3019265d1acSnicm 	},
3025416581eSnicm 	{ .key = KEYC_NPAGE|KEYC_BUILD_MODIFIERS,
3039265d1acSnicm 	  .data = "\033[6;_~"
3049265d1acSnicm 	},
3055416581eSnicm 	{ .key = KEYC_IC|KEYC_BUILD_MODIFIERS,
3069265d1acSnicm 	  .data = "\033[2;_~"
3079265d1acSnicm 	},
3085416581eSnicm 	{ .key = KEYC_DC|KEYC_BUILD_MODIFIERS,
309dce21a56Snicm 	  .data = "\033[3;_~"
3103c4ca0e6Snicm 	},
311311827fbSnicm };
3129265d1acSnicm static const key_code input_key_modifiers[] = {
3139265d1acSnicm 	0,
3149265d1acSnicm 	0,
3155416581eSnicm 	KEYC_SHIFT,
3165416581eSnicm 	KEYC_META|KEYC_IMPLIED_META,
3175416581eSnicm 	KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META,
3185416581eSnicm 	KEYC_CTRL,
3195416581eSnicm 	KEYC_SHIFT|KEYC_CTRL,
3205416581eSnicm 	KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL,
3215416581eSnicm 	KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL
3229265d1acSnicm };
3239265d1acSnicm 
3249265d1acSnicm /* Input key comparison function. */
3259265d1acSnicm static int
3269265d1acSnicm input_key_cmp(struct input_key_entry *ike1, struct input_key_entry *ike2)
3279265d1acSnicm {
3289265d1acSnicm 	if (ike1->key < ike2->key)
3299265d1acSnicm 		return (-1);
3309265d1acSnicm 	if (ike1->key > ike2->key)
3319265d1acSnicm 		return (1);
3329265d1acSnicm 	return (0);
3339265d1acSnicm }
334311827fbSnicm 
3355416581eSnicm /* Look for key in tree. */
3365416581eSnicm static struct input_key_entry *
3375416581eSnicm input_key_get(key_code key)
3385416581eSnicm {
3395416581eSnicm 	struct input_key_entry	entry = { .key = key };
3405416581eSnicm 
3415416581eSnicm 	return (RB_FIND(input_key_tree, &input_key_tree, &entry));
3425416581eSnicm }
3435416581eSnicm 
344f5015ed6Snicm /* Split a character into two UTF-8 bytes. */
345f5015ed6Snicm static size_t
3469265d1acSnicm input_key_split2(u_int c, u_char *dst)
347f5015ed6Snicm {
348f5015ed6Snicm 	if (c > 0x7f) {
349f5015ed6Snicm 		dst[0] = (c >> 6) | 0xc0;
350f5015ed6Snicm 		dst[1] = (c & 0x3f) | 0x80;
351f5015ed6Snicm 		return (2);
352f5015ed6Snicm 	}
353f5015ed6Snicm 	dst[0] = c;
354f5015ed6Snicm 	return (1);
355f5015ed6Snicm }
356f5015ed6Snicm 
3579265d1acSnicm /* Build input key tree. */
3589265d1acSnicm void
3599265d1acSnicm input_key_build(void)
3609265d1acSnicm {
3619265d1acSnicm 	struct input_key_entry	*ike, *new;
3629265d1acSnicm 	u_int			 i, j;
3639265d1acSnicm 	char			*data;
3645416581eSnicm 	key_code		 key;
3659265d1acSnicm 
3669265d1acSnicm 	for (i = 0; i < nitems(input_key_defaults); i++) {
3679265d1acSnicm 		ike = &input_key_defaults[i];
3685416581eSnicm 		if (~ike->key & KEYC_BUILD_MODIFIERS) {
3699265d1acSnicm 			RB_INSERT(input_key_tree, &input_key_tree, ike);
3709265d1acSnicm 			continue;
3719265d1acSnicm 		}
3729265d1acSnicm 
3739265d1acSnicm 		for (j = 2; j < nitems(input_key_modifiers); j++) {
3745416581eSnicm 			key = (ike->key & ~KEYC_BUILD_MODIFIERS);
3759265d1acSnicm 			data = xstrdup(ike->data);
3769265d1acSnicm 			data[strcspn(data, "_")] = '0' + j;
3779265d1acSnicm 
3789265d1acSnicm 			new = xcalloc(1, sizeof *new);
3795416581eSnicm 			new->key = key|input_key_modifiers[j];
3809265d1acSnicm 			new->data = data;
3819265d1acSnicm 			RB_INSERT(input_key_tree, &input_key_tree, new);
3829265d1acSnicm 		}
3839265d1acSnicm 	}
3849265d1acSnicm 
3859265d1acSnicm 	RB_FOREACH(ike, input_key_tree, &input_key_tree) {
3869265d1acSnicm 		log_debug("%s: 0x%llx (%s) is %s", __func__, ike->key,
3875416581eSnicm 		    key_string_lookup_key(ike->key, 1), ike->data);
3889265d1acSnicm 	}
3899265d1acSnicm }
3909265d1acSnicm 
39129ebed37Snicm /* Translate a key code into an output key sequence for a pane. */
39229ebed37Snicm int
39329ebed37Snicm input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m)
39429ebed37Snicm {
3959265d1acSnicm 	if (log_get_level() != 0) {
39629ebed37Snicm 		log_debug("writing key 0x%llx (%s) to %%%u", key,
3975416581eSnicm 		    key_string_lookup_key(key, 1), wp->id);
3989265d1acSnicm 	}
39929ebed37Snicm 
40029ebed37Snicm 	if (KEYC_IS_MOUSE(key)) {
40129ebed37Snicm 		if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id)
40229ebed37Snicm 			input_key_mouse(wp, m);
40329ebed37Snicm 		return (0);
40429ebed37Snicm 	}
4059265d1acSnicm 	return (input_key(wp->screen, wp->event, key));
40629ebed37Snicm }
40729ebed37Snicm 
408090e14efSnicm static void
409be6be6edSnicm input_key_write(const char *from, struct bufferevent *bev, const char *data,
410090e14efSnicm     size_t size)
411090e14efSnicm {
412090e14efSnicm 	log_debug("%s: %.*s", from, (int)size, data);
413090e14efSnicm 	bufferevent_write(bev, data, size);
414090e14efSnicm }
415090e14efSnicm 
416719f5715Snicm /*
417719f5715Snicm  * Encode and write an extended key escape sequence in one of the two
418719f5715Snicm  * possible formats, depending on the configured output mode.
419719f5715Snicm  */
420719f5715Snicm static int
421719f5715Snicm input_key_extended(struct bufferevent *bev, key_code key)
422311827fbSnicm {
423a16e511eSnicm 	char		 tmp[64], modifier;
424719f5715Snicm 	struct utf8_data ud;
425719f5715Snicm 	wchar_t		 wc;
426311827fbSnicm 
427719f5715Snicm 	switch (key & KEYC_MASK_MODIFIERS) {
428a16e511eSnicm 	case KEYC_SHIFT:
429a16e511eSnicm 		modifier = '2';
430a16e511eSnicm 		break;
431a16e511eSnicm 	case KEYC_META:
432a16e511eSnicm 		modifier = '3';
433a16e511eSnicm 		break;
434a16e511eSnicm 	case KEYC_SHIFT|KEYC_META:
435a16e511eSnicm 		modifier = '4';
436a16e511eSnicm 		break;
437a16e511eSnicm 	case KEYC_CTRL:
438a16e511eSnicm 		modifier = '5';
439a16e511eSnicm 		break;
440a16e511eSnicm 	case KEYC_SHIFT|KEYC_CTRL:
441a16e511eSnicm 		modifier = '6';
442a16e511eSnicm 		break;
443a16e511eSnicm 	case KEYC_META|KEYC_CTRL:
444a16e511eSnicm 		modifier = '7';
445a16e511eSnicm 		break;
446a16e511eSnicm 	case KEYC_SHIFT|KEYC_META|KEYC_CTRL:
447a16e511eSnicm 		modifier = '8';
448a16e511eSnicm 		break;
449cf415eadSnicm 	default:
450719f5715Snicm 		return (-1);
451a16e511eSnicm 	}
452719f5715Snicm 
453719f5715Snicm 	if (KEYC_IS_UNICODE(key)) {
454719f5715Snicm 		utf8_to_data(key & KEYC_MASK_KEY, &ud);
455719f5715Snicm 		if (utf8_towc(&ud, &wc) == UTF8_DONE)
456719f5715Snicm 			key = wc;
457719f5715Snicm 		else
458719f5715Snicm 			return (-1);
459719f5715Snicm 	} else
460719f5715Snicm 		key &= KEYC_MASK_KEY;
461719f5715Snicm 
462719f5715Snicm 	if (options_get_number(global_options, "extended-keys-format") == 1)
463719f5715Snicm 		xsnprintf(tmp, sizeof tmp, "\033[27;%c;%llu~", modifier, key);
464719f5715Snicm 	else
465719f5715Snicm 		xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", key, modifier);
466719f5715Snicm 
467090e14efSnicm 	input_key_write(__func__, bev, tmp, strlen(tmp));
468a16e511eSnicm 	return (0);
469719f5715Snicm }
470a16e511eSnicm 
471719f5715Snicm /*
472719f5715Snicm  * Outputs the key in the "standard" mode. This is by far the most
473719f5715Snicm  * complicated output mode, with a lot of remapping in order to
474719f5715Snicm  * emulate quirks of terminals that today can be only found in museums.
475719f5715Snicm  */
476719f5715Snicm static int
477719f5715Snicm input_key_vt10x(struct bufferevent *bev, key_code key)
478719f5715Snicm {
479719f5715Snicm 	struct utf8_data	 ud;
480719f5715Snicm 	key_code		 onlykey;
481719f5715Snicm 	char			*p;
482719f5715Snicm 	static const char	*standard_map[2] = {
483719f5715Snicm 		"1!9(0)=+;:'\",<.>/-8? 2",
484719f5715Snicm 		"119900=+;;'',,..\x1f\x1f\x7f\x7f\0\0",
485719f5715Snicm 	};
486719f5715Snicm 
487719f5715Snicm 	log_debug("%s: key in %llx", __func__, key);
488719f5715Snicm 
489719f5715Snicm 	if (key & KEYC_META)
490719f5715Snicm 		input_key_write(__func__, bev, "\033", 1);
491719f5715Snicm 
492719f5715Snicm 	/*
493719f5715Snicm 	 * There's no way to report modifiers for unicode keys in standard mode
494719f5715Snicm 	 * so lose the modifiers.
495719f5715Snicm 	 */
496719f5715Snicm 	if (KEYC_IS_UNICODE(key)) {
497719f5715Snicm 		utf8_to_data(key, &ud);
498719f5715Snicm                 input_key_write(__func__, bev, ud.data, ud.size);
499719f5715Snicm 		return (0);
500719f5715Snicm 	}
501719f5715Snicm 
502482624f4Snicm 	/*
503482624f4Snicm 	 * Prevent TAB, CR and LF from being swallowed by the C0 remapping
504482624f4Snicm 	 * logic.
505482624f4Snicm 	 */
5063be60a96Snicm 	onlykey = key & KEYC_MASK_KEY;
507482624f4Snicm 	if (onlykey == '\r' || onlykey == '\n' || onlykey == '\t')
508719f5715Snicm 		key &= ~KEYC_CTRL;
509719f5715Snicm 
510719f5715Snicm 	/*
511719f5715Snicm 	 * Convert keys with Ctrl modifier into corresponding C0 control codes,
512719f5715Snicm 	 * with the exception of *some* keys, which are remapped into printable
513719f5715Snicm 	 * ASCII characters.
514719f5715Snicm 	 *
515719f5715Snicm 	 * There is no special handling for Shift modifier, which is pretty
516719f5715Snicm 	 * much redundant anyway, as no terminal will send <base key>|SHIFT,
517719f5715Snicm 	 * but only <shifted key>|SHIFT.
518719f5715Snicm 	 */
519719f5715Snicm 	if (key & KEYC_CTRL) {
520719f5715Snicm 		p = strchr(standard_map[0], onlykey);
521719f5715Snicm 		if (p != NULL)
522719f5715Snicm 			key = standard_map[1][p - standard_map[0]];
523719f5715Snicm 		else if (onlykey >= '3' && onlykey <= '7')
524719f5715Snicm 			key = onlykey - '\030';
525719f5715Snicm 		else if (onlykey >= '@' && onlykey <= '~')
526719f5715Snicm 			key = onlykey & 0x1f;
527719f5715Snicm 		else
528a16e511eSnicm 			return (-1);
529a16e511eSnicm 	}
530a16e511eSnicm 
531719f5715Snicm 	log_debug("%s: key out %llx", __func__, key);
532719f5715Snicm 
533719f5715Snicm 	ud.data[0] = key & 0x7f;
534719f5715Snicm 	input_key_write(__func__, bev, &ud.data[0], 1);
535719f5715Snicm 	return (0);
536719f5715Snicm }
537719f5715Snicm 
538719f5715Snicm /* Pick keys that are reported as vt10x keys in modifyOtherKeys=1 mode. */
539719f5715Snicm static int
540719f5715Snicm input_key_mode1(struct bufferevent *bev, key_code key)
541719f5715Snicm {
542719f5715Snicm 	key_code	 onlykey;
543719f5715Snicm 
544719f5715Snicm 	log_debug("%s: key in %llx", __func__, key);
545719f5715Snicm 
546429aa45eSnicm 	/* A regular or shifted key + Meta. */
547429aa45eSnicm 	if ((key & (KEYC_CTRL | KEYC_META)) == KEYC_META)
548429aa45eSnicm 		return (input_key_vt10x(bev, key));
549429aa45eSnicm 
550719f5715Snicm 	/*
551719f5715Snicm 	 * As per
552719f5715Snicm 	 * https://invisible-island.net/xterm/modified-keys-us-pc105.html.
553719f5715Snicm 	 */
554719f5715Snicm 	onlykey = key & KEYC_MASK_KEY;
555429aa45eSnicm 	if ((key & KEYC_CTRL) &&
556de3e4ea1Snicm 	    (onlykey == ' ' ||
557de3e4ea1Snicm 	     onlykey == '/' ||
558de3e4ea1Snicm 	     onlykey == '@' ||
559de3e4ea1Snicm 	     onlykey == '^' ||
560719f5715Snicm 	     (onlykey >= '2' && onlykey <= '8') ||
561719f5715Snicm 	     (onlykey >= '@' && onlykey <= '~')))
562719f5715Snicm 		return (input_key_vt10x(bev, key));
563719f5715Snicm 
564719f5715Snicm 	return (-1);
565719f5715Snicm }
566719f5715Snicm 
567719f5715Snicm /* Translate a key code into an output key sequence. */
568719f5715Snicm int
569719f5715Snicm input_key(struct screen *s, struct bufferevent *bev, key_code key)
570719f5715Snicm {
571719f5715Snicm 	struct input_key_entry	*ike = NULL;
572719f5715Snicm 	key_code		 newkey;
573719f5715Snicm 	struct utf8_data	 ud;
574719f5715Snicm 
575719f5715Snicm 	/* Mouse keys need a pane. */
576719f5715Snicm 	if (KEYC_IS_MOUSE(key))
577719f5715Snicm 		return (0);
578719f5715Snicm 
579719f5715Snicm 	/* Literal keys go as themselves (can't be more than eight bits). */
580719f5715Snicm 	if (key & KEYC_LITERAL) {
581719f5715Snicm 		ud.data[0] = (u_char)key;
582719f5715Snicm 		input_key_write(__func__, bev, &ud.data[0], 1);
583719f5715Snicm 		return (0);
584719f5715Snicm 	}
585719f5715Snicm 
586719f5715Snicm 	/* Is this backspace? */
587719f5715Snicm 	if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
588719f5715Snicm 		newkey = options_get_number(global_options, "backspace");
589*2f106d38Snicm 		log_debug("%s: key 0x%llx is backspace -> 0x%llx", __func__,
590*2f106d38Snicm 		    key, newkey);
591*2f106d38Snicm 		if ((key & KEYC_MASK_MODIFIERS) == 0) {
592*2f106d38Snicm 			ud.data[0] = 255;
593*2f106d38Snicm 			if ((newkey & KEYC_MASK_MODIFIERS) == 0)
594*2f106d38Snicm 				ud.data[0] = newkey;
595*2f106d38Snicm 			else if ((newkey & KEYC_MASK_MODIFIERS) == KEYC_CTRL) {
596*2f106d38Snicm 				newkey &= KEYC_MASK_KEY;
597*2f106d38Snicm 				if (newkey >= 'A' && newkey <= 'Z')
598*2f106d38Snicm 					ud.data[0] = newkey - 0x40;
599*2f106d38Snicm 				else if (newkey >= 'a' && newkey <= 'z')
600*2f106d38Snicm 					ud.data[0] = newkey - 0x60;
601*2f106d38Snicm 			}
602*2f106d38Snicm 			if (ud.data[0] != 255)
603*2f106d38Snicm 				input_key_write(__func__, bev, &ud.data[0], 1);
604*2f106d38Snicm 			return (0);
605*2f106d38Snicm 		}
606*2f106d38Snicm 		key = newkey|(key & (KEYC_MASK_FLAGS|KEYC_MASK_MODIFIERS));
607719f5715Snicm 	}
608719f5715Snicm 
609719f5715Snicm 	/* Is this backtab? */
610719f5715Snicm 	if ((key & KEYC_MASK_KEY) == KEYC_BTAB) {
6119e4e6261Snicm 		if (s->mode & MODE_KEYS_EXTENDED_2) {
612719f5715Snicm 			/* When in xterm extended mode, remap into S-Tab. */
613719f5715Snicm 			key = '\011' | (key & ~KEYC_MASK_KEY) | KEYC_SHIFT;
614719f5715Snicm 		} else {
615719f5715Snicm 			/* Otherwise clear modifiers. */
616719f5715Snicm 			key &= ~KEYC_MASK_MODIFIERS;
617719f5715Snicm 		}
618719f5715Snicm 	}
619719f5715Snicm 
620719f5715Snicm 	/*
621719f5715Snicm 	 * A trivial case, that is a 7-bit key, excluding C0 control characters
622719f5715Snicm 	 * that can't be entered from the keyboard, and no modifiers; or a UTF-8
623719f5715Snicm 	 * key and no modifiers.
624719f5715Snicm 	 */
625719f5715Snicm 	if (!(key & ~KEYC_MASK_KEY)) {
6264aaf2c8dSnicm 		if (key == C0_HT ||
6274aaf2c8dSnicm 		    key == C0_CR ||
6284aaf2c8dSnicm 		    key == C0_ESC ||
629719f5715Snicm 		    (key >= 0x20 && key <= 0x7f)) {
630719f5715Snicm 			ud.data[0] = key;
631719f5715Snicm 			input_key_write(__func__, bev, &ud.data[0], 1);
632719f5715Snicm 			return (0);
633719f5715Snicm 		}
634719f5715Snicm 		if (KEYC_IS_UNICODE(key)) {
635719f5715Snicm 			utf8_to_data(key, &ud);
636719f5715Snicm 			input_key_write(__func__, bev, ud.data, ud.size);
637719f5715Snicm 			return (0);
638719f5715Snicm 		}
639719f5715Snicm 	}
640719f5715Snicm 
641719f5715Snicm 	/*
642719f5715Snicm 	 * Look up the standard VT10x keys in the tree. If not in application
643719f5715Snicm 	 * keypad or cursor mode, remove the respective flags from the key.
644719f5715Snicm 	 */
645719f5715Snicm 	if (~s->mode & MODE_KKEYPAD)
646719f5715Snicm 		key &= ~KEYC_KEYPAD;
647719f5715Snicm 	if (~s->mode & MODE_KCURSOR)
648719f5715Snicm 		key &= ~KEYC_CURSOR;
649719f5715Snicm 	if (ike == NULL)
650719f5715Snicm 		ike = input_key_get(key);
651719f5715Snicm 	if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META))
652719f5715Snicm 		ike = input_key_get(key & ~KEYC_META);
653719f5715Snicm 	if (ike == NULL && (key & KEYC_CURSOR))
654719f5715Snicm 		ike = input_key_get(key & ~KEYC_CURSOR);
655719f5715Snicm 	if (ike == NULL && (key & KEYC_KEYPAD))
656719f5715Snicm 		ike = input_key_get(key & ~KEYC_KEYPAD);
657719f5715Snicm 	if (ike != NULL) {
658719f5715Snicm 		log_debug("%s: found key 0x%llx: \"%s\"", __func__, key,
659719f5715Snicm 		    ike->data);
660a1f482feSnicm 		if (KEYC_IS_PASTE(key) && (~s->mode & MODE_BRACKETPASTE))
661719f5715Snicm 			return (0);
662719f5715Snicm 		if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
663719f5715Snicm 			input_key_write(__func__, bev, "\033", 1);
664719f5715Snicm 		input_key_write(__func__, bev, ike->data, strlen(ike->data));
665719f5715Snicm 		return (0);
666719f5715Snicm 	}
667719f5715Snicm 
6683be60a96Snicm 	/* Ignore internal function key codes. */
6693be60a96Snicm 	if ((key >= KEYC_BASE && key < KEYC_BASE_END) ||
6703be60a96Snicm 	    (key >= KEYC_USER && key < KEYC_USER_END)) {
6713be60a96Snicm 		log_debug("%s: ignoring key 0x%llx", __func__, key);
6723be60a96Snicm 		return (0);
6733be60a96Snicm 	}
6743be60a96Snicm 
675719f5715Snicm 	/*
676719f5715Snicm 	 * No builtin key sequence; construct an extended key sequence
677719f5715Snicm 	 * depending on the client mode.
678719f5715Snicm 	 *
679719f5715Snicm 	 * If something invalid reaches here, an invalid output may be
680719f5715Snicm 	 * produced. For example Ctrl-Shift-2 is invalid (as there's
681719f5715Snicm 	 * no way to enter it). The correct form is Ctrl-Shift-@, at
682719f5715Snicm 	 * least in US English keyboard layout.
683719f5715Snicm 	 */
684719f5715Snicm 	switch (s->mode & EXTENDED_KEY_MODES) {
685719f5715Snicm 	case MODE_KEYS_EXTENDED_2:
686719f5715Snicm 		/*
687719f5715Snicm 		 * The simplest mode to handle - *all* modified keys are
688719f5715Snicm 		 * reported in the extended form.
689719f5715Snicm 		 */
690719f5715Snicm 		return (input_key_extended(bev, key));
691719f5715Snicm         case MODE_KEYS_EXTENDED:
692719f5715Snicm 		/*
693719f5715Snicm 		 * Some keys are still reported in standard mode, to maintain
694719f5715Snicm 		 * compatibility with applications unaware of extended keys.
695719f5715Snicm 		 */
696719f5715Snicm 		if (input_key_mode1(bev, key) == -1)
697719f5715Snicm 			return (input_key_extended(bev, key));
698719f5715Snicm 		return (0);
699719f5715Snicm 	default:
700719f5715Snicm 		/* The standard mode. */
701719f5715Snicm 		return (input_key_vt10x(bev, key));
702719f5715Snicm 	}
703719f5715Snicm }
704719f5715Snicm 
70529724ccbSnicm /* Get mouse event string. */
70629724ccbSnicm int
70729724ccbSnicm input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y,
70829724ccbSnicm     const char **rbuf, size_t *rlen)
709311827fbSnicm {
71029724ccbSnicm 	static char	 buf[40];
711699995ddSnicm 	size_t		 len;
712fdcac80eSnicm 
71329724ccbSnicm 	*rbuf = NULL;
71429724ccbSnicm 	*rlen = 0;
715e048bb79Snicm 
7169f26c5b1Snicm 	/* If this pane is not in button or all mode, discard motion events. */
71713e036e4Snicm 	if (MOUSE_DRAG(m->b) && (s->mode & MOTION_MOUSE_MODES) == 0)
71829724ccbSnicm 		return (0);
71957d747eeSnicm 	if ((s->mode & ALL_MOUSE_MODES) == 0)
72057d747eeSnicm 		return (0);
721e048bb79Snicm 
7226c0777c5Snicm 	/*
7239f26c5b1Snicm 	 * If this event is a release event and not in all mode, discard it.
7249f26c5b1Snicm 	 * In SGR mode we can tell absolutely because a release is normally
7259f26c5b1Snicm 	 * shown by the last character. Without SGR, we check if the last
7269f26c5b1Snicm 	 * buttons was also a release.
7279f26c5b1Snicm 	 */
7289f26c5b1Snicm 	if (m->sgr_type != ' ') {
7299f26c5b1Snicm 		if (MOUSE_DRAG(m->sgr_b) &&
7304e5846a2Snicm 		    MOUSE_RELEASE(m->sgr_b) &&
73113e036e4Snicm 		    (~s->mode & MODE_MOUSE_ALL))
73229724ccbSnicm 			return (0);
7339f26c5b1Snicm 	} else {
7349f26c5b1Snicm 		if (MOUSE_DRAG(m->b) &&
7354e5846a2Snicm 		    MOUSE_RELEASE(m->b) &&
7364e5846a2Snicm 		    MOUSE_RELEASE(m->lb) &&
73713e036e4Snicm 		    (~s->mode & MODE_MOUSE_ALL))
73829724ccbSnicm 			return (0);
7399f26c5b1Snicm 	}
7409f26c5b1Snicm 
7419f26c5b1Snicm 	/*
742e048bb79Snicm 	 * Use the SGR (1006) extension only if the application requested it
743e048bb79Snicm 	 * and the underlying terminal also sent the event in this format (this
744e048bb79Snicm 	 * is because an old style mouse release event cannot be converted into
745e048bb79Snicm 	 * the new SGR format, since the released button is unknown). Otherwise
746e048bb79Snicm 	 * pretend that tmux doesn't speak this extension, and fall back to the
7473b213501Snicm 	 * UTF-8 (1005) extension if the application requested, or to the
7486c0777c5Snicm 	 * legacy format.
7496c0777c5Snicm 	 */
7509f26c5b1Snicm 	if (m->sgr_type != ' ' && (s->mode & MODE_MOUSE_SGR)) {
751e5f4d2afSnicm 		len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c",
752e048bb79Snicm 		    m->sgr_b, x + 1, y + 1, m->sgr_type);
7539f26c5b1Snicm 	} else if (s->mode & MODE_MOUSE_UTF8) {
75481e6b289Snicm 		if (m->b > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_BTN_OFF ||
75581e6b289Snicm 		    x > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF ||
75681e6b289Snicm 		    y > MOUSE_PARAM_UTF8_MAX - MOUSE_PARAM_POS_OFF)
75729724ccbSnicm 			return (0);
7583b213501Snicm 		len = xsnprintf(buf, sizeof buf, "\033[M");
75981e6b289Snicm 		len += input_key_split2(m->b + MOUSE_PARAM_BTN_OFF, &buf[len]);
76081e6b289Snicm 		len += input_key_split2(x + MOUSE_PARAM_POS_OFF, &buf[len]);
76181e6b289Snicm 		len += input_key_split2(y + MOUSE_PARAM_POS_OFF, &buf[len]);
762699995ddSnicm 	} else {
76381e6b289Snicm 		if (m->b + MOUSE_PARAM_BTN_OFF > MOUSE_PARAM_MAX)
76429724ccbSnicm 			return (0);
76581e6b289Snicm 
766699995ddSnicm 		len = xsnprintf(buf, sizeof buf, "\033[M");
76781e6b289Snicm 		buf[len++] = m->b + MOUSE_PARAM_BTN_OFF;
76881e6b289Snicm 
76981e6b289Snicm 		/*
77081e6b289Snicm 		 * The incoming x and y may be out of the range which can be
77181e6b289Snicm 		 * supported by the "normal" mouse protocol. Clamp the
77281e6b289Snicm 		 * coordinates to the supported range.
77381e6b289Snicm 		 */
77481e6b289Snicm 		if (x + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX)
77581e6b289Snicm 			buf[len++] = MOUSE_PARAM_MAX;
77681e6b289Snicm 		else
77781e6b289Snicm 			buf[len++] = x + MOUSE_PARAM_POS_OFF;
77881e6b289Snicm 		if (y + MOUSE_PARAM_POS_OFF > MOUSE_PARAM_MAX)
77981e6b289Snicm 			buf[len++] = MOUSE_PARAM_MAX;
78081e6b289Snicm 		else
78181e6b289Snicm 			buf[len++] = y + MOUSE_PARAM_POS_OFF;
782699995ddSnicm 	}
78329724ccbSnicm 
78429724ccbSnicm 	*rbuf = buf;
78529724ccbSnicm 	*rlen = len;
78629724ccbSnicm 	return (1);
78729724ccbSnicm }
78829724ccbSnicm 
78929724ccbSnicm /* Translate mouse and output. */
79029724ccbSnicm static void
79129724ccbSnicm input_key_mouse(struct window_pane *wp, struct mouse_event *m)
79229724ccbSnicm {
79329724ccbSnicm 	struct screen	*s = wp->screen;
79429724ccbSnicm 	u_int		 x, y;
79529724ccbSnicm 	const char	*buf;
79629724ccbSnicm 	size_t		 len;
79729724ccbSnicm 
79829724ccbSnicm 	/* Ignore events if no mouse mode or the pane is not visible. */
79929724ccbSnicm 	if (m->ignore || (s->mode & ALL_MOUSE_MODES) == 0)
80029724ccbSnicm 		return;
80129724ccbSnicm 	if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
80229724ccbSnicm 		return;
80329724ccbSnicm 	if (!window_pane_visible(wp))
80429724ccbSnicm 		return;
80529724ccbSnicm 	if (!input_key_get_mouse(s, m, x, y, &buf, &len))
80629724ccbSnicm 		return;
807302fe310Snicm 	log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id);
808090e14efSnicm 	input_key_write(__func__, wp->event, buf, len);
809311827fbSnicm }
810