GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/input-keys.c Lines: 0 91 0.0 %
Date: 2017-11-07 Branches: 0 82 0.0 %

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