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