xref: /openbsd-src/usr.bin/tmux/tty-keys.c (revision 2f106d387997d65d1eab0db72078a13e5d51f6cf)
1*2f106d38Snicm /* $OpenBSD: tty-keys.c,v 1.185 2025/01/02 10:34:45 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm #include <sys/time.h>
21311827fbSnicm 
2290866723Snicm #include <netinet/in.h>
2390866723Snicm 
24f1c69fe1Snicm #include <ctype.h>
255e5e6809Snicm #include <limits.h>
2690866723Snicm #include <resolv.h>
275e5e6809Snicm #include <stdlib.h>
28311827fbSnicm #include <string.h>
294cf01325Snicm #include <termios.h>
304cf01325Snicm #include <unistd.h>
31311827fbSnicm 
32311827fbSnicm #include "tmux.h"
33311827fbSnicm 
34cc6ff773Snicm /*
359f8fac72Snicm  * Handle keys input from the outside terminal. tty_default_*_keys[] are a base
369f8fac72Snicm  * table of supported keys which are looked up in terminfo(5) and translated
379f8fac72Snicm  * into a ternary tree.
38cc6ff773Snicm  */
39cc6ff773Snicm 
404a29328cSnicm static void	tty_keys_add1(struct tty_key **, const char *, key_code);
414a29328cSnicm static void	tty_keys_add(struct tty *, const char *, key_code);
424a29328cSnicm static void	tty_keys_free1(struct tty_key *);
434a29328cSnicm static struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t,
44885a4698Snicm 		    size_t *);
454a29328cSnicm static struct tty_key *tty_keys_find(struct tty *, const char *, size_t,
464a29328cSnicm 		    size_t *);
474a29328cSnicm static int	tty_keys_next1(struct tty *, const char *, size_t, key_code *,
48fa0ecfe4Snicm 		    size_t *, int);
494a29328cSnicm static void	tty_keys_callback(int, short, void *);
50f1c69fe1Snicm static int	tty_keys_extended_key(struct tty *, const char *, size_t,
51f1c69fe1Snicm 		    size_t *, key_code *);
52a88e9db6Snicm static int	tty_keys_mouse(struct tty *, const char *, size_t, size_t *,
53a88e9db6Snicm 		    struct mouse_event *);
5490866723Snicm static int	tty_keys_clipboard(struct tty *, const char *, size_t,
5590866723Snicm 		    size_t *);
56f00ac6e6Snicm static int	tty_keys_device_attributes(struct tty *, const char *, size_t,
57f00ac6e6Snicm 		    size_t *);
58e0697ebcSnicm static int	tty_keys_device_attributes2(struct tty *, const char *, size_t,
59e0697ebcSnicm 		    size_t *);
60ecd9db25Snicm static int	tty_keys_extended_device_attributes(struct tty *, const char *,
616110b15cSnicm 		    size_t, size_t *);
62311827fbSnicm 
63109d1157Snicm /* A key tree entry. */
64109d1157Snicm struct tty_key {
65109d1157Snicm 	char		 ch;
66109d1157Snicm 	key_code	 key;
67109d1157Snicm 
68109d1157Snicm 	struct tty_key	*left;
69109d1157Snicm 	struct tty_key	*right;
70109d1157Snicm 
71109d1157Snicm 	struct tty_key	*next;
72109d1157Snicm };
73109d1157Snicm 
74f48c717dSnicm /* Default raw keys. */
75f48c717dSnicm struct tty_default_key_raw {
76311827fbSnicm 	const char	       *string;
77885a4698Snicm 	key_code		key;
78311827fbSnicm };
799883b791Snicm static const struct tty_default_key_raw tty_default_raw_keys[] = {
8016171a6cSnicm 	/* Application escape. */
8116171a6cSnicm 	{ "\033O[", '\033' },
8216171a6cSnicm 
83311827fbSnicm 	/*
844ffe3d28Snicm 	 * Numeric keypad. Just use the vt100 escape sequences here and always
854ffe3d28Snicm 	 * put the terminal into keypad_xmit mode. Translation of numbers
864ffe3d28Snicm 	 * mode/applications mode is done in input-keys.c.
87311827fbSnicm 	 */
889265d1acSnicm 	{ "\033Oo", KEYC_KP_SLASH|KEYC_KEYPAD },
899265d1acSnicm 	{ "\033Oj", KEYC_KP_STAR|KEYC_KEYPAD },
909265d1acSnicm 	{ "\033Om", KEYC_KP_MINUS|KEYC_KEYPAD },
919265d1acSnicm 	{ "\033Ow", KEYC_KP_SEVEN|KEYC_KEYPAD },
929265d1acSnicm 	{ "\033Ox", KEYC_KP_EIGHT|KEYC_KEYPAD },
939265d1acSnicm 	{ "\033Oy", KEYC_KP_NINE|KEYC_KEYPAD },
949265d1acSnicm 	{ "\033Ok", KEYC_KP_PLUS|KEYC_KEYPAD },
959265d1acSnicm 	{ "\033Ot", KEYC_KP_FOUR|KEYC_KEYPAD },
969265d1acSnicm 	{ "\033Ou", KEYC_KP_FIVE|KEYC_KEYPAD },
979265d1acSnicm 	{ "\033Ov", KEYC_KP_SIX|KEYC_KEYPAD },
989265d1acSnicm 	{ "\033Oq", KEYC_KP_ONE|KEYC_KEYPAD },
999265d1acSnicm 	{ "\033Or", KEYC_KP_TWO|KEYC_KEYPAD },
1009265d1acSnicm 	{ "\033Os", KEYC_KP_THREE|KEYC_KEYPAD },
1019265d1acSnicm 	{ "\033OM", KEYC_KP_ENTER|KEYC_KEYPAD },
1029265d1acSnicm 	{ "\033Op", KEYC_KP_ZERO|KEYC_KEYPAD },
1039265d1acSnicm 	{ "\033On", KEYC_KP_PERIOD|KEYC_KEYPAD },
10444827713Snicm 
1053904313cSnicm 	/* Arrow keys. */
1069265d1acSnicm 	{ "\033OA", KEYC_UP|KEYC_CURSOR },
1079265d1acSnicm 	{ "\033OB", KEYC_DOWN|KEYC_CURSOR },
1089265d1acSnicm 	{ "\033OC", KEYC_RIGHT|KEYC_CURSOR },
1099265d1acSnicm 	{ "\033OD", KEYC_LEFT|KEYC_CURSOR },
1103904313cSnicm 
1119265d1acSnicm 	{ "\033[A", KEYC_UP|KEYC_CURSOR },
1129265d1acSnicm 	{ "\033[B", KEYC_DOWN|KEYC_CURSOR },
1139265d1acSnicm 	{ "\033[C", KEYC_RIGHT|KEYC_CURSOR },
1149265d1acSnicm 	{ "\033[D", KEYC_LEFT|KEYC_CURSOR },
11583980076Snicm 
11630233ff7Snicm 	/*
11730233ff7Snicm 	 * Meta arrow keys. These do not get the IMPLIED_META flag so they
11830233ff7Snicm 	 * don't match the xterm-style meta keys in the output tree - Escape+Up
11930233ff7Snicm 	 * should stay as Escape+Up and not become M-Up.
12030233ff7Snicm 	 */
12130233ff7Snicm 	{ "\033\033OA", KEYC_UP|KEYC_CURSOR|KEYC_META },
12230233ff7Snicm 	{ "\033\033OB", KEYC_DOWN|KEYC_CURSOR|KEYC_META },
12330233ff7Snicm 	{ "\033\033OC", KEYC_RIGHT|KEYC_CURSOR|KEYC_META },
12430233ff7Snicm 	{ "\033\033OD", KEYC_LEFT|KEYC_CURSOR|KEYC_META },
12530233ff7Snicm 
12630233ff7Snicm 	{ "\033\033[A", KEYC_UP|KEYC_CURSOR|KEYC_META },
12730233ff7Snicm 	{ "\033\033[B", KEYC_DOWN|KEYC_CURSOR|KEYC_META },
12830233ff7Snicm 	{ "\033\033[C", KEYC_RIGHT|KEYC_CURSOR|KEYC_META },
12930233ff7Snicm 	{ "\033\033[D", KEYC_LEFT|KEYC_CURSOR|KEYC_META },
1307dbc9071Snicm 
131c620fc9fSnicm 	/* Other xterm keys. */
13299ad9c15Snicm 	{ "\033OH", KEYC_HOME },
13399ad9c15Snicm 	{ "\033OF", KEYC_END },
13499ad9c15Snicm 
1357dbc9071Snicm 	{ "\033\033OH", KEYC_HOME|KEYC_META|KEYC_IMPLIED_META },
1367dbc9071Snicm 	{ "\033\033OF", KEYC_END|KEYC_META|KEYC_IMPLIED_META },
1377dbc9071Snicm 
13899ad9c15Snicm 	{ "\033[H", KEYC_HOME },
13999ad9c15Snicm 	{ "\033[F", KEYC_END },
14099ad9c15Snicm 
1417dbc9071Snicm 	{ "\033\033[H", KEYC_HOME|KEYC_META|KEYC_IMPLIED_META },
1427dbc9071Snicm 	{ "\033\033[F", KEYC_END|KEYC_META|KEYC_IMPLIED_META },
1437dbc9071Snicm 
144c620fc9fSnicm 	/* rxvt arrow keys. */
145f48c717dSnicm 	{ "\033Oa", KEYC_UP|KEYC_CTRL },
146f48c717dSnicm 	{ "\033Ob", KEYC_DOWN|KEYC_CTRL },
147f48c717dSnicm 	{ "\033Oc", KEYC_RIGHT|KEYC_CTRL },
148f48c717dSnicm 	{ "\033Od", KEYC_LEFT|KEYC_CTRL },
14983980076Snicm 
150f48c717dSnicm 	{ "\033[a", KEYC_UP|KEYC_SHIFT },
151f48c717dSnicm 	{ "\033[b", KEYC_DOWN|KEYC_SHIFT },
152f48c717dSnicm 	{ "\033[c", KEYC_RIGHT|KEYC_SHIFT },
153f48c717dSnicm 	{ "\033[d", KEYC_LEFT|KEYC_SHIFT },
15483980076Snicm 
155c620fc9fSnicm 	/* rxvt function keys. */
156c620fc9fSnicm 	{ "\033[11~", KEYC_F1 },
157c620fc9fSnicm 	{ "\033[12~", KEYC_F2 },
158c620fc9fSnicm 	{ "\033[13~", KEYC_F3 },
159c620fc9fSnicm 	{ "\033[14~", KEYC_F4 },
160c620fc9fSnicm 	{ "\033[15~", KEYC_F5 },
161c620fc9fSnicm 	{ "\033[17~", KEYC_F6 },
162c620fc9fSnicm 	{ "\033[18~", KEYC_F7 },
163c620fc9fSnicm 	{ "\033[19~", KEYC_F8 },
164c620fc9fSnicm 	{ "\033[20~", KEYC_F9 },
165c620fc9fSnicm 	{ "\033[21~", KEYC_F10 },
166c620fc9fSnicm 
167c620fc9fSnicm 	{ "\033[23~", KEYC_F1|KEYC_SHIFT },
168c620fc9fSnicm 	{ "\033[24~", KEYC_F2|KEYC_SHIFT },
169c620fc9fSnicm 	{ "\033[25~", KEYC_F3|KEYC_SHIFT },
170c620fc9fSnicm 	{ "\033[26~", KEYC_F4|KEYC_SHIFT },
171c620fc9fSnicm 	{ "\033[28~", KEYC_F5|KEYC_SHIFT },
172c620fc9fSnicm 	{ "\033[29~", KEYC_F6|KEYC_SHIFT },
173c620fc9fSnicm 	{ "\033[31~", KEYC_F7|KEYC_SHIFT },
174c620fc9fSnicm 	{ "\033[32~", KEYC_F8|KEYC_SHIFT },
175c620fc9fSnicm 	{ "\033[33~", KEYC_F9|KEYC_SHIFT },
176c620fc9fSnicm 	{ "\033[34~", KEYC_F10|KEYC_SHIFT },
177c620fc9fSnicm 	{ "\033[23$", KEYC_F11|KEYC_SHIFT },
178c620fc9fSnicm 	{ "\033[24$", KEYC_F12|KEYC_SHIFT },
179c620fc9fSnicm 
180f48c717dSnicm 	{ "\033[11^", KEYC_F1|KEYC_CTRL },
181f48c717dSnicm 	{ "\033[12^", KEYC_F2|KEYC_CTRL },
182f48c717dSnicm 	{ "\033[13^", KEYC_F3|KEYC_CTRL },
183f48c717dSnicm 	{ "\033[14^", KEYC_F4|KEYC_CTRL },
184f48c717dSnicm 	{ "\033[15^", KEYC_F5|KEYC_CTRL },
185f48c717dSnicm 	{ "\033[17^", KEYC_F6|KEYC_CTRL },
186f48c717dSnicm 	{ "\033[18^", KEYC_F7|KEYC_CTRL },
187f48c717dSnicm 	{ "\033[19^", KEYC_F8|KEYC_CTRL },
188f48c717dSnicm 	{ "\033[20^", KEYC_F9|KEYC_CTRL },
189f48c717dSnicm 	{ "\033[21^", KEYC_F10|KEYC_CTRL },
190f48c717dSnicm 	{ "\033[23^", KEYC_F11|KEYC_CTRL },
191f48c717dSnicm 	{ "\033[24^", KEYC_F12|KEYC_CTRL },
19283980076Snicm 
193f48c717dSnicm 	{ "\033[11@", KEYC_F1|KEYC_CTRL|KEYC_SHIFT },
194f48c717dSnicm 	{ "\033[12@", KEYC_F2|KEYC_CTRL|KEYC_SHIFT },
195f48c717dSnicm 	{ "\033[13@", KEYC_F3|KEYC_CTRL|KEYC_SHIFT },
196f48c717dSnicm 	{ "\033[14@", KEYC_F4|KEYC_CTRL|KEYC_SHIFT },
197f48c717dSnicm 	{ "\033[15@", KEYC_F5|KEYC_CTRL|KEYC_SHIFT },
198f48c717dSnicm 	{ "\033[17@", KEYC_F6|KEYC_CTRL|KEYC_SHIFT },
199f48c717dSnicm 	{ "\033[18@", KEYC_F7|KEYC_CTRL|KEYC_SHIFT },
200f48c717dSnicm 	{ "\033[19@", KEYC_F8|KEYC_CTRL|KEYC_SHIFT },
201f48c717dSnicm 	{ "\033[20@", KEYC_F9|KEYC_CTRL|KEYC_SHIFT },
202f48c717dSnicm 	{ "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT },
203f48c717dSnicm 	{ "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT },
204f48c717dSnicm 	{ "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT },
2056ddcc08fSnicm 
2066ddcc08fSnicm 	/* Focus tracking. */
2076ddcc08fSnicm 	{ "\033[I", KEYC_FOCUS_IN },
2086ddcc08fSnicm 	{ "\033[O", KEYC_FOCUS_OUT },
209b99601f2Snicm 
210b99601f2Snicm 	/* Paste keys. */
211b99601f2Snicm 	{ "\033[200~", KEYC_PASTE_START },
212b99601f2Snicm 	{ "\033[201~", KEYC_PASTE_END },
2133c4ca0e6Snicm 
2143c4ca0e6Snicm 	/* Extended keys. */
2158f86830fSnicm 	{ "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT },
216311827fbSnicm };
217311827fbSnicm 
2189265d1acSnicm /* Default xterm keys. */
2199265d1acSnicm struct tty_default_key_xterm {
2209265d1acSnicm 	const char	*template;
2219265d1acSnicm 	key_code	 key;
2229265d1acSnicm };
2239265d1acSnicm static const struct tty_default_key_xterm tty_default_xterm_keys[] = {
2249265d1acSnicm 	{ "\033[1;_P", KEYC_F1 },
2259265d1acSnicm 	{ "\033O1;_P", KEYC_F1 },
2269265d1acSnicm 	{ "\033O_P", KEYC_F1 },
2279265d1acSnicm 	{ "\033[1;_Q", KEYC_F2 },
2289265d1acSnicm 	{ "\033O1;_Q", KEYC_F2 },
2299265d1acSnicm 	{ "\033O_Q", KEYC_F2 },
2309265d1acSnicm 	{ "\033[1;_R", KEYC_F3 },
2319265d1acSnicm 	{ "\033O1;_R", KEYC_F3 },
2329265d1acSnicm 	{ "\033O_R", KEYC_F3 },
2339265d1acSnicm 	{ "\033[1;_S", KEYC_F4 },
2349265d1acSnicm 	{ "\033O1;_S", KEYC_F4 },
2359265d1acSnicm 	{ "\033O_S", KEYC_F4 },
2369265d1acSnicm 	{ "\033[15;_~", KEYC_F5 },
2379265d1acSnicm 	{ "\033[17;_~", KEYC_F6 },
2389265d1acSnicm 	{ "\033[18;_~", KEYC_F7 },
2399265d1acSnicm 	{ "\033[19;_~", KEYC_F8 },
2409265d1acSnicm 	{ "\033[20;_~", KEYC_F9 },
2419265d1acSnicm 	{ "\033[21;_~", KEYC_F10 },
2429265d1acSnicm 	{ "\033[23;_~", KEYC_F11 },
2439265d1acSnicm 	{ "\033[24;_~", KEYC_F12 },
2449265d1acSnicm 	{ "\033[1;_A", KEYC_UP },
2459265d1acSnicm 	{ "\033[1;_B", KEYC_DOWN },
2469265d1acSnicm 	{ "\033[1;_C", KEYC_RIGHT },
2479265d1acSnicm 	{ "\033[1;_D", KEYC_LEFT },
2489265d1acSnicm 	{ "\033[1;_H", KEYC_HOME },
2499265d1acSnicm 	{ "\033[1;_F", KEYC_END },
2509265d1acSnicm 	{ "\033[5;_~", KEYC_PPAGE },
2519265d1acSnicm 	{ "\033[6;_~", KEYC_NPAGE },
2529265d1acSnicm 	{ "\033[2;_~", KEYC_IC },
2539265d1acSnicm 	{ "\033[3;_~", KEYC_DC },
2549265d1acSnicm };
2559265d1acSnicm static const key_code tty_default_xterm_modifiers[] = {
2569265d1acSnicm 	0,
2579265d1acSnicm 	0,
2585416581eSnicm 	KEYC_SHIFT,
2595416581eSnicm 	KEYC_META|KEYC_IMPLIED_META,
2605416581eSnicm 	KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META,
2615416581eSnicm 	KEYC_CTRL,
2625416581eSnicm 	KEYC_SHIFT|KEYC_CTRL,
2635416581eSnicm 	KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL,
2649e79a802Snicm 	KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL,
2659e79a802Snicm 	KEYC_META|KEYC_IMPLIED_META
2669265d1acSnicm };
2679265d1acSnicm 
268fd054becSnicm /*
2699265d1acSnicm  * Default terminfo(5) keys. Any keys that have builtin modifiers (that is,
2709265d1acSnicm  * where the key itself contains the modifiers) has the KEYC_XTERM flag set so
2719265d1acSnicm  * a leading escape is not treated as meta (and probably removed).
272fd054becSnicm  */
273f48c717dSnicm struct tty_default_key_code {
274f48c717dSnicm 	enum tty_code_code	code;
275885a4698Snicm 	key_code		key;
276f48c717dSnicm };
2779883b791Snicm static const struct tty_default_key_code tty_default_code_keys[] = {
278f48c717dSnicm 	/* Function keys. */
279f48c717dSnicm 	{ TTYC_KF1, KEYC_F1 },
280f48c717dSnicm 	{ TTYC_KF2, KEYC_F2 },
281f48c717dSnicm 	{ TTYC_KF3, KEYC_F3 },
282f48c717dSnicm 	{ TTYC_KF4, KEYC_F4 },
283f48c717dSnicm 	{ TTYC_KF5, KEYC_F5 },
284f48c717dSnicm 	{ TTYC_KF6, KEYC_F6 },
285f48c717dSnicm 	{ TTYC_KF7, KEYC_F7 },
286f48c717dSnicm 	{ TTYC_KF8, KEYC_F8 },
287f48c717dSnicm 	{ TTYC_KF9, KEYC_F9 },
288f48c717dSnicm 	{ TTYC_KF10, KEYC_F10 },
289f48c717dSnicm 	{ TTYC_KF11, KEYC_F11 },
290f48c717dSnicm 	{ TTYC_KF12, KEYC_F12 },
291dfe29c0aSnicm 
2925416581eSnicm 	{ TTYC_KF13, KEYC_F1|KEYC_SHIFT },
2935416581eSnicm 	{ TTYC_KF14, KEYC_F2|KEYC_SHIFT },
2945416581eSnicm 	{ TTYC_KF15, KEYC_F3|KEYC_SHIFT },
2955416581eSnicm 	{ TTYC_KF16, KEYC_F4|KEYC_SHIFT },
2965416581eSnicm 	{ TTYC_KF17, KEYC_F5|KEYC_SHIFT },
2975416581eSnicm 	{ TTYC_KF18, KEYC_F6|KEYC_SHIFT },
2985416581eSnicm 	{ TTYC_KF19, KEYC_F7|KEYC_SHIFT },
2995416581eSnicm 	{ TTYC_KF20, KEYC_F8|KEYC_SHIFT },
3005416581eSnicm 	{ TTYC_KF21, KEYC_F9|KEYC_SHIFT },
3015416581eSnicm 	{ TTYC_KF22, KEYC_F10|KEYC_SHIFT },
3025416581eSnicm 	{ TTYC_KF23, KEYC_F11|KEYC_SHIFT },
3035416581eSnicm 	{ TTYC_KF24, KEYC_F12|KEYC_SHIFT },
304dfe29c0aSnicm 
3055416581eSnicm 	{ TTYC_KF25, KEYC_F1|KEYC_CTRL },
3065416581eSnicm 	{ TTYC_KF26, KEYC_F2|KEYC_CTRL },
3075416581eSnicm 	{ TTYC_KF27, KEYC_F3|KEYC_CTRL },
3085416581eSnicm 	{ TTYC_KF28, KEYC_F4|KEYC_CTRL },
3095416581eSnicm 	{ TTYC_KF29, KEYC_F5|KEYC_CTRL },
3105416581eSnicm 	{ TTYC_KF30, KEYC_F6|KEYC_CTRL },
3115416581eSnicm 	{ TTYC_KF31, KEYC_F7|KEYC_CTRL },
3125416581eSnicm 	{ TTYC_KF32, KEYC_F8|KEYC_CTRL },
3135416581eSnicm 	{ TTYC_KF33, KEYC_F9|KEYC_CTRL },
3145416581eSnicm 	{ TTYC_KF34, KEYC_F10|KEYC_CTRL },
3155416581eSnicm 	{ TTYC_KF35, KEYC_F11|KEYC_CTRL },
3165416581eSnicm 	{ TTYC_KF36, KEYC_F12|KEYC_CTRL },
317dfe29c0aSnicm 
3185416581eSnicm 	{ TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL },
3195416581eSnicm 	{ TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL },
3205416581eSnicm 	{ TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL },
3215416581eSnicm 	{ TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL },
3225416581eSnicm 	{ TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL },
3235416581eSnicm 	{ TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL },
3245416581eSnicm 	{ TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL },
3255416581eSnicm 	{ TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL },
3265416581eSnicm 	{ TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL },
3275416581eSnicm 	{ TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL },
3285416581eSnicm 	{ TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL },
3295416581eSnicm 	{ TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL },
330dfe29c0aSnicm 
3315416581eSnicm 	{ TTYC_KF49, KEYC_F1|KEYC_META|KEYC_IMPLIED_META },
3325416581eSnicm 	{ TTYC_KF50, KEYC_F2|KEYC_META|KEYC_IMPLIED_META },
3335416581eSnicm 	{ TTYC_KF51, KEYC_F3|KEYC_META|KEYC_IMPLIED_META },
3345416581eSnicm 	{ TTYC_KF52, KEYC_F4|KEYC_META|KEYC_IMPLIED_META },
3355416581eSnicm 	{ TTYC_KF53, KEYC_F5|KEYC_META|KEYC_IMPLIED_META },
3365416581eSnicm 	{ TTYC_KF54, KEYC_F6|KEYC_META|KEYC_IMPLIED_META },
3375416581eSnicm 	{ TTYC_KF55, KEYC_F7|KEYC_META|KEYC_IMPLIED_META },
3385416581eSnicm 	{ TTYC_KF56, KEYC_F8|KEYC_META|KEYC_IMPLIED_META },
3395416581eSnicm 	{ TTYC_KF57, KEYC_F9|KEYC_META|KEYC_IMPLIED_META },
3405416581eSnicm 	{ TTYC_KF58, KEYC_F10|KEYC_META|KEYC_IMPLIED_META },
3415416581eSnicm 	{ TTYC_KF59, KEYC_F11|KEYC_META|KEYC_IMPLIED_META },
3425416581eSnicm 	{ TTYC_KF60, KEYC_F12|KEYC_META|KEYC_IMPLIED_META },
343dfe29c0aSnicm 
3445416581eSnicm 	{ TTYC_KF61, KEYC_F1|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT },
3455416581eSnicm 	{ TTYC_KF62, KEYC_F2|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT },
3465416581eSnicm 	{ TTYC_KF63, KEYC_F3|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT },
347dfe29c0aSnicm 
348f48c717dSnicm 	{ TTYC_KICH1, KEYC_IC },
349f48c717dSnicm 	{ TTYC_KDCH1, KEYC_DC },
350f48c717dSnicm 	{ TTYC_KHOME, KEYC_HOME },
351f48c717dSnicm 	{ TTYC_KEND, KEYC_END },
352f48c717dSnicm 	{ TTYC_KNP, KEYC_NPAGE },
353f48c717dSnicm 	{ TTYC_KPP, KEYC_PPAGE },
354f48c717dSnicm 	{ TTYC_KCBT, KEYC_BTAB },
355f48c717dSnicm 
356f48c717dSnicm 	/* Arrow keys from terminfo. */
3579265d1acSnicm 	{ TTYC_KCUU1, KEYC_UP|KEYC_CURSOR },
3589265d1acSnicm 	{ TTYC_KCUD1, KEYC_DOWN|KEYC_CURSOR },
3599265d1acSnicm 	{ TTYC_KCUB1, KEYC_LEFT|KEYC_CURSOR },
3609265d1acSnicm 	{ TTYC_KCUF1, KEYC_RIGHT|KEYC_CURSOR },
361f48c717dSnicm 
362fd054becSnicm 	/* Key and modifier capabilities. */
3635416581eSnicm 	{ TTYC_KDC2, KEYC_DC|KEYC_SHIFT },
3645416581eSnicm 	{ TTYC_KDC3, KEYC_DC|KEYC_META|KEYC_IMPLIED_META },
3655416581eSnicm 	{ TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META },
3665416581eSnicm 	{ TTYC_KDC5, KEYC_DC|KEYC_CTRL },
3675416581eSnicm 	{ TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL },
3685416581eSnicm 	{ TTYC_KDC7, KEYC_DC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL },
3695416581eSnicm 	{ TTYC_KIND, KEYC_DOWN|KEYC_SHIFT },
3705416581eSnicm 	{ TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT },
3715416581eSnicm 	{ TTYC_KDN3, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META },
3725416581eSnicm 	{ TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META },
3735416581eSnicm 	{ TTYC_KDN5, KEYC_DOWN|KEYC_CTRL },
3745416581eSnicm 	{ TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL },
3755416581eSnicm 	{ TTYC_KDN7, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL },
3765416581eSnicm 	{ TTYC_KEND2, KEYC_END|KEYC_SHIFT },
3775416581eSnicm 	{ TTYC_KEND3, KEYC_END|KEYC_META|KEYC_IMPLIED_META },
3785416581eSnicm 	{ TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META },
3795416581eSnicm 	{ TTYC_KEND5, KEYC_END|KEYC_CTRL },
3805416581eSnicm 	{ TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL },
3815416581eSnicm 	{ TTYC_KEND7, KEYC_END|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL },
3825416581eSnicm 	{ TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT },
3835416581eSnicm 	{ TTYC_KHOM3, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META },
3845416581eSnicm 	{ TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META },
3855416581eSnicm 	{ TTYC_KHOM5, KEYC_HOME|KEYC_CTRL },
3865416581eSnicm 	{ TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL },
3875416581eSnicm 	{ TTYC_KHOM7, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL },
3885416581eSnicm 	{ TTYC_KIC2, KEYC_IC|KEYC_SHIFT },
3895416581eSnicm 	{ TTYC_KIC3, KEYC_IC|KEYC_META|KEYC_IMPLIED_META },
3905416581eSnicm 	{ TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META },
3915416581eSnicm 	{ TTYC_KIC5, KEYC_IC|KEYC_CTRL },
3925416581eSnicm 	{ TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL },
3935416581eSnicm 	{ TTYC_KIC7, KEYC_IC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL },
3945416581eSnicm 	{ TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT },
3955416581eSnicm 	{ TTYC_KLFT3, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META },
3965416581eSnicm 	{ TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META },
3975416581eSnicm 	{ TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL },
3985416581eSnicm 	{ TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL },
3995416581eSnicm 	{ TTYC_KLFT7, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL },
4005416581eSnicm 	{ TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT },
4015416581eSnicm 	{ TTYC_KNXT3, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META },
4025416581eSnicm 	{ TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META },
4035416581eSnicm 	{ TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL },
4045416581eSnicm 	{ TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL },
4055416581eSnicm 	{ TTYC_KNXT7, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL },
4065416581eSnicm 	{ TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT },
4075416581eSnicm 	{ TTYC_KPRV3, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META },
4085416581eSnicm 	{ TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META },
4095416581eSnicm 	{ TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL },
4105416581eSnicm 	{ TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL },
4115416581eSnicm 	{ TTYC_KPRV7, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL },
4125416581eSnicm 	{ TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT },
4135416581eSnicm 	{ TTYC_KRIT3, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META },
4145416581eSnicm 	{ TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META },
4155416581eSnicm 	{ TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL },
4165416581eSnicm 	{ TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL },
4175416581eSnicm 	{ TTYC_KRIT7, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL },
4185416581eSnicm 	{ TTYC_KRI, KEYC_UP|KEYC_SHIFT },
4195416581eSnicm 	{ TTYC_KUP2, KEYC_UP|KEYC_SHIFT },
4205416581eSnicm 	{ TTYC_KUP3, KEYC_UP|KEYC_META|KEYC_IMPLIED_META },
4215416581eSnicm 	{ TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META },
4225416581eSnicm 	{ TTYC_KUP5, KEYC_UP|KEYC_CTRL },
4235416581eSnicm 	{ TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL },
4245416581eSnicm 	{ TTYC_KUP7, KEYC_UP|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL },
425f48c717dSnicm };
426f48c717dSnicm 
427f48c717dSnicm /* Add key to tree. */
4284a29328cSnicm static void
429885a4698Snicm tty_keys_add(struct tty *tty, const char *s, key_code key)
430311827fbSnicm {
4313904313cSnicm 	struct tty_key	*tk;
432e8bb025fSnicm 	size_t		 size;
4333904313cSnicm 	const char	*keystr;
434311827fbSnicm 
4355416581eSnicm 	keystr = key_string_lookup_key(key, 1);
4363904313cSnicm 	if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) {
437885a4698Snicm 		log_debug("new key %s: 0x%llx (%s)", s, key, keystr);
438e8bb025fSnicm 		tty_keys_add1(&tty->key_tree, s, key);
4393904313cSnicm 	} else {
440885a4698Snicm 		log_debug("replacing key %s: 0x%llx (%s)", s, key, keystr);
4413904313cSnicm 		tk->key = key;
442e8bb025fSnicm 	}
443e8bb025fSnicm }
444e8bb025fSnicm 
445e8bb025fSnicm /* Add next node to the tree. */
4464a29328cSnicm static void
447885a4698Snicm tty_keys_add1(struct tty_key **tkp, const char *s, key_code key)
448e8bb025fSnicm {
449e8bb025fSnicm 	struct tty_key	*tk;
450e8bb025fSnicm 
451e8bb025fSnicm 	/* Allocate a tree entry if there isn't one already. */
452e8bb025fSnicm 	tk = *tkp;
453e8bb025fSnicm 	if (tk == NULL) {
454e8bb025fSnicm 		tk = *tkp = xcalloc(1, sizeof *tk);
455e8bb025fSnicm 		tk->ch = *s;
4560bd9fd8fSnicm 		tk->key = KEYC_UNKNOWN;
457e8bb025fSnicm 	}
458e8bb025fSnicm 
459e8bb025fSnicm 	/* Find the next entry. */
460e8bb025fSnicm 	if (*s == tk->ch) {
461e8bb025fSnicm 		/* Move forward in string. */
462e8bb025fSnicm 		s++;
463e8bb025fSnicm 
464e8bb025fSnicm 		/* If this is the end of the string, no more is necessary. */
465e8bb025fSnicm 		if (*s == '\0') {
466311827fbSnicm 			tk->key = key;
467311827fbSnicm 			return;
468311827fbSnicm 		}
469311827fbSnicm 
470e8bb025fSnicm 		/* Use the child tree for the next character. */
471e8bb025fSnicm 		tkp = &tk->next;
472e8bb025fSnicm 	} else {
473e8bb025fSnicm 		if (*s < tk->ch)
474e8bb025fSnicm 			tkp = &tk->left;
475e8bb025fSnicm 		else if (*s > tk->ch)
476e8bb025fSnicm 			tkp = &tk->right;
477311827fbSnicm 	}
478311827fbSnicm 
479e8bb025fSnicm 	/* And recurse to add it. */
480e8bb025fSnicm 	tty_keys_add1(tkp, s, key);
481e8bb025fSnicm }
482e8bb025fSnicm 
483e8bb025fSnicm /* Initialise a key tree from the table. */
484311827fbSnicm void
485f48c717dSnicm tty_keys_build(struct tty *tty)
486311827fbSnicm {
487f48c717dSnicm 	const struct tty_default_key_raw	*tdkr;
4889265d1acSnicm 	const struct tty_default_key_xterm	*tdkx;
489f48c717dSnicm 	const struct tty_default_key_code	*tdkc;
4909265d1acSnicm 	u_int					 i, j;
49161b6b4dcSnicm 	const char				*s;
492b846cb6cSnicm 	struct options_entry			*o;
49339052edfSnicm 	struct options_array_item		*a;
49461b6b4dcSnicm 	union options_value			*ov;
4959265d1acSnicm 	char					 copy[16];
4969265d1acSnicm 	key_code				 key;
497311827fbSnicm 
498f48c717dSnicm 	if (tty->key_tree != NULL)
499998b1aadSnicm 		tty_keys_free(tty);
500e8bb025fSnicm 	tty->key_tree = NULL;
501311827fbSnicm 
5029265d1acSnicm 	for (i = 0; i < nitems(tty_default_xterm_keys); i++) {
5039265d1acSnicm 		tdkx = &tty_default_xterm_keys[i];
5049265d1acSnicm 		for (j = 2; j < nitems(tty_default_xterm_modifiers); j++) {
5059265d1acSnicm 			strlcpy(copy, tdkx->template, sizeof copy);
5069265d1acSnicm 			copy[strcspn(copy, "_")] = '0' + j;
5079265d1acSnicm 
5089265d1acSnicm 			key = tdkx->key|tty_default_xterm_modifiers[j];
5099265d1acSnicm 			tty_keys_add(tty, copy, key);
5109265d1acSnicm 		}
5119265d1acSnicm 	}
512f48c717dSnicm 	for (i = 0; i < nitems(tty_default_raw_keys); i++) {
513f48c717dSnicm 		tdkr = &tty_default_raw_keys[i];
514f48c717dSnicm 
515f48c717dSnicm 		s = tdkr->string;
516c740be85Snicm 		if (*s != '\0')
517c740be85Snicm 			tty_keys_add(tty, s, tdkr->key);
518e8bb025fSnicm 	}
519f48c717dSnicm 	for (i = 0; i < nitems(tty_default_code_keys); i++) {
520f48c717dSnicm 		tdkc = &tty_default_code_keys[i];
521311827fbSnicm 
522f48c717dSnicm 		s = tty_term_string(tty->term, tdkc->code);
523c740be85Snicm 		if (*s != '\0')
524c740be85Snicm 			tty_keys_add(tty, s, tdkc->key);
525f48c717dSnicm 
526311827fbSnicm 	}
527b846cb6cSnicm 
528b846cb6cSnicm 	o = options_get(global_options, "user-keys");
52939052edfSnicm 	if (o != NULL) {
53039052edfSnicm 		a = options_array_first(o);
53139052edfSnicm 		while (a != NULL) {
532a35741c0Snicm 			i = options_array_item_index(a);
53361b6b4dcSnicm 			ov = options_array_item_value(a);
53461b6b4dcSnicm 			tty_keys_add(tty, ov->string, KEYC_USER + i);
53539052edfSnicm 			a = options_array_next(a);
536b846cb6cSnicm 		}
537b846cb6cSnicm 	}
538311827fbSnicm }
539311827fbSnicm 
540e8bb025fSnicm /* Free the entire key tree. */
541311827fbSnicm void
542311827fbSnicm tty_keys_free(struct tty *tty)
543311827fbSnicm {
544e8bb025fSnicm 	tty_keys_free1(tty->key_tree);
545311827fbSnicm }
546311827fbSnicm 
547e8bb025fSnicm /* Free a single key. */
5484a29328cSnicm static void
549e8bb025fSnicm tty_keys_free1(struct tty_key *tk)
550311827fbSnicm {
551e8bb025fSnicm 	if (tk->next != NULL)
552e8bb025fSnicm 		tty_keys_free1(tk->next);
553e8bb025fSnicm 	if (tk->left != NULL)
554e8bb025fSnicm 		tty_keys_free1(tk->left);
555e8bb025fSnicm 	if (tk->right != NULL)
556e8bb025fSnicm 		tty_keys_free1(tk->right);
5577d053cf9Snicm 	free(tk);
558e8bb025fSnicm }
559e8bb025fSnicm 
560e8bb025fSnicm /* Lookup a key in the tree. */
5614a29328cSnicm static struct tty_key *
562e8bb025fSnicm tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size)
563e8bb025fSnicm {
564e8bb025fSnicm 	*size = 0;
565e8bb025fSnicm 	return (tty_keys_find1(tty->key_tree, buf, len, size));
566e8bb025fSnicm }
567e8bb025fSnicm 
568e8bb025fSnicm /* Find the next node. */
5694a29328cSnicm static struct tty_key *
570e8bb025fSnicm tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size)
571e8bb025fSnicm {
5727fa57c5bSnicm 	/* If no data, no match. */
5737fa57c5bSnicm 	if (len == 0)
5747fa57c5bSnicm 		return (NULL);
5757fa57c5bSnicm 
576e8bb025fSnicm 	/* If the node is NULL, this is the end of the tree. No match. */
577e8bb025fSnicm 	if (tk == NULL)
578e8bb025fSnicm 		return (NULL);
579e8bb025fSnicm 
580e8bb025fSnicm 	/* Pick the next in the sequence. */
581e8bb025fSnicm 	if (tk->ch == *buf) {
582e8bb025fSnicm 		/* Move forward in the string. */
583e8bb025fSnicm 		buf++; len--;
584e8bb025fSnicm 		(*size)++;
585e8bb025fSnicm 
586e8bb025fSnicm 		/* At the end of the string, return the current node. */
5870bd9fd8fSnicm 		if (len == 0 || (tk->next == NULL && tk->key != KEYC_UNKNOWN))
588311827fbSnicm 			return (tk);
589311827fbSnicm 
590e8bb025fSnicm 		/* Move into the next tree for the following character. */
591e8bb025fSnicm 		tk = tk->next;
592e8bb025fSnicm 	} else {
593e8bb025fSnicm 		if (*buf < tk->ch)
594e8bb025fSnicm 			tk = tk->left;
595e8bb025fSnicm 		else if (*buf > tk->ch)
596e8bb025fSnicm 			tk = tk->right;
597311827fbSnicm 	}
598311827fbSnicm 
599e8bb025fSnicm 	/* Move to the next in the tree. */
600e8bb025fSnicm 	return (tty_keys_find1(tk, buf, len, size));
601e8bb025fSnicm }
602e8bb025fSnicm 
6034a29328cSnicm /* Look up part of the next key. */
6044a29328cSnicm static int
6054a29328cSnicm tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key,
606fa0ecfe4Snicm     size_t *size, int expired)
6074a29328cSnicm {
6081f2e7474Snicm 	struct client		*c = tty->client;
6094a29328cSnicm 	struct tty_key		*tk, *tk1;
6104a29328cSnicm 	struct utf8_data	 ud;
6114a29328cSnicm 	enum utf8_state		 more;
6126852c63bSnicm 	utf8_char		 uc;
6134a29328cSnicm 	u_int			 i;
6144a29328cSnicm 
6151f2e7474Snicm 	log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len,
6161f2e7474Snicm 	    (int)len, buf, expired);
6174a29328cSnicm 
6184a29328cSnicm 	/* Is this a known key? */
6194a29328cSnicm 	tk = tty_keys_find(tty, buf, len, size);
620fa0ecfe4Snicm 	if (tk != NULL && tk->key != KEYC_UNKNOWN) {
6214a29328cSnicm 		tk1 = tk;
6224a29328cSnicm 		do
6231f2e7474Snicm 			log_debug("%s: keys in list: %#llx", c->name, tk1->key);
6244a29328cSnicm 		while ((tk1 = tk1->next) != NULL);
625fa0ecfe4Snicm 		if (tk->next != NULL && !expired)
626fa0ecfe4Snicm 			return (1);
6274a29328cSnicm 		*key = tk->key;
628fa0ecfe4Snicm 		return (0);
6294a29328cSnicm 	}
6304a29328cSnicm 
6314a29328cSnicm 	/* Is this valid UTF-8? */
6324a29328cSnicm 	more = utf8_open(&ud, (u_char)*buf);
6334a29328cSnicm 	if (more == UTF8_MORE) {
6344a29328cSnicm 		*size = ud.size;
635fa0ecfe4Snicm 		if (len < ud.size) {
636fa0ecfe4Snicm 			if (!expired)
6374a29328cSnicm 				return (1);
638fa0ecfe4Snicm 			return (-1);
639fa0ecfe4Snicm 		}
6404a29328cSnicm 		for (i = 1; i < ud.size; i++)
6414a29328cSnicm 			more = utf8_append(&ud, (u_char)buf[i]);
6424a29328cSnicm 		if (more != UTF8_DONE)
643fa0ecfe4Snicm 			return (-1);
6444a29328cSnicm 
6456852c63bSnicm 		if (utf8_from_data(&ud, &uc) != UTF8_DONE)
646fa0ecfe4Snicm 			return (-1);
6476852c63bSnicm 		*key = uc;
6484a29328cSnicm 
6491f2e7474Snicm 		log_debug("%s: UTF-8 key %.*s %#llx", c->name, (int)ud.size,
6506852c63bSnicm 		    ud.data, *key);
6514a29328cSnicm 		return (0);
6524a29328cSnicm 	}
6534a29328cSnicm 
6544a29328cSnicm 	return (-1);
6554a29328cSnicm }
6564a29328cSnicm 
657a1c6091aSnicm /* Process window size change escape sequences. */
658a1c6091aSnicm static int
659a1c6091aSnicm tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size)
660a1c6091aSnicm {
661a1c6091aSnicm 	struct client	*c = tty->client;
662a1c6091aSnicm 	size_t		 end;
663a1c6091aSnicm 	char		 tmp[64];
664a1c6091aSnicm 	u_int		 sx, sy, xpixel, ypixel, char_x, char_y;
665a1c6091aSnicm 
666a1c6091aSnicm 	*size = 0;
667a1c6091aSnicm 
668a1c6091aSnicm 	/* If we did not request this, ignore it. */
669a1c6091aSnicm 	if (!(tty->flags & TTY_WINSIZEQUERY))
670a1c6091aSnicm 		return (-1);
671a1c6091aSnicm 
672a1c6091aSnicm 	/* First two bytes are always \033[. */
673a1c6091aSnicm 	if (buf[0] != '\033')
674a1c6091aSnicm 		return (-1);
675a1c6091aSnicm 	if (len == 1)
676a1c6091aSnicm 		return (1);
677a1c6091aSnicm 	if (buf[1] != '[')
678a1c6091aSnicm 		return (-1);
679a1c6091aSnicm 	if (len == 2)
680a1c6091aSnicm 		return (1);
681a1c6091aSnicm 
682a1c6091aSnicm 	/*
683a1c6091aSnicm 	 * Stop at either 't' or anything that isn't a
684a1c6091aSnicm 	 * number or ';'.
685a1c6091aSnicm 	 */
686a1c6091aSnicm 	for (end = 2; end < len && end != sizeof tmp; end++) {
687a1c6091aSnicm 		if (buf[end] == 't')
688a1c6091aSnicm 			break;
689a1c6091aSnicm 		if (!isdigit((u_char)buf[end]) && buf[end] != ';')
690a1c6091aSnicm 			break;
691a1c6091aSnicm 	}
692a1c6091aSnicm 	if (end == len)
693a1c6091aSnicm 		return (1);
694a1c6091aSnicm 	if (end == sizeof tmp || buf[end] != 't')
695a1c6091aSnicm 		return (-1);
696a1c6091aSnicm 
697a1c6091aSnicm 	/* Copy to the buffer. */
698a1c6091aSnicm 	memcpy(tmp, buf + 2, end - 2);
699a1c6091aSnicm 	tmp[end - 2] = '\0';
700a1c6091aSnicm 
701a1c6091aSnicm 	/* Try to parse the window size sequence. */
702a1c6091aSnicm 	if (sscanf(tmp, "8;%u;%u", &sy, &sx) == 2) {
703a1c6091aSnicm 		/* Window size in characters. */
704a1c6091aSnicm 		tty_set_size(tty, sx, sy, tty->xpixel, tty->ypixel);
705a1c6091aSnicm 
706a1c6091aSnicm 		*size = end + 1;
707a1c6091aSnicm 		return (0);
708a1c6091aSnicm 	} else if (sscanf(tmp, "4;%u;%u", &ypixel, &xpixel) == 2) {
709a1c6091aSnicm 		/* Window size in pixels. */
710a1c6091aSnicm 		char_x = (xpixel && tty->sx) ? xpixel / tty->sx : 0;
711a1c6091aSnicm 		char_y = (ypixel && tty->sy) ? ypixel / tty->sy : 0;
712a1c6091aSnicm 		tty_set_size(tty, tty->sx, tty->sy, char_x, char_y);
713a1c6091aSnicm 		tty_invalidate(tty);
714a1c6091aSnicm 
715a1c6091aSnicm 		tty->flags &= ~TTY_WINSIZEQUERY;
716a1c6091aSnicm 		*size = end + 1;
717a1c6091aSnicm 		return (0);
718a1c6091aSnicm 	}
719a1c6091aSnicm 
720a1c6091aSnicm 	log_debug("%s: unrecognized window size sequence: %s", c->name, tmp);
721a1c6091aSnicm 	return (-1);
722a1c6091aSnicm }
723a1c6091aSnicm 
724a1c6091aSnicm 
725a88e9db6Snicm /* Process at least one key in the buffer. Return 0 if no keys present. */
726a88e9db6Snicm int
727a3d8f346Snicm tty_keys_next(struct tty *tty)
728311827fbSnicm {
7291f2e7474Snicm 	struct client		*c = tty->client;
730311827fbSnicm 	struct timeval		 tv;
731586d3997Snicm 	const char		*buf;
732311827fbSnicm 	size_t			 len, size;
7334cf01325Snicm 	cc_t			 bspace;
734fa0ecfe4Snicm 	int			 delay, expired = 0, n;
735719f5715Snicm 	key_code		 key, onlykey;
736a88e9db6Snicm 	struct mouse_event	 m = { 0 };
737a88e9db6Snicm 	struct key_event	*event;
738a88e9db6Snicm 
739c740be85Snicm 	/* Get key buffer. */
74099f5cecfSnicm 	buf = EVBUFFER_DATA(tty->in);
74199f5cecfSnicm 	len = EVBUFFER_LENGTH(tty->in);
742311827fbSnicm 	if (len == 0)
743a3d8f346Snicm 		return (0);
7441f2e7474Snicm 	log_debug("%s: keys are %zu (%.*s)", c->name, len, (int)len, buf);
745311827fbSnicm 
74690866723Snicm 	/* Is this a clipboard response? */
74790866723Snicm 	switch (tty_keys_clipboard(tty, buf, len, &size)) {
74890866723Snicm 	case 0:		/* yes */
74990866723Snicm 		key = KEYC_UNKNOWN;
75090866723Snicm 		goto complete_key;
75190866723Snicm 	case -1:	/* no, or not valid */
75290866723Snicm 		break;
75390866723Snicm 	case 1:		/* partial */
75490866723Snicm 		goto partial_key;
75590866723Snicm 	}
75690866723Snicm 
757e0697ebcSnicm 	/* Is this a primary device attributes response? */
758f00ac6e6Snicm 	switch (tty_keys_device_attributes(tty, buf, len, &size)) {
759f00ac6e6Snicm 	case 0:		/* yes */
760f00ac6e6Snicm 		key = KEYC_UNKNOWN;
761f00ac6e6Snicm 		goto complete_key;
762f00ac6e6Snicm 	case -1:	/* no, or not valid */
763f00ac6e6Snicm 		break;
764f00ac6e6Snicm 	case 1:		/* partial */
765f00ac6e6Snicm 		goto partial_key;
766f00ac6e6Snicm 	}
767f00ac6e6Snicm 
768e0697ebcSnicm 	/* Is this a secondary device attributes response? */
769e0697ebcSnicm 	switch (tty_keys_device_attributes2(tty, buf, len, &size)) {
770e0697ebcSnicm 	case 0:		/* yes */
771e0697ebcSnicm 		key = KEYC_UNKNOWN;
772e0697ebcSnicm 		goto complete_key;
773e0697ebcSnicm 	case -1:	/* no, or not valid */
774e0697ebcSnicm 		break;
775e0697ebcSnicm 	case 1:		/* partial */
776e0697ebcSnicm 		goto partial_key;
777e0697ebcSnicm 	}
778e0697ebcSnicm 
779ecd9db25Snicm 	/* Is this an extended device attributes response? */
780ecd9db25Snicm 	switch (tty_keys_extended_device_attributes(tty, buf, len, &size)) {
7816110b15cSnicm 	case 0:		/* yes */
7826110b15cSnicm 		key = KEYC_UNKNOWN;
7836110b15cSnicm 		goto complete_key;
7846110b15cSnicm 	case -1:	/* no, or not valid */
7856110b15cSnicm 		break;
7866110b15cSnicm 	case 1:		/* partial */
7876110b15cSnicm 		goto partial_key;
7886110b15cSnicm 	}
7896110b15cSnicm 
790d21788ceSnicm 	/* Is this a colours response? */
791891e2565Snicm 	switch (tty_keys_colours(tty, buf, len, &size, &tty->fg, &tty->bg)) {
792d21788ceSnicm 	case 0:		/* yes */
793d21788ceSnicm 		key = KEYC_UNKNOWN;
794d21788ceSnicm 		goto complete_key;
795d21788ceSnicm 	case -1:	/* no, or not valid */
796d21788ceSnicm 		break;
797d21788ceSnicm 	case 1:		/* partial */
798d21788ceSnicm 		goto partial_key;
799d21788ceSnicm 	}
800d21788ceSnicm 
80197f93609Snicm 	/* Is this a mouse key press? */
802a88e9db6Snicm 	switch (tty_keys_mouse(tty, buf, len, &size, &m)) {
803888b88f4Snicm 	case 0:		/* yes */
804888b88f4Snicm 		key = KEYC_MOUSE;
805c740be85Snicm 		goto complete_key;
806888b88f4Snicm 	case -1:	/* no, or not valid */
80789ce6bdbSnicm 		break;
8089146dae2Snicm 	case -2:	/* yes, but we don't care. */
809737b0b47Snicm 		key = KEYC_MOUSE;
8109146dae2Snicm 		goto discard_key;
811888b88f4Snicm 	case 1:		/* partial */
812888b88f4Snicm 		goto partial_key;
813311827fbSnicm 	}
814311827fbSnicm 
815f1c69fe1Snicm 	/* Is this an extended key press? */
816f1c69fe1Snicm 	switch (tty_keys_extended_key(tty, buf, len, &size, &key)) {
817f1c69fe1Snicm 	case 0:		/* yes */
818f1c69fe1Snicm 		goto complete_key;
819f1c69fe1Snicm 	case -1:	/* no, or not valid */
820f1c69fe1Snicm 		break;
821f1c69fe1Snicm 	case 1:		/* partial */
822f1c69fe1Snicm 		goto partial_key;
823f1c69fe1Snicm 	}
824f1c69fe1Snicm 
825a1c6091aSnicm 	/* Check for window size query */
826a1c6091aSnicm 	switch (tty_keys_winsz(tty, buf, len, &size)) {
827a1c6091aSnicm 	case 0:		/* yes */
828a1c6091aSnicm 		key = KEYC_UNKNOWN;
829a1c6091aSnicm 		goto complete_key;
830a1c6091aSnicm 	case -1:	/* no, or not valid */
831a1c6091aSnicm 		break;
832a1c6091aSnicm 	case 1:		/* partial */
833a1c6091aSnicm 		goto partial_key;
834a1c6091aSnicm 	}
835a1c6091aSnicm 
8364a29328cSnicm first_key:
837b70f7e17Snicm 	/* Try to lookup complete key. */
838b70f7e17Snicm 	n = tty_keys_next1(tty, buf, len, &key, &size, expired);
839b70f7e17Snicm 	if (n == 0)	/* found */
840b70f7e17Snicm 		goto complete_key;
841b70f7e17Snicm 	if (n == 1)
842b70f7e17Snicm 		goto partial_key;
843b70f7e17Snicm 
844b70f7e17Snicm 	/*
845b70f7e17Snicm 	 * If not a complete key, look for key with an escape prefix (meta
846b70f7e17Snicm 	 * modifier).
847b70f7e17Snicm 	 */
848ec97f12dSnicm 	if (*buf == '\033' && len > 1) {
849fa0ecfe4Snicm 		/* Look for a key without the escape. */
850fa0ecfe4Snicm 		n = tty_keys_next1(tty, buf + 1, len - 1, &key, &size, expired);
851fa0ecfe4Snicm 		if (n == 0) {	/* found */
8525416581eSnicm 			if (key & KEYC_IMPLIED_META) {
85328dafa00Snicm 				/*
85428dafa00Snicm 				 * We want the escape key as well as the xterm
85528dafa00Snicm 				 * key, because the xterm sequence implicitly
85628dafa00Snicm 				 * includes the escape (so if we see
85728dafa00Snicm 				 * \033\033[1;3D we know it is an Escape
85828dafa00Snicm 				 * followed by M-Left, not just M-Left).
85928dafa00Snicm 				 */
86028dafa00Snicm 				key = '\033';
86128dafa00Snicm 				size = 1;
86228dafa00Snicm 				goto complete_key;
86328dafa00Snicm 			}
8646a385b80Snicm 			key |= KEYC_META;
865fa0ecfe4Snicm 			size++;
86606b16c35Snicm 			goto complete_key;
867fa0ecfe4Snicm 		}
868fa0ecfe4Snicm 		if (n == 1)	/* partial */
8694a29328cSnicm 			goto partial_key;
8701c8b05a8Snicm 	}
87106b16c35Snicm 
8724a29328cSnicm 	/*
8731c8b05a8Snicm 	 * At this point, we know the key is not partial (with or without
8741c8b05a8Snicm 	 * escape). So pass it through even if the timer has not expired.
8754a29328cSnicm 	 */
8761c8b05a8Snicm 	if (*buf == '\033' && len >= 2) {
8776a385b80Snicm 		key = (u_char)buf[1] | KEYC_META;
878c740be85Snicm 		size = 2;
8791c8b05a8Snicm 	} else {
8801c8b05a8Snicm 		key = (u_char)buf[0];
881c740be85Snicm 		size = 1;
8821c8b05a8Snicm 	}
883719f5715Snicm 
884719f5715Snicm 	/* C-Space is special. */
885719f5715Snicm 	if ((key & KEYC_MASK_KEY) == C0_NUL)
886719f5715Snicm 		key = ' ' | KEYC_CTRL | (key & KEYC_META);
887719f5715Snicm 
888719f5715Snicm 	/*
889*2f106d38Snicm 	 * Check for backspace key using termios VERASE - the terminfo
890*2f106d38Snicm 	 * kbs entry is extremely unreliable, so cannot be safely
891*2f106d38Snicm 	 * used. termios should have a better idea.
892*2f106d38Snicm 	 */
893*2f106d38Snicm 	bspace = tty->tio.c_cc[VERASE];
894*2f106d38Snicm 	if (bspace != _POSIX_VDISABLE && key == bspace) {
895*2f106d38Snicm 		log_debug("%s: key %#llx is backspace", c->name, key);
896*2f106d38Snicm 		key = KEYC_BSPACE;
897*2f106d38Snicm 	}
898*2f106d38Snicm 
899*2f106d38Snicm 	/*
900719f5715Snicm 	 * Fix up all C0 control codes that don't have a dedicated key into
901719f5715Snicm 	 * corresponding Ctrl keys. Convert characters in the A-Z range into
902719f5715Snicm 	 * lowercase, so ^A becomes a|CTRL.
903719f5715Snicm 	 */
904719f5715Snicm 	onlykey = key & KEYC_MASK_KEY;
9054aaf2c8dSnicm 	if (onlykey < 0x20 &&
9064aaf2c8dSnicm 	    onlykey != C0_HT &&
9074aaf2c8dSnicm 	    onlykey != C0_CR &&
908719f5715Snicm 	    onlykey != C0_ESC) {
909719f5715Snicm 		onlykey |= 0x40;
910719f5715Snicm 		if (onlykey >= 'A' && onlykey <= 'Z')
911719f5715Snicm 			onlykey |= 0x20;
912719f5715Snicm 		key = onlykey | KEYC_CTRL | (key & KEYC_META);
913719f5715Snicm 	}
914719f5715Snicm 
915c740be85Snicm 	goto complete_key;
916e8bb025fSnicm 
917e8bb025fSnicm partial_key:
9181f2e7474Snicm 	log_debug("%s: partial key %.*s", c->name, (int)len, buf);
919c740be85Snicm 
920c740be85Snicm 	/* If timer is going, check for expiration. */
921c740be85Snicm 	if (tty->flags & TTY_TIMER) {
922c740be85Snicm 		if (evtimer_initialized(&tty->key_timer) &&
923d9aede6bSnicm 		    !evtimer_pending(&tty->key_timer, NULL)) {
924d9aede6bSnicm 			expired = 1;
925c740be85Snicm 			goto first_key;
926d9aede6bSnicm 		}
927c740be85Snicm 		return (0);
928a3d8f346Snicm 	}
929a3d8f346Snicm 
930c740be85Snicm 	/* Get the time period. */
931d89252e5Snicm 	delay = options_get_number(global_options, "escape-time");
932ad7a034aSnicm 	if (delay == 0)
933ad7a034aSnicm 		delay = 1;
934298db98fSnicm 	tv.tv_sec = delay / 1000;
935298db98fSnicm 	tv.tv_usec = (delay % 1000) * 1000L;
936a3d8f346Snicm 
937c740be85Snicm 	/* Start the timer. */
938346357b7Snicm 	if (event_initialized(&tty->key_timer))
939a3d8f346Snicm 		evtimer_del(&tty->key_timer);
940a3d8f346Snicm 	evtimer_set(&tty->key_timer, tty_keys_callback, tty);
941a3d8f346Snicm 	evtimer_add(&tty->key_timer, &tv);
942a3d8f346Snicm 
943c740be85Snicm 	tty->flags |= TTY_TIMER;
944a3d8f346Snicm 	return (0);
945311827fbSnicm 
946c740be85Snicm complete_key:
9471f2e7474Snicm 	log_debug("%s: complete key %.*s %#llx", c->name, (int)size, buf, key);
948e8bb025fSnicm 
949c740be85Snicm 	/* Remove key timer. */
950346357b7Snicm 	if (event_initialized(&tty->key_timer))
951a3d8f346Snicm 		evtimer_del(&tty->key_timer);
952c740be85Snicm 	tty->flags &= ~TTY_TIMER;
953e8bb025fSnicm 
9546ddcc08fSnicm 	/* Check for focus events. */
955698bdd54Snicm 	if (key == KEYC_FOCUS_OUT) {
9561a773291Snicm 		c->flags &= ~CLIENT_FOCUSED;
9571a773291Snicm 		window_update_focus(c->session->curw->window);
958698bdd54Snicm 		notify_client("client-focus-out", c);
959698bdd54Snicm 	} else if (key == KEYC_FOCUS_IN) {
9601a773291Snicm 		c->flags |= CLIENT_FOCUSED;
961698bdd54Snicm 		notify_client("client-focus-in", c);
9621a773291Snicm 		window_update_focus(c->session->curw->window);
963698bdd54Snicm 	}
9646ddcc08fSnicm 
965c740be85Snicm 	/* Fire the key. */
966a88e9db6Snicm 	if (key != KEYC_UNKNOWN) {
967482624f4Snicm 		event = xcalloc(1, sizeof *event);
968a88e9db6Snicm 		event->key = key;
969a88e9db6Snicm 		memcpy(&event->m, &m, sizeof event->m);
970482624f4Snicm 
971482624f4Snicm 		event->buf = xmalloc(size);
972482624f4Snicm 		event->len = size;
973482624f4Snicm 		memcpy (event->buf, buf, event->len);
974482624f4Snicm 
975482624f4Snicm 		if (!server_client_handle_key(c, event)) {
976482624f4Snicm 			free(event->buf);
9777df29c9dSnicm 			free(event);
978a88e9db6Snicm 		}
979482624f4Snicm 	}
980482624f4Snicm 
981482624f4Snicm 	/* Remove data from buffer. */
982482624f4Snicm 	evbuffer_drain(tty->in, size);
983e8bb025fSnicm 
984a3d8f346Snicm 	return (1);
9859146dae2Snicm 
9869146dae2Snicm discard_key:
9871f2e7474Snicm 	log_debug("%s: discard key %.*s %#llx", c->name, (int)size, buf, key);
9889146dae2Snicm 
9899146dae2Snicm 	/* Remove data from buffer. */
99099f5cecfSnicm 	evbuffer_drain(tty->in, size);
9919146dae2Snicm 
9929146dae2Snicm 	return (1);
993a3d8f346Snicm }
994a3d8f346Snicm 
995e8bb025fSnicm /* Key timer callback. */
9964a29328cSnicm static void
997d0e2e7f1Snicm tty_keys_callback(__unused int fd, __unused short events, void *data)
998a3d8f346Snicm {
999a3d8f346Snicm 	struct tty	*tty = data;
1000a3d8f346Snicm 
1001c740be85Snicm 	if (tty->flags & TTY_TIMER) {
1002e8bb025fSnicm 		while (tty_keys_next(tty))
1003e8bb025fSnicm 			;
1004311827fbSnicm 	}
1005c740be85Snicm }
1006311827fbSnicm 
1007888b88f4Snicm /*
1008f1c69fe1Snicm  * Handle extended key input. This has two forms: \033[27;m;k~ and \033[k;mu,
1009f1c69fe1Snicm  * where k is key as a number and m is a modifier. Returns 0 for success, -1
1010f1c69fe1Snicm  * for failure, 1 for partial;
1011f1c69fe1Snicm  */
1012f1c69fe1Snicm static int
1013f1c69fe1Snicm tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
1014f1c69fe1Snicm     size_t *size, key_code *key)
1015f1c69fe1Snicm {
1016f1c69fe1Snicm 	struct client	*c = tty->client;
1017f1c69fe1Snicm 	size_t		 end;
1018f1c69fe1Snicm 	u_int		 number, modifiers;
1019f1c69fe1Snicm 	char		 tmp[64];
1020188fd23eSnicm 	cc_t		 bspace;
10213835afffSnicm 	key_code	 nkey, onlykey;
1022b843f94bSnicm 	struct utf8_data ud;
1023b843f94bSnicm 	utf8_char        uc;
1024f1c69fe1Snicm 
1025f1c69fe1Snicm 	*size = 0;
1026f1c69fe1Snicm 
1027f1c69fe1Snicm 	/* First two bytes are always \033[. */
1028f1c69fe1Snicm 	if (buf[0] != '\033')
1029f1c69fe1Snicm 		return (-1);
1030f1c69fe1Snicm 	if (len == 1)
1031f1c69fe1Snicm 		return (1);
1032f1c69fe1Snicm 	if (buf[1] != '[')
1033f1c69fe1Snicm 		return (-1);
1034f1c69fe1Snicm 	if (len == 2)
1035f1c69fe1Snicm 		return (1);
1036f1c69fe1Snicm 
1037f1c69fe1Snicm 	/*
1038f1c69fe1Snicm 	 * Look for a terminator. Stop at either '~' or anything that isn't a
1039f1c69fe1Snicm 	 * number or ';'.
1040f1c69fe1Snicm 	 */
1041f1c69fe1Snicm 	for (end = 2; end < len && end != sizeof tmp; end++) {
1042f1c69fe1Snicm 		if (buf[end] == '~')
1043f1c69fe1Snicm 			break;
1044f1c69fe1Snicm 		if (!isdigit((u_char)buf[end]) && buf[end] != ';')
1045f1c69fe1Snicm 			break;
1046f1c69fe1Snicm 	}
1047f1c69fe1Snicm 	if (end == len)
1048f1c69fe1Snicm 		return (1);
1049f1c69fe1Snicm 	if (end == sizeof tmp || (buf[end] != '~' && buf[end] != 'u'))
1050f1c69fe1Snicm 		return (-1);
1051f1c69fe1Snicm 
1052f1c69fe1Snicm 	/* Copy to the buffer. */
1053f1c69fe1Snicm 	memcpy(tmp, buf + 2, end);
1054f1c69fe1Snicm 	tmp[end] = '\0';
1055f1c69fe1Snicm 
1056f1c69fe1Snicm 	/* Try to parse either form of key. */
1057f1c69fe1Snicm 	if (buf[end] == '~') {
1058f1c69fe1Snicm 		if (sscanf(tmp, "27;%u;%u", &modifiers, &number) != 2)
1059f1c69fe1Snicm 			return (-1);
1060f1c69fe1Snicm 	} else {
1061f1c69fe1Snicm 		if (sscanf(tmp ,"%u;%u", &number, &modifiers) != 2)
1062f1c69fe1Snicm 			return (-1);
1063f1c69fe1Snicm 	}
1064f1c69fe1Snicm 	*size = end + 1;
1065f1c69fe1Snicm 
1066188fd23eSnicm 	/* Store the key. */
1067188fd23eSnicm 	bspace = tty->tio.c_cc[VERASE];
1068188fd23eSnicm 	if (bspace != _POSIX_VDISABLE && number == bspace)
1069188fd23eSnicm 		nkey = KEYC_BSPACE;
1070188fd23eSnicm 	else
1071188fd23eSnicm 		nkey = number;
1072188fd23eSnicm 
1073b843f94bSnicm 	/* Convert UTF-32 codepoint into internal representation. */
10748d6395f7Snicm 	if (nkey != KEYC_BSPACE && nkey & ~0x7f) {
1075b843f94bSnicm 		if (utf8_fromwc(nkey, &ud) == UTF8_DONE &&
1076b843f94bSnicm 		    utf8_from_data(&ud, &uc) == UTF8_DONE)
1077b843f94bSnicm 			nkey = uc;
1078b843f94bSnicm 		else
1079b843f94bSnicm 			return (-1);
1080b843f94bSnicm 	}
1081b843f94bSnicm 
1082188fd23eSnicm 	/* Update the modifiers. */
1083babee3ebSnicm 	if (modifiers > 0) {
1084babee3ebSnicm 		modifiers--;
10853835afffSnicm 		if (modifiers & 1)
1086188fd23eSnicm 			nkey |= KEYC_SHIFT;
1087babee3ebSnicm 		if (modifiers & 2)
1088babee3ebSnicm 			nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Alt */
1089babee3ebSnicm 		if (modifiers & 4)
1090188fd23eSnicm 			nkey |= KEYC_CTRL;
1091babee3ebSnicm 		if (modifiers & 8)
1092babee3ebSnicm 			nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Meta */
1093f1c69fe1Snicm 	}
1094188fd23eSnicm 
1095719f5715Snicm 	/* Convert S-Tab into Backtab. */
1096719f5715Snicm 	if ((nkey & KEYC_MASK_KEY) == '\011' && (nkey & KEYC_SHIFT))
1097719f5715Snicm 		nkey = KEYC_BTAB | (nkey & ~KEYC_MASK_KEY & ~KEYC_SHIFT);
1098188fd23eSnicm 
10993835afffSnicm 	/*
11003835afffSnicm 	 * Deal with the Shift modifier when present alone. The problem is that
11013835afffSnicm 	 * in mode 2 some terminals would report shifted keys, like S-a, as
11023835afffSnicm 	 * just A, and some as S-A.
11033835afffSnicm 	 *
11043835afffSnicm 	 * Because we need an unambiguous internal representation, and because
11053835afffSnicm 	 * restoring the Shift modifier when it's missing would require knowing
11063835afffSnicm 	 * the keyboard layout, and because S-A would cause a lot of issues
11073835afffSnicm 	 * downstream, we choose to lose the Shift for all printable
11083835afffSnicm 	 * characters.
11093835afffSnicm 	 *
11103835afffSnicm 	 * That still leaves some ambiguity, such as C-S-A vs. C-A, but that's
11113835afffSnicm 	 * OK, and applications can handle that.
11123835afffSnicm 	 */
11133835afffSnicm 	onlykey = nkey & KEYC_MASK_KEY;
11143835afffSnicm 	if (((onlykey > 0x20 && onlykey < 0x7f) ||
11153835afffSnicm 	    KEYC_IS_UNICODE(nkey)) &&
11163835afffSnicm 	    (nkey & KEYC_MASK_MODIFIERS) == KEYC_SHIFT)
11173835afffSnicm 		nkey &= ~KEYC_SHIFT;
11183835afffSnicm 
1119f1c69fe1Snicm 	if (log_get_level() != 0) {
1120f1c69fe1Snicm 		log_debug("%s: extended key %.*s is %llx (%s)", c->name,
1121188fd23eSnicm 		    (int)*size, buf, nkey, key_string_lookup_key(nkey, 1));
1122f1c69fe1Snicm 	}
1123719f5715Snicm 
11248f86830fSnicm 	*key = nkey;
1125f1c69fe1Snicm 	return (0);
1126f1c69fe1Snicm }
1127f1c69fe1Snicm 
1128f1c69fe1Snicm /*
1129888b88f4Snicm  * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial
113030fb8e30Snicm  * (probably a mouse sequence but need more data), -2 if an invalid mouse
113130fb8e30Snicm  * sequence.
1132888b88f4Snicm  */
11334a29328cSnicm static int
1134a88e9db6Snicm tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
1135a88e9db6Snicm     struct mouse_event *m)
1136311827fbSnicm {
11371f2e7474Snicm 	struct client	*c = tty->client;
11382ac09bbfSnicm 	u_int		 i, x, y, b, sgr_b;
11391f2e7474Snicm 	u_char		 sgr_type, ch;
1140699995ddSnicm 
1141311827fbSnicm 	/*
1142699995ddSnicm 	 * Standard mouse sequences are \033[M followed by three characters
11436c0777c5Snicm 	 * indicating button, X and Y, all based at 32 with 1,1 top-left.
1144699995ddSnicm 	 *
1145699995ddSnicm 	 * UTF-8 mouse sequences are similar but the three are expressed as
1146699995ddSnicm 	 * UTF-8 characters.
11476c0777c5Snicm 	 *
11486c0777c5Snicm 	 * SGR extended mouse sequences are \033[< followed by three numbers in
11496c0777c5Snicm 	 * decimal and separated by semicolons indicating button, X and Y. A
11506c0777c5Snicm 	 * trailing 'M' is click or scroll and trailing 'm' release. All are
11516c0777c5Snicm 	 * based at 0 with 1,1 top-left.
1152311827fbSnicm 	 */
1153311827fbSnicm 
1154888b88f4Snicm 	*size = 0;
1155e048bb79Snicm 	x = y = b = sgr_b = 0;
1156e048bb79Snicm 	sgr_type = ' ';
1157888b88f4Snicm 
11586c0777c5Snicm 	/* First two bytes are always \033[. */
1159888b88f4Snicm 	if (buf[0] != '\033')
1160888b88f4Snicm 		return (-1);
1161888b88f4Snicm 	if (len == 1)
1162888b88f4Snicm 		return (1);
1163888b88f4Snicm 	if (buf[1] != '[')
1164888b88f4Snicm 		return (-1);
1165888b88f4Snicm 	if (len == 2)
1166888b88f4Snicm 		return (1);
1167888b88f4Snicm 
11686c0777c5Snicm 	/*
11692ac09bbfSnicm 	 * Third byte is M in old standard (and UTF-8 extension which we do not
11702ac09bbfSnicm 	 * support), < in SGR extension.
11716c0777c5Snicm 	 */
11726c0777c5Snicm 	if (buf[2] == 'M') {
1173699995ddSnicm 		/* Read the three inputs. */
1174699995ddSnicm 		*size = 3;
1175699995ddSnicm 		for (i = 0; i < 3; i++) {
11766c0777c5Snicm 			if (len <= *size)
1177888b88f4Snicm 				return (1);
11781f2e7474Snicm 			ch = (u_char)buf[(*size)++];
1179699995ddSnicm 			if (i == 0)
11801f2e7474Snicm 				b = ch;
1181699995ddSnicm 			else if (i == 1)
11821f2e7474Snicm 				x = ch;
1183699995ddSnicm 			else
11841f2e7474Snicm 				y = ch;
1185699995ddSnicm 		}
11861f2e7474Snicm 		log_debug("%s: mouse input: %.*s", c->name, (int)*size, buf);
1187699995ddSnicm 
1188699995ddSnicm 		/* Check and return the mouse input. */
118981e6b289Snicm 		if (b < MOUSE_PARAM_BTN_OFF ||
119081e6b289Snicm 		    x < MOUSE_PARAM_POS_OFF ||
119181e6b289Snicm 		    y < MOUSE_PARAM_POS_OFF)
119230fb8e30Snicm 			return (-2);
119381e6b289Snicm 		b -= MOUSE_PARAM_BTN_OFF;
119481e6b289Snicm 		x -= MOUSE_PARAM_POS_OFF;
119581e6b289Snicm 		y -= MOUSE_PARAM_POS_OFF;
11966c0777c5Snicm 	} else if (buf[2] == '<') {
11976c0777c5Snicm 		/* Read the three inputs. */
11986c0777c5Snicm 		*size = 3;
11996c0777c5Snicm 		while (1) {
12006c0777c5Snicm 			if (len <= *size)
12016c0777c5Snicm 				return (1);
12021f2e7474Snicm 			ch = (u_char)buf[(*size)++];
12031f2e7474Snicm 			if (ch == ';')
12046c0777c5Snicm 				break;
12051f2e7474Snicm 			if (ch < '0' || ch > '9')
12066c0777c5Snicm 				return (-1);
12071f2e7474Snicm 			sgr_b = 10 * sgr_b + (ch - '0');
12086c0777c5Snicm 		}
12096c0777c5Snicm 		while (1) {
12106c0777c5Snicm 			if (len <= *size)
12116c0777c5Snicm 				return (1);
12121f2e7474Snicm 			ch = (u_char)buf[(*size)++];
12131f2e7474Snicm 			if (ch == ';')
12146c0777c5Snicm 				break;
12151f2e7474Snicm 			if (ch < '0' || ch > '9')
12166c0777c5Snicm 				return (-1);
12171f2e7474Snicm 			x = 10 * x + (ch - '0');
12186c0777c5Snicm 		}
12196c0777c5Snicm 		while (1) {
12206c0777c5Snicm 			if (len <= *size)
12216c0777c5Snicm 				return (1);
12221f2e7474Snicm 			ch = (u_char)buf[(*size)++];
12231f2e7474Snicm 			if (ch == 'M' || ch == 'm')
12246c0777c5Snicm 				break;
12251f2e7474Snicm 			if (ch < '0' || ch > '9')
12266c0777c5Snicm 				return (-1);
12271f2e7474Snicm 			y = 10 * y + (ch - '0');
12286c0777c5Snicm 		}
12291f2e7474Snicm 		log_debug("%s: mouse input (SGR): %.*s", c->name, (int)*size,
12301f2e7474Snicm 		    buf);
12316c0777c5Snicm 
12326c0777c5Snicm 		/* Check and return the mouse input. */
12336c0777c5Snicm 		if (x < 1 || y < 1)
123430fb8e30Snicm 			return (-2);
12356c0777c5Snicm 		x--;
12366c0777c5Snicm 		y--;
1237e048bb79Snicm 		b = sgr_b;
1238e048bb79Snicm 
1239e048bb79Snicm 		/* Type is M for press, m for release. */
12401f2e7474Snicm 		sgr_type = ch;
1241e048bb79Snicm 		if (sgr_type == 'm')
12424e5846a2Snicm 			b = 3;
12436c0777c5Snicm 
12449146dae2Snicm 		/*
12459146dae2Snicm 		 * Some terminals (like PuTTY 0.63) mistakenly send
12469146dae2Snicm 		 * button-release events for scroll-wheel button-press event.
12479146dae2Snicm 		 * Discard it before it reaches any program running inside
12489146dae2Snicm 		 * tmux.
12499146dae2Snicm 		 */
12504e5846a2Snicm 		if (sgr_type == 'm' && MOUSE_WHEEL(sgr_b))
12519146dae2Snicm 		    return (-2);
12526c0777c5Snicm 	} else
12536c0777c5Snicm 		return (-1);
12546fb64d94Snicm 
1255e048bb79Snicm 	/* Fill mouse event. */
1256a88e9db6Snicm 	m->lx = tty->mouse_last_x;
125785539586Snicm 	m->x = x;
1258a88e9db6Snicm 	m->ly = tty->mouse_last_y;
125985539586Snicm 	m->y = y;
1260bdacbbeeSnicm 	m->lb = tty->mouse_last_b;
1261e048bb79Snicm 	m->b = b;
1262e048bb79Snicm 	m->sgr_type = sgr_type;
1263e048bb79Snicm 	m->sgr_b = sgr_b;
12646fb64d94Snicm 
1265a88e9db6Snicm 	/* Update last mouse state. */
1266a88e9db6Snicm 	tty->mouse_last_x = x;
1267a88e9db6Snicm 	tty->mouse_last_y = y;
1268bdacbbeeSnicm 	tty->mouse_last_b = b;
1269a88e9db6Snicm 
1270888b88f4Snicm 	return (0);
1271311827fbSnicm }
1272f00ac6e6Snicm 
1273f00ac6e6Snicm /*
127490866723Snicm  * Handle OSC 52 clipboard input. Returns 0 for success, -1 for failure, 1 for
127590866723Snicm  * partial.
127690866723Snicm  */
127790866723Snicm static int
1278f9bfc3f8Snicm tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
127990866723Snicm {
1280f9bfc3f8Snicm 	struct client		*c = tty->client;
1281f9bfc3f8Snicm 	struct window_pane	*wp;
1282e3eeb0f8Snicm 	size_t			 end, terminator = 0, needed;
128390866723Snicm 	char			*copy, *out;
128490866723Snicm 	int			 outlen;
1285f9bfc3f8Snicm 	u_int			 i;
128690866723Snicm 
128790866723Snicm 	*size = 0;
128890866723Snicm 
1289ecd9db25Snicm 	/* First five bytes are always \033]52;. */
129090866723Snicm 	if (buf[0] != '\033')
129190866723Snicm 		return (-1);
129290866723Snicm 	if (len == 1)
129390866723Snicm 		return (1);
129490866723Snicm 	if (buf[1] != ']')
129590866723Snicm 		return (-1);
129690866723Snicm 	if (len == 2)
129790866723Snicm 		return (1);
129890866723Snicm 	if (buf[2] != '5')
129990866723Snicm 		return (-1);
130090866723Snicm 	if (len == 3)
130190866723Snicm 		return (1);
130290866723Snicm 	if (buf[3] != '2')
130390866723Snicm 		return (-1);
130490866723Snicm 	if (len == 4)
130590866723Snicm 		return (1);
130690866723Snicm 	if (buf[4] != ';')
130790866723Snicm 		return (-1);
130890866723Snicm 	if (len == 5)
130990866723Snicm 		return (1);
131090866723Snicm 
131190866723Snicm 	/* Find the terminator if any. */
131290866723Snicm 	for (end = 5; end < len; end++) {
131390866723Snicm 		if (buf[end] == '\007') {
131490866723Snicm 			terminator = 1;
131590866723Snicm 			break;
131690866723Snicm 		}
131790866723Snicm 		if (end > 5 && buf[end - 1] == '\033' && buf[end] == '\\') {
131890866723Snicm 			terminator = 2;
131990866723Snicm 			break;
132090866723Snicm 		}
132190866723Snicm 	}
132290866723Snicm 	if (end == len)
132390866723Snicm 		return (1);
13243d0c1d47Snicm 	*size = end + 1;
132590866723Snicm 
132690866723Snicm 	/* Skip the initial part. */
132790866723Snicm 	buf += 5;
132890866723Snicm 	end -= 5;
132990866723Snicm 
13309bd2c8e2Snicm 	/* Adjust end so that it points to the start of the terminator. */
13319bd2c8e2Snicm 	end -= terminator - 1;
13329bd2c8e2Snicm 
133390866723Snicm 	/* Get the second argument. */
133490866723Snicm 	while (end != 0 && *buf != ';') {
133590866723Snicm 		buf++;
133690866723Snicm 		end--;
133790866723Snicm 	}
133890866723Snicm 	if (end == 0 || end == 1)
133990866723Snicm 		return (0);
134090866723Snicm 	buf++;
134190866723Snicm 	end--;
134290866723Snicm 
1343b0386005Snicm 	/* If we did not request this, ignore it. */
1344b0386005Snicm 	if (~tty->flags & TTY_OSC52QUERY)
1345b0386005Snicm 		return (0);
1346b0386005Snicm 	tty->flags &= ~TTY_OSC52QUERY;
1347f9bfc3f8Snicm 	evtimer_del(&tty->clipboard_timer);
1348b0386005Snicm 
134990866723Snicm 	/* It has to be a string so copy it. */
135090866723Snicm 	copy = xmalloc(end + 1);
135190866723Snicm 	memcpy(copy, buf, end);
135290866723Snicm 	copy[end] = '\0';
135390866723Snicm 
135490866723Snicm 	/* Convert from base64. */
135590866723Snicm 	needed = (end / 4) * 3;
135690866723Snicm 	out = xmalloc(needed);
135790866723Snicm 	if ((outlen = b64_pton(copy, out, len)) == -1) {
135890866723Snicm 		free(out);
135990866723Snicm 		free(copy);
136090866723Snicm 		return (0);
136190866723Snicm 	}
136290866723Snicm 	free(copy);
136390866723Snicm 
1364f9bfc3f8Snicm 	/* Create a new paste buffer and forward to panes. */
136590866723Snicm 	log_debug("%s: %.*s", __func__, outlen, out);
1366f9bfc3f8Snicm 	if (c->flags & CLIENT_CLIPBOARDBUFFER) {
13674aafca52Snicm 		paste_add(NULL, out, outlen);
1368f9bfc3f8Snicm 		c->flags &= ~CLIENT_CLIPBOARDBUFFER;
1369f9bfc3f8Snicm 	}
1370f9bfc3f8Snicm 	for (i = 0; i < c->clipboard_npanes; i++) {
1371f9bfc3f8Snicm 		wp = window_pane_find_by_id(c->clipboard_panes[i]);
1372f9bfc3f8Snicm 		if (wp != NULL)
1373f9bfc3f8Snicm 			input_reply_clipboard(wp->event, out, outlen, "\033\\");
1374f9bfc3f8Snicm 	}
1375f9bfc3f8Snicm 	free(c->clipboard_panes);
1376f9bfc3f8Snicm 	c->clipboard_panes = NULL;
1377f9bfc3f8Snicm 	c->clipboard_npanes = 0;
137890866723Snicm 
137990866723Snicm 	return (0);
138090866723Snicm }
138190866723Snicm 
138290866723Snicm /*
1383e0697ebcSnicm  * Handle primary device attributes input. Returns 0 for success, -1 for
138425e90a40Snicm  * failure, 1 for partial.
1385f00ac6e6Snicm  */
1386f00ac6e6Snicm static int
1387f00ac6e6Snicm tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len,
1388f00ac6e6Snicm     size_t *size)
1389f00ac6e6Snicm {
13901f2e7474Snicm 	struct client	*c = tty->client;
1391f4ec6bcaSnicm 	int		*features = &c->term_features;
13922afb856aSnicm 	u_int		 i, n = 0;
1393e0697ebcSnicm 	char		 tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
1394f00ac6e6Snicm 
1395f00ac6e6Snicm 	*size = 0;
13966648e05cSnicm 	if (tty->flags & TTY_HAVEDA)
13976648e05cSnicm 		return (-1);
1398f00ac6e6Snicm 
1399e0697ebcSnicm 	/* First three bytes are always \033[?. */
1400f00ac6e6Snicm 	if (buf[0] != '\033')
1401f00ac6e6Snicm 		return (-1);
1402f00ac6e6Snicm 	if (len == 1)
1403f00ac6e6Snicm 		return (1);
1404f00ac6e6Snicm 	if (buf[1] != '[')
1405f00ac6e6Snicm 		return (-1);
1406f00ac6e6Snicm 	if (len == 2)
1407f00ac6e6Snicm 		return (1);
1408e0697ebcSnicm 	if (buf[2] != '?')
1409f00ac6e6Snicm 		return (-1);
1410f00ac6e6Snicm 	if (len == 3)
1411f00ac6e6Snicm 		return (1);
1412f00ac6e6Snicm 
1413d21788ceSnicm 	/* Copy the rest up to a c. */
1414e0697ebcSnicm 	for (i = 0; i < (sizeof tmp); i++) {
1415f00ac6e6Snicm 		if (3 + i == len)
1416f00ac6e6Snicm 			return (1);
1417ecd9db25Snicm 		if (buf[3 + i] == 'c')
1418ecd9db25Snicm 			break;
1419f00ac6e6Snicm 		tmp[i] = buf[3 + i];
1420f00ac6e6Snicm 	}
1421e0697ebcSnicm 	if (i == (sizeof tmp))
1422f00ac6e6Snicm 		return (-1);
1423f00ac6e6Snicm 	tmp[i] = '\0';
1424f00ac6e6Snicm 	*size = 4 + i;
1425f00ac6e6Snicm 
142625e90a40Snicm 	/* Convert all arguments to numbers. */
14272afb856aSnicm 	cp = tmp;
14282afb856aSnicm 	while ((next = strsep(&cp, ";")) != NULL) {
14292afb856aSnicm 		p[n] = strtoul(next, &endptr, 10);
1430be3ccdcbSnicm 		if (*endptr != '\0')
14312afb856aSnicm 			p[n] = 0;
1432e0697ebcSnicm 		if (++n == nitems(p))
1433e0697ebcSnicm 			break;
1434e0697ebcSnicm 	}
1435e0697ebcSnicm 
143627676e5cSnicm 	/* Add terminal features. */
1437e0697ebcSnicm 	switch (p[0]) {
143827676e5cSnicm 	case 61: /* level 1 */
143957c744a4Snicm 	case 62: /* level 2 */
144057c744a4Snicm 	case 63: /* level 3 */
144127676e5cSnicm 	case 64: /* level 4 */
144257c744a4Snicm 	case 65: /* level 5 */
1443e0697ebcSnicm 		for (i = 1; i < n; i++) {
1444e0697ebcSnicm 			log_debug("%s: DA feature: %d", c->name, p[i]);
1445e0697ebcSnicm 			if (p[i] == 4)
1446f4ec6bcaSnicm 				tty_add_features(features, "sixel", ",");
144727676e5cSnicm 			if (p[i] == 21)
144827676e5cSnicm 				tty_add_features(features, "margins", ",");
144927676e5cSnicm 			if (p[i] == 28)
145027676e5cSnicm 				tty_add_features(features, "rectfill", ",");
1451e0697ebcSnicm 		}
1452e0697ebcSnicm 		break;
1453e0697ebcSnicm 	}
1454e0697ebcSnicm 	log_debug("%s: received primary DA %.*s", c->name, (int)*size, buf);
1455e0697ebcSnicm 
1456e0697ebcSnicm 	tty_update_features(tty);
1457e0697ebcSnicm 	tty->flags |= TTY_HAVEDA;
1458e0697ebcSnicm 
1459e0697ebcSnicm 	return (0);
1460e0697ebcSnicm }
1461e0697ebcSnicm 
1462e0697ebcSnicm /*
1463e0697ebcSnicm  * Handle secondary device attributes input. Returns 0 for success, -1 for
1464e0697ebcSnicm  * failure, 1 for partial.
1465e0697ebcSnicm  */
1466e0697ebcSnicm static int
1467e0697ebcSnicm tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len,
1468e0697ebcSnicm     size_t *size)
1469e0697ebcSnicm {
1470e0697ebcSnicm 	struct client	*c = tty->client;
1471f4ec6bcaSnicm 	int		*features = &c->term_features;
1472e0697ebcSnicm 	u_int		 i, n = 0;
1473e0697ebcSnicm 	char		 tmp[128], *endptr, p[32] = { 0 }, *cp, *next;
1474e0697ebcSnicm 
1475e0697ebcSnicm 	*size = 0;
1476e0697ebcSnicm 	if (tty->flags & TTY_HAVEDA2)
1477e0697ebcSnicm 		return (-1);
1478e0697ebcSnicm 
1479e0697ebcSnicm 	/* First three bytes are always \033[>. */
1480e0697ebcSnicm 	if (buf[0] != '\033')
1481e0697ebcSnicm 		return (-1);
1482e0697ebcSnicm 	if (len == 1)
1483e0697ebcSnicm 		return (1);
1484e0697ebcSnicm 	if (buf[1] != '[')
1485e0697ebcSnicm 		return (-1);
1486e0697ebcSnicm 	if (len == 2)
1487e0697ebcSnicm 		return (1);
1488e0697ebcSnicm 	if (buf[2] != '>')
1489e0697ebcSnicm 		return (-1);
1490e0697ebcSnicm 	if (len == 3)
1491e0697ebcSnicm 		return (1);
1492e0697ebcSnicm 
1493d21788ceSnicm 	/* Copy the rest up to a c. */
1494e0697ebcSnicm 	for (i = 0; i < (sizeof tmp); i++) {
1495e0697ebcSnicm 		if (3 + i == len)
1496e0697ebcSnicm 			return (1);
1497e0697ebcSnicm 		if (buf[3 + i] == 'c')
1498e0697ebcSnicm 			break;
1499e0697ebcSnicm 		tmp[i] = buf[3 + i];
1500e0697ebcSnicm 	}
1501e0697ebcSnicm 	if (i == (sizeof tmp))
1502e0697ebcSnicm 		return (-1);
1503e0697ebcSnicm 	tmp[i] = '\0';
1504e0697ebcSnicm 	*size = 4 + i;
1505e0697ebcSnicm 
1506e0697ebcSnicm 	/* Convert all arguments to numbers. */
1507e0697ebcSnicm 	cp = tmp;
1508e0697ebcSnicm 	while ((next = strsep(&cp, ";")) != NULL) {
1509e0697ebcSnicm 		p[n] = strtoul(next, &endptr, 10);
1510e0697ebcSnicm 		if (*endptr != '\0')
1511e0697ebcSnicm 			p[n] = 0;
1512e0697ebcSnicm 		if (++n == nitems(p))
1513e0697ebcSnicm 			break;
15142afb856aSnicm 	}
1515f00ac6e6Snicm 
1516da4edc7fSnicm 	/*
1517da4edc7fSnicm 	 * Add terminal features. We add DECSLRM and DECFRA for some
1518da4edc7fSnicm 	 * identification codes here, notably 64 will catch VT520, even though
1519da4edc7fSnicm 	 * we can't use level 5 from DA because of VTE.
1520da4edc7fSnicm 	 */
15212afb856aSnicm 	switch (p[0]) {
152225e90a40Snicm 	case 'M': /* mintty */
1523f4ec6bcaSnicm 		tty_default_features(features, "mintty", 0);
152425e90a40Snicm 		break;
15255a160f88Snicm 	case 'T': /* tmux */
1526f4ec6bcaSnicm 		tty_default_features(features, "tmux", 0);
152725e90a40Snicm 		break;
152825e90a40Snicm 	case 'U': /* rxvt-unicode */
1529f4ec6bcaSnicm 		tty_default_features(features, "rxvt-unicode", 0);
153025e90a40Snicm 		break;
1531f00ac6e6Snicm 	}
153225e90a40Snicm 	log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf);
15336648e05cSnicm 
15345a160f88Snicm 	tty_update_features(tty);
1535e0697ebcSnicm 	tty->flags |= TTY_HAVEDA2;
15366648e05cSnicm 
1537f00ac6e6Snicm 	return (0);
1538f00ac6e6Snicm }
15396110b15cSnicm 
15406110b15cSnicm /*
1541ecd9db25Snicm  * Handle extended device attributes input. Returns 0 for success, -1 for
1542ecd9db25Snicm  * failure, 1 for partial.
15436110b15cSnicm  */
15446110b15cSnicm static int
1545ecd9db25Snicm tty_keys_extended_device_attributes(struct tty *tty, const char *buf,
1546ecd9db25Snicm     size_t len, size_t *size)
15476110b15cSnicm {
15486110b15cSnicm 	struct client	*c = tty->client;
1549f4ec6bcaSnicm 	int		*features = &c->term_features;
15506110b15cSnicm 	u_int		 i;
15518942cc58Snicm 	char		 tmp[128];
15526110b15cSnicm 
15536110b15cSnicm 	*size = 0;
1554ecd9db25Snicm 	if (tty->flags & TTY_HAVEXDA)
15556648e05cSnicm 		return (-1);
15566110b15cSnicm 
1557ecd9db25Snicm 	/* First four bytes are always \033P>|. */
15586110b15cSnicm 	if (buf[0] != '\033')
15596110b15cSnicm 		return (-1);
15606110b15cSnicm 	if (len == 1)
15616110b15cSnicm 		return (1);
1562ecd9db25Snicm 	if (buf[1] != 'P')
15636110b15cSnicm 		return (-1);
15646110b15cSnicm 	if (len == 2)
15656110b15cSnicm 		return (1);
1566ecd9db25Snicm 	if (buf[2] != '>')
15676648e05cSnicm 		return (-1);
15686648e05cSnicm 	if (len == 3)
15696648e05cSnicm 		return (1);
1570ecd9db25Snicm 	if (buf[3] != '|')
1571ecd9db25Snicm 		return (-1);
1572ecd9db25Snicm 	if (len == 4)
15736110b15cSnicm 		return (1);
1574ecd9db25Snicm 
1575d21788ceSnicm 	/* Copy the rest up to \033\. */
1576ecd9db25Snicm 	for (i = 0; i < (sizeof tmp) - 1; i++) {
1577ecd9db25Snicm 		if (4 + i == len)
1578ecd9db25Snicm 			return (1);
1579ecd9db25Snicm 		if (buf[4 + i - 1] == '\033' && buf[4 + i] == '\\')
1580ecd9db25Snicm 			break;
1581ecd9db25Snicm 		tmp[i] = buf[4 + i];
15826110b15cSnicm 	}
15836110b15cSnicm 	if (i == (sizeof tmp) - 1)
15846110b15cSnicm 		return (-1);
1585ecd9db25Snicm 	tmp[i - 1] = '\0';
1586ecd9db25Snicm 	*size = 5 + i;
15876110b15cSnicm 
15885a160f88Snicm 	/* Add terminal features. */
15898942cc58Snicm 	if (strncmp(tmp, "iTerm2 ", 7) == 0)
1590f4ec6bcaSnicm 		tty_default_features(features, "iTerm2", 0);
15918942cc58Snicm 	else if (strncmp(tmp, "tmux ", 5) == 0)
1592f4ec6bcaSnicm 		tty_default_features(features, "tmux", 0);
15938942cc58Snicm 	else if (strncmp(tmp, "XTerm(", 6) == 0)
1594f4ec6bcaSnicm 		tty_default_features(features, "XTerm", 0);
15958942cc58Snicm 	else if (strncmp(tmp, "mintty ", 7) == 0)
1596f4ec6bcaSnicm 		tty_default_features(features, "mintty", 0);
1597a65ab485Snicm 	else if (strncmp(tmp, "foot(", 5) == 0)
1598a65ab485Snicm 		tty_default_features(features, "foot", 0);
1599ecd9db25Snicm 	log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf);
16006648e05cSnicm 
16018942cc58Snicm 	free(c->term_type);
16028942cc58Snicm 	c->term_type = xstrdup(tmp);
16038942cc58Snicm 
16045a160f88Snicm 	tty_update_features(tty);
1605ecd9db25Snicm 	tty->flags |= TTY_HAVEXDA;
16066648e05cSnicm 
16076110b15cSnicm 	return (0);
16086110b15cSnicm }
1609d21788ceSnicm 
1610d21788ceSnicm /*
1611d21788ceSnicm  * Handle foreground or background input. Returns 0 for success, -1 for
1612d21788ceSnicm  * failure, 1 for partial.
1613d21788ceSnicm  */
1614891e2565Snicm int
1615891e2565Snicm tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size,
1616891e2565Snicm     int *fg, int *bg)
1617d21788ceSnicm {
1618d21788ceSnicm 	struct client	*c = tty->client;
1619d21788ceSnicm 	u_int		 i;
1620d21788ceSnicm 	char		 tmp[128];
1621d21788ceSnicm 	int		 n;
1622d21788ceSnicm 
1623d21788ceSnicm 	*size = 0;
1624d21788ceSnicm 
1625d21788ceSnicm 	/* First four bytes are always \033]1 and 0 or 1 and ;. */
1626d21788ceSnicm 	if (buf[0] != '\033')
1627d21788ceSnicm 		return (-1);
1628d21788ceSnicm 	if (len == 1)
1629d21788ceSnicm 		return (1);
1630d21788ceSnicm 	if (buf[1] != ']')
1631d21788ceSnicm 		return (-1);
1632d21788ceSnicm 	if (len == 2)
1633d21788ceSnicm 		return (1);
1634d21788ceSnicm 	if (buf[2] != '1')
1635d21788ceSnicm 		return (-1);
1636d21788ceSnicm 	if (len == 3)
1637d21788ceSnicm 		return (1);
1638d21788ceSnicm 	if (buf[3] != '0' && buf[3] != '1')
1639d21788ceSnicm 		return (-1);
1640d21788ceSnicm 	if (len == 4)
1641d21788ceSnicm 		return (1);
1642d21788ceSnicm 	if (buf[4] != ';')
1643d21788ceSnicm 		return (-1);
1644d21788ceSnicm 	if (len == 5)
1645d21788ceSnicm 		return (1);
1646d21788ceSnicm 
1647ce2a7883Snicm 	/* Copy the rest up to \033\ or \007. */
1648d21788ceSnicm 	for (i = 0; i < (sizeof tmp) - 1; i++) {
1649d21788ceSnicm 		if (5 + i == len)
1650d21788ceSnicm 			return (1);
1651d21788ceSnicm 		if (buf[5 + i - 1] == '\033' && buf[5 + i] == '\\')
1652d21788ceSnicm 			break;
1653ce2a7883Snicm 		if (buf[5 + i] == '\007')
1654ce2a7883Snicm 			break;
1655d21788ceSnicm 		tmp[i] = buf[5 + i];
1656d21788ceSnicm 	}
1657d21788ceSnicm 	if (i == (sizeof tmp) - 1)
1658d21788ceSnicm 		return (-1);
1659affa3703Snicm 	if (tmp[i - 1] == '\033')
1660ce2a7883Snicm 		tmp[i - 1] = '\0';
1661affa3703Snicm 	else
1662affa3703Snicm 		tmp[i] = '\0';
1663affa3703Snicm 	*size = 6 + i;
1664d21788ceSnicm 
1665d21788ceSnicm 	n = colour_parseX11(tmp);
1666d21788ceSnicm 	if (n != -1 && buf[3] == '0') {
1667891e2565Snicm 		if (c != NULL)
1668891e2565Snicm 			log_debug("%s fg is %s", c->name, colour_tostring(n));
1669891e2565Snicm 		else
1670891e2565Snicm 			log_debug("fg is %s", colour_tostring(n));
1671891e2565Snicm 		*fg = n;
1672d21788ceSnicm 	} else if (n != -1) {
1673891e2565Snicm 		if (c != NULL)
1674891e2565Snicm 			log_debug("%s bg is %s", c->name, colour_tostring(n));
1675891e2565Snicm 		else
1676891e2565Snicm 			log_debug("bg is %s", colour_tostring(n));
1677891e2565Snicm 		*bg = n;
1678d21788ceSnicm 	}
1679d21788ceSnicm 
1680d21788ceSnicm 	return (0);
1681d21788ceSnicm }
1682