Line | Count | Source |
1 | | /* $OpenBSD: tty-keys.c,v 1.222 2026/10/26 10:14:59 nicm Exp $ */ |
2 | | |
3 | | /* |
4 | | * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> |
5 | | * |
6 | | * Permission to use, copy, modify, and distribute this software for any |
7 | | * purpose with or without fee is hereby granted, provided that the above |
8 | | * copyright notice and this permission notice appear in all copies. |
9 | | * |
10 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
15 | | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
16 | | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | | */ |
18 | | |
19 | | #include <sys/types.h> |
20 | | #include <sys/time.h> |
21 | | |
22 | | #include <netinet/in.h> |
23 | | |
24 | | #include <ctype.h> |
25 | | #include <limits.h> |
26 | | #include <resolv.h> |
27 | | #include <stdlib.h> |
28 | | #include <string.h> |
29 | | #include <termios.h> |
30 | | #include <unistd.h> |
31 | | |
32 | | #include "tmux.h" |
33 | | |
34 | | /* |
35 | | * Handle keys input from the outside terminal. tty_default_*_keys[] are a base |
36 | | * table of supported keys which are looked up in terminfo(5) and translated |
37 | | * into a ternary tree. |
38 | | */ |
39 | | |
40 | | /* |
41 | | * A key or terminal reply parsed from the input buffer. A reply is parsed |
42 | | * without side effects and applied only once it is complete and not part of |
43 | | * a longer key. |
44 | | */ |
45 | | struct tty_key_parse { |
46 | | key_code key; |
47 | | size_t size; |
48 | | struct mouse_event m; |
49 | | |
50 | | int flags; |
51 | 0 | #define TTY_KEY_PARSE_NO_META 0x1 |
52 | | }; |
53 | | |
54 | | static void tty_keys_add1(struct tty_key **, const char *, key_code); |
55 | | static void tty_keys_add(struct tty *, const char *, key_code); |
56 | | static void tty_keys_free1(struct tty_key *); |
57 | | static struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t, |
58 | | size_t *); |
59 | | static struct tty_key *tty_keys_find(struct tty *, const char *, size_t, |
60 | | size_t *); |
61 | | static void tty_keys_callback(int, short, void *); |
62 | | static int tty_keys_extended_key(struct tty *, const char *, size_t, |
63 | | size_t *, key_code *); |
64 | | static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *, |
65 | | struct mouse_event *); |
66 | | static int tty_keys_clipboard(struct tty *, const char *, size_t, |
67 | | size_t *, int); |
68 | | static int tty_keys_device_attributes(struct tty *, const char *, size_t, |
69 | | size_t *, int); |
70 | | static int tty_keys_device_attributes2(struct tty *, const char *, size_t, |
71 | | size_t *, int); |
72 | | static int tty_keys_extended_device_attributes(struct tty *, const char *, |
73 | | size_t, size_t *, int); |
74 | | static int tty_keys_sync(struct tty *, const char *, size_t, size_t *, |
75 | | int); |
76 | | #ifdef ENABLE_IMAGES |
77 | | static int tty_keys_kitty_graphics(struct tty *, const char *, size_t, |
78 | | size_t *, int); |
79 | | #endif |
80 | | static int tty_keys_colours1(struct tty *, const char *, size_t, size_t *, |
81 | | int); |
82 | | static int tty_keys_palette(struct tty *, const char *, size_t, size_t *, |
83 | | int); |
84 | | static int tty_keys_winsz(struct tty *, const char *, size_t, size_t *, |
85 | | int); |
86 | | static int tty_keys_next1(struct tty *, const char *, size_t, |
87 | | struct tty_key_parse *, int); |
88 | | |
89 | | /* Terminal replies, in the order they are checked. */ |
90 | | static const struct { |
91 | | key_code key; |
92 | | int (*fn)(struct tty *, const char *, size_t, size_t *, int); |
93 | | } tty_keys_replies[] = { |
94 | | { KEYC_REPORT_CLIPBOARD, tty_keys_clipboard }, |
95 | | { KEYC_REPORT_SYNC, tty_keys_sync }, |
96 | | { KEYC_REPORT_DA, tty_keys_device_attributes }, |
97 | | { KEYC_REPORT_DA2, tty_keys_device_attributes2 }, |
98 | | { KEYC_REPORT_XDA, tty_keys_extended_device_attributes }, |
99 | | { KEYC_REPORT_COLOURS, tty_keys_colours1 }, |
100 | | { KEYC_REPORT_PALETTE, tty_keys_palette }, |
101 | | { KEYC_REPORT_WINSZ, tty_keys_winsz }, |
102 | | #ifdef ENABLE_IMAGES |
103 | | { KEYC_REPORT_KITTY_GRAPHICS, tty_keys_kitty_graphics } |
104 | | #endif |
105 | | }; |
106 | | |
107 | | /* A key tree entry. */ |
108 | | struct tty_key { |
109 | | char ch; |
110 | | key_code key; |
111 | | |
112 | | struct tty_key *left; |
113 | | struct tty_key *right; |
114 | | |
115 | | struct tty_key *next; |
116 | | }; |
117 | | |
118 | | /* Default raw keys. */ |
119 | | struct tty_default_key_raw { |
120 | | const char *string; |
121 | | key_code key; |
122 | | }; |
123 | | static const struct tty_default_key_raw tty_default_raw_keys[] = { |
124 | | /* Application escape. */ |
125 | | { "\033O[", '\033' }, |
126 | | |
127 | | /* |
128 | | * Numeric keypad. Just use the vt100 escape sequences here and always |
129 | | * put the terminal into keypad_xmit mode. Translation of numbers |
130 | | * mode/applications mode is done in input-keys.c. |
131 | | */ |
132 | | { "\033Oo", KEYC_KP_SLASH|KEYC_KEYPAD }, |
133 | | { "\033Oj", KEYC_KP_STAR|KEYC_KEYPAD }, |
134 | | { "\033Om", KEYC_KP_MINUS|KEYC_KEYPAD }, |
135 | | { "\033Ow", KEYC_KP_SEVEN|KEYC_KEYPAD }, |
136 | | { "\033Ox", KEYC_KP_EIGHT|KEYC_KEYPAD }, |
137 | | { "\033Oy", KEYC_KP_NINE|KEYC_KEYPAD }, |
138 | | { "\033Ok", KEYC_KP_PLUS|KEYC_KEYPAD }, |
139 | | { "\033Ot", KEYC_KP_FOUR|KEYC_KEYPAD }, |
140 | | { "\033Ou", KEYC_KP_FIVE|KEYC_KEYPAD }, |
141 | | { "\033Ov", KEYC_KP_SIX|KEYC_KEYPAD }, |
142 | | { "\033Oq", KEYC_KP_ONE|KEYC_KEYPAD }, |
143 | | { "\033Or", KEYC_KP_TWO|KEYC_KEYPAD }, |
144 | | { "\033Os", KEYC_KP_THREE|KEYC_KEYPAD }, |
145 | | { "\033OM", KEYC_KP_ENTER|KEYC_KEYPAD }, |
146 | | { "\033Op", KEYC_KP_ZERO|KEYC_KEYPAD }, |
147 | | { "\033On", KEYC_KP_PERIOD|KEYC_KEYPAD }, |
148 | | |
149 | | /* Arrow keys. */ |
150 | | { "\033OA", KEYC_UP|KEYC_CURSOR }, |
151 | | { "\033OB", KEYC_DOWN|KEYC_CURSOR }, |
152 | | { "\033OC", KEYC_RIGHT|KEYC_CURSOR }, |
153 | | { "\033OD", KEYC_LEFT|KEYC_CURSOR }, |
154 | | |
155 | | { "\033[A", KEYC_UP|KEYC_CURSOR }, |
156 | | { "\033[B", KEYC_DOWN|KEYC_CURSOR }, |
157 | | { "\033[C", KEYC_RIGHT|KEYC_CURSOR }, |
158 | | { "\033[D", KEYC_LEFT|KEYC_CURSOR }, |
159 | | |
160 | | /* |
161 | | * Meta arrow keys. These do not get the IMPLIED_META flag so they |
162 | | * don't match the xterm-style meta keys in the output tree - Escape+Up |
163 | | * should stay as Escape+Up and not become M-Up. |
164 | | */ |
165 | | { "\033\033OA", KEYC_UP|KEYC_CURSOR|KEYC_META }, |
166 | | { "\033\033OB", KEYC_DOWN|KEYC_CURSOR|KEYC_META }, |
167 | | { "\033\033OC", KEYC_RIGHT|KEYC_CURSOR|KEYC_META }, |
168 | | { "\033\033OD", KEYC_LEFT|KEYC_CURSOR|KEYC_META }, |
169 | | |
170 | | { "\033\033[A", KEYC_UP|KEYC_CURSOR|KEYC_META }, |
171 | | { "\033\033[B", KEYC_DOWN|KEYC_CURSOR|KEYC_META }, |
172 | | { "\033\033[C", KEYC_RIGHT|KEYC_CURSOR|KEYC_META }, |
173 | | { "\033\033[D", KEYC_LEFT|KEYC_CURSOR|KEYC_META }, |
174 | | |
175 | | /* Other xterm keys. */ |
176 | | { "\033OH", KEYC_HOME }, |
177 | | { "\033OF", KEYC_END }, |
178 | | |
179 | | { "\033\033OH", KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, |
180 | | { "\033\033OF", KEYC_END|KEYC_META|KEYC_IMPLIED_META }, |
181 | | |
182 | | { "\033[H", KEYC_HOME }, |
183 | | { "\033[F", KEYC_END }, |
184 | | |
185 | | { "\033\033[H", KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, |
186 | | { "\033\033[F", KEYC_END|KEYC_META|KEYC_IMPLIED_META }, |
187 | | |
188 | | /* rxvt arrow keys. */ |
189 | | { "\033Oa", KEYC_UP|KEYC_CTRL }, |
190 | | { "\033Ob", KEYC_DOWN|KEYC_CTRL }, |
191 | | { "\033Oc", KEYC_RIGHT|KEYC_CTRL }, |
192 | | { "\033Od", KEYC_LEFT|KEYC_CTRL }, |
193 | | |
194 | | { "\033[a", KEYC_UP|KEYC_SHIFT }, |
195 | | { "\033[b", KEYC_DOWN|KEYC_SHIFT }, |
196 | | { "\033[c", KEYC_RIGHT|KEYC_SHIFT }, |
197 | | { "\033[d", KEYC_LEFT|KEYC_SHIFT }, |
198 | | |
199 | | /* rxvt function keys. */ |
200 | | { "\033[11~", KEYC_F1 }, |
201 | | { "\033[12~", KEYC_F2 }, |
202 | | { "\033[13~", KEYC_F3 }, |
203 | | { "\033[14~", KEYC_F4 }, |
204 | | { "\033[15~", KEYC_F5 }, |
205 | | { "\033[17~", KEYC_F6 }, |
206 | | { "\033[18~", KEYC_F7 }, |
207 | | { "\033[19~", KEYC_F8 }, |
208 | | { "\033[20~", KEYC_F9 }, |
209 | | { "\033[21~", KEYC_F10 }, |
210 | | |
211 | | { "\033[23~", KEYC_F1|KEYC_SHIFT }, |
212 | | { "\033[24~", KEYC_F2|KEYC_SHIFT }, |
213 | | { "\033[25~", KEYC_F3|KEYC_SHIFT }, |
214 | | { "\033[26~", KEYC_F4|KEYC_SHIFT }, |
215 | | { "\033[28~", KEYC_F5|KEYC_SHIFT }, |
216 | | { "\033[29~", KEYC_F6|KEYC_SHIFT }, |
217 | | { "\033[31~", KEYC_F7|KEYC_SHIFT }, |
218 | | { "\033[32~", KEYC_F8|KEYC_SHIFT }, |
219 | | { "\033[33~", KEYC_F9|KEYC_SHIFT }, |
220 | | { "\033[34~", KEYC_F10|KEYC_SHIFT }, |
221 | | { "\033[23$", KEYC_F11|KEYC_SHIFT }, |
222 | | { "\033[24$", KEYC_F12|KEYC_SHIFT }, |
223 | | |
224 | | { "\033[11^", KEYC_F1|KEYC_CTRL }, |
225 | | { "\033[12^", KEYC_F2|KEYC_CTRL }, |
226 | | { "\033[13^", KEYC_F3|KEYC_CTRL }, |
227 | | { "\033[14^", KEYC_F4|KEYC_CTRL }, |
228 | | { "\033[15^", KEYC_F5|KEYC_CTRL }, |
229 | | { "\033[17^", KEYC_F6|KEYC_CTRL }, |
230 | | { "\033[18^", KEYC_F7|KEYC_CTRL }, |
231 | | { "\033[19^", KEYC_F8|KEYC_CTRL }, |
232 | | { "\033[20^", KEYC_F9|KEYC_CTRL }, |
233 | | { "\033[21^", KEYC_F10|KEYC_CTRL }, |
234 | | { "\033[23^", KEYC_F11|KEYC_CTRL }, |
235 | | { "\033[24^", KEYC_F12|KEYC_CTRL }, |
236 | | |
237 | | { "\033[11@", KEYC_F1|KEYC_CTRL|KEYC_SHIFT }, |
238 | | { "\033[12@", KEYC_F2|KEYC_CTRL|KEYC_SHIFT }, |
239 | | { "\033[13@", KEYC_F3|KEYC_CTRL|KEYC_SHIFT }, |
240 | | { "\033[14@", KEYC_F4|KEYC_CTRL|KEYC_SHIFT }, |
241 | | { "\033[15@", KEYC_F5|KEYC_CTRL|KEYC_SHIFT }, |
242 | | { "\033[17@", KEYC_F6|KEYC_CTRL|KEYC_SHIFT }, |
243 | | { "\033[18@", KEYC_F7|KEYC_CTRL|KEYC_SHIFT }, |
244 | | { "\033[19@", KEYC_F8|KEYC_CTRL|KEYC_SHIFT }, |
245 | | { "\033[20@", KEYC_F9|KEYC_CTRL|KEYC_SHIFT }, |
246 | | { "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT }, |
247 | | { "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT }, |
248 | | { "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT }, |
249 | | |
250 | | /* Focus tracking. */ |
251 | | { "\033[I", KEYC_FOCUS_IN }, |
252 | | { "\033[O", KEYC_FOCUS_OUT }, |
253 | | |
254 | | /* Paste keys. */ |
255 | | { "\033[200~", KEYC_PASTE_START|KEYC_IMPLIED_META }, |
256 | | { "\033[201~", KEYC_PASTE_END|KEYC_IMPLIED_META }, |
257 | | |
258 | | /* Extended keys. */ |
259 | | { "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT }, |
260 | | |
261 | | /* Theme reporting. */ |
262 | | { "\033[?997;1n", KEYC_REPORT_DARK_THEME }, |
263 | | { "\033[?997;2n", KEYC_REPORT_LIGHT_THEME }, |
264 | | }; |
265 | | |
266 | | /* Default xterm keys. */ |
267 | | struct tty_default_key_xterm { |
268 | | const char *template; |
269 | | key_code key; |
270 | | }; |
271 | | static const struct tty_default_key_xterm tty_default_xterm_keys[] = { |
272 | | { "\033[1;_P", KEYC_F1 }, |
273 | | { "\033O1;_P", KEYC_F1 }, |
274 | | { "\033O_P", KEYC_F1 }, |
275 | | { "\033[1;_Q", KEYC_F2 }, |
276 | | { "\033O1;_Q", KEYC_F2 }, |
277 | | { "\033O_Q", KEYC_F2 }, |
278 | | { "\033[1;_R", KEYC_F3 }, |
279 | | { "\033O1;_R", KEYC_F3 }, |
280 | | { "\033O_R", KEYC_F3 }, |
281 | | { "\033[1;_S", KEYC_F4 }, |
282 | | { "\033O1;_S", KEYC_F4 }, |
283 | | { "\033O_S", KEYC_F4 }, |
284 | | { "\033[15;_~", KEYC_F5 }, |
285 | | { "\033[17;_~", KEYC_F6 }, |
286 | | { "\033[18;_~", KEYC_F7 }, |
287 | | { "\033[19;_~", KEYC_F8 }, |
288 | | { "\033[20;_~", KEYC_F9 }, |
289 | | { "\033[21;_~", KEYC_F10 }, |
290 | | { "\033[23;_~", KEYC_F11 }, |
291 | | { "\033[24;_~", KEYC_F12 }, |
292 | | { "\033[1;_A", KEYC_UP }, |
293 | | { "\033[1;_B", KEYC_DOWN }, |
294 | | { "\033[1;_C", KEYC_RIGHT }, |
295 | | { "\033[1;_D", KEYC_LEFT }, |
296 | | { "\033[1;_H", KEYC_HOME }, |
297 | | { "\033[1;_F", KEYC_END }, |
298 | | { "\033[5;_~", KEYC_PPAGE }, |
299 | | { "\033[6;_~", KEYC_NPAGE }, |
300 | | { "\033[2;_~", KEYC_IC }, |
301 | | { "\033[3;_~", KEYC_DC }, |
302 | | }; |
303 | | static const key_code tty_default_xterm_modifiers[] = { |
304 | | 0, |
305 | | 0, |
306 | | KEYC_SHIFT, |
307 | | KEYC_META|KEYC_IMPLIED_META, |
308 | | KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META, |
309 | | KEYC_CTRL, |
310 | | KEYC_SHIFT|KEYC_CTRL, |
311 | | KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, |
312 | | KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL, |
313 | | KEYC_META|KEYC_IMPLIED_META |
314 | | }; |
315 | | |
316 | | /* |
317 | | * Default terminfo(5) keys. Any keys that have builtin modifiers (that is, |
318 | | * where the key itself contains the modifiers) has the KEYC_XTERM flag set so |
319 | | * a leading escape is not treated as meta (and probably removed). |
320 | | */ |
321 | | struct tty_default_key_code { |
322 | | enum tty_code_code code; |
323 | | key_code key; |
324 | | }; |
325 | | static const struct tty_default_key_code tty_default_code_keys[] = { |
326 | | /* Function keys. */ |
327 | | { TTYC_KF1, KEYC_F1 }, |
328 | | { TTYC_KF2, KEYC_F2 }, |
329 | | { TTYC_KF3, KEYC_F3 }, |
330 | | { TTYC_KF4, KEYC_F4 }, |
331 | | { TTYC_KF5, KEYC_F5 }, |
332 | | { TTYC_KF6, KEYC_F6 }, |
333 | | { TTYC_KF7, KEYC_F7 }, |
334 | | { TTYC_KF8, KEYC_F8 }, |
335 | | { TTYC_KF9, KEYC_F9 }, |
336 | | { TTYC_KF10, KEYC_F10 }, |
337 | | { TTYC_KF11, KEYC_F11 }, |
338 | | { TTYC_KF12, KEYC_F12 }, |
339 | | |
340 | | { TTYC_KF13, KEYC_F1|KEYC_SHIFT }, |
341 | | { TTYC_KF14, KEYC_F2|KEYC_SHIFT }, |
342 | | { TTYC_KF15, KEYC_F3|KEYC_SHIFT }, |
343 | | { TTYC_KF16, KEYC_F4|KEYC_SHIFT }, |
344 | | { TTYC_KF17, KEYC_F5|KEYC_SHIFT }, |
345 | | { TTYC_KF18, KEYC_F6|KEYC_SHIFT }, |
346 | | { TTYC_KF19, KEYC_F7|KEYC_SHIFT }, |
347 | | { TTYC_KF20, KEYC_F8|KEYC_SHIFT }, |
348 | | { TTYC_KF21, KEYC_F9|KEYC_SHIFT }, |
349 | | { TTYC_KF22, KEYC_F10|KEYC_SHIFT }, |
350 | | { TTYC_KF23, KEYC_F11|KEYC_SHIFT }, |
351 | | { TTYC_KF24, KEYC_F12|KEYC_SHIFT }, |
352 | | |
353 | | { TTYC_KF25, KEYC_F1|KEYC_CTRL }, |
354 | | { TTYC_KF26, KEYC_F2|KEYC_CTRL }, |
355 | | { TTYC_KF27, KEYC_F3|KEYC_CTRL }, |
356 | | { TTYC_KF28, KEYC_F4|KEYC_CTRL }, |
357 | | { TTYC_KF29, KEYC_F5|KEYC_CTRL }, |
358 | | { TTYC_KF30, KEYC_F6|KEYC_CTRL }, |
359 | | { TTYC_KF31, KEYC_F7|KEYC_CTRL }, |
360 | | { TTYC_KF32, KEYC_F8|KEYC_CTRL }, |
361 | | { TTYC_KF33, KEYC_F9|KEYC_CTRL }, |
362 | | { TTYC_KF34, KEYC_F10|KEYC_CTRL }, |
363 | | { TTYC_KF35, KEYC_F11|KEYC_CTRL }, |
364 | | { TTYC_KF36, KEYC_F12|KEYC_CTRL }, |
365 | | |
366 | | { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL }, |
367 | | { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL }, |
368 | | { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL }, |
369 | | { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL }, |
370 | | { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL }, |
371 | | { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL }, |
372 | | { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL }, |
373 | | { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL }, |
374 | | { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL }, |
375 | | { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL }, |
376 | | { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL }, |
377 | | { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL }, |
378 | | |
379 | | { TTYC_KF49, KEYC_F1|KEYC_META|KEYC_IMPLIED_META }, |
380 | | { TTYC_KF50, KEYC_F2|KEYC_META|KEYC_IMPLIED_META }, |
381 | | { TTYC_KF51, KEYC_F3|KEYC_META|KEYC_IMPLIED_META }, |
382 | | { TTYC_KF52, KEYC_F4|KEYC_META|KEYC_IMPLIED_META }, |
383 | | { TTYC_KF53, KEYC_F5|KEYC_META|KEYC_IMPLIED_META }, |
384 | | { TTYC_KF54, KEYC_F6|KEYC_META|KEYC_IMPLIED_META }, |
385 | | { TTYC_KF55, KEYC_F7|KEYC_META|KEYC_IMPLIED_META }, |
386 | | { TTYC_KF56, KEYC_F8|KEYC_META|KEYC_IMPLIED_META }, |
387 | | { TTYC_KF57, KEYC_F9|KEYC_META|KEYC_IMPLIED_META }, |
388 | | { TTYC_KF58, KEYC_F10|KEYC_META|KEYC_IMPLIED_META }, |
389 | | { TTYC_KF59, KEYC_F11|KEYC_META|KEYC_IMPLIED_META }, |
390 | | { TTYC_KF60, KEYC_F12|KEYC_META|KEYC_IMPLIED_META }, |
391 | | |
392 | | { TTYC_KF61, KEYC_F1|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, |
393 | | { TTYC_KF62, KEYC_F2|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, |
394 | | { TTYC_KF63, KEYC_F3|KEYC_META|KEYC_IMPLIED_META|KEYC_SHIFT }, |
395 | | |
396 | | { TTYC_KICH1, KEYC_IC }, |
397 | | { TTYC_KDCH1, KEYC_DC }, |
398 | | { TTYC_KHOME, KEYC_HOME }, |
399 | | { TTYC_KEND, KEYC_END }, |
400 | | { TTYC_KNP, KEYC_NPAGE }, |
401 | | { TTYC_KPP, KEYC_PPAGE }, |
402 | | { TTYC_KCBT, KEYC_BTAB }, |
403 | | |
404 | | /* Arrow keys from terminfo. */ |
405 | | { TTYC_KCUU1, KEYC_UP|KEYC_CURSOR }, |
406 | | { TTYC_KCUD1, KEYC_DOWN|KEYC_CURSOR }, |
407 | | { TTYC_KCUB1, KEYC_LEFT|KEYC_CURSOR }, |
408 | | { TTYC_KCUF1, KEYC_RIGHT|KEYC_CURSOR }, |
409 | | |
410 | | /* Key and modifier capabilities. */ |
411 | | { TTYC_KDC2, KEYC_DC|KEYC_SHIFT }, |
412 | | { TTYC_KDC3, KEYC_DC|KEYC_META|KEYC_IMPLIED_META }, |
413 | | { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, |
414 | | { TTYC_KDC5, KEYC_DC|KEYC_CTRL }, |
415 | | { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL }, |
416 | | { TTYC_KDC7, KEYC_DC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, |
417 | | { TTYC_KIND, KEYC_DOWN|KEYC_SHIFT }, |
418 | | { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT }, |
419 | | { TTYC_KDN3, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META }, |
420 | | { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, |
421 | | { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL }, |
422 | | { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL }, |
423 | | { TTYC_KDN7, KEYC_DOWN|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, |
424 | | { TTYC_KEND2, KEYC_END|KEYC_SHIFT }, |
425 | | { TTYC_KEND3, KEYC_END|KEYC_META|KEYC_IMPLIED_META }, |
426 | | { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, |
427 | | { TTYC_KEND5, KEYC_END|KEYC_CTRL }, |
428 | | { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL }, |
429 | | { TTYC_KEND7, KEYC_END|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, |
430 | | { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT }, |
431 | | { TTYC_KHOM3, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META }, |
432 | | { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, |
433 | | { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL }, |
434 | | { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL }, |
435 | | { TTYC_KHOM7, KEYC_HOME|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, |
436 | | { TTYC_KIC2, KEYC_IC|KEYC_SHIFT }, |
437 | | { TTYC_KIC3, KEYC_IC|KEYC_META|KEYC_IMPLIED_META }, |
438 | | { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, |
439 | | { TTYC_KIC5, KEYC_IC|KEYC_CTRL }, |
440 | | { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL }, |
441 | | { TTYC_KIC7, KEYC_IC|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, |
442 | | { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT }, |
443 | | { TTYC_KLFT3, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META }, |
444 | | { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, |
445 | | { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL }, |
446 | | { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL }, |
447 | | { TTYC_KLFT7, KEYC_LEFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, |
448 | | { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT }, |
449 | | { TTYC_KNXT3, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META }, |
450 | | { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, |
451 | | { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL }, |
452 | | { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL }, |
453 | | { TTYC_KNXT7, KEYC_NPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, |
454 | | { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT }, |
455 | | { TTYC_KPRV3, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META }, |
456 | | { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, |
457 | | { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL }, |
458 | | { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL }, |
459 | | { TTYC_KPRV7, KEYC_PPAGE|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, |
460 | | { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT }, |
461 | | { TTYC_KRIT3, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META }, |
462 | | { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, |
463 | | { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL }, |
464 | | { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL }, |
465 | | { TTYC_KRIT7, KEYC_RIGHT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, |
466 | | { TTYC_KRI, KEYC_UP|KEYC_SHIFT }, |
467 | | { TTYC_KUP2, KEYC_UP|KEYC_SHIFT }, |
468 | | { TTYC_KUP3, KEYC_UP|KEYC_META|KEYC_IMPLIED_META }, |
469 | | { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META }, |
470 | | { TTYC_KUP5, KEYC_UP|KEYC_CTRL }, |
471 | | { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL }, |
472 | | { TTYC_KUP7, KEYC_UP|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL }, |
473 | | }; |
474 | | |
475 | | /* Add key to tree. */ |
476 | | static void |
477 | | tty_keys_add(struct tty *tty, const char *s, key_code key) |
478 | 0 | { |
479 | 0 | struct tty_key *tk; |
480 | 0 | size_t size; |
481 | 0 | const char *keystr; |
482 | |
|
483 | 0 | keystr = key_string_lookup_key(key, 1); |
484 | 0 | if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { |
485 | 0 | log_debug("new key %s: 0x%llx (%s)", s, key, keystr); |
486 | 0 | tty_keys_add1(&tty->key_tree, s, key); |
487 | 0 | } else { |
488 | 0 | log_debug("replacing key %s: 0x%llx (%s)", s, key, keystr); |
489 | 0 | tk->key = key; |
490 | 0 | } |
491 | 0 | } |
492 | | |
493 | | /* Add next node to the tree. */ |
494 | | static void |
495 | | tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) |
496 | 0 | { |
497 | 0 | struct tty_key *tk; |
498 | | |
499 | | /* Allocate a tree entry if there isn't one already. */ |
500 | 0 | tk = *tkp; |
501 | 0 | if (tk == NULL) { |
502 | 0 | tk = *tkp = xcalloc(1, sizeof *tk); |
503 | 0 | tk->ch = *s; |
504 | 0 | tk->key = KEYC_UNKNOWN; |
505 | 0 | } |
506 | | |
507 | | /* Find the next entry. */ |
508 | 0 | if (*s == tk->ch) { |
509 | | /* Move forward in string. */ |
510 | 0 | s++; |
511 | | |
512 | | /* If this is the end of the string, no more is necessary. */ |
513 | 0 | if (*s == '\0') { |
514 | 0 | tk->key = key; |
515 | 0 | return; |
516 | 0 | } |
517 | | |
518 | | /* Use the child tree for the next character. */ |
519 | 0 | tkp = &tk->next; |
520 | 0 | } else { |
521 | 0 | if (*s < tk->ch) |
522 | 0 | tkp = &tk->left; |
523 | 0 | else if (*s > tk->ch) |
524 | 0 | tkp = &tk->right; |
525 | 0 | } |
526 | | |
527 | | /* And recurse to add it. */ |
528 | 0 | tty_keys_add1(tkp, s, key); |
529 | 0 | } |
530 | | |
531 | | /* Initialise a key tree from the table. */ |
532 | | void |
533 | | tty_keys_build(struct tty *tty) |
534 | 0 | { |
535 | 0 | const struct tty_default_key_raw *tdkr; |
536 | 0 | const struct tty_default_key_xterm *tdkx; |
537 | 0 | const struct tty_default_key_code *tdkc; |
538 | 0 | u_int i, j; |
539 | 0 | const char *s; |
540 | 0 | struct options_entry *o; |
541 | 0 | union options_value *ov; |
542 | 0 | char copy[16]; |
543 | 0 | key_code key; |
544 | |
|
545 | 0 | if (tty->key_tree != NULL) |
546 | 0 | tty_keys_free(tty); |
547 | 0 | tty->key_tree = NULL; |
548 | |
|
549 | 0 | for (i = 0; i < nitems(tty_default_xterm_keys); i++) { |
550 | 0 | tdkx = &tty_default_xterm_keys[i]; |
551 | 0 | for (j = 2; j < nitems(tty_default_xterm_modifiers); j++) { |
552 | 0 | strlcpy(copy, tdkx->template, sizeof copy); |
553 | 0 | copy[strcspn(copy, "_")] = '0' + j; |
554 | |
|
555 | 0 | key = tdkx->key|tty_default_xterm_modifiers[j]; |
556 | 0 | tty_keys_add(tty, copy, key); |
557 | 0 | } |
558 | 0 | } |
559 | 0 | for (i = 0; i < nitems(tty_default_raw_keys); i++) { |
560 | 0 | tdkr = &tty_default_raw_keys[i]; |
561 | |
|
562 | 0 | s = tdkr->string; |
563 | 0 | if (*s != '\0') |
564 | 0 | tty_keys_add(tty, s, tdkr->key); |
565 | 0 | } |
566 | 0 | for (i = 0; i < nitems(tty_default_code_keys); i++) { |
567 | 0 | tdkc = &tty_default_code_keys[i]; |
568 | |
|
569 | 0 | s = tty_term_string(tty->term, tdkc->code); |
570 | 0 | if (*s != '\0') |
571 | 0 | tty_keys_add(tty, s, tdkc->key); |
572 | |
|
573 | 0 | } |
574 | |
|
575 | 0 | o = options_get(global_options, "user-keys"); |
576 | 0 | if (o != NULL) { |
577 | 0 | for (i = 0; i <= KEYC_NUSER; i++) { |
578 | 0 | ov = options_array_getv(o, "%u", i); |
579 | 0 | if (ov != NULL) |
580 | 0 | tty_keys_add(tty, ov->string, KEYC_USER + i); |
581 | 0 | } |
582 | 0 | } |
583 | 0 | } |
584 | | |
585 | | /* Free the entire key tree. */ |
586 | | void |
587 | | tty_keys_free(struct tty *tty) |
588 | 0 | { |
589 | 0 | tty_keys_free1(tty->key_tree); |
590 | 0 | } |
591 | | |
592 | | /* Free a single key. */ |
593 | | static void |
594 | | tty_keys_free1(struct tty_key *tk) |
595 | 0 | { |
596 | 0 | if (tk->next != NULL) |
597 | 0 | tty_keys_free1(tk->next); |
598 | 0 | if (tk->left != NULL) |
599 | 0 | tty_keys_free1(tk->left); |
600 | 0 | if (tk->right != NULL) |
601 | 0 | tty_keys_free1(tk->right); |
602 | 0 | free(tk); |
603 | 0 | } |
604 | | |
605 | | /* Lookup a key in the tree. */ |
606 | | static struct tty_key * |
607 | | tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size) |
608 | 0 | { |
609 | 0 | *size = 0; |
610 | 0 | return (tty_keys_find1(tty->key_tree, buf, len, size)); |
611 | 0 | } |
612 | | |
613 | | /* Find the next node. */ |
614 | | static struct tty_key * |
615 | | tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) |
616 | 0 | { |
617 | | /* If no data, no match. */ |
618 | 0 | if (len == 0) |
619 | 0 | return (NULL); |
620 | | |
621 | | /* If the node is NULL, this is the end of the tree. No match. */ |
622 | 0 | if (tk == NULL) |
623 | 0 | return (NULL); |
624 | | |
625 | | /* Pick the next in the sequence. */ |
626 | 0 | if (tk->ch == *buf) { |
627 | | /* Move forward in the string. */ |
628 | 0 | buf++; len--; |
629 | 0 | (*size)++; |
630 | | |
631 | | /* At the end of the string, return the current node. */ |
632 | 0 | if (len == 0 || (tk->next == NULL && tk->key != KEYC_UNKNOWN)) |
633 | 0 | return (tk); |
634 | | |
635 | | /* Move into the next tree for the following character. */ |
636 | 0 | tk = tk->next; |
637 | 0 | } else { |
638 | 0 | if (*buf < tk->ch) |
639 | 0 | tk = tk->left; |
640 | 0 | else if (*buf > tk->ch) |
641 | 0 | tk = tk->right; |
642 | 0 | } |
643 | | |
644 | | /* Move to the next in the tree. */ |
645 | 0 | return (tty_keys_find1(tk, buf, len, size)); |
646 | 0 | } |
647 | | |
648 | | static int |
649 | | tty_keys_partial_paste_end(const char *buf, size_t len) |
650 | 0 | { |
651 | 0 | static const char paste_end[] = "\033[201~"; |
652 | 0 | size_t paste_end_len = (sizeof paste_end) - 1; |
653 | |
|
654 | 0 | if (len == 0 || len >= paste_end_len) |
655 | 0 | return (0); |
656 | 0 | return (memcmp(buf, paste_end, len) == 0); |
657 | 0 | } |
658 | | |
659 | | /* |
660 | | * Look for a terminal reply, mouse event or extended key. These sequences |
661 | | * carry their own modifiers, so none can follow an Escape as a Meta prefix. |
662 | | */ |
663 | | static int |
664 | | tty_keys_reply(struct tty *tty, const char *buf, size_t len, |
665 | | struct tty_key_parse *kp) |
666 | 0 | { |
667 | 0 | u_int i; |
668 | 0 | int n; |
669 | |
|
670 | 0 | for (i = 0; i < nitems(tty_keys_replies); i++) { |
671 | 0 | n = tty_keys_replies[i].fn(tty, buf, len, &kp->size, 0); |
672 | 0 | if (n == 0) |
673 | 0 | kp->key = tty_keys_replies[i].key; |
674 | 0 | if (n != -1) |
675 | 0 | return (n); |
676 | 0 | } |
677 | | |
678 | 0 | switch (tty_keys_mouse(tty, buf, len, &kp->size, &kp->m)) { |
679 | 0 | case 0: /* yes */ |
680 | 0 | kp->key = KEYC_MOUSE; |
681 | 0 | return (0); |
682 | 0 | case -1: /* no, or not valid */ |
683 | 0 | break; |
684 | 0 | case -2: /* yes, but we don't care */ |
685 | 0 | kp->key = KEYC_UNKNOWN; |
686 | 0 | return (0); |
687 | 0 | case 1: /* partial */ |
688 | 0 | return (1); |
689 | 0 | } |
690 | | |
691 | 0 | return (tty_keys_extended_key(tty, buf, len, &kp->size, &kp->key)); |
692 | 0 | } |
693 | | |
694 | | /* Look up part of the next key. */ |
695 | | static int |
696 | | tty_keys_next1(struct tty *tty, const char *buf, size_t len, |
697 | | struct tty_key_parse *kp, int expired) |
698 | 0 | { |
699 | 0 | struct client *c = tty->client; |
700 | 0 | struct tty_key *tk, *tk1; |
701 | 0 | struct utf8_data ud; |
702 | 0 | enum utf8_state more; |
703 | 0 | utf8_char uc; |
704 | 0 | u_int i; |
705 | 0 | key_code key; |
706 | |
|
707 | 0 | log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len, |
708 | 0 | (int)len, buf, expired); |
709 | 0 | memset(kp, 0, sizeof *kp); |
710 | | |
711 | | /* Is this a terminal reply, mouse event or extended key? */ |
712 | 0 | switch (tty_keys_reply(tty, buf, len, kp)) { |
713 | 0 | case 0: /* yes */ |
714 | 0 | kp->flags |= TTY_KEY_PARSE_NO_META; |
715 | 0 | return (0); |
716 | 0 | case -1: /* no, or not valid */ |
717 | 0 | break; |
718 | 0 | case 1: /* partial */ |
719 | 0 | if (!expired) |
720 | 0 | return (1); |
721 | 0 | break; |
722 | 0 | } |
723 | | |
724 | | /* Is this a known key? */ |
725 | 0 | tk = tty_keys_find(tty, buf, len, &kp->size); |
726 | 0 | if (tk != NULL && tk->key != KEYC_UNKNOWN) { |
727 | 0 | tk1 = tk; |
728 | 0 | do |
729 | 0 | log_debug("%s: keys in list: %#llx", c->name, tk1->key); |
730 | 0 | while ((tk1 = tk1->next) != NULL); |
731 | 0 | if (tk->next != NULL && !expired) |
732 | 0 | return (1); |
733 | 0 | key = kp->key = tk->key; |
734 | | |
735 | | /* |
736 | | * A key with an implied Meta already includes its Escape, and |
737 | | * focus and theme reports never have a Meta modifier. |
738 | | */ |
739 | 0 | if ((key & KEYC_IMPLIED_META) || |
740 | 0 | key == KEYC_FOCUS_IN || |
741 | 0 | key == KEYC_FOCUS_OUT || |
742 | 0 | key == KEYC_REPORT_DARK_THEME || |
743 | 0 | key == KEYC_REPORT_LIGHT_THEME) |
744 | 0 | kp->flags |= TTY_KEY_PARSE_NO_META; |
745 | 0 | return (0); |
746 | 0 | } |
747 | | |
748 | | /* Is this the start of a known key? */ |
749 | 0 | if (tk != NULL && !expired) |
750 | 0 | return (1); |
751 | | |
752 | | /* Is this valid UTF-8? */ |
753 | 0 | more = utf8_open(&ud, (u_char)*buf); |
754 | 0 | if (more == UTF8_MORE) { |
755 | 0 | kp->size = ud.size; |
756 | 0 | if (len < ud.size) { |
757 | 0 | if (!expired) |
758 | 0 | return (1); |
759 | 0 | return (-1); |
760 | 0 | } |
761 | 0 | for (i = 1; i < ud.size; i++) |
762 | 0 | more = utf8_append(&ud, (u_char)buf[i]); |
763 | 0 | if (more != UTF8_DONE) |
764 | 0 | return (-1); |
765 | | |
766 | 0 | if (utf8_from_data(&ud, &uc) != UTF8_DONE) |
767 | 0 | return (-1); |
768 | 0 | kp->key = uc; |
769 | |
|
770 | 0 | log_debug("%s: UTF-8 key %.*s %#llx", c->name, (int)ud.size, |
771 | 0 | ud.data, kp->key); |
772 | 0 | return (0); |
773 | 0 | } |
774 | | |
775 | 0 | return (-1); |
776 | 0 | } |
777 | | |
778 | | /* Apply a complete terminal reply. */ |
779 | | static void |
780 | | tty_keys_apply(struct tty *tty, const char *buf, struct tty_key_parse *kp) |
781 | 0 | { |
782 | 0 | size_t size; |
783 | 0 | u_int i; |
784 | |
|
785 | 0 | if (kp->key == KEYC_MOUSE) { |
786 | 0 | tty->mouse_last_x = kp->m.x; |
787 | 0 | tty->mouse_last_y = kp->m.y; |
788 | 0 | tty->mouse_last_b = kp->m.b; |
789 | 0 | return; |
790 | 0 | } |
791 | 0 | for (i = 0; i < nitems(tty_keys_replies); i++) { |
792 | 0 | if (tty_keys_replies[i].key == kp->key) { |
793 | 0 | tty_keys_replies[i].fn(tty, buf, kp->size, &size, 1); |
794 | 0 | kp->key = KEYC_UNKNOWN; |
795 | 0 | return; |
796 | 0 | } |
797 | 0 | } |
798 | 0 | } |
799 | | |
800 | | /* Process at least one key in the buffer. Return 0 if no keys present. */ |
801 | | int |
802 | | tty_keys_next(struct tty *tty) |
803 | 0 | { |
804 | 0 | struct client *c = tty->client; |
805 | 0 | struct timeval tv; |
806 | 0 | const char *buf; |
807 | 0 | size_t len, size; |
808 | 0 | cc_t bspace; |
809 | 0 | int delay, expired = 0, n; |
810 | 0 | key_code key, onlykey; |
811 | 0 | struct tty_key_parse kp; |
812 | 0 | struct key_event *event; |
813 | | |
814 | | /* Get key buffer. */ |
815 | 0 | buf = EVBUFFER_DATA(tty->in); |
816 | 0 | len = EVBUFFER_LENGTH(tty->in); |
817 | 0 | if (len == 0) |
818 | 0 | return (0); |
819 | 0 | log_debug("%s: keys are %zu (%.*s)", c->name, len, (int)len, buf); |
820 | | |
821 | | /* If the client has no session, there is nowhere to send input. */ |
822 | 0 | if (c->session == NULL) { |
823 | 0 | log_debug("%s: discard keys with no session", c->name); |
824 | 0 | if (event_initialized(&tty->key_timer)) |
825 | 0 | evtimer_del(&tty->key_timer); |
826 | 0 | tty->flags &= ~TTY_TIMER; |
827 | 0 | evbuffer_drain(tty->in, len); |
828 | 0 | return (1); |
829 | 0 | } |
830 | | |
831 | 0 | first_key: |
832 | | /* Try to lookup complete key. */ |
833 | 0 | n = tty_keys_next1(tty, buf, len, &kp, expired); |
834 | 0 | if (n == 0) /* found */ |
835 | 0 | goto complete_key; |
836 | 0 | if (n == 1) |
837 | 0 | goto partial_key; |
838 | | |
839 | | /* |
840 | | * If not a complete key, look for key with an escape prefix (meta |
841 | | * modifier). |
842 | | */ |
843 | 0 | if (*buf == '\033' && len > 1) { |
844 | | /* Look for a key without the escape. */ |
845 | 0 | n = tty_keys_next1(tty, buf + 1, len - 1, &kp, expired); |
846 | 0 | if (n == 0) { /* found */ |
847 | 0 | if (kp.flags & TTY_KEY_PARSE_NO_META) { |
848 | | /* |
849 | | * We want the escape key as well as the xterm |
850 | | * key, because the xterm sequence implicitly |
851 | | * includes the escape (so if we see |
852 | | * \033\033[1;3D we know it is an Escape |
853 | | * followed by M-Left, not just M-Left). The |
854 | | * same applies to terminal replies, mouse |
855 | | * events and extended keys. |
856 | | */ |
857 | 0 | memset(&kp, 0, sizeof kp); |
858 | 0 | kp.key = '\033'; |
859 | 0 | kp.size = 1; |
860 | 0 | goto complete_key; |
861 | 0 | } |
862 | 0 | kp.key |= KEYC_META; |
863 | 0 | kp.size++; |
864 | 0 | goto complete_key; |
865 | 0 | } |
866 | 0 | if (n == 1) /* partial */ |
867 | 0 | goto partial_key; |
868 | 0 | } |
869 | | |
870 | | /* |
871 | | * At this point, we know the key is not partial (with or without |
872 | | * escape). So pass it through even if the timer has not expired. |
873 | | */ |
874 | 0 | if (*buf == '\033' && len >= 2) { |
875 | 0 | key = (u_char)buf[1] | KEYC_META; |
876 | 0 | size = 2; |
877 | 0 | } else { |
878 | 0 | key = (u_char)buf[0]; |
879 | 0 | size = 1; |
880 | 0 | } |
881 | | |
882 | | /* C-Space is special. */ |
883 | 0 | if ((key & KEYC_MASK_KEY) == C0_NUL) |
884 | 0 | key = ' ' | KEYC_CTRL | (key & KEYC_META); |
885 | | |
886 | | /* |
887 | | * Check for backspace key using termios VERASE - the terminfo |
888 | | * kbs entry is extremely unreliable, so cannot be safely |
889 | | * used. termios should have a better idea. |
890 | | */ |
891 | 0 | bspace = tty->tio.c_cc[VERASE]; |
892 | 0 | if (bspace != _POSIX_VDISABLE) { |
893 | 0 | if (key == bspace) { |
894 | 0 | log_debug("%s: key %#llx is BSpace", c->name, key); |
895 | 0 | key = KEYC_BSPACE; |
896 | 0 | } |
897 | 0 | if (key == (bspace|KEYC_META)) { |
898 | 0 | log_debug("%s: key %#llx is M-BSpace", c->name, key); |
899 | 0 | key = KEYC_BSPACE|KEYC_META; |
900 | 0 | } |
901 | 0 | } |
902 | | |
903 | | /* |
904 | | * Fix up all C0 control codes that don't have a dedicated key into |
905 | | * corresponding Ctrl keys. Convert characters in the A-Z range into |
906 | | * lowercase, so ^A becomes a|CTRL. |
907 | | */ |
908 | 0 | onlykey = key & KEYC_MASK_KEY; |
909 | 0 | if (onlykey < 0x20 && |
910 | 0 | onlykey != C0_HT && |
911 | 0 | onlykey != C0_CR && |
912 | 0 | onlykey != C0_ESC) { |
913 | 0 | onlykey |= 0x40; |
914 | 0 | if (onlykey >= 'A' && onlykey <= 'Z') |
915 | 0 | onlykey |= 0x20; |
916 | 0 | key = onlykey | KEYC_CTRL | (key & KEYC_META); |
917 | 0 | } |
918 | |
|
919 | 0 | memset(&kp, 0, sizeof kp); |
920 | 0 | kp.key = key; |
921 | 0 | kp.size = size; |
922 | 0 | goto complete_key; |
923 | | |
924 | 0 | partial_key: |
925 | 0 | log_debug("%s: partial key %.*s", c->name, (int)len, buf); |
926 | | |
927 | | /* If timer is going, check for expiration. */ |
928 | 0 | if (tty->flags & TTY_TIMER) { |
929 | 0 | if (evtimer_initialized(&tty->key_timer) && |
930 | 0 | !evtimer_pending(&tty->key_timer, NULL)) { |
931 | 0 | expired = 1; |
932 | 0 | goto first_key; |
933 | 0 | } |
934 | 0 | return (0); |
935 | 0 | } |
936 | | |
937 | | /* Get the time period. */ |
938 | 0 | delay = options_get_number(global_options, "escape-time"); |
939 | 0 | if (delay == 0) |
940 | 0 | delay = 1; |
941 | 0 | if ((tty->flags & TTY_BRACKETPASTE) && |
942 | 0 | tty_keys_partial_paste_end(buf, len)) { |
943 | 0 | log_debug("%s: increasing delay (partial paste end)", c->name); |
944 | 0 | if (delay < 500) |
945 | 0 | delay = 500; |
946 | 0 | } |
947 | 0 | if (tty->flags & (TTY_WAITFG|TTY_WAITBG|TTY_OSC52QUERY) || |
948 | 0 | tty->flags & (TTY_WINSZQUERY|TTY_WINSZPIXELS) || |
949 | 0 | (tty->flags & TTY_ALL_REQUEST_FLAGS) != TTY_ALL_REQUEST_FLAGS || |
950 | 0 | !TAILQ_EMPTY(&c->input_requests)) { |
951 | 0 | log_debug("%s: increasing delay (active query)", c->name); |
952 | 0 | if (delay < 500) |
953 | 0 | delay = 500; |
954 | 0 | } |
955 | 0 | tv.tv_sec = delay / 1000; |
956 | 0 | tv.tv_usec = (delay % 1000) * 1000L; |
957 | | |
958 | | /* Start the timer. */ |
959 | 0 | if (event_initialized(&tty->key_timer)) |
960 | 0 | evtimer_del(&tty->key_timer); |
961 | 0 | evtimer_set(&tty->key_timer, tty_keys_callback, tty); |
962 | 0 | evtimer_add(&tty->key_timer, &tv); |
963 | |
|
964 | 0 | tty->flags |= TTY_TIMER; |
965 | 0 | return (0); |
966 | | |
967 | 0 | complete_key: |
968 | 0 | log_debug("%s: complete key %.*s %#llx", c->name, (int)kp.size, buf, |
969 | 0 | kp.key); |
970 | | |
971 | | /* Remove key timer. */ |
972 | 0 | if (event_initialized(&tty->key_timer)) |
973 | 0 | evtimer_del(&tty->key_timer); |
974 | 0 | tty->flags &= ~TTY_TIMER; |
975 | | |
976 | | /* Apply terminal replies and mouse state. */ |
977 | 0 | tty_keys_apply(tty, buf, &kp); |
978 | | |
979 | | /* Check for bracketed paste. */ |
980 | 0 | if ((kp.key & KEYC_MASK_KEY) == KEYC_PASTE_START) |
981 | 0 | tty->flags |= TTY_BRACKETPASTE; |
982 | 0 | else if ((kp.key & KEYC_MASK_KEY) == KEYC_PASTE_END) |
983 | 0 | tty->flags &= ~TTY_BRACKETPASTE; |
984 | | |
985 | | /* Check for focus events. */ |
986 | 0 | if (kp.key == KEYC_FOCUS_OUT) { |
987 | 0 | c->flags &= ~CLIENT_FOCUSED; |
988 | 0 | window_update_focus(c->session->curw->window); |
989 | 0 | events_fire_client("client-focus-out", c); |
990 | 0 | } else if (kp.key == KEYC_FOCUS_IN) { |
991 | 0 | c->flags |= CLIENT_FOCUSED; |
992 | 0 | events_fire_client("client-focus-in", c); |
993 | 0 | window_update_focus(c->session->curw->window); |
994 | 0 | } |
995 | | |
996 | | /* Fire the key. */ |
997 | 0 | if (kp.key != KEYC_UNKNOWN) { |
998 | 0 | event = xcalloc(1, sizeof *event); |
999 | 0 | event->key = kp.key; |
1000 | 0 | memcpy(&event->m, &kp.m, sizeof event->m); |
1001 | |
|
1002 | 0 | event->buf = xmalloc(kp.size); |
1003 | 0 | event->len = kp.size; |
1004 | 0 | memcpy (event->buf, buf, event->len); |
1005 | |
|
1006 | 0 | if (!server_client_handle_key(c, event)) { |
1007 | 0 | free(event->buf); |
1008 | 0 | free(event); |
1009 | 0 | } |
1010 | 0 | } |
1011 | | |
1012 | | /* Remove data from buffer. */ |
1013 | 0 | evbuffer_drain(tty->in, kp.size); |
1014 | |
|
1015 | 0 | return (1); |
1016 | 0 | } |
1017 | | |
1018 | | /* Key timer callback. */ |
1019 | | static void |
1020 | | tty_keys_callback(__unused int fd, __unused short events, void *data) |
1021 | 0 | { |
1022 | 0 | struct tty *tty = data; |
1023 | |
|
1024 | 0 | if (tty->flags & TTY_TIMER) { |
1025 | 0 | while (tty_keys_next(tty)) |
1026 | 0 | ; |
1027 | 0 | } |
1028 | 0 | } |
1029 | | |
1030 | | /* |
1031 | | * Handle extended key input. This has two forms: \033[27;m;k~ and \033[k;mu, |
1032 | | * where k is key as a number and m is a modifier. Returns 0 for success, -1 |
1033 | | * for failure, 1 for partial; |
1034 | | */ |
1035 | | static int |
1036 | | tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, |
1037 | | size_t *size, key_code *key) |
1038 | 0 | { |
1039 | 0 | struct client *c = tty->client; |
1040 | 0 | size_t end; |
1041 | 0 | u_int number, modifiers; |
1042 | 0 | char tmp[64]; |
1043 | 0 | cc_t bspace; |
1044 | 0 | key_code nkey, onlykey; |
1045 | 0 | struct utf8_data ud; |
1046 | 0 | utf8_char uc; |
1047 | |
|
1048 | 0 | *size = 0; |
1049 | | |
1050 | | /* First two bytes are always \033[. */ |
1051 | 0 | if (buf[0] != '\033') |
1052 | 0 | return (-1); |
1053 | 0 | if (len == 1) |
1054 | 0 | return (1); |
1055 | 0 | if (buf[1] != '[') |
1056 | 0 | return (-1); |
1057 | 0 | if (len == 2) |
1058 | 0 | return (1); |
1059 | | |
1060 | | /* |
1061 | | * Look for a terminator. Stop at either '~' or anything that isn't a |
1062 | | * number or ';'. |
1063 | | */ |
1064 | 0 | for (end = 2; end < len && end != sizeof tmp; end++) { |
1065 | 0 | if (buf[end] == '~') |
1066 | 0 | break; |
1067 | 0 | if (!isdigit((u_char)buf[end]) && buf[end] != ';') |
1068 | 0 | break; |
1069 | 0 | } |
1070 | 0 | if (end == len) |
1071 | 0 | return (1); |
1072 | 0 | if (end == sizeof tmp || (buf[end] != '~' && buf[end] != 'u')) |
1073 | 0 | return (-1); |
1074 | | |
1075 | | /* Copy to the buffer. */ |
1076 | 0 | memcpy(tmp, buf + 2, end - 2); |
1077 | 0 | tmp[end - 2] = '\0'; |
1078 | | |
1079 | | /* Try to parse either form of key. */ |
1080 | 0 | if (buf[end] == '~') { |
1081 | 0 | if (sscanf(tmp, "27;%u;%u", &modifiers, &number) != 2) |
1082 | 0 | return (-1); |
1083 | 0 | } else { |
1084 | 0 | if (sscanf(tmp ,"%u;%u", &number, &modifiers) != 2) |
1085 | 0 | return (-1); |
1086 | 0 | } |
1087 | 0 | *size = end + 1; |
1088 | | |
1089 | | /* Store the key. */ |
1090 | 0 | bspace = tty->tio.c_cc[VERASE]; |
1091 | 0 | if (bspace != _POSIX_VDISABLE && number == bspace) |
1092 | 0 | nkey = KEYC_BSPACE; |
1093 | 0 | else |
1094 | 0 | nkey = number; |
1095 | | |
1096 | | /* Convert UTF-32 codepoint into internal representation. */ |
1097 | 0 | if (nkey != KEYC_BSPACE && nkey & ~0x7f) { |
1098 | 0 | if (utf8_fromwc(nkey, &ud) == UTF8_DONE && |
1099 | 0 | utf8_from_data(&ud, &uc) == UTF8_DONE) |
1100 | 0 | nkey = uc; |
1101 | 0 | else |
1102 | 0 | return (-1); |
1103 | 0 | } |
1104 | | |
1105 | | /* Update the modifiers. */ |
1106 | 0 | if (modifiers > 0) { |
1107 | 0 | modifiers--; |
1108 | 0 | if (modifiers & 1) |
1109 | 0 | nkey |= KEYC_SHIFT; |
1110 | 0 | if (modifiers & 2) |
1111 | 0 | nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Alt */ |
1112 | 0 | if (modifiers & 4) |
1113 | 0 | nkey |= KEYC_CTRL; |
1114 | 0 | if (modifiers & 8) |
1115 | 0 | nkey |= (KEYC_META|KEYC_IMPLIED_META); /* Meta */ |
1116 | 0 | } |
1117 | | |
1118 | | /* Convert S-Tab into Backtab. */ |
1119 | 0 | if ((nkey & KEYC_MASK_KEY) == '\011' && (nkey & KEYC_SHIFT)) |
1120 | 0 | nkey = KEYC_BTAB | (nkey & ~KEYC_MASK_KEY & ~KEYC_SHIFT); |
1121 | | |
1122 | | /* |
1123 | | * Deal with the Shift modifier when present alone. The problem is that |
1124 | | * in mode 2 some terminals would report shifted keys, like S-a, as |
1125 | | * just A, and some as S-A. |
1126 | | * |
1127 | | * Because we need an unambiguous internal representation, and because |
1128 | | * restoring the Shift modifier when it's missing would require knowing |
1129 | | * the keyboard layout, and because S-A would cause a lot of issues |
1130 | | * downstream, we choose to lose the Shift for all printable |
1131 | | * characters. |
1132 | | * |
1133 | | * That still leaves some ambiguity, such as C-S-A vs. C-A, but that's |
1134 | | * OK, and applications can handle that. |
1135 | | */ |
1136 | 0 | onlykey = nkey & KEYC_MASK_KEY; |
1137 | 0 | if (((onlykey > 0x20 && onlykey < 0x7f) || |
1138 | 0 | KEYC_IS_UNICODE(nkey)) && |
1139 | 0 | (nkey & KEYC_MASK_MODIFIERS) == KEYC_SHIFT) |
1140 | 0 | nkey &= ~KEYC_SHIFT; |
1141 | |
|
1142 | 0 | if (log_get_level() != 0) { |
1143 | 0 | log_debug("%s: extended key %.*s is %llx (%s)", c->name, |
1144 | 0 | (int)*size, buf, nkey, key_string_lookup_key(nkey, 1)); |
1145 | 0 | } |
1146 | |
|
1147 | 0 | *key = nkey; |
1148 | 0 | return (0); |
1149 | 0 | } |
1150 | | |
1151 | | /* |
1152 | | * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial |
1153 | | * (probably a mouse sequence but need more data), -2 if an invalid mouse |
1154 | | * sequence. |
1155 | | */ |
1156 | | static int |
1157 | | tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, |
1158 | | struct mouse_event *m) |
1159 | 0 | { |
1160 | 0 | struct client *c = tty->client; |
1161 | 0 | u_int i, x, y, b, sgr_b; |
1162 | 0 | u_char sgr_type, ch; |
1163 | | |
1164 | | /* |
1165 | | * Standard mouse sequences are \033[M followed by three characters |
1166 | | * indicating button, X and Y, all based at 32 with 1,1 top-left. |
1167 | | * |
1168 | | * UTF-8 mouse sequences are similar but the three are expressed as |
1169 | | * UTF-8 characters. |
1170 | | * |
1171 | | * SGR extended mouse sequences are \033[< followed by three numbers in |
1172 | | * decimal and separated by semicolons indicating button, X and Y. A |
1173 | | * trailing 'M' is click or scroll and trailing 'm' release. All are |
1174 | | * based at 0 with 1,1 top-left. |
1175 | | */ |
1176 | |
|
1177 | 0 | *size = 0; |
1178 | 0 | x = y = b = sgr_b = 0; |
1179 | 0 | sgr_type = ' '; |
1180 | | |
1181 | | /* First two bytes are always \033[. */ |
1182 | 0 | if (buf[0] != '\033') |
1183 | 0 | return (-1); |
1184 | 0 | if (len == 1) |
1185 | 0 | return (1); |
1186 | 0 | if (buf[1] != '[') |
1187 | 0 | return (-1); |
1188 | 0 | if (len == 2) |
1189 | 0 | return (1); |
1190 | | |
1191 | | /* |
1192 | | * Third byte is M in old standard (and UTF-8 extension which we do not |
1193 | | * support), < in SGR extension. |
1194 | | */ |
1195 | 0 | if (buf[2] == 'M') { |
1196 | | /* Read the three inputs. */ |
1197 | 0 | *size = 3; |
1198 | 0 | for (i = 0; i < 3; i++) { |
1199 | 0 | if (len <= *size) |
1200 | 0 | return (1); |
1201 | 0 | ch = (u_char)buf[(*size)++]; |
1202 | 0 | if (i == 0) |
1203 | 0 | b = ch; |
1204 | 0 | else if (i == 1) |
1205 | 0 | x = ch; |
1206 | 0 | else |
1207 | 0 | y = ch; |
1208 | 0 | } |
1209 | 0 | log_debug("%s: mouse input: %.*s", c->name, (int)*size, buf); |
1210 | | |
1211 | | /* Check and return the mouse input. */ |
1212 | 0 | if (b < MOUSE_PARAM_BTN_OFF || |
1213 | 0 | x < MOUSE_PARAM_POS_OFF || |
1214 | 0 | y < MOUSE_PARAM_POS_OFF) |
1215 | 0 | return (-2); |
1216 | 0 | b -= MOUSE_PARAM_BTN_OFF; |
1217 | 0 | x -= MOUSE_PARAM_POS_OFF; |
1218 | 0 | y -= MOUSE_PARAM_POS_OFF; |
1219 | 0 | } else if (buf[2] == '<') { |
1220 | | /* Read the three inputs. */ |
1221 | 0 | *size = 3; |
1222 | 0 | while (1) { |
1223 | 0 | if (len <= *size) |
1224 | 0 | return (1); |
1225 | 0 | ch = (u_char)buf[(*size)++]; |
1226 | 0 | if (ch == ';') |
1227 | 0 | break; |
1228 | 0 | if (ch < '0' || ch > '9') |
1229 | 0 | return (-1); |
1230 | 0 | sgr_b = 10 * sgr_b + (ch - '0'); |
1231 | 0 | } |
1232 | 0 | while (1) { |
1233 | 0 | if (len <= *size) |
1234 | 0 | return (1); |
1235 | 0 | ch = (u_char)buf[(*size)++]; |
1236 | 0 | if (ch == ';') |
1237 | 0 | break; |
1238 | 0 | if (ch < '0' || ch > '9') |
1239 | 0 | return (-1); |
1240 | 0 | x = 10 * x + (ch - '0'); |
1241 | 0 | } |
1242 | 0 | while (1) { |
1243 | 0 | if (len <= *size) |
1244 | 0 | return (1); |
1245 | 0 | ch = (u_char)buf[(*size)++]; |
1246 | 0 | if (ch == 'M' || ch == 'm') |
1247 | 0 | break; |
1248 | 0 | if (ch < '0' || ch > '9') |
1249 | 0 | return (-1); |
1250 | 0 | y = 10 * y + (ch - '0'); |
1251 | 0 | } |
1252 | 0 | log_debug("%s: mouse input (SGR): %.*s", c->name, (int)*size, |
1253 | 0 | buf); |
1254 | | |
1255 | | /* Check and return the mouse input. */ |
1256 | 0 | if (x < 1 || y < 1) |
1257 | 0 | return (-2); |
1258 | 0 | x--; |
1259 | 0 | y--; |
1260 | 0 | b = sgr_b; |
1261 | | |
1262 | | /* Type is M for press, m for release. */ |
1263 | 0 | sgr_type = ch; |
1264 | 0 | if (sgr_type == 'm') |
1265 | 0 | b = 3; |
1266 | | |
1267 | | /* |
1268 | | * Some terminals (like PuTTY 0.63) mistakenly send |
1269 | | * button-release events for scroll-wheel button-press event. |
1270 | | * Discard it before it reaches any program running inside |
1271 | | * tmux. |
1272 | | */ |
1273 | 0 | if (sgr_type == 'm' && MOUSE_WHEEL(sgr_b)) |
1274 | 0 | return (-2); |
1275 | 0 | } else |
1276 | 0 | return (-1); |
1277 | | |
1278 | | /* Fill mouse event. The last mouse state is updated when applied. */ |
1279 | 0 | m->lx = tty->mouse_last_x; |
1280 | 0 | m->x = x; |
1281 | 0 | m->ly = tty->mouse_last_y; |
1282 | 0 | m->y = y; |
1283 | 0 | m->lb = tty->mouse_last_b; |
1284 | 0 | m->b = b; |
1285 | 0 | m->sgr_type = sgr_type; |
1286 | 0 | m->sgr_b = sgr_b; |
1287 | |
|
1288 | 0 | return (0); |
1289 | 0 | } |
1290 | | |
1291 | | /* |
1292 | | * Handle OSC 52 clipboard input. Returns 0 for success, -1 for failure, 1 for |
1293 | | * partial. |
1294 | | */ |
1295 | | static int |
1296 | | tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size, |
1297 | | int apply) |
1298 | 0 | { |
1299 | 0 | struct client *c = tty->client; |
1300 | 0 | size_t end, terminator = 0, needed; |
1301 | 0 | char *copy, *out, clip = 0; |
1302 | 0 | int outlen; |
1303 | 0 | struct input_request_clipboard_data cd; |
1304 | |
|
1305 | 0 | *size = 0; |
1306 | | |
1307 | | /* First five bytes are always \033]52;. */ |
1308 | 0 | if (buf[0] != '\033') |
1309 | 0 | return (-1); |
1310 | 0 | if (len == 1) |
1311 | 0 | return (1); |
1312 | 0 | if (buf[1] != ']') |
1313 | 0 | return (-1); |
1314 | 0 | if (len == 2) |
1315 | 0 | return (1); |
1316 | 0 | if (buf[2] != '5') |
1317 | 0 | return (-1); |
1318 | 0 | if (len == 3) |
1319 | 0 | return (1); |
1320 | 0 | if (buf[3] != '2') |
1321 | 0 | return (-1); |
1322 | 0 | if (len == 4) |
1323 | 0 | return (1); |
1324 | 0 | if (buf[4] != ';') |
1325 | 0 | return (-1); |
1326 | 0 | if (len == 5) |
1327 | 0 | return (1); |
1328 | | |
1329 | | /* Find the terminator if any. */ |
1330 | 0 | for (end = 5; end < len; end++) { |
1331 | 0 | if (buf[end] == '\007') { |
1332 | 0 | terminator = 1; |
1333 | 0 | break; |
1334 | 0 | } |
1335 | 0 | if (end > 5 && buf[end - 1] == '\033' && buf[end] == '\\') { |
1336 | 0 | terminator = 2; |
1337 | 0 | break; |
1338 | 0 | } |
1339 | 0 | } |
1340 | 0 | if (end == len) |
1341 | 0 | return (1); |
1342 | 0 | *size = end + 1; |
1343 | 0 | if (!apply) |
1344 | 0 | return (0); |
1345 | | |
1346 | | /* Skip the initial part. */ |
1347 | 0 | buf += 5; |
1348 | 0 | end -= 5; |
1349 | | |
1350 | | /* Adjust end so that it points to the start of the terminator. */ |
1351 | 0 | end -= terminator - 1; |
1352 | | |
1353 | | /* |
1354 | | * Save which clipboard was used from the second argument. If more than |
1355 | | * one is specified (should not happen), ignore the argument. |
1356 | | */ |
1357 | 0 | if (end >= 2 && buf[0] != ';' && buf[1] == ';') |
1358 | 0 | clip = buf[0]; |
1359 | | |
1360 | | /* Skip the second argument. */ |
1361 | 0 | while (end != 0 && *buf != ';') { |
1362 | 0 | buf++; |
1363 | 0 | end--; |
1364 | 0 | } |
1365 | 0 | if (end == 0 || end == 1) |
1366 | 0 | return (0); |
1367 | 0 | buf++; |
1368 | 0 | end--; |
1369 | | |
1370 | | /* It has to be a string so copy it. */ |
1371 | 0 | copy = xmalloc(end + 1); |
1372 | 0 | memcpy(copy, buf, end); |
1373 | 0 | copy[end] = '\0'; |
1374 | | |
1375 | | /* Convert from base64. */ |
1376 | 0 | needed = ((end + 3) / 4) * 3; |
1377 | 0 | if (needed == 0) { |
1378 | 0 | free(copy); |
1379 | 0 | return (0); |
1380 | 0 | } |
1381 | 0 | out = xmalloc(needed); |
1382 | 0 | if ((outlen = b64_pton(copy, out, needed)) == -1) { |
1383 | 0 | free(out); |
1384 | 0 | free(copy); |
1385 | 0 | return (0); |
1386 | 0 | } |
1387 | 0 | free(copy); |
1388 | 0 | log_debug("%s: %.*s", __func__, outlen, out); |
1389 | | |
1390 | | /* Set reply if any. */ |
1391 | 0 | cd.buf = out; |
1392 | 0 | cd.len = outlen; |
1393 | 0 | cd.clip = clip; |
1394 | 0 | input_request_reply(c, INPUT_REQUEST_CLIPBOARD, &cd); |
1395 | | |
1396 | | /* Create a buffer if requested. */ |
1397 | 0 | if (tty->flags & TTY_OSC52QUERY) { |
1398 | 0 | paste_add(NULL, out, outlen); |
1399 | 0 | out = NULL; |
1400 | 0 | evtimer_del(&tty->clipboard_timer); |
1401 | 0 | tty->flags &= ~TTY_OSC52QUERY; |
1402 | 0 | } |
1403 | |
|
1404 | 0 | free(out); |
1405 | 0 | return (0); |
1406 | 0 | } |
1407 | | |
1408 | | /* |
1409 | | * Handle primary device attributes input. Returns 0 for success, -1 for |
1410 | | * failure, 1 for partial. |
1411 | | */ |
1412 | | static int |
1413 | | tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, |
1414 | | size_t *size, int apply) |
1415 | 0 | { |
1416 | 0 | struct client *c = tty->client; |
1417 | 0 | u_int i, n = 0; |
1418 | 0 | char tmp[128], *endptr, p[32] = { 0 }, *cp, *next; |
1419 | |
|
1420 | 0 | *size = 0; |
1421 | 0 | if (tty->flags & TTY_HAVEDA) |
1422 | 0 | return (-1); |
1423 | | |
1424 | | /* First three bytes are always \033[?. */ |
1425 | 0 | if (buf[0] != '\033') |
1426 | 0 | return (-1); |
1427 | 0 | if (len == 1) |
1428 | 0 | return (1); |
1429 | 0 | if (buf[1] != '[') |
1430 | 0 | return (-1); |
1431 | 0 | if (len == 2) |
1432 | 0 | return (1); |
1433 | 0 | if (buf[2] != '?') |
1434 | 0 | return (-1); |
1435 | 0 | if (len == 3) |
1436 | 0 | return (1); |
1437 | | |
1438 | | /* Copy the rest up to a c. */ |
1439 | 0 | for (i = 0; i < sizeof tmp; i++) { |
1440 | 0 | if (3 + i == len) |
1441 | 0 | return (1); |
1442 | 0 | if (buf[3 + i] >= 'a' && buf[3 + i] <= 'z') |
1443 | 0 | break; |
1444 | 0 | tmp[i] = buf[3 + i]; |
1445 | 0 | } |
1446 | 0 | if (i == sizeof tmp) |
1447 | 0 | return (-1); |
1448 | 0 | if (buf[3 + i] != 'c') |
1449 | 0 | return (-1); |
1450 | 0 | tmp[i] = '\0'; |
1451 | 0 | *size = 4 + i; |
1452 | 0 | if (!apply) |
1453 | 0 | return (0); |
1454 | | |
1455 | | /* Convert all arguments to numbers. */ |
1456 | 0 | cp = tmp; |
1457 | 0 | while ((next = strsep(&cp, ";")) != NULL) { |
1458 | 0 | p[n] = strtoul(next, &endptr, 10); |
1459 | 0 | if (*endptr != '\0') |
1460 | 0 | p[n] = 0; |
1461 | 0 | if (++n == nitems(p)) |
1462 | 0 | break; |
1463 | 0 | } |
1464 | | |
1465 | | /* Add terminal features. */ |
1466 | 0 | switch (p[0]) { |
1467 | 0 | case 61: /* level 1 */ |
1468 | 0 | case 62: /* level 2 */ |
1469 | 0 | case 63: /* level 3 */ |
1470 | 0 | case 64: /* level 4 */ |
1471 | 0 | case 65: /* level 5 */ |
1472 | 0 | for (i = 1; i < n; i++) { |
1473 | 0 | log_debug("%s: DA feature: %d", c->name, p[i]); |
1474 | 0 | if (p[i] == 4) |
1475 | 0 | tty_parse_client_features(c, "sixel", ","); |
1476 | 0 | if (p[i] == 21) |
1477 | 0 | tty_parse_client_features(c, "margins", ","); |
1478 | 0 | if (p[i] == 28) |
1479 | 0 | tty_parse_client_features(c, "rectfill", ","); |
1480 | 0 | if (p[i] == 52) |
1481 | 0 | tty_parse_client_features(c, "clipboard", ","); |
1482 | 0 | } |
1483 | 0 | break; |
1484 | 0 | } |
1485 | 0 | log_debug("%s: received primary DA %.*s", c->name, (int)*size, buf); |
1486 | |
|
1487 | 0 | tty_update_features(tty); |
1488 | 0 | tty->flags |= TTY_HAVEDA; |
1489 | |
|
1490 | 0 | return (0); |
1491 | 0 | } |
1492 | | |
1493 | | /* |
1494 | | * Handle a synchronized update mode response. Returns 0 for success, -1 for |
1495 | | * failure, 1 for partial. |
1496 | | */ |
1497 | | static int |
1498 | | tty_keys_sync(struct tty *tty, const char *buf, size_t len, size_t *size, |
1499 | | int apply) |
1500 | 0 | { |
1501 | 0 | struct client *c = tty->client; |
1502 | 0 | static const char prefix[] = "\033[?2026;"; |
1503 | 0 | size_t i; |
1504 | 0 | int status; |
1505 | |
|
1506 | 0 | *size = 0; |
1507 | 0 | if (tty->flags & TTY_HAVESYNC) |
1508 | 0 | return (-1); |
1509 | | |
1510 | | /* The response is always \033[?2026;Ps$y. */ |
1511 | 0 | for (i = 0; i < (sizeof prefix) - 1; i++) { |
1512 | 0 | if (i == len) |
1513 | 0 | return (1); |
1514 | 0 | if (buf[i] != prefix[i]) |
1515 | 0 | return (-1); |
1516 | 0 | } |
1517 | 0 | if (i == len) |
1518 | 0 | return (1); |
1519 | 0 | if (buf[i] < '0' || buf[i] > '4') |
1520 | 0 | return (-1); |
1521 | 0 | status = buf[i++] - '0'; |
1522 | 0 | if (i == len) |
1523 | 0 | return (1); |
1524 | 0 | if (buf[i++] != '$') |
1525 | 0 | return (-1); |
1526 | 0 | if (i == len) |
1527 | 0 | return (1); |
1528 | 0 | if (buf[i++] != 'y') |
1529 | 0 | return (-1); |
1530 | 0 | *size = i; |
1531 | 0 | if (!apply) |
1532 | 0 | return (0); |
1533 | | |
1534 | 0 | if (status == 1 || status == 2 || status == 3) { |
1535 | 0 | tty_parse_client_features(c, "sync", ","); |
1536 | 0 | tty_update_features(tty); |
1537 | 0 | } |
1538 | 0 | log_debug("%s: received DECRPM %.*s", c->name, (int)*size, buf); |
1539 | 0 | tty->flags |= TTY_HAVESYNC; |
1540 | |
|
1541 | 0 | return (0); |
1542 | 0 | } |
1543 | | |
1544 | | #ifdef ENABLE_IMAGES |
1545 | | /* |
1546 | | * Handle a Kitty graphics query response. Returns 0 for success, -1 for |
1547 | | * failure, 1 for partial. |
1548 | | */ |
1549 | | static int |
1550 | | tty_keys_kitty_graphics(struct tty *tty, const char *buf, size_t len, |
1551 | | size_t *size, int apply) |
1552 | | { |
1553 | | struct client *c = tty->client; |
1554 | | static const char prefix[] = "\033_Gi=31;"; |
1555 | | size_t i, end; |
1556 | | |
1557 | | *size = 0; |
1558 | | if (tty->flags & TTY_HAVEKITTYGRAPHICS) |
1559 | | return (-1); |
1560 | | |
1561 | | for (i = 0; i < (sizeof prefix) - 1; i++) { |
1562 | | if (i == len) |
1563 | | return (1); |
1564 | | if (buf[i] != prefix[i]) |
1565 | | return (-1); |
1566 | | } |
1567 | | for (end = i; end < len; end++) { |
1568 | | if (buf[end] == '\033') |
1569 | | break; |
1570 | | if (buf[end] < ' ' || buf[end] > '~') |
1571 | | return (-1); |
1572 | | } |
1573 | | if (end == len || end + 1 == len) |
1574 | | return (1); |
1575 | | if (end == i || buf[end + 1] != '\\') |
1576 | | return (-1); |
1577 | | *size = end + 2; |
1578 | | if (!apply) |
1579 | | return (0); |
1580 | | |
1581 | | if (end - i == 2 && memcmp(buf + i, "OK", 2) == 0) { |
1582 | | tty_parse_client_features(c, "kittygraphics", ","); |
1583 | | tty_update_features(tty); |
1584 | | } |
1585 | | log_debug("%s: received Kitty graphics response %.*s", c->name, |
1586 | | (int)*size, buf); |
1587 | | tty->flags |= TTY_HAVEKITTYGRAPHICS; |
1588 | | |
1589 | | return (0); |
1590 | | } |
1591 | | #endif |
1592 | | |
1593 | | /* |
1594 | | * Handle secondary device attributes input. Returns 0 for success, -1 for |
1595 | | * failure, 1 for partial. |
1596 | | */ |
1597 | | static int |
1598 | | tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len, |
1599 | | size_t *size, int apply) |
1600 | 0 | { |
1601 | 0 | struct client *c = tty->client; |
1602 | 0 | u_int i, n = 0; |
1603 | 0 | char tmp[128], *endptr, p[32] = { 0 }, *cp, *next; |
1604 | |
|
1605 | 0 | *size = 0; |
1606 | 0 | if (tty->flags & TTY_HAVEDA2) |
1607 | 0 | return (-1); |
1608 | | |
1609 | | /* First three bytes are always \033[>. */ |
1610 | 0 | if (buf[0] != '\033') |
1611 | 0 | return (-1); |
1612 | 0 | if (len == 1) |
1613 | 0 | return (1); |
1614 | 0 | if (buf[1] != '[') |
1615 | 0 | return (-1); |
1616 | 0 | if (len == 2) |
1617 | 0 | return (1); |
1618 | 0 | if (buf[2] != '>') |
1619 | 0 | return (-1); |
1620 | 0 | if (len == 3) |
1621 | 0 | return (1); |
1622 | | |
1623 | | /* Copy the rest up to a c. */ |
1624 | 0 | for (i = 0; i < sizeof tmp; i++) { |
1625 | 0 | if (3 + i == len) |
1626 | 0 | return (1); |
1627 | 0 | if (buf[3 + i] >= 'a' && buf[3 + i] <= 'z') |
1628 | 0 | break; |
1629 | 0 | tmp[i] = buf[3 + i]; |
1630 | 0 | } |
1631 | 0 | if (i == sizeof tmp) |
1632 | 0 | return (-1); |
1633 | 0 | if (buf[3 + i] != 'c') |
1634 | 0 | return (-1); |
1635 | 0 | tmp[i] = '\0'; |
1636 | 0 | *size = 4 + i; |
1637 | 0 | if (!apply) |
1638 | 0 | return (0); |
1639 | | |
1640 | | /* Convert all arguments to numbers. */ |
1641 | 0 | cp = tmp; |
1642 | 0 | while ((next = strsep(&cp, ";")) != NULL) { |
1643 | 0 | p[n] = strtoul(next, &endptr, 10); |
1644 | 0 | if (*endptr != '\0') |
1645 | 0 | p[n] = 0; |
1646 | 0 | if (++n == nitems(p)) |
1647 | 0 | break; |
1648 | 0 | } |
1649 | | |
1650 | | /* |
1651 | | * Add terminal features. We add DECSLRM and DECFRA for some |
1652 | | * identification codes here, notably 64 will catch VT520, even though |
1653 | | * we can't use level 5 from DA because of VTE. |
1654 | | */ |
1655 | 0 | switch (p[0]) { |
1656 | 0 | case 'M': /* mintty */ |
1657 | 0 | tty_default_features(c, "mintty", 0); |
1658 | 0 | break; |
1659 | 0 | case 'T': /* tmux */ |
1660 | 0 | tty_default_features(c, "tmux", 0); |
1661 | 0 | break; |
1662 | 0 | case 'U': /* rxvt-unicode */ |
1663 | 0 | tty_default_features(c, "rxvt-unicode", 0); |
1664 | 0 | break; |
1665 | 0 | } |
1666 | 0 | log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf); |
1667 | |
|
1668 | 0 | tty_update_features(tty); |
1669 | 0 | tty->flags |= TTY_HAVEDA2; |
1670 | |
|
1671 | 0 | return (0); |
1672 | 0 | } |
1673 | | |
1674 | | /* |
1675 | | * Handle extended device attributes input. Returns 0 for success, -1 for |
1676 | | * failure, 1 for partial. |
1677 | | */ |
1678 | | static int |
1679 | | tty_keys_extended_device_attributes(struct tty *tty, const char *buf, |
1680 | | size_t len, size_t *size, int apply) |
1681 | 0 | { |
1682 | 0 | struct client *c = tty->client; |
1683 | 0 | u_int i; |
1684 | 0 | char tmp[128]; |
1685 | |
|
1686 | 0 | *size = 0; |
1687 | 0 | if (tty->flags & TTY_HAVEXDA) |
1688 | 0 | return (-1); |
1689 | | |
1690 | | /* First four bytes are always \033P>|. */ |
1691 | 0 | if (buf[0] != '\033') |
1692 | 0 | return (-1); |
1693 | 0 | if (len == 1) |
1694 | 0 | return (1); |
1695 | 0 | if (buf[1] != 'P') |
1696 | 0 | return (-1); |
1697 | 0 | if (len == 2) |
1698 | 0 | return (1); |
1699 | 0 | if (buf[2] != '>') |
1700 | 0 | return (-1); |
1701 | 0 | if (len == 3) |
1702 | 0 | return (1); |
1703 | 0 | if (buf[3] != '|') |
1704 | 0 | return (-1); |
1705 | 0 | if (len == 4) |
1706 | 0 | return (1); |
1707 | | |
1708 | | /* Copy the rest up to \033\. */ |
1709 | 0 | for (i = 0; i < (sizeof tmp) - 1; i++) { |
1710 | 0 | if (4 + i == len) |
1711 | 0 | return (1); |
1712 | 0 | if (buf[4 + i - 1] == '\033' && buf[4 + i] == '\\') |
1713 | 0 | break; |
1714 | 0 | tmp[i] = buf[4 + i]; |
1715 | 0 | } |
1716 | 0 | if (i == (sizeof tmp) - 1) |
1717 | 0 | return (-1); |
1718 | 0 | *size = 5 + i; |
1719 | 0 | if (i == 0 || !apply) |
1720 | 0 | return (0); |
1721 | 0 | tmp[i - 1] = '\0'; |
1722 | | |
1723 | | /* Add terminal features. */ |
1724 | 0 | if (strncmp(tmp, "iTerm2 ", 7) == 0) |
1725 | 0 | tty_default_features(c, "iTerm2", 0); |
1726 | 0 | else if (strncmp(tmp, "tmux ", 5) == 0) |
1727 | 0 | tty_default_features(c, "tmux", 0); |
1728 | 0 | else if (strncmp(tmp, "XTerm(", 6) == 0) |
1729 | 0 | tty_default_features(c, "XTerm", 0); |
1730 | 0 | else if (strncmp(tmp, "mintty ", 7) == 0) |
1731 | 0 | tty_default_features(c, "mintty", 0); |
1732 | 0 | else if (strncmp(tmp, "foot(", 5) == 0) |
1733 | 0 | tty_default_features(c, "foot", 0); |
1734 | 0 | else if (strncmp(tmp, "kitty(", 6) == 0) |
1735 | 0 | tty_default_features(c, "kitty", 0); |
1736 | 0 | else if (strncmp(tmp, "WezTerm ", 7) == 0) |
1737 | 0 | tty_default_features(c, "WezTerm", 0); |
1738 | 0 | else if (strncmp(tmp, "ghostty ", 8) == 0) |
1739 | 0 | tty_default_features(c, "ghostty", 0); |
1740 | 0 | else if (strncmp(tmp, "Rio ", 4) == 0) |
1741 | 0 | tty_default_features(c, "Rio", 0); |
1742 | 0 | log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); |
1743 | |
|
1744 | 0 | free(c->term_type); |
1745 | 0 | c->term_type = xstrdup(tmp); |
1746 | |
|
1747 | 0 | tty_update_features(tty); |
1748 | 0 | tty->flags |= TTY_HAVEXDA; |
1749 | |
|
1750 | 0 | return (0); |
1751 | 0 | } |
1752 | | |
1753 | | /* |
1754 | | * Handle foreground or background input. Returns 0 for success, -1 for |
1755 | | * failure, 1 for partial. If fg is NULL, only check the reply. |
1756 | | */ |
1757 | | int |
1758 | | tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size, |
1759 | | int *fg, int *bg) |
1760 | 0 | { |
1761 | 0 | struct client *c = tty->client; |
1762 | 0 | u_int i; |
1763 | 0 | char tmp[128]; |
1764 | 0 | int n; |
1765 | |
|
1766 | 0 | *size = 0; |
1767 | | |
1768 | | /* First four bytes are always \033]1 and 0 or 1 and ;. */ |
1769 | 0 | if (buf[0] != '\033') |
1770 | 0 | return (-1); |
1771 | 0 | if (len == 1) |
1772 | 0 | return (1); |
1773 | 0 | if (buf[1] != ']') |
1774 | 0 | return (-1); |
1775 | 0 | if (len == 2) |
1776 | 0 | return (1); |
1777 | 0 | if (buf[2] != '1') |
1778 | 0 | return (-1); |
1779 | 0 | if (len == 3) |
1780 | 0 | return (1); |
1781 | 0 | if (buf[3] != '0' && buf[3] != '1') |
1782 | 0 | return (-1); |
1783 | 0 | if (len == 4) |
1784 | 0 | return (1); |
1785 | 0 | if (buf[4] != ';') |
1786 | 0 | return (-1); |
1787 | 0 | if (len == 5) |
1788 | 0 | return (1); |
1789 | | |
1790 | | /* Copy the rest up to \033\ or \007. */ |
1791 | 0 | for (i = 0; i < (sizeof tmp) - 1; i++) { |
1792 | 0 | if (5 + i == len) |
1793 | 0 | return (1); |
1794 | 0 | if (buf[5 + i - 1] == '\033' && buf[5 + i] == '\\') |
1795 | 0 | break; |
1796 | 0 | if (buf[5 + i] == '\007') |
1797 | 0 | break; |
1798 | 0 | tmp[i] = buf[5 + i]; |
1799 | 0 | } |
1800 | 0 | if (i == (sizeof tmp) - 1) |
1801 | 0 | return (-1); |
1802 | 0 | *size = 6 + i; |
1803 | 0 | if (i == 0 || fg == NULL) |
1804 | 0 | return (0); |
1805 | 0 | if (tmp[i - 1] == '\033') |
1806 | 0 | tmp[i - 1] = '\0'; |
1807 | 0 | else |
1808 | 0 | tmp[i] = '\0'; |
1809 | | |
1810 | | /* Work out the colour. */ |
1811 | 0 | n = colour_parseX11(tmp); |
1812 | 0 | if (n != -1 && buf[3] == '0') { |
1813 | 0 | if (c != NULL) |
1814 | 0 | log_debug("%s fg is %s", c->name, colour_tostring(n)); |
1815 | 0 | else |
1816 | 0 | log_debug("fg is %s", colour_tostring(n)); |
1817 | 0 | *fg = n; |
1818 | 0 | tty->flags &= ~TTY_WAITFG; |
1819 | 0 | } else if (n != -1) { |
1820 | 0 | if (c != NULL) |
1821 | 0 | log_debug("%s bg is %s", c->name, colour_tostring(n)); |
1822 | 0 | else |
1823 | 0 | log_debug("bg is %s", colour_tostring(n)); |
1824 | 0 | *bg = n; |
1825 | 0 | tty->flags &= ~TTY_WAITBG; |
1826 | 0 | } |
1827 | |
|
1828 | 0 | return (0); |
1829 | 0 | } |
1830 | | |
1831 | | /* Handle foreground or background input from the terminal. */ |
1832 | | static int |
1833 | | tty_keys_colours1(struct tty *tty, const char *buf, size_t len, size_t *size, |
1834 | | int apply) |
1835 | 0 | { |
1836 | 0 | struct client *c = tty->client; |
1837 | 0 | int bg = tty->bg, n; |
1838 | |
|
1839 | 0 | if (!apply) |
1840 | 0 | return (tty_keys_colours(tty, buf, len, size, NULL, NULL)); |
1841 | | |
1842 | 0 | n = tty_keys_colours(tty, buf, len, size, &tty->fg, &tty->bg); |
1843 | 0 | if (n == 0) { |
1844 | 0 | if (tty->bg != bg) { |
1845 | 0 | server_client_update_theme_colours(c); |
1846 | 0 | if (tty->flags & TTY_OPENED) |
1847 | 0 | tty_invalidate(tty); |
1848 | 0 | server_redraw_client(c); |
1849 | 0 | } |
1850 | 0 | session_theme_changed(c->session); |
1851 | 0 | } |
1852 | 0 | return (n); |
1853 | 0 | } |
1854 | | |
1855 | | /* Handle OSC 4 palette colour responses. */ |
1856 | | static int |
1857 | | tty_keys_palette(struct tty *tty, const char *buf, size_t len, size_t *size, |
1858 | | int apply) |
1859 | 0 | { |
1860 | 0 | struct client *c = tty->client; |
1861 | 0 | u_int i; |
1862 | 0 | char tmp[128], *endptr; |
1863 | 0 | int idx; |
1864 | 0 | struct input_request_palette_data pd; |
1865 | |
|
1866 | 0 | *size = 0; |
1867 | | |
1868 | | /* First three bytes are always \033]4. */ |
1869 | 0 | if (buf[0] != '\033') |
1870 | 0 | return (-1); |
1871 | 0 | if (len == 1) |
1872 | 0 | return (1); |
1873 | 0 | if (buf[1] != ']') |
1874 | 0 | return (-1); |
1875 | 0 | if (len == 2) |
1876 | 0 | return (1); |
1877 | 0 | if (buf[2] != '4') |
1878 | 0 | return (-1); |
1879 | 0 | if (len == 3) |
1880 | 0 | return (1); |
1881 | 0 | if (buf[3] != ';') |
1882 | 0 | return (-1); |
1883 | 0 | if (len == 4) |
1884 | 0 | return (1); |
1885 | | |
1886 | | /* Copy the rest up to \033\ or \007. */ |
1887 | 0 | for (i = 0; i < (sizeof tmp) - 1; i++) { |
1888 | 0 | if (4 + i == len) |
1889 | 0 | return (1); |
1890 | 0 | if (buf[4 + i - 1] == '\033' && buf[4 + i] == '\\') |
1891 | 0 | break; |
1892 | 0 | if (buf[4 + i] == '\007') |
1893 | 0 | break; |
1894 | 0 | tmp[i] = buf[4 + i]; |
1895 | 0 | } |
1896 | 0 | if (i == (sizeof tmp) - 1) |
1897 | 0 | return (-1); |
1898 | 0 | *size = 5 + i; |
1899 | 0 | if (i == 0) |
1900 | 0 | return (0); |
1901 | 0 | if (tmp[i - 1] == '\033') |
1902 | 0 | tmp[i - 1] = '\0'; |
1903 | 0 | else |
1904 | 0 | tmp[i] = '\0'; |
1905 | | |
1906 | | /* Parse index. */ |
1907 | 0 | idx = strtol(tmp, &endptr, 10); |
1908 | 0 | if (*endptr != ';') |
1909 | 0 | return (-1); |
1910 | 0 | if (idx < 0 || idx > 255) |
1911 | 0 | return (-1); |
1912 | 0 | if (!apply) |
1913 | 0 | return (0); |
1914 | | |
1915 | | /* Work out the colour. */ |
1916 | 0 | pd.c = colour_parseX11(endptr + 1); |
1917 | 0 | if (pd.c == -1) |
1918 | 0 | return (0); |
1919 | 0 | pd.idx = idx; |
1920 | 0 | input_request_reply(c, INPUT_REQUEST_PALETTE, &pd); |
1921 | |
|
1922 | 0 | return (0); |
1923 | 0 | } |
1924 | | |
1925 | | /* Process window size change escape sequences. */ |
1926 | | static int |
1927 | | tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size, |
1928 | | int apply) |
1929 | 0 | { |
1930 | 0 | struct client *c = tty->client; |
1931 | 0 | size_t end; |
1932 | 0 | char tmp[64]; |
1933 | 0 | u_int sx, sy, xpixel, ypixel, char_x, char_y; |
1934 | 0 | int resize; |
1935 | |
|
1936 | 0 | *size = 0; |
1937 | | |
1938 | | /* If we did not request this, ignore it. */ |
1939 | 0 | if (!(tty->flags & (TTY_WINSZQUERY|TTY_WINSZPIXELS))) |
1940 | 0 | return (-1); |
1941 | | |
1942 | | /* First two bytes are always \033[. */ |
1943 | 0 | if (buf[0] != '\033') |
1944 | 0 | return (-1); |
1945 | 0 | if (len == 1) |
1946 | 0 | return (1); |
1947 | 0 | if (buf[1] != '[') |
1948 | 0 | return (-1); |
1949 | 0 | if (len == 2) |
1950 | 0 | return (1); |
1951 | | |
1952 | | /* Stop at either 't' or anything that isn't a number or ';'. */ |
1953 | 0 | for (end = 2; end < len && end != sizeof tmp; end++) { |
1954 | 0 | if (buf[end] == 't') |
1955 | 0 | break; |
1956 | 0 | if (!isdigit((u_char)buf[end]) && buf[end] != ';') |
1957 | 0 | break; |
1958 | 0 | } |
1959 | 0 | if (end == len) |
1960 | 0 | return (1); |
1961 | 0 | if (end == sizeof tmp || buf[end] != 't') |
1962 | 0 | return (-1); |
1963 | | |
1964 | | /* Copy to the buffer. */ |
1965 | 0 | memcpy(tmp, buf + 2, end - 2); |
1966 | 0 | tmp[end - 2] = '\0'; |
1967 | | |
1968 | | /* Try to parse the window size sequence. */ |
1969 | 0 | if (sscanf(tmp, "8;%u;%u", &sy, &sx) == 2) { |
1970 | 0 | if (!(tty->flags & TTY_WINSZQUERY)) |
1971 | 0 | return (-1); |
1972 | 0 | *size = end + 1; |
1973 | 0 | if (!apply) |
1974 | 0 | return (0); |
1975 | | |
1976 | 0 | resize = (tty->flags & TTY_WINSZAPPLY); |
1977 | 0 | tty->flags &= ~(TTY_WINSZQUERY|TTY_WINSZAPPLY); |
1978 | |
|
1979 | 0 | if (sx == 0 || |
1980 | 0 | sy == 0 || |
1981 | 0 | sx > CLIENT_MAX_TERM_SIZE || |
1982 | 0 | sy > CLIENT_MAX_TERM_SIZE) { |
1983 | 0 | log_debug("%s: ignoring reported size %ux%u", |
1984 | 0 | c->name, sx, sy); |
1985 | 0 | return (0); |
1986 | 0 | } |
1987 | 0 | if (resize) |
1988 | 0 | server_client_size_changed(c, sx, sy); |
1989 | 0 | else if (tty_set_size(tty, sx, sy, tty->xpixel, tty->ypixel)) |
1990 | 0 | server_redraw_client(c); |
1991 | 0 | return (0); |
1992 | 0 | } else if (sscanf(tmp, "4;%u;%u", &ypixel, &xpixel) == 2) { |
1993 | 0 | if (!(tty->flags & TTY_WINSZPIXELS)) |
1994 | 0 | return (-1); |
1995 | 0 | *size = end + 1; |
1996 | 0 | if (!apply) |
1997 | 0 | return (0); |
1998 | 0 | tty->flags &= ~TTY_WINSZPIXELS; |
1999 | |
|
2000 | 0 | char_x = (xpixel && tty->sx) ? xpixel / tty->sx : 0; |
2001 | 0 | char_y = (ypixel && tty->sy) ? ypixel / tty->sy : 0; |
2002 | 0 | if (tty_set_size(tty, tty->sx, tty->sy, char_x, char_y)) |
2003 | 0 | server_redraw_client(c); |
2004 | 0 | tty_invalidate(tty); |
2005 | 0 | recalculate_sizes(); |
2006 | 0 | return (0); |
2007 | 0 | } |
2008 | | |
2009 | 0 | log_debug("%s: unrecognized window size sequence: %s", c->name, tmp); |
2010 | 0 | return (-1); |
2011 | 0 | } |