1 /* $OpenBSD: tty.c,v 1.440 2024/10/25 19:36:38 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/ioctl.h>
21
22 #include <netinet/in.h>
23
24 #include <curses.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <resolv.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <termios.h>
31 #include <time.h>
32 #include <unistd.h>
33
34 #include "tmux.h"
35
36 static int tty_log_fd = -1;
37
38 static void tty_set_italics(struct tty *);
39 static int tty_try_colour(struct tty *, int, const char *);
40 static void tty_force_cursor_colour(struct tty *, int);
41 static void tty_cursor_pane(struct tty *, const struct tty_ctx *, u_int,
42 u_int);
43 static void tty_cursor_pane_unless_wrap(struct tty *,
44 const struct tty_ctx *, u_int, u_int);
45 static void tty_colours(struct tty *, const struct grid_cell *);
46 static void tty_check_fg(struct tty *, struct colour_palette *,
47 struct grid_cell *);
48 static void tty_check_bg(struct tty *, struct colour_palette *,
49 struct grid_cell *);
50 static void tty_check_us(struct tty *, struct colour_palette *,
51 struct grid_cell *);
52 static void tty_colours_fg(struct tty *, const struct grid_cell *);
53 static void tty_colours_bg(struct tty *, const struct grid_cell *);
54 static void tty_colours_us(struct tty *, const struct grid_cell *);
55
56 static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int,
57 u_int);
58 static void tty_region(struct tty *, u_int, u_int);
59 static void tty_margin_pane(struct tty *, const struct tty_ctx *);
60 static void tty_margin(struct tty *, u_int, u_int);
61 static int tty_large_region(struct tty *, const struct tty_ctx *);
62 static int tty_fake_bce(const struct tty *, const struct grid_cell *,
63 u_int);
64 static void tty_redraw_region(struct tty *, const struct tty_ctx *);
65 static void tty_emulate_repeat(struct tty *, enum tty_code_code,
66 enum tty_code_code, u_int);
67 static void tty_repeat_space(struct tty *, u_int);
68 static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
69 static void tty_default_attributes(struct tty *, const struct grid_cell *,
70 struct colour_palette *, u_int, struct hyperlinks *);
71 static int tty_check_overlay(struct tty *, u_int, u_int);
72 static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int,
73 struct overlay_ranges *);
74
75 #define tty_use_margin(tty) \
76 (tty->term->flags & TERM_DECSLRM)
77 #define tty_full_width(tty, ctx) \
78 ((ctx)->xoff == 0 && (ctx)->sx >= (tty)->sx)
79
80 #define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */)
81 #define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8)
82 #define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8)
83
84 #define TTY_QUERY_TIMEOUT 5
85 #define TTY_REQUEST_LIMIT 30
86
87 void
tty_create_log(void)88 tty_create_log(void)
89 {
90 char name[64];
91
92 xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid());
93
94 tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644);
95 if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1)
96 fatal("fcntl failed");
97 }
98
99 int
tty_init(struct tty * tty,struct client * c)100 tty_init(struct tty *tty, struct client *c)
101 {
102 if (!isatty(c->fd))
103 return (-1);
104
105 memset(tty, 0, sizeof *tty);
106 tty->client = c;
107
108 tty->cstyle = SCREEN_CURSOR_DEFAULT;
109 tty->ccolour = -1;
110 tty->fg = tty->bg = -1;
111
112 if (tcgetattr(c->fd, &tty->tio) != 0)
113 return (-1);
114 return (0);
115 }
116
117 void
tty_resize(struct tty * tty)118 tty_resize(struct tty *tty)
119 {
120 struct client *c = tty->client;
121 struct winsize ws;
122 u_int sx, sy, xpixel, ypixel;
123
124 if (ioctl(c->fd, TIOCGWINSZ, &ws) != -1) {
125 sx = ws.ws_col;
126 if (sx == 0) {
127 sx = 80;
128 xpixel = 0;
129 } else
130 xpixel = ws.ws_xpixel / sx;
131 sy = ws.ws_row;
132 if (sy == 0) {
133 sy = 24;
134 ypixel = 0;
135 } else
136 ypixel = ws.ws_ypixel / sy;
137
138 if ((xpixel == 0 || ypixel == 0) &&
139 tty->out != NULL &&
140 !(tty->flags & TTY_WINSIZEQUERY) &&
141 (tty->term->flags & TERM_VT100LIKE)) {
142 tty_puts(tty, "\033[18t\033[14t");
143 tty->flags |= TTY_WINSIZEQUERY;
144 }
145 } else {
146 sx = 80;
147 sy = 24;
148 xpixel = 0;
149 ypixel = 0;
150 }
151 log_debug("%s: %s now %ux%u (%ux%u)", __func__, c->name, sx, sy,
152 xpixel, ypixel);
153 tty_set_size(tty, sx, sy, xpixel, ypixel);
154 tty_invalidate(tty);
155 }
156
157 void
tty_set_size(struct tty * tty,u_int sx,u_int sy,u_int xpixel,u_int ypixel)158 tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel)
159 {
160 tty->sx = sx;
161 tty->sy = sy;
162 tty->xpixel = xpixel;
163 tty->ypixel = ypixel;
164 }
165
166 static void
tty_read_callback(__unused int fd,__unused short events,void * data)167 tty_read_callback(__unused int fd, __unused short events, void *data)
168 {
169 struct tty *tty = data;
170 struct client *c = tty->client;
171 const char *name = c->name;
172 size_t size = EVBUFFER_LENGTH(tty->in);
173 int nread;
174
175 nread = evbuffer_read(tty->in, c->fd, -1);
176 if (nread == 0 || nread == -1) {
177 if (nread == 0)
178 log_debug("%s: read closed", name);
179 else
180 log_debug("%s: read error: %s", name, strerror(errno));
181 event_del(&tty->event_in);
182 server_client_lost(tty->client);
183 return;
184 }
185 log_debug("%s: read %d bytes (already %zu)", name, nread, size);
186
187 while (tty_keys_next(tty))
188 ;
189 }
190
191 static void
tty_timer_callback(__unused int fd,__unused short events,void * data)192 tty_timer_callback(__unused int fd, __unused short events, void *data)
193 {
194 struct tty *tty = data;
195 struct client *c = tty->client;
196 struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL };
197
198 log_debug("%s: %zu discarded", c->name, tty->discarded);
199
200 c->flags |= CLIENT_ALLREDRAWFLAGS;
201 c->discarded += tty->discarded;
202
203 if (tty->discarded < TTY_BLOCK_STOP(tty)) {
204 tty->flags &= ~TTY_BLOCK;
205 tty_invalidate(tty);
206 return;
207 }
208 tty->discarded = 0;
209 evtimer_add(&tty->timer, &tv);
210 }
211
212 static int
tty_block_maybe(struct tty * tty)213 tty_block_maybe(struct tty *tty)
214 {
215 struct client *c = tty->client;
216 size_t size = EVBUFFER_LENGTH(tty->out);
217 struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL };
218
219 if (size == 0)
220 tty->flags &= ~TTY_NOBLOCK;
221 else if (tty->flags & TTY_NOBLOCK)
222 return (0);
223
224 if (size < TTY_BLOCK_START(tty))
225 return (0);
226
227 if (tty->flags & TTY_BLOCK)
228 return (1);
229 tty->flags |= TTY_BLOCK;
230
231 log_debug("%s: can't keep up, %zu discarded", c->name, size);
232
233 evbuffer_drain(tty->out, size);
234 c->discarded += size;
235
236 tty->discarded = 0;
237 evtimer_add(&tty->timer, &tv);
238 return (1);
239 }
240
241 static void
tty_write_callback(__unused int fd,__unused short events,void * data)242 tty_write_callback(__unused int fd, __unused short events, void *data)
243 {
244 struct tty *tty = data;
245 struct client *c = tty->client;
246 size_t size = EVBUFFER_LENGTH(tty->out);
247 int nwrite;
248
249 nwrite = evbuffer_write(tty->out, c->fd);
250 if (nwrite == -1)
251 return;
252 log_debug("%s: wrote %d bytes (of %zu)", c->name, nwrite, size);
253
254 if (c->redraw > 0) {
255 if ((size_t)nwrite >= c->redraw)
256 c->redraw = 0;
257 else
258 c->redraw -= nwrite;
259 log_debug("%s: waiting for redraw, %zu bytes left", c->name,
260 c->redraw);
261 } else if (tty_block_maybe(tty))
262 return;
263
264 if (EVBUFFER_LENGTH(tty->out) != 0)
265 event_add(&tty->event_out, NULL);
266 }
267
268 int
tty_open(struct tty * tty,char ** cause)269 tty_open(struct tty *tty, char **cause)
270 {
271 struct client *c = tty->client;
272
273 tty->term = tty_term_create(tty, c->term_name, c->term_caps,
274 c->term_ncaps, &c->term_features, cause);
275 if (tty->term == NULL) {
276 tty_close(tty);
277 return (-1);
278 }
279 tty->flags |= TTY_OPENED;
280
281 tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER);
282
283 event_set(&tty->event_in, c->fd, EV_PERSIST|EV_READ,
284 tty_read_callback, tty);
285 tty->in = evbuffer_new();
286 if (tty->in == NULL)
287 fatal("out of memory");
288
289 event_set(&tty->event_out, c->fd, EV_WRITE, tty_write_callback, tty);
290 tty->out = evbuffer_new();
291 if (tty->out == NULL)
292 fatal("out of memory");
293
294 evtimer_set(&tty->timer, tty_timer_callback, tty);
295
296 tty_start_tty(tty);
297 tty_keys_build(tty);
298
299 return (0);
300 }
301
302 static void
tty_start_timer_callback(__unused int fd,__unused short events,void * data)303 tty_start_timer_callback(__unused int fd, __unused short events, void *data)
304 {
305 struct tty *tty = data;
306 struct client *c = tty->client;
307
308 log_debug("%s: start timer fired", c->name);
309 if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0)
310 tty_update_features(tty);
311 tty->flags |= TTY_ALL_REQUEST_FLAGS;
312 }
313
314 void
tty_start_tty(struct tty * tty)315 tty_start_tty(struct tty *tty)
316 {
317 struct client *c = tty->client;
318 struct termios tio;
319 struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT };
320
321 setblocking(c->fd, 0);
322 event_add(&tty->event_in, NULL);
323
324 memcpy(&tio, &tty->tio, sizeof tio);
325 tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP);
326 tio.c_iflag |= IGNBRK;
327 tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
328 tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|ECHOPRT|
329 ECHOKE|ISIG);
330 tio.c_cc[VMIN] = 1;
331 tio.c_cc[VTIME] = 0;
332 if (tcsetattr(c->fd, TCSANOW, &tio) == 0)
333 tcflush(c->fd, TCOFLUSH);
334
335 tty_putcode(tty, TTYC_SMCUP);
336
337 tty_putcode(tty, TTYC_SMKX);
338 tty_putcode(tty, TTYC_CLEAR);
339
340 if (tty_acs_needed(tty)) {
341 log_debug("%s: using capabilities for ACS", c->name);
342 tty_putcode(tty, TTYC_ENACS);
343 } else
344 log_debug("%s: using UTF-8 for ACS", c->name);
345
346 tty_putcode(tty, TTYC_CNORM);
347 if (tty_term_has(tty->term, TTYC_KMOUS)) {
348 tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l");
349 tty_puts(tty, "\033[?1006l\033[?1005l");
350 }
351 if (tty_term_has(tty->term, TTYC_ENBP))
352 tty_putcode(tty, TTYC_ENBP);
353
354 evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
355 evtimer_add(&tty->start_timer, &tv);
356
357 tty->flags |= TTY_STARTED;
358 tty_invalidate(tty);
359
360 if (tty->ccolour != -1)
361 tty_force_cursor_colour(tty, -1);
362
363 tty->mouse_drag_flag = 0;
364 tty->mouse_drag_update = NULL;
365 tty->mouse_drag_release = NULL;
366 }
367
368 void
tty_send_requests(struct tty * tty)369 tty_send_requests(struct tty *tty)
370 {
371 if (~tty->flags & TTY_STARTED)
372 return;
373
374 if (tty->term->flags & TERM_VT100LIKE) {
375 if (~tty->term->flags & TTY_HAVEDA)
376 tty_puts(tty, "\033[c");
377 if (~tty->flags & TTY_HAVEDA2)
378 tty_puts(tty, "\033[>c");
379 if (~tty->flags & TTY_HAVEXDA)
380 tty_puts(tty, "\033[>q");
381 tty_puts(tty, "\033]10;?\033\\");
382 tty_puts(tty, "\033]11;?\033\\");
383 } else
384 tty->flags |= TTY_ALL_REQUEST_FLAGS;
385 tty->last_requests = time(NULL);
386 }
387
388 void
tty_repeat_requests(struct tty * tty)389 tty_repeat_requests(struct tty *tty)
390 {
391 time_t t = time(NULL);
392
393 if (~tty->flags & TTY_STARTED)
394 return;
395
396 if (t - tty->last_requests <= TTY_REQUEST_LIMIT)
397 return;
398 tty->last_requests = t;
399
400 if (tty->term->flags & TERM_VT100LIKE) {
401 tty_puts(tty, "\033]10;?\033\\");
402 tty_puts(tty, "\033]11;?\033\\");
403 }
404 }
405
406 void
tty_stop_tty(struct tty * tty)407 tty_stop_tty(struct tty *tty)
408 {
409 struct client *c = tty->client;
410 struct winsize ws;
411
412 if (!(tty->flags & TTY_STARTED))
413 return;
414 tty->flags &= ~TTY_STARTED;
415
416 evtimer_del(&tty->start_timer);
417
418 event_del(&tty->timer);
419 tty->flags &= ~TTY_BLOCK;
420
421 event_del(&tty->event_in);
422 event_del(&tty->event_out);
423
424 /*
425 * Be flexible about error handling and try not kill the server just
426 * because the fd is invalid. Things like ssh -t can easily leave us
427 * with a dead tty.
428 */
429 if (ioctl(c->fd, TIOCGWINSZ, &ws) == -1)
430 return;
431 if (tcsetattr(c->fd, TCSANOW, &tty->tio) == -1)
432 return;
433
434 tty_raw(tty, tty_term_string_ii(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
435 if (tty_acs_needed(tty))
436 tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
437 tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
438 tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
439 tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
440 if (tty->cstyle != SCREEN_CURSOR_DEFAULT) {
441 if (tty_term_has(tty->term, TTYC_SE))
442 tty_raw(tty, tty_term_string(tty->term, TTYC_SE));
443 else if (tty_term_has(tty->term, TTYC_SS))
444 tty_raw(tty, tty_term_string_i(tty->term, TTYC_SS, 0));
445 }
446 if (tty->ccolour != -1)
447 tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
448
449 tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
450 if (tty_term_has(tty->term, TTYC_KMOUS)) {
451 tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l");
452 tty_raw(tty, "\033[?1006l\033[?1005l");
453 }
454 if (tty_term_has(tty->term, TTYC_DSBP))
455 tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
456
457 if (tty->term->flags & TERM_VT100LIKE)
458 tty_raw(tty, "\033[?7727l");
459 tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS));
460 tty_raw(tty, tty_term_string(tty->term, TTYC_DSEKS));
461
462 if (tty_use_margin(tty))
463 tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG));
464 tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
465
466 setblocking(c->fd, 1);
467 }
468
469 void
tty_close(struct tty * tty)470 tty_close(struct tty *tty)
471 {
472 if (event_initialized(&tty->key_timer))
473 evtimer_del(&tty->key_timer);
474 tty_stop_tty(tty);
475
476 if (tty->flags & TTY_OPENED) {
477 evbuffer_free(tty->in);
478 event_del(&tty->event_in);
479 evbuffer_free(tty->out);
480 event_del(&tty->event_out);
481
482 tty_term_free(tty->term);
483 tty_keys_free(tty);
484
485 tty->flags &= ~TTY_OPENED;
486 }
487 }
488
489 void
tty_free(struct tty * tty)490 tty_free(struct tty *tty)
491 {
492 tty_close(tty);
493 }
494
495 void
tty_update_features(struct tty * tty)496 tty_update_features(struct tty *tty)
497 {
498 struct client *c = tty->client;
499
500 if (tty_apply_features(tty->term, c->term_features))
501 tty_term_apply_overrides(tty->term);
502
503 if (tty_use_margin(tty))
504 tty_putcode(tty, TTYC_ENMG);
505 if (options_get_number(global_options, "extended-keys"))
506 tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS));
507 if (options_get_number(global_options, "focus-events"))
508 tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS));
509 if (tty->term->flags & TERM_VT100LIKE)
510 tty_puts(tty, "\033[?7727h");
511
512 /*
513 * Features might have changed since the first draw during attach. For
514 * example, this happens when DA responses are received.
515 */
516 server_redraw_client(c);
517
518 tty_invalidate(tty);
519 }
520
521 void
tty_raw(struct tty * tty,const char * s)522 tty_raw(struct tty *tty, const char *s)
523 {
524 struct client *c = tty->client;
525 ssize_t n, slen;
526 u_int i;
527
528 slen = strlen(s);
529 for (i = 0; i < 5; i++) {
530 n = write(c->fd, s, slen);
531 if (n >= 0) {
532 s += n;
533 slen -= n;
534 if (slen == 0)
535 break;
536 } else if (n == -1 && errno != EAGAIN)
537 break;
538 usleep(100);
539 }
540 }
541
542 void
tty_putcode(struct tty * tty,enum tty_code_code code)543 tty_putcode(struct tty *tty, enum tty_code_code code)
544 {
545 tty_puts(tty, tty_term_string(tty->term, code));
546 }
547
548 void
tty_putcode_i(struct tty * tty,enum tty_code_code code,int a)549 tty_putcode_i(struct tty *tty, enum tty_code_code code, int a)
550 {
551 if (a < 0)
552 return;
553 tty_puts(tty, tty_term_string_i(tty->term, code, a));
554 }
555
556 void
tty_putcode_ii(struct tty * tty,enum tty_code_code code,int a,int b)557 tty_putcode_ii(struct tty *tty, enum tty_code_code code, int a, int b)
558 {
559 if (a < 0 || b < 0)
560 return;
561 tty_puts(tty, tty_term_string_ii(tty->term, code, a, b));
562 }
563
564 void
tty_putcode_iii(struct tty * tty,enum tty_code_code code,int a,int b,int c)565 tty_putcode_iii(struct tty *tty, enum tty_code_code code, int a, int b, int c)
566 {
567 if (a < 0 || b < 0 || c < 0)
568 return;
569 tty_puts(tty, tty_term_string_iii(tty->term, code, a, b, c));
570 }
571
572 void
tty_putcode_s(struct tty * tty,enum tty_code_code code,const char * a)573 tty_putcode_s(struct tty *tty, enum tty_code_code code, const char *a)
574 {
575 if (a != NULL)
576 tty_puts(tty, tty_term_string_s(tty->term, code, a));
577 }
578
579 void
tty_putcode_ss(struct tty * tty,enum tty_code_code code,const char * a,const char * b)580 tty_putcode_ss(struct tty *tty, enum tty_code_code code, const char *a,
581 const char *b)
582 {
583 if (a != NULL && b != NULL)
584 tty_puts(tty, tty_term_string_ss(tty->term, code, a, b));
585 }
586
587 static void
tty_add(struct tty * tty,const char * buf,size_t len)588 tty_add(struct tty *tty, const char *buf, size_t len)
589 {
590 struct client *c = tty->client;
591
592 if (tty->flags & TTY_BLOCK) {
593 tty->discarded += len;
594 return;
595 }
596
597 evbuffer_add(tty->out, buf, len);
598 log_debug("%s: %.*s", c->name, (int)len, buf);
599 c->written += len;
600
601 if (tty_log_fd != -1)
602 write(tty_log_fd, buf, len);
603 if (tty->flags & TTY_STARTED)
604 event_add(&tty->event_out, NULL);
605 }
606
607 void
tty_puts(struct tty * tty,const char * s)608 tty_puts(struct tty *tty, const char *s)
609 {
610 if (*s != '\0')
611 tty_add(tty, s, strlen(s));
612 }
613
614 void
tty_putc(struct tty * tty,u_char ch)615 tty_putc(struct tty *tty, u_char ch)
616 {
617 const char *acs;
618
619 if ((tty->term->flags & TERM_NOAM) &&
620 ch >= 0x20 && ch != 0x7f &&
621 tty->cy == tty->sy - 1 &&
622 tty->cx + 1 >= tty->sx)
623 return;
624
625 if (tty->cell.attr & GRID_ATTR_CHARSET) {
626 acs = tty_acs_get(tty, ch);
627 if (acs != NULL)
628 tty_add(tty, acs, strlen(acs));
629 else
630 tty_add(tty, &ch, 1);
631 } else
632 tty_add(tty, &ch, 1);
633
634 if (ch >= 0x20 && ch != 0x7f) {
635 if (tty->cx >= tty->sx) {
636 tty->cx = 1;
637 if (tty->cy != tty->rlower)
638 tty->cy++;
639
640 /*
641 * On !am terminals, force the cursor position to where
642 * we think it should be after a line wrap - this means
643 * it works on sensible terminals as well.
644 */
645 if (tty->term->flags & TERM_NOAM)
646 tty_putcode_ii(tty, TTYC_CUP, tty->cy, tty->cx);
647 } else
648 tty->cx++;
649 }
650 }
651
652 void
tty_putn(struct tty * tty,const void * buf,size_t len,u_int width)653 tty_putn(struct tty *tty, const void *buf, size_t len, u_int width)
654 {
655 if ((tty->term->flags & TERM_NOAM) &&
656 tty->cy == tty->sy - 1 &&
657 tty->cx + len >= tty->sx)
658 len = tty->sx - tty->cx - 1;
659
660 tty_add(tty, buf, len);
661 if (tty->cx + width > tty->sx) {
662 tty->cx = (tty->cx + width) - tty->sx;
663 if (tty->cx <= tty->sx)
664 tty->cy++;
665 else
666 tty->cx = tty->cy = UINT_MAX;
667 } else
668 tty->cx += width;
669 }
670
671 static void
tty_set_italics(struct tty * tty)672 tty_set_italics(struct tty *tty)
673 {
674 const char *s;
675
676 if (tty_term_has(tty->term, TTYC_SITM)) {
677 s = options_get_string(global_options, "default-terminal");
678 if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) {
679 tty_putcode(tty, TTYC_SITM);
680 return;
681 }
682 }
683 tty_putcode(tty, TTYC_SMSO);
684 }
685
686 void
tty_set_title(struct tty * tty,const char * title)687 tty_set_title(struct tty *tty, const char *title)
688 {
689 if (!tty_term_has(tty->term, TTYC_TSL) ||
690 !tty_term_has(tty->term, TTYC_FSL))
691 return;
692
693 tty_putcode(tty, TTYC_TSL);
694 tty_puts(tty, title);
695 tty_putcode(tty, TTYC_FSL);
696 }
697
698 void
tty_set_path(struct tty * tty,const char * title)699 tty_set_path(struct tty *tty, const char *title)
700 {
701 if (!tty_term_has(tty->term, TTYC_SWD) ||
702 !tty_term_has(tty->term, TTYC_FSL))
703 return;
704
705 tty_putcode(tty, TTYC_SWD);
706 tty_puts(tty, title);
707 tty_putcode(tty, TTYC_FSL);
708 }
709
710 static void
tty_force_cursor_colour(struct tty * tty,int c)711 tty_force_cursor_colour(struct tty *tty, int c)
712 {
713 u_char r, g, b;
714 char s[13];
715
716 if (c != -1)
717 c = colour_force_rgb(c);
718 if (c == tty->ccolour)
719 return;
720 if (c == -1)
721 tty_putcode(tty, TTYC_CR);
722 else {
723 colour_split_rgb(c, &r, &g, &b);
724 xsnprintf(s, sizeof s, "rgb:%02hhx/%02hhx/%02hhx", r, g, b);
725 tty_putcode_s(tty, TTYC_CS, s);
726 }
727 tty->ccolour = c;
728 }
729
730 static int
tty_update_cursor(struct tty * tty,int mode,struct screen * s)731 tty_update_cursor(struct tty *tty, int mode, struct screen *s)
732 {
733 enum screen_cursor_style cstyle;
734 int ccolour, changed, cmode = mode;
735
736 /* Set cursor colour if changed. */
737 if (s != NULL) {
738 ccolour = s->ccolour;
739 if (s->ccolour == -1)
740 ccolour = s->default_ccolour;
741 tty_force_cursor_colour(tty, ccolour);
742 }
743
744 /* If cursor is off, set as invisible. */
745 if (~cmode & MODE_CURSOR) {
746 if (tty->mode & MODE_CURSOR)
747 tty_putcode(tty, TTYC_CIVIS);
748 return (cmode);
749 }
750
751 /* Check if blinking or very visible flag changed or style changed. */
752 if (s == NULL)
753 cstyle = tty->cstyle;
754 else {
755 cstyle = s->cstyle;
756 if (cstyle == SCREEN_CURSOR_DEFAULT) {
757 if (~cmode & MODE_CURSOR_BLINKING_SET) {
758 if (s->default_mode & MODE_CURSOR_BLINKING)
759 cmode |= MODE_CURSOR_BLINKING;
760 else
761 cmode &= ~MODE_CURSOR_BLINKING;
762 }
763 cstyle = s->default_cstyle;
764 }
765 }
766
767 /* If nothing changed, do nothing. */
768 changed = cmode ^ tty->mode;
769 if ((changed & CURSOR_MODES) == 0 && cstyle == tty->cstyle)
770 return (cmode);
771
772 /*
773 * Set cursor style. If an explicit style has been set with DECSCUSR,
774 * set it if supported, otherwise send cvvis for blinking styles.
775 *
776 * If no style, has been set (SCREEN_CURSOR_DEFAULT), then send cvvis
777 * if either the blinking or very visible flags are set.
778 */
779 tty_putcode(tty, TTYC_CNORM);
780 switch (cstyle) {
781 case SCREEN_CURSOR_DEFAULT:
782 if (tty->cstyle != SCREEN_CURSOR_DEFAULT) {
783 if (tty_term_has(tty->term, TTYC_SE))
784 tty_putcode(tty, TTYC_SE);
785 else
786 tty_putcode_i(tty, TTYC_SS, 0);
787 }
788 if (cmode & (MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE))
789 tty_putcode(tty, TTYC_CVVIS);
790 break;
791 case SCREEN_CURSOR_BLOCK:
792 if (tty_term_has(tty->term, TTYC_SS)) {
793 if (cmode & MODE_CURSOR_BLINKING)
794 tty_putcode_i(tty, TTYC_SS, 1);
795 else
796 tty_putcode_i(tty, TTYC_SS, 2);
797 } else if (cmode & MODE_CURSOR_BLINKING)
798 tty_putcode(tty, TTYC_CVVIS);
799 break;
800 case SCREEN_CURSOR_UNDERLINE:
801 if (tty_term_has(tty->term, TTYC_SS)) {
802 if (cmode & MODE_CURSOR_BLINKING)
803 tty_putcode_i(tty, TTYC_SS, 3);
804 else
805 tty_putcode_i(tty, TTYC_SS, 4);
806 } else if (cmode & MODE_CURSOR_BLINKING)
807 tty_putcode(tty, TTYC_CVVIS);
808 break;
809 case SCREEN_CURSOR_BAR:
810 if (tty_term_has(tty->term, TTYC_SS)) {
811 if (cmode & MODE_CURSOR_BLINKING)
812 tty_putcode_i(tty, TTYC_SS, 5);
813 else
814 tty_putcode_i(tty, TTYC_SS, 6);
815 } else if (cmode & MODE_CURSOR_BLINKING)
816 tty_putcode(tty, TTYC_CVVIS);
817 break;
818 }
819 tty->cstyle = cstyle;
820 return (cmode);
821 }
822
823 void
tty_update_mode(struct tty * tty,int mode,struct screen * s)824 tty_update_mode(struct tty *tty, int mode, struct screen *s)
825 {
826 struct tty_term *term = tty->term;
827 struct client *c = tty->client;
828 int changed;
829
830 if (tty->flags & TTY_NOCURSOR)
831 mode &= ~MODE_CURSOR;
832
833 if (tty_update_cursor(tty, mode, s) & MODE_CURSOR_BLINKING)
834 mode |= MODE_CURSOR_BLINKING;
835 else
836 mode &= ~MODE_CURSOR_BLINKING;
837
838 changed = mode ^ tty->mode;
839 if (log_get_level() != 0 && changed != 0) {
840 log_debug("%s: current mode %s", c->name,
841 screen_mode_to_string(tty->mode));
842 log_debug("%s: setting mode %s", c->name,
843 screen_mode_to_string(mode));
844 }
845
846 if ((changed & ALL_MOUSE_MODES) && tty_term_has(term, TTYC_KMOUS)) {
847 /*
848 * If the mouse modes have changed, clear then all and apply
849 * again. There are differences in how terminals track the
850 * various bits.
851 */
852 tty_puts(tty, "\033[?1006l\033[?1000l\033[?1002l\033[?1003l");
853 if (mode & ALL_MOUSE_MODES)
854 tty_puts(tty, "\033[?1006h");
855 if (mode & MODE_MOUSE_ALL)
856 tty_puts(tty, "\033[?1000h\033[?1002h\033[?1003h");
857 else if (mode & MODE_MOUSE_BUTTON)
858 tty_puts(tty, "\033[?1000h\033[?1002h");
859 else if (mode & MODE_MOUSE_STANDARD)
860 tty_puts(tty, "\033[?1000h");
861 }
862 tty->mode = mode;
863 }
864
865 static void
tty_emulate_repeat(struct tty * tty,enum tty_code_code code,enum tty_code_code code1,u_int n)866 tty_emulate_repeat(struct tty *tty, enum tty_code_code code,
867 enum tty_code_code code1, u_int n)
868 {
869 if (tty_term_has(tty->term, code))
870 tty_putcode_i(tty, code, n);
871 else {
872 while (n-- > 0)
873 tty_putcode(tty, code1);
874 }
875 }
876
877 static void
tty_repeat_space(struct tty * tty,u_int n)878 tty_repeat_space(struct tty *tty, u_int n)
879 {
880 static char s[500];
881
882 if (*s != ' ')
883 memset(s, ' ', sizeof s);
884
885 while (n > sizeof s) {
886 tty_putn(tty, s, sizeof s, sizeof s);
887 n -= sizeof s;
888 }
889 if (n != 0)
890 tty_putn(tty, s, n, n);
891 }
892
893 /* Is this window bigger than the terminal? */
894 int
tty_window_bigger(struct tty * tty)895 tty_window_bigger(struct tty *tty)
896 {
897 struct client *c = tty->client;
898 struct window *w = c->session->curw->window;
899
900 return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy);
901 }
902
903 /* What offset should this window be drawn at? */
904 int
tty_window_offset(struct tty * tty,u_int * ox,u_int * oy,u_int * sx,u_int * sy)905 tty_window_offset(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
906 {
907 *ox = tty->oox;
908 *oy = tty->ooy;
909 *sx = tty->osx;
910 *sy = tty->osy;
911
912 return (tty->oflag);
913 }
914
915 /* What offset should this window be drawn at? */
916 static int
tty_window_offset1(struct tty * tty,u_int * ox,u_int * oy,u_int * sx,u_int * sy)917 tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
918 {
919 struct client *c = tty->client;
920 struct window *w = c->session->curw->window;
921 struct window_pane *wp = server_client_get_pane(c);
922 u_int cx, cy, lines;
923
924 lines = status_line_size(c);
925
926 if (tty->sx >= w->sx && tty->sy - lines >= w->sy) {
927 *ox = 0;
928 *oy = 0;
929 *sx = w->sx;
930 *sy = w->sy;
931
932 c->pan_window = NULL;
933 return (0);
934 }
935
936 *sx = tty->sx;
937 *sy = tty->sy - lines;
938
939 if (c->pan_window == w) {
940 if (*sx >= w->sx)
941 c->pan_ox = 0;
942 else if (c->pan_ox + *sx > w->sx)
943 c->pan_ox = w->sx - *sx;
944 *ox = c->pan_ox;
945 if (*sy >= w->sy)
946 c->pan_oy = 0;
947 else if (c->pan_oy + *sy > w->sy)
948 c->pan_oy = w->sy - *sy;
949 *oy = c->pan_oy;
950 return (1);
951 }
952
953 if (~wp->screen->mode & MODE_CURSOR) {
954 *ox = 0;
955 *oy = 0;
956 } else {
957 cx = wp->xoff + wp->screen->cx;
958 cy = wp->yoff + wp->screen->cy;
959
960 if (cx < *sx)
961 *ox = 0;
962 else if (cx > w->sx - *sx)
963 *ox = w->sx - *sx;
964 else
965 *ox = cx - *sx / 2;
966
967 if (cy < *sy)
968 *oy = 0;
969 else if (cy > w->sy - *sy)
970 *oy = w->sy - *sy;
971 else
972 *oy = cy - *sy / 2;
973 }
974
975 c->pan_window = NULL;
976 return (1);
977 }
978
979 /* Update stored offsets for a window and redraw if necessary. */
980 void
tty_update_window_offset(struct window * w)981 tty_update_window_offset(struct window *w)
982 {
983 struct client *c;
984
985 TAILQ_FOREACH(c, &clients, entry) {
986 if (c->session != NULL &&
987 c->session->curw != NULL &&
988 c->session->curw->window == w)
989 tty_update_client_offset(c);
990 }
991 }
992
993 /* Update stored offsets for a client and redraw if necessary. */
994 void
tty_update_client_offset(struct client * c)995 tty_update_client_offset(struct client *c)
996 {
997 u_int ox, oy, sx, sy;
998
999 if (~c->flags & CLIENT_TERMINAL)
1000 return;
1001
1002 c->tty.oflag = tty_window_offset1(&c->tty, &ox, &oy, &sx, &sy);
1003 if (ox == c->tty.oox &&
1004 oy == c->tty.ooy &&
1005 sx == c->tty.osx &&
1006 sy == c->tty.osy)
1007 return;
1008
1009 log_debug ("%s: %s offset has changed (%u,%u %ux%u -> %u,%u %ux%u)",
1010 __func__, c->name, c->tty.oox, c->tty.ooy, c->tty.osx, c->tty.osy,
1011 ox, oy, sx, sy);
1012
1013 c->tty.oox = ox;
1014 c->tty.ooy = oy;
1015 c->tty.osx = sx;
1016 c->tty.osy = sy;
1017
1018 c->flags |= (CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS);
1019 }
1020
1021 /*
1022 * Is the region large enough to be worth redrawing once later rather than
1023 * probably several times now? Currently yes if it is more than 50% of the
1024 * pane.
1025 */
1026 static int
tty_large_region(__unused struct tty * tty,const struct tty_ctx * ctx)1027 tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx)
1028 {
1029 return (ctx->orlower - ctx->orupper >= ctx->sy / 2);
1030 }
1031
1032 /*
1033 * Return if BCE is needed but the terminal doesn't have it - it'll need to be
1034 * emulated.
1035 */
1036 static int
tty_fake_bce(const struct tty * tty,const struct grid_cell * gc,u_int bg)1037 tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg)
1038 {
1039 if (tty_term_flag(tty->term, TTYC_BCE))
1040 return (0);
1041 if (!COLOUR_DEFAULT(bg) || !COLOUR_DEFAULT(gc->bg))
1042 return (1);
1043 return (0);
1044 }
1045
1046 /*
1047 * Redraw scroll region using data from screen (already updated). Used when
1048 * CSR not supported, or window is a pane that doesn't take up the full
1049 * width of the terminal.
1050 */
1051 static void
tty_redraw_region(struct tty * tty,const struct tty_ctx * ctx)1052 tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
1053 {
1054 struct client *c = tty->client;
1055 u_int i;
1056
1057 /*
1058 * If region is large, schedule a redraw. In most cases this is likely
1059 * to be followed by some more scrolling.
1060 */
1061 if (tty_large_region(tty, ctx)) {
1062 log_debug("%s: %s large redraw", __func__, c->name);
1063 ctx->redraw_cb(ctx);
1064 return;
1065 }
1066
1067 for (i = ctx->orupper; i <= ctx->orlower; i++)
1068 tty_draw_pane(tty, ctx, i);
1069 }
1070
1071 /* Is this position visible in the pane? */
1072 static int
tty_is_visible(__unused struct tty * tty,const struct tty_ctx * ctx,u_int px,u_int py,u_int nx,u_int ny)1073 tty_is_visible(__unused struct tty *tty, const struct tty_ctx *ctx, u_int px,
1074 u_int py, u_int nx, u_int ny)
1075 {
1076 u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py;
1077
1078 if (!ctx->bigger)
1079 return (1);
1080
1081 if (xoff + nx <= ctx->wox || xoff >= ctx->wox + ctx->wsx ||
1082 yoff + ny <= ctx->woy || yoff >= ctx->woy + ctx->wsy)
1083 return (0);
1084 return (1);
1085 }
1086
1087 /* Clamp line position to visible part of pane. */
1088 static int
tty_clamp_line(struct tty * tty,const struct tty_ctx * ctx,u_int px,u_int py,u_int nx,u_int * i,u_int * x,u_int * rx,u_int * ry)1089 tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
1090 u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry)
1091 {
1092 u_int xoff = ctx->rxoff + px;
1093
1094 if (!tty_is_visible(tty, ctx, px, py, nx, 1))
1095 return (0);
1096 *ry = ctx->yoff + py - ctx->woy;
1097
1098 if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) {
1099 /* All visible. */
1100 *i = 0;
1101 *x = ctx->xoff + px - ctx->wox;
1102 *rx = nx;
1103 } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) {
1104 /* Both left and right not visible. */
1105 *i = ctx->wox;
1106 *x = 0;
1107 *rx = ctx->wsx;
1108 } else if (xoff < ctx->wox) {
1109 /* Left not visible. */
1110 *i = ctx->wox - (ctx->xoff + px);
1111 *x = 0;
1112 *rx = nx - *i;
1113 } else {
1114 /* Right not visible. */
1115 *i = 0;
1116 *x = (ctx->xoff + px) - ctx->wox;
1117 *rx = ctx->wsx - *x;
1118 }
1119 if (*rx > nx)
1120 fatalx("%s: x too big, %u > %u", __func__, *rx, nx);
1121
1122 return (1);
1123 }
1124
1125 /* Clear a line. */
1126 static void
tty_clear_line(struct tty * tty,const struct grid_cell * defaults,u_int py,u_int px,u_int nx,u_int bg)1127 tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
1128 u_int px, u_int nx, u_int bg)
1129 {
1130 struct client *c = tty->client;
1131 struct overlay_ranges r;
1132 u_int i;
1133
1134 log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
1135
1136 /* Nothing to clear. */
1137 if (nx == 0)
1138 return;
1139
1140 /* If genuine BCE is available, can try escape sequences. */
1141 if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
1142 /* Off the end of the line, use EL if available. */
1143 if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) {
1144 tty_cursor(tty, px, py);
1145 tty_putcode(tty, TTYC_EL);
1146 return;
1147 }
1148
1149 /* At the start of the line. Use EL1. */
1150 if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) {
1151 tty_cursor(tty, px + nx - 1, py);
1152 tty_putcode(tty, TTYC_EL1);
1153 return;
1154 }
1155
1156 /* Section of line. Use ECH if possible. */
1157 if (tty_term_has(tty->term, TTYC_ECH)) {
1158 tty_cursor(tty, px, py);
1159 tty_putcode_i(tty, TTYC_ECH, nx);
1160 return;
1161 }
1162 }
1163
1164 /*
1165 * Couldn't use an escape sequence, use spaces. Clear only the visible
1166 * bit if there is an overlay.
1167 */
1168 tty_check_overlay_range(tty, px, py, nx, &r);
1169 for (i = 0; i < OVERLAY_MAX_RANGES; i++) {
1170 if (r.nx[i] == 0)
1171 continue;
1172 tty_cursor(tty, r.px[i], py);
1173 tty_repeat_space(tty, r.nx[i]);
1174 }
1175 }
1176
1177 /* Clear a line, adjusting to visible part of pane. */
1178 static void
tty_clear_pane_line(struct tty * tty,const struct tty_ctx * ctx,u_int py,u_int px,u_int nx,u_int bg)1179 tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py,
1180 u_int px, u_int nx, u_int bg)
1181 {
1182 struct client *c = tty->client;
1183 u_int i, x, rx, ry;
1184
1185 log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
1186
1187 if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry))
1188 tty_clear_line(tty, &ctx->defaults, ry, x, rx, bg);
1189 }
1190
1191 /* Clamp area position to visible part of pane. */
1192 static int
tty_clamp_area(struct tty * tty,const struct tty_ctx * ctx,u_int px,u_int py,u_int nx,u_int ny,u_int * i,u_int * j,u_int * x,u_int * y,u_int * rx,u_int * ry)1193 tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
1194 u_int nx, u_int ny, u_int *i, u_int *j, u_int *x, u_int *y, u_int *rx,
1195 u_int *ry)
1196 {
1197 u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py;
1198
1199 if (!tty_is_visible(tty, ctx, px, py, nx, ny))
1200 return (0);
1201
1202 if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) {
1203 /* All visible. */
1204 *i = 0;
1205 *x = ctx->xoff + px - ctx->wox;
1206 *rx = nx;
1207 } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) {
1208 /* Both left and right not visible. */
1209 *i = ctx->wox;
1210 *x = 0;
1211 *rx = ctx->wsx;
1212 } else if (xoff < ctx->wox) {
1213 /* Left not visible. */
1214 *i = ctx->wox - (ctx->xoff + px);
1215 *x = 0;
1216 *rx = nx - *i;
1217 } else {
1218 /* Right not visible. */
1219 *i = 0;
1220 *x = (ctx->xoff + px) - ctx->wox;
1221 *rx = ctx->wsx - *x;
1222 }
1223 if (*rx > nx)
1224 fatalx("%s: x too big, %u > %u", __func__, *rx, nx);
1225
1226 if (yoff >= ctx->woy && yoff + ny <= ctx->woy + ctx->wsy) {
1227 /* All visible. */
1228 *j = 0;
1229 *y = ctx->yoff + py - ctx->woy;
1230 *ry = ny;
1231 } else if (yoff < ctx->woy && yoff + ny > ctx->woy + ctx->wsy) {
1232 /* Both top and bottom not visible. */
1233 *j = ctx->woy;
1234 *y = 0;
1235 *ry = ctx->wsy;
1236 } else if (yoff < ctx->woy) {
1237 /* Top not visible. */
1238 *j = ctx->woy - (ctx->yoff + py);
1239 *y = 0;
1240 *ry = ny - *j;
1241 } else {
1242 /* Bottom not visible. */
1243 *j = 0;
1244 *y = (ctx->yoff + py) - ctx->woy;
1245 *ry = ctx->wsy - *y;
1246 }
1247 if (*ry > ny)
1248 fatalx("%s: y too big, %u > %u", __func__, *ry, ny);
1249
1250 return (1);
1251 }
1252
1253 /* Clear an area, adjusting to visible part of pane. */
1254 static void
tty_clear_area(struct tty * tty,const struct grid_cell * defaults,u_int py,u_int ny,u_int px,u_int nx,u_int bg)1255 tty_clear_area(struct tty *tty, const struct grid_cell *defaults, u_int py,
1256 u_int ny, u_int px, u_int nx, u_int bg)
1257 {
1258 struct client *c = tty->client;
1259 u_int yy;
1260 char tmp[64];
1261
1262 log_debug("%s: %s, %u,%u at %u,%u", __func__, c->name, nx, ny, px, py);
1263
1264 /* Nothing to clear. */
1265 if (nx == 0 || ny == 0)
1266 return;
1267
1268 /* If genuine BCE is available, can try escape sequences. */
1269 if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
1270 /* Use ED if clearing off the bottom of the terminal. */
1271 if (px == 0 &&
1272 px + nx >= tty->sx &&
1273 py + ny >= tty->sy &&
1274 tty_term_has(tty->term, TTYC_ED)) {
1275 tty_cursor(tty, 0, py);
1276 tty_putcode(tty, TTYC_ED);
1277 return;
1278 }
1279
1280 /*
1281 * On VT420 compatible terminals we can use DECFRA if the
1282 * background colour isn't default (because it doesn't work
1283 * after SGR 0).
1284 */
1285 if ((tty->term->flags & TERM_DECFRA) && !COLOUR_DEFAULT(bg)) {
1286 xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x",
1287 py + 1, px + 1, py + ny, px + nx);
1288 tty_puts(tty, tmp);
1289 return;
1290 }
1291
1292 /* Full lines can be scrolled away to clear them. */
1293 if (px == 0 &&
1294 px + nx >= tty->sx &&
1295 ny > 2 &&
1296 tty_term_has(tty->term, TTYC_CSR) &&
1297 tty_term_has(tty->term, TTYC_INDN)) {
1298 tty_region(tty, py, py + ny - 1);
1299 tty_margin_off(tty);
1300 tty_putcode_i(tty, TTYC_INDN, ny);
1301 return;
1302 }
1303
1304 /*
1305 * If margins are supported, can just scroll the area off to
1306 * clear it.
1307 */
1308 if (nx > 2 &&
1309 ny > 2 &&
1310 tty_term_has(tty->term, TTYC_CSR) &&
1311 tty_use_margin(tty) &&
1312 tty_term_has(tty->term, TTYC_INDN)) {
1313 tty_region(tty, py, py + ny - 1);
1314 tty_margin(tty, px, px + nx - 1);
1315 tty_putcode_i(tty, TTYC_INDN, ny);
1316 return;
1317 }
1318 }
1319
1320 /* Couldn't use an escape sequence, loop over the lines. */
1321 for (yy = py; yy < py + ny; yy++)
1322 tty_clear_line(tty, defaults, yy, px, nx, bg);
1323 }
1324
1325 /* Clear an area in a pane. */
1326 static void
tty_clear_pane_area(struct tty * tty,const struct tty_ctx * ctx,u_int py,u_int ny,u_int px,u_int nx,u_int bg)1327 tty_clear_pane_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
1328 u_int ny, u_int px, u_int nx, u_int bg)
1329 {
1330 u_int i, j, x, y, rx, ry;
1331
1332 if (tty_clamp_area(tty, ctx, px, py, nx, ny, &i, &j, &x, &y, &rx, &ry))
1333 tty_clear_area(tty, &ctx->defaults, y, ry, x, rx, bg);
1334 }
1335
1336 static void
tty_draw_pane(struct tty * tty,const struct tty_ctx * ctx,u_int py)1337 tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
1338 {
1339 struct screen *s = ctx->s;
1340 u_int nx = ctx->sx, i, x, rx, ry;
1341
1342 log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger);
1343
1344 if (!ctx->bigger) {
1345 tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py,
1346 &ctx->defaults, ctx->palette);
1347 return;
1348 }
1349 if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) {
1350 tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->defaults,
1351 ctx->palette);
1352 }
1353 }
1354
1355 static const struct grid_cell *
tty_check_codeset(struct tty * tty,const struct grid_cell * gc)1356 tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
1357 {
1358 static struct grid_cell new;
1359 int c;
1360
1361 /* Characters less than 0x7f are always fine, no matter what. */
1362 if (gc->data.size == 1 && *gc->data.data < 0x7f)
1363 return (gc);
1364 if (gc->flags & GRID_FLAG_TAB)
1365 return (gc);
1366
1367 /* UTF-8 terminal and a UTF-8 character - fine. */
1368 if (tty->client->flags & CLIENT_UTF8)
1369 return (gc);
1370 memcpy(&new, gc, sizeof new);
1371
1372 /* See if this can be mapped to an ACS character. */
1373 c = tty_acs_reverse_get(tty, gc->data.data, gc->data.size);
1374 if (c != -1) {
1375 utf8_set(&new.data, c);
1376 new.attr |= GRID_ATTR_CHARSET;
1377 return (&new);
1378 }
1379
1380 /* Replace by the right number of underscores. */
1381 new.data.size = gc->data.width;
1382 if (new.data.size > UTF8_SIZE)
1383 new.data.size = UTF8_SIZE;
1384 memset(new.data.data, '_', new.data.size);
1385 return (&new);
1386 }
1387
1388 /*
1389 * Check if a single character is obstructed by the overlay and return a
1390 * boolean.
1391 */
1392 static int
tty_check_overlay(struct tty * tty,u_int px,u_int py)1393 tty_check_overlay(struct tty *tty, u_int px, u_int py)
1394 {
1395 struct overlay_ranges r;
1396
1397 /*
1398 * A unit width range will always return nx[2] == 0 from a check, even
1399 * with multiple overlays, so it's sufficient to check just the first
1400 * two entries.
1401 */
1402 tty_check_overlay_range(tty, px, py, 1, &r);
1403 if (r.nx[0] + r.nx[1] == 0)
1404 return (0);
1405 return (1);
1406 }
1407
1408 /* Return parts of the input range which are visible. */
1409 static void
tty_check_overlay_range(struct tty * tty,u_int px,u_int py,u_int nx,struct overlay_ranges * r)1410 tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx,
1411 struct overlay_ranges *r)
1412 {
1413 struct client *c = tty->client;
1414
1415 if (c->overlay_check == NULL) {
1416 r->px[0] = px;
1417 r->nx[0] = nx;
1418 r->px[1] = 0;
1419 r->nx[1] = 0;
1420 r->px[2] = 0;
1421 r->nx[2] = 0;
1422 return;
1423 }
1424
1425 c->overlay_check(c, c->overlay_data, px, py, nx, r);
1426 }
1427
1428 void
tty_draw_line(struct tty * tty,struct screen * s,u_int px,u_int py,u_int nx,u_int atx,u_int aty,const struct grid_cell * defaults,struct colour_palette * palette)1429 tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
1430 u_int atx, u_int aty, const struct grid_cell *defaults,
1431 struct colour_palette *palette)
1432 {
1433 struct grid *gd = s->grid;
1434 struct grid_cell gc, last;
1435 const struct grid_cell *gcp;
1436 struct grid_line *gl;
1437 struct client *c = tty->client;
1438 struct overlay_ranges r;
1439 u_int i, j, ux, sx, width, hidden, eux, nxx;
1440 u_int cellsize;
1441 int flags, cleared = 0, wrapped = 0;
1442 char buf[512];
1443 size_t len;
1444
1445 log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__,
1446 px, py, nx, atx, aty);
1447 log_debug("%s: defaults: fg=%d, bg=%d", __func__, defaults->fg,
1448 defaults->bg);
1449
1450 /*
1451 * py is the line in the screen to draw.
1452 * px is the start x and nx is the width to draw.
1453 * atx,aty is the line on the terminal to draw it.
1454 */
1455
1456 flags = (tty->flags & TTY_NOCURSOR);
1457 tty->flags |= TTY_NOCURSOR;
1458 tty_update_mode(tty, tty->mode, s);
1459
1460 tty_region_off(tty);
1461 tty_margin_off(tty);
1462
1463 /*
1464 * Clamp the width to cellsize - note this is not cellused, because
1465 * there may be empty background cells after it (from BCE).
1466 */
1467 sx = screen_size_x(s);
1468 if (nx > sx)
1469 nx = sx;
1470 cellsize = grid_get_line(gd, gd->hsize + py)->cellsize;
1471 if (sx > cellsize)
1472 sx = cellsize;
1473 if (sx > tty->sx)
1474 sx = tty->sx;
1475 if (sx > nx)
1476 sx = nx;
1477 ux = 0;
1478
1479 if (py == 0)
1480 gl = NULL;
1481 else
1482 gl = grid_get_line(gd, gd->hsize + py - 1);
1483 if (gl == NULL ||
1484 (~gl->flags & GRID_LINE_WRAPPED) ||
1485 atx != 0 ||
1486 tty->cx < tty->sx ||
1487 nx < tty->sx) {
1488 if (nx < tty->sx &&
1489 atx == 0 &&
1490 px + sx != nx &&
1491 tty_term_has(tty->term, TTYC_EL1) &&
1492 !tty_fake_bce(tty, defaults, 8) &&
1493 c->overlay_check == NULL) {
1494 tty_default_attributes(tty, defaults, palette, 8,
1495 s->hyperlinks);
1496 tty_cursor(tty, nx - 1, aty);
1497 tty_putcode(tty, TTYC_EL1);
1498 cleared = 1;
1499 }
1500 } else {
1501 log_debug("%s: wrapped line %u", __func__, aty);
1502 wrapped = 1;
1503 }
1504
1505 memcpy(&last, &grid_default_cell, sizeof last);
1506 len = 0;
1507 width = 0;
1508
1509 for (i = 0; i < sx; i++) {
1510 grid_view_get_cell(gd, px + i, py, &gc);
1511 gcp = tty_check_codeset(tty, &gc);
1512 if (len != 0 &&
1513 (!tty_check_overlay(tty, atx + ux + width, aty) ||
1514 (gcp->attr & GRID_ATTR_CHARSET) ||
1515 gcp->flags != last.flags ||
1516 gcp->attr != last.attr ||
1517 gcp->fg != last.fg ||
1518 gcp->bg != last.bg ||
1519 gcp->us != last.us ||
1520 gcp->link != last.link ||
1521 ux + width + gcp->data.width > nx ||
1522 (sizeof buf) - len < gcp->data.size)) {
1523 tty_attributes(tty, &last, defaults, palette,
1524 s->hyperlinks);
1525 if (last.flags & GRID_FLAG_CLEARED) {
1526 log_debug("%s: %zu cleared", __func__, len);
1527 tty_clear_line(tty, defaults, aty, atx + ux,
1528 width, last.bg);
1529 } else {
1530 if (!wrapped || atx != 0 || ux != 0)
1531 tty_cursor(tty, atx + ux, aty);
1532 tty_putn(tty, buf, len, width);
1533 }
1534 ux += width;
1535
1536 len = 0;
1537 width = 0;
1538 wrapped = 0;
1539 }
1540
1541 if (gcp->flags & GRID_FLAG_SELECTED)
1542 screen_select_cell(s, &last, gcp);
1543 else
1544 memcpy(&last, gcp, sizeof last);
1545
1546 tty_check_overlay_range(tty, atx + ux, aty, gcp->data.width,
1547 &r);
1548 hidden = 0;
1549 for (j = 0; j < OVERLAY_MAX_RANGES; j++)
1550 hidden += r.nx[j];
1551 hidden = gcp->data.width - hidden;
1552 if (hidden != 0 && hidden == gcp->data.width) {
1553 if (~gcp->flags & GRID_FLAG_PADDING)
1554 ux += gcp->data.width;
1555 } else if (hidden != 0 || ux + gcp->data.width > nx) {
1556 if (~gcp->flags & GRID_FLAG_PADDING) {
1557 tty_attributes(tty, &last, defaults, palette,
1558 s->hyperlinks);
1559 for (j = 0; j < OVERLAY_MAX_RANGES; j++) {
1560 if (r.nx[j] == 0)
1561 continue;
1562 /* Effective width drawn so far. */
1563 eux = r.px[j] - atx;
1564 if (eux < nx) {
1565 tty_cursor(tty, r.px[j], aty);
1566 nxx = nx - eux;
1567 if (r.nx[j] > nxx)
1568 r.nx[j] = nxx;
1569 tty_repeat_space(tty, r.nx[j]);
1570 ux = eux + r.nx[j];
1571 }
1572 }
1573 }
1574 } else if (gcp->attr & GRID_ATTR_CHARSET) {
1575 tty_attributes(tty, &last, defaults, palette,
1576 s->hyperlinks);
1577 tty_cursor(tty, atx + ux, aty);
1578 for (j = 0; j < gcp->data.size; j++)
1579 tty_putc(tty, gcp->data.data[j]);
1580 ux += gcp->data.width;
1581 } else if (~gcp->flags & GRID_FLAG_PADDING) {
1582 memcpy(buf + len, gcp->data.data, gcp->data.size);
1583 len += gcp->data.size;
1584 width += gcp->data.width;
1585 }
1586 }
1587 if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) {
1588 tty_attributes(tty, &last, defaults, palette, s->hyperlinks);
1589 if (last.flags & GRID_FLAG_CLEARED) {
1590 log_debug("%s: %zu cleared (end)", __func__, len);
1591 tty_clear_line(tty, defaults, aty, atx + ux, width,
1592 last.bg);
1593 } else {
1594 if (!wrapped || atx != 0 || ux != 0)
1595 tty_cursor(tty, atx + ux, aty);
1596 tty_putn(tty, buf, len, width);
1597 }
1598 ux += width;
1599 }
1600
1601 if (!cleared && ux < nx) {
1602 log_debug("%s: %u to end of line (%zu cleared)", __func__,
1603 nx - ux, len);
1604 tty_default_attributes(tty, defaults, palette, 8,
1605 s->hyperlinks);
1606 tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8);
1607 }
1608
1609 tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags;
1610 tty_update_mode(tty, tty->mode, s);
1611 }
1612
1613 void
tty_sync_start(struct tty * tty)1614 tty_sync_start(struct tty *tty)
1615 {
1616 if (tty->flags & TTY_BLOCK)
1617 return;
1618 if (tty->flags & TTY_SYNCING)
1619 return;
1620 tty->flags |= TTY_SYNCING;
1621
1622 if (tty_term_has(tty->term, TTYC_SYNC)) {
1623 log_debug("%s sync start", tty->client->name);
1624 tty_putcode_i(tty, TTYC_SYNC, 1);
1625 }
1626 }
1627
1628 void
tty_sync_end(struct tty * tty)1629 tty_sync_end(struct tty *tty)
1630 {
1631 if (tty->flags & TTY_BLOCK)
1632 return;
1633 if (~tty->flags & TTY_SYNCING)
1634 return;
1635 tty->flags &= ~TTY_SYNCING;
1636
1637 if (tty_term_has(tty->term, TTYC_SYNC)) {
1638 log_debug("%s sync end", tty->client->name);
1639 tty_putcode_i(tty, TTYC_SYNC, 2);
1640 }
1641 }
1642
1643 static int
tty_client_ready(const struct tty_ctx * ctx,struct client * c)1644 tty_client_ready(const struct tty_ctx *ctx, struct client *c)
1645 {
1646 if (c->session == NULL || c->tty.term == NULL)
1647 return (0);
1648 if (c->flags & CLIENT_SUSPENDED)
1649 return (0);
1650
1651 /*
1652 * If invisible panes are allowed (used for passthrough), don't care if
1653 * redrawing or frozen.
1654 */
1655 if (ctx->allow_invisible_panes)
1656 return (1);
1657
1658 if (c->flags & CLIENT_REDRAWWINDOW)
1659 return (0);
1660 if (c->tty.flags & TTY_FREEZE)
1661 return (0);
1662 return (1);
1663 }
1664
1665 void
tty_write(void (* cmdfn)(struct tty *,const struct tty_ctx *),struct tty_ctx * ctx)1666 tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
1667 struct tty_ctx *ctx)
1668 {
1669 struct client *c;
1670 int state;
1671
1672 if (ctx->set_client_cb == NULL)
1673 return;
1674 TAILQ_FOREACH(c, &clients, entry) {
1675 if (tty_client_ready(ctx, c)) {
1676 state = ctx->set_client_cb(ctx, c);
1677 if (state == -1)
1678 break;
1679 if (state == 0)
1680 continue;
1681 cmdfn(&c->tty, ctx);
1682 }
1683 }
1684 }
1685
1686 void
tty_cmd_insertcharacter(struct tty * tty,const struct tty_ctx * ctx)1687 tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
1688 {
1689 struct client *c = tty->client;
1690
1691 if (ctx->bigger ||
1692 !tty_full_width(tty, ctx) ||
1693 tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1694 (!tty_term_has(tty->term, TTYC_ICH) &&
1695 !tty_term_has(tty->term, TTYC_ICH1)) ||
1696 c->overlay_check != NULL) {
1697 tty_draw_pane(tty, ctx, ctx->ocy);
1698 return;
1699 }
1700
1701 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1702 ctx->s->hyperlinks);
1703
1704 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1705
1706 tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num);
1707 }
1708
1709 void
tty_cmd_deletecharacter(struct tty * tty,const struct tty_ctx * ctx)1710 tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
1711 {
1712 struct client *c = tty->client;
1713
1714 if (ctx->bigger ||
1715 !tty_full_width(tty, ctx) ||
1716 tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1717 (!tty_term_has(tty->term, TTYC_DCH) &&
1718 !tty_term_has(tty->term, TTYC_DCH1)) ||
1719 c->overlay_check != NULL) {
1720 tty_draw_pane(tty, ctx, ctx->ocy);
1721 return;
1722 }
1723
1724 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1725 ctx->s->hyperlinks);
1726
1727 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1728
1729 tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num);
1730 }
1731
1732 void
tty_cmd_clearcharacter(struct tty * tty,const struct tty_ctx * ctx)1733 tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
1734 {
1735 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1736 ctx->s->hyperlinks);
1737
1738 tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg);
1739 }
1740
1741 void
tty_cmd_insertline(struct tty * tty,const struct tty_ctx * ctx)1742 tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
1743 {
1744 struct client *c = tty->client;
1745
1746 if (ctx->bigger ||
1747 !tty_full_width(tty, ctx) ||
1748 tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1749 !tty_term_has(tty->term, TTYC_CSR) ||
1750 !tty_term_has(tty->term, TTYC_IL1) ||
1751 ctx->sx == 1 ||
1752 ctx->sy == 1 ||
1753 c->overlay_check != NULL) {
1754 tty_redraw_region(tty, ctx);
1755 return;
1756 }
1757
1758 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1759 ctx->s->hyperlinks);
1760
1761 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1762 tty_margin_off(tty);
1763 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1764
1765 tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
1766 tty->cx = tty->cy = UINT_MAX;
1767 }
1768
1769 void
tty_cmd_deleteline(struct tty * tty,const struct tty_ctx * ctx)1770 tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
1771 {
1772 struct client *c = tty->client;
1773
1774 if (ctx->bigger ||
1775 !tty_full_width(tty, ctx) ||
1776 tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1777 !tty_term_has(tty->term, TTYC_CSR) ||
1778 !tty_term_has(tty->term, TTYC_DL1) ||
1779 ctx->sx == 1 ||
1780 ctx->sy == 1 ||
1781 c->overlay_check != NULL) {
1782 tty_redraw_region(tty, ctx);
1783 return;
1784 }
1785
1786 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1787 ctx->s->hyperlinks);
1788
1789 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1790 tty_margin_off(tty);
1791 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1792
1793 tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
1794 tty->cx = tty->cy = UINT_MAX;
1795 }
1796
1797 void
tty_cmd_clearline(struct tty * tty,const struct tty_ctx * ctx)1798 tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
1799 {
1800 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1801 ctx->s->hyperlinks);
1802
1803 tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg);
1804 }
1805
1806 void
tty_cmd_clearendofline(struct tty * tty,const struct tty_ctx * ctx)1807 tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
1808 {
1809 u_int nx = ctx->sx - ctx->ocx;
1810
1811 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1812 ctx->s->hyperlinks);
1813
1814 tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg);
1815 }
1816
1817 void
tty_cmd_clearstartofline(struct tty * tty,const struct tty_ctx * ctx)1818 tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
1819 {
1820 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1821 ctx->s->hyperlinks);
1822
1823 tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg);
1824 }
1825
1826 void
tty_cmd_reverseindex(struct tty * tty,const struct tty_ctx * ctx)1827 tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
1828 {
1829 struct client *c = tty->client;
1830
1831 if (ctx->ocy != ctx->orupper)
1832 return;
1833
1834 if (ctx->bigger ||
1835 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1836 tty_fake_bce(tty, &ctx->defaults, 8) ||
1837 !tty_term_has(tty->term, TTYC_CSR) ||
1838 (!tty_term_has(tty->term, TTYC_RI) &&
1839 !tty_term_has(tty->term, TTYC_RIN)) ||
1840 ctx->sx == 1 ||
1841 ctx->sy == 1 ||
1842 c->overlay_check != NULL) {
1843 tty_redraw_region(tty, ctx);
1844 return;
1845 }
1846
1847 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1848 ctx->s->hyperlinks);
1849
1850 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1851 tty_margin_pane(tty, ctx);
1852 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
1853
1854 if (tty_term_has(tty->term, TTYC_RI))
1855 tty_putcode(tty, TTYC_RI);
1856 else
1857 tty_putcode_i(tty, TTYC_RIN, 1);
1858 }
1859
1860 void
tty_cmd_linefeed(struct tty * tty,const struct tty_ctx * ctx)1861 tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
1862 {
1863 struct client *c = tty->client;
1864
1865 if (ctx->ocy != ctx->orlower)
1866 return;
1867
1868 if (ctx->bigger ||
1869 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1870 tty_fake_bce(tty, &ctx->defaults, 8) ||
1871 !tty_term_has(tty->term, TTYC_CSR) ||
1872 ctx->sx == 1 ||
1873 ctx->sy == 1 ||
1874 c->overlay_check != NULL) {
1875 tty_redraw_region(tty, ctx);
1876 return;
1877 }
1878
1879 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1880 ctx->s->hyperlinks);
1881
1882 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1883 tty_margin_pane(tty, ctx);
1884
1885 /*
1886 * If we want to wrap a pane while using margins, the cursor needs to
1887 * be exactly on the right of the region. If the cursor is entirely off
1888 * the edge - move it back to the right. Some terminals are funny about
1889 * this and insert extra spaces, so only use the right if margins are
1890 * enabled.
1891 */
1892 if (ctx->xoff + ctx->ocx > tty->rright) {
1893 if (!tty_use_margin(tty))
1894 tty_cursor(tty, 0, ctx->yoff + ctx->ocy);
1895 else
1896 tty_cursor(tty, tty->rright, ctx->yoff + ctx->ocy);
1897 } else
1898 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1899
1900 tty_putc(tty, '\n');
1901 }
1902
1903 void
tty_cmd_scrollup(struct tty * tty,const struct tty_ctx * ctx)1904 tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
1905 {
1906 struct client *c = tty->client;
1907 u_int i;
1908
1909 if (ctx->bigger ||
1910 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1911 tty_fake_bce(tty, &ctx->defaults, 8) ||
1912 !tty_term_has(tty->term, TTYC_CSR) ||
1913 ctx->sx == 1 ||
1914 ctx->sy == 1 ||
1915 c->overlay_check != NULL) {
1916 tty_redraw_region(tty, ctx);
1917 return;
1918 }
1919
1920 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1921 ctx->s->hyperlinks);
1922
1923 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1924 tty_margin_pane(tty, ctx);
1925
1926 if (ctx->num == 1 || !tty_term_has(tty->term, TTYC_INDN)) {
1927 if (!tty_use_margin(tty))
1928 tty_cursor(tty, 0, tty->rlower);
1929 else
1930 tty_cursor(tty, tty->rright, tty->rlower);
1931 for (i = 0; i < ctx->num; i++)
1932 tty_putc(tty, '\n');
1933 } else {
1934 if (tty->cy == UINT_MAX)
1935 tty_cursor(tty, 0, 0);
1936 else
1937 tty_cursor(tty, 0, tty->cy);
1938 tty_putcode_i(tty, TTYC_INDN, ctx->num);
1939 }
1940 }
1941
1942 void
tty_cmd_scrolldown(struct tty * tty,const struct tty_ctx * ctx)1943 tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
1944 {
1945 u_int i;
1946 struct client *c = tty->client;
1947
1948 if (ctx->bigger ||
1949 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1950 tty_fake_bce(tty, &ctx->defaults, 8) ||
1951 !tty_term_has(tty->term, TTYC_CSR) ||
1952 (!tty_term_has(tty->term, TTYC_RI) &&
1953 !tty_term_has(tty->term, TTYC_RIN)) ||
1954 ctx->sx == 1 ||
1955 ctx->sy == 1 ||
1956 c->overlay_check != NULL) {
1957 tty_redraw_region(tty, ctx);
1958 return;
1959 }
1960
1961 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1962 ctx->s->hyperlinks);
1963
1964 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1965 tty_margin_pane(tty, ctx);
1966 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
1967
1968 if (tty_term_has(tty->term, TTYC_RIN))
1969 tty_putcode_i(tty, TTYC_RIN, ctx->num);
1970 else {
1971 for (i = 0; i < ctx->num; i++)
1972 tty_putcode(tty, TTYC_RI);
1973 }
1974 }
1975
1976 void
tty_cmd_clearendofscreen(struct tty * tty,const struct tty_ctx * ctx)1977 tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
1978 {
1979 u_int px, py, nx, ny;
1980
1981 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1982 ctx->s->hyperlinks);
1983
1984 tty_region_pane(tty, ctx, 0, ctx->sy - 1);
1985 tty_margin_off(tty);
1986
1987 px = 0;
1988 nx = ctx->sx;
1989 py = ctx->ocy + 1;
1990 ny = ctx->sy - ctx->ocy - 1;
1991
1992 tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg);
1993
1994 px = ctx->ocx;
1995 nx = ctx->sx - ctx->ocx;
1996 py = ctx->ocy;
1997
1998 tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg);
1999 }
2000
2001 void
tty_cmd_clearstartofscreen(struct tty * tty,const struct tty_ctx * ctx)2002 tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
2003 {
2004 u_int px, py, nx, ny;
2005
2006 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
2007 ctx->s->hyperlinks);
2008
2009 tty_region_pane(tty, ctx, 0, ctx->sy - 1);
2010 tty_margin_off(tty);
2011
2012 px = 0;
2013 nx = ctx->sx;
2014 py = 0;
2015 ny = ctx->ocy;
2016
2017 tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg);
2018
2019 px = 0;
2020 nx = ctx->ocx + 1;
2021 py = ctx->ocy;
2022
2023 tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg);
2024 }
2025
2026 void
tty_cmd_clearscreen(struct tty * tty,const struct tty_ctx * ctx)2027 tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
2028 {
2029 u_int px, py, nx, ny;
2030
2031 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
2032 ctx->s->hyperlinks);
2033
2034 tty_region_pane(tty, ctx, 0, ctx->sy - 1);
2035 tty_margin_off(tty);
2036
2037 px = 0;
2038 nx = ctx->sx;
2039 py = 0;
2040 ny = ctx->sy;
2041
2042 tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg);
2043 }
2044
2045 void
tty_cmd_alignmenttest(struct tty * tty,const struct tty_ctx * ctx)2046 tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
2047 {
2048 u_int i, j;
2049
2050 if (ctx->bigger) {
2051 ctx->redraw_cb(ctx);
2052 return;
2053 }
2054
2055 tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette,
2056 ctx->s->hyperlinks);
2057
2058 tty_region_pane(tty, ctx, 0, ctx->sy - 1);
2059 tty_margin_off(tty);
2060
2061 for (j = 0; j < ctx->sy; j++) {
2062 tty_cursor_pane(tty, ctx, 0, j);
2063 for (i = 0; i < ctx->sx; i++)
2064 tty_putc(tty, 'E');
2065 }
2066 }
2067
2068 void
tty_cmd_cell(struct tty * tty,const struct tty_ctx * ctx)2069 tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
2070 {
2071 const struct grid_cell *gcp = ctx->cell;
2072 struct screen *s = ctx->s;
2073 struct overlay_ranges r;
2074 u_int px, py, i, vis = 0;
2075
2076 px = ctx->xoff + ctx->ocx - ctx->wox;
2077 py = ctx->yoff + ctx->ocy - ctx->woy;
2078 if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1) ||
2079 (gcp->data.width == 1 && !tty_check_overlay(tty, px, py)))
2080 return;
2081
2082 /* Handle partially obstructed wide characters. */
2083 if (gcp->data.width > 1) {
2084 tty_check_overlay_range(tty, px, py, gcp->data.width, &r);
2085 for (i = 0; i < OVERLAY_MAX_RANGES; i++)
2086 vis += r.nx[i];
2087 if (vis < gcp->data.width) {
2088 tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width,
2089 px, py, &ctx->defaults, ctx->palette);
2090 return;
2091 }
2092 }
2093
2094 if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 &&
2095 ctx->ocy == ctx->orlower &&
2096 tty_full_width(tty, ctx))
2097 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
2098
2099 tty_margin_off(tty);
2100 tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
2101
2102 tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette,
2103 ctx->s->hyperlinks);
2104
2105 if (ctx->num == 1)
2106 tty_invalidate(tty);
2107 }
2108
2109 void
tty_cmd_cells(struct tty * tty,const struct tty_ctx * ctx)2110 tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
2111 {
2112 struct overlay_ranges r;
2113 u_int i, px, py, cx;
2114 char *cp = ctx->ptr;
2115
2116 if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, ctx->num, 1))
2117 return;
2118
2119 if (ctx->bigger &&
2120 (ctx->xoff + ctx->ocx < ctx->wox ||
2121 ctx->xoff + ctx->ocx + ctx->num > ctx->wox + ctx->wsx)) {
2122 if (!ctx->wrapped ||
2123 !tty_full_width(tty, ctx) ||
2124 (tty->term->flags & TERM_NOAM) ||
2125 ctx->xoff + ctx->ocx != 0 ||
2126 ctx->yoff + ctx->ocy != tty->cy + 1 ||
2127 tty->cx < tty->sx ||
2128 tty->cy == tty->rlower)
2129 tty_draw_pane(tty, ctx, ctx->ocy);
2130 else
2131 ctx->redraw_cb(ctx);
2132 return;
2133 }
2134
2135 tty_margin_off(tty);
2136 tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
2137 tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, ctx->s->hyperlinks);
2138
2139 /* Get tty position from pane position for overlay check. */
2140 px = ctx->xoff + ctx->ocx - ctx->wox;
2141 py = ctx->yoff + ctx->ocy - ctx->woy;
2142
2143 tty_check_overlay_range(tty, px, py, ctx->num, &r);
2144 for (i = 0; i < OVERLAY_MAX_RANGES; i++) {
2145 if (r.nx[i] == 0)
2146 continue;
2147 /* Convert back to pane position for printing. */
2148 cx = r.px[i] - ctx->xoff + ctx->wox;
2149 tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
2150 tty_putn(tty, cp + r.px[i] - px, r.nx[i], r.nx[i]);
2151 }
2152 }
2153
2154 void
tty_cmd_setselection(struct tty * tty,const struct tty_ctx * ctx)2155 tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
2156 {
2157 tty_set_selection(tty, ctx->ptr2, ctx->ptr, ctx->num);
2158 }
2159
2160 void
tty_set_selection(struct tty * tty,const char * flags,const char * buf,size_t len)2161 tty_set_selection(struct tty *tty, const char *flags, const char *buf,
2162 size_t len)
2163 {
2164 char *encoded;
2165 size_t size;
2166
2167 if (~tty->flags & TTY_STARTED)
2168 return;
2169 if (!tty_term_has(tty->term, TTYC_MS))
2170 return;
2171
2172 size = 4 * ((len + 2) / 3) + 1; /* storage for base64 */
2173 encoded = xmalloc(size);
2174
2175 b64_ntop(buf, len, encoded, size);
2176 tty->flags |= TTY_NOBLOCK;
2177 tty_putcode_ss(tty, TTYC_MS, flags, encoded);
2178
2179 free(encoded);
2180 }
2181
2182 void
tty_cmd_rawstring(struct tty * tty,const struct tty_ctx * ctx)2183 tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
2184 {
2185 tty->flags |= TTY_NOBLOCK;
2186 tty_add(tty, ctx->ptr, ctx->num);
2187 tty_invalidate(tty);
2188 }
2189
2190 void
tty_cmd_syncstart(struct tty * tty,const struct tty_ctx * ctx)2191 tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
2192 {
2193 if (ctx->num == 0x11) {
2194 /*
2195 * This is an overlay and a command that moves the cursor so
2196 * start synchronized updates.
2197 */
2198 tty_sync_start(tty);
2199 } else if (~ctx->num & 0x10) {
2200 /*
2201 * This is a pane. If there is an overlay, always start;
2202 * otherwise, only if requested.
2203 */
2204 if (ctx->num || tty->client->overlay_draw != NULL)
2205 tty_sync_start(tty);
2206 }
2207 }
2208
2209 void
tty_cell(struct tty * tty,const struct grid_cell * gc,const struct grid_cell * defaults,struct colour_palette * palette,struct hyperlinks * hl)2210 tty_cell(struct tty *tty, const struct grid_cell *gc,
2211 const struct grid_cell *defaults, struct colour_palette *palette,
2212 struct hyperlinks *hl)
2213 {
2214 const struct grid_cell *gcp;
2215
2216 /* Skip last character if terminal is stupid. */
2217 if ((tty->term->flags & TERM_NOAM) &&
2218 tty->cy == tty->sy - 1 &&
2219 tty->cx == tty->sx - 1)
2220 return;
2221
2222 /* If this is a padding character, do nothing. */
2223 if (gc->flags & GRID_FLAG_PADDING)
2224 return;
2225
2226 /* Check the output codeset and apply attributes. */
2227 gcp = tty_check_codeset(tty, gc);
2228 tty_attributes(tty, gcp, defaults, palette, hl);
2229
2230 /* If it is a single character, write with putc to handle ACS. */
2231 if (gcp->data.size == 1) {
2232 tty_attributes(tty, gcp, defaults, palette, hl);
2233 if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f)
2234 return;
2235 tty_putc(tty, *gcp->data.data);
2236 return;
2237 }
2238
2239 /* Write the data. */
2240 tty_putn(tty, gcp->data.data, gcp->data.size, gcp->data.width);
2241 }
2242
2243 void
tty_reset(struct tty * tty)2244 tty_reset(struct tty *tty)
2245 {
2246 struct grid_cell *gc = &tty->cell;
2247
2248 if (!grid_cells_equal(gc, &grid_default_cell)) {
2249 if (gc->link != 0)
2250 tty_putcode_ss(tty, TTYC_HLS, "", "");
2251 if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
2252 tty_putcode(tty, TTYC_RMACS);
2253 tty_putcode(tty, TTYC_SGR0);
2254 memcpy(gc, &grid_default_cell, sizeof *gc);
2255 }
2256 memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell);
2257 }
2258
2259 void
tty_invalidate(struct tty * tty)2260 tty_invalidate(struct tty *tty)
2261 {
2262 memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
2263 memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell);
2264
2265 tty->cx = tty->cy = UINT_MAX;
2266 tty->rupper = tty->rleft = UINT_MAX;
2267 tty->rlower = tty->rright = UINT_MAX;
2268
2269 if (tty->flags & TTY_STARTED) {
2270 if (tty_use_margin(tty))
2271 tty_putcode(tty, TTYC_ENMG);
2272 tty_putcode(tty, TTYC_SGR0);
2273
2274 tty->mode = ALL_MODES;
2275 tty_update_mode(tty, MODE_CURSOR, NULL);
2276
2277 tty_cursor(tty, 0, 0);
2278 tty_region_off(tty);
2279 tty_margin_off(tty);
2280 } else
2281 tty->mode = MODE_CURSOR;
2282 }
2283
2284 /* Turn off margin. */
2285 void
tty_region_off(struct tty * tty)2286 tty_region_off(struct tty *tty)
2287 {
2288 tty_region(tty, 0, tty->sy - 1);
2289 }
2290
2291 /* Set region inside pane. */
2292 static void
tty_region_pane(struct tty * tty,const struct tty_ctx * ctx,u_int rupper,u_int rlower)2293 tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper,
2294 u_int rlower)
2295 {
2296 tty_region(tty, ctx->yoff + rupper - ctx->woy,
2297 ctx->yoff + rlower - ctx->woy);
2298 }
2299
2300 /* Set region at absolute position. */
2301 static void
tty_region(struct tty * tty,u_int rupper,u_int rlower)2302 tty_region(struct tty *tty, u_int rupper, u_int rlower)
2303 {
2304 if (tty->rlower == rlower && tty->rupper == rupper)
2305 return;
2306 if (!tty_term_has(tty->term, TTYC_CSR))
2307 return;
2308
2309 tty->rupper = rupper;
2310 tty->rlower = rlower;
2311
2312 /*
2313 * Some terminals (such as PuTTY) do not correctly reset the cursor to
2314 * 0,0 if it is beyond the last column (they do not reset their wrap
2315 * flag so further output causes a line feed). As a workaround, do an
2316 * explicit move to 0 first.
2317 */
2318 if (tty->cx >= tty->sx) {
2319 if (tty->cy == UINT_MAX)
2320 tty_cursor(tty, 0, 0);
2321 else
2322 tty_cursor(tty, 0, tty->cy);
2323 }
2324
2325 tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower);
2326 tty->cx = tty->cy = UINT_MAX;
2327 }
2328
2329 /* Turn off margin. */
2330 void
tty_margin_off(struct tty * tty)2331 tty_margin_off(struct tty *tty)
2332 {
2333 tty_margin(tty, 0, tty->sx - 1);
2334 }
2335
2336 /* Set margin inside pane. */
2337 static void
tty_margin_pane(struct tty * tty,const struct tty_ctx * ctx)2338 tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx)
2339 {
2340 tty_margin(tty, ctx->xoff - ctx->wox,
2341 ctx->xoff + ctx->sx - 1 - ctx->wox);
2342 }
2343
2344 /* Set margin at absolute position. */
2345 static void
tty_margin(struct tty * tty,u_int rleft,u_int rright)2346 tty_margin(struct tty *tty, u_int rleft, u_int rright)
2347 {
2348 if (!tty_use_margin(tty))
2349 return;
2350 if (tty->rleft == rleft && tty->rright == rright)
2351 return;
2352
2353 tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower);
2354
2355 tty->rleft = rleft;
2356 tty->rright = rright;
2357
2358 if (rleft == 0 && rright == tty->sx - 1)
2359 tty_putcode(tty, TTYC_CLMG);
2360 else
2361 tty_putcode_ii(tty, TTYC_CMG, rleft, rright);
2362 tty->cx = tty->cy = UINT_MAX;
2363 }
2364
2365 /*
2366 * Move the cursor, unless it would wrap itself when the next character is
2367 * printed.
2368 */
2369 static void
tty_cursor_pane_unless_wrap(struct tty * tty,const struct tty_ctx * ctx,u_int cx,u_int cy)2370 tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx,
2371 u_int cx, u_int cy)
2372 {
2373 if (!ctx->wrapped ||
2374 !tty_full_width(tty, ctx) ||
2375 (tty->term->flags & TERM_NOAM) ||
2376 ctx->xoff + cx != 0 ||
2377 ctx->yoff + cy != tty->cy + 1 ||
2378 tty->cx < tty->sx ||
2379 tty->cy == tty->rlower)
2380 tty_cursor_pane(tty, ctx, cx, cy);
2381 else
2382 log_debug("%s: will wrap at %u,%u", __func__, tty->cx, tty->cy);
2383 }
2384
2385 /* Move cursor inside pane. */
2386 static void
tty_cursor_pane(struct tty * tty,const struct tty_ctx * ctx,u_int cx,u_int cy)2387 tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy)
2388 {
2389 tty_cursor(tty, ctx->xoff + cx - ctx->wox, ctx->yoff + cy - ctx->woy);
2390 }
2391
2392 /* Move cursor to absolute position. */
2393 void
tty_cursor(struct tty * tty,u_int cx,u_int cy)2394 tty_cursor(struct tty *tty, u_int cx, u_int cy)
2395 {
2396 struct tty_term *term = tty->term;
2397 u_int thisx, thisy;
2398 int change;
2399
2400 if (tty->flags & TTY_BLOCK)
2401 return;
2402
2403 thisx = tty->cx;
2404 thisy = tty->cy;
2405
2406 /*
2407 * If in the automargin space, and want to be there, do not move.
2408 * Otherwise, force the cursor to be in range (and complain).
2409 */
2410 if (cx == thisx && cy == thisy && cx == tty->sx)
2411 return;
2412 if (cx > tty->sx - 1) {
2413 log_debug("%s: x too big %u > %u", __func__, cx, tty->sx - 1);
2414 cx = tty->sx - 1;
2415 }
2416
2417 /* No change. */
2418 if (cx == thisx && cy == thisy)
2419 return;
2420
2421 /* Currently at the very end of the line - use absolute movement. */
2422 if (thisx > tty->sx - 1)
2423 goto absolute;
2424
2425 /* Move to home position (0, 0). */
2426 if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) {
2427 tty_putcode(tty, TTYC_HOME);
2428 goto out;
2429 }
2430
2431 /* Zero on the next line. */
2432 if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower &&
2433 (!tty_use_margin(tty) || tty->rleft == 0)) {
2434 tty_putc(tty, '\r');
2435 tty_putc(tty, '\n');
2436 goto out;
2437 }
2438
2439 /* Moving column or row. */
2440 if (cy == thisy) {
2441 /*
2442 * Moving column only, row staying the same.
2443 */
2444
2445 /* To left edge. */
2446 if (cx == 0 && (!tty_use_margin(tty) || tty->rleft == 0)) {
2447 tty_putc(tty, '\r');
2448 goto out;
2449 }
2450
2451 /* One to the left. */
2452 if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) {
2453 tty_putcode(tty, TTYC_CUB1);
2454 goto out;
2455 }
2456
2457 /* One to the right. */
2458 if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) {
2459 tty_putcode(tty, TTYC_CUF1);
2460 goto out;
2461 }
2462
2463 /* Calculate difference. */
2464 change = thisx - cx; /* +ve left, -ve right */
2465
2466 /*
2467 * Use HPA if change is larger than absolute, otherwise move
2468 * the cursor with CUB/CUF.
2469 */
2470 if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
2471 tty_putcode_i(tty, TTYC_HPA, cx);
2472 goto out;
2473 } else if (change > 0 &&
2474 tty_term_has(term, TTYC_CUB) &&
2475 !tty_use_margin(tty)) {
2476 if (change == 2 && tty_term_has(term, TTYC_CUB1)) {
2477 tty_putcode(tty, TTYC_CUB1);
2478 tty_putcode(tty, TTYC_CUB1);
2479 goto out;
2480 }
2481 tty_putcode_i(tty, TTYC_CUB, change);
2482 goto out;
2483 } else if (change < 0 &&
2484 tty_term_has(term, TTYC_CUF) &&
2485 !tty_use_margin(tty)) {
2486 tty_putcode_i(tty, TTYC_CUF, -change);
2487 goto out;
2488 }
2489 } else if (cx == thisx) {
2490 /*
2491 * Moving row only, column staying the same.
2492 */
2493
2494 /* One above. */
2495 if (thisy != tty->rupper &&
2496 cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) {
2497 tty_putcode(tty, TTYC_CUU1);
2498 goto out;
2499 }
2500
2501 /* One below. */
2502 if (thisy != tty->rlower &&
2503 cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) {
2504 tty_putcode(tty, TTYC_CUD1);
2505 goto out;
2506 }
2507
2508 /* Calculate difference. */
2509 change = thisy - cy; /* +ve up, -ve down */
2510
2511 /*
2512 * Try to use VPA if change is larger than absolute or if this
2513 * change would cross the scroll region, otherwise use CUU/CUD.
2514 */
2515 if ((u_int) abs(change) > cy ||
2516 (change < 0 && cy - change > tty->rlower) ||
2517 (change > 0 && cy - change < tty->rupper)) {
2518 if (tty_term_has(term, TTYC_VPA)) {
2519 tty_putcode_i(tty, TTYC_VPA, cy);
2520 goto out;
2521 }
2522 } else if (change > 0 && tty_term_has(term, TTYC_CUU)) {
2523 tty_putcode_i(tty, TTYC_CUU, change);
2524 goto out;
2525 } else if (change < 0 && tty_term_has(term, TTYC_CUD)) {
2526 tty_putcode_i(tty, TTYC_CUD, -change);
2527 goto out;
2528 }
2529 }
2530
2531 absolute:
2532 /* Absolute movement. */
2533 tty_putcode_ii(tty, TTYC_CUP, cy, cx);
2534
2535 out:
2536 tty->cx = cx;
2537 tty->cy = cy;
2538 }
2539
2540 static void
tty_hyperlink(struct tty * tty,const struct grid_cell * gc,struct hyperlinks * hl)2541 tty_hyperlink(struct tty *tty, const struct grid_cell *gc,
2542 struct hyperlinks *hl)
2543 {
2544 const char *uri, *id;
2545
2546 if (gc->link == tty->cell.link)
2547 return;
2548 tty->cell.link = gc->link;
2549
2550 if (hl == NULL)
2551 return;
2552
2553 if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, NULL, &id))
2554 tty_putcode_ss(tty, TTYC_HLS, "", "");
2555 else
2556 tty_putcode_ss(tty, TTYC_HLS, id, uri);
2557 }
2558
2559 void
tty_attributes(struct tty * tty,const struct grid_cell * gc,const struct grid_cell * defaults,struct colour_palette * palette,struct hyperlinks * hl)2560 tty_attributes(struct tty *tty, const struct grid_cell *gc,
2561 const struct grid_cell *defaults, struct colour_palette *palette,
2562 struct hyperlinks *hl)
2563 {
2564 struct grid_cell *tc = &tty->cell, gc2;
2565 int changed;
2566
2567 /* Copy cell and update default colours. */
2568 memcpy(&gc2, gc, sizeof gc2);
2569 if (~gc->flags & GRID_FLAG_NOPALETTE) {
2570 if (gc2.fg == 8)
2571 gc2.fg = defaults->fg;
2572 if (gc2.bg == 8)
2573 gc2.bg = defaults->bg;
2574 }
2575
2576 /* Ignore cell if it is the same as the last one. */
2577 if (gc2.attr == tty->last_cell.attr &&
2578 gc2.fg == tty->last_cell.fg &&
2579 gc2.bg == tty->last_cell.bg &&
2580 gc2.us == tty->last_cell.us &&
2581 gc2.link == tty->last_cell.link)
2582 return;
2583
2584 /*
2585 * If no setab, try to use the reverse attribute as a best-effort for a
2586 * non-default background. This is a bit of a hack but it doesn't do
2587 * any serious harm and makes a couple of applications happier.
2588 */
2589 if (!tty_term_has(tty->term, TTYC_SETAB)) {
2590 if (gc2.attr & GRID_ATTR_REVERSE) {
2591 if (gc2.fg != 7 && !COLOUR_DEFAULT(gc2.fg))
2592 gc2.attr &= ~GRID_ATTR_REVERSE;
2593 } else {
2594 if (gc2.bg != 0 && !COLOUR_DEFAULT(gc2.bg))
2595 gc2.attr |= GRID_ATTR_REVERSE;
2596 }
2597 }
2598
2599 /* Fix up the colours if necessary. */
2600 tty_check_fg(tty, palette, &gc2);
2601 tty_check_bg(tty, palette, &gc2);
2602 tty_check_us(tty, palette, &gc2);
2603
2604 /*
2605 * If any bits are being cleared or the underline colour is now default,
2606 * reset everything.
2607 */
2608 if ((tc->attr & ~gc2.attr) || (tc->us != gc2.us && gc2.us == 0))
2609 tty_reset(tty);
2610
2611 /*
2612 * Set the colours. This may call tty_reset() (so it comes next) and
2613 * may add to (NOT remove) the desired attributes.
2614 */
2615 tty_colours(tty, &gc2);
2616
2617 /* Filter out attribute bits already set. */
2618 changed = gc2.attr & ~tc->attr;
2619 tc->attr = gc2.attr;
2620
2621 /* Set the attributes. */
2622 if (changed & GRID_ATTR_BRIGHT)
2623 tty_putcode(tty, TTYC_BOLD);
2624 if (changed & GRID_ATTR_DIM)
2625 tty_putcode(tty, TTYC_DIM);
2626 if (changed & GRID_ATTR_ITALICS)
2627 tty_set_italics(tty);
2628 if (changed & GRID_ATTR_ALL_UNDERSCORE) {
2629 if ((changed & GRID_ATTR_UNDERSCORE) ||
2630 !tty_term_has(tty->term, TTYC_SMULX))
2631 tty_putcode(tty, TTYC_SMUL);
2632 else if (changed & GRID_ATTR_UNDERSCORE_2)
2633 tty_putcode_i(tty, TTYC_SMULX, 2);
2634 else if (changed & GRID_ATTR_UNDERSCORE_3)
2635 tty_putcode_i(tty, TTYC_SMULX, 3);
2636 else if (changed & GRID_ATTR_UNDERSCORE_4)
2637 tty_putcode_i(tty, TTYC_SMULX, 4);
2638 else if (changed & GRID_ATTR_UNDERSCORE_5)
2639 tty_putcode_i(tty, TTYC_SMULX, 5);
2640 }
2641 if (changed & GRID_ATTR_BLINK)
2642 tty_putcode(tty, TTYC_BLINK);
2643 if (changed & GRID_ATTR_REVERSE) {
2644 if (tty_term_has(tty->term, TTYC_REV))
2645 tty_putcode(tty, TTYC_REV);
2646 else if (tty_term_has(tty->term, TTYC_SMSO))
2647 tty_putcode(tty, TTYC_SMSO);
2648 }
2649 if (changed & GRID_ATTR_HIDDEN)
2650 tty_putcode(tty, TTYC_INVIS);
2651 if (changed & GRID_ATTR_STRIKETHROUGH)
2652 tty_putcode(tty, TTYC_SMXX);
2653 if (changed & GRID_ATTR_OVERLINE)
2654 tty_putcode(tty, TTYC_SMOL);
2655 if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
2656 tty_putcode(tty, TTYC_SMACS);
2657
2658 /* Set hyperlink if any. */
2659 tty_hyperlink(tty, gc, hl);
2660
2661 memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell);
2662 }
2663
2664 static void
tty_colours(struct tty * tty,const struct grid_cell * gc)2665 tty_colours(struct tty *tty, const struct grid_cell *gc)
2666 {
2667 struct grid_cell *tc = &tty->cell;
2668
2669 /* No changes? Nothing is necessary. */
2670 if (gc->fg == tc->fg && gc->bg == tc->bg && gc->us == tc->us)
2671 return;
2672
2673 /*
2674 * Is either the default colour? This is handled specially because the
2675 * best solution might be to reset both colours to default, in which
2676 * case if only one is default need to fall onward to set the other
2677 * colour.
2678 */
2679 if (COLOUR_DEFAULT(gc->fg) || COLOUR_DEFAULT(gc->bg)) {
2680 /*
2681 * If don't have AX, send sgr0. This resets both colours to default.
2682 * Otherwise, try to set the default colour only as needed.
2683 */
2684 if (!tty_term_flag(tty->term, TTYC_AX))
2685 tty_reset(tty);
2686 else {
2687 if (COLOUR_DEFAULT(gc->fg) && !COLOUR_DEFAULT(tc->fg)) {
2688 tty_puts(tty, "\033[39m");
2689 tc->fg = gc->fg;
2690 }
2691 if (COLOUR_DEFAULT(gc->bg) && !COLOUR_DEFAULT(tc->bg)) {
2692 tty_puts(tty, "\033[49m");
2693 tc->bg = gc->bg;
2694 }
2695 }
2696 }
2697
2698 /* Set the foreground colour. */
2699 if (!COLOUR_DEFAULT(gc->fg) && gc->fg != tc->fg)
2700 tty_colours_fg(tty, gc);
2701
2702 /*
2703 * Set the background colour. This must come after the foreground as
2704 * tty_colours_fg() can call tty_reset().
2705 */
2706 if (!COLOUR_DEFAULT(gc->bg) && gc->bg != tc->bg)
2707 tty_colours_bg(tty, gc);
2708
2709 /* Set the underscore colour. */
2710 if (gc->us != tc->us)
2711 tty_colours_us(tty, gc);
2712 }
2713
2714 static void
tty_check_fg(struct tty * tty,struct colour_palette * palette,struct grid_cell * gc)2715 tty_check_fg(struct tty *tty, struct colour_palette *palette,
2716 struct grid_cell *gc)
2717 {
2718 u_char r, g, b;
2719 u_int colours;
2720 int c;
2721
2722 /*
2723 * Perform substitution if this pane has a palette. If the bright
2724 * attribute is set and Nobr is not present, use the bright entry in
2725 * the palette by changing to the aixterm colour
2726 */
2727 if (~gc->flags & GRID_FLAG_NOPALETTE) {
2728 c = gc->fg;
2729 if (c < 8 &&
2730 gc->attr & GRID_ATTR_BRIGHT &&
2731 !tty_term_has(tty->term, TTYC_NOBR))
2732 c += 90;
2733 if ((c = colour_palette_get(palette, c)) != -1)
2734 gc->fg = c;
2735 }
2736
2737 /* Is this a 24-bit colour? */
2738 if (gc->fg & COLOUR_FLAG_RGB) {
2739 /* Not a 24-bit terminal? Translate to 256-colour palette. */
2740 if (tty->term->flags & TERM_RGBCOLOURS)
2741 return;
2742 colour_split_rgb(gc->fg, &r, &g, &b);
2743 gc->fg = colour_find_rgb(r, g, b);
2744 }
2745
2746 /* How many colours does this terminal have? */
2747 if (tty->term->flags & TERM_256COLOURS)
2748 colours = 256;
2749 else
2750 colours = tty_term_number(tty->term, TTYC_COLORS);
2751
2752 /* Is this a 256-colour colour? */
2753 if (gc->fg & COLOUR_FLAG_256) {
2754 /* And not a 256 colour mode? */
2755 if (colours < 256) {
2756 gc->fg = colour_256to16(gc->fg);
2757 if (gc->fg & 8) {
2758 gc->fg &= 7;
2759 if (colours >= 16)
2760 gc->fg += 90;
2761 }
2762 }
2763 return;
2764 }
2765
2766 /* Is this an aixterm colour? */
2767 if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) {
2768 gc->fg -= 90;
2769 gc->attr |= GRID_ATTR_BRIGHT;
2770 }
2771 }
2772
2773 static void
tty_check_bg(struct tty * tty,struct colour_palette * palette,struct grid_cell * gc)2774 tty_check_bg(struct tty *tty, struct colour_palette *palette,
2775 struct grid_cell *gc)
2776 {
2777 u_char r, g, b;
2778 u_int colours;
2779 int c;
2780
2781 /* Perform substitution if this pane has a palette. */
2782 if (~gc->flags & GRID_FLAG_NOPALETTE) {
2783 if ((c = colour_palette_get(palette, gc->bg)) != -1)
2784 gc->bg = c;
2785 }
2786
2787 /* Is this a 24-bit colour? */
2788 if (gc->bg & COLOUR_FLAG_RGB) {
2789 /* Not a 24-bit terminal? Translate to 256-colour palette. */
2790 if (tty->term->flags & TERM_RGBCOLOURS)
2791 return;
2792 colour_split_rgb(gc->bg, &r, &g, &b);
2793 gc->bg = colour_find_rgb(r, g, b);
2794 }
2795
2796 /* How many colours does this terminal have? */
2797 if (tty->term->flags & TERM_256COLOURS)
2798 colours = 256;
2799 else
2800 colours = tty_term_number(tty->term, TTYC_COLORS);
2801
2802 /* Is this a 256-colour colour? */
2803 if (gc->bg & COLOUR_FLAG_256) {
2804 /*
2805 * And not a 256 colour mode? Translate to 16-colour
2806 * palette. Bold background doesn't exist portably, so just
2807 * discard the bold bit if set.
2808 */
2809 if (colours < 256) {
2810 gc->bg = colour_256to16(gc->bg);
2811 if (gc->bg & 8) {
2812 gc->bg &= 7;
2813 if (colours >= 16)
2814 gc->bg += 90;
2815 }
2816 }
2817 return;
2818 }
2819
2820 /* Is this an aixterm colour? */
2821 if (gc->bg >= 90 && gc->bg <= 97 && colours < 16)
2822 gc->bg -= 90;
2823 }
2824
2825 static void
tty_check_us(__unused struct tty * tty,struct colour_palette * palette,struct grid_cell * gc)2826 tty_check_us(__unused struct tty *tty, struct colour_palette *palette,
2827 struct grid_cell *gc)
2828 {
2829 int c;
2830
2831 /* Perform substitution if this pane has a palette. */
2832 if (~gc->flags & GRID_FLAG_NOPALETTE) {
2833 if ((c = colour_palette_get(palette, gc->us)) != -1)
2834 gc->us = c;
2835 }
2836
2837 /* Convert underscore colour if only RGB can be supported. */
2838 if (!tty_term_has(tty->term, TTYC_SETULC1)) {
2839 if ((c = colour_force_rgb (gc->us)) == -1)
2840 gc->us = 8;
2841 else
2842 gc->us = c;
2843 }
2844 }
2845
2846 static void
tty_colours_fg(struct tty * tty,const struct grid_cell * gc)2847 tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
2848 {
2849 struct grid_cell *tc = &tty->cell;
2850 char s[32];
2851
2852 /*
2853 * If the current colour is an aixterm bright colour and the new is not,
2854 * reset because some terminals do not clear bright correctly.
2855 */
2856 if (tty->cell.fg >= 90 &&
2857 tty->cell.bg <= 97 &&
2858 (gc->fg < 90 || gc->fg > 97))
2859 tty_reset(tty);
2860
2861 /* Is this a 24-bit or 256-colour colour? */
2862 if (gc->fg & COLOUR_FLAG_RGB || gc->fg & COLOUR_FLAG_256) {
2863 if (tty_try_colour(tty, gc->fg, "38") == 0)
2864 goto save;
2865 /* Should not get here, already converted in tty_check_fg. */
2866 return;
2867 }
2868
2869 /* Is this an aixterm bright colour? */
2870 if (gc->fg >= 90 && gc->fg <= 97) {
2871 if (tty->term->flags & TERM_256COLOURS) {
2872 xsnprintf(s, sizeof s, "\033[%dm", gc->fg);
2873 tty_puts(tty, s);
2874 } else
2875 tty_putcode_i(tty, TTYC_SETAF, gc->fg - 90 + 8);
2876 goto save;
2877 }
2878
2879 /* Otherwise set the foreground colour. */
2880 tty_putcode_i(tty, TTYC_SETAF, gc->fg);
2881
2882 save:
2883 /* Save the new values in the terminal current cell. */
2884 tc->fg = gc->fg;
2885 }
2886
2887 static void
tty_colours_bg(struct tty * tty,const struct grid_cell * gc)2888 tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
2889 {
2890 struct grid_cell *tc = &tty->cell;
2891 char s[32];
2892
2893 /* Is this a 24-bit or 256-colour colour? */
2894 if (gc->bg & COLOUR_FLAG_RGB || gc->bg & COLOUR_FLAG_256) {
2895 if (tty_try_colour(tty, gc->bg, "48") == 0)
2896 goto save;
2897 /* Should not get here, already converted in tty_check_bg. */
2898 return;
2899 }
2900
2901 /* Is this an aixterm bright colour? */
2902 if (gc->bg >= 90 && gc->bg <= 97) {
2903 if (tty->term->flags & TERM_256COLOURS) {
2904 xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10);
2905 tty_puts(tty, s);
2906 } else
2907 tty_putcode_i(tty, TTYC_SETAB, gc->bg - 90 + 8);
2908 goto save;
2909 }
2910
2911 /* Otherwise set the background colour. */
2912 tty_putcode_i(tty, TTYC_SETAB, gc->bg);
2913
2914 save:
2915 /* Save the new values in the terminal current cell. */
2916 tc->bg = gc->bg;
2917 }
2918
2919 static void
tty_colours_us(struct tty * tty,const struct grid_cell * gc)2920 tty_colours_us(struct tty *tty, const struct grid_cell *gc)
2921 {
2922 struct grid_cell *tc = &tty->cell;
2923 u_int c;
2924 u_char r, g, b;
2925
2926 /* Clear underline colour. */
2927 if (COLOUR_DEFAULT(gc->us)) {
2928 tty_putcode(tty, TTYC_OL);
2929 goto save;
2930 }
2931
2932 /*
2933 * If this is not an RGB colour, use Setulc1 if it exists, otherwise
2934 * convert.
2935 */
2936 if (~gc->us & COLOUR_FLAG_RGB) {
2937 c = gc->us;
2938 if ((~c & COLOUR_FLAG_256) && (c >= 90 && c <= 97))
2939 c -= 82;
2940 tty_putcode_i(tty, TTYC_SETULC1, c & ~COLOUR_FLAG_256);
2941 return;
2942 }
2943
2944 /*
2945 * Setulc and setal follows the ncurses(3) one argument "direct colour"
2946 * capability format. Calculate the colour value.
2947 */
2948 colour_split_rgb(gc->us, &r, &g, &b);
2949 c = (65536 * r) + (256 * g) + b;
2950
2951 /*
2952 * Write the colour. Only use setal if the RGB flag is set because the
2953 * non-RGB version may be wrong.
2954 */
2955 if (tty_term_has(tty->term, TTYC_SETULC))
2956 tty_putcode_i(tty, TTYC_SETULC, c);
2957 else if (tty_term_has(tty->term, TTYC_SETAL) &&
2958 tty_term_has(tty->term, TTYC_RGB))
2959 tty_putcode_i(tty, TTYC_SETAL, c);
2960
2961 save:
2962 /* Save the new values in the terminal current cell. */
2963 tc->us = gc->us;
2964 }
2965
2966 static int
tty_try_colour(struct tty * tty,int colour,const char * type)2967 tty_try_colour(struct tty *tty, int colour, const char *type)
2968 {
2969 u_char r, g, b;
2970
2971 if (colour & COLOUR_FLAG_256) {
2972 if (*type == '3' && tty_term_has(tty->term, TTYC_SETAF))
2973 tty_putcode_i(tty, TTYC_SETAF, colour & 0xff);
2974 else if (tty_term_has(tty->term, TTYC_SETAB))
2975 tty_putcode_i(tty, TTYC_SETAB, colour & 0xff);
2976 return (0);
2977 }
2978
2979 if (colour & COLOUR_FLAG_RGB) {
2980 colour_split_rgb(colour & 0xffffff, &r, &g, &b);
2981 if (*type == '3' && tty_term_has(tty->term, TTYC_SETRGBF))
2982 tty_putcode_iii(tty, TTYC_SETRGBF, r, g, b);
2983 else if (tty_term_has(tty->term, TTYC_SETRGBB))
2984 tty_putcode_iii(tty, TTYC_SETRGBB, r, g, b);
2985 return (0);
2986 }
2987
2988 return (-1);
2989 }
2990
2991 static void
tty_window_default_style(struct grid_cell * gc,struct window_pane * wp)2992 tty_window_default_style(struct grid_cell *gc, struct window_pane *wp)
2993 {
2994 memcpy(gc, &grid_default_cell, sizeof *gc);
2995 gc->fg = wp->palette.fg;
2996 gc->bg = wp->palette.bg;
2997 }
2998
2999 void
tty_default_colours(struct grid_cell * gc,struct window_pane * wp)3000 tty_default_colours(struct grid_cell *gc, struct window_pane *wp)
3001 {
3002 struct options *oo = wp->options;
3003 struct format_tree *ft;
3004
3005 memcpy(gc, &grid_default_cell, sizeof *gc);
3006
3007 if (wp->flags & PANE_STYLECHANGED) {
3008 log_debug("%%%u: style changed", wp->id);
3009 wp->flags &= ~PANE_STYLECHANGED;
3010
3011 ft = format_create(NULL, NULL, FORMAT_PANE|wp->id,
3012 FORMAT_NOJOBS);
3013 format_defaults(ft, NULL, NULL, NULL, wp);
3014 tty_window_default_style(&wp->cached_active_gc, wp);
3015 style_add(&wp->cached_active_gc, oo, "window-active-style", ft);
3016 tty_window_default_style(&wp->cached_gc, wp);
3017 style_add(&wp->cached_gc, oo, "window-style", ft);
3018 format_free(ft);
3019 }
3020
3021 if (gc->fg == 8) {
3022 if (wp == wp->window->active && wp->cached_active_gc.fg != 8)
3023 gc->fg = wp->cached_active_gc.fg;
3024 else
3025 gc->fg = wp->cached_gc.fg;
3026 }
3027
3028 if (gc->bg == 8) {
3029 if (wp == wp->window->active && wp->cached_active_gc.bg != 8)
3030 gc->bg = wp->cached_active_gc.bg;
3031 else
3032 gc->bg = wp->cached_gc.bg;
3033 }
3034 }
3035
3036 static void
tty_default_attributes(struct tty * tty,const struct grid_cell * defaults,struct colour_palette * palette,u_int bg,struct hyperlinks * hl)3037 tty_default_attributes(struct tty *tty, const struct grid_cell *defaults,
3038 struct colour_palette *palette, u_int bg, struct hyperlinks *hl)
3039 {
3040 struct grid_cell gc;
3041
3042 memcpy(&gc, &grid_default_cell, sizeof gc);
3043 gc.bg = bg;
3044 tty_attributes(tty, &gc, defaults, palette, hl);
3045 }
3046
3047 static void
tty_clipboard_query_callback(__unused int fd,__unused short events,void * data)3048 tty_clipboard_query_callback(__unused int fd, __unused short events, void *data)
3049 {
3050 struct tty *tty = data;
3051 struct client *c = tty->client;
3052
3053 c->flags &= ~CLIENT_CLIPBOARDBUFFER;
3054 free(c->clipboard_panes);
3055 c->clipboard_panes = NULL;
3056 c->clipboard_npanes = 0;
3057
3058 tty->flags &= ~TTY_OSC52QUERY;
3059 }
3060
3061 void
tty_clipboard_query(struct tty * tty)3062 tty_clipboard_query(struct tty *tty)
3063 {
3064 struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT };
3065
3066 if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY))
3067 return;
3068 tty_putcode_ss(tty, TTYC_MS, "", "?");
3069
3070 tty->flags |= TTY_OSC52QUERY;
3071 evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty);
3072 evtimer_add(&tty->clipboard_timer, &tv);
3073 }
3074