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