Annotation of src/usr.bin/tmux/input-keys.c, Revision 1.73
1.73 ! nicm 1: /* $OpenBSD: input-keys.c,v 1.72 2020/05/16 16:30:59 nicm Exp $ */
1.1 nicm 2:
3: /*
1.53 nicm 4: * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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:
1.8 nicm 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:
1.56 nicm 34: static void input_key_mouse(struct window_pane *, struct mouse_event *);
1.42 nicm 35:
1.72 nicm 36: /* Entry in the key tree. */
37: struct input_key_entry {
38: key_code key;
39: const char *data;
40:
41: RB_ENTRY(input_key_entry) entry;
1.1 nicm 42: };
1.72 nicm 43: RB_HEAD(input_key_tree, input_key_entry);
44:
45: /* Tree of input keys. */
46: static int input_key_cmp(struct input_key_entry *,
47: struct input_key_entry *);
48: RB_GENERATE_STATIC(input_key_tree, input_key_entry, entry, input_key_cmp);
49: struct input_key_tree input_key_tree = RB_INITIALIZER(&input_key_tree);
1.1 nicm 50:
1.72 nicm 51: /* List of default keys, the tree is built from this. */
52: static struct input_key_entry input_key_defaults[] = {
1.61 nicm 53: /* Paste keys. */
1.72 nicm 54: { .key = KEYC_PASTE_START,
55: .data = "\033[200~"
56: },
57: { .key = KEYC_PASTE_END,
58: .data = "\033[201~"
59: },
1.3 nicm 60:
1.1 nicm 61: /* Function keys. */
1.72 nicm 62: { .key = KEYC_F1,
63: .data = "\033OP"
64: },
65: { .key = KEYC_F2,
66: .data = "\033OQ"
67: },
68: { .key = KEYC_F3,
69: .data = "\033OR"
70: },
71: { .key = KEYC_F4,
72: .data = "\033OS"
73: },
74: { .key = KEYC_F5,
75: .data = "\033[15~"
76: },
77: { .key = KEYC_F6,
78: .data = "\033[17~"
79: },
80: { .key = KEYC_F7,
81: .data = "\033[18~"
82: },
83: { .key = KEYC_F8,
84: .data = "\033[19~"
85: },
86: { .key = KEYC_F9,
87: .data = "\033[20~"
88: },
89: { .key = KEYC_F10,
90: .data = "\033[21~"
91: },
92: { .key = KEYC_F11,
93: .data = "\033[23~"
94: },
95: { .key = KEYC_F12,
96: .data = "\033[24~"
97: },
98: { .key = KEYC_F1|KEYC_SHIFT,
99: .data = "\033[25~"
100: },
101: { .key = KEYC_F2|KEYC_SHIFT,
102: .data = "\033[26~"
103: },
104: { .key = KEYC_F3|KEYC_SHIFT,
105: .data = "\033[28~"
106: },
107: { .key = KEYC_F4|KEYC_SHIFT,
108: .data = "\033[29~"
109: },
110: { .key = KEYC_F5|KEYC_SHIFT,
111: .data = "\033[31~"
112: },
113: { .key = KEYC_F6|KEYC_SHIFT,
114: .data = "\033[32~"
115: },
116: { .key = KEYC_F7|KEYC_SHIFT,
117: .data = "\033[33~"
118: },
119: { .key = KEYC_F8|KEYC_SHIFT,
120: .data = "\033[34~"
121: },
122: { .key = KEYC_IC,
123: .data = "\033[2~"
124: },
125: { .key = KEYC_DC,
126: .data = "\033[3~"
127: },
128: { .key = KEYC_HOME,
129: .data = "\033[1~"
130: },
131: { .key = KEYC_END,
132: .data = "\033[4~"
133: },
134: { .key = KEYC_NPAGE,
135: .data = "\033[6~"
136: },
137: { .key = KEYC_PPAGE,
138: .data = "\033[5~"
139: },
140: { .key = KEYC_BTAB,
141: .data = "\033[Z"
142: },
143:
144: /* Arrow keys. */
145: { .key = KEYC_UP|KEYC_CURSOR,
146: .data = "\033OA"
147: },
148: { .key = KEYC_DOWN|KEYC_CURSOR,
149: .data = "\033OB"
150: },
151: { .key = KEYC_RIGHT|KEYC_CURSOR,
152: .data = "\033OC"
153: },
154: { .key = KEYC_LEFT|KEYC_CURSOR,
155: .data = "\033OD"
156: },
157: { .key = KEYC_UP,
158: .data = "\033[A"
159: },
160: { .key = KEYC_DOWN,
161: .data = "\033[B"
162: },
163: { .key = KEYC_RIGHT,
164: .data = "\033[C"
165: },
166: { .key = KEYC_LEFT,
167: .data = "\033[D"
168: },
169:
170: /* Keypad keys. */
171: { .key = KEYC_KP_SLASH|KEYC_KEYPAD,
172: .data = "\033Oo"
173: },
174: { .key = KEYC_KP_STAR|KEYC_KEYPAD,
175: .data = "\033Oj"
176: },
177: { .key = KEYC_KP_MINUS|KEYC_KEYPAD,
178: .data = "\033Om"
179: },
180: { .key = KEYC_KP_SEVEN|KEYC_KEYPAD,
181: .data = "\033Ow"
182: },
183: { .key = KEYC_KP_EIGHT|KEYC_KEYPAD,
184: .data = "\033Ox"
185: },
186: { .key = KEYC_KP_NINE|KEYC_KEYPAD,
187: .data = "\033Oy"
188: },
189: { .key = KEYC_KP_PLUS|KEYC_KEYPAD,
190: .data = "\033Ok"
191: },
192: { .key = KEYC_KP_FOUR|KEYC_KEYPAD,
193: .data = "\033Ot"
194: },
195: { .key = KEYC_KP_FIVE|KEYC_KEYPAD,
196: .data = "\033Ou"
197: },
198: { .key = KEYC_KP_SIX|KEYC_KEYPAD,
199: .data = "\033Ov"
200: },
201: { .key = KEYC_KP_ONE|KEYC_KEYPAD,
202: .data = "\033Oq"
203: },
204: { .key = KEYC_KP_TWO|KEYC_KEYPAD,
205: .data = "\033Or"
206: },
207: { .key = KEYC_KP_THREE|KEYC_KEYPAD,
208: .data = "\033Os"
209: },
210: { .key = KEYC_KP_ENTER|KEYC_KEYPAD,
211: .data = "\033OM"
212: },
213: { .key = KEYC_KP_ZERO|KEYC_KEYPAD,
214: .data = "\033Op"
215: },
216: { .key = KEYC_KP_PERIOD|KEYC_KEYPAD,
217: .data = "\033On"
218: },
219: { .key = KEYC_KP_SLASH,
220: .data = "/"
221: },
222: { .key = KEYC_KP_STAR,
223: .data = "*"
224: },
225: { .key = KEYC_KP_MINUS,
226: .data = "-"
227: },
228: { .key = KEYC_KP_SEVEN,
229: .data = "7"
230: },
231: { .key = KEYC_KP_EIGHT,
232: .data = "8"
233: },
234: { .key = KEYC_KP_NINE,
235: .data = "9"
236: },
237: { .key = KEYC_KP_PLUS,
238: .data = "+"
239: },
240: { .key = KEYC_KP_FOUR,
241: .data = "4"
242: },
243: { .key = KEYC_KP_FIVE,
244: .data = "5"
245: },
246: { .key = KEYC_KP_SIX,
247: .data = "6"
248: },
249: { .key = KEYC_KP_ONE,
250: .data = "1"
251: },
252: { .key = KEYC_KP_TWO,
253: .data = "2"
254: },
255: { .key = KEYC_KP_THREE,
256: .data = "3"
257: },
258: { .key = KEYC_KP_ENTER,
259: .data = "\n"
260: },
261: { .key = KEYC_KP_ZERO,
262: .data = "0"
263: },
264: { .key = KEYC_KP_PERIOD,
265: .data = "."
266: },
267:
268: /* Keys with an embedded modifier. */
269: { .key = KEYC_F1|KEYC_XTERM,
270: .data = "\033[1;_P"
271: },
272: { .key = KEYC_F2|KEYC_XTERM,
273: .data = "\033[1;_Q"
274: },
275: { .key = KEYC_F3|KEYC_XTERM,
276: .data = "\033[1;_R"
277: },
278: { .key = KEYC_F4|KEYC_XTERM,
279: .data = "\033[1;_S"
280: },
281: { .key = KEYC_F5|KEYC_XTERM,
282: .data = "\033[15;_~"
283: },
284: { .key = KEYC_F6|KEYC_XTERM,
285: .data = "\033[17;_~"
286: },
287: { .key = KEYC_F7|KEYC_XTERM,
288: .data = "\033[18;_~"
289: },
290: { .key = KEYC_F8|KEYC_XTERM,
291: .data = "\033[19;_~"
292: },
293: { .key = KEYC_F9|KEYC_XTERM,
294: .data = "\033[20;_~"
295: },
296: { .key = KEYC_F10|KEYC_XTERM,
297: .data = "\033[21;_~"
298: },
299: { .key = KEYC_F11|KEYC_XTERM,
300: .data = "\033[23;_~"
301: },
302: { .key = KEYC_F12|KEYC_XTERM,
303: .data = "\033[24;_~"
304: },
305: { .key = KEYC_UP|KEYC_XTERM,
306: .data = "\033[1;_A"
307: },
308: { .key = KEYC_DOWN|KEYC_XTERM,
309: .data = "\033[1;_B"
310: },
311: { .key = KEYC_RIGHT|KEYC_XTERM,
312: .data = "\033[1;_C"
313: },
314: { .key = KEYC_LEFT|KEYC_XTERM,
315: .data = "\033[1;_D"
316: },
317: { .key = KEYC_HOME|KEYC_XTERM,
318: .data = "\033[1;_H"
319: },
320: { .key = KEYC_END|KEYC_XTERM,
321: .data = "\033[1;_F"
322: },
323: { .key = KEYC_PPAGE|KEYC_XTERM,
324: .data = "\033[5;_~"
325: },
326: { .key = KEYC_NPAGE|KEYC_XTERM,
327: .data = "\033[6;_~"
328: },
329: { .key = KEYC_IC|KEYC_XTERM,
330: .data = "\033[2;_~"
331: },
332: { .key = KEYC_DC|KEYC_XTERM,
333: .data = "\033[3;_~" }
334: };
335: static const key_code input_key_modifiers[] = {
336: 0,
337: 0,
338: KEYC_SHIFT|KEYC_XTERM,
1.73 ! nicm 339: KEYC_META|KEYC_XTERM,
! 340: KEYC_SHIFT|KEYC_META|KEYC_XTERM,
1.72 nicm 341: KEYC_CTRL|KEYC_XTERM,
342: KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM,
1.73 ! nicm 343: KEYC_META|KEYC_CTRL|KEYC_XTERM,
! 344: KEYC_SHIFT|KEYC_META|KEYC_CTRL|KEYC_XTERM
1.72 nicm 345: };
1.7 nicm 346:
1.72 nicm 347: /* Input key comparison function. */
348: static int
349: input_key_cmp(struct input_key_entry *ike1, struct input_key_entry *ike2)
350: {
351: if (ike1->key < ike2->key)
352: return (-1);
353: if (ike1->key > ike2->key)
354: return (1);
355: return (0);
356: }
1.1 nicm 357:
1.54 nicm 358: /* Split a character into two UTF-8 bytes. */
359: static size_t
1.72 nicm 360: input_key_split2(u_int c, u_char *dst)
1.54 nicm 361: {
362: if (c > 0x7f) {
363: dst[0] = (c >> 6) | 0xc0;
364: dst[1] = (c & 0x3f) | 0x80;
365: return (2);
366: }
367: dst[0] = c;
368: return (1);
369: }
370:
1.72 nicm 371: /* Build input key tree. */
372: void
373: input_key_build(void)
374: {
375: struct input_key_entry *ike, *new;
376: u_int i, j;
377: char *data;
378:
379: for (i = 0; i < nitems(input_key_defaults); i++) {
380: ike = &input_key_defaults[i];
381: if (~ike->key & KEYC_XTERM) {
382: RB_INSERT(input_key_tree, &input_key_tree, ike);
383: continue;
384: }
385:
386: for (j = 2; j < nitems(input_key_modifiers); j++) {
387: data = xstrdup(ike->data);
388: data[strcspn(data, "_")] = '0' + j;
389:
390: new = xcalloc(1, sizeof *new);
391: new->key = ike->key|input_key_modifiers[j];
392: new->data = data;
393: RB_INSERT(input_key_tree, &input_key_tree, new);
394: }
395: }
396:
397: RB_FOREACH(ike, input_key_tree, &input_key_tree) {
398: log_debug("%s: 0x%llx (%s) is %s", __func__, ike->key,
399: key_string_lookup_key(ike->key), ike->data);
400: }
401: }
402:
1.69 nicm 403: /* Translate a key code into an output key sequence for a pane. */
404: int
405: input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m)
406: {
1.72 nicm 407: if (log_get_level() != 0) {
408: log_debug("writing key 0x%llx (%s) to %%%u", key,
409: key_string_lookup_key(key), wp->id);
410: }
1.69 nicm 411:
412: if (KEYC_IS_MOUSE(key)) {
413: if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id)
414: input_key_mouse(wp, m);
415: return (0);
416: }
1.72 nicm 417: return (input_key(wp->screen, wp->event, key));
1.69 nicm 418: }
419:
1.8 nicm 420: /* Translate a key code into an output key sequence. */
1.67 nicm 421: int
1.72 nicm 422: input_key(struct screen *s, struct bufferevent *bev, key_code key)
1.1 nicm 423: {
1.72 nicm 424: struct input_key_entry *ike, entry;
425: size_t datalen;
426: key_code justkey, newkey;
427: struct utf8_data ud;
1.1 nicm 428:
1.69 nicm 429: /* Mouse keys need a pane. */
430: if (KEYC_IS_MOUSE(key))
1.67 nicm 431: return (0);
1.64 nicm 432:
433: /* Literal keys go as themselves (can't be more than eight bits). */
434: if (key & KEYC_LITERAL) {
435: ud.data[0] = (u_char)key;
1.69 nicm 436: bufferevent_write(bev, &ud.data[0], 1);
1.67 nicm 437: return (0);
1.65 nicm 438: }
439:
440: /* Is this backspace? */
441: if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
1.66 nicm 442: newkey = options_get_number(global_options, "backspace");
443: if (newkey >= 0x7f)
444: newkey = '\177';
445: key = newkey|(key & KEYC_MASK_MOD);
1.42 nicm 446: }
1.1 nicm 447:
1.8 nicm 448: /*
449: * If this is a normal 7-bit key, just send it, with a leading escape
1.46 nicm 450: * if necessary. If it is a UTF-8 key, split it and send it.
1.8 nicm 451: */
1.73 ! nicm 452: justkey = (key & ~(KEYC_XTERM|KEYC_META));
1.52 nicm 453: if (justkey <= 0x7f) {
1.73 ! nicm 454: if (key & KEYC_META)
1.69 nicm 455: bufferevent_write(bev, "\033", 1);
1.47 nicm 456: ud.data[0] = justkey;
1.69 nicm 457: bufferevent_write(bev, &ud.data[0], 1);
1.67 nicm 458: return (0);
1.46 nicm 459: }
1.52 nicm 460: if (justkey > 0x7f && justkey < KEYC_BASE) {
1.48 nicm 461: if (utf8_split(justkey, &ud) != UTF8_DONE)
1.67 nicm 462: return (-1);
1.73 ! nicm 463: if (key & KEYC_META)
1.69 nicm 464: bufferevent_write(bev, "\033", 1);
465: bufferevent_write(bev, ud.data, ud.size);
1.67 nicm 466: return (0);
1.12 nicm 467: }
468:
1.18 nicm 469: /*
1.72 nicm 470: * Look up in the tree. If not in application keypad or cursor mode,
471: * remove the flags from the key.
1.12 nicm 472: */
1.72 nicm 473: if (~s->mode & MODE_KKEYPAD)
474: key &= ~KEYC_KEYPAD;
475: if (~s->mode & MODE_KCURSOR)
476: key &= ~KEYC_CURSOR;
477: entry.key = key;
478: if ((ike = RB_FIND(input_key_tree, &input_key_tree, &entry)) == NULL) {
1.46 nicm 479: log_debug("key 0x%llx missing", key);
1.67 nicm 480: return (-1);
1.1 nicm 481: }
1.72 nicm 482: datalen = strlen(ike->data);
1.46 nicm 483: log_debug("found key 0x%llx: \"%s\"", key, ike->data);
1.1 nicm 484:
1.9 nicm 485: /* Prefix a \033 for escape. */
1.73 ! nicm 486: if (key & KEYC_META)
1.69 nicm 487: bufferevent_write(bev, "\033", 1);
1.72 nicm 488: bufferevent_write(bev, ike->data, datalen);
1.67 nicm 489: return (0);
1.1 nicm 490: }
491:
1.70 nicm 492: /* Get mouse event string. */
493: int
494: input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y,
495: const char **rbuf, size_t *rlen)
1.1 nicm 496: {
1.70 nicm 497: static char buf[40];
1.59 nicm 498: size_t len;
1.42 nicm 499:
1.70 nicm 500: *rbuf = NULL;
501: *rlen = 0;
1.24 nicm 502:
1.59 nicm 503: /* If this pane is not in button or all mode, discard motion events. */
1.68 nicm 504: if (MOUSE_DRAG(m->b) && (s->mode & MOTION_MOUSE_MODES) == 0)
1.71 nicm 505: return (0);
506: if ((s->mode & ALL_MOUSE_MODES) == 0)
1.70 nicm 507: return (0);
1.59 nicm 508:
509: /*
510: * If this event is a release event and not in all mode, discard it.
511: * In SGR mode we can tell absolutely because a release is normally
512: * shown by the last character. Without SGR, we check if the last
513: * buttons was also a release.
514: */
515: if (m->sgr_type != ' ') {
516: if (MOUSE_DRAG(m->sgr_b) &&
517: MOUSE_BUTTONS(m->sgr_b) == 3 &&
1.68 nicm 518: (~s->mode & MODE_MOUSE_ALL))
1.70 nicm 519: return (0);
1.59 nicm 520: } else {
521: if (MOUSE_DRAG(m->b) &&
522: MOUSE_BUTTONS(m->b) == 3 &&
523: MOUSE_BUTTONS(m->lb) == 3 &&
1.68 nicm 524: (~s->mode & MODE_MOUSE_ALL))
1.70 nicm 525: return (0);
1.59 nicm 526: }
1.42 nicm 527:
528: /*
529: * Use the SGR (1006) extension only if the application requested it
530: * and the underlying terminal also sent the event in this format (this
531: * is because an old style mouse release event cannot be converted into
532: * the new SGR format, since the released button is unknown). Otherwise
533: * pretend that tmux doesn't speak this extension, and fall back to the
1.51 nicm 534: * UTF-8 (1005) extension if the application requested, or to the
1.42 nicm 535: * legacy format.
536: */
1.59 nicm 537: if (m->sgr_type != ' ' && (s->mode & MODE_MOUSE_SGR)) {
1.42 nicm 538: len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c",
539: m->sgr_b, x + 1, y + 1, m->sgr_type);
1.59 nicm 540: } else if (s->mode & MODE_MOUSE_UTF8) {
1.55 nicm 541: if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33)
1.70 nicm 542: return (0);
1.51 nicm 543: len = xsnprintf(buf, sizeof buf, "\033[M");
1.72 nicm 544: len += input_key_split2(m->b + 32, &buf[len]);
545: len += input_key_split2(x + 33, &buf[len]);
546: len += input_key_split2(y + 33, &buf[len]);
1.42 nicm 547: } else {
548: if (m->b > 223)
1.70 nicm 549: return (0);
1.42 nicm 550: len = xsnprintf(buf, sizeof buf, "\033[M");
551: buf[len++] = m->b + 32;
552: buf[len++] = x + 33;
553: buf[len++] = y + 33;
1.1 nicm 554: }
1.70 nicm 555:
556: *rbuf = buf;
557: *rlen = len;
558: return (1);
559: }
560:
561: /* Translate mouse and output. */
562: static void
563: input_key_mouse(struct window_pane *wp, struct mouse_event *m)
564: {
565: struct screen *s = wp->screen;
566: u_int x, y;
567: const char *buf;
568: size_t len;
569:
570: /* Ignore events if no mouse mode or the pane is not visible. */
571: if (m->ignore || (s->mode & ALL_MOUSE_MODES) == 0)
572: return;
573: if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
574: return;
575: if (!window_pane_visible(wp))
576: return;
577: if (!input_key_get_mouse(s, m, x, y, &buf, &len))
578: return;
1.44 nicm 579: log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id);
1.42 nicm 580: bufferevent_write(wp->event, buf, len);
1.1 nicm 581: }