xref: /minix/external/bsd/tmux/dist/input-keys.c (revision 0a6a1f1d)
1 /* Id */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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 <stdlib.h>
22 #include <string.h>
23 
24 #include "tmux.h"
25 
26 /*
27  * This file is rather misleadingly named, it contains the code which takes a
28  * key code and translates it into something suitable to be sent to the
29  * application running in a pane (similar to input.c does in the other
30  * direction with output).
31  */
32 
33 struct input_key_ent {
34 	int		 key;
35 	const char	*data;
36 
37 	int		 flags;
38 #define INPUTKEY_KEYPAD 0x1	/* keypad key */
39 #define INPUTKEY_CURSOR 0x2	/* cursor key */
40 };
41 
42 const struct input_key_ent input_keys[] = {
43 	/* Backspace key. */
44 	{ KEYC_BSPACE,		"\177",		0 },
45 
46 	/* Function keys. */
47 	{ KEYC_F1,		"\033OP",	0 },
48 	{ KEYC_F2,		"\033OQ",	0 },
49 	{ KEYC_F3,		"\033OR",	0 },
50 	{ KEYC_F4,		"\033OS",	0 },
51 	{ KEYC_F5,		"\033[15~",	0 },
52 	{ KEYC_F6,		"\033[17~",	0 },
53 	{ KEYC_F7,		"\033[18~",	0 },
54 	{ KEYC_F8,		"\033[19~",	0 },
55 	{ KEYC_F9,		"\033[20~",	0 },
56 	{ KEYC_F10,		"\033[21~",	0 },
57 	{ KEYC_F11,		"\033[23~",	0 },
58 	{ KEYC_F12,		"\033[24~",	0 },
59 	{ KEYC_F13,		"\033[25~",	0 },
60 	{ KEYC_F14,		"\033[26~",	0 },
61 	{ KEYC_F15,		"\033[28~",	0 },
62 	{ KEYC_F16,		"\033[29~",	0 },
63 	{ KEYC_F17,		"\033[31~",	0 },
64 	{ KEYC_F18,		"\033[32~",	0 },
65 	{ KEYC_F19,		"\033[33~",	0 },
66 	{ KEYC_F20,		"\033[34~",	0 },
67 	{ KEYC_IC,		"\033[2~",	0 },
68 	{ KEYC_DC,		"\033[3~",	0 },
69 	{ KEYC_HOME,		"\033[1~",	0 },
70 	{ KEYC_END,		"\033[4~",	0 },
71 	{ KEYC_NPAGE,		"\033[6~",	0 },
72 	{ KEYC_PPAGE,		"\033[5~",	0 },
73 	{ KEYC_BTAB,		"\033[Z",	0 },
74 
75 	/*
76 	 * Arrow keys. Cursor versions must come first. The codes are toggled
77 	 * between CSI and SS3 versions when ctrl is pressed.
78 	 */
79 	{ KEYC_UP|KEYC_CTRL,	"\033[A",	INPUTKEY_CURSOR },
80 	{ KEYC_DOWN|KEYC_CTRL,	"\033[B",	INPUTKEY_CURSOR },
81 	{ KEYC_RIGHT|KEYC_CTRL,	"\033[C",	INPUTKEY_CURSOR },
82 	{ KEYC_LEFT|KEYC_CTRL,	"\033[D",	INPUTKEY_CURSOR },
83 
84 	{ KEYC_UP,		"\033OA",	INPUTKEY_CURSOR },
85 	{ KEYC_DOWN,		"\033OB",	INPUTKEY_CURSOR },
86 	{ KEYC_RIGHT,		"\033OC",	INPUTKEY_CURSOR },
87 	{ KEYC_LEFT,		"\033OD",	INPUTKEY_CURSOR },
88 
89 	{ KEYC_UP|KEYC_CTRL,	"\033OA",	0 },
90 	{ KEYC_DOWN|KEYC_CTRL,	"\033OB",	0 },
91 	{ KEYC_RIGHT|KEYC_CTRL,	"\033OC",	0 },
92 	{ KEYC_LEFT|KEYC_CTRL,	"\033OD",	0 },
93 
94 	{ KEYC_UP,		"\033[A",	0 },
95 	{ KEYC_DOWN,		"\033[B",	0 },
96 	{ KEYC_RIGHT,		"\033[C",	0 },
97 	{ KEYC_LEFT,		"\033[D",	0 },
98 
99 	/* Keypad keys. Keypad versions must come first. */
100 	{ KEYC_KP_SLASH,	"\033Oo",	INPUTKEY_KEYPAD },
101 	{ KEYC_KP_STAR,		"\033Oj",	INPUTKEY_KEYPAD },
102 	{ KEYC_KP_MINUS,	"\033Om",	INPUTKEY_KEYPAD },
103 	{ KEYC_KP_SEVEN,	"\033Ow",	INPUTKEY_KEYPAD },
104 	{ KEYC_KP_EIGHT,	"\033Ox",	INPUTKEY_KEYPAD },
105 	{ KEYC_KP_NINE,		"\033Oy",	INPUTKEY_KEYPAD },
106 	{ KEYC_KP_PLUS,		"\033Ok",	INPUTKEY_KEYPAD },
107 	{ KEYC_KP_FOUR,		"\033Ot",	INPUTKEY_KEYPAD },
108 	{ KEYC_KP_FIVE,		"\033Ou",	INPUTKEY_KEYPAD },
109 	{ KEYC_KP_SIX,		"\033Ov",	INPUTKEY_KEYPAD },
110 	{ KEYC_KP_ONE,		"\033Oq",	INPUTKEY_KEYPAD },
111 	{ KEYC_KP_TWO,		"\033Or",	INPUTKEY_KEYPAD },
112 	{ KEYC_KP_THREE,	"\033Os",	INPUTKEY_KEYPAD },
113 	{ KEYC_KP_ENTER,	"\033OM",	INPUTKEY_KEYPAD },
114 	{ KEYC_KP_ZERO,		"\033Op",	INPUTKEY_KEYPAD },
115 	{ KEYC_KP_PERIOD,	"\033On",	INPUTKEY_KEYPAD },
116 
117 	{ KEYC_KP_SLASH,	"/",		0 },
118 	{ KEYC_KP_STAR,		"*",		0 },
119 	{ KEYC_KP_MINUS,	"-",		0 },
120 	{ KEYC_KP_SEVEN,	"7",		0 },
121 	{ KEYC_KP_EIGHT,	"8",		0 },
122 	{ KEYC_KP_NINE,		"9",		0 },
123 	{ KEYC_KP_PLUS,		"+",		0 },
124 	{ KEYC_KP_FOUR,		"4",		0 },
125 	{ KEYC_KP_FIVE,		"5",		0 },
126 	{ KEYC_KP_SIX,		"6",		0 },
127 	{ KEYC_KP_ONE,		"1",		0 },
128 	{ KEYC_KP_TWO,		"2",		0 },
129 	{ KEYC_KP_THREE,	"3",		0 },
130 	{ KEYC_KP_ENTER,	"\n",		0 },
131 	{ KEYC_KP_ZERO,		"0",		0 },
132 	{ KEYC_KP_PERIOD,	".",		0 },
133 };
134 
135 /* Translate a key code into an output key sequence. */
136 void
input_key(struct window_pane * wp,int key)137 input_key(struct window_pane *wp, int key)
138 {
139 	const struct input_key_ent     *ike;
140 	u_int				i;
141 	size_t				dlen;
142 	char			       *out;
143 	u_char				ch;
144 
145 	log_debug2("writing key 0x%x", key);
146 
147 	/*
148 	 * If this is a normal 7-bit key, just send it, with a leading escape
149 	 * if necessary.
150 	 */
151 	if (key != KEYC_NONE && (key & ~KEYC_ESCAPE) < 0x100) {
152 		if (key & KEYC_ESCAPE)
153 			bufferevent_write(wp->event, "\033", 1);
154 		ch = key & ~KEYC_ESCAPE;
155 		bufferevent_write(wp->event, &ch, 1);
156 		return;
157 	}
158 
159 	/*
160 	 * Then try to look this up as an xterm key, if the flag to output them
161 	 * is set.
162 	 */
163 	if (options_get_number(&wp->window->options, "xterm-keys")) {
164 		if ((out = xterm_keys_lookup(key)) != NULL) {
165 			bufferevent_write(wp->event, out, strlen(out));
166 			free(out);
167 			return;
168 		}
169 	}
170 
171 	/* Otherwise look the key up in the table. */
172 	for (i = 0; i < nitems(input_keys); i++) {
173 		ike = &input_keys[i];
174 
175 		if ((ike->flags & INPUTKEY_KEYPAD) &&
176 		    !(wp->screen->mode & MODE_KKEYPAD))
177 			continue;
178 		if ((ike->flags & INPUTKEY_CURSOR) &&
179 		    !(wp->screen->mode & MODE_KCURSOR))
180 			continue;
181 
182 		if ((key & KEYC_ESCAPE) && (ike->key | KEYC_ESCAPE) == key)
183 			break;
184 		if (ike->key == key)
185 			break;
186 	}
187 	if (i == nitems(input_keys)) {
188 		log_debug2("key 0x%x missing", key);
189 		return;
190 	}
191 	dlen = strlen(ike->data);
192 	log_debug2("found key 0x%x: \"%s\"", key, ike->data);
193 
194 	/* Prefix a \033 for escape. */
195 	if (key & KEYC_ESCAPE)
196 		bufferevent_write(wp->event, "\033", 1);
197 	bufferevent_write(wp->event, ike->data, dlen);
198 }
199 
200 /* Translate mouse and output. */
201 void
input_mouse(struct window_pane * wp,struct session * s,struct mouse_event * m)202 input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m)
203 {
204 	char			 buf[40];
205 	size_t			 len;
206 	struct paste_buffer	*pb;
207 
208 	if (wp->screen->mode & ALL_MOUSE_MODES) {
209 		/*
210 		 * Use the SGR (1006) extension only if the application
211 		 * requested it and the underlying terminal also sent the event
212 		 * in this format (this is because an old style mouse release
213 		 * event cannot be converted into the new SGR format, since the
214 		 * released button is unknown). Otherwise pretend that tmux
215 		 * doesn't speak this extension, and fall back to the UTF-8
216 		 * (1005) extension if the application requested, or to the
217 		 * legacy format.
218 		 */
219 		if (m->sgr && (wp->screen->mode & MODE_MOUSE_SGR)) {
220 			len = xsnprintf(buf, sizeof buf, "\033[<%d;%d;%d%c",
221 			    m->sgr_xb, m->x + 1, m->y + 1,
222 			    m->sgr_rel ? 'm' : 'M');
223 		} else if (wp->screen->mode & MODE_MOUSE_UTF8) {
224 			len = xsnprintf(buf, sizeof buf, "\033[M");
225 			len += utf8_split2(m->xb + 32, (u_char *)&buf[len]);
226 			len += utf8_split2(m->x + 33, (u_char *)&buf[len]);
227 			len += utf8_split2(m->y + 33, (u_char *)&buf[len]);
228 		} else {
229 			if (m->xb > 223)
230 				return;
231 			len = xsnprintf(buf, sizeof buf, "\033[M");
232 			buf[len++] = m->xb + 32;
233 			buf[len++] = m->x + 33;
234 			buf[len++] = m->y + 33;
235 		}
236 		bufferevent_write(wp->event, buf, len);
237 		return;
238 	}
239 
240 	if (m->button == 1 && (m->event & MOUSE_EVENT_CLICK) &&
241 	    options_get_number(&wp->window->options, "mode-mouse") == 1) {
242 		pb = paste_get_top(&global_buffers);
243 		if (pb != NULL) {
244 			paste_send_pane(pb, wp, "\r",
245 			    wp->screen->mode & MODE_BRACKETPASTE);
246 		}
247 	} else if ((m->xb & 3) != 1 &&
248 	    options_get_number(&wp->window->options, "mode-mouse") == 1) {
249 		if (window_pane_set_mode(wp, &window_copy_mode) == 0) {
250 			window_copy_init_from_pane(wp);
251 			if (wp->mode->mouse != NULL)
252 				wp->mode->mouse(wp, s, m);
253 		}
254 	}
255 }
256