xref: /openbsd-src/usr.bin/tmux/input-keys.c (revision 29ebed3733c53f26b45edd4bc4795dfaa9d323c8)
1 /* $OpenBSD: input-keys.c,v 1.69 2020/03/19 14:03:48 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 struct input_key_ent {
37 	key_code	 key;
38 	const char	*data;
39 
40 	int		 flags;
41 #define INPUTKEY_KEYPAD 0x1	/* keypad key */
42 #define INPUTKEY_CURSOR 0x2	/* cursor key */
43 };
44 
45 static const struct input_key_ent input_keys[] = {
46 	/* Paste keys. */
47 	{ KEYC_PASTE_START,	"\033[200~",	0 },
48 	{ KEYC_PASTE_END,	"\033[201~",	0 },
49 
50 	/* Function keys. */
51 	{ KEYC_F1,		"\033OP",	0 },
52 	{ KEYC_F2,		"\033OQ",	0 },
53 	{ KEYC_F3,		"\033OR",	0 },
54 	{ KEYC_F4,		"\033OS",	0 },
55 	{ KEYC_F5,		"\033[15~",	0 },
56 	{ KEYC_F6,		"\033[17~",	0 },
57 	{ KEYC_F7,		"\033[18~",	0 },
58 	{ KEYC_F8,		"\033[19~",	0 },
59 	{ KEYC_F9,		"\033[20~",	0 },
60 	{ KEYC_F10,		"\033[21~",	0 },
61 	{ KEYC_F11,		"\033[23~",	0 },
62 	{ KEYC_F12,		"\033[24~",	0 },
63 	{ KEYC_F1|KEYC_SHIFT,	"\033[25~",	0 },
64 	{ KEYC_F2|KEYC_SHIFT,	"\033[26~",	0 },
65 	{ KEYC_F3|KEYC_SHIFT,	"\033[28~",	0 },
66 	{ KEYC_F4|KEYC_SHIFT,	"\033[29~",	0 },
67 	{ KEYC_F5|KEYC_SHIFT,	"\033[31~",	0 },
68 	{ KEYC_F6|KEYC_SHIFT,	"\033[32~",	0 },
69 	{ KEYC_F7|KEYC_SHIFT,	"\033[33~",	0 },
70 	{ KEYC_F8|KEYC_SHIFT,	"\033[34~",	0 },
71 	{ KEYC_IC,		"\033[2~",	0 },
72 	{ KEYC_DC,		"\033[3~",	0 },
73 	{ KEYC_HOME,		"\033[1~",	0 },
74 	{ KEYC_END,		"\033[4~",	0 },
75 	{ KEYC_NPAGE,		"\033[6~",	0 },
76 	{ KEYC_PPAGE,		"\033[5~",	0 },
77 	{ KEYC_BTAB,		"\033[Z",	0 },
78 
79 	/*
80 	 * Arrow keys. Cursor versions must come first. The codes are toggled
81 	 * between CSI and SS3 versions when ctrl is pressed.
82 	 */
83 	{ KEYC_UP|KEYC_CTRL,	"\033[A",	INPUTKEY_CURSOR },
84 	{ KEYC_DOWN|KEYC_CTRL,	"\033[B",	INPUTKEY_CURSOR },
85 	{ KEYC_RIGHT|KEYC_CTRL,	"\033[C",	INPUTKEY_CURSOR },
86 	{ KEYC_LEFT|KEYC_CTRL,	"\033[D",	INPUTKEY_CURSOR },
87 
88 	{ KEYC_UP,		"\033OA",	INPUTKEY_CURSOR },
89 	{ KEYC_DOWN,		"\033OB",	INPUTKEY_CURSOR },
90 	{ KEYC_RIGHT,		"\033OC",	INPUTKEY_CURSOR },
91 	{ KEYC_LEFT,		"\033OD",	INPUTKEY_CURSOR },
92 
93 	{ KEYC_UP|KEYC_CTRL,	"\033OA",	0 },
94 	{ KEYC_DOWN|KEYC_CTRL,	"\033OB",	0 },
95 	{ KEYC_RIGHT|KEYC_CTRL,	"\033OC",	0 },
96 	{ KEYC_LEFT|KEYC_CTRL,	"\033OD",	0 },
97 
98 	{ KEYC_UP,		"\033[A",	0 },
99 	{ KEYC_DOWN,		"\033[B",	0 },
100 	{ KEYC_RIGHT,		"\033[C",	0 },
101 	{ KEYC_LEFT,		"\033[D",	0 },
102 
103 	/* Keypad keys. Keypad versions must come first. */
104 	{ KEYC_KP_SLASH,	"\033Oo",	INPUTKEY_KEYPAD },
105 	{ KEYC_KP_STAR,		"\033Oj",	INPUTKEY_KEYPAD },
106 	{ KEYC_KP_MINUS,	"\033Om",	INPUTKEY_KEYPAD },
107 	{ KEYC_KP_SEVEN,	"\033Ow",	INPUTKEY_KEYPAD },
108 	{ KEYC_KP_EIGHT,	"\033Ox",	INPUTKEY_KEYPAD },
109 	{ KEYC_KP_NINE,		"\033Oy",	INPUTKEY_KEYPAD },
110 	{ KEYC_KP_PLUS,		"\033Ok",	INPUTKEY_KEYPAD },
111 	{ KEYC_KP_FOUR,		"\033Ot",	INPUTKEY_KEYPAD },
112 	{ KEYC_KP_FIVE,		"\033Ou",	INPUTKEY_KEYPAD },
113 	{ KEYC_KP_SIX,		"\033Ov",	INPUTKEY_KEYPAD },
114 	{ KEYC_KP_ONE,		"\033Oq",	INPUTKEY_KEYPAD },
115 	{ KEYC_KP_TWO,		"\033Or",	INPUTKEY_KEYPAD },
116 	{ KEYC_KP_THREE,	"\033Os",	INPUTKEY_KEYPAD },
117 	{ KEYC_KP_ENTER,	"\033OM",	INPUTKEY_KEYPAD },
118 	{ KEYC_KP_ZERO,		"\033Op",	INPUTKEY_KEYPAD },
119 	{ KEYC_KP_PERIOD,	"\033On",	INPUTKEY_KEYPAD },
120 
121 	{ KEYC_KP_SLASH,	"/",		0 },
122 	{ KEYC_KP_STAR,		"*",		0 },
123 	{ KEYC_KP_MINUS,	"-",		0 },
124 	{ KEYC_KP_SEVEN,	"7",		0 },
125 	{ KEYC_KP_EIGHT,	"8",		0 },
126 	{ KEYC_KP_NINE,		"9",		0 },
127 	{ KEYC_KP_PLUS,		"+",		0 },
128 	{ KEYC_KP_FOUR,		"4",		0 },
129 	{ KEYC_KP_FIVE,		"5",		0 },
130 	{ KEYC_KP_SIX,		"6",		0 },
131 	{ KEYC_KP_ONE,		"1",		0 },
132 	{ KEYC_KP_TWO,		"2",		0 },
133 	{ KEYC_KP_THREE,	"3",		0 },
134 	{ KEYC_KP_ENTER,	"\n",		0 },
135 	{ KEYC_KP_ZERO,		"0",		0 },
136 	{ KEYC_KP_PERIOD,	".",		0 },
137 };
138 
139 /* Split a character into two UTF-8 bytes. */
140 static size_t
141 input_split2(u_int c, u_char *dst)
142 {
143 	if (c > 0x7f) {
144 		dst[0] = (c >> 6) | 0xc0;
145 		dst[1] = (c & 0x3f) | 0x80;
146 		return (2);
147 	}
148 	dst[0] = c;
149 	return (1);
150 }
151 
152 /* Translate a key code into an output key sequence for a pane. */
153 int
154 input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m)
155 {
156 	log_debug("writing key 0x%llx (%s) to %%%u", key,
157 	    key_string_lookup_key(key), wp->id);
158 
159 	if (KEYC_IS_MOUSE(key)) {
160 		if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id)
161 			input_key_mouse(wp, m);
162 		return (0);
163 	}
164 	return (input_key(wp, wp->screen, wp->event, key));
165 }
166 
167 /* Translate a key code into an output key sequence. */
168 int
169 input_key(struct window_pane *wp, struct screen *s, struct bufferevent *bev,
170     key_code key)
171 {
172 	const struct input_key_ent	*ike;
173 	u_int				 i;
174 	size_t				 dlen;
175 	char				*out;
176 	key_code			 justkey, newkey;
177 	struct utf8_data		 ud;
178 
179 	/* Mouse keys need a pane. */
180 	if (KEYC_IS_MOUSE(key))
181 		return (0);
182 
183 	/* Literal keys go as themselves (can't be more than eight bits). */
184 	if (key & KEYC_LITERAL) {
185 		ud.data[0] = (u_char)key;
186 		bufferevent_write(bev, &ud.data[0], 1);
187 		return (0);
188 	}
189 
190 	/* Is this backspace? */
191 	if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
192 		newkey = options_get_number(global_options, "backspace");
193 		if (newkey >= 0x7f)
194 			newkey = '\177';
195 		key = newkey|(key & KEYC_MASK_MOD);
196 	}
197 
198 	/*
199 	 * If this is a normal 7-bit key, just send it, with a leading escape
200 	 * if necessary. If it is a UTF-8 key, split it and send it.
201 	 */
202 	justkey = (key & ~(KEYC_XTERM|KEYC_ESCAPE));
203 	if (justkey <= 0x7f) {
204 		if (key & KEYC_ESCAPE)
205 			bufferevent_write(bev, "\033", 1);
206 		ud.data[0] = justkey;
207 		bufferevent_write(bev, &ud.data[0], 1);
208 		return (0);
209 	}
210 	if (justkey > 0x7f && justkey < KEYC_BASE) {
211 		if (utf8_split(justkey, &ud) != UTF8_DONE)
212 			return (-1);
213 		if (key & KEYC_ESCAPE)
214 			bufferevent_write(bev, "\033", 1);
215 		bufferevent_write(bev, ud.data, ud.size);
216 		return (0);
217 	}
218 
219 	/*
220 	 * Then try to look this up as an xterm key, if the flag to output them
221 	 * is set.
222 	 */
223 	if (wp == NULL || options_get_number(wp->window->options, "xterm-keys")) {
224 		if ((out = xterm_keys_lookup(key)) != NULL) {
225 			bufferevent_write(bev, out, strlen(out));
226 			free(out);
227 			return (0);
228 		}
229 	}
230 	key &= ~KEYC_XTERM;
231 
232 	/* Otherwise look the key up in the table. */
233 	for (i = 0; i < nitems(input_keys); i++) {
234 		ike = &input_keys[i];
235 
236 		if ((ike->flags & INPUTKEY_KEYPAD) && (~s->mode & MODE_KKEYPAD))
237 			continue;
238 		if ((ike->flags & INPUTKEY_CURSOR) && (~s->mode & MODE_KCURSOR))
239 			continue;
240 
241 		if ((key & KEYC_ESCAPE) && (ike->key | KEYC_ESCAPE) == key)
242 			break;
243 		if (ike->key == key)
244 			break;
245 	}
246 	if (i == nitems(input_keys)) {
247 		log_debug("key 0x%llx missing", key);
248 		return (-1);
249 	}
250 	dlen = strlen(ike->data);
251 	log_debug("found key 0x%llx: \"%s\"", key, ike->data);
252 
253 	/* Prefix a \033 for escape. */
254 	if (key & KEYC_ESCAPE)
255 		bufferevent_write(bev, "\033", 1);
256 	bufferevent_write(bev, ike->data, dlen);
257 	return (0);
258 }
259 
260 /* Translate mouse and output. */
261 static void
262 input_key_mouse(struct window_pane *wp, struct mouse_event *m)
263 {
264 	struct screen	*s = wp->screen;
265 	char		 buf[40];
266 	size_t		 len;
267 	u_int		 x, y;
268 
269 	/* Ignore events if no mouse mode or the pane is not visible. */
270 	if (m->ignore || (s->mode & ALL_MOUSE_MODES) == 0)
271 		return;
272 	if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
273 		return;
274 	if (!window_pane_visible(wp))
275 		return;
276 
277 	/* If this pane is not in button or all mode, discard motion events. */
278 	if (MOUSE_DRAG(m->b) && (s->mode & MOTION_MOUSE_MODES) == 0)
279 	    return;
280 
281 	/*
282 	 * If this event is a release event and not in all mode, discard it.
283 	 * In SGR mode we can tell absolutely because a release is normally
284 	 * shown by the last character. Without SGR, we check if the last
285 	 * buttons was also a release.
286 	 */
287 	if (m->sgr_type != ' ') {
288 		if (MOUSE_DRAG(m->sgr_b) &&
289 		    MOUSE_BUTTONS(m->sgr_b) == 3 &&
290 		    (~s->mode & MODE_MOUSE_ALL))
291 			return;
292 	} else {
293 		if (MOUSE_DRAG(m->b) &&
294 		    MOUSE_BUTTONS(m->b) == 3 &&
295 		    MOUSE_BUTTONS(m->lb) == 3 &&
296 		    (~s->mode & MODE_MOUSE_ALL))
297 			return;
298 	}
299 
300 	/*
301 	 * Use the SGR (1006) extension only if the application requested it
302 	 * and the underlying terminal also sent the event in this format (this
303 	 * is because an old style mouse release event cannot be converted into
304 	 * the new SGR format, since the released button is unknown). Otherwise
305 	 * pretend that tmux doesn't speak this extension, and fall back to the
306 	 * UTF-8 (1005) extension if the application requested, or to the
307 	 * legacy format.
308 	 */
309 	if (m->sgr_type != ' ' && (s->mode & MODE_MOUSE_SGR)) {
310 		len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c",
311 		    m->sgr_b, x + 1, y + 1, m->sgr_type);
312 	} else if (s->mode & MODE_MOUSE_UTF8) {
313 		if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33)
314 			return;
315 		len = xsnprintf(buf, sizeof buf, "\033[M");
316 		len += input_split2(m->b + 32, &buf[len]);
317 		len += input_split2(x + 33, &buf[len]);
318 		len += input_split2(y + 33, &buf[len]);
319 	} else {
320 		if (m->b > 223)
321 			return;
322 		len = xsnprintf(buf, sizeof buf, "\033[M");
323 		buf[len++] = m->b + 32;
324 		buf[len++] = x + 33;
325 		buf[len++] = y + 33;
326 	}
327 	log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id);
328 	bufferevent_write(wp->event, buf, len);
329 }
330