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