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