Add missing time.h to tty.c (from Ismail Donmez), also remove some stray
[tmux.git] / tty.c
blob2064f534df7bfb8ebd3e7635bc7bfc3e2ef82aa1
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
22 #include <netinet/in.h>
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>
34 #include "tmux.h"
36 static int tty_log_fd = -1;
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_invalidate(struct tty *);
46 static void tty_colours(struct tty *, const struct grid_cell *);
47 static void tty_check_fg(struct tty *, struct colour_palette *,
48 struct grid_cell *);
49 static void tty_check_bg(struct tty *, struct colour_palette *,
50 struct grid_cell *);
51 static void tty_check_us(struct tty *, struct colour_palette *,
52 struct grid_cell *);
53 static void tty_colours_fg(struct tty *, const struct grid_cell *);
54 static void tty_colours_bg(struct tty *, const struct grid_cell *);
55 static void tty_colours_us(struct tty *, const struct grid_cell *);
57 static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int,
58 u_int);
59 static void tty_region(struct tty *, u_int, u_int);
60 static void tty_margin_pane(struct tty *, const struct tty_ctx *);
61 static void tty_margin(struct tty *, u_int, u_int);
62 static int tty_large_region(struct tty *, const struct tty_ctx *);
63 static int tty_fake_bce(const struct tty *, const struct grid_cell *,
64 u_int);
65 static void tty_redraw_region(struct tty *, const struct tty_ctx *);
66 static void tty_emulate_repeat(struct tty *, enum tty_code_code,
67 enum tty_code_code, u_int);
68 static void tty_repeat_space(struct tty *, u_int);
69 static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
70 static void tty_default_attributes(struct tty *, const struct grid_cell *,
71 struct colour_palette *, u_int, struct hyperlinks *);
72 static int tty_check_overlay(struct tty *, u_int, u_int);
73 static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int,
74 struct overlay_ranges *);
76 #define tty_use_margin(tty) \
77 (tty->term->flags & TERM_DECSLRM)
78 #define tty_full_width(tty, ctx) \
79 ((ctx)->xoff == 0 && (ctx)->sx >= (tty)->sx)
81 #define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */)
82 #define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8)
83 #define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8)
85 #define TTY_QUERY_TIMEOUT 5
86 #define TTY_REQUEST_LIMIT 30
88 void
89 tty_create_log(void)
91 char name[64];
93 xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid());
95 tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644);
96 if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1)
97 fatal("fcntl failed");
101 tty_init(struct tty *tty, struct client *c)
103 if (!isatty(c->fd))
104 return (-1);
106 memset(tty, 0, sizeof *tty);
107 tty->client = c;
109 tty->cstyle = SCREEN_CURSOR_DEFAULT;
110 tty->ccolour = -1;
111 tty->fg = tty->bg = -1;
113 if (tcgetattr(c->fd, &tty->tio) != 0)
114 return (-1);
115 return (0);
118 void
119 tty_resize(struct tty *tty)
121 struct client *c = tty->client;
122 struct winsize ws;
123 u_int sx, sy, xpixel, ypixel;
125 if (ioctl(c->fd, TIOCGWINSZ, &ws) != -1) {
126 sx = ws.ws_col;
127 if (sx == 0) {
128 sx = 80;
129 xpixel = 0;
130 } else
131 xpixel = ws.ws_xpixel / sx;
132 sy = ws.ws_row;
133 if (sy == 0) {
134 sy = 24;
135 ypixel = 0;
136 } else
137 ypixel = ws.ws_ypixel / sy;
138 } else {
139 sx = 80;
140 sy = 24;
141 xpixel = 0;
142 ypixel = 0;
144 log_debug("%s: %s now %ux%u (%ux%u)", __func__, c->name, sx, sy,
145 xpixel, ypixel);
146 tty_set_size(tty, sx, sy, xpixel, ypixel);
147 tty_invalidate(tty);
150 void
151 tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel)
153 tty->sx = sx;
154 tty->sy = sy;
155 tty->xpixel = xpixel;
156 tty->ypixel = ypixel;
159 static void
160 tty_read_callback(__unused int fd, __unused short events, void *data)
162 struct tty *tty = data;
163 struct client *c = tty->client;
164 const char *name = c->name;
165 size_t size = EVBUFFER_LENGTH(tty->in);
166 int nread;
168 nread = evbuffer_read(tty->in, c->fd, -1);
169 if (nread == 0 || nread == -1) {
170 if (nread == 0)
171 log_debug("%s: read closed", name);
172 else
173 log_debug("%s: read error: %s", name, strerror(errno));
174 event_del(&tty->event_in);
175 server_client_lost(tty->client);
176 return;
178 log_debug("%s: read %d bytes (already %zu)", name, nread, size);
180 while (tty_keys_next(tty))
184 static void
185 tty_timer_callback(__unused int fd, __unused short events, void *data)
187 struct tty *tty = data;
188 struct client *c = tty->client;
189 struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL };
191 log_debug("%s: %zu discarded", c->name, tty->discarded);
193 c->flags |= CLIENT_ALLREDRAWFLAGS;
194 c->discarded += tty->discarded;
196 if (tty->discarded < TTY_BLOCK_STOP(tty)) {
197 tty->flags &= ~TTY_BLOCK;
198 tty_invalidate(tty);
199 return;
201 tty->discarded = 0;
202 evtimer_add(&tty->timer, &tv);
205 static int
206 tty_block_maybe(struct tty *tty)
208 struct client *c = tty->client;
209 size_t size = EVBUFFER_LENGTH(tty->out);
210 struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL };
212 if (size == 0)
213 tty->flags &= ~TTY_NOBLOCK;
214 else if (tty->flags & TTY_NOBLOCK)
215 return (0);
217 if (size < TTY_BLOCK_START(tty))
218 return (0);
220 if (tty->flags & TTY_BLOCK)
221 return (1);
222 tty->flags |= TTY_BLOCK;
224 log_debug("%s: can't keep up, %zu discarded", c->name, size);
226 evbuffer_drain(tty->out, size);
227 c->discarded += size;
229 tty->discarded = 0;
230 evtimer_add(&tty->timer, &tv);
231 return (1);
234 static void
235 tty_write_callback(__unused int fd, __unused short events, void *data)
237 struct tty *tty = data;
238 struct client *c = tty->client;
239 size_t size = EVBUFFER_LENGTH(tty->out);
240 int nwrite;
242 nwrite = evbuffer_write(tty->out, c->fd);
243 if (nwrite == -1)
244 return;
245 log_debug("%s: wrote %d bytes (of %zu)", c->name, nwrite, size);
247 if (c->redraw > 0) {
248 if ((size_t)nwrite >= c->redraw)
249 c->redraw = 0;
250 else
251 c->redraw -= nwrite;
252 log_debug("%s: waiting for redraw, %zu bytes left", c->name,
253 c->redraw);
254 } else if (tty_block_maybe(tty))
255 return;
257 if (EVBUFFER_LENGTH(tty->out) != 0)
258 event_add(&tty->event_out, NULL);
262 tty_open(struct tty *tty, char **cause)
264 struct client *c = tty->client;
266 tty->term = tty_term_create(tty, c->term_name, c->term_caps,
267 c->term_ncaps, &c->term_features, cause);
268 if (tty->term == NULL) {
269 tty_close(tty);
270 return (-1);
272 tty->flags |= TTY_OPENED;
274 tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER);
276 event_set(&tty->event_in, c->fd, EV_PERSIST|EV_READ,
277 tty_read_callback, tty);
278 tty->in = evbuffer_new();
279 if (tty->in == NULL)
280 fatal("out of memory");
282 event_set(&tty->event_out, c->fd, EV_WRITE, tty_write_callback, tty);
283 tty->out = evbuffer_new();
284 if (tty->out == NULL)
285 fatal("out of memory");
287 evtimer_set(&tty->timer, tty_timer_callback, tty);
289 tty_start_tty(tty);
290 tty_keys_build(tty);
292 return (0);
295 static void
296 tty_start_timer_callback(__unused int fd, __unused short events, void *data)
298 struct tty *tty = data;
299 struct client *c = tty->client;
301 log_debug("%s: start timer fired", c->name);
302 if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0)
303 tty_update_features(tty);
304 tty->flags |= TTY_ALL_REQUEST_FLAGS;
307 void
308 tty_start_tty(struct tty *tty)
310 struct client *c = tty->client;
311 struct termios tio;
312 struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT };
314 setblocking(c->fd, 0);
315 event_add(&tty->event_in, NULL);
317 memcpy(&tio, &tty->tio, sizeof tio);
318 tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP);
319 tio.c_iflag |= IGNBRK;
320 tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
321 tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|ECHOPRT|
322 ECHOKE|ISIG);
323 tio.c_cc[VMIN] = 1;
324 tio.c_cc[VTIME] = 0;
325 if (tcsetattr(c->fd, TCSANOW, &tio) == 0)
326 tcflush(c->fd, TCOFLUSH);
328 tty_putcode(tty, TTYC_SMCUP);
330 tty_putcode(tty, TTYC_SMKX);
331 tty_putcode(tty, TTYC_CLEAR);
333 if (tty_acs_needed(tty)) {
334 log_debug("%s: using capabilities for ACS", c->name);
335 tty_putcode(tty, TTYC_ENACS);
336 } else
337 log_debug("%s: using UTF-8 for ACS", c->name);
339 tty_putcode(tty, TTYC_CNORM);
340 if (tty_term_has(tty->term, TTYC_KMOUS)) {
341 tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l");
342 tty_puts(tty, "\033[?1006l\033[?1005l");
344 if (tty_term_has(tty->term, TTYC_ENBP))
345 tty_putcode(tty, TTYC_ENBP);
347 evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
348 evtimer_add(&tty->start_timer, &tv);
350 tty->flags |= TTY_STARTED;
351 tty_invalidate(tty);
353 if (tty->ccolour != -1)
354 tty_force_cursor_colour(tty, -1);
356 tty->mouse_drag_flag = 0;
357 tty->mouse_drag_update = NULL;
358 tty->mouse_drag_release = NULL;
361 void
362 tty_send_requests(struct tty *tty)
364 if (~tty->flags & TTY_STARTED)
365 return;
367 if (tty->term->flags & TERM_VT100LIKE) {
368 if (~tty->term->flags & TTY_HAVEDA)
369 tty_puts(tty, "\033[c");
370 if (~tty->flags & TTY_HAVEDA2)
371 tty_puts(tty, "\033[>c");
372 if (~tty->flags & TTY_HAVEXDA)
373 tty_puts(tty, "\033[>q");
374 tty_puts(tty, "\033]10;?\033\\");
375 tty_puts(tty, "\033]11;?\033\\");
376 } else
377 tty->flags |= TTY_ALL_REQUEST_FLAGS;
378 tty->last_requests = time(NULL);
381 void
382 tty_repeat_requests(struct tty *tty)
384 time_t t = time(NULL);
386 if (~tty->flags & TTY_STARTED)
387 return;
389 if (t - tty->last_requests <= TTY_REQUEST_LIMIT)
390 return;
391 tty->last_requests = t;
393 if (tty->term->flags & TERM_VT100LIKE) {
394 tty_puts(tty, "\033]10;?\033\\");
395 tty_puts(tty, "\033]11;?\033\\");
399 void
400 tty_stop_tty(struct tty *tty)
402 struct client *c = tty->client;
403 struct winsize ws;
405 if (!(tty->flags & TTY_STARTED))
406 return;
407 tty->flags &= ~TTY_STARTED;
409 evtimer_del(&tty->start_timer);
411 event_del(&tty->timer);
412 tty->flags &= ~TTY_BLOCK;
414 event_del(&tty->event_in);
415 event_del(&tty->event_out);
418 * Be flexible about error handling and try not kill the server just
419 * because the fd is invalid. Things like ssh -t can easily leave us
420 * with a dead tty.
422 if (ioctl(c->fd, TIOCGWINSZ, &ws) == -1)
423 return;
424 if (tcsetattr(c->fd, TCSANOW, &tty->tio) == -1)
425 return;
427 tty_raw(tty, tty_term_string_ii(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
428 if (tty_acs_needed(tty))
429 tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
430 tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
431 tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
432 tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
433 if (tty->cstyle != SCREEN_CURSOR_DEFAULT) {
434 if (tty_term_has(tty->term, TTYC_SE))
435 tty_raw(tty, tty_term_string(tty->term, TTYC_SE));
436 else if (tty_term_has(tty->term, TTYC_SS))
437 tty_raw(tty, tty_term_string_i(tty->term, TTYC_SS, 0));
439 if (tty->ccolour != -1)
440 tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
442 tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
443 if (tty_term_has(tty->term, TTYC_KMOUS)) {
444 tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l");
445 tty_raw(tty, "\033[?1006l\033[?1005l");
447 if (tty_term_has(tty->term, TTYC_DSBP))
448 tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
450 if (tty->term->flags & TERM_VT100LIKE)
451 tty_raw(tty, "\033[?7727l");
452 tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS));
453 tty_raw(tty, tty_term_string(tty->term, TTYC_DSEKS));
455 if (tty_use_margin(tty))
456 tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG));
457 tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
459 setblocking(c->fd, 1);
462 void
463 tty_close(struct tty *tty)
465 if (event_initialized(&tty->key_timer))
466 evtimer_del(&tty->key_timer);
467 tty_stop_tty(tty);
469 if (tty->flags & TTY_OPENED) {
470 evbuffer_free(tty->in);
471 event_del(&tty->event_in);
472 evbuffer_free(tty->out);
473 event_del(&tty->event_out);
475 tty_term_free(tty->term);
476 tty_keys_free(tty);
478 tty->flags &= ~TTY_OPENED;
482 void
483 tty_free(struct tty *tty)
485 tty_close(tty);
488 void
489 tty_update_features(struct tty *tty)
491 struct client *c = tty->client;
493 if (tty_apply_features(tty->term, c->term_features))
494 tty_term_apply_overrides(tty->term);
496 if (tty_use_margin(tty))
497 tty_putcode(tty, TTYC_ENMG);
498 if (options_get_number(global_options, "extended-keys"))
499 tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS));
500 if (options_get_number(global_options, "focus-events"))
501 tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS));
502 if (tty->term->flags & TERM_VT100LIKE)
503 tty_puts(tty, "\033[?7727h");
506 * Features might have changed since the first draw during attach. For
507 * example, this happens when DA responses are received.
509 server_redraw_client(c);
511 tty_invalidate(tty);
514 void
515 tty_raw(struct tty *tty, const char *s)
517 struct client *c = tty->client;
518 ssize_t n, slen;
519 u_int i;
521 slen = strlen(s);
522 for (i = 0; i < 5; i++) {
523 n = write(c->fd, s, slen);
524 if (n >= 0) {
525 s += n;
526 slen -= n;
527 if (slen == 0)
528 break;
529 } else if (n == -1 && errno != EAGAIN)
530 break;
531 usleep(100);
535 void
536 tty_putcode(struct tty *tty, enum tty_code_code code)
538 tty_puts(tty, tty_term_string(tty->term, code));
541 void
542 tty_putcode_i(struct tty *tty, enum tty_code_code code, int a)
544 if (a < 0)
545 return;
546 tty_puts(tty, tty_term_string_i(tty->term, code, a));
549 void
550 tty_putcode_ii(struct tty *tty, enum tty_code_code code, int a, int b)
552 if (a < 0 || b < 0)
553 return;
554 tty_puts(tty, tty_term_string_ii(tty->term, code, a, b));
557 void
558 tty_putcode_iii(struct tty *tty, enum tty_code_code code, int a, int b, int c)
560 if (a < 0 || b < 0 || c < 0)
561 return;
562 tty_puts(tty, tty_term_string_iii(tty->term, code, a, b, c));
565 void
566 tty_putcode_s(struct tty *tty, enum tty_code_code code, const char *a)
568 if (a != NULL)
569 tty_puts(tty, tty_term_string_s(tty->term, code, a));
572 void
573 tty_putcode_ss(struct tty *tty, enum tty_code_code code, const char *a,
574 const char *b)
576 if (a != NULL && b != NULL)
577 tty_puts(tty, tty_term_string_ss(tty->term, code, a, b));
580 static void
581 tty_add(struct tty *tty, const char *buf, size_t len)
583 struct client *c = tty->client;
585 if (tty->flags & TTY_BLOCK) {
586 tty->discarded += len;
587 return;
590 evbuffer_add(tty->out, buf, len);
591 log_debug("%s: %.*s", c->name, (int)len, buf);
592 c->written += len;
594 if (tty_log_fd != -1)
595 write(tty_log_fd, buf, len);
596 if (tty->flags & TTY_STARTED)
597 event_add(&tty->event_out, NULL);
600 void
601 tty_puts(struct tty *tty, const char *s)
603 if (*s != '\0')
604 tty_add(tty, s, strlen(s));
607 void
608 tty_putc(struct tty *tty, u_char ch)
610 const char *acs;
612 if ((tty->term->flags & TERM_NOAM) &&
613 ch >= 0x20 && ch != 0x7f &&
614 tty->cy == tty->sy - 1 &&
615 tty->cx + 1 >= tty->sx)
616 return;
618 if (tty->cell.attr & GRID_ATTR_CHARSET) {
619 acs = tty_acs_get(tty, ch);
620 if (acs != NULL)
621 tty_add(tty, acs, strlen(acs));
622 else
623 tty_add(tty, &ch, 1);
624 } else
625 tty_add(tty, &ch, 1);
627 if (ch >= 0x20 && ch != 0x7f) {
628 if (tty->cx >= tty->sx) {
629 tty->cx = 1;
630 if (tty->cy != tty->rlower)
631 tty->cy++;
634 * On !am terminals, force the cursor position to where
635 * we think it should be after a line wrap - this means
636 * it works on sensible terminals as well.
638 if (tty->term->flags & TERM_NOAM)
639 tty_putcode_ii(tty, TTYC_CUP, tty->cy, tty->cx);
640 } else
641 tty->cx++;
645 void
646 tty_putn(struct tty *tty, const void *buf, size_t len, u_int width)
648 if ((tty->term->flags & TERM_NOAM) &&
649 tty->cy == tty->sy - 1 &&
650 tty->cx + len >= tty->sx)
651 len = tty->sx - tty->cx - 1;
653 tty_add(tty, buf, len);
654 if (tty->cx + width > tty->sx) {
655 tty->cx = (tty->cx + width) - tty->sx;
656 if (tty->cx <= tty->sx)
657 tty->cy++;
658 else
659 tty->cx = tty->cy = UINT_MAX;
660 } else
661 tty->cx += width;
664 static void
665 tty_set_italics(struct tty *tty)
667 const char *s;
669 if (tty_term_has(tty->term, TTYC_SITM)) {
670 s = options_get_string(global_options, "default-terminal");
671 if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) {
672 tty_putcode(tty, TTYC_SITM);
673 return;
676 tty_putcode(tty, TTYC_SMSO);
679 void
680 tty_set_title(struct tty *tty, const char *title)
682 if (!tty_term_has(tty->term, TTYC_TSL) ||
683 !tty_term_has(tty->term, TTYC_FSL))
684 return;
686 tty_putcode(tty, TTYC_TSL);
687 tty_puts(tty, title);
688 tty_putcode(tty, TTYC_FSL);
691 void
692 tty_set_path(struct tty *tty, const char *title)
694 if (!tty_term_has(tty->term, TTYC_SWD) ||
695 !tty_term_has(tty->term, TTYC_FSL))
696 return;
698 tty_putcode(tty, TTYC_SWD);
699 tty_puts(tty, title);
700 tty_putcode(tty, TTYC_FSL);
703 static void
704 tty_force_cursor_colour(struct tty *tty, int c)
706 u_char r, g, b;
707 char s[13];
709 if (c != -1)
710 c = colour_force_rgb(c);
711 if (c == tty->ccolour)
712 return;
713 if (c == -1)
714 tty_putcode(tty, TTYC_CR);
715 else {
716 colour_split_rgb(c, &r, &g, &b);
717 xsnprintf(s, sizeof s, "rgb:%02hhx/%02hhx/%02hhx", r, g, b);
718 tty_putcode_s(tty, TTYC_CS, s);
720 tty->ccolour = c;
723 static int
724 tty_update_cursor(struct tty *tty, int mode, struct screen *s)
726 enum screen_cursor_style cstyle;
727 int ccolour, changed, cmode = mode;
729 /* Set cursor colour if changed. */
730 if (s != NULL) {
731 ccolour = s->ccolour;
732 if (s->ccolour == -1)
733 ccolour = s->default_ccolour;
734 tty_force_cursor_colour(tty, ccolour);
737 /* If cursor is off, set as invisible. */
738 if (~cmode & MODE_CURSOR) {
739 if (tty->mode & MODE_CURSOR)
740 tty_putcode(tty, TTYC_CIVIS);
741 return (cmode);
744 /* Check if blinking or very visible flag changed or style changed. */
745 if (s == NULL)
746 cstyle = tty->cstyle;
747 else {
748 cstyle = s->cstyle;
749 if (cstyle == SCREEN_CURSOR_DEFAULT) {
750 if (~cmode & MODE_CURSOR_BLINKING_SET) {
751 if (s->default_mode & MODE_CURSOR_BLINKING)
752 cmode |= MODE_CURSOR_BLINKING;
753 else
754 cmode &= ~MODE_CURSOR_BLINKING;
756 cstyle = s->default_cstyle;
760 /* If nothing changed, do nothing. */
761 changed = cmode ^ tty->mode;
762 if ((changed & CURSOR_MODES) == 0 && cstyle == tty->cstyle)
763 return (cmode);
766 * Set cursor style. If an explicit style has been set with DECSCUSR,
767 * set it if supported, otherwise send cvvis for blinking styles.
769 * If no style, has been set (SCREEN_CURSOR_DEFAULT), then send cvvis
770 * if either the blinking or very visible flags are set.
772 tty_putcode(tty, TTYC_CNORM);
773 switch (cstyle) {
774 case SCREEN_CURSOR_DEFAULT:
775 if (tty->cstyle != SCREEN_CURSOR_DEFAULT) {
776 if (tty_term_has(tty->term, TTYC_SE))
777 tty_putcode(tty, TTYC_SE);
778 else
779 tty_putcode_i(tty, TTYC_SS, 0);
781 if (cmode & (MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE))
782 tty_putcode(tty, TTYC_CVVIS);
783 break;
784 case SCREEN_CURSOR_BLOCK:
785 if (tty_term_has(tty->term, TTYC_SS)) {
786 if (cmode & MODE_CURSOR_BLINKING)
787 tty_putcode_i(tty, TTYC_SS, 1);
788 else
789 tty_putcode_i(tty, TTYC_SS, 2);
790 } else if (cmode & MODE_CURSOR_BLINKING)
791 tty_putcode(tty, TTYC_CVVIS);
792 break;
793 case SCREEN_CURSOR_UNDERLINE:
794 if (tty_term_has(tty->term, TTYC_SS)) {
795 if (cmode & MODE_CURSOR_BLINKING)
796 tty_putcode_i(tty, TTYC_SS, 3);
797 else
798 tty_putcode_i(tty, TTYC_SS, 4);
799 } else if (cmode & MODE_CURSOR_BLINKING)
800 tty_putcode(tty, TTYC_CVVIS);
801 break;
802 case SCREEN_CURSOR_BAR:
803 if (tty_term_has(tty->term, TTYC_SS)) {
804 if (cmode & MODE_CURSOR_BLINKING)
805 tty_putcode_i(tty, TTYC_SS, 5);
806 else
807 tty_putcode_i(tty, TTYC_SS, 6);
808 } else if (cmode & MODE_CURSOR_BLINKING)
809 tty_putcode(tty, TTYC_CVVIS);
810 break;
812 tty->cstyle = cstyle;
813 return (cmode);
816 void
817 tty_update_mode(struct tty *tty, int mode, struct screen *s)
819 struct tty_term *term = tty->term;
820 struct client *c = tty->client;
821 int changed;
823 if (tty->flags & TTY_NOCURSOR)
824 mode &= ~MODE_CURSOR;
826 if (tty_update_cursor(tty, mode, s) & MODE_CURSOR_BLINKING)
827 mode |= MODE_CURSOR_BLINKING;
828 else
829 mode &= ~MODE_CURSOR_BLINKING;
831 changed = mode ^ tty->mode;
832 if (log_get_level() != 0 && changed != 0) {
833 log_debug("%s: current mode %s", c->name,
834 screen_mode_to_string(tty->mode));
835 log_debug("%s: setting mode %s", c->name,
836 screen_mode_to_string(mode));
839 if ((changed & ALL_MOUSE_MODES) && tty_term_has(term, TTYC_KMOUS)) {
841 * If the mouse modes have changed, clear then all and apply
842 * again. There are differences in how terminals track the
843 * various bits.
845 tty_puts(tty, "\033[?1006l\033[?1000l\033[?1002l\033[?1003l");
846 if (mode & ALL_MOUSE_MODES)
847 tty_puts(tty, "\033[?1006h");
848 if (mode & MODE_MOUSE_ALL)
849 tty_puts(tty, "\033[?1000h\033[?1002h\033[?1003h");
850 else if (mode & MODE_MOUSE_BUTTON)
851 tty_puts(tty, "\033[?1000h\033[?1002h");
852 else if (mode & MODE_MOUSE_STANDARD)
853 tty_puts(tty, "\033[?1000h");
855 tty->mode = mode;
858 static void
859 tty_emulate_repeat(struct tty *tty, enum tty_code_code code,
860 enum tty_code_code code1, u_int n)
862 if (tty_term_has(tty->term, code))
863 tty_putcode_i(tty, code, n);
864 else {
865 while (n-- > 0)
866 tty_putcode(tty, code1);
870 static void
871 tty_repeat_space(struct tty *tty, u_int n)
873 static char s[500];
875 if (*s != ' ')
876 memset(s, ' ', sizeof s);
878 while (n > sizeof s) {
879 tty_putn(tty, s, sizeof s, sizeof s);
880 n -= sizeof s;
882 if (n != 0)
883 tty_putn(tty, s, n, n);
886 /* Is this window bigger than the terminal? */
888 tty_window_bigger(struct tty *tty)
890 struct client *c = tty->client;
891 struct window *w = c->session->curw->window;
893 return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy);
896 /* What offset should this window be drawn at? */
898 tty_window_offset(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
900 *ox = tty->oox;
901 *oy = tty->ooy;
902 *sx = tty->osx;
903 *sy = tty->osy;
905 return (tty->oflag);
908 /* What offset should this window be drawn at? */
909 static int
910 tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
912 struct client *c = tty->client;
913 struct window *w = c->session->curw->window;
914 struct window_pane *wp = server_client_get_pane(c);
915 u_int cx, cy, lines;
917 lines = status_line_size(c);
919 if (tty->sx >= w->sx && tty->sy - lines >= w->sy) {
920 *ox = 0;
921 *oy = 0;
922 *sx = w->sx;
923 *sy = w->sy;
925 c->pan_window = NULL;
926 return (0);
929 *sx = tty->sx;
930 *sy = tty->sy - lines;
932 if (c->pan_window == w) {
933 if (*sx >= w->sx)
934 c->pan_ox = 0;
935 else if (c->pan_ox + *sx > w->sx)
936 c->pan_ox = w->sx - *sx;
937 *ox = c->pan_ox;
938 if (*sy >= w->sy)
939 c->pan_oy = 0;
940 else if (c->pan_oy + *sy > w->sy)
941 c->pan_oy = w->sy - *sy;
942 *oy = c->pan_oy;
943 return (1);
946 if (~wp->screen->mode & MODE_CURSOR) {
947 *ox = 0;
948 *oy = 0;
949 } else {
950 cx = wp->xoff + wp->screen->cx;
951 cy = wp->yoff + wp->screen->cy;
953 if (cx < *sx)
954 *ox = 0;
955 else if (cx > w->sx - *sx)
956 *ox = w->sx - *sx;
957 else
958 *ox = cx - *sx / 2;
960 if (cy < *sy)
961 *oy = 0;
962 else if (cy > w->sy - *sy)
963 *oy = w->sy - *sy;
964 else
965 *oy = cy - *sy / 2;
968 c->pan_window = NULL;
969 return (1);
972 /* Update stored offsets for a window and redraw if necessary. */
973 void
974 tty_update_window_offset(struct window *w)
976 struct client *c;
978 TAILQ_FOREACH(c, &clients, entry) {
979 if (c->session != NULL &&
980 c->session->curw != NULL &&
981 c->session->curw->window == w)
982 tty_update_client_offset(c);
986 /* Update stored offsets for a client and redraw if necessary. */
987 void
988 tty_update_client_offset(struct client *c)
990 u_int ox, oy, sx, sy;
992 if (~c->flags & CLIENT_TERMINAL)
993 return;
995 c->tty.oflag = tty_window_offset1(&c->tty, &ox, &oy, &sx, &sy);
996 if (ox == c->tty.oox &&
997 oy == c->tty.ooy &&
998 sx == c->tty.osx &&
999 sy == c->tty.osy)
1000 return;
1002 log_debug ("%s: %s offset has changed (%u,%u %ux%u -> %u,%u %ux%u)",
1003 __func__, c->name, c->tty.oox, c->tty.ooy, c->tty.osx, c->tty.osy,
1004 ox, oy, sx, sy);
1006 c->tty.oox = ox;
1007 c->tty.ooy = oy;
1008 c->tty.osx = sx;
1009 c->tty.osy = sy;
1011 c->flags |= (CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS);
1015 * Is the region large enough to be worth redrawing once later rather than
1016 * probably several times now? Currently yes if it is more than 50% of the
1017 * pane.
1019 static int
1020 tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx)
1022 return (ctx->orlower - ctx->orupper >= ctx->sy / 2);
1026 * Return if BCE is needed but the terminal doesn't have it - it'll need to be
1027 * emulated.
1029 static int
1030 tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg)
1032 if (tty_term_flag(tty->term, TTYC_BCE))
1033 return (0);
1034 if (!COLOUR_DEFAULT(bg) || !COLOUR_DEFAULT(gc->bg))
1035 return (1);
1036 return (0);
1040 * Redraw scroll region using data from screen (already updated). Used when
1041 * CSR not supported, or window is a pane that doesn't take up the full
1042 * width of the terminal.
1044 static void
1045 tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
1047 struct client *c = tty->client;
1048 u_int i;
1051 * If region is large, schedule a redraw. In most cases this is likely
1052 * to be followed by some more scrolling.
1054 if (tty_large_region(tty, ctx)) {
1055 log_debug("%s: %s large redraw", __func__, c->name);
1056 ctx->redraw_cb(ctx);
1057 return;
1060 for (i = ctx->orupper; i <= ctx->orlower; i++)
1061 tty_draw_pane(tty, ctx, i);
1064 /* Is this position visible in the pane? */
1065 static int
1066 tty_is_visible(__unused struct tty *tty, const struct tty_ctx *ctx, u_int px,
1067 u_int py, u_int nx, u_int ny)
1069 u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py;
1071 if (!ctx->bigger)
1072 return (1);
1074 if (xoff + nx <= ctx->wox || xoff >= ctx->wox + ctx->wsx ||
1075 yoff + ny <= ctx->woy || yoff >= ctx->woy + ctx->wsy)
1076 return (0);
1077 return (1);
1080 /* Clamp line position to visible part of pane. */
1081 static int
1082 tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
1083 u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry)
1085 u_int xoff = ctx->rxoff + px;
1087 if (!tty_is_visible(tty, ctx, px, py, nx, 1))
1088 return (0);
1089 *ry = ctx->yoff + py - ctx->woy;
1091 if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) {
1092 /* All visible. */
1093 *i = 0;
1094 *x = ctx->xoff + px - ctx->wox;
1095 *rx = nx;
1096 } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) {
1097 /* Both left and right not visible. */
1098 *i = ctx->wox;
1099 *x = 0;
1100 *rx = ctx->wsx;
1101 } else if (xoff < ctx->wox) {
1102 /* Left not visible. */
1103 *i = ctx->wox - (ctx->xoff + px);
1104 *x = 0;
1105 *rx = nx - *i;
1106 } else {
1107 /* Right not visible. */
1108 *i = 0;
1109 *x = (ctx->xoff + px) - ctx->wox;
1110 *rx = ctx->wsx - *x;
1112 if (*rx > nx)
1113 fatalx("%s: x too big, %u > %u", __func__, *rx, nx);
1115 return (1);
1118 /* Clear a line. */
1119 static void
1120 tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
1121 u_int px, u_int nx, u_int bg)
1123 struct client *c = tty->client;
1124 struct overlay_ranges r;
1125 u_int i;
1127 log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
1129 /* Nothing to clear. */
1130 if (nx == 0)
1131 return;
1133 /* If genuine BCE is available, can try escape sequences. */
1134 if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
1135 /* Off the end of the line, use EL if available. */
1136 if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) {
1137 tty_cursor(tty, px, py);
1138 tty_putcode(tty, TTYC_EL);
1139 return;
1142 /* At the start of the line. Use EL1. */
1143 if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) {
1144 tty_cursor(tty, px + nx - 1, py);
1145 tty_putcode(tty, TTYC_EL1);
1146 return;
1149 /* Section of line. Use ECH if possible. */
1150 if (tty_term_has(tty->term, TTYC_ECH)) {
1151 tty_cursor(tty, px, py);
1152 tty_putcode_i(tty, TTYC_ECH, nx);
1153 return;
1158 * Couldn't use an escape sequence, use spaces. Clear only the visible
1159 * bit if there is an overlay.
1161 tty_check_overlay_range(tty, px, py, nx, &r);
1162 for (i = 0; i < OVERLAY_MAX_RANGES; i++) {
1163 if (r.nx[i] == 0)
1164 continue;
1165 tty_cursor(tty, r.px[i], py);
1166 tty_repeat_space(tty, r.nx[i]);
1170 /* Clear a line, adjusting to visible part of pane. */
1171 static void
1172 tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py,
1173 u_int px, u_int nx, u_int bg)
1175 struct client *c = tty->client;
1176 u_int i, x, rx, ry;
1178 log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
1180 if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry))
1181 tty_clear_line(tty, &ctx->defaults, ry, x, rx, bg);
1184 /* Clamp area position to visible part of pane. */
1185 static int
1186 tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
1187 u_int nx, u_int ny, u_int *i, u_int *j, u_int *x, u_int *y, u_int *rx,
1188 u_int *ry)
1190 u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py;
1192 if (!tty_is_visible(tty, ctx, px, py, nx, ny))
1193 return (0);
1195 if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) {
1196 /* All visible. */
1197 *i = 0;
1198 *x = ctx->xoff + px - ctx->wox;
1199 *rx = nx;
1200 } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) {
1201 /* Both left and right not visible. */
1202 *i = ctx->wox;
1203 *x = 0;
1204 *rx = ctx->wsx;
1205 } else if (xoff < ctx->wox) {
1206 /* Left not visible. */
1207 *i = ctx->wox - (ctx->xoff + px);
1208 *x = 0;
1209 *rx = nx - *i;
1210 } else {
1211 /* Right not visible. */
1212 *i = 0;
1213 *x = (ctx->xoff + px) - ctx->wox;
1214 *rx = ctx->wsx - *x;
1216 if (*rx > nx)
1217 fatalx("%s: x too big, %u > %u", __func__, *rx, nx);
1219 if (yoff >= ctx->woy && yoff + ny <= ctx->woy + ctx->wsy) {
1220 /* All visible. */
1221 *j = 0;
1222 *y = ctx->yoff + py - ctx->woy;
1223 *ry = ny;
1224 } else if (yoff < ctx->woy && yoff + ny > ctx->woy + ctx->wsy) {
1225 /* Both top and bottom not visible. */
1226 *j = ctx->woy;
1227 *y = 0;
1228 *ry = ctx->wsy;
1229 } else if (yoff < ctx->woy) {
1230 /* Top not visible. */
1231 *j = ctx->woy - (ctx->yoff + py);
1232 *y = 0;
1233 *ry = ny - *j;
1234 } else {
1235 /* Bottom not visible. */
1236 *j = 0;
1237 *y = (ctx->yoff + py) - ctx->woy;
1238 *ry = ctx->wsy - *y;
1240 if (*ry > ny)
1241 fatalx("%s: y too big, %u > %u", __func__, *ry, ny);
1243 return (1);
1246 /* Clear an area, adjusting to visible part of pane. */
1247 static void
1248 tty_clear_area(struct tty *tty, const struct grid_cell *defaults, u_int py,
1249 u_int ny, u_int px, u_int nx, u_int bg)
1251 struct client *c = tty->client;
1252 u_int yy;
1253 char tmp[64];
1255 log_debug("%s: %s, %u,%u at %u,%u", __func__, c->name, nx, ny, px, py);
1257 /* Nothing to clear. */
1258 if (nx == 0 || ny == 0)
1259 return;
1261 /* If genuine BCE is available, can try escape sequences. */
1262 if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
1263 /* Use ED if clearing off the bottom of the terminal. */
1264 if (px == 0 &&
1265 px + nx >= tty->sx &&
1266 py + ny >= tty->sy &&
1267 tty_term_has(tty->term, TTYC_ED)) {
1268 tty_cursor(tty, 0, py);
1269 tty_putcode(tty, TTYC_ED);
1270 return;
1274 * On VT420 compatible terminals we can use DECFRA if the
1275 * background colour isn't default (because it doesn't work
1276 * after SGR 0).
1278 if ((tty->term->flags & TERM_DECFRA) && !COLOUR_DEFAULT(bg)) {
1279 xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x",
1280 py + 1, px + 1, py + ny, px + nx);
1281 tty_puts(tty, tmp);
1282 return;
1285 /* Full lines can be scrolled away to clear them. */
1286 if (px == 0 &&
1287 px + nx >= tty->sx &&
1288 ny > 2 &&
1289 tty_term_has(tty->term, TTYC_CSR) &&
1290 tty_term_has(tty->term, TTYC_INDN)) {
1291 tty_region(tty, py, py + ny - 1);
1292 tty_margin_off(tty);
1293 tty_putcode_i(tty, TTYC_INDN, ny);
1294 return;
1298 * If margins are supported, can just scroll the area off to
1299 * clear it.
1301 if (nx > 2 &&
1302 ny > 2 &&
1303 tty_term_has(tty->term, TTYC_CSR) &&
1304 tty_use_margin(tty) &&
1305 tty_term_has(tty->term, TTYC_INDN)) {
1306 tty_region(tty, py, py + ny - 1);
1307 tty_margin(tty, px, px + nx - 1);
1308 tty_putcode_i(tty, TTYC_INDN, ny);
1309 return;
1313 /* Couldn't use an escape sequence, loop over the lines. */
1314 for (yy = py; yy < py + ny; yy++)
1315 tty_clear_line(tty, defaults, yy, px, nx, bg);
1318 /* Clear an area in a pane. */
1319 static void
1320 tty_clear_pane_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
1321 u_int ny, u_int px, u_int nx, u_int bg)
1323 u_int i, j, x, y, rx, ry;
1325 if (tty_clamp_area(tty, ctx, px, py, nx, ny, &i, &j, &x, &y, &rx, &ry))
1326 tty_clear_area(tty, &ctx->defaults, y, ry, x, rx, bg);
1329 static void
1330 tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
1332 struct screen *s = ctx->s;
1333 u_int nx = ctx->sx, i, x, rx, ry;
1335 log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger);
1337 if (!ctx->bigger) {
1338 tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py,
1339 &ctx->defaults, ctx->palette);
1340 return;
1342 if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) {
1343 tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->defaults,
1344 ctx->palette);
1348 static const struct grid_cell *
1349 tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
1351 static struct grid_cell new;
1352 int c;
1354 /* Characters less than 0x7f are always fine, no matter what. */
1355 if (gc->data.size == 1 && *gc->data.data < 0x7f)
1356 return (gc);
1358 /* UTF-8 terminal and a UTF-8 character - fine. */
1359 if (tty->client->flags & CLIENT_UTF8)
1360 return (gc);
1361 memcpy(&new, gc, sizeof new);
1363 /* See if this can be mapped to an ACS character. */
1364 c = tty_acs_reverse_get(tty, gc->data.data, gc->data.size);
1365 if (c != -1) {
1366 utf8_set(&new.data, c);
1367 new.attr |= GRID_ATTR_CHARSET;
1368 return (&new);
1371 /* Replace by the right number of underscores. */
1372 new.data.size = gc->data.width;
1373 if (new.data.size > UTF8_SIZE)
1374 new.data.size = UTF8_SIZE;
1375 memset(new.data.data, '_', new.data.size);
1376 return (&new);
1380 * Check if a single character is obstructed by the overlay and return a
1381 * boolean.
1383 static int
1384 tty_check_overlay(struct tty *tty, u_int px, u_int py)
1386 struct overlay_ranges r;
1389 * A unit width range will always return nx[2] == 0 from a check, even
1390 * with multiple overlays, so it's sufficient to check just the first
1391 * two entries.
1393 tty_check_overlay_range(tty, px, py, 1, &r);
1394 if (r.nx[0] + r.nx[1] == 0)
1395 return (0);
1396 return (1);
1399 /* Return parts of the input range which are visible. */
1400 static void
1401 tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx,
1402 struct overlay_ranges *r)
1404 struct client *c = tty->client;
1406 if (c->overlay_check == NULL) {
1407 r->px[0] = px;
1408 r->nx[0] = nx;
1409 r->px[1] = 0;
1410 r->nx[1] = 0;
1411 r->px[2] = 0;
1412 r->nx[2] = 0;
1413 return;
1416 c->overlay_check(c, c->overlay_data, px, py, nx, r);
1419 void
1420 tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
1421 u_int atx, u_int aty, const struct grid_cell *defaults,
1422 struct colour_palette *palette)
1424 struct grid *gd = s->grid;
1425 struct grid_cell gc, last;
1426 const struct grid_cell *gcp;
1427 struct grid_line *gl;
1428 struct client *c = tty->client;
1429 struct overlay_ranges r;
1430 u_int i, j, ux, sx, width, hidden, eux, nxx;
1431 u_int cellsize;
1432 int flags, cleared = 0, wrapped = 0;
1433 char buf[512];
1434 size_t len;
1436 log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__,
1437 px, py, nx, atx, aty);
1438 log_debug("%s: defaults: fg=%d, bg=%d", __func__, defaults->fg,
1439 defaults->bg);
1442 * py is the line in the screen to draw.
1443 * px is the start x and nx is the width to draw.
1444 * atx,aty is the line on the terminal to draw it.
1447 flags = (tty->flags & TTY_NOCURSOR);
1448 tty->flags |= TTY_NOCURSOR;
1449 tty_update_mode(tty, tty->mode, s);
1451 tty_region_off(tty);
1452 tty_margin_off(tty);
1455 * Clamp the width to cellsize - note this is not cellused, because
1456 * there may be empty background cells after it (from BCE).
1458 sx = screen_size_x(s);
1459 if (nx > sx)
1460 nx = sx;
1461 cellsize = grid_get_line(gd, gd->hsize + py)->cellsize;
1462 if (sx > cellsize)
1463 sx = cellsize;
1464 if (sx > tty->sx)
1465 sx = tty->sx;
1466 if (sx > nx)
1467 sx = nx;
1468 ux = 0;
1470 if (py == 0)
1471 gl = NULL;
1472 else
1473 gl = grid_get_line(gd, gd->hsize + py - 1);
1474 if (gl == NULL ||
1475 (~gl->flags & GRID_LINE_WRAPPED) ||
1476 atx != 0 ||
1477 tty->cx < tty->sx ||
1478 nx < tty->sx) {
1479 if (nx < tty->sx &&
1480 atx == 0 &&
1481 px + sx != nx &&
1482 tty_term_has(tty->term, TTYC_EL1) &&
1483 !tty_fake_bce(tty, defaults, 8) &&
1484 c->overlay_check == NULL) {
1485 tty_default_attributes(tty, defaults, palette, 8,
1486 s->hyperlinks);
1487 tty_cursor(tty, nx - 1, aty);
1488 tty_putcode(tty, TTYC_EL1);
1489 cleared = 1;
1491 } else {
1492 log_debug("%s: wrapped line %u", __func__, aty);
1493 wrapped = 1;
1496 memcpy(&last, &grid_default_cell, sizeof last);
1497 len = 0;
1498 width = 0;
1500 for (i = 0; i < sx; i++) {
1501 grid_view_get_cell(gd, px + i, py, &gc);
1502 gcp = tty_check_codeset(tty, &gc);
1503 if (len != 0 &&
1504 (!tty_check_overlay(tty, atx + ux + width, aty) ||
1505 (gcp->attr & GRID_ATTR_CHARSET) ||
1506 gcp->flags != last.flags ||
1507 gcp->attr != last.attr ||
1508 gcp->fg != last.fg ||
1509 gcp->bg != last.bg ||
1510 gcp->us != last.us ||
1511 gcp->link != last.link ||
1512 ux + width + gcp->data.width > nx ||
1513 (sizeof buf) - len < gcp->data.size)) {
1514 tty_attributes(tty, &last, defaults, palette,
1515 s->hyperlinks);
1516 if (last.flags & GRID_FLAG_CLEARED) {
1517 log_debug("%s: %zu cleared", __func__, len);
1518 tty_clear_line(tty, defaults, aty, atx + ux,
1519 width, last.bg);
1520 } else {
1521 if (!wrapped || atx != 0 || ux != 0)
1522 tty_cursor(tty, atx + ux, aty);
1523 tty_putn(tty, buf, len, width);
1525 ux += width;
1527 len = 0;
1528 width = 0;
1529 wrapped = 0;
1532 if (gcp->flags & GRID_FLAG_SELECTED)
1533 screen_select_cell(s, &last, gcp);
1534 else
1535 memcpy(&last, gcp, sizeof last);
1537 tty_check_overlay_range(tty, atx + ux, aty, gcp->data.width,
1538 &r);
1539 hidden = 0;
1540 for (j = 0; j < OVERLAY_MAX_RANGES; j++)
1541 hidden += r.nx[j];
1542 hidden = gcp->data.width - hidden;
1543 if (hidden != 0 && hidden == gcp->data.width) {
1544 if (~gcp->flags & GRID_FLAG_PADDING)
1545 ux += gcp->data.width;
1546 } else if (hidden != 0 || ux + gcp->data.width > nx) {
1547 if (~gcp->flags & GRID_FLAG_PADDING) {
1548 tty_attributes(tty, &last, defaults, palette,
1549 s->hyperlinks);
1550 for (j = 0; j < OVERLAY_MAX_RANGES; j++) {
1551 if (r.nx[j] == 0)
1552 continue;
1553 /* Effective width drawn so far. */
1554 eux = r.px[j] - atx;
1555 if (eux < nx) {
1556 tty_cursor(tty, r.px[j], aty);
1557 nxx = nx - eux;
1558 if (r.nx[j] > nxx)
1559 r.nx[j] = nxx;
1560 tty_repeat_space(tty, r.nx[j]);
1561 ux = eux + r.nx[j];
1565 } else if (gcp->attr & GRID_ATTR_CHARSET) {
1566 tty_attributes(tty, &last, defaults, palette,
1567 s->hyperlinks);
1568 tty_cursor(tty, atx + ux, aty);
1569 for (j = 0; j < gcp->data.size; j++)
1570 tty_putc(tty, gcp->data.data[j]);
1571 ux += gcp->data.width;
1572 } else if (~gcp->flags & GRID_FLAG_PADDING) {
1573 memcpy(buf + len, gcp->data.data, gcp->data.size);
1574 len += gcp->data.size;
1575 width += gcp->data.width;
1578 if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) {
1579 tty_attributes(tty, &last, defaults, palette, s->hyperlinks);
1580 if (last.flags & GRID_FLAG_CLEARED) {
1581 log_debug("%s: %zu cleared (end)", __func__, len);
1582 tty_clear_line(tty, defaults, aty, atx + ux, width,
1583 last.bg);
1584 } else {
1585 if (!wrapped || atx != 0 || ux != 0)
1586 tty_cursor(tty, atx + ux, aty);
1587 tty_putn(tty, buf, len, width);
1589 ux += width;
1592 if (!cleared && ux < nx) {
1593 log_debug("%s: %u to end of line (%zu cleared)", __func__,
1594 nx - ux, len);
1595 tty_default_attributes(tty, defaults, palette, 8,
1596 s->hyperlinks);
1597 tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8);
1600 tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags;
1601 tty_update_mode(tty, tty->mode, s);
1604 void
1605 tty_sync_start(struct tty *tty)
1607 if (tty->flags & TTY_BLOCK)
1608 return;
1609 if (tty->flags & TTY_SYNCING)
1610 return;
1611 tty->flags |= TTY_SYNCING;
1613 if (tty_term_has(tty->term, TTYC_SYNC)) {
1614 log_debug("%s sync start", tty->client->name);
1615 tty_putcode_i(tty, TTYC_SYNC, 1);
1619 void
1620 tty_sync_end(struct tty *tty)
1622 if (tty->flags & TTY_BLOCK)
1623 return;
1624 if (~tty->flags & TTY_SYNCING)
1625 return;
1626 tty->flags &= ~TTY_SYNCING;
1628 if (tty_term_has(tty->term, TTYC_SYNC)) {
1629 log_debug("%s sync end", tty->client->name);
1630 tty_putcode_i(tty, TTYC_SYNC, 2);
1634 static int
1635 tty_client_ready(const struct tty_ctx *ctx, struct client *c)
1637 if (c->session == NULL || c->tty.term == NULL)
1638 return (0);
1639 if (c->flags & CLIENT_SUSPENDED)
1640 return (0);
1643 * If invisible panes are allowed (used for passthrough), don't care if
1644 * redrawing or frozen.
1646 if (ctx->allow_invisible_panes)
1647 return (1);
1649 if (c->flags & CLIENT_REDRAWWINDOW)
1650 return (0);
1651 if (c->tty.flags & TTY_FREEZE)
1652 return (0);
1653 return (1);
1656 void
1657 tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
1658 struct tty_ctx *ctx)
1660 struct client *c;
1661 int state;
1663 if (ctx->set_client_cb == NULL)
1664 return;
1665 TAILQ_FOREACH(c, &clients, entry) {
1666 if (tty_client_ready(ctx, c)) {
1667 state = ctx->set_client_cb(ctx, c);
1668 if (state == -1)
1669 break;
1670 if (state == 0)
1671 continue;
1672 cmdfn(&c->tty, ctx);
1677 void
1678 tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
1680 struct client *c = tty->client;
1682 if (ctx->bigger ||
1683 !tty_full_width(tty, ctx) ||
1684 tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1685 (!tty_term_has(tty->term, TTYC_ICH) &&
1686 !tty_term_has(tty->term, TTYC_ICH1)) ||
1687 c->overlay_check != NULL) {
1688 tty_draw_pane(tty, ctx, ctx->ocy);
1689 return;
1692 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1693 ctx->s->hyperlinks);
1695 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1697 tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num);
1700 void
1701 tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
1703 struct client *c = tty->client;
1705 if (ctx->bigger ||
1706 !tty_full_width(tty, ctx) ||
1707 tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1708 (!tty_term_has(tty->term, TTYC_DCH) &&
1709 !tty_term_has(tty->term, TTYC_DCH1)) ||
1710 c->overlay_check != NULL) {
1711 tty_draw_pane(tty, ctx, ctx->ocy);
1712 return;
1715 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1716 ctx->s->hyperlinks);
1718 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1720 tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num);
1723 void
1724 tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
1726 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1727 ctx->s->hyperlinks);
1729 tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg);
1732 void
1733 tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
1735 struct client *c = tty->client;
1737 if (ctx->bigger ||
1738 !tty_full_width(tty, ctx) ||
1739 tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1740 !tty_term_has(tty->term, TTYC_CSR) ||
1741 !tty_term_has(tty->term, TTYC_IL1) ||
1742 ctx->sx == 1 ||
1743 ctx->sy == 1 ||
1744 c->overlay_check != NULL) {
1745 tty_redraw_region(tty, ctx);
1746 return;
1749 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1750 ctx->s->hyperlinks);
1752 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1753 tty_margin_off(tty);
1754 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1756 tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
1757 tty->cx = tty->cy = UINT_MAX;
1760 void
1761 tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
1763 struct client *c = tty->client;
1765 if (ctx->bigger ||
1766 !tty_full_width(tty, ctx) ||
1767 tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1768 !tty_term_has(tty->term, TTYC_CSR) ||
1769 !tty_term_has(tty->term, TTYC_DL1) ||
1770 ctx->sx == 1 ||
1771 ctx->sy == 1 ||
1772 c->overlay_check != NULL) {
1773 tty_redraw_region(tty, ctx);
1774 return;
1777 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1778 ctx->s->hyperlinks);
1780 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1781 tty_margin_off(tty);
1782 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1784 tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
1785 tty->cx = tty->cy = UINT_MAX;
1788 void
1789 tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
1791 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1792 ctx->s->hyperlinks);
1794 tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg);
1797 void
1798 tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
1800 u_int nx = ctx->sx - ctx->ocx;
1802 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1803 ctx->s->hyperlinks);
1805 tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg);
1808 void
1809 tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
1811 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1812 ctx->s->hyperlinks);
1814 tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg);
1817 void
1818 tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
1820 struct client *c = tty->client;
1822 if (ctx->ocy != ctx->orupper)
1823 return;
1825 if (ctx->bigger ||
1826 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1827 tty_fake_bce(tty, &ctx->defaults, 8) ||
1828 !tty_term_has(tty->term, TTYC_CSR) ||
1829 (!tty_term_has(tty->term, TTYC_RI) &&
1830 !tty_term_has(tty->term, TTYC_RIN)) ||
1831 ctx->sx == 1 ||
1832 ctx->sy == 1 ||
1833 c->overlay_check != NULL) {
1834 tty_redraw_region(tty, ctx);
1835 return;
1838 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1839 ctx->s->hyperlinks);
1841 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1842 tty_margin_pane(tty, ctx);
1843 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
1845 if (tty_term_has(tty->term, TTYC_RI))
1846 tty_putcode(tty, TTYC_RI);
1847 else
1848 tty_putcode_i(tty, TTYC_RIN, 1);
1851 void
1852 tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
1854 struct client *c = tty->client;
1856 if (ctx->ocy != ctx->orlower)
1857 return;
1859 if (ctx->bigger ||
1860 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1861 tty_fake_bce(tty, &ctx->defaults, 8) ||
1862 !tty_term_has(tty->term, TTYC_CSR) ||
1863 ctx->sx == 1 ||
1864 ctx->sy == 1 ||
1865 c->overlay_check != NULL) {
1866 tty_redraw_region(tty, ctx);
1867 return;
1870 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1871 ctx->s->hyperlinks);
1873 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1874 tty_margin_pane(tty, ctx);
1877 * If we want to wrap a pane while using margins, the cursor needs to
1878 * be exactly on the right of the region. If the cursor is entirely off
1879 * the edge - move it back to the right. Some terminals are funny about
1880 * this and insert extra spaces, so only use the right if margins are
1881 * enabled.
1883 if (ctx->xoff + ctx->ocx > tty->rright) {
1884 if (!tty_use_margin(tty))
1885 tty_cursor(tty, 0, ctx->yoff + ctx->ocy);
1886 else
1887 tty_cursor(tty, tty->rright, ctx->yoff + ctx->ocy);
1888 } else
1889 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1891 tty_putc(tty, '\n');
1894 void
1895 tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
1897 struct client *c = tty->client;
1898 u_int i;
1900 if (ctx->bigger ||
1901 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1902 tty_fake_bce(tty, &ctx->defaults, 8) ||
1903 !tty_term_has(tty->term, TTYC_CSR) ||
1904 ctx->sx == 1 ||
1905 ctx->sy == 1 ||
1906 c->overlay_check != NULL) {
1907 tty_redraw_region(tty, ctx);
1908 return;
1911 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1912 ctx->s->hyperlinks);
1914 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1915 tty_margin_pane(tty, ctx);
1917 if (ctx->num == 1 || !tty_term_has(tty->term, TTYC_INDN)) {
1918 if (!tty_use_margin(tty))
1919 tty_cursor(tty, 0, tty->rlower);
1920 else
1921 tty_cursor(tty, tty->rright, tty->rlower);
1922 for (i = 0; i < ctx->num; i++)
1923 tty_putc(tty, '\n');
1924 } else {
1925 if (tty->cy == UINT_MAX)
1926 tty_cursor(tty, 0, 0);
1927 else
1928 tty_cursor(tty, 0, tty->cy);
1929 tty_putcode_i(tty, TTYC_INDN, ctx->num);
1933 void
1934 tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
1936 u_int i;
1937 struct client *c = tty->client;
1939 if (ctx->bigger ||
1940 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1941 tty_fake_bce(tty, &ctx->defaults, 8) ||
1942 !tty_term_has(tty->term, TTYC_CSR) ||
1943 (!tty_term_has(tty->term, TTYC_RI) &&
1944 !tty_term_has(tty->term, TTYC_RIN)) ||
1945 ctx->sx == 1 ||
1946 ctx->sy == 1 ||
1947 c->overlay_check != NULL) {
1948 tty_redraw_region(tty, ctx);
1949 return;
1952 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1953 ctx->s->hyperlinks);
1955 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1956 tty_margin_pane(tty, ctx);
1957 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
1959 if (tty_term_has(tty->term, TTYC_RIN))
1960 tty_putcode_i(tty, TTYC_RIN, ctx->num);
1961 else {
1962 for (i = 0; i < ctx->num; i++)
1963 tty_putcode(tty, TTYC_RI);
1967 void
1968 tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
1970 u_int px, py, nx, ny;
1972 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1973 ctx->s->hyperlinks);
1975 tty_region_pane(tty, ctx, 0, ctx->sy - 1);
1976 tty_margin_off(tty);
1978 px = 0;
1979 nx = ctx->sx;
1980 py = ctx->ocy + 1;
1981 ny = ctx->sy - ctx->ocy - 1;
1983 tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg);
1985 px = ctx->ocx;
1986 nx = ctx->sx - ctx->ocx;
1987 py = ctx->ocy;
1989 tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg);
1992 void
1993 tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
1995 u_int px, py, nx, ny;
1997 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
1998 ctx->s->hyperlinks);
2000 tty_region_pane(tty, ctx, 0, ctx->sy - 1);
2001 tty_margin_off(tty);
2003 px = 0;
2004 nx = ctx->sx;
2005 py = 0;
2006 ny = ctx->ocy;
2008 tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg);
2010 px = 0;
2011 nx = ctx->ocx + 1;
2012 py = ctx->ocy;
2014 tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg);
2017 void
2018 tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
2020 u_int px, py, nx, ny;
2022 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg,
2023 ctx->s->hyperlinks);
2025 tty_region_pane(tty, ctx, 0, ctx->sy - 1);
2026 tty_margin_off(tty);
2028 px = 0;
2029 nx = ctx->sx;
2030 py = 0;
2031 ny = ctx->sy;
2033 tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg);
2036 void
2037 tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
2039 u_int i, j;
2041 if (ctx->bigger) {
2042 ctx->redraw_cb(ctx);
2043 return;
2046 tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette,
2047 ctx->s->hyperlinks);
2049 tty_region_pane(tty, ctx, 0, ctx->sy - 1);
2050 tty_margin_off(tty);
2052 for (j = 0; j < ctx->sy; j++) {
2053 tty_cursor_pane(tty, ctx, 0, j);
2054 for (i = 0; i < ctx->sx; i++)
2055 tty_putc(tty, 'E');
2059 void
2060 tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
2062 const struct grid_cell *gcp = ctx->cell;
2063 struct screen *s = ctx->s;
2064 struct overlay_ranges r;
2065 u_int px, py, i, vis = 0;
2067 px = ctx->xoff + ctx->ocx - ctx->wox;
2068 py = ctx->yoff + ctx->ocy - ctx->woy;
2069 if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1) ||
2070 (gcp->data.width == 1 && !tty_check_overlay(tty, px, py)))
2071 return;
2073 /* Handle partially obstructed wide characters. */
2074 if (gcp->data.width > 1) {
2075 tty_check_overlay_range(tty, px, py, gcp->data.width, &r);
2076 for (i = 0; i < OVERLAY_MAX_RANGES; i++)
2077 vis += r.nx[i];
2078 if (vis < gcp->data.width) {
2079 tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width,
2080 px, py, &ctx->defaults, ctx->palette);
2081 return;
2085 if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 &&
2086 ctx->ocy == ctx->orlower &&
2087 tty_full_width(tty, ctx))
2088 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
2090 tty_margin_off(tty);
2091 tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
2093 tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette,
2094 ctx->s->hyperlinks);
2096 if (ctx->num == 1)
2097 tty_invalidate(tty);
2100 void
2101 tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
2103 struct overlay_ranges r;
2104 u_int i, px, py, cx;
2105 char *cp = ctx->ptr;
2107 if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, ctx->num, 1))
2108 return;
2110 if (ctx->bigger &&
2111 (ctx->xoff + ctx->ocx < ctx->wox ||
2112 ctx->xoff + ctx->ocx + ctx->num > ctx->wox + ctx->wsx)) {
2113 if (!ctx->wrapped ||
2114 !tty_full_width(tty, ctx) ||
2115 (tty->term->flags & TERM_NOAM) ||
2116 ctx->xoff + ctx->ocx != 0 ||
2117 ctx->yoff + ctx->ocy != tty->cy + 1 ||
2118 tty->cx < tty->sx ||
2119 tty->cy == tty->rlower)
2120 tty_draw_pane(tty, ctx, ctx->ocy);
2121 else
2122 ctx->redraw_cb(ctx);
2123 return;
2126 tty_margin_off(tty);
2127 tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
2128 tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, ctx->s->hyperlinks);
2130 /* Get tty position from pane position for overlay check. */
2131 px = ctx->xoff + ctx->ocx - ctx->wox;
2132 py = ctx->yoff + ctx->ocy - ctx->woy;
2134 tty_check_overlay_range(tty, px, py, ctx->num, &r);
2135 for (i = 0; i < OVERLAY_MAX_RANGES; i++) {
2136 if (r.nx[i] == 0)
2137 continue;
2138 /* Convert back to pane position for printing. */
2139 cx = r.px[i] - ctx->xoff + ctx->wox;
2140 tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
2141 tty_putn(tty, cp + r.px[i] - px, r.nx[i], r.nx[i]);
2145 void
2146 tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
2148 tty_set_selection(tty, ctx->ptr2, ctx->ptr, ctx->num);
2151 void
2152 tty_set_selection(struct tty *tty, const char *flags, const char *buf,
2153 size_t len)
2155 char *encoded;
2156 size_t size;
2158 if (~tty->flags & TTY_STARTED)
2159 return;
2160 if (!tty_term_has(tty->term, TTYC_MS))
2161 return;
2163 size = 4 * ((len + 2) / 3) + 1; /* storage for base64 */
2164 encoded = xmalloc(size);
2166 b64_ntop(buf, len, encoded, size);
2167 tty->flags |= TTY_NOBLOCK;
2168 tty_putcode_ss(tty, TTYC_MS, flags, encoded);
2170 free(encoded);
2173 void
2174 tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
2176 tty->flags |= TTY_NOBLOCK;
2177 tty_add(tty, ctx->ptr, ctx->num);
2178 tty_invalidate(tty);
2181 void
2182 tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
2184 if (ctx->num == 0x11) {
2186 * This is an overlay and a command that moves the cursor so
2187 * start synchronized updates.
2189 tty_sync_start(tty);
2190 } else if (~ctx->num & 0x10) {
2192 * This is a pane. If there is an overlay, always start;
2193 * otherwise, only if requested.
2195 if (ctx->num || tty->client->overlay_draw != NULL)
2196 tty_sync_start(tty);
2200 void
2201 tty_cell(struct tty *tty, const struct grid_cell *gc,
2202 const struct grid_cell *defaults, struct colour_palette *palette,
2203 struct hyperlinks *hl)
2205 const struct grid_cell *gcp;
2207 /* Skip last character if terminal is stupid. */
2208 if ((tty->term->flags & TERM_NOAM) &&
2209 tty->cy == tty->sy - 1 &&
2210 tty->cx == tty->sx - 1)
2211 return;
2213 /* If this is a padding character, do nothing. */
2214 if (gc->flags & GRID_FLAG_PADDING)
2215 return;
2217 /* Check the output codeset and apply attributes. */
2218 gcp = tty_check_codeset(tty, gc);
2219 tty_attributes(tty, gcp, defaults, palette, hl);
2221 /* If it is a single character, write with putc to handle ACS. */
2222 if (gcp->data.size == 1) {
2223 tty_attributes(tty, gcp, defaults, palette, hl);
2224 if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f)
2225 return;
2226 tty_putc(tty, *gcp->data.data);
2227 return;
2230 /* Write the data. */
2231 tty_putn(tty, gcp->data.data, gcp->data.size, gcp->data.width);
2234 void
2235 tty_reset(struct tty *tty)
2237 struct grid_cell *gc = &tty->cell;
2239 if (!grid_cells_equal(gc, &grid_default_cell)) {
2240 if (gc->link != 0)
2241 tty_putcode_ss(tty, TTYC_HLS, "", "");
2242 if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
2243 tty_putcode(tty, TTYC_RMACS);
2244 tty_putcode(tty, TTYC_SGR0);
2245 memcpy(gc, &grid_default_cell, sizeof *gc);
2247 memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell);
2250 static void
2251 tty_invalidate(struct tty *tty)
2253 memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
2254 memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell);
2256 tty->cx = tty->cy = UINT_MAX;
2257 tty->rupper = tty->rleft = UINT_MAX;
2258 tty->rlower = tty->rright = UINT_MAX;
2260 if (tty->flags & TTY_STARTED) {
2261 if (tty_use_margin(tty))
2262 tty_putcode(tty, TTYC_ENMG);
2263 tty_putcode(tty, TTYC_SGR0);
2265 tty->mode = ALL_MODES;
2266 tty_update_mode(tty, MODE_CURSOR, NULL);
2268 tty_cursor(tty, 0, 0);
2269 tty_region_off(tty);
2270 tty_margin_off(tty);
2271 } else
2272 tty->mode = MODE_CURSOR;
2275 /* Turn off margin. */
2276 void
2277 tty_region_off(struct tty *tty)
2279 tty_region(tty, 0, tty->sy - 1);
2282 /* Set region inside pane. */
2283 static void
2284 tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper,
2285 u_int rlower)
2287 tty_region(tty, ctx->yoff + rupper - ctx->woy,
2288 ctx->yoff + rlower - ctx->woy);
2291 /* Set region at absolute position. */
2292 static void
2293 tty_region(struct tty *tty, u_int rupper, u_int rlower)
2295 if (tty->rlower == rlower && tty->rupper == rupper)
2296 return;
2297 if (!tty_term_has(tty->term, TTYC_CSR))
2298 return;
2300 tty->rupper = rupper;
2301 tty->rlower = rlower;
2304 * Some terminals (such as PuTTY) do not correctly reset the cursor to
2305 * 0,0 if it is beyond the last column (they do not reset their wrap
2306 * flag so further output causes a line feed). As a workaround, do an
2307 * explicit move to 0 first.
2309 if (tty->cx >= tty->sx) {
2310 if (tty->cy == UINT_MAX)
2311 tty_cursor(tty, 0, 0);
2312 else
2313 tty_cursor(tty, 0, tty->cy);
2316 tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower);
2317 tty->cx = tty->cy = UINT_MAX;
2320 /* Turn off margin. */
2321 void
2322 tty_margin_off(struct tty *tty)
2324 tty_margin(tty, 0, tty->sx - 1);
2327 /* Set margin inside pane. */
2328 static void
2329 tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx)
2331 tty_margin(tty, ctx->xoff - ctx->wox,
2332 ctx->xoff + ctx->sx - 1 - ctx->wox);
2335 /* Set margin at absolute position. */
2336 static void
2337 tty_margin(struct tty *tty, u_int rleft, u_int rright)
2339 if (!tty_use_margin(tty))
2340 return;
2341 if (tty->rleft == rleft && tty->rright == rright)
2342 return;
2344 tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower);
2346 tty->rleft = rleft;
2347 tty->rright = rright;
2349 if (rleft == 0 && rright == tty->sx - 1)
2350 tty_putcode(tty, TTYC_CLMG);
2351 else
2352 tty_putcode_ii(tty, TTYC_CMG, rleft, rright);
2353 tty->cx = tty->cy = UINT_MAX;
2357 * Move the cursor, unless it would wrap itself when the next character is
2358 * printed.
2360 static void
2361 tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx,
2362 u_int cx, u_int cy)
2364 if (!ctx->wrapped ||
2365 !tty_full_width(tty, ctx) ||
2366 (tty->term->flags & TERM_NOAM) ||
2367 ctx->xoff + cx != 0 ||
2368 ctx->yoff + cy != tty->cy + 1 ||
2369 tty->cx < tty->sx ||
2370 tty->cy == tty->rlower)
2371 tty_cursor_pane(tty, ctx, cx, cy);
2372 else
2373 log_debug("%s: will wrap at %u,%u", __func__, tty->cx, tty->cy);
2376 /* Move cursor inside pane. */
2377 static void
2378 tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy)
2380 tty_cursor(tty, ctx->xoff + cx - ctx->wox, ctx->yoff + cy - ctx->woy);
2383 /* Move cursor to absolute position. */
2384 void
2385 tty_cursor(struct tty *tty, u_int cx, u_int cy)
2387 struct tty_term *term = tty->term;
2388 u_int thisx, thisy;
2389 int change;
2391 if (tty->flags & TTY_BLOCK)
2392 return;
2394 thisx = tty->cx;
2395 thisy = tty->cy;
2398 * If in the automargin space, and want to be there, do not move.
2399 * Otherwise, force the cursor to be in range (and complain).
2401 if (cx == thisx && cy == thisy && cx == tty->sx)
2402 return;
2403 if (cx > tty->sx - 1) {
2404 log_debug("%s: x too big %u > %u", __func__, cx, tty->sx - 1);
2405 cx = tty->sx - 1;
2408 /* No change. */
2409 if (cx == thisx && cy == thisy)
2410 return;
2412 /* Currently at the very end of the line - use absolute movement. */
2413 if (thisx > tty->sx - 1)
2414 goto absolute;
2416 /* Move to home position (0, 0). */
2417 if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) {
2418 tty_putcode(tty, TTYC_HOME);
2419 goto out;
2422 /* Zero on the next line. */
2423 if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower &&
2424 (!tty_use_margin(tty) || tty->rleft == 0)) {
2425 tty_putc(tty, '\r');
2426 tty_putc(tty, '\n');
2427 goto out;
2430 /* Moving column or row. */
2431 if (cy == thisy) {
2433 * Moving column only, row staying the same.
2436 /* To left edge. */
2437 if (cx == 0 && (!tty_use_margin(tty) || tty->rleft == 0)) {
2438 tty_putc(tty, '\r');
2439 goto out;
2442 /* One to the left. */
2443 if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) {
2444 tty_putcode(tty, TTYC_CUB1);
2445 goto out;
2448 /* One to the right. */
2449 if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) {
2450 tty_putcode(tty, TTYC_CUF1);
2451 goto out;
2454 /* Calculate difference. */
2455 change = thisx - cx; /* +ve left, -ve right */
2458 * Use HPA if change is larger than absolute, otherwise move
2459 * the cursor with CUB/CUF.
2461 if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
2462 tty_putcode_i(tty, TTYC_HPA, cx);
2463 goto out;
2464 } else if (change > 0 &&
2465 tty_term_has(term, TTYC_CUB) &&
2466 !tty_use_margin(tty)) {
2467 if (change == 2 && tty_term_has(term, TTYC_CUB1)) {
2468 tty_putcode(tty, TTYC_CUB1);
2469 tty_putcode(tty, TTYC_CUB1);
2470 goto out;
2472 tty_putcode_i(tty, TTYC_CUB, change);
2473 goto out;
2474 } else if (change < 0 &&
2475 tty_term_has(term, TTYC_CUF) &&
2476 !tty_use_margin(tty)) {
2477 tty_putcode_i(tty, TTYC_CUF, -change);
2478 goto out;
2480 } else if (cx == thisx) {
2482 * Moving row only, column staying the same.
2485 /* One above. */
2486 if (thisy != tty->rupper &&
2487 cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) {
2488 tty_putcode(tty, TTYC_CUU1);
2489 goto out;
2492 /* One below. */
2493 if (thisy != tty->rlower &&
2494 cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) {
2495 tty_putcode(tty, TTYC_CUD1);
2496 goto out;
2499 /* Calculate difference. */
2500 change = thisy - cy; /* +ve up, -ve down */
2503 * Try to use VPA if change is larger than absolute or if this
2504 * change would cross the scroll region, otherwise use CUU/CUD.
2506 if ((u_int) abs(change) > cy ||
2507 (change < 0 && cy - change > tty->rlower) ||
2508 (change > 0 && cy - change < tty->rupper)) {
2509 if (tty_term_has(term, TTYC_VPA)) {
2510 tty_putcode_i(tty, TTYC_VPA, cy);
2511 goto out;
2513 } else if (change > 0 && tty_term_has(term, TTYC_CUU)) {
2514 tty_putcode_i(tty, TTYC_CUU, change);
2515 goto out;
2516 } else if (change < 0 && tty_term_has(term, TTYC_CUD)) {
2517 tty_putcode_i(tty, TTYC_CUD, -change);
2518 goto out;
2522 absolute:
2523 /* Absolute movement. */
2524 tty_putcode_ii(tty, TTYC_CUP, cy, cx);
2526 out:
2527 tty->cx = cx;
2528 tty->cy = cy;
2531 static void
2532 tty_hyperlink(struct tty *tty, const struct grid_cell *gc,
2533 struct hyperlinks *hl)
2535 const char *uri, *id;
2537 if (gc->link == tty->cell.link)
2538 return;
2539 tty->cell.link = gc->link;
2541 if (hl == NULL)
2542 return;
2544 if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, NULL, &id))
2545 tty_putcode_ss(tty, TTYC_HLS, "", "");
2546 else
2547 tty_putcode_ss(tty, TTYC_HLS, id, uri);
2550 void
2551 tty_attributes(struct tty *tty, const struct grid_cell *gc,
2552 const struct grid_cell *defaults, struct colour_palette *palette,
2553 struct hyperlinks *hl)
2555 struct grid_cell *tc = &tty->cell, gc2;
2556 int changed;
2558 /* Copy cell and update default colours. */
2559 memcpy(&gc2, gc, sizeof gc2);
2560 if (~gc->flags & GRID_FLAG_NOPALETTE) {
2561 if (gc2.fg == 8)
2562 gc2.fg = defaults->fg;
2563 if (gc2.bg == 8)
2564 gc2.bg = defaults->bg;
2567 /* Ignore cell if it is the same as the last one. */
2568 if (gc2.attr == tty->last_cell.attr &&
2569 gc2.fg == tty->last_cell.fg &&
2570 gc2.bg == tty->last_cell.bg &&
2571 gc2.us == tty->last_cell.us &&
2572 gc2.link == tty->last_cell.link)
2573 return;
2576 * If no setab, try to use the reverse attribute as a best-effort for a
2577 * non-default background. This is a bit of a hack but it doesn't do
2578 * any serious harm and makes a couple of applications happier.
2580 if (!tty_term_has(tty->term, TTYC_SETAB)) {
2581 if (gc2.attr & GRID_ATTR_REVERSE) {
2582 if (gc2.fg != 7 && !COLOUR_DEFAULT(gc2.fg))
2583 gc2.attr &= ~GRID_ATTR_REVERSE;
2584 } else {
2585 if (gc2.bg != 0 && !COLOUR_DEFAULT(gc2.bg))
2586 gc2.attr |= GRID_ATTR_REVERSE;
2590 /* Fix up the colours if necessary. */
2591 tty_check_fg(tty, palette, &gc2);
2592 tty_check_bg(tty, palette, &gc2);
2593 tty_check_us(tty, palette, &gc2);
2596 * If any bits are being cleared or the underline colour is now default,
2597 * reset everything.
2599 if ((tc->attr & ~gc2.attr) || (tc->us != gc2.us && gc2.us == 0))
2600 tty_reset(tty);
2603 * Set the colours. This may call tty_reset() (so it comes next) and
2604 * may add to (NOT remove) the desired attributes.
2606 tty_colours(tty, &gc2);
2608 /* Filter out attribute bits already set. */
2609 changed = gc2.attr & ~tc->attr;
2610 tc->attr = gc2.attr;
2612 /* Set the attributes. */
2613 if (changed & GRID_ATTR_BRIGHT)
2614 tty_putcode(tty, TTYC_BOLD);
2615 if (changed & GRID_ATTR_DIM)
2616 tty_putcode(tty, TTYC_DIM);
2617 if (changed & GRID_ATTR_ITALICS)
2618 tty_set_italics(tty);
2619 if (changed & GRID_ATTR_ALL_UNDERSCORE) {
2620 if ((changed & GRID_ATTR_UNDERSCORE) ||
2621 !tty_term_has(tty->term, TTYC_SMULX))
2622 tty_putcode(tty, TTYC_SMUL);
2623 else if (changed & GRID_ATTR_UNDERSCORE_2)
2624 tty_putcode_i(tty, TTYC_SMULX, 2);
2625 else if (changed & GRID_ATTR_UNDERSCORE_3)
2626 tty_putcode_i(tty, TTYC_SMULX, 3);
2627 else if (changed & GRID_ATTR_UNDERSCORE_4)
2628 tty_putcode_i(tty, TTYC_SMULX, 4);
2629 else if (changed & GRID_ATTR_UNDERSCORE_5)
2630 tty_putcode_i(tty, TTYC_SMULX, 5);
2632 if (changed & GRID_ATTR_BLINK)
2633 tty_putcode(tty, TTYC_BLINK);
2634 if (changed & GRID_ATTR_REVERSE) {
2635 if (tty_term_has(tty->term, TTYC_REV))
2636 tty_putcode(tty, TTYC_REV);
2637 else if (tty_term_has(tty->term, TTYC_SMSO))
2638 tty_putcode(tty, TTYC_SMSO);
2640 if (changed & GRID_ATTR_HIDDEN)
2641 tty_putcode(tty, TTYC_INVIS);
2642 if (changed & GRID_ATTR_STRIKETHROUGH)
2643 tty_putcode(tty, TTYC_SMXX);
2644 if (changed & GRID_ATTR_OVERLINE)
2645 tty_putcode(tty, TTYC_SMOL);
2646 if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
2647 tty_putcode(tty, TTYC_SMACS);
2649 /* Set hyperlink if any. */
2650 tty_hyperlink(tty, gc, hl);
2652 memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell);
2655 static void
2656 tty_colours(struct tty *tty, const struct grid_cell *gc)
2658 struct grid_cell *tc = &tty->cell;
2659 int have_ax;
2661 /* No changes? Nothing is necessary. */
2662 if (gc->fg == tc->fg && gc->bg == tc->bg && gc->us == tc->us)
2663 return;
2666 * Is either the default colour? This is handled specially because the
2667 * best solution might be to reset both colours to default, in which
2668 * case if only one is default need to fall onward to set the other
2669 * colour.
2671 if (COLOUR_DEFAULT(gc->fg) || COLOUR_DEFAULT(gc->bg)) {
2673 * If don't have AX but do have op, send sgr0 (op can't
2674 * actually be used because it is sometimes the same as sgr0
2675 * and sometimes isn't). This resets both colours to default.
2677 * Otherwise, try to set the default colour only as needed.
2679 have_ax = tty_term_flag(tty->term, TTYC_AX);
2680 if (!have_ax && tty_term_has(tty->term, TTYC_OP))
2681 tty_reset(tty);
2682 else {
2683 if (COLOUR_DEFAULT(gc->fg) && !COLOUR_DEFAULT(tc->fg)) {
2684 if (have_ax)
2685 tty_puts(tty, "\033[39m");
2686 else if (tc->fg != 7)
2687 tty_putcode_i(tty, TTYC_SETAF, 7);
2688 tc->fg = gc->fg;
2690 if (COLOUR_DEFAULT(gc->bg) && !COLOUR_DEFAULT(tc->bg)) {
2691 if (have_ax)
2692 tty_puts(tty, "\033[49m");
2693 else if (tc->bg != 0)
2694 tty_putcode_i(tty, TTYC_SETAB, 0);
2695 tc->bg = gc->bg;
2700 /* Set the foreground colour. */
2701 if (!COLOUR_DEFAULT(gc->fg) && gc->fg != tc->fg)
2702 tty_colours_fg(tty, gc);
2705 * Set the background colour. This must come after the foreground as
2706 * tty_colour_fg() can call tty_reset().
2708 if (!COLOUR_DEFAULT(gc->bg) && gc->bg != tc->bg)
2709 tty_colours_bg(tty, gc);
2711 /* Set the underscore colour. */
2712 if (gc->us != tc->us)
2713 tty_colours_us(tty, gc);
2716 static void
2717 tty_check_fg(struct tty *tty, struct colour_palette *palette,
2718 struct grid_cell *gc)
2720 u_char r, g, b;
2721 u_int colours;
2722 int c;
2725 * Perform substitution if this pane has a palette. If the bright
2726 * attribute is set and Nobr is not present, use the bright entry in
2727 * the palette by changing to the aixterm colour
2729 if (~gc->flags & GRID_FLAG_NOPALETTE) {
2730 c = gc->fg;
2731 if (c < 8 &&
2732 gc->attr & GRID_ATTR_BRIGHT &&
2733 !tty_term_has(tty->term, TTYC_NOBR))
2734 c += 90;
2735 if ((c = colour_palette_get(palette, c)) != -1)
2736 gc->fg = c;
2739 /* Is this a 24-bit colour? */
2740 if (gc->fg & COLOUR_FLAG_RGB) {
2741 /* Not a 24-bit terminal? Translate to 256-colour palette. */
2742 if (tty->term->flags & TERM_RGBCOLOURS)
2743 return;
2744 colour_split_rgb(gc->fg, &r, &g, &b);
2745 gc->fg = colour_find_rgb(r, g, b);
2748 /* How many colours does this terminal have? */
2749 if (tty->term->flags & TERM_256COLOURS)
2750 colours = 256;
2751 else
2752 colours = tty_term_number(tty->term, TTYC_COLORS);
2754 /* Is this a 256-colour colour? */
2755 if (gc->fg & COLOUR_FLAG_256) {
2756 /* And not a 256 colour mode? */
2757 if (colours < 256) {
2758 gc->fg = colour_256to16(gc->fg);
2759 if (gc->fg & 8) {
2760 gc->fg &= 7;
2761 if (colours >= 16)
2762 gc->fg += 90;
2765 return;
2768 /* Is this an aixterm colour? */
2769 if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) {
2770 gc->fg -= 90;
2771 gc->attr |= GRID_ATTR_BRIGHT;
2775 static void
2776 tty_check_bg(struct tty *tty, struct colour_palette *palette,
2777 struct grid_cell *gc)
2779 u_char r, g, b;
2780 u_int colours;
2781 int c;
2783 /* Perform substitution if this pane has a palette. */
2784 if (~gc->flags & GRID_FLAG_NOPALETTE) {
2785 if ((c = colour_palette_get(palette, gc->bg)) != -1)
2786 gc->bg = c;
2789 /* Is this a 24-bit colour? */
2790 if (gc->bg & COLOUR_FLAG_RGB) {
2791 /* Not a 24-bit terminal? Translate to 256-colour palette. */
2792 if (tty->term->flags & TERM_RGBCOLOURS)
2793 return;
2794 colour_split_rgb(gc->bg, &r, &g, &b);
2795 gc->bg = colour_find_rgb(r, g, b);
2798 /* How many colours does this terminal have? */
2799 if (tty->term->flags & TERM_256COLOURS)
2800 colours = 256;
2801 else
2802 colours = tty_term_number(tty->term, TTYC_COLORS);
2804 /* Is this a 256-colour colour? */
2805 if (gc->bg & COLOUR_FLAG_256) {
2807 * And not a 256 colour mode? Translate to 16-colour
2808 * palette. Bold background doesn't exist portably, so just
2809 * discard the bold bit if set.
2811 if (colours < 256) {
2812 gc->bg = colour_256to16(gc->bg);
2813 if (gc->bg & 8) {
2814 gc->bg &= 7;
2815 if (colours >= 16)
2816 gc->bg += 90;
2819 return;
2822 /* Is this an aixterm colour? */
2823 if (gc->bg >= 90 && gc->bg <= 97 && colours < 16)
2824 gc->bg -= 90;
2827 static void
2828 tty_check_us(__unused struct tty *tty, struct colour_palette *palette,
2829 struct grid_cell *gc)
2831 int c;
2833 /* Perform substitution if this pane has a palette. */
2834 if (~gc->flags & GRID_FLAG_NOPALETTE) {
2835 if ((c = colour_palette_get(palette, gc->us)) != -1)
2836 gc->us = c;
2839 /* Convert underscore colour if only RGB can be supported. */
2840 if (!tty_term_has(tty->term, TTYC_SETULC1)) {
2841 if ((c = colour_force_rgb (gc->us)) == -1)
2842 gc->us = 8;
2843 else
2844 gc->us = c;
2848 static void
2849 tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
2851 struct grid_cell *tc = &tty->cell;
2852 char s[32];
2854 /* Is this a 24-bit or 256-colour colour? */
2855 if (gc->fg & COLOUR_FLAG_RGB || gc->fg & COLOUR_FLAG_256) {
2856 if (tty_try_colour(tty, gc->fg, "38") == 0)
2857 goto save;
2858 /* Should not get here, already converted in tty_check_fg. */
2859 return;
2862 /* Is this an aixterm bright colour? */
2863 if (gc->fg >= 90 && gc->fg <= 97) {
2864 if (tty->term->flags & TERM_256COLOURS) {
2865 xsnprintf(s, sizeof s, "\033[%dm", gc->fg);
2866 tty_puts(tty, s);
2867 } else
2868 tty_putcode_i(tty, TTYC_SETAF, gc->fg - 90 + 8);
2869 goto save;
2872 /* Otherwise set the foreground colour. */
2873 tty_putcode_i(tty, TTYC_SETAF, gc->fg);
2875 save:
2876 /* Save the new values in the terminal current cell. */
2877 tc->fg = gc->fg;
2880 static void
2881 tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
2883 struct grid_cell *tc = &tty->cell;
2884 char s[32];
2886 /* Is this a 24-bit or 256-colour colour? */
2887 if (gc->bg & COLOUR_FLAG_RGB || gc->bg & COLOUR_FLAG_256) {
2888 if (tty_try_colour(tty, gc->bg, "48") == 0)
2889 goto save;
2890 /* Should not get here, already converted in tty_check_bg. */
2891 return;
2894 /* Is this an aixterm bright colour? */
2895 if (gc->bg >= 90 && gc->bg <= 97) {
2896 if (tty->term->flags & TERM_256COLOURS) {
2897 xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10);
2898 tty_puts(tty, s);
2899 } else
2900 tty_putcode_i(tty, TTYC_SETAB, gc->bg - 90 + 8);
2901 goto save;
2904 /* Otherwise set the background colour. */
2905 tty_putcode_i(tty, TTYC_SETAB, gc->bg);
2907 save:
2908 /* Save the new values in the terminal current cell. */
2909 tc->bg = gc->bg;
2912 static void
2913 tty_colours_us(struct tty *tty, const struct grid_cell *gc)
2915 struct grid_cell *tc = &tty->cell;
2916 u_int c;
2917 u_char r, g, b;
2919 /* Clear underline colour. */
2920 if (COLOUR_DEFAULT(gc->us)) {
2921 tty_putcode(tty, TTYC_OL);
2922 goto save;
2926 * If this is not an RGB colour, use Setulc1 if it exists, otherwise
2927 * convert.
2929 if (~gc->us & COLOUR_FLAG_RGB) {
2930 c = gc->us;
2931 if ((~c & COLOUR_FLAG_256) && (c >= 90 && c <= 97))
2932 c -= 82;
2933 tty_putcode_i(tty, TTYC_SETULC1, c & ~COLOUR_FLAG_256);
2934 return;
2938 * Setulc and setal follows the ncurses(3) one argument "direct colour"
2939 * capability format. Calculate the colour value.
2941 colour_split_rgb(gc->us, &r, &g, &b);
2942 c = (65536 * r) + (256 * g) + b;
2945 * Write the colour. Only use setal if the RGB flag is set because the
2946 * non-RGB version may be wrong.
2948 if (tty_term_has(tty->term, TTYC_SETULC))
2949 tty_putcode_i(tty, TTYC_SETULC, c);
2950 else if (tty_term_has(tty->term, TTYC_SETAL) &&
2951 tty_term_has(tty->term, TTYC_RGB))
2952 tty_putcode_i(tty, TTYC_SETAL, c);
2954 save:
2955 /* Save the new values in the terminal current cell. */
2956 tc->us = gc->us;
2959 static int
2960 tty_try_colour(struct tty *tty, int colour, const char *type)
2962 u_char r, g, b;
2964 if (colour & COLOUR_FLAG_256) {
2965 if (*type == '3' && tty_term_has(tty->term, TTYC_SETAF))
2966 tty_putcode_i(tty, TTYC_SETAF, colour & 0xff);
2967 else if (tty_term_has(tty->term, TTYC_SETAB))
2968 tty_putcode_i(tty, TTYC_SETAB, colour & 0xff);
2969 return (0);
2972 if (colour & COLOUR_FLAG_RGB) {
2973 colour_split_rgb(colour & 0xffffff, &r, &g, &b);
2974 if (*type == '3' && tty_term_has(tty->term, TTYC_SETRGBF))
2975 tty_putcode_iii(tty, TTYC_SETRGBF, r, g, b);
2976 else if (tty_term_has(tty->term, TTYC_SETRGBB))
2977 tty_putcode_iii(tty, TTYC_SETRGBB, r, g, b);
2978 return (0);
2981 return (-1);
2984 static void
2985 tty_window_default_style(struct grid_cell *gc, struct window_pane *wp)
2987 memcpy(gc, &grid_default_cell, sizeof *gc);
2988 gc->fg = wp->palette.fg;
2989 gc->bg = wp->palette.bg;
2992 void
2993 tty_default_colours(struct grid_cell *gc, struct window_pane *wp)
2995 struct options *oo = wp->options;
2996 struct format_tree *ft;
2998 memcpy(gc, &grid_default_cell, sizeof *gc);
3000 if (wp->flags & PANE_STYLECHANGED) {
3001 log_debug("%%%u: style changed", wp->id);
3002 wp->flags &= ~PANE_STYLECHANGED;
3004 ft = format_create(NULL, NULL, FORMAT_PANE|wp->id,
3005 FORMAT_NOJOBS);
3006 format_defaults(ft, NULL, NULL, NULL, wp);
3007 tty_window_default_style(&wp->cached_active_gc, wp);
3008 style_add(&wp->cached_active_gc, oo, "window-active-style", ft);
3009 tty_window_default_style(&wp->cached_gc, wp);
3010 style_add(&wp->cached_gc, oo, "window-style", ft);
3011 format_free(ft);
3014 if (gc->fg == 8) {
3015 if (wp == wp->window->active && wp->cached_active_gc.fg != 8)
3016 gc->fg = wp->cached_active_gc.fg;
3017 else
3018 gc->fg = wp->cached_gc.fg;
3021 if (gc->bg == 8) {
3022 if (wp == wp->window->active && wp->cached_active_gc.bg != 8)
3023 gc->bg = wp->cached_active_gc.bg;
3024 else
3025 gc->bg = wp->cached_gc.bg;
3029 static void
3030 tty_default_attributes(struct tty *tty, const struct grid_cell *defaults,
3031 struct colour_palette *palette, u_int bg, struct hyperlinks *hl)
3033 struct grid_cell gc;
3035 memcpy(&gc, &grid_default_cell, sizeof gc);
3036 gc.bg = bg;
3037 tty_attributes(tty, &gc, defaults, palette, hl);
3040 static void
3041 tty_clipboard_query_callback(__unused int fd, __unused short events, void *data)
3043 struct tty *tty = data;
3044 struct client *c = tty->client;
3046 c->flags &= ~CLIENT_CLIPBOARDBUFFER;
3047 free(c->clipboard_panes);
3048 c->clipboard_panes = NULL;
3049 c->clipboard_npanes = 0;
3051 tty->flags &= ~TTY_OSC52QUERY;
3054 void
3055 tty_clipboard_query(struct tty *tty)
3057 struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT };
3059 if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY))
3060 return;
3061 tty_putcode_ss(tty, TTYC_MS, "", "?");
3063 tty->flags |= TTY_OSC52QUERY;
3064 evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty);
3065 evtimer_add(&tty->clipboard_timer, &tv);