1 /* $OpenBSD: screen-redraw.c,v 1.90 2021/10/14 13:19:01 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 21 #include <stdlib.h> 22 #include <string.h> 23 24 #include "tmux.h" 25 26 static void screen_redraw_draw_borders(struct screen_redraw_ctx *); 27 static void screen_redraw_draw_panes(struct screen_redraw_ctx *); 28 static void screen_redraw_draw_status(struct screen_redraw_ctx *); 29 static void screen_redraw_draw_pane(struct screen_redraw_ctx *, 30 struct window_pane *); 31 static void screen_redraw_set_context(struct client *, 32 struct screen_redraw_ctx *); 33 34 #define START_ISOLATE "\342\201\246" 35 #define END_ISOLATE "\342\201\251" 36 37 enum screen_redraw_border_type { 38 SCREEN_REDRAW_OUTSIDE, 39 SCREEN_REDRAW_INSIDE, 40 SCREEN_REDRAW_BORDER 41 }; 42 43 /* Get cell border character. */ 44 static void 45 screen_redraw_border_set(struct window_pane *wp, enum pane_lines pane_lines, 46 int cell_type, struct grid_cell *gc) 47 { 48 u_int idx; 49 50 switch (pane_lines) { 51 case PANE_LINES_NUMBER: 52 if (cell_type == CELL_OUTSIDE) { 53 gc->attr |= GRID_ATTR_CHARSET; 54 utf8_set(&gc->data, CELL_BORDERS[CELL_OUTSIDE]); 55 break; 56 } 57 gc->attr &= ~GRID_ATTR_CHARSET; 58 if (wp != NULL && window_pane_index(wp, &idx) == 0) 59 utf8_set(&gc->data, '0' + (idx % 10)); 60 else 61 utf8_set(&gc->data, '*'); 62 break; 63 case PANE_LINES_DOUBLE: 64 gc->attr &= ~GRID_ATTR_CHARSET; 65 utf8_copy(&gc->data, tty_acs_double_borders(cell_type)); 66 break; 67 case PANE_LINES_HEAVY: 68 gc->attr &= ~GRID_ATTR_CHARSET; 69 utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type)); 70 break; 71 case PANE_LINES_SIMPLE: 72 gc->attr &= ~GRID_ATTR_CHARSET; 73 utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]); 74 break; 75 default: 76 gc->attr |= GRID_ATTR_CHARSET; 77 utf8_set(&gc->data, CELL_BORDERS[cell_type]); 78 break; 79 } 80 } 81 82 /* Return if window has only two panes. */ 83 static int 84 screen_redraw_two_panes(struct window *w, int direction) 85 { 86 struct window_pane *wp; 87 88 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); 89 if (wp == NULL) 90 return (0); /* one pane */ 91 if (TAILQ_NEXT(wp, entry) != NULL) 92 return (0); /* more than two panes */ 93 if (direction == 0 && wp->xoff == 0) 94 return (0); 95 if (direction == 1 && wp->yoff == 0) 96 return (0); 97 return (1); 98 } 99 100 /* Check if cell is on the border of a pane. */ 101 static enum screen_redraw_border_type 102 screen_redraw_pane_border(struct window_pane *wp, u_int px, u_int py, 103 int pane_status) 104 { 105 u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy; 106 107 /* Inside pane. */ 108 if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey) 109 return (SCREEN_REDRAW_INSIDE); 110 111 /* Left/right borders. */ 112 if (pane_status == PANE_STATUS_OFF) { 113 if (screen_redraw_two_panes(wp->window, 0)) { 114 if (wp->xoff == 0 && px == wp->sx && py <= wp->sy / 2) 115 return (SCREEN_REDRAW_BORDER); 116 if (wp->xoff != 0 && 117 px == wp->xoff - 1 && 118 py > wp->sy / 2) 119 return (SCREEN_REDRAW_BORDER); 120 } else { 121 if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { 122 if (wp->xoff != 0 && px == wp->xoff - 1) 123 return (SCREEN_REDRAW_BORDER); 124 if (px == ex) 125 return (SCREEN_REDRAW_BORDER); 126 } 127 } 128 } else { 129 if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { 130 if (wp->xoff != 0 && px == wp->xoff - 1) 131 return (SCREEN_REDRAW_BORDER); 132 if (px == ex) 133 return (SCREEN_REDRAW_BORDER); 134 } 135 } 136 137 /* Top/bottom borders. */ 138 if (pane_status == PANE_STATUS_OFF) { 139 if (screen_redraw_two_panes(wp->window, 1)) { 140 if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2) 141 return (SCREEN_REDRAW_BORDER); 142 if (wp->yoff != 0 && 143 py == wp->yoff - 1 && 144 px > wp->sx / 2) 145 return (SCREEN_REDRAW_BORDER); 146 } else { 147 if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { 148 if (wp->yoff != 0 && py == wp->yoff - 1) 149 return (SCREEN_REDRAW_BORDER); 150 if (py == ey) 151 return (SCREEN_REDRAW_BORDER); 152 } 153 } 154 } else if (pane_status == PANE_STATUS_TOP) { 155 if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { 156 if (wp->yoff != 0 && py == wp->yoff - 1) 157 return (SCREEN_REDRAW_BORDER); 158 } 159 } else { 160 if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) { 161 if (py == ey) 162 return (SCREEN_REDRAW_BORDER); 163 } 164 } 165 166 /* Outside pane. */ 167 return (SCREEN_REDRAW_OUTSIDE); 168 } 169 170 /* Check if a cell is on a border. */ 171 static int 172 screen_redraw_cell_border(struct client *c, u_int px, u_int py, int pane_status) 173 { 174 struct window *w = c->session->curw->window; 175 struct window_pane *wp; 176 177 /* Outside the window? */ 178 if (px > w->sx || py > w->sy) 179 return (0); 180 181 /* On the window border? */ 182 if (px == w->sx || py == w->sy) 183 return (1); 184 185 /* Check all the panes. */ 186 TAILQ_FOREACH(wp, &w->panes, entry) { 187 if (!window_pane_visible(wp)) 188 continue; 189 switch (screen_redraw_pane_border(wp, px, py, pane_status)) { 190 case SCREEN_REDRAW_INSIDE: 191 return (0); 192 case SCREEN_REDRAW_BORDER: 193 return (1); 194 case SCREEN_REDRAW_OUTSIDE: 195 break; 196 } 197 } 198 199 return (0); 200 } 201 202 /* Work out type of border cell from surrounding cells. */ 203 static int 204 screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, 205 int pane_status) 206 { 207 struct window *w = c->session->curw->window; 208 u_int sx = w->sx, sy = w->sy; 209 int borders = 0; 210 211 /* Is this outside the window? */ 212 if (px > sx || py > sy) 213 return (CELL_OUTSIDE); 214 215 /* 216 * Construct a bitmask of whether the cells to the left (bit 4), right, 217 * top, and bottom (bit 1) of this cell are borders. 218 */ 219 if (px == 0 || screen_redraw_cell_border(c, px - 1, py, pane_status)) 220 borders |= 8; 221 if (px <= sx && screen_redraw_cell_border(c, px + 1, py, pane_status)) 222 borders |= 4; 223 if (pane_status == PANE_STATUS_TOP) { 224 if (py != 0 && 225 screen_redraw_cell_border(c, px, py - 1, pane_status)) 226 borders |= 2; 227 if (screen_redraw_cell_border(c, px, py + 1, pane_status)) 228 borders |= 1; 229 } else if (pane_status == PANE_STATUS_BOTTOM) { 230 if (py == 0 || 231 screen_redraw_cell_border(c, px, py - 1, pane_status)) 232 borders |= 2; 233 if (py != sy - 1 && 234 screen_redraw_cell_border(c, px, py + 1, pane_status)) 235 borders |= 1; 236 } else { 237 if (py == 0 || 238 screen_redraw_cell_border(c, px, py - 1, pane_status)) 239 borders |= 2; 240 if (screen_redraw_cell_border(c, px, py + 1, pane_status)) 241 borders |= 1; 242 } 243 244 /* 245 * Figure out what kind of border this cell is. Only one bit set 246 * doesn't make sense (can't have a border cell with no others 247 * connected). 248 */ 249 switch (borders) { 250 case 15: /* 1111, left right top bottom */ 251 return (CELL_JOIN); 252 case 14: /* 1110, left right top */ 253 return (CELL_BOTTOMJOIN); 254 case 13: /* 1101, left right bottom */ 255 return (CELL_TOPJOIN); 256 case 12: /* 1100, left right */ 257 return (CELL_LEFTRIGHT); 258 case 11: /* 1011, left top bottom */ 259 return (CELL_RIGHTJOIN); 260 case 10: /* 1010, left top */ 261 return (CELL_BOTTOMRIGHT); 262 case 9: /* 1001, left bottom */ 263 return (CELL_TOPRIGHT); 264 case 7: /* 0111, right top bottom */ 265 return (CELL_LEFTJOIN); 266 case 6: /* 0110, right top */ 267 return (CELL_BOTTOMLEFT); 268 case 5: /* 0101, right bottom */ 269 return (CELL_TOPLEFT); 270 case 3: /* 0011, top bottom */ 271 return (CELL_TOPBOTTOM); 272 } 273 return (CELL_OUTSIDE); 274 } 275 276 /* Check if cell inside a pane. */ 277 static int 278 screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, 279 struct window_pane **wpp) 280 { 281 struct window *w = c->session->curw->window; 282 struct window_pane *wp, *active; 283 int border; 284 u_int right, line; 285 286 *wpp = NULL; 287 288 if (px > w->sx || py > w->sy) 289 return (CELL_OUTSIDE); 290 if (px == w->sx || py == w->sy) /* window border */ 291 return (screen_redraw_type_of_cell(c, px, py, pane_status)); 292 293 if (pane_status != PANE_STATUS_OFF) { 294 active = wp = server_client_get_pane(c); 295 do { 296 if (!window_pane_visible(wp)) 297 goto next1; 298 299 if (pane_status == PANE_STATUS_TOP) 300 line = wp->yoff - 1; 301 else 302 line = wp->yoff + wp->sy; 303 right = wp->xoff + 2 + wp->status_size - 1; 304 305 if (py == line && px >= wp->xoff + 2 && px <= right) 306 return (CELL_INSIDE); 307 308 next1: 309 wp = TAILQ_NEXT(wp, entry); 310 if (wp == NULL) 311 wp = TAILQ_FIRST(&w->panes); 312 } while (wp != active); 313 } 314 315 active = wp = server_client_get_pane(c); 316 do { 317 if (!window_pane_visible(wp)) 318 goto next2; 319 *wpp = wp; 320 321 /* 322 * If definitely inside, return. If not on border, skip. 323 * Otherwise work out the cell. 324 */ 325 border = screen_redraw_pane_border(wp, px, py, pane_status); 326 if (border == SCREEN_REDRAW_INSIDE) 327 return (CELL_INSIDE); 328 if (border == SCREEN_REDRAW_OUTSIDE) 329 goto next2; 330 return (screen_redraw_type_of_cell(c, px, py, pane_status)); 331 332 next2: 333 wp = TAILQ_NEXT(wp, entry); 334 if (wp == NULL) 335 wp = TAILQ_FIRST(&w->panes); 336 } while (wp != active); 337 338 return (CELL_OUTSIDE); 339 } 340 341 /* Check if the border of a particular pane. */ 342 static int 343 screen_redraw_check_is(u_int px, u_int py, int pane_status, 344 struct window_pane *wp) 345 { 346 enum screen_redraw_border_type border; 347 348 border = screen_redraw_pane_border(wp, px, py, pane_status); 349 if (border == SCREEN_REDRAW_BORDER) 350 return (1); 351 return (0); 352 } 353 354 /* Update pane status. */ 355 static int 356 screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, 357 struct screen_redraw_ctx *rctx, enum pane_lines pane_lines) 358 { 359 struct window *w = wp->window; 360 struct grid_cell gc; 361 const char *fmt; 362 struct format_tree *ft; 363 char *expanded; 364 int pane_status = rctx->pane_status; 365 u_int width, i, cell_type, px, py; 366 struct screen_write_ctx ctx; 367 struct screen old; 368 369 ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); 370 format_defaults(ft, c, c->session, c->session->curw, wp); 371 372 if (wp == server_client_get_pane(c)) 373 style_apply(&gc, w->options, "pane-active-border-style", ft); 374 else 375 style_apply(&gc, w->options, "pane-border-style", ft); 376 fmt = options_get_string(w->options, "pane-border-format"); 377 378 expanded = format_expand_time(ft, fmt); 379 if (wp->sx < 4) 380 wp->status_size = width = 0; 381 else 382 wp->status_size = width = wp->sx - 4; 383 384 memcpy(&old, &wp->status_screen, sizeof old); 385 screen_init(&wp->status_screen, width, 1, 0); 386 wp->status_screen.mode = 0; 387 388 screen_write_start(&ctx, &wp->status_screen); 389 390 for (i = 0; i < width; i++) { 391 px = wp->xoff + 2 + i; 392 if (rctx->pane_status == PANE_STATUS_TOP) 393 py = wp->yoff - 1; 394 else 395 py = wp->yoff + wp->sy; 396 cell_type = screen_redraw_type_of_cell(c, px, py, pane_status); 397 screen_redraw_border_set(wp, pane_lines, cell_type, &gc); 398 screen_write_cell(&ctx, &gc); 399 } 400 gc.attr &= ~GRID_ATTR_CHARSET; 401 402 screen_write_cursormove(&ctx, 0, 0, 0); 403 format_draw(&ctx, &gc, width, expanded, NULL); 404 screen_write_stop(&ctx); 405 406 free(expanded); 407 format_free(ft); 408 409 if (grid_compare(wp->status_screen.grid, old.grid) == 0) { 410 screen_free(&old); 411 return (0); 412 } 413 screen_free(&old); 414 return (1); 415 } 416 417 /* Draw pane status. */ 418 static void 419 screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) 420 { 421 struct client *c = ctx->c; 422 struct window *w = c->session->curw->window; 423 struct tty *tty = &c->tty; 424 struct window_pane *wp; 425 struct screen *s; 426 u_int i, x, width, xoff, yoff, size; 427 428 log_debug("%s: %s @%u", __func__, c->name, w->id); 429 430 TAILQ_FOREACH(wp, &w->panes, entry) { 431 if (!window_pane_visible(wp)) 432 continue; 433 s = &wp->status_screen; 434 435 size = wp->status_size; 436 if (ctx->pane_status == PANE_STATUS_TOP) 437 yoff = wp->yoff - 1; 438 else 439 yoff = wp->yoff + wp->sy; 440 xoff = wp->xoff + 2; 441 442 if (xoff + size <= ctx->ox || 443 xoff >= ctx->ox + ctx->sx || 444 yoff < ctx->oy || 445 yoff >= ctx->oy + ctx->sy) 446 continue; 447 448 if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) { 449 /* All visible. */ 450 i = 0; 451 x = xoff - ctx->ox; 452 width = size; 453 } else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) { 454 /* Both left and right not visible. */ 455 i = ctx->ox; 456 x = 0; 457 width = ctx->sx; 458 } else if (xoff < ctx->ox) { 459 /* Left not visible. */ 460 i = ctx->ox - xoff; 461 x = 0; 462 width = size - i; 463 } else { 464 /* Right not visible. */ 465 i = 0; 466 x = xoff - ctx->ox; 467 width = size - x; 468 } 469 470 if (ctx->statustop) 471 yoff += ctx->statuslines; 472 tty_draw_line(tty, s, i, 0, width, x, yoff - ctx->oy, 473 &grid_default_cell, NULL); 474 } 475 tty_cursor(tty, 0, 0); 476 } 477 478 /* Update status line and change flags if unchanged. */ 479 static int 480 screen_redraw_update(struct client *c, int flags) 481 { 482 struct window *w = c->session->curw->window; 483 struct window_pane *wp; 484 struct options *wo = w->options; 485 int redraw; 486 enum pane_lines lines; 487 struct screen_redraw_ctx ctx; 488 489 if (c->message_string != NULL) 490 redraw = status_message_redraw(c); 491 else if (c->prompt_string != NULL) 492 redraw = status_prompt_redraw(c); 493 else 494 redraw = status_redraw(c); 495 if (!redraw && (~flags & CLIENT_REDRAWSTATUSALWAYS)) 496 flags &= ~CLIENT_REDRAWSTATUS; 497 498 if (c->overlay_draw != NULL) 499 flags |= CLIENT_REDRAWOVERLAY; 500 501 if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) { 502 screen_redraw_set_context(c, &ctx); 503 lines = options_get_number(wo, "pane-border-lines"); 504 redraw = 0; 505 TAILQ_FOREACH(wp, &w->panes, entry) { 506 if (screen_redraw_make_pane_status(c, wp, &ctx, lines)) 507 redraw = 1; 508 } 509 if (redraw) 510 flags |= CLIENT_REDRAWBORDERS; 511 } 512 return (flags); 513 } 514 515 /* Set up redraw context. */ 516 static void 517 screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx) 518 { 519 struct session *s = c->session; 520 struct options *oo = s->options; 521 struct window *w = s->curw->window; 522 struct options *wo = w->options; 523 u_int lines; 524 525 memset(ctx, 0, sizeof *ctx); 526 ctx->c = c; 527 528 lines = status_line_size(c); 529 if (c->message_string != NULL || c->prompt_string != NULL) 530 lines = (lines == 0) ? 1 : lines; 531 if (lines != 0 && options_get_number(oo, "status-position") == 0) 532 ctx->statustop = 1; 533 ctx->statuslines = lines; 534 535 ctx->pane_status = options_get_number(wo, "pane-border-status"); 536 ctx->pane_lines = options_get_number(wo, "pane-border-lines"); 537 538 tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); 539 540 log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name, 541 w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines, 542 ctx->statustop); 543 } 544 545 /* Redraw entire screen. */ 546 void 547 screen_redraw_screen(struct client *c) 548 { 549 struct screen_redraw_ctx ctx; 550 int flags; 551 552 if (c->flags & CLIENT_SUSPENDED) 553 return; 554 555 flags = screen_redraw_update(c, c->flags); 556 if ((flags & CLIENT_ALLREDRAWFLAGS) == 0) 557 return; 558 559 screen_redraw_set_context(c, &ctx); 560 tty_sync_start(&c->tty); 561 tty_update_mode(&c->tty, c->tty.mode, NULL); 562 563 if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { 564 log_debug("%s: redrawing borders", c->name); 565 if (ctx.pane_status != PANE_STATUS_OFF) 566 screen_redraw_draw_pane_status(&ctx); 567 screen_redraw_draw_borders(&ctx); 568 } 569 if (flags & CLIENT_REDRAWWINDOW) { 570 log_debug("%s: redrawing panes", c->name); 571 screen_redraw_draw_panes(&ctx); 572 } 573 if (ctx.statuslines != 0 && 574 (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) { 575 log_debug("%s: redrawing status", c->name); 576 screen_redraw_draw_status(&ctx); 577 } 578 if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) { 579 log_debug("%s: redrawing overlay", c->name); 580 c->overlay_draw(c, c->overlay_data, &ctx); 581 } 582 583 tty_reset(&c->tty); 584 } 585 586 /* Redraw a single pane. */ 587 void 588 screen_redraw_pane(struct client *c, struct window_pane *wp) 589 { 590 struct screen_redraw_ctx ctx; 591 592 if (!window_pane_visible(wp)) 593 return; 594 595 screen_redraw_set_context(c, &ctx); 596 tty_sync_start(&c->tty); 597 tty_update_mode(&c->tty, c->tty.mode, NULL); 598 599 screen_redraw_draw_pane(&ctx, wp); 600 601 tty_reset(&c->tty); 602 } 603 604 /* Get border cell style. */ 605 static const struct grid_cell * 606 screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, 607 u_int y, struct window_pane *wp) 608 { 609 struct client *c = ctx->c; 610 struct session *s = c->session; 611 struct window *w = s->curw->window; 612 struct window_pane *active = server_client_get_pane(c); 613 struct options *oo = w->options; 614 struct format_tree *ft; 615 616 if (wp->border_gc_set) 617 return (&wp->border_gc); 618 wp->border_gc_set = 1; 619 620 ft = format_create_defaults(NULL, c, s, s->curw, wp); 621 if (screen_redraw_check_is(x, y, ctx->pane_status, active)) 622 style_apply(&wp->border_gc, oo, "pane-active-border-style", ft); 623 else 624 style_apply(&wp->border_gc, oo, "pane-border-style", ft); 625 format_free(ft); 626 627 return (&wp->border_gc); 628 } 629 630 /* Draw a border cell. */ 631 static void 632 screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) 633 { 634 struct client *c = ctx->c; 635 struct session *s = c->session; 636 struct window *w = s->curw->window; 637 struct options *oo = w->options; 638 struct tty *tty = &c->tty; 639 struct format_tree *ft; 640 struct window_pane *wp; 641 struct grid_cell gc; 642 const struct grid_cell *tmp; 643 struct overlay_ranges r; 644 u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; 645 int pane_status = ctx->pane_status, isolates; 646 647 if (c->overlay_check != NULL) { 648 c->overlay_check(c, c->overlay_data, x, y, 1, &r); 649 if (r.nx[0] + r.nx[1] == 0) 650 return; 651 } 652 653 cell_type = screen_redraw_check_cell(c, x, y, pane_status, &wp); 654 if (cell_type == CELL_INSIDE) 655 return; 656 657 if (wp == NULL) { 658 if (!ctx->no_pane_gc_set) { 659 ft = format_create_defaults(NULL, c, s, s->curw, NULL); 660 memcpy(&ctx->no_pane_gc, &grid_default_cell, sizeof gc); 661 style_add(&ctx->no_pane_gc, oo, "pane-border-style", 662 ft); 663 format_free(ft); 664 ctx->no_pane_gc_set = 1; 665 } 666 memcpy(&gc, &ctx->no_pane_gc, sizeof gc); 667 } else { 668 tmp = screen_redraw_draw_borders_style(ctx, x, y, wp); 669 if (tmp == NULL) 670 return; 671 memcpy(&gc, tmp, sizeof gc); 672 673 if (server_is_marked(s, s->curw, marked_pane.wp) && 674 screen_redraw_check_is(x, y, pane_status, marked_pane.wp)) 675 gc.attr ^= GRID_ATTR_REVERSE; 676 } 677 screen_redraw_border_set(wp, ctx->pane_lines, cell_type, &gc); 678 679 if (cell_type == CELL_TOPBOTTOM && 680 (c->flags & CLIENT_UTF8) && 681 tty_term_has(tty->term, TTYC_BIDI)) 682 isolates = 1; 683 else 684 isolates = 0; 685 686 if (ctx->statustop) 687 tty_cursor(tty, i, ctx->statuslines + j); 688 else 689 tty_cursor(tty, i, j); 690 if (isolates) 691 tty_puts(tty, END_ISOLATE); 692 tty_cell(tty, &gc, &grid_default_cell, NULL); 693 if (isolates) 694 tty_puts(tty, START_ISOLATE); 695 } 696 697 /* Draw the borders. */ 698 static void 699 screen_redraw_draw_borders(struct screen_redraw_ctx *ctx) 700 { 701 struct client *c = ctx->c; 702 struct session *s = c->session; 703 struct window *w = s->curw->window; 704 struct window_pane *wp; 705 u_int i, j; 706 707 log_debug("%s: %s @%u", __func__, c->name, w->id); 708 709 TAILQ_FOREACH(wp, &w->panes, entry) 710 wp->border_gc_set = 0; 711 712 for (j = 0; j < c->tty.sy - ctx->statuslines; j++) { 713 for (i = 0; i < c->tty.sx; i++) 714 screen_redraw_draw_borders_cell(ctx, i, j); 715 } 716 } 717 718 /* Draw the panes. */ 719 static void 720 screen_redraw_draw_panes(struct screen_redraw_ctx *ctx) 721 { 722 struct client *c = ctx->c; 723 struct window *w = c->session->curw->window; 724 struct window_pane *wp; 725 726 log_debug("%s: %s @%u", __func__, c->name, w->id); 727 728 TAILQ_FOREACH(wp, &w->panes, entry) { 729 if (window_pane_visible(wp)) 730 screen_redraw_draw_pane(ctx, wp); 731 } 732 } 733 734 /* Draw the status line. */ 735 static void 736 screen_redraw_draw_status(struct screen_redraw_ctx *ctx) 737 { 738 struct client *c = ctx->c; 739 struct window *w = c->session->curw->window; 740 struct tty *tty = &c->tty; 741 struct screen *s = c->status.active; 742 u_int i, y; 743 744 log_debug("%s: %s @%u", __func__, c->name, w->id); 745 746 if (ctx->statustop) 747 y = 0; 748 else 749 y = c->tty.sy - ctx->statuslines; 750 for (i = 0; i < ctx->statuslines; i++) { 751 tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i, 752 &grid_default_cell, NULL); 753 } 754 } 755 756 /* Draw one pane. */ 757 static void 758 screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) 759 { 760 struct client *c = ctx->c; 761 struct window *w = c->session->curw->window; 762 struct tty *tty = &c->tty; 763 struct screen *s = wp->screen; 764 struct colour_palette *palette = &wp->palette; 765 struct grid_cell defaults; 766 u_int i, j, top, x, y, width; 767 768 log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id); 769 770 if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx) 771 return; 772 if (ctx->statustop) 773 top = ctx->statuslines; 774 else 775 top = 0; 776 for (j = 0; j < wp->sy; j++) { 777 if (wp->yoff + j < ctx->oy || wp->yoff + j >= ctx->oy + ctx->sy) 778 continue; 779 y = top + wp->yoff + j - ctx->oy; 780 781 if (wp->xoff >= ctx->ox && 782 wp->xoff + wp->sx <= ctx->ox + ctx->sx) { 783 /* All visible. */ 784 i = 0; 785 x = wp->xoff - ctx->ox; 786 width = wp->sx; 787 } else if (wp->xoff < ctx->ox && 788 wp->xoff + wp->sx > ctx->ox + ctx->sx) { 789 /* Both left and right not visible. */ 790 i = ctx->ox; 791 x = 0; 792 width = ctx->sx; 793 } else if (wp->xoff < ctx->ox) { 794 /* Left not visible. */ 795 i = ctx->ox - wp->xoff; 796 x = 0; 797 width = wp->sx - i; 798 } else { 799 /* Right not visible. */ 800 i = 0; 801 x = wp->xoff - ctx->ox; 802 width = ctx->sx - x; 803 } 804 log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u", 805 __func__, c->name, wp->id, i, j, x, y, width); 806 807 tty_default_colours(&defaults, wp); 808 tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette); 809 } 810 } 811