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