1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2007 Nicholas Marriott <[email protected]> |
5 | * |
6 | * Permission to use, copy, modify, and distribute this software for any |
7 | * purpose with or without fee is hereby granted, provided that the above |
8 | * copyright notice and this permission notice appear in all copies. |
9 | * |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ |
18 | |
19 | #include <sys/types.h> |
20 | |
21 | #include <netinet/in.h> |
22 | |
23 | #include <ctype.h> |
24 | #include <resolv.h> |
25 | #include <stdlib.h> |
26 | #include <string.h> |
27 | #include <time.h> |
28 | |
29 | #include "tmux.h" |
30 | |
31 | /* |
32 | * Based on the description by Paul Williams at: |
33 | * |
34 | * https://vt100.net/emu/dec_ansi_parser |
35 | * |
36 | * With the following changes: |
37 | * |
38 | * - 7-bit only. |
39 | * |
40 | * - Support for UTF-8. |
41 | * |
42 | * - OSC (but not APC) may be terminated by \007 as well as ST. |
43 | * |
44 | * - A state for APC similar to OSC. Some terminals appear to use this to set |
45 | * the title. |
46 | * |
47 | * - A state for the screen \033k...\033\\ sequence to rename a window. This is |
48 | * pretty stupid but not supporting it is more trouble than it is worth. |
49 | * |
50 | * - Special handling for ESC inside a DCS to allow arbitrary byte sequences to |
51 | * be passed to the underlying terminals. |
52 | */ |
53 | |
54 | /* Input parser cell. */ |
55 | struct input_cell { |
56 | struct grid_cell cell; |
57 | int set; |
58 | int g0set; /* 1 if ACS */ |
59 | int g1set; /* 1 if ACS */ |
60 | }; |
61 | |
62 | /* Input parser argument. */ |
63 | struct input_param { |
64 | enum { |
65 | INPUT_MISSING, |
66 | INPUT_NUMBER, |
67 | INPUT_STRING |
68 | } type; |
69 | union { |
70 | int num; |
71 | char *str; |
72 | }; |
73 | }; |
74 | |
75 | /* Input parser context. */ |
76 | struct input_ctx { |
77 | struct window_pane *wp; |
78 | struct screen_write_ctx ctx; |
79 | |
80 | struct input_cell cell; |
81 | |
82 | struct input_cell old_cell; |
83 | u_int old_cx; |
84 | u_int old_cy; |
85 | int old_mode; |
86 | |
87 | u_char interm_buf[4]; |
88 | size_t interm_len; |
89 | |
90 | u_char param_buf[64]; |
91 | size_t param_len; |
92 | |
93 | #define INPUT_BUF_START 32 |
94 | #define INPUT_BUF_LIMIT 1048576 |
95 | u_char *input_buf; |
96 | size_t input_len; |
97 | size_t input_space; |
98 | enum { |
99 | INPUT_END_ST, |
100 | INPUT_END_BEL |
101 | } input_end; |
102 | |
103 | struct input_param param_list[24]; |
104 | u_int param_list_len; |
105 | |
106 | struct utf8_data utf8data; |
107 | int utf8started; |
108 | |
109 | int ch; |
110 | int last; |
111 | |
112 | int flags; |
113 | #define INPUT_DISCARD 0x1 |
114 | |
115 | const struct input_state *state; |
116 | |
117 | struct event timer; |
118 | |
119 | /* |
120 | * All input received since we were last in the ground state. Sent to |
121 | * control clients on connection. |
122 | */ |
123 | struct evbuffer *since_ground; |
124 | }; |
125 | |
126 | /* Helper functions. */ |
127 | struct input_transition; |
128 | static int input_split(struct input_ctx *); |
129 | static int input_get(struct input_ctx *, u_int, int, int); |
130 | static void printflike(2, 3) input_reply(struct input_ctx *, const char *, ...); |
131 | static void input_set_state(struct window_pane *, |
132 | const struct input_transition *); |
133 | static void input_reset_cell(struct input_ctx *); |
134 | |
135 | static void input_osc_4(struct input_ctx *, const char *); |
136 | static void input_osc_10(struct input_ctx *, const char *); |
137 | static void input_osc_11(struct input_ctx *, const char *); |
138 | static void input_osc_52(struct input_ctx *, const char *); |
139 | static void input_osc_104(struct input_ctx *, const char *); |
140 | |
141 | /* Transition entry/exit handlers. */ |
142 | static void input_clear(struct input_ctx *); |
143 | static void input_ground(struct input_ctx *); |
144 | static void input_enter_dcs(struct input_ctx *); |
145 | static void input_enter_osc(struct input_ctx *); |
146 | static void input_exit_osc(struct input_ctx *); |
147 | static void input_enter_apc(struct input_ctx *); |
148 | static void input_exit_apc(struct input_ctx *); |
149 | static void input_enter_rename(struct input_ctx *); |
150 | static void input_exit_rename(struct input_ctx *); |
151 | |
152 | /* Input state handlers. */ |
153 | static int input_print(struct input_ctx *); |
154 | static int input_intermediate(struct input_ctx *); |
155 | static int input_parameter(struct input_ctx *); |
156 | static int input_input(struct input_ctx *); |
157 | static int input_c0_dispatch(struct input_ctx *); |
158 | static int input_esc_dispatch(struct input_ctx *); |
159 | static int input_csi_dispatch(struct input_ctx *); |
160 | static void input_csi_dispatch_rm(struct input_ctx *); |
161 | static void input_csi_dispatch_rm_private(struct input_ctx *); |
162 | static void input_csi_dispatch_sm(struct input_ctx *); |
163 | static void input_csi_dispatch_sm_private(struct input_ctx *); |
164 | static void input_csi_dispatch_winops(struct input_ctx *); |
165 | static void input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *); |
166 | static void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *); |
167 | static void input_csi_dispatch_sgr(struct input_ctx *); |
168 | static int input_dcs_dispatch(struct input_ctx *); |
169 | static int input_top_bit_set(struct input_ctx *); |
170 | static int input_end_bel(struct input_ctx *); |
171 | |
172 | /* Command table comparison function. */ |
173 | static int input_table_compare(const void *, const void *); |
174 | |
175 | /* Command table entry. */ |
176 | struct input_table_entry { |
177 | int ch; |
178 | const char *interm; |
179 | int type; |
180 | }; |
181 | |
182 | /* Escape commands. */ |
183 | enum input_esc_type { |
184 | INPUT_ESC_DECALN, |
185 | INPUT_ESC_DECKPAM, |
186 | INPUT_ESC_DECKPNM, |
187 | INPUT_ESC_DECRC, |
188 | INPUT_ESC_DECSC, |
189 | INPUT_ESC_HTS, |
190 | INPUT_ESC_IND, |
191 | INPUT_ESC_NEL, |
192 | INPUT_ESC_RI, |
193 | INPUT_ESC_RIS, |
194 | INPUT_ESC_SCSG0_OFF, |
195 | INPUT_ESC_SCSG0_ON, |
196 | INPUT_ESC_SCSG1_OFF, |
197 | INPUT_ESC_SCSG1_ON, |
198 | INPUT_ESC_ST, |
199 | }; |
200 | |
201 | /* Escape command table. */ |
202 | static const struct input_table_entry input_esc_table[] = { |
203 | { '0', "(" , INPUT_ESC_SCSG0_ON }, |
204 | { '0', ")" , INPUT_ESC_SCSG1_ON }, |
205 | { '7', "" , INPUT_ESC_DECSC }, |
206 | { '8', "" , INPUT_ESC_DECRC }, |
207 | { '8', "#" , INPUT_ESC_DECALN }, |
208 | { '=', "" , INPUT_ESC_DECKPAM }, |
209 | { '>', "" , INPUT_ESC_DECKPNM }, |
210 | { 'B', "(" , INPUT_ESC_SCSG0_OFF }, |
211 | { 'B', ")" , INPUT_ESC_SCSG1_OFF }, |
212 | { 'D', "" , INPUT_ESC_IND }, |
213 | { 'E', "" , INPUT_ESC_NEL }, |
214 | { 'H', "" , INPUT_ESC_HTS }, |
215 | { 'M', "" , INPUT_ESC_RI }, |
216 | { '\\', "" , INPUT_ESC_ST }, |
217 | { 'c', "" , INPUT_ESC_RIS }, |
218 | }; |
219 | |
220 | /* Control (CSI) commands. */ |
221 | enum input_csi_type { |
222 | INPUT_CSI_CBT, |
223 | INPUT_CSI_CNL, |
224 | INPUT_CSI_CPL, |
225 | INPUT_CSI_CUB, |
226 | INPUT_CSI_CUD, |
227 | INPUT_CSI_CUF, |
228 | INPUT_CSI_CUP, |
229 | INPUT_CSI_CUU, |
230 | INPUT_CSI_DA, |
231 | INPUT_CSI_DA_TWO, |
232 | INPUT_CSI_DCH, |
233 | INPUT_CSI_DECSCUSR, |
234 | INPUT_CSI_DECSTBM, |
235 | INPUT_CSI_DL, |
236 | INPUT_CSI_DSR, |
237 | INPUT_CSI_ECH, |
238 | INPUT_CSI_ED, |
239 | INPUT_CSI_EL, |
240 | INPUT_CSI_HPA, |
241 | INPUT_CSI_ICH, |
242 | INPUT_CSI_IL, |
243 | INPUT_CSI_RCP, |
244 | INPUT_CSI_REP, |
245 | INPUT_CSI_RM, |
246 | INPUT_CSI_RM_PRIVATE, |
247 | INPUT_CSI_SCP, |
248 | INPUT_CSI_SD, |
249 | INPUT_CSI_SGR, |
250 | INPUT_CSI_SM, |
251 | INPUT_CSI_SM_PRIVATE, |
252 | INPUT_CSI_SU, |
253 | INPUT_CSI_TBC, |
254 | INPUT_CSI_VPA, |
255 | INPUT_CSI_WINOPS, |
256 | }; |
257 | |
258 | /* Control (CSI) command table. */ |
259 | static const struct input_table_entry input_csi_table[] = { |
260 | { '@', "" , INPUT_CSI_ICH }, |
261 | { 'A', "" , INPUT_CSI_CUU }, |
262 | { 'B', "" , INPUT_CSI_CUD }, |
263 | { 'C', "" , INPUT_CSI_CUF }, |
264 | { 'D', "" , INPUT_CSI_CUB }, |
265 | { 'E', "" , INPUT_CSI_CNL }, |
266 | { 'F', "" , INPUT_CSI_CPL }, |
267 | { 'G', "" , INPUT_CSI_HPA }, |
268 | { 'H', "" , INPUT_CSI_CUP }, |
269 | { 'J', "" , INPUT_CSI_ED }, |
270 | { 'K', "" , INPUT_CSI_EL }, |
271 | { 'L', "" , INPUT_CSI_IL }, |
272 | { 'M', "" , INPUT_CSI_DL }, |
273 | { 'P', "" , INPUT_CSI_DCH }, |
274 | { 'S', "" , INPUT_CSI_SU }, |
275 | { 'T', "" , INPUT_CSI_SD }, |
276 | { 'X', "" , INPUT_CSI_ECH }, |
277 | { 'Z', "" , INPUT_CSI_CBT }, |
278 | { '`', "" , INPUT_CSI_HPA }, |
279 | { 'b', "" , INPUT_CSI_REP }, |
280 | { 'c', "" , INPUT_CSI_DA }, |
281 | { 'c', ">" , INPUT_CSI_DA_TWO }, |
282 | { 'd', "" , INPUT_CSI_VPA }, |
283 | { 'f', "" , INPUT_CSI_CUP }, |
284 | { 'g', "" , INPUT_CSI_TBC }, |
285 | { 'h', "" , INPUT_CSI_SM }, |
286 | { 'h', "?" , INPUT_CSI_SM_PRIVATE }, |
287 | { 'l', "" , INPUT_CSI_RM }, |
288 | { 'l', "?" , INPUT_CSI_RM_PRIVATE }, |
289 | { 'm', "" , INPUT_CSI_SGR }, |
290 | { 'n', "" , INPUT_CSI_DSR }, |
291 | { 'q', " " , INPUT_CSI_DECSCUSR }, |
292 | { 'r', "" , INPUT_CSI_DECSTBM }, |
293 | { 's', "" , INPUT_CSI_SCP }, |
294 | { 't', "" , INPUT_CSI_WINOPS }, |
295 | { 'u', "" , INPUT_CSI_RCP }, |
296 | }; |
297 | |
298 | /* Input transition. */ |
299 | struct input_transition { |
300 | int first; |
301 | int last; |
302 | |
303 | int (*handler)(struct input_ctx *); |
304 | const struct input_state *state; |
305 | }; |
306 | |
307 | /* Input state. */ |
308 | struct input_state { |
309 | const char *name; |
310 | void (*enter)(struct input_ctx *); |
311 | void (*exit)(struct input_ctx *); |
312 | const struct input_transition *transitions; |
313 | }; |
314 | |
315 | /* State transitions available from all states. */ |
316 | #define INPUT_STATE_ANYWHERE \ |
317 | { 0x18, 0x18, input_c0_dispatch, &input_state_ground }, \ |
318 | { 0x1a, 0x1a, input_c0_dispatch, &input_state_ground }, \ |
319 | { 0x1b, 0x1b, NULL, &input_state_esc_enter } |
320 | |
321 | /* Forward declarations of state tables. */ |
322 | static const struct input_transition input_state_ground_table[]; |
323 | static const struct input_transition input_state_esc_enter_table[]; |
324 | static const struct input_transition input_state_esc_intermediate_table[]; |
325 | static const struct input_transition input_state_csi_enter_table[]; |
326 | static const struct input_transition input_state_csi_parameter_table[]; |
327 | static const struct input_transition input_state_csi_intermediate_table[]; |
328 | static const struct input_transition input_state_csi_ignore_table[]; |
329 | static const struct input_transition input_state_dcs_enter_table[]; |
330 | static const struct input_transition input_state_dcs_parameter_table[]; |
331 | static const struct input_transition input_state_dcs_intermediate_table[]; |
332 | static const struct input_transition input_state_dcs_handler_table[]; |
333 | static const struct input_transition input_state_dcs_escape_table[]; |
334 | static const struct input_transition input_state_dcs_ignore_table[]; |
335 | static const struct input_transition input_state_osc_string_table[]; |
336 | static const struct input_transition input_state_apc_string_table[]; |
337 | static const struct input_transition input_state_rename_string_table[]; |
338 | static const struct input_transition input_state_consume_st_table[]; |
339 | |
340 | /* ground state definition. */ |
341 | static const struct input_state input_state_ground = { |
342 | "ground" , |
343 | input_ground, NULL, |
344 | input_state_ground_table |
345 | }; |
346 | |
347 | /* esc_enter state definition. */ |
348 | static const struct input_state input_state_esc_enter = { |
349 | "esc_enter" , |
350 | input_clear, NULL, |
351 | input_state_esc_enter_table |
352 | }; |
353 | |
354 | /* esc_intermediate state definition. */ |
355 | static const struct input_state input_state_esc_intermediate = { |
356 | "esc_intermediate" , |
357 | NULL, NULL, |
358 | input_state_esc_intermediate_table |
359 | }; |
360 | |
361 | /* csi_enter state definition. */ |
362 | static const struct input_state input_state_csi_enter = { |
363 | "csi_enter" , |
364 | input_clear, NULL, |
365 | input_state_csi_enter_table |
366 | }; |
367 | |
368 | /* csi_parameter state definition. */ |
369 | static const struct input_state input_state_csi_parameter = { |
370 | "csi_parameter" , |
371 | NULL, NULL, |
372 | input_state_csi_parameter_table |
373 | }; |
374 | |
375 | /* csi_intermediate state definition. */ |
376 | static const struct input_state input_state_csi_intermediate = { |
377 | "csi_intermediate" , |
378 | NULL, NULL, |
379 | input_state_csi_intermediate_table |
380 | }; |
381 | |
382 | /* csi_ignore state definition. */ |
383 | static const struct input_state input_state_csi_ignore = { |
384 | "csi_ignore" , |
385 | NULL, NULL, |
386 | input_state_csi_ignore_table |
387 | }; |
388 | |
389 | /* dcs_enter state definition. */ |
390 | static const struct input_state input_state_dcs_enter = { |
391 | "dcs_enter" , |
392 | input_enter_dcs, NULL, |
393 | input_state_dcs_enter_table |
394 | }; |
395 | |
396 | /* dcs_parameter state definition. */ |
397 | static const struct input_state input_state_dcs_parameter = { |
398 | "dcs_parameter" , |
399 | NULL, NULL, |
400 | input_state_dcs_parameter_table |
401 | }; |
402 | |
403 | /* dcs_intermediate state definition. */ |
404 | static const struct input_state input_state_dcs_intermediate = { |
405 | "dcs_intermediate" , |
406 | NULL, NULL, |
407 | input_state_dcs_intermediate_table |
408 | }; |
409 | |
410 | /* dcs_handler state definition. */ |
411 | static const struct input_state input_state_dcs_handler = { |
412 | "dcs_handler" , |
413 | NULL, NULL, |
414 | input_state_dcs_handler_table |
415 | }; |
416 | |
417 | /* dcs_escape state definition. */ |
418 | static const struct input_state input_state_dcs_escape = { |
419 | "dcs_escape" , |
420 | NULL, NULL, |
421 | input_state_dcs_escape_table |
422 | }; |
423 | |
424 | /* dcs_ignore state definition. */ |
425 | static const struct input_state input_state_dcs_ignore = { |
426 | "dcs_ignore" , |
427 | NULL, NULL, |
428 | input_state_dcs_ignore_table |
429 | }; |
430 | |
431 | /* osc_string state definition. */ |
432 | static const struct input_state input_state_osc_string = { |
433 | "osc_string" , |
434 | input_enter_osc, input_exit_osc, |
435 | input_state_osc_string_table |
436 | }; |
437 | |
438 | /* apc_string state definition. */ |
439 | static const struct input_state input_state_apc_string = { |
440 | "apc_string" , |
441 | input_enter_apc, input_exit_apc, |
442 | input_state_apc_string_table |
443 | }; |
444 | |
445 | /* rename_string state definition. */ |
446 | static const struct input_state input_state_rename_string = { |
447 | "rename_string" , |
448 | input_enter_rename, input_exit_rename, |
449 | input_state_rename_string_table |
450 | }; |
451 | |
452 | /* consume_st state definition. */ |
453 | static const struct input_state input_state_consume_st = { |
454 | "consume_st" , |
455 | input_enter_rename, NULL, /* rename also waits for ST */ |
456 | input_state_consume_st_table |
457 | }; |
458 | |
459 | /* ground state table. */ |
460 | static const struct input_transition input_state_ground_table[] = { |
461 | INPUT_STATE_ANYWHERE, |
462 | |
463 | { 0x00, 0x17, input_c0_dispatch, NULL }, |
464 | { 0x19, 0x19, input_c0_dispatch, NULL }, |
465 | { 0x1c, 0x1f, input_c0_dispatch, NULL }, |
466 | { 0x20, 0x7e, input_print, NULL }, |
467 | { 0x7f, 0x7f, NULL, NULL }, |
468 | { 0x80, 0xff, input_top_bit_set, NULL }, |
469 | |
470 | { -1, -1, NULL, NULL } |
471 | }; |
472 | |
473 | /* esc_enter state table. */ |
474 | static const struct input_transition input_state_esc_enter_table[] = { |
475 | INPUT_STATE_ANYWHERE, |
476 | |
477 | { 0x00, 0x17, input_c0_dispatch, NULL }, |
478 | { 0x19, 0x19, input_c0_dispatch, NULL }, |
479 | { 0x1c, 0x1f, input_c0_dispatch, NULL }, |
480 | { 0x20, 0x2f, input_intermediate, &input_state_esc_intermediate }, |
481 | { 0x30, 0x4f, input_esc_dispatch, &input_state_ground }, |
482 | { 0x50, 0x50, NULL, &input_state_dcs_enter }, |
483 | { 0x51, 0x57, input_esc_dispatch, &input_state_ground }, |
484 | { 0x58, 0x58, NULL, &input_state_consume_st }, |
485 | { 0x59, 0x59, input_esc_dispatch, &input_state_ground }, |
486 | { 0x5a, 0x5a, input_esc_dispatch, &input_state_ground }, |
487 | { 0x5b, 0x5b, NULL, &input_state_csi_enter }, |
488 | { 0x5c, 0x5c, input_esc_dispatch, &input_state_ground }, |
489 | { 0x5d, 0x5d, NULL, &input_state_osc_string }, |
490 | { 0x5e, 0x5e, NULL, &input_state_consume_st }, |
491 | { 0x5f, 0x5f, NULL, &input_state_apc_string }, |
492 | { 0x60, 0x6a, input_esc_dispatch, &input_state_ground }, |
493 | { 0x6b, 0x6b, NULL, &input_state_rename_string }, |
494 | { 0x6c, 0x7e, input_esc_dispatch, &input_state_ground }, |
495 | { 0x7f, 0xff, NULL, NULL }, |
496 | |
497 | { -1, -1, NULL, NULL } |
498 | }; |
499 | |
500 | /* esc_intermediate state table. */ |
501 | static const struct input_transition input_state_esc_intermediate_table[] = { |
502 | INPUT_STATE_ANYWHERE, |
503 | |
504 | { 0x00, 0x17, input_c0_dispatch, NULL }, |
505 | { 0x19, 0x19, input_c0_dispatch, NULL }, |
506 | { 0x1c, 0x1f, input_c0_dispatch, NULL }, |
507 | { 0x20, 0x2f, input_intermediate, NULL }, |
508 | { 0x30, 0x7e, input_esc_dispatch, &input_state_ground }, |
509 | { 0x7f, 0xff, NULL, NULL }, |
510 | |
511 | { -1, -1, NULL, NULL } |
512 | }; |
513 | |
514 | /* csi_enter state table. */ |
515 | static const struct input_transition input_state_csi_enter_table[] = { |
516 | INPUT_STATE_ANYWHERE, |
517 | |
518 | { 0x00, 0x17, input_c0_dispatch, NULL }, |
519 | { 0x19, 0x19, input_c0_dispatch, NULL }, |
520 | { 0x1c, 0x1f, input_c0_dispatch, NULL }, |
521 | { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, |
522 | { 0x30, 0x39, input_parameter, &input_state_csi_parameter }, |
523 | { 0x3a, 0x3a, input_parameter, &input_state_csi_parameter }, |
524 | { 0x3b, 0x3b, input_parameter, &input_state_csi_parameter }, |
525 | { 0x3c, 0x3f, input_intermediate, &input_state_csi_parameter }, |
526 | { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, |
527 | { 0x7f, 0xff, NULL, NULL }, |
528 | |
529 | { -1, -1, NULL, NULL } |
530 | }; |
531 | |
532 | /* csi_parameter state table. */ |
533 | static const struct input_transition input_state_csi_parameter_table[] = { |
534 | INPUT_STATE_ANYWHERE, |
535 | |
536 | { 0x00, 0x17, input_c0_dispatch, NULL }, |
537 | { 0x19, 0x19, input_c0_dispatch, NULL }, |
538 | { 0x1c, 0x1f, input_c0_dispatch, NULL }, |
539 | { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, |
540 | { 0x30, 0x39, input_parameter, NULL }, |
541 | { 0x3a, 0x3a, input_parameter, NULL }, |
542 | { 0x3b, 0x3b, input_parameter, NULL }, |
543 | { 0x3c, 0x3f, NULL, &input_state_csi_ignore }, |
544 | { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, |
545 | { 0x7f, 0xff, NULL, NULL }, |
546 | |
547 | { -1, -1, NULL, NULL } |
548 | }; |
549 | |
550 | /* csi_intermediate state table. */ |
551 | static const struct input_transition input_state_csi_intermediate_table[] = { |
552 | INPUT_STATE_ANYWHERE, |
553 | |
554 | { 0x00, 0x17, input_c0_dispatch, NULL }, |
555 | { 0x19, 0x19, input_c0_dispatch, NULL }, |
556 | { 0x1c, 0x1f, input_c0_dispatch, NULL }, |
557 | { 0x20, 0x2f, input_intermediate, NULL }, |
558 | { 0x30, 0x3f, NULL, &input_state_csi_ignore }, |
559 | { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, |
560 | { 0x7f, 0xff, NULL, NULL }, |
561 | |
562 | { -1, -1, NULL, NULL } |
563 | }; |
564 | |
565 | /* csi_ignore state table. */ |
566 | static const struct input_transition input_state_csi_ignore_table[] = { |
567 | INPUT_STATE_ANYWHERE, |
568 | |
569 | { 0x00, 0x17, input_c0_dispatch, NULL }, |
570 | { 0x19, 0x19, input_c0_dispatch, NULL }, |
571 | { 0x1c, 0x1f, input_c0_dispatch, NULL }, |
572 | { 0x20, 0x3f, NULL, NULL }, |
573 | { 0x40, 0x7e, NULL, &input_state_ground }, |
574 | { 0x7f, 0xff, NULL, NULL }, |
575 | |
576 | { -1, -1, NULL, NULL } |
577 | }; |
578 | |
579 | /* dcs_enter state table. */ |
580 | static const struct input_transition input_state_dcs_enter_table[] = { |
581 | INPUT_STATE_ANYWHERE, |
582 | |
583 | { 0x00, 0x17, NULL, NULL }, |
584 | { 0x19, 0x19, NULL, NULL }, |
585 | { 0x1c, 0x1f, NULL, NULL }, |
586 | { 0x20, 0x2f, input_intermediate, &input_state_dcs_intermediate }, |
587 | { 0x30, 0x39, input_parameter, &input_state_dcs_parameter }, |
588 | { 0x3a, 0x3a, NULL, &input_state_dcs_ignore }, |
589 | { 0x3b, 0x3b, input_parameter, &input_state_dcs_parameter }, |
590 | { 0x3c, 0x3f, input_intermediate, &input_state_dcs_parameter }, |
591 | { 0x40, 0x7e, input_input, &input_state_dcs_handler }, |
592 | { 0x7f, 0xff, NULL, NULL }, |
593 | |
594 | { -1, -1, NULL, NULL } |
595 | }; |
596 | |
597 | /* dcs_parameter state table. */ |
598 | static const struct input_transition input_state_dcs_parameter_table[] = { |
599 | INPUT_STATE_ANYWHERE, |
600 | |
601 | { 0x00, 0x17, NULL, NULL }, |
602 | { 0x19, 0x19, NULL, NULL }, |
603 | { 0x1c, 0x1f, NULL, NULL }, |
604 | { 0x20, 0x2f, input_intermediate, &input_state_dcs_intermediate }, |
605 | { 0x30, 0x39, input_parameter, NULL }, |
606 | { 0x3a, 0x3a, NULL, &input_state_dcs_ignore }, |
607 | { 0x3b, 0x3b, input_parameter, NULL }, |
608 | { 0x3c, 0x3f, NULL, &input_state_dcs_ignore }, |
609 | { 0x40, 0x7e, input_input, &input_state_dcs_handler }, |
610 | { 0x7f, 0xff, NULL, NULL }, |
611 | |
612 | { -1, -1, NULL, NULL } |
613 | }; |
614 | |
615 | /* dcs_intermediate state table. */ |
616 | static const struct input_transition input_state_dcs_intermediate_table[] = { |
617 | INPUT_STATE_ANYWHERE, |
618 | |
619 | { 0x00, 0x17, NULL, NULL }, |
620 | { 0x19, 0x19, NULL, NULL }, |
621 | { 0x1c, 0x1f, NULL, NULL }, |
622 | { 0x20, 0x2f, input_intermediate, NULL }, |
623 | { 0x30, 0x3f, NULL, &input_state_dcs_ignore }, |
624 | { 0x40, 0x7e, input_input, &input_state_dcs_handler }, |
625 | { 0x7f, 0xff, NULL, NULL }, |
626 | |
627 | { -1, -1, NULL, NULL } |
628 | }; |
629 | |
630 | /* dcs_handler state table. */ |
631 | static const struct input_transition input_state_dcs_handler_table[] = { |
632 | /* No INPUT_STATE_ANYWHERE */ |
633 | |
634 | { 0x00, 0x1a, input_input, NULL }, |
635 | { 0x1b, 0x1b, NULL, &input_state_dcs_escape }, |
636 | { 0x1c, 0xff, input_input, NULL }, |
637 | |
638 | { -1, -1, NULL, NULL } |
639 | }; |
640 | |
641 | /* dcs_escape state table. */ |
642 | static const struct input_transition input_state_dcs_escape_table[] = { |
643 | /* No INPUT_STATE_ANYWHERE */ |
644 | |
645 | { 0x00, 0x5b, input_input, &input_state_dcs_handler }, |
646 | { 0x5c, 0x5c, input_dcs_dispatch, &input_state_ground }, |
647 | { 0x5d, 0xff, input_input, &input_state_dcs_handler }, |
648 | |
649 | { -1, -1, NULL, NULL } |
650 | }; |
651 | |
652 | /* dcs_ignore state table. */ |
653 | static const struct input_transition input_state_dcs_ignore_table[] = { |
654 | INPUT_STATE_ANYWHERE, |
655 | |
656 | { 0x00, 0x17, NULL, NULL }, |
657 | { 0x19, 0x19, NULL, NULL }, |
658 | { 0x1c, 0x1f, NULL, NULL }, |
659 | { 0x20, 0xff, NULL, NULL }, |
660 | |
661 | { -1, -1, NULL, NULL } |
662 | }; |
663 | |
664 | /* osc_string state table. */ |
665 | static const struct input_transition input_state_osc_string_table[] = { |
666 | INPUT_STATE_ANYWHERE, |
667 | |
668 | { 0x00, 0x06, NULL, NULL }, |
669 | { 0x07, 0x07, input_end_bel, &input_state_ground }, |
670 | { 0x08, 0x17, NULL, NULL }, |
671 | { 0x19, 0x19, NULL, NULL }, |
672 | { 0x1c, 0x1f, NULL, NULL }, |
673 | { 0x20, 0xff, input_input, NULL }, |
674 | |
675 | { -1, -1, NULL, NULL } |
676 | }; |
677 | |
678 | /* apc_string state table. */ |
679 | static const struct input_transition input_state_apc_string_table[] = { |
680 | INPUT_STATE_ANYWHERE, |
681 | |
682 | { 0x00, 0x17, NULL, NULL }, |
683 | { 0x19, 0x19, NULL, NULL }, |
684 | { 0x1c, 0x1f, NULL, NULL }, |
685 | { 0x20, 0xff, input_input, NULL }, |
686 | |
687 | { -1, -1, NULL, NULL } |
688 | }; |
689 | |
690 | /* rename_string state table. */ |
691 | static const struct input_transition input_state_rename_string_table[] = { |
692 | INPUT_STATE_ANYWHERE, |
693 | |
694 | { 0x00, 0x17, NULL, NULL }, |
695 | { 0x19, 0x19, NULL, NULL }, |
696 | { 0x1c, 0x1f, NULL, NULL }, |
697 | { 0x20, 0xff, input_input, NULL }, |
698 | |
699 | { -1, -1, NULL, NULL } |
700 | }; |
701 | |
702 | /* consume_st state table. */ |
703 | static const struct input_transition input_state_consume_st_table[] = { |
704 | INPUT_STATE_ANYWHERE, |
705 | |
706 | { 0x00, 0x17, NULL, NULL }, |
707 | { 0x19, 0x19, NULL, NULL }, |
708 | { 0x1c, 0x1f, NULL, NULL }, |
709 | { 0x20, 0xff, NULL, NULL }, |
710 | |
711 | { -1, -1, NULL, NULL } |
712 | }; |
713 | |
714 | /* Input table compare. */ |
715 | static int |
716 | input_table_compare(const void *key, const void *value) |
717 | { |
718 | const struct input_ctx *ictx = key; |
719 | const struct input_table_entry *entry = value; |
720 | |
721 | if (ictx->ch != entry->ch) |
722 | return (ictx->ch - entry->ch); |
723 | return (strcmp(ictx->interm_buf, entry->interm)); |
724 | } |
725 | |
726 | /* |
727 | * Timer - if this expires then have been waiting for a terminator for too |
728 | * long, so reset to ground. |
729 | */ |
730 | static void |
731 | input_timer_callback(__unused int fd, __unused short events, void *arg) |
732 | { |
733 | struct input_ctx *ictx = arg; |
734 | struct window_pane *wp = ictx->wp; |
735 | |
736 | log_debug("%s: %%%u %s expired" , __func__, wp->id, ictx->state->name); |
737 | input_reset(wp, 0); |
738 | } |
739 | |
740 | /* Start the timer. */ |
741 | static void |
742 | input_start_timer(struct input_ctx *ictx) |
743 | { |
744 | struct timeval tv = { .tv_sec = 5, .tv_usec = 0 }; |
745 | |
746 | event_del(&ictx->timer); |
747 | event_add(&ictx->timer, &tv); |
748 | } |
749 | |
750 | /* Reset cell state to default. */ |
751 | static void |
752 | input_reset_cell(struct input_ctx *ictx) |
753 | { |
754 | memcpy(&ictx->cell.cell, &grid_default_cell, sizeof ictx->cell.cell); |
755 | ictx->cell.set = 0; |
756 | ictx->cell.g0set = ictx->cell.g1set = 0; |
757 | |
758 | memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); |
759 | ictx->old_cx = 0; |
760 | ictx->old_cy = 0; |
761 | } |
762 | |
763 | /* Save screen state. */ |
764 | static void |
765 | input_save_state(struct input_ctx *ictx) |
766 | { |
767 | struct screen_write_ctx *sctx = &ictx->ctx; |
768 | struct screen *s = sctx->s; |
769 | |
770 | memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); |
771 | ictx->old_cx = s->cx; |
772 | ictx->old_cy = s->cy; |
773 | ictx->old_mode = s->mode; |
774 | } |
775 | |
776 | /* Restore screen state. */ |
777 | static void |
778 | input_restore_state(struct input_ctx *ictx) |
779 | { |
780 | struct screen_write_ctx *sctx = &ictx->ctx; |
781 | |
782 | memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell); |
783 | if (ictx->old_mode & MODE_ORIGIN) |
784 | screen_write_mode_set(sctx, MODE_ORIGIN); |
785 | else |
786 | screen_write_mode_clear(sctx, MODE_ORIGIN); |
787 | screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy, 0); |
788 | } |
789 | |
790 | /* Initialise input parser. */ |
791 | void |
792 | input_init(struct window_pane *wp) |
793 | { |
794 | struct input_ctx *ictx; |
795 | |
796 | ictx = wp->ictx = xcalloc(1, sizeof *ictx); |
797 | |
798 | ictx->input_space = INPUT_BUF_START; |
799 | ictx->input_buf = xmalloc(INPUT_BUF_START); |
800 | |
801 | ictx->since_ground = evbuffer_new(); |
802 | if (ictx->since_ground == NULL) |
803 | fatalx("out of memory" ); |
804 | |
805 | evtimer_set(&ictx->timer, input_timer_callback, ictx); |
806 | |
807 | input_reset(wp, 0); |
808 | } |
809 | |
810 | /* Destroy input parser. */ |
811 | void |
812 | input_free(struct window_pane *wp) |
813 | { |
814 | struct input_ctx *ictx = wp->ictx; |
815 | u_int i; |
816 | |
817 | for (i = 0; i < ictx->param_list_len; i++) { |
818 | if (ictx->param_list[i].type == INPUT_STRING) |
819 | free(ictx->param_list[i].str); |
820 | } |
821 | |
822 | event_del(&ictx->timer); |
823 | |
824 | free(ictx->input_buf); |
825 | evbuffer_free(ictx->since_ground); |
826 | |
827 | free(ictx); |
828 | wp->ictx = NULL; |
829 | } |
830 | |
831 | /* Reset input state and clear screen. */ |
832 | void |
833 | input_reset(struct window_pane *wp, int clear) |
834 | { |
835 | struct input_ctx *ictx = wp->ictx; |
836 | struct screen_write_ctx *sctx = &ictx->ctx; |
837 | |
838 | input_reset_cell(ictx); |
839 | |
840 | if (clear) { |
841 | if (TAILQ_EMPTY(&wp->modes)) |
842 | screen_write_start(sctx, wp, &wp->base); |
843 | else |
844 | screen_write_start(sctx, NULL, &wp->base); |
845 | screen_write_reset(sctx); |
846 | screen_write_stop(sctx); |
847 | } |
848 | |
849 | input_clear(ictx); |
850 | |
851 | ictx->last = -1; |
852 | |
853 | ictx->state = &input_state_ground; |
854 | ictx->flags = 0; |
855 | } |
856 | |
857 | /* Return pending data. */ |
858 | struct evbuffer * |
859 | input_pending(struct window_pane *wp) |
860 | { |
861 | return (wp->ictx->since_ground); |
862 | } |
863 | |
864 | /* Change input state. */ |
865 | static void |
866 | input_set_state(struct window_pane *wp, const struct input_transition *itr) |
867 | { |
868 | struct input_ctx *ictx = wp->ictx; |
869 | |
870 | if (ictx->state->exit != NULL) |
871 | ictx->state->exit(ictx); |
872 | ictx->state = itr->state; |
873 | if (ictx->state->enter != NULL) |
874 | ictx->state->enter(ictx); |
875 | } |
876 | |
877 | /* Parse input. */ |
878 | void |
879 | input_parse(struct window_pane *wp) |
880 | { |
881 | struct evbuffer *evb = wp->event->input; |
882 | |
883 | input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb)); |
884 | evbuffer_drain(evb, EVBUFFER_LENGTH(evb)); |
885 | } |
886 | |
887 | /* Parse given input. */ |
888 | void |
889 | input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) |
890 | { |
891 | struct input_ctx *ictx = wp->ictx; |
892 | struct screen_write_ctx *sctx = &ictx->ctx; |
893 | const struct input_state *state = NULL; |
894 | const struct input_transition *itr = NULL; |
895 | size_t off = 0; |
896 | |
897 | if (len == 0) |
898 | return; |
899 | |
900 | window_update_activity(wp->window); |
901 | wp->flags |= PANE_CHANGED; |
902 | notify_input(wp, buf, len); |
903 | |
904 | /* |
905 | * Open the screen. Use NULL wp if there is a mode set as don't want to |
906 | * update the tty. |
907 | */ |
908 | if (TAILQ_EMPTY(&wp->modes)) |
909 | screen_write_start(sctx, wp, &wp->base); |
910 | else |
911 | screen_write_start(sctx, NULL, &wp->base); |
912 | ictx->wp = wp; |
913 | |
914 | log_debug("%s: %%%u %s, %zu bytes: %.*s" , __func__, wp->id, |
915 | ictx->state->name, len, (int)len, buf); |
916 | |
917 | /* Parse the input. */ |
918 | while (off < len) { |
919 | ictx->ch = buf[off++]; |
920 | |
921 | /* Find the transition. */ |
922 | if (ictx->state != state || |
923 | itr == NULL || |
924 | ictx->ch < itr->first || |
925 | ictx->ch > itr->last) { |
926 | itr = ictx->state->transitions; |
927 | while (itr->first != -1 && itr->last != -1) { |
928 | if (ictx->ch >= itr->first && |
929 | ictx->ch <= itr->last) |
930 | break; |
931 | itr++; |
932 | } |
933 | if (itr->first == -1 || itr->last == -1) { |
934 | /* No transition? Eh? */ |
935 | fatalx("no transition from state" ); |
936 | } |
937 | } |
938 | state = ictx->state; |
939 | |
940 | /* |
941 | * Any state except print stops the current collection. This is |
942 | * an optimization to avoid checking if the attributes have |
943 | * changed for every character. It will stop unnecessarily for |
944 | * sequences that don't make a terminal change, but they should |
945 | * be the minority. |
946 | */ |
947 | if (itr->handler != input_print) |
948 | screen_write_collect_end(sctx); |
949 | |
950 | /* |
951 | * Execute the handler, if any. Don't switch state if it |
952 | * returns non-zero. |
953 | */ |
954 | if (itr->handler != NULL && itr->handler(ictx) != 0) |
955 | continue; |
956 | |
957 | /* And switch state, if necessary. */ |
958 | if (itr->state != NULL) |
959 | input_set_state(wp, itr); |
960 | |
961 | /* If not in ground state, save input. */ |
962 | if (ictx->state != &input_state_ground) |
963 | evbuffer_add(ictx->since_ground, &ictx->ch, 1); |
964 | } |
965 | |
966 | /* Close the screen. */ |
967 | screen_write_stop(sctx); |
968 | } |
969 | |
970 | /* Split the parameter list (if any). */ |
971 | static int |
972 | input_split(struct input_ctx *ictx) |
973 | { |
974 | const char *errstr; |
975 | char *ptr, *out; |
976 | struct input_param *ip; |
977 | u_int i; |
978 | |
979 | for (i = 0; i < ictx->param_list_len; i++) { |
980 | if (ictx->param_list[i].type == INPUT_STRING) |
981 | free(ictx->param_list[i].str); |
982 | } |
983 | ictx->param_list_len = 0; |
984 | |
985 | if (ictx->param_len == 0) |
986 | return (0); |
987 | ip = &ictx->param_list[0]; |
988 | |
989 | ptr = ictx->param_buf; |
990 | while ((out = strsep(&ptr, ";" )) != NULL) { |
991 | if (*out == '\0') |
992 | ip->type = INPUT_MISSING; |
993 | else { |
994 | if (strchr(out, ':') != NULL) { |
995 | ip->type = INPUT_STRING; |
996 | ip->str = xstrdup(out); |
997 | } else { |
998 | ip->type = INPUT_NUMBER; |
999 | ip->num = strtonum(out, 0, INT_MAX, &errstr); |
1000 | if (errstr != NULL) |
1001 | return (-1); |
1002 | } |
1003 | } |
1004 | ip = &ictx->param_list[++ictx->param_list_len]; |
1005 | if (ictx->param_list_len == nitems(ictx->param_list)) |
1006 | return (-1); |
1007 | } |
1008 | |
1009 | for (i = 0; i < ictx->param_list_len; i++) { |
1010 | ip = &ictx->param_list[i]; |
1011 | if (ip->type == INPUT_MISSING) |
1012 | log_debug("parameter %u: missing" , i); |
1013 | else if (ip->type == INPUT_STRING) |
1014 | log_debug("parameter %u: string %s" , i, ip->str); |
1015 | else if (ip->type == INPUT_NUMBER) |
1016 | log_debug("parameter %u: number %d" , i, ip->num); |
1017 | } |
1018 | |
1019 | return (0); |
1020 | } |
1021 | |
1022 | /* Get an argument or return default value. */ |
1023 | static int |
1024 | input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) |
1025 | { |
1026 | struct input_param *ip; |
1027 | int retval; |
1028 | |
1029 | if (validx >= ictx->param_list_len) |
1030 | return (defval); |
1031 | ip = &ictx->param_list[validx]; |
1032 | if (ip->type == INPUT_MISSING) |
1033 | return (defval); |
1034 | if (ip->type == INPUT_STRING) |
1035 | return (-1); |
1036 | retval = ip->num; |
1037 | if (retval < minval) |
1038 | return (minval); |
1039 | return (retval); |
1040 | } |
1041 | |
1042 | /* Reply to terminal query. */ |
1043 | static void |
1044 | input_reply(struct input_ctx *ictx, const char *fmt, ...) |
1045 | { |
1046 | va_list ap; |
1047 | char *reply; |
1048 | |
1049 | va_start(ap, fmt); |
1050 | xvasprintf(&reply, fmt, ap); |
1051 | va_end(ap); |
1052 | |
1053 | bufferevent_write(ictx->wp->event, reply, strlen(reply)); |
1054 | free(reply); |
1055 | } |
1056 | |
1057 | /* Clear saved state. */ |
1058 | static void |
1059 | input_clear(struct input_ctx *ictx) |
1060 | { |
1061 | event_del(&ictx->timer); |
1062 | |
1063 | *ictx->interm_buf = '\0'; |
1064 | ictx->interm_len = 0; |
1065 | |
1066 | *ictx->param_buf = '\0'; |
1067 | ictx->param_len = 0; |
1068 | |
1069 | *ictx->input_buf = '\0'; |
1070 | ictx->input_len = 0; |
1071 | |
1072 | ictx->input_end = INPUT_END_ST; |
1073 | |
1074 | ictx->flags &= ~INPUT_DISCARD; |
1075 | } |
1076 | |
1077 | /* Reset for ground state. */ |
1078 | static void |
1079 | input_ground(struct input_ctx *ictx) |
1080 | { |
1081 | event_del(&ictx->timer); |
1082 | evbuffer_drain(ictx->since_ground, EVBUFFER_LENGTH(ictx->since_ground)); |
1083 | |
1084 | if (ictx->input_space > INPUT_BUF_START) { |
1085 | ictx->input_space = INPUT_BUF_START; |
1086 | ictx->input_buf = xrealloc(ictx->input_buf, INPUT_BUF_START); |
1087 | } |
1088 | } |
1089 | |
1090 | /* Output this character to the screen. */ |
1091 | static int |
1092 | input_print(struct input_ctx *ictx) |
1093 | { |
1094 | struct screen_write_ctx *sctx = &ictx->ctx; |
1095 | int set; |
1096 | |
1097 | ictx->utf8started = 0; /* can't be valid UTF-8 */ |
1098 | |
1099 | set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set; |
1100 | if (set == 1) |
1101 | ictx->cell.cell.attr |= GRID_ATTR_CHARSET; |
1102 | else |
1103 | ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; |
1104 | |
1105 | utf8_set(&ictx->cell.cell.data, ictx->ch); |
1106 | screen_write_collect_add(sctx, &ictx->cell.cell); |
1107 | ictx->last = ictx->ch; |
1108 | |
1109 | ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; |
1110 | |
1111 | return (0); |
1112 | } |
1113 | |
1114 | /* Collect intermediate string. */ |
1115 | static int |
1116 | input_intermediate(struct input_ctx *ictx) |
1117 | { |
1118 | if (ictx->interm_len == (sizeof ictx->interm_buf) - 1) |
1119 | ictx->flags |= INPUT_DISCARD; |
1120 | else { |
1121 | ictx->interm_buf[ictx->interm_len++] = ictx->ch; |
1122 | ictx->interm_buf[ictx->interm_len] = '\0'; |
1123 | } |
1124 | |
1125 | return (0); |
1126 | } |
1127 | |
1128 | /* Collect parameter string. */ |
1129 | static int |
1130 | input_parameter(struct input_ctx *ictx) |
1131 | { |
1132 | if (ictx->param_len == (sizeof ictx->param_buf) - 1) |
1133 | ictx->flags |= INPUT_DISCARD; |
1134 | else { |
1135 | ictx->param_buf[ictx->param_len++] = ictx->ch; |
1136 | ictx->param_buf[ictx->param_len] = '\0'; |
1137 | } |
1138 | |
1139 | return (0); |
1140 | } |
1141 | |
1142 | /* Collect input string. */ |
1143 | static int |
1144 | input_input(struct input_ctx *ictx) |
1145 | { |
1146 | size_t available; |
1147 | |
1148 | available = ictx->input_space; |
1149 | while (ictx->input_len + 1 >= available) { |
1150 | available *= 2; |
1151 | if (available > INPUT_BUF_LIMIT) { |
1152 | ictx->flags |= INPUT_DISCARD; |
1153 | return (0); |
1154 | } |
1155 | ictx->input_buf = xrealloc(ictx->input_buf, available); |
1156 | ictx->input_space = available; |
1157 | } |
1158 | ictx->input_buf[ictx->input_len++] = ictx->ch; |
1159 | ictx->input_buf[ictx->input_len] = '\0'; |
1160 | |
1161 | return (0); |
1162 | } |
1163 | |
1164 | /* Execute C0 control sequence. */ |
1165 | static int |
1166 | input_c0_dispatch(struct input_ctx *ictx) |
1167 | { |
1168 | struct screen_write_ctx *sctx = &ictx->ctx; |
1169 | struct window_pane *wp = ictx->wp; |
1170 | struct screen *s = sctx->s; |
1171 | |
1172 | ictx->utf8started = 0; /* can't be valid UTF-8 */ |
1173 | |
1174 | log_debug("%s: '%c'" , __func__, ictx->ch); |
1175 | |
1176 | switch (ictx->ch) { |
1177 | case '\000': /* NUL */ |
1178 | break; |
1179 | case '\007': /* BEL */ |
1180 | alerts_queue(wp->window, WINDOW_BELL); |
1181 | break; |
1182 | case '\010': /* BS */ |
1183 | screen_write_backspace(sctx); |
1184 | break; |
1185 | case '\011': /* HT */ |
1186 | /* Don't tab beyond the end of the line. */ |
1187 | if (s->cx >= screen_size_x(s) - 1) |
1188 | break; |
1189 | |
1190 | /* Find the next tab point, or use the last column if none. */ |
1191 | do { |
1192 | s->cx++; |
1193 | if (bit_test(s->tabs, s->cx)) |
1194 | break; |
1195 | } while (s->cx < screen_size_x(s) - 1); |
1196 | break; |
1197 | case '\012': /* LF */ |
1198 | case '\013': /* VT */ |
1199 | case '\014': /* FF */ |
1200 | screen_write_linefeed(sctx, 0, ictx->cell.cell.bg); |
1201 | if (s->mode & MODE_CRLF) |
1202 | screen_write_carriagereturn(sctx); |
1203 | break; |
1204 | case '\015': /* CR */ |
1205 | screen_write_carriagereturn(sctx); |
1206 | break; |
1207 | case '\016': /* SO */ |
1208 | ictx->cell.set = 1; |
1209 | break; |
1210 | case '\017': /* SI */ |
1211 | ictx->cell.set = 0; |
1212 | break; |
1213 | default: |
1214 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1215 | break; |
1216 | } |
1217 | |
1218 | ictx->last = -1; |
1219 | return (0); |
1220 | } |
1221 | |
1222 | /* Execute escape sequence. */ |
1223 | static int |
1224 | input_esc_dispatch(struct input_ctx *ictx) |
1225 | { |
1226 | struct screen_write_ctx *sctx = &ictx->ctx; |
1227 | struct screen *s = sctx->s; |
1228 | struct input_table_entry *entry; |
1229 | |
1230 | if (ictx->flags & INPUT_DISCARD) |
1231 | return (0); |
1232 | log_debug("%s: '%c', %s" , __func__, ictx->ch, ictx->interm_buf); |
1233 | |
1234 | entry = bsearch(ictx, input_esc_table, nitems(input_esc_table), |
1235 | sizeof input_esc_table[0], input_table_compare); |
1236 | if (entry == NULL) { |
1237 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1238 | return (0); |
1239 | } |
1240 | |
1241 | switch (entry->type) { |
1242 | case INPUT_ESC_RIS: |
1243 | window_pane_reset_palette(ictx->wp); |
1244 | input_reset_cell(ictx); |
1245 | screen_write_reset(sctx); |
1246 | break; |
1247 | case INPUT_ESC_IND: |
1248 | screen_write_linefeed(sctx, 0, ictx->cell.cell.bg); |
1249 | break; |
1250 | case INPUT_ESC_NEL: |
1251 | screen_write_carriagereturn(sctx); |
1252 | screen_write_linefeed(sctx, 0, ictx->cell.cell.bg); |
1253 | break; |
1254 | case INPUT_ESC_HTS: |
1255 | if (s->cx < screen_size_x(s)) |
1256 | bit_set(s->tabs, s->cx); |
1257 | break; |
1258 | case INPUT_ESC_RI: |
1259 | screen_write_reverseindex(sctx, ictx->cell.cell.bg); |
1260 | break; |
1261 | case INPUT_ESC_DECKPAM: |
1262 | screen_write_mode_set(sctx, MODE_KKEYPAD); |
1263 | break; |
1264 | case INPUT_ESC_DECKPNM: |
1265 | screen_write_mode_clear(sctx, MODE_KKEYPAD); |
1266 | break; |
1267 | case INPUT_ESC_DECSC: |
1268 | input_save_state(ictx); |
1269 | break; |
1270 | case INPUT_ESC_DECRC: |
1271 | input_restore_state(ictx); |
1272 | break; |
1273 | case INPUT_ESC_DECALN: |
1274 | screen_write_alignmenttest(sctx); |
1275 | break; |
1276 | case INPUT_ESC_SCSG0_ON: |
1277 | ictx->cell.g0set = 1; |
1278 | break; |
1279 | case INPUT_ESC_SCSG0_OFF: |
1280 | ictx->cell.g0set = 0; |
1281 | break; |
1282 | case INPUT_ESC_SCSG1_ON: |
1283 | ictx->cell.g1set = 1; |
1284 | break; |
1285 | case INPUT_ESC_SCSG1_OFF: |
1286 | ictx->cell.g1set = 0; |
1287 | break; |
1288 | case INPUT_ESC_ST: |
1289 | /* ST terminates OSC but the state transition already did it. */ |
1290 | break; |
1291 | } |
1292 | |
1293 | ictx->last = -1; |
1294 | return (0); |
1295 | } |
1296 | |
1297 | /* Execute control sequence. */ |
1298 | static int |
1299 | input_csi_dispatch(struct input_ctx *ictx) |
1300 | { |
1301 | struct screen_write_ctx *sctx = &ictx->ctx; |
1302 | struct screen *s = sctx->s; |
1303 | struct input_table_entry *entry; |
1304 | int i, n, m; |
1305 | u_int cx, bg = ictx->cell.cell.bg; |
1306 | char *copy, *cp; |
1307 | |
1308 | if (ictx->flags & INPUT_DISCARD) |
1309 | return (0); |
1310 | |
1311 | log_debug("%s: '%c' \"%s\" \"%s\"" , |
1312 | __func__, ictx->ch, ictx->interm_buf, ictx->param_buf); |
1313 | |
1314 | if (input_split(ictx) != 0) |
1315 | return (0); |
1316 | |
1317 | entry = bsearch(ictx, input_csi_table, nitems(input_csi_table), |
1318 | sizeof input_csi_table[0], input_table_compare); |
1319 | if (entry == NULL) { |
1320 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1321 | return (0); |
1322 | } |
1323 | |
1324 | switch (entry->type) { |
1325 | case INPUT_CSI_CBT: |
1326 | /* Find the previous tab point, n times. */ |
1327 | cx = s->cx; |
1328 | if (cx > screen_size_x(s) - 1) |
1329 | cx = screen_size_x(s) - 1; |
1330 | n = input_get(ictx, 0, 1, 1); |
1331 | if (n == -1) |
1332 | break; |
1333 | while (cx > 0 && n-- > 0) { |
1334 | do |
1335 | cx--; |
1336 | while (cx > 0 && !bit_test(s->tabs, cx)); |
1337 | } |
1338 | s->cx = cx; |
1339 | break; |
1340 | case INPUT_CSI_CUB: |
1341 | n = input_get(ictx, 0, 1, 1); |
1342 | if (n != -1) |
1343 | screen_write_cursorleft(sctx, n); |
1344 | break; |
1345 | case INPUT_CSI_CUD: |
1346 | n = input_get(ictx, 0, 1, 1); |
1347 | if (n != -1) |
1348 | screen_write_cursordown(sctx, n); |
1349 | break; |
1350 | case INPUT_CSI_CUF: |
1351 | n = input_get(ictx, 0, 1, 1); |
1352 | if (n != -1) |
1353 | screen_write_cursorright(sctx, n); |
1354 | break; |
1355 | case INPUT_CSI_CUP: |
1356 | n = input_get(ictx, 0, 1, 1); |
1357 | m = input_get(ictx, 1, 1, 1); |
1358 | if (n != -1 && m != -1) |
1359 | screen_write_cursormove(sctx, m - 1, n - 1, 1); |
1360 | break; |
1361 | case INPUT_CSI_WINOPS: |
1362 | input_csi_dispatch_winops(ictx); |
1363 | break; |
1364 | case INPUT_CSI_CUU: |
1365 | n = input_get(ictx, 0, 1, 1); |
1366 | if (n != -1) |
1367 | screen_write_cursorup(sctx, n); |
1368 | break; |
1369 | case INPUT_CSI_CNL: |
1370 | n = input_get(ictx, 0, 1, 1); |
1371 | if (n != -1) { |
1372 | screen_write_carriagereturn(sctx); |
1373 | screen_write_cursordown(sctx, n); |
1374 | } |
1375 | break; |
1376 | case INPUT_CSI_CPL: |
1377 | n = input_get(ictx, 0, 1, 1); |
1378 | if (n != -1) { |
1379 | screen_write_carriagereturn(sctx); |
1380 | screen_write_cursorup(sctx, n); |
1381 | } |
1382 | break; |
1383 | case INPUT_CSI_DA: |
1384 | switch (input_get(ictx, 0, 0, 0)) { |
1385 | case -1: |
1386 | break; |
1387 | case 0: |
1388 | input_reply(ictx, "\033[?1;2c" ); |
1389 | break; |
1390 | default: |
1391 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1392 | break; |
1393 | } |
1394 | break; |
1395 | case INPUT_CSI_DA_TWO: |
1396 | switch (input_get(ictx, 0, 0, 0)) { |
1397 | case -1: |
1398 | break; |
1399 | case 0: |
1400 | input_reply(ictx, "\033[>84;0;0c" ); |
1401 | break; |
1402 | default: |
1403 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1404 | break; |
1405 | } |
1406 | break; |
1407 | case INPUT_CSI_ECH: |
1408 | n = input_get(ictx, 0, 1, 1); |
1409 | if (n != -1) |
1410 | screen_write_clearcharacter(sctx, n, bg); |
1411 | break; |
1412 | case INPUT_CSI_DCH: |
1413 | n = input_get(ictx, 0, 1, 1); |
1414 | if (n != -1) |
1415 | screen_write_deletecharacter(sctx, n, bg); |
1416 | break; |
1417 | case INPUT_CSI_DECSTBM: |
1418 | n = input_get(ictx, 0, 1, 1); |
1419 | m = input_get(ictx, 1, 1, screen_size_y(s)); |
1420 | if (n != -1 && m != -1) |
1421 | screen_write_scrollregion(sctx, n - 1, m - 1); |
1422 | break; |
1423 | case INPUT_CSI_DL: |
1424 | n = input_get(ictx, 0, 1, 1); |
1425 | if (n != -1) |
1426 | screen_write_deleteline(sctx, n, bg); |
1427 | break; |
1428 | case INPUT_CSI_DSR: |
1429 | switch (input_get(ictx, 0, 0, 0)) { |
1430 | case -1: |
1431 | break; |
1432 | case 5: |
1433 | input_reply(ictx, "\033[0n" ); |
1434 | break; |
1435 | case 6: |
1436 | input_reply(ictx, "\033[%u;%uR" , s->cy + 1, s->cx + 1); |
1437 | break; |
1438 | case 1337: /* Terminal version, from iTerm2. */ |
1439 | copy = xstrdup(getversion()); |
1440 | for (cp = copy; *cp != '\0'; cp++) |
1441 | *cp = toupper((u_char)*cp); |
1442 | input_reply(ictx, "\033[TMUX %sn" , copy); |
1443 | free(copy); |
1444 | break; |
1445 | default: |
1446 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1447 | break; |
1448 | } |
1449 | break; |
1450 | case INPUT_CSI_ED: |
1451 | switch (input_get(ictx, 0, 0, 0)) { |
1452 | case -1: |
1453 | break; |
1454 | case 0: |
1455 | screen_write_clearendofscreen(sctx, bg); |
1456 | break; |
1457 | case 1: |
1458 | screen_write_clearstartofscreen(sctx, bg); |
1459 | break; |
1460 | case 2: |
1461 | screen_write_clearscreen(sctx, bg); |
1462 | break; |
1463 | case 3: |
1464 | if (input_get(ictx, 1, 0, 0) == 0) { |
1465 | /* |
1466 | * Linux console extension to clear history |
1467 | * (for example before locking the screen). |
1468 | */ |
1469 | screen_write_clearhistory(sctx); |
1470 | } |
1471 | break; |
1472 | default: |
1473 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1474 | break; |
1475 | } |
1476 | break; |
1477 | case INPUT_CSI_EL: |
1478 | switch (input_get(ictx, 0, 0, 0)) { |
1479 | case -1: |
1480 | break; |
1481 | case 0: |
1482 | screen_write_clearendofline(sctx, bg); |
1483 | break; |
1484 | case 1: |
1485 | screen_write_clearstartofline(sctx, bg); |
1486 | break; |
1487 | case 2: |
1488 | screen_write_clearline(sctx, bg); |
1489 | break; |
1490 | default: |
1491 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1492 | break; |
1493 | } |
1494 | break; |
1495 | case INPUT_CSI_HPA: |
1496 | n = input_get(ictx, 0, 1, 1); |
1497 | if (n != -1) |
1498 | screen_write_cursormove(sctx, n - 1, -1, 1); |
1499 | break; |
1500 | case INPUT_CSI_ICH: |
1501 | n = input_get(ictx, 0, 1, 1); |
1502 | if (n != -1) |
1503 | screen_write_insertcharacter(sctx, n, bg); |
1504 | break; |
1505 | case INPUT_CSI_IL: |
1506 | n = input_get(ictx, 0, 1, 1); |
1507 | if (n != -1) |
1508 | screen_write_insertline(sctx, n, bg); |
1509 | break; |
1510 | case INPUT_CSI_REP: |
1511 | n = input_get(ictx, 0, 1, 1); |
1512 | if (n == -1) |
1513 | break; |
1514 | |
1515 | if (ictx->last == -1) |
1516 | break; |
1517 | ictx->ch = ictx->last; |
1518 | |
1519 | for (i = 0; i < n; i++) |
1520 | input_print(ictx); |
1521 | break; |
1522 | case INPUT_CSI_RCP: |
1523 | input_restore_state(ictx); |
1524 | break; |
1525 | case INPUT_CSI_RM: |
1526 | input_csi_dispatch_rm(ictx); |
1527 | break; |
1528 | case INPUT_CSI_RM_PRIVATE: |
1529 | input_csi_dispatch_rm_private(ictx); |
1530 | break; |
1531 | case INPUT_CSI_SCP: |
1532 | input_save_state(ictx); |
1533 | break; |
1534 | case INPUT_CSI_SGR: |
1535 | input_csi_dispatch_sgr(ictx); |
1536 | break; |
1537 | case INPUT_CSI_SM: |
1538 | input_csi_dispatch_sm(ictx); |
1539 | break; |
1540 | case INPUT_CSI_SM_PRIVATE: |
1541 | input_csi_dispatch_sm_private(ictx); |
1542 | break; |
1543 | case INPUT_CSI_SU: |
1544 | n = input_get(ictx, 0, 1, 1); |
1545 | if (n != -1) |
1546 | screen_write_scrollup(sctx, n, bg); |
1547 | break; |
1548 | case INPUT_CSI_SD: |
1549 | n = input_get(ictx, 0, 1, 1); |
1550 | if (n != -1) |
1551 | screen_write_scrolldown(sctx, n, bg); |
1552 | break; |
1553 | case INPUT_CSI_TBC: |
1554 | switch (input_get(ictx, 0, 0, 0)) { |
1555 | case -1: |
1556 | break; |
1557 | case 0: |
1558 | if (s->cx < screen_size_x(s)) |
1559 | bit_clear(s->tabs, s->cx); |
1560 | break; |
1561 | case 3: |
1562 | bit_nclear(s->tabs, 0, screen_size_x(s) - 1); |
1563 | break; |
1564 | default: |
1565 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1566 | break; |
1567 | } |
1568 | break; |
1569 | case INPUT_CSI_VPA: |
1570 | n = input_get(ictx, 0, 1, 1); |
1571 | if (n != -1) |
1572 | screen_write_cursormove(sctx, -1, n - 1, 1); |
1573 | break; |
1574 | case INPUT_CSI_DECSCUSR: |
1575 | n = input_get(ictx, 0, 0, 0); |
1576 | if (n != -1) |
1577 | screen_set_cursor_style(s, n); |
1578 | break; |
1579 | } |
1580 | |
1581 | ictx->last = -1; |
1582 | return (0); |
1583 | } |
1584 | |
1585 | /* Handle CSI RM. */ |
1586 | static void |
1587 | input_csi_dispatch_rm(struct input_ctx *ictx) |
1588 | { |
1589 | struct screen_write_ctx *sctx = &ictx->ctx; |
1590 | u_int i; |
1591 | |
1592 | for (i = 0; i < ictx->param_list_len; i++) { |
1593 | switch (input_get(ictx, i, 0, -1)) { |
1594 | case -1: |
1595 | break; |
1596 | case 4: /* IRM */ |
1597 | screen_write_mode_clear(sctx, MODE_INSERT); |
1598 | break; |
1599 | case 34: |
1600 | screen_write_mode_set(sctx, MODE_BLINKING); |
1601 | break; |
1602 | default: |
1603 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1604 | break; |
1605 | } |
1606 | } |
1607 | } |
1608 | |
1609 | /* Handle CSI private RM. */ |
1610 | static void |
1611 | input_csi_dispatch_rm_private(struct input_ctx *ictx) |
1612 | { |
1613 | struct screen_write_ctx *sctx = &ictx->ctx; |
1614 | struct window_pane *wp = ictx->wp; |
1615 | u_int i; |
1616 | |
1617 | for (i = 0; i < ictx->param_list_len; i++) { |
1618 | switch (input_get(ictx, i, 0, -1)) { |
1619 | case -1: |
1620 | break; |
1621 | case 1: /* DECCKM */ |
1622 | screen_write_mode_clear(sctx, MODE_KCURSOR); |
1623 | break; |
1624 | case 3: /* DECCOLM */ |
1625 | screen_write_cursormove(sctx, 0, 0, 1); |
1626 | screen_write_clearscreen(sctx, ictx->cell.cell.bg); |
1627 | break; |
1628 | case 6: /* DECOM */ |
1629 | screen_write_mode_clear(sctx, MODE_ORIGIN); |
1630 | screen_write_cursormove(sctx, 0, 0, 1); |
1631 | break; |
1632 | case 7: /* DECAWM */ |
1633 | screen_write_mode_clear(sctx, MODE_WRAP); |
1634 | break; |
1635 | case 12: |
1636 | screen_write_mode_clear(sctx, MODE_BLINKING); |
1637 | break; |
1638 | case 25: /* TCEM */ |
1639 | screen_write_mode_clear(sctx, MODE_CURSOR); |
1640 | break; |
1641 | case 1000: |
1642 | case 1001: |
1643 | case 1002: |
1644 | case 1003: |
1645 | screen_write_mode_clear(sctx, ALL_MOUSE_MODES); |
1646 | break; |
1647 | case 1004: |
1648 | screen_write_mode_clear(sctx, MODE_FOCUSON); |
1649 | break; |
1650 | case 1005: |
1651 | screen_write_mode_clear(sctx, MODE_MOUSE_UTF8); |
1652 | break; |
1653 | case 1006: |
1654 | screen_write_mode_clear(sctx, MODE_MOUSE_SGR); |
1655 | break; |
1656 | case 47: |
1657 | case 1047: |
1658 | window_pane_alternate_off(wp, &ictx->cell.cell, 0); |
1659 | break; |
1660 | case 1049: |
1661 | window_pane_alternate_off(wp, &ictx->cell.cell, 1); |
1662 | break; |
1663 | case 2004: |
1664 | screen_write_mode_clear(sctx, MODE_BRACKETPASTE); |
1665 | break; |
1666 | default: |
1667 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1668 | break; |
1669 | } |
1670 | } |
1671 | } |
1672 | |
1673 | /* Handle CSI SM. */ |
1674 | static void |
1675 | input_csi_dispatch_sm(struct input_ctx *ictx) |
1676 | { |
1677 | struct screen_write_ctx *sctx = &ictx->ctx; |
1678 | u_int i; |
1679 | |
1680 | for (i = 0; i < ictx->param_list_len; i++) { |
1681 | switch (input_get(ictx, i, 0, -1)) { |
1682 | case -1: |
1683 | break; |
1684 | case 4: /* IRM */ |
1685 | screen_write_mode_set(sctx, MODE_INSERT); |
1686 | break; |
1687 | case 34: |
1688 | screen_write_mode_clear(sctx, MODE_BLINKING); |
1689 | break; |
1690 | default: |
1691 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1692 | break; |
1693 | } |
1694 | } |
1695 | } |
1696 | |
1697 | /* Handle CSI private SM. */ |
1698 | static void |
1699 | input_csi_dispatch_sm_private(struct input_ctx *ictx) |
1700 | { |
1701 | struct screen_write_ctx *sctx = &ictx->ctx; |
1702 | struct window_pane *wp = ictx->wp; |
1703 | u_int i; |
1704 | |
1705 | for (i = 0; i < ictx->param_list_len; i++) { |
1706 | switch (input_get(ictx, i, 0, -1)) { |
1707 | case -1: |
1708 | break; |
1709 | case 1: /* DECCKM */ |
1710 | screen_write_mode_set(sctx, MODE_KCURSOR); |
1711 | break; |
1712 | case 3: /* DECCOLM */ |
1713 | screen_write_cursormove(sctx, 0, 0, 1); |
1714 | screen_write_clearscreen(sctx, ictx->cell.cell.bg); |
1715 | break; |
1716 | case 6: /* DECOM */ |
1717 | screen_write_mode_set(sctx, MODE_ORIGIN); |
1718 | screen_write_cursormove(sctx, 0, 0, 1); |
1719 | break; |
1720 | case 7: /* DECAWM */ |
1721 | screen_write_mode_set(sctx, MODE_WRAP); |
1722 | break; |
1723 | case 12: |
1724 | screen_write_mode_set(sctx, MODE_BLINKING); |
1725 | break; |
1726 | case 25: /* TCEM */ |
1727 | screen_write_mode_set(sctx, MODE_CURSOR); |
1728 | break; |
1729 | case 1000: |
1730 | screen_write_mode_clear(sctx, ALL_MOUSE_MODES); |
1731 | screen_write_mode_set(sctx, MODE_MOUSE_STANDARD); |
1732 | break; |
1733 | case 1002: |
1734 | screen_write_mode_clear(sctx, ALL_MOUSE_MODES); |
1735 | screen_write_mode_set(sctx, MODE_MOUSE_BUTTON); |
1736 | break; |
1737 | case 1003: |
1738 | screen_write_mode_clear(sctx, ALL_MOUSE_MODES); |
1739 | screen_write_mode_set(sctx, MODE_MOUSE_ALL); |
1740 | break; |
1741 | case 1004: |
1742 | if (sctx->s->mode & MODE_FOCUSON) |
1743 | break; |
1744 | screen_write_mode_set(sctx, MODE_FOCUSON); |
1745 | wp->flags |= PANE_FOCUSPUSH; /* force update */ |
1746 | break; |
1747 | case 1005: |
1748 | screen_write_mode_set(sctx, MODE_MOUSE_UTF8); |
1749 | break; |
1750 | case 1006: |
1751 | screen_write_mode_set(sctx, MODE_MOUSE_SGR); |
1752 | break; |
1753 | case 47: |
1754 | case 1047: |
1755 | window_pane_alternate_on(wp, &ictx->cell.cell, 0); |
1756 | break; |
1757 | case 1049: |
1758 | window_pane_alternate_on(wp, &ictx->cell.cell, 1); |
1759 | break; |
1760 | case 2004: |
1761 | screen_write_mode_set(sctx, MODE_BRACKETPASTE); |
1762 | break; |
1763 | default: |
1764 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1765 | break; |
1766 | } |
1767 | } |
1768 | } |
1769 | |
1770 | /* Handle CSI window operations. */ |
1771 | static void |
1772 | input_csi_dispatch_winops(struct input_ctx *ictx) |
1773 | { |
1774 | struct screen_write_ctx *sctx = &ictx->ctx; |
1775 | struct window_pane *wp = ictx->wp; |
1776 | int n, m; |
1777 | |
1778 | m = 0; |
1779 | while ((n = input_get(ictx, m, 0, -1)) != -1) { |
1780 | switch (n) { |
1781 | case 1: |
1782 | case 2: |
1783 | case 5: |
1784 | case 6: |
1785 | case 7: |
1786 | case 11: |
1787 | case 13: |
1788 | case 14: |
1789 | case 19: |
1790 | case 20: |
1791 | case 21: |
1792 | case 24: |
1793 | break; |
1794 | case 3: |
1795 | case 4: |
1796 | case 8: |
1797 | m++; |
1798 | if (input_get(ictx, m, 0, -1) == -1) |
1799 | return; |
1800 | /* FALLTHROUGH */ |
1801 | case 9: |
1802 | case 10: |
1803 | m++; |
1804 | if (input_get(ictx, m, 0, -1) == -1) |
1805 | return; |
1806 | break; |
1807 | case 22: |
1808 | m++; |
1809 | switch (input_get(ictx, m, 0, -1)) { |
1810 | case -1: |
1811 | return; |
1812 | case 0: |
1813 | case 2: |
1814 | screen_push_title(sctx->s); |
1815 | break; |
1816 | } |
1817 | break; |
1818 | case 23: |
1819 | m++; |
1820 | switch (input_get(ictx, m, 0, -1)) { |
1821 | case -1: |
1822 | return; |
1823 | case 0: |
1824 | case 2: |
1825 | screen_pop_title(sctx->s); |
1826 | server_status_window(ictx->wp->window); |
1827 | break; |
1828 | } |
1829 | break; |
1830 | case 18: |
1831 | input_reply(ictx, "\033[8;%u;%ut" , wp->sy, wp->sx); |
1832 | break; |
1833 | default: |
1834 | log_debug("%s: unknown '%c'" , __func__, ictx->ch); |
1835 | break; |
1836 | } |
1837 | m++; |
1838 | } |
1839 | } |
1840 | |
1841 | /* Helper for 256 colour SGR. */ |
1842 | static int |
1843 | input_csi_dispatch_sgr_256_do(struct input_ctx *ictx, int fgbg, int c) |
1844 | { |
1845 | struct grid_cell *gc = &ictx->cell.cell; |
1846 | |
1847 | if (c == -1 || c > 255) { |
1848 | if (fgbg == 38) |
1849 | gc->fg = 8; |
1850 | else if (fgbg == 48) |
1851 | gc->bg = 8; |
1852 | } else { |
1853 | if (fgbg == 38) |
1854 | gc->fg = c | COLOUR_FLAG_256; |
1855 | else if (fgbg == 48) |
1856 | gc->bg = c | COLOUR_FLAG_256; |
1857 | else if (fgbg == 58) |
1858 | gc->us = c | COLOUR_FLAG_256; |
1859 | } |
1860 | return (1); |
1861 | } |
1862 | |
1863 | /* Handle CSI SGR for 256 colours. */ |
1864 | static void |
1865 | input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) |
1866 | { |
1867 | int c; |
1868 | |
1869 | c = input_get(ictx, (*i) + 1, 0, -1); |
1870 | if (input_csi_dispatch_sgr_256_do(ictx, fgbg, c)) |
1871 | (*i)++; |
1872 | } |
1873 | |
1874 | /* Helper for RGB colour SGR. */ |
1875 | static int |
1876 | input_csi_dispatch_sgr_rgb_do(struct input_ctx *ictx, int fgbg, int r, int g, |
1877 | int b) |
1878 | { |
1879 | struct grid_cell *gc = &ictx->cell.cell; |
1880 | |
1881 | if (r == -1 || r > 255) |
1882 | return (0); |
1883 | if (g == -1 || g > 255) |
1884 | return (0); |
1885 | if (b == -1 || b > 255) |
1886 | return (0); |
1887 | |
1888 | if (fgbg == 38) |
1889 | gc->fg = colour_join_rgb(r, g, b); |
1890 | else if (fgbg == 48) |
1891 | gc->bg = colour_join_rgb(r, g, b); |
1892 | else if (fgbg == 58) |
1893 | gc->us = colour_join_rgb(r, g, b); |
1894 | return (1); |
1895 | } |
1896 | |
1897 | /* Handle CSI SGR for RGB colours. */ |
1898 | static void |
1899 | input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) |
1900 | { |
1901 | int r, g, b; |
1902 | |
1903 | r = input_get(ictx, (*i) + 1, 0, -1); |
1904 | g = input_get(ictx, (*i) + 2, 0, -1); |
1905 | b = input_get(ictx, (*i) + 3, 0, -1); |
1906 | if (input_csi_dispatch_sgr_rgb_do(ictx, fgbg, r, g, b)) |
1907 | (*i) += 3; |
1908 | } |
1909 | |
1910 | /* Handle CSI SGR with a ISO parameter. */ |
1911 | static void |
1912 | input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i) |
1913 | { |
1914 | struct grid_cell *gc = &ictx->cell.cell; |
1915 | char *s = ictx->param_list[i].str, *copy, *ptr, *out; |
1916 | int p[8]; |
1917 | u_int n; |
1918 | const char *errstr; |
1919 | |
1920 | for (n = 0; n < nitems(p); n++) |
1921 | p[n] = -1; |
1922 | n = 0; |
1923 | |
1924 | ptr = copy = xstrdup(s); |
1925 | while ((out = strsep(&ptr, ":" )) != NULL) { |
1926 | if (*out != '\0') { |
1927 | p[n++] = strtonum(out, 0, INT_MAX, &errstr); |
1928 | if (errstr != NULL || n == nitems(p)) { |
1929 | free(copy); |
1930 | return; |
1931 | } |
1932 | } else |
1933 | n++; |
1934 | log_debug("%s: %u = %d" , __func__, n - 1, p[n - 1]); |
1935 | } |
1936 | free(copy); |
1937 | |
1938 | if (n == 0) |
1939 | return; |
1940 | if (p[0] == 4) { |
1941 | if (n != 2) |
1942 | return; |
1943 | switch (p[1]) { |
1944 | case 0: |
1945 | gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; |
1946 | break; |
1947 | case 1: |
1948 | gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; |
1949 | gc->attr |= GRID_ATTR_UNDERSCORE; |
1950 | break; |
1951 | case 2: |
1952 | gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; |
1953 | gc->attr |= GRID_ATTR_UNDERSCORE_2; |
1954 | break; |
1955 | case 3: |
1956 | gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; |
1957 | gc->attr |= GRID_ATTR_UNDERSCORE_3; |
1958 | break; |
1959 | case 4: |
1960 | gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; |
1961 | gc->attr |= GRID_ATTR_UNDERSCORE_4; |
1962 | break; |
1963 | case 5: |
1964 | gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; |
1965 | gc->attr |= GRID_ATTR_UNDERSCORE_5; |
1966 | break; |
1967 | } |
1968 | return; |
1969 | } |
1970 | if (n < 2 || (p[0] != 38 && p[0] != 48 && p[0] != 58)) |
1971 | return; |
1972 | switch (p[1]) { |
1973 | case 2: |
1974 | if (n < 3) |
1975 | break; |
1976 | if (n == 5) |
1977 | i = 2; |
1978 | else |
1979 | i = 3; |
1980 | if (n < i + 3) |
1981 | break; |
1982 | input_csi_dispatch_sgr_rgb_do(ictx, p[0], p[i], p[i + 1], |
1983 | p[i + 2]); |
1984 | break; |
1985 | case 5: |
1986 | if (n < 3) |
1987 | break; |
1988 | input_csi_dispatch_sgr_256_do(ictx, p[0], p[2]); |
1989 | break; |
1990 | } |
1991 | } |
1992 | |
1993 | /* Handle CSI SGR. */ |
1994 | static void |
1995 | input_csi_dispatch_sgr(struct input_ctx *ictx) |
1996 | { |
1997 | struct grid_cell *gc = &ictx->cell.cell; |
1998 | u_int i; |
1999 | int n; |
2000 | |
2001 | if (ictx->param_list_len == 0) { |
2002 | memcpy(gc, &grid_default_cell, sizeof *gc); |
2003 | return; |
2004 | } |
2005 | |
2006 | for (i = 0; i < ictx->param_list_len; i++) { |
2007 | if (ictx->param_list[i].type == INPUT_STRING) { |
2008 | input_csi_dispatch_sgr_colon(ictx, i); |
2009 | continue; |
2010 | } |
2011 | n = input_get(ictx, i, 0, 0); |
2012 | if (n == -1) |
2013 | continue; |
2014 | |
2015 | if (n == 38 || n == 48 || n == 58) { |
2016 | i++; |
2017 | switch (input_get(ictx, i, 0, -1)) { |
2018 | case 2: |
2019 | input_csi_dispatch_sgr_rgb(ictx, n, &i); |
2020 | break; |
2021 | case 5: |
2022 | input_csi_dispatch_sgr_256(ictx, n, &i); |
2023 | break; |
2024 | } |
2025 | continue; |
2026 | } |
2027 | |
2028 | switch (n) { |
2029 | case 0: |
2030 | memcpy(gc, &grid_default_cell, sizeof *gc); |
2031 | break; |
2032 | case 1: |
2033 | gc->attr |= GRID_ATTR_BRIGHT; |
2034 | break; |
2035 | case 2: |
2036 | gc->attr |= GRID_ATTR_DIM; |
2037 | break; |
2038 | case 3: |
2039 | gc->attr |= GRID_ATTR_ITALICS; |
2040 | break; |
2041 | case 4: |
2042 | gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; |
2043 | gc->attr |= GRID_ATTR_UNDERSCORE; |
2044 | break; |
2045 | case 5: |
2046 | gc->attr |= GRID_ATTR_BLINK; |
2047 | break; |
2048 | case 7: |
2049 | gc->attr |= GRID_ATTR_REVERSE; |
2050 | break; |
2051 | case 8: |
2052 | gc->attr |= GRID_ATTR_HIDDEN; |
2053 | break; |
2054 | case 9: |
2055 | gc->attr |= GRID_ATTR_STRIKETHROUGH; |
2056 | break; |
2057 | case 22: |
2058 | gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM); |
2059 | break; |
2060 | case 23: |
2061 | gc->attr &= ~GRID_ATTR_ITALICS; |
2062 | break; |
2063 | case 24: |
2064 | gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; |
2065 | break; |
2066 | case 25: |
2067 | gc->attr &= ~GRID_ATTR_BLINK; |
2068 | break; |
2069 | case 27: |
2070 | gc->attr &= ~GRID_ATTR_REVERSE; |
2071 | break; |
2072 | case 28: |
2073 | gc->attr &= ~GRID_ATTR_HIDDEN; |
2074 | break; |
2075 | case 29: |
2076 | gc->attr &= ~GRID_ATTR_STRIKETHROUGH; |
2077 | break; |
2078 | case 30: |
2079 | case 31: |
2080 | case 32: |
2081 | case 33: |
2082 | case 34: |
2083 | case 35: |
2084 | case 36: |
2085 | case 37: |
2086 | gc->fg = n - 30; |
2087 | break; |
2088 | case 39: |
2089 | gc->fg = 8; |
2090 | break; |
2091 | case 40: |
2092 | case 41: |
2093 | case 42: |
2094 | case 43: |
2095 | case 44: |
2096 | case 45: |
2097 | case 46: |
2098 | case 47: |
2099 | gc->bg = n - 40; |
2100 | break; |
2101 | case 49: |
2102 | gc->bg = 8; |
2103 | break; |
2104 | case 53: |
2105 | gc->attr |= GRID_ATTR_OVERLINE; |
2106 | break; |
2107 | case 55: |
2108 | gc->attr &= ~GRID_ATTR_OVERLINE; |
2109 | break; |
2110 | case 59: |
2111 | gc->us = 0; |
2112 | break; |
2113 | case 90: |
2114 | case 91: |
2115 | case 92: |
2116 | case 93: |
2117 | case 94: |
2118 | case 95: |
2119 | case 96: |
2120 | case 97: |
2121 | gc->fg = n; |
2122 | break; |
2123 | case 100: |
2124 | case 101: |
2125 | case 102: |
2126 | case 103: |
2127 | case 104: |
2128 | case 105: |
2129 | case 106: |
2130 | case 107: |
2131 | gc->bg = n - 10; |
2132 | break; |
2133 | } |
2134 | } |
2135 | } |
2136 | |
2137 | /* End of input with BEL. */ |
2138 | static int |
2139 | input_end_bel(struct input_ctx *ictx) |
2140 | { |
2141 | log_debug("%s" , __func__); |
2142 | |
2143 | ictx->input_end = INPUT_END_BEL; |
2144 | |
2145 | return (0); |
2146 | } |
2147 | |
2148 | /* DCS string started. */ |
2149 | static void |
2150 | input_enter_dcs(struct input_ctx *ictx) |
2151 | { |
2152 | log_debug("%s" , __func__); |
2153 | |
2154 | input_clear(ictx); |
2155 | input_start_timer(ictx); |
2156 | ictx->last = -1; |
2157 | } |
2158 | |
2159 | /* DCS terminator (ST) received. */ |
2160 | static int |
2161 | input_dcs_dispatch(struct input_ctx *ictx) |
2162 | { |
2163 | struct screen_write_ctx *sctx = &ictx->ctx; |
2164 | u_char *buf = ictx->input_buf; |
2165 | size_t len = ictx->input_len; |
2166 | const char prefix[] = "tmux;" ; |
2167 | const u_int prefixlen = (sizeof prefix) - 1; |
2168 | |
2169 | if (ictx->flags & INPUT_DISCARD) |
2170 | return (0); |
2171 | |
2172 | log_debug("%s: \"%s\"" , __func__, buf); |
2173 | |
2174 | if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) |
2175 | screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen); |
2176 | |
2177 | return (0); |
2178 | } |
2179 | |
2180 | /* OSC string started. */ |
2181 | static void |
2182 | input_enter_osc(struct input_ctx *ictx) |
2183 | { |
2184 | log_debug("%s" , __func__); |
2185 | |
2186 | input_clear(ictx); |
2187 | input_start_timer(ictx); |
2188 | ictx->last = -1; |
2189 | } |
2190 | |
2191 | /* OSC terminator (ST) received. */ |
2192 | static void |
2193 | input_exit_osc(struct input_ctx *ictx) |
2194 | { |
2195 | struct screen_write_ctx *sctx = &ictx->ctx; |
2196 | u_char *p = ictx->input_buf; |
2197 | u_int option; |
2198 | |
2199 | if (ictx->flags & INPUT_DISCARD) |
2200 | return; |
2201 | if (ictx->input_len < 1 || *p < '0' || *p > '9') |
2202 | return; |
2203 | |
2204 | log_debug("%s: \"%s\" (end %s)" , __func__, p, |
2205 | ictx->input_end == INPUT_END_ST ? "ST" : "BEL" ); |
2206 | |
2207 | option = 0; |
2208 | while (*p >= '0' && *p <= '9') |
2209 | option = option * 10 + *p++ - '0'; |
2210 | if (*p == ';') |
2211 | p++; |
2212 | |
2213 | switch (option) { |
2214 | case 0: |
2215 | case 2: |
2216 | if (screen_set_title(sctx->s, p)) |
2217 | server_status_window(ictx->wp->window); |
2218 | break; |
2219 | case 4: |
2220 | input_osc_4(ictx, p); |
2221 | break; |
2222 | case 7: |
2223 | if (utf8_isvalid(p)) { |
2224 | screen_set_path(sctx->s, p); |
2225 | server_status_window(ictx->wp->window); |
2226 | } |
2227 | break; |
2228 | case 10: |
2229 | input_osc_10(ictx, p); |
2230 | break; |
2231 | case 11: |
2232 | input_osc_11(ictx, p); |
2233 | break; |
2234 | case 12: |
2235 | if (utf8_isvalid(p) && *p != '?') /* ? is colour request */ |
2236 | screen_set_cursor_colour(sctx->s, p); |
2237 | break; |
2238 | case 52: |
2239 | input_osc_52(ictx, p); |
2240 | break; |
2241 | case 104: |
2242 | input_osc_104(ictx, p); |
2243 | break; |
2244 | case 112: |
2245 | if (*p == '\0') /* no arguments allowed */ |
2246 | screen_set_cursor_colour(sctx->s, "" ); |
2247 | break; |
2248 | default: |
2249 | log_debug("%s: unknown '%u'" , __func__, option); |
2250 | break; |
2251 | } |
2252 | } |
2253 | |
2254 | /* APC string started. */ |
2255 | static void |
2256 | input_enter_apc(struct input_ctx *ictx) |
2257 | { |
2258 | log_debug("%s" , __func__); |
2259 | |
2260 | input_clear(ictx); |
2261 | input_start_timer(ictx); |
2262 | ictx->last = -1; |
2263 | } |
2264 | |
2265 | /* APC terminator (ST) received. */ |
2266 | static void |
2267 | input_exit_apc(struct input_ctx *ictx) |
2268 | { |
2269 | struct screen_write_ctx *sctx = &ictx->ctx; |
2270 | |
2271 | if (ictx->flags & INPUT_DISCARD) |
2272 | return; |
2273 | log_debug("%s: \"%s\"" , __func__, ictx->input_buf); |
2274 | |
2275 | if (screen_set_title(sctx->s, ictx->input_buf)) |
2276 | server_status_window(ictx->wp->window); |
2277 | } |
2278 | |
2279 | /* Rename string started. */ |
2280 | static void |
2281 | input_enter_rename(struct input_ctx *ictx) |
2282 | { |
2283 | log_debug("%s" , __func__); |
2284 | |
2285 | input_clear(ictx); |
2286 | input_start_timer(ictx); |
2287 | ictx->last = -1; |
2288 | } |
2289 | |
2290 | /* Rename terminator (ST) received. */ |
2291 | static void |
2292 | input_exit_rename(struct input_ctx *ictx) |
2293 | { |
2294 | struct window_pane *wp = ictx->wp; |
2295 | struct options_entry *oe; |
2296 | |
2297 | if (ictx->flags & INPUT_DISCARD) |
2298 | return; |
2299 | if (!options_get_number(ictx->wp->options, "allow-rename" )) |
2300 | return; |
2301 | log_debug("%s: \"%s\"" , __func__, ictx->input_buf); |
2302 | |
2303 | if (!utf8_isvalid(ictx->input_buf)) |
2304 | return; |
2305 | |
2306 | if (ictx->input_len == 0) { |
2307 | oe = options_get(wp->window->options, "automatic-rename" ); |
2308 | if (oe != NULL) |
2309 | options_remove(oe); |
2310 | return; |
2311 | } |
2312 | window_set_name(ictx->wp->window, ictx->input_buf); |
2313 | options_set_number(ictx->wp->window->options, "automatic-rename" , 0); |
2314 | server_status_window(ictx->wp->window); |
2315 | } |
2316 | |
2317 | /* Open UTF-8 character. */ |
2318 | static int |
2319 | input_top_bit_set(struct input_ctx *ictx) |
2320 | { |
2321 | struct screen_write_ctx *sctx = &ictx->ctx; |
2322 | struct utf8_data *ud = &ictx->utf8data; |
2323 | |
2324 | ictx->last = -1; |
2325 | |
2326 | if (!ictx->utf8started) { |
2327 | if (utf8_open(ud, ictx->ch) != UTF8_MORE) |
2328 | return (0); |
2329 | ictx->utf8started = 1; |
2330 | return (0); |
2331 | } |
2332 | |
2333 | switch (utf8_append(ud, ictx->ch)) { |
2334 | case UTF8_MORE: |
2335 | return (0); |
2336 | case UTF8_ERROR: |
2337 | ictx->utf8started = 0; |
2338 | return (0); |
2339 | case UTF8_DONE: |
2340 | break; |
2341 | } |
2342 | ictx->utf8started = 0; |
2343 | |
2344 | log_debug("%s %hhu '%*s' (width %hhu)" , __func__, ud->size, |
2345 | (int)ud->size, ud->data, ud->width); |
2346 | |
2347 | utf8_copy(&ictx->cell.cell.data, ud); |
2348 | screen_write_collect_add(sctx, &ictx->cell.cell); |
2349 | |
2350 | return (0); |
2351 | } |
2352 | |
2353 | /* Parse colour from OSC. */ |
2354 | static int |
2355 | input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b) |
2356 | { |
2357 | u_int rsize, gsize, bsize; |
2358 | const char *cp, *s = p; |
2359 | |
2360 | if (sscanf(p, "rgb:%x/%x/%x" , r, g, b) != 3) |
2361 | return (0); |
2362 | p += 4; |
2363 | |
2364 | cp = strchr(p, '/'); |
2365 | rsize = cp - p; |
2366 | if (rsize == 1) |
2367 | (*r) = (*r) | ((*r) << 4); |
2368 | else if (rsize == 3) |
2369 | (*r) >>= 4; |
2370 | else if (rsize == 4) |
2371 | (*r) >>= 8; |
2372 | else if (rsize != 2) |
2373 | return (0); |
2374 | |
2375 | p = cp + 1; |
2376 | cp = strchr(p, '/'); |
2377 | gsize = cp - p; |
2378 | if (gsize == 1) |
2379 | (*g) = (*g) | ((*g) << 4); |
2380 | else if (gsize == 3) |
2381 | (*g) >>= 4; |
2382 | else if (gsize == 4) |
2383 | (*g) >>= 8; |
2384 | else if (gsize != 2) |
2385 | return (0); |
2386 | |
2387 | bsize = strlen(cp + 1); |
2388 | if (bsize == 1) |
2389 | (*b) = (*b) | ((*b) << 4); |
2390 | else if (bsize == 3) |
2391 | (*b) >>= 4; |
2392 | else if (bsize == 4) |
2393 | (*b) >>= 8; |
2394 | else if (bsize != 2) |
2395 | return (0); |
2396 | |
2397 | log_debug("%s: %s = %02x%02x%02x" , __func__, s, *r, *g, *b); |
2398 | return (1); |
2399 | } |
2400 | |
2401 | /* Handle the OSC 4 sequence for setting (multiple) palette entries. */ |
2402 | static void |
2403 | input_osc_4(struct input_ctx *ictx, const char *p) |
2404 | { |
2405 | struct window_pane *wp = ictx->wp; |
2406 | char *copy, *s, *next = NULL; |
2407 | long idx; |
2408 | u_int r, g, b; |
2409 | |
2410 | copy = s = xstrdup(p); |
2411 | while (s != NULL && *s != '\0') { |
2412 | idx = strtol(s, &next, 10); |
2413 | if (*next++ != ';') |
2414 | goto bad; |
2415 | if (idx < 0 || idx >= 0x100) |
2416 | goto bad; |
2417 | |
2418 | s = strsep(&next, ";" ); |
2419 | if (!input_osc_parse_colour(s, &r, &g, &b)) { |
2420 | s = next; |
2421 | continue; |
2422 | } |
2423 | |
2424 | window_pane_set_palette(wp, idx, colour_join_rgb(r, g, b)); |
2425 | s = next; |
2426 | } |
2427 | |
2428 | free(copy); |
2429 | return; |
2430 | |
2431 | bad: |
2432 | log_debug("bad OSC 4: %s" , p); |
2433 | free(copy); |
2434 | } |
2435 | |
2436 | /* Handle the OSC 10 sequence for setting foreground colour. */ |
2437 | static void |
2438 | input_osc_10(struct input_ctx *ictx, const char *p) |
2439 | { |
2440 | struct window_pane *wp = ictx->wp; |
2441 | u_int r, g, b; |
2442 | char tmp[16]; |
2443 | |
2444 | if (strcmp(p, "?" ) == 0) |
2445 | return; |
2446 | |
2447 | if (!input_osc_parse_colour(p, &r, &g, &b)) |
2448 | goto bad; |
2449 | xsnprintf(tmp, sizeof tmp, "fg=#%02x%02x%02x" , r, g, b); |
2450 | options_set_style(wp->options, "window-style" , 1, tmp); |
2451 | options_set_style(wp->options, "window-active-style" , 1, tmp); |
2452 | wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); |
2453 | |
2454 | return; |
2455 | |
2456 | bad: |
2457 | log_debug("bad OSC 10: %s" , p); |
2458 | } |
2459 | |
2460 | /* Handle the OSC 11 sequence for setting background colour. */ |
2461 | static void |
2462 | input_osc_11(struct input_ctx *ictx, const char *p) |
2463 | { |
2464 | struct window_pane *wp = ictx->wp; |
2465 | u_int r, g, b; |
2466 | char tmp[16]; |
2467 | |
2468 | if (strcmp(p, "?" ) == 0) |
2469 | return; |
2470 | |
2471 | if (!input_osc_parse_colour(p, &r, &g, &b)) |
2472 | goto bad; |
2473 | xsnprintf(tmp, sizeof tmp, "bg=#%02x%02x%02x" , r, g, b); |
2474 | options_set_style(wp->options, "window-style" , 1, tmp); |
2475 | options_set_style(wp->options, "window-active-style" , 1, tmp); |
2476 | wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); |
2477 | |
2478 | return; |
2479 | |
2480 | bad: |
2481 | log_debug("bad OSC 11: %s" , p); |
2482 | } |
2483 | |
2484 | /* Handle the OSC 52 sequence for setting the clipboard. */ |
2485 | static void |
2486 | input_osc_52(struct input_ctx *ictx, const char *p) |
2487 | { |
2488 | struct window_pane *wp = ictx->wp; |
2489 | char *end; |
2490 | const char *buf; |
2491 | size_t len; |
2492 | u_char *out; |
2493 | int outlen, state; |
2494 | struct screen_write_ctx ctx; |
2495 | struct paste_buffer *pb; |
2496 | |
2497 | state = options_get_number(global_options, "set-clipboard" ); |
2498 | if (state != 2) |
2499 | return; |
2500 | |
2501 | if ((end = strchr(p, ';')) == NULL) |
2502 | return; |
2503 | end++; |
2504 | if (*end == '\0') |
2505 | return; |
2506 | log_debug("%s: %s" , __func__, end); |
2507 | |
2508 | if (strcmp(end, "?" ) == 0) { |
2509 | if ((pb = paste_get_top(NULL)) != NULL) { |
2510 | buf = paste_buffer_data(pb, &len); |
2511 | outlen = 4 * ((len + 2) / 3) + 1; |
2512 | out = xmalloc(outlen); |
2513 | if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { |
2514 | free(out); |
2515 | return; |
2516 | } |
2517 | } else { |
2518 | outlen = 0; |
2519 | out = NULL; |
2520 | } |
2521 | bufferevent_write(wp->event, "\033]52;;" , 6); |
2522 | if (outlen != 0) |
2523 | bufferevent_write(wp->event, out, outlen); |
2524 | if (ictx->input_end == INPUT_END_BEL) |
2525 | bufferevent_write(wp->event, "\007" , 1); |
2526 | else |
2527 | bufferevent_write(wp->event, "\033\\" , 2); |
2528 | free(out); |
2529 | return; |
2530 | } |
2531 | |
2532 | len = (strlen(end) / 4) * 3; |
2533 | if (len == 0) |
2534 | return; |
2535 | |
2536 | out = xmalloc(len); |
2537 | if ((outlen = b64_pton(end, out, len)) == -1) { |
2538 | free(out); |
2539 | return; |
2540 | } |
2541 | |
2542 | screen_write_start(&ctx, wp, NULL); |
2543 | screen_write_setselection(&ctx, out, outlen); |
2544 | screen_write_stop(&ctx); |
2545 | notify_pane("pane-set-clipboard" , wp); |
2546 | |
2547 | paste_add(NULL, out, outlen); |
2548 | } |
2549 | |
2550 | /* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */ |
2551 | static void |
2552 | input_osc_104(struct input_ctx *ictx, const char *p) |
2553 | { |
2554 | struct window_pane *wp = ictx->wp; |
2555 | char *copy, *s; |
2556 | long idx; |
2557 | |
2558 | if (*p == '\0') { |
2559 | window_pane_reset_palette(wp); |
2560 | return; |
2561 | } |
2562 | |
2563 | copy = s = xstrdup(p); |
2564 | while (*s != '\0') { |
2565 | idx = strtol(s, &s, 10); |
2566 | if (*s != '\0' && *s != ';') |
2567 | goto bad; |
2568 | if (idx < 0 || idx >= 0x100) |
2569 | goto bad; |
2570 | |
2571 | window_pane_unset_palette(wp, idx); |
2572 | if (*s == ';') |
2573 | s++; |
2574 | } |
2575 | free(copy); |
2576 | return; |
2577 | |
2578 | bad: |
2579 | log_debug("bad OSC 104: %s" , p); |
2580 | free(copy); |
2581 | } |
2582 | |