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