1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott (at) 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 static void screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *); 34 static void screen_redraw_draw_scrollbar(struct screen_redraw_ctx *, 35 struct window_pane *, int, int, int, u_int, u_int, u_int); 36 static void screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *, 37 struct window_pane *); 38 39 #define START_ISOLATE "\342\201\246" 40 #define END_ISOLATE "\342\201\251" 41 42 /* Border in relation to a pane. */ 43 enum screen_redraw_border_type { 44 SCREEN_REDRAW_OUTSIDE, 45 SCREEN_REDRAW_INSIDE, 46 SCREEN_REDRAW_BORDER_LEFT, 47 SCREEN_REDRAW_BORDER_RIGHT, 48 SCREEN_REDRAW_BORDER_TOP, 49 SCREEN_REDRAW_BORDER_BOTTOM 50 }; 51 #define BORDER_MARKERS " +,.-" 52 53 /* Get cell border character. */ 54 static void 55 screen_redraw_border_set(struct window *w, struct window_pane *wp, 56 enum pane_lines pane_lines, int cell_type, struct grid_cell *gc) 57 { 58 u_int idx; 59 60 if (cell_type == CELL_OUTSIDE && w->fill_character != NULL) { 61 utf8_copy(&gc->data, &w->fill_character[0]); 62 return; 63 } 64 65 switch (pane_lines) { 66 case PANE_LINES_NUMBER: 67 if (cell_type == CELL_OUTSIDE) { 68 gc->attr |= GRID_ATTR_CHARSET; 69 utf8_set(&gc->data, CELL_BORDERS[CELL_OUTSIDE]); 70 break; 71 } 72 gc->attr &= ~GRID_ATTR_CHARSET; 73 if (wp != NULL && window_pane_index(wp, &idx) == 0) 74 utf8_set(&gc->data, '0' + (idx % 10)); 75 else 76 utf8_set(&gc->data, '*'); 77 break; 78 case PANE_LINES_DOUBLE: 79 gc->attr &= ~GRID_ATTR_CHARSET; 80 utf8_copy(&gc->data, tty_acs_double_borders(cell_type)); 81 break; 82 case PANE_LINES_HEAVY: 83 gc->attr &= ~GRID_ATTR_CHARSET; 84 utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type)); 85 break; 86 case PANE_LINES_SIMPLE: 87 gc->attr &= ~GRID_ATTR_CHARSET; 88 utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]); 89 break; 90 case PANE_LINES_SPACES: 91 gc->attr &= ~GRID_ATTR_CHARSET; 92 utf8_set(&gc->data, ' '); 93 break; 94 default: 95 gc->attr |= GRID_ATTR_CHARSET; 96 utf8_set(&gc->data, CELL_BORDERS[cell_type]); 97 break; 98 } 99 } 100 101 /* Return if window has only two panes. */ 102 static int 103 screen_redraw_two_panes(struct window *w, int direction) 104 { 105 struct window_pane *wp; 106 107 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); 108 if (wp == NULL) 109 return (0); /* one pane */ 110 if (TAILQ_NEXT(wp, entry) != NULL) 111 return (0); /* more than two panes */ 112 if (direction == 0 && wp->xoff == 0) 113 return (0); 114 if (direction == 1 && wp->yoff == 0) 115 return (0); 116 return (1); 117 } 118 119 /* Check if cell is on the border of a pane. */ 120 static enum screen_redraw_border_type 121 screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, 122 u_int px, u_int py) 123 { 124 struct options *oo = wp->window->options; 125 u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy; 126 int hsplit = 0, vsplit = 0, pane_status = ctx->pane_status; 127 int pane_scrollbars = ctx->pane_scrollbars, sb_w = 0; 128 int sb_pos; 129 130 if (pane_scrollbars != 0) 131 sb_pos = ctx->pane_scrollbars_pos; 132 else 133 sb_pos = 0; 134 135 /* Inside pane. */ 136 if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey) 137 return (SCREEN_REDRAW_INSIDE); 138 139 /* Get pane indicator. */ 140 switch (options_get_number(oo, "pane-border-indicators")) { 141 case PANE_BORDER_COLOUR: 142 case PANE_BORDER_BOTH: 143 hsplit = screen_redraw_two_panes(wp->window, 0); 144 vsplit = screen_redraw_two_panes(wp->window, 1); 145 break; 146 } 147 148 /* Are scrollbars enabled? */ 149 if (window_pane_show_scrollbar(wp, pane_scrollbars)) 150 sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; 151 152 /* 153 * Left/right borders. The wp->sy / 2 test is to colour only half the 154 * active window's border when there are two panes. 155 */ 156 if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { 157 if (sb_pos == PANE_SCROLLBARS_LEFT) { 158 if (wp->xoff - sb_w == 0 && px == wp->sx + sb_w) 159 if (!hsplit || (hsplit && py <= wp->sy / 2)) 160 return (SCREEN_REDRAW_BORDER_RIGHT); 161 if (wp->xoff - sb_w != 0) { 162 if (px == wp->xoff - sb_w - 1 && 163 (!hsplit || (hsplit && py > wp->sy / 2))) 164 return (SCREEN_REDRAW_BORDER_LEFT); 165 if (px == wp->xoff + wp->sx + sb_w - 1) 166 return (SCREEN_REDRAW_BORDER_RIGHT); 167 } 168 } else { /* sb_pos == PANE_SCROLLBARS_RIGHT or disabled*/ 169 if (wp->xoff == 0 && px == wp->sx + sb_w) 170 if (!hsplit || (hsplit && py <= wp->sy / 2)) 171 return (SCREEN_REDRAW_BORDER_RIGHT); 172 if (wp->xoff != 0) { 173 if (px == wp->xoff - 1 && 174 (!hsplit || (hsplit && py > wp->sy / 2))) 175 return (SCREEN_REDRAW_BORDER_LEFT); 176 if (px == wp->xoff + wp->sx + sb_w) 177 return (SCREEN_REDRAW_BORDER_RIGHT); 178 } 179 } 180 } 181 182 /* Top/bottom borders. */ 183 if (vsplit && pane_status == PANE_STATUS_OFF && sb_w == 0) { 184 if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2) 185 return (SCREEN_REDRAW_BORDER_BOTTOM); 186 if (wp->yoff != 0 && py == wp->yoff - 1 && px > wp->sx / 2) 187 return (SCREEN_REDRAW_BORDER_TOP); 188 } else { 189 if (sb_pos == PANE_SCROLLBARS_LEFT) { 190 if ((wp->xoff - sb_w == 0 || px >= wp->xoff - sb_w) && 191 (px <= ex || (sb_w != 0 && px < ex + sb_w))) { 192 if (wp->yoff != 0 && py == wp->yoff - 1) 193 return (SCREEN_REDRAW_BORDER_TOP); 194 if (py == ey) 195 return (SCREEN_REDRAW_BORDER_BOTTOM); 196 } 197 } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */ 198 if ((wp->xoff == 0 || px >= wp->xoff) && 199 (px <= ex || (sb_w != 0 && px < ex + sb_w))) { 200 if (wp->yoff != 0 && py == wp->yoff - 1) 201 return (SCREEN_REDRAW_BORDER_TOP); 202 if (py == ey) 203 return (SCREEN_REDRAW_BORDER_BOTTOM); 204 } 205 } 206 } 207 208 /* Outside pane. */ 209 return (SCREEN_REDRAW_OUTSIDE); 210 } 211 212 /* Check if a cell is on a border. */ 213 static int 214 screen_redraw_cell_border(struct screen_redraw_ctx *ctx, u_int px, u_int py) 215 { 216 struct client *c = ctx->c; 217 struct window *w = c->session->curw->window; 218 struct window_pane *wp; 219 u_int sy = w->sy; 220 221 if (ctx->pane_status == PANE_STATUS_BOTTOM) 222 sy--; 223 224 /* Outside the window? */ 225 if (px > w->sx || py > sy) 226 return (0); 227 228 /* On the window border? */ 229 if (px == w->sx || py == sy) 230 return (1); 231 232 /* Check all the panes. */ 233 TAILQ_FOREACH(wp, &w->panes, entry) { 234 if (!window_pane_visible(wp)) 235 continue; 236 switch (screen_redraw_pane_border(ctx, wp, px, py)) { 237 case SCREEN_REDRAW_INSIDE: 238 return (0); 239 case SCREEN_REDRAW_OUTSIDE: 240 break; 241 default: 242 return (1); 243 } 244 } 245 246 return (0); 247 } 248 249 /* Work out type of border cell from surrounding cells. */ 250 static int 251 screen_redraw_type_of_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py) 252 { 253 struct client *c = ctx->c; 254 int pane_status = ctx->pane_status; 255 struct window *w = c->session->curw->window; 256 u_int sx = w->sx, sy = w->sy; 257 int borders = 0; 258 259 if (pane_status == PANE_STATUS_BOTTOM) 260 sy--; 261 262 /* Is this outside the window? */ 263 if (px > sx || py > sy) 264 return (CELL_OUTSIDE); 265 266 /* 267 * Construct a bitmask of whether the cells to the left (bit 8), right, 268 * top, and bottom (bit 1) of this cell are borders. 269 * 270 * bits 8 4 2 1: 2 271 * 8 + 4 272 * 1 273 */ 274 if (px == 0 || screen_redraw_cell_border(ctx, px - 1, py)) 275 borders |= 8; 276 if (px <= sx && screen_redraw_cell_border(ctx, px + 1, py)) 277 borders |= 4; 278 if (pane_status == PANE_STATUS_TOP) { 279 if (py != 0 && 280 screen_redraw_cell_border(ctx, px, py - 1)) 281 borders |= 2; 282 if (screen_redraw_cell_border(ctx, px, py + 1)) 283 borders |= 1; 284 } else if (pane_status == PANE_STATUS_BOTTOM) { 285 if (py == 0 || 286 screen_redraw_cell_border(ctx, px, py - 1)) 287 borders |= 2; 288 if (py != sy && 289 screen_redraw_cell_border(ctx, px, py + 1)) 290 borders |= 1; 291 } else { 292 if (py == 0 || 293 screen_redraw_cell_border(ctx, px, py - 1)) 294 borders |= 2; 295 if (screen_redraw_cell_border(ctx, px, py + 1)) 296 borders |= 1; 297 } 298 299 /* 300 * Figure out what kind of border this cell is. Only one bit set 301 * doesn't make sense (can't have a border cell with no others 302 * connected). 303 */ 304 switch (borders) { 305 case 15: /* 1111, left right top bottom */ 306 return (CELL_JOIN); 307 case 14: /* 1110, left right top */ 308 return (CELL_BOTTOMJOIN); 309 case 13: /* 1101, left right bottom */ 310 return (CELL_TOPJOIN); 311 case 12: /* 1100, left right */ 312 return (CELL_LEFTRIGHT); 313 case 11: /* 1011, left top bottom */ 314 return (CELL_RIGHTJOIN); 315 case 10: /* 1010, left top */ 316 return (CELL_BOTTOMRIGHT); 317 case 9: /* 1001, left bottom */ 318 return (CELL_TOPRIGHT); 319 case 7: /* 0111, right top bottom */ 320 return (CELL_LEFTJOIN); 321 case 6: /* 0110, right top */ 322 return (CELL_BOTTOMLEFT); 323 case 5: /* 0101, right bottom */ 324 return (CELL_TOPLEFT); 325 case 3: /* 0011, top bottom */ 326 return (CELL_TOPBOTTOM); 327 } 328 return (CELL_OUTSIDE); 329 } 330 331 /* Check if cell inside a pane. */ 332 static int 333 screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py, 334 struct window_pane **wpp) 335 { 336 struct client *c = ctx->c; 337 struct window *w = c->session->curw->window; 338 struct window_pane *wp, *active; 339 int pane_status = ctx->pane_status; 340 u_int sx = w->sx, sy = w->sy; 341 int border, pane_scrollbars = ctx->pane_scrollbars; 342 u_int right, line; 343 int sb_pos = ctx->pane_scrollbars_pos; 344 int sb_w; 345 346 *wpp = NULL; 347 348 if (px > sx || py > sy) 349 return (CELL_OUTSIDE); 350 if (px == sx || py == sy) /* window border */ 351 return (screen_redraw_type_of_cell(ctx, px, py)); 352 353 if (pane_status != PANE_STATUS_OFF) { 354 active = wp = server_client_get_pane(c); 355 do { 356 if (!window_pane_visible(wp)) 357 goto next1; 358 359 if (pane_status == PANE_STATUS_TOP) 360 line = wp->yoff - 1; 361 else 362 line = wp->yoff + sy; 363 right = wp->xoff + 2 + wp->status_size - 1; 364 365 if (py == line && px >= wp->xoff + 2 && px <= right) 366 return (CELL_INSIDE); 367 368 next1: 369 wp = TAILQ_NEXT(wp, entry); 370 if (wp == NULL) 371 wp = TAILQ_FIRST(&w->panes); 372 } while (wp != active); 373 } 374 375 active = wp = server_client_get_pane(c); 376 do { 377 if (!window_pane_visible(wp)) 378 goto next2; 379 *wpp = wp; 380 381 /* Check if CELL_SCROLLBAR */ 382 if (window_pane_show_scrollbar(wp, pane_scrollbars)) { 383 384 if (pane_status == PANE_STATUS_TOP) 385 line = wp->yoff - 1; 386 else 387 line = wp->yoff + wp->sy; 388 389 /* 390 * Check if py could lie within a scrollbar. If the 391 * pane is at the top then py == 0 to sy; if the pane 392 * is not at the top, then yoff to yoff + sy. 393 */ 394 sb_w = wp->scrollbar_style.width + 395 wp->scrollbar_style.pad; 396 if ((pane_status && py != line) || 397 (wp->yoff == 0 && py < wp->sy) || 398 (py >= wp->yoff && py < wp->yoff + wp->sy)) { 399 /* Check if px lies within a scrollbar. */ 400 if ((sb_pos == PANE_SCROLLBARS_RIGHT && 401 (px >= wp->xoff + wp->sx && 402 px < wp->xoff + wp->sx + sb_w)) || 403 (sb_pos == PANE_SCROLLBARS_LEFT && 404 (px >= wp->xoff - sb_w && 405 px < wp->xoff))) 406 return (CELL_SCROLLBAR); 407 } 408 } 409 410 /* 411 * If definitely inside, return. If not on border, skip. 412 * Otherwise work out the cell. 413 */ 414 border = screen_redraw_pane_border(ctx, wp, px, py); 415 if (border == SCREEN_REDRAW_INSIDE) 416 return (CELL_INSIDE); 417 if (border == SCREEN_REDRAW_OUTSIDE) 418 goto next2; 419 return (screen_redraw_type_of_cell(ctx, px, py)); 420 421 next2: 422 wp = TAILQ_NEXT(wp, entry); 423 if (wp == NULL) 424 wp = TAILQ_FIRST(&w->panes); 425 } while (wp != active); 426 427 return (CELL_OUTSIDE); 428 } 429 430 /* Check if the border of a particular pane. */ 431 static int 432 screen_redraw_check_is(struct screen_redraw_ctx *ctx, u_int px, u_int py, 433 struct window_pane *wp) 434 { 435 enum screen_redraw_border_type border; 436 437 border = screen_redraw_pane_border(ctx, wp, px, py); 438 if (border != SCREEN_REDRAW_INSIDE && border != SCREEN_REDRAW_OUTSIDE) 439 return (1); 440 return (0); 441 } 442 443 /* Update pane status. */ 444 static int 445 screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, 446 struct screen_redraw_ctx *rctx, enum pane_lines pane_lines) 447 { 448 struct window *w = wp->window; 449 struct grid_cell gc; 450 const char *fmt; 451 struct format_tree *ft; 452 char *expanded; 453 int pane_status = rctx->pane_status, sb_w = 0; 454 int pane_scrollbars = rctx->pane_scrollbars; 455 u_int width, i, cell_type, px, py; 456 struct screen_write_ctx ctx; 457 struct screen old; 458 459 if (window_pane_show_scrollbar(wp, pane_scrollbars)) 460 sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; 461 462 ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); 463 format_defaults(ft, c, c->session, c->session->curw, wp); 464 465 if (wp == server_client_get_pane(c)) 466 style_apply(&gc, w->options, "pane-active-border-style", ft); 467 else 468 style_apply(&gc, w->options, "pane-border-style", ft); 469 fmt = options_get_string(wp->options, "pane-border-format"); 470 471 expanded = format_expand_time(ft, fmt); 472 if (wp->sx < 4) 473 wp->status_size = width = 0; 474 else 475 wp->status_size = width = wp->sx + sb_w - 2; 476 477 memcpy(&old, &wp->status_screen, sizeof old); 478 screen_init(&wp->status_screen, width, 1, 0); 479 wp->status_screen.mode = 0; 480 481 screen_write_start(&ctx, &wp->status_screen); 482 483 for (i = 0; i < width; i++) { 484 px = wp->xoff + 2 + i; 485 if (pane_status == PANE_STATUS_TOP) 486 py = wp->yoff - 1; 487 else 488 py = wp->yoff + wp->sy; 489 cell_type = screen_redraw_type_of_cell(rctx, px, py); 490 screen_redraw_border_set(w, wp, pane_lines, cell_type, &gc); 491 screen_write_cell(&ctx, &gc); 492 } 493 gc.attr &= ~GRID_ATTR_CHARSET; 494 495 screen_write_cursormove(&ctx, 0, 0, 0); 496 format_draw(&ctx, &gc, width, expanded, NULL, 0); 497 screen_write_stop(&ctx); 498 499 free(expanded); 500 format_free(ft); 501 502 if (grid_compare(wp->status_screen.grid, old.grid) == 0) { 503 screen_free(&old); 504 return (0); 505 } 506 screen_free(&old); 507 return (1); 508 } 509 510 /* Draw pane status. */ 511 static void 512 screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) 513 { 514 struct client *c = ctx->c; 515 struct window *w = c->session->curw->window; 516 struct tty *tty = &c->tty; 517 struct window_pane *wp; 518 struct screen *s; 519 u_int i, x, width, xoff, yoff, size; 520 521 log_debug("%s: %s @%u", __func__, c->name, w->id); 522 523 TAILQ_FOREACH(wp, &w->panes, entry) { 524 if (!window_pane_visible(wp)) 525 continue; 526 s = &wp->status_screen; 527 528 size = wp->status_size; 529 if (ctx->pane_status == PANE_STATUS_TOP) 530 yoff = wp->yoff - 1; 531 else 532 yoff = wp->yoff + wp->sy; 533 xoff = wp->xoff + 2; 534 535 if (xoff + size <= ctx->ox || 536 xoff >= ctx->ox + ctx->sx || 537 yoff < ctx->oy || 538 yoff >= ctx->oy + ctx->sy) 539 continue; 540 541 if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) { 542 /* All visible. */ 543 i = 0; 544 x = xoff - ctx->ox; 545 width = size; 546 } else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) { 547 /* Both left and right not visible. */ 548 i = ctx->ox; 549 x = 0; 550 width = ctx->sx; 551 } else if (xoff < ctx->ox) { 552 /* Left not visible. */ 553 i = ctx->ox - xoff; 554 x = 0; 555 width = size - i; 556 } else { 557 /* Right not visible. */ 558 i = 0; 559 x = xoff - ctx->ox; 560 width = size - x; 561 } 562 563 if (ctx->statustop) 564 yoff += ctx->statuslines; 565 tty_draw_line(tty, s, i, 0, width, x, yoff - ctx->oy, 566 &grid_default_cell, NULL); 567 } 568 tty_cursor(tty, 0, 0); 569 } 570 571 /* Update status line and change flags if unchanged. */ 572 static uint64_t 573 screen_redraw_update(struct screen_redraw_ctx *ctx, uint64_t flags) 574 { 575 struct client *c = ctx->c; 576 struct window *w = c->session->curw->window; 577 struct window_pane *wp; 578 int redraw; 579 enum pane_lines lines; 580 581 if (c->message_string != NULL) 582 redraw = status_message_redraw(c); 583 else if (c->prompt_string != NULL) 584 redraw = status_prompt_redraw(c); 585 else 586 redraw = status_redraw(c); 587 if (!redraw && (~flags & CLIENT_REDRAWSTATUSALWAYS)) 588 flags &= ~CLIENT_REDRAWSTATUS; 589 590 if (c->overlay_draw != NULL) 591 flags |= CLIENT_REDRAWOVERLAY; 592 593 if (ctx->pane_status != PANE_STATUS_OFF) { 594 lines = ctx->pane_lines; 595 redraw = 0; 596 TAILQ_FOREACH(wp, &w->panes, entry) { 597 if (screen_redraw_make_pane_status(c, wp, ctx, lines)) 598 redraw = 1; 599 } 600 if (redraw) 601 flags |= CLIENT_REDRAWBORDERS; 602 } 603 604 return (flags); 605 } 606 607 /* Set up redraw context. */ 608 static void 609 screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx) 610 { 611 struct session *s = c->session; 612 struct options *oo = s->options; 613 struct window *w = s->curw->window; 614 struct options *wo = w->options; 615 u_int lines; 616 617 memset(ctx, 0, sizeof *ctx); 618 ctx->c = c; 619 620 lines = status_line_size(c); 621 if (c->message_string != NULL || c->prompt_string != NULL) 622 lines = (lines == 0) ? 1 : lines; 623 if (lines != 0 && options_get_number(oo, "status-position") == 0) 624 ctx->statustop = 1; 625 ctx->statuslines = lines; 626 627 ctx->pane_status = options_get_number(wo, "pane-border-status"); 628 ctx->pane_lines = options_get_number(wo, "pane-border-lines"); 629 630 ctx->pane_scrollbars = options_get_number(wo, "pane-scrollbars"); 631 ctx->pane_scrollbars_pos = options_get_number(wo, 632 "pane-scrollbars-position"); 633 634 tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); 635 636 log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name, 637 w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines, 638 ctx->statustop); 639 } 640 641 /* Redraw entire screen. */ 642 void 643 screen_redraw_screen(struct client *c) 644 { 645 struct screen_redraw_ctx ctx; 646 uint64_t flags; 647 648 if (c->flags & CLIENT_SUSPENDED) 649 return; 650 651 screen_redraw_set_context(c, &ctx); 652 653 flags = screen_redraw_update(&ctx, c->flags); 654 if ((flags & CLIENT_ALLREDRAWFLAGS) == 0) 655 return; 656 657 tty_sync_start(&c->tty); 658 tty_update_mode(&c->tty, c->tty.mode, NULL); 659 660 if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { 661 log_debug("%s: redrawing borders", c->name); 662 screen_redraw_draw_borders(&ctx); 663 if (ctx.pane_status != PANE_STATUS_OFF) 664 screen_redraw_draw_pane_status(&ctx); 665 screen_redraw_draw_pane_scrollbars(&ctx); 666 } 667 if (flags & CLIENT_REDRAWWINDOW) { 668 log_debug("%s: redrawing panes", c->name); 669 screen_redraw_draw_panes(&ctx); 670 screen_redraw_draw_pane_scrollbars(&ctx); 671 } 672 if (ctx.statuslines != 0 && 673 (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) { 674 log_debug("%s: redrawing status", c->name); 675 screen_redraw_draw_status(&ctx); 676 } 677 if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) { 678 log_debug("%s: redrawing overlay", c->name); 679 c->overlay_draw(c, c->overlay_data, &ctx); 680 } 681 682 tty_reset(&c->tty); 683 } 684 685 /* Redraw a single pane and its scrollbar. */ 686 void 687 screen_redraw_pane(struct client *c, struct window_pane *wp, 688 int redraw_scrollbar_only) 689 { 690 struct screen_redraw_ctx ctx; 691 692 if (!window_pane_visible(wp)) 693 return; 694 695 screen_redraw_set_context(c, &ctx); 696 tty_sync_start(&c->tty); 697 tty_update_mode(&c->tty, c->tty.mode, NULL); 698 699 if (!redraw_scrollbar_only) 700 screen_redraw_draw_pane(&ctx, wp); 701 702 if (window_pane_show_scrollbar(wp, ctx.pane_scrollbars)) 703 screen_redraw_draw_pane_scrollbar(&ctx, wp); 704 705 tty_reset(&c->tty); 706 } 707 708 /* Get border cell style. */ 709 static const struct grid_cell * 710 screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, 711 u_int y, struct window_pane *wp) 712 { 713 struct client *c = ctx->c; 714 struct session *s = c->session; 715 struct window *w = s->curw->window; 716 struct window_pane *active = server_client_get_pane(c); 717 struct options *oo = w->options; 718 struct format_tree *ft; 719 720 if (wp->border_gc_set) 721 return (&wp->border_gc); 722 wp->border_gc_set = 1; 723 724 ft = format_create_defaults(NULL, c, s, s->curw, wp); 725 if (screen_redraw_check_is(ctx, x, y, active)) 726 style_apply(&wp->border_gc, oo, "pane-active-border-style", ft); 727 else 728 style_apply(&wp->border_gc, oo, "pane-border-style", ft); 729 format_free(ft); 730 731 return (&wp->border_gc); 732 } 733 734 /* Draw a border cell. */ 735 static void 736 screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) 737 { 738 struct client *c = ctx->c; 739 struct session *s = c->session; 740 struct window *w = s->curw->window; 741 struct options *oo = w->options; 742 struct tty *tty = &c->tty; 743 struct format_tree *ft; 744 struct window_pane *wp, *active = server_client_get_pane(c); 745 struct grid_cell gc; 746 const struct grid_cell *tmp; 747 struct overlay_ranges r; 748 u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; 749 int arrows = 0, border, isolates; 750 751 if (c->overlay_check != NULL) { 752 c->overlay_check(c, c->overlay_data, x, y, 1, &r); 753 if (r.nx[0] + r.nx[1] == 0) 754 return; 755 } 756 757 cell_type = screen_redraw_check_cell(ctx, x, y, &wp); 758 if (cell_type == CELL_INSIDE || cell_type == CELL_SCROLLBAR) 759 return; 760 761 if (wp == NULL) { 762 if (!ctx->no_pane_gc_set) { 763 ft = format_create_defaults(NULL, c, s, s->curw, NULL); 764 memcpy(&ctx->no_pane_gc, &grid_default_cell, sizeof gc); 765 style_add(&ctx->no_pane_gc, oo, "pane-border-style", 766 ft); 767 format_free(ft); 768 ctx->no_pane_gc_set = 1; 769 } 770 memcpy(&gc, &ctx->no_pane_gc, sizeof gc); 771 } else { 772 tmp = screen_redraw_draw_borders_style(ctx, x, y, wp); 773 if (tmp == NULL) 774 return; 775 memcpy(&gc, tmp, sizeof gc); 776 777 if (server_is_marked(s, s->curw, marked_pane.wp) && 778 screen_redraw_check_is(ctx, x, y, marked_pane.wp)) 779 gc.attr ^= GRID_ATTR_REVERSE; 780 } 781 screen_redraw_border_set(w, wp, ctx->pane_lines, cell_type, &gc); 782 783 if (cell_type == CELL_TOPBOTTOM && 784 (c->flags & CLIENT_UTF8) && 785 tty_term_has(tty->term, TTYC_BIDI)) 786 isolates = 1; 787 else 788 isolates = 0; 789 790 if (ctx->statustop) 791 tty_cursor(tty, i, ctx->statuslines + j); 792 else 793 tty_cursor(tty, i, j); 794 if (isolates) 795 tty_puts(tty, END_ISOLATE); 796 797 switch (options_get_number(oo, "pane-border-indicators")) { 798 case PANE_BORDER_ARROWS: 799 case PANE_BORDER_BOTH: 800 arrows = 1; 801 break; 802 } 803 804 if (wp != NULL && arrows) { 805 border = screen_redraw_pane_border(ctx, active, x, y); 806 if (((i == wp->xoff + 1 && 807 (cell_type == CELL_LEFTRIGHT || 808 (cell_type == CELL_TOPJOIN && 809 border == SCREEN_REDRAW_BORDER_BOTTOM) || 810 (cell_type == CELL_BOTTOMJOIN && 811 border == SCREEN_REDRAW_BORDER_TOP))) || 812 (j == wp->yoff + 1 && 813 (cell_type == CELL_TOPBOTTOM || 814 (cell_type == CELL_LEFTJOIN && 815 border == SCREEN_REDRAW_BORDER_RIGHT) || 816 (cell_type == CELL_RIGHTJOIN && 817 border == SCREEN_REDRAW_BORDER_LEFT)))) && 818 screen_redraw_check_is(ctx, x, y, active)) { 819 gc.attr |= GRID_ATTR_CHARSET; 820 utf8_set(&gc.data, BORDER_MARKERS[border]); 821 } 822 } 823 824 tty_cell(tty, &gc, &grid_default_cell, NULL, NULL); 825 if (isolates) 826 tty_puts(tty, START_ISOLATE); 827 } 828 829 /* Draw the borders. */ 830 static void 831 screen_redraw_draw_borders(struct screen_redraw_ctx *ctx) 832 { 833 struct client *c = ctx->c; 834 struct session *s = c->session; 835 struct window *w = s->curw->window; 836 struct window_pane *wp; 837 u_int i, j; 838 839 log_debug("%s: %s @%u", __func__, c->name, w->id); 840 841 TAILQ_FOREACH(wp, &w->panes, entry) 842 wp->border_gc_set = 0; 843 844 for (j = 0; j < c->tty.sy - ctx->statuslines; j++) { 845 for (i = 0; i < c->tty.sx; i++) 846 screen_redraw_draw_borders_cell(ctx, i, j); 847 } 848 } 849 850 /* Draw the panes. */ 851 static void 852 screen_redraw_draw_panes(struct screen_redraw_ctx *ctx) 853 { 854 struct client *c = ctx->c; 855 struct window *w = c->session->curw->window; 856 struct window_pane *wp; 857 858 log_debug("%s: %s @%u", __func__, c->name, w->id); 859 860 TAILQ_FOREACH(wp, &w->panes, entry) { 861 if (window_pane_visible(wp)) 862 screen_redraw_draw_pane(ctx, wp); 863 } 864 } 865 866 /* Draw the status line. */ 867 static void 868 screen_redraw_draw_status(struct screen_redraw_ctx *ctx) 869 { 870 struct client *c = ctx->c; 871 struct window *w = c->session->curw->window; 872 struct tty *tty = &c->tty; 873 struct screen *s = c->status.active; 874 u_int i, y; 875 876 log_debug("%s: %s @%u", __func__, c->name, w->id); 877 878 if (ctx->statustop) 879 y = 0; 880 else 881 y = c->tty.sy - ctx->statuslines; 882 for (i = 0; i < ctx->statuslines; i++) { 883 tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i, 884 &grid_default_cell, NULL); 885 } 886 } 887 888 /* Draw one pane. */ 889 static void 890 screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) 891 { 892 struct client *c = ctx->c; 893 struct window *w = c->session->curw->window; 894 struct tty *tty = &c->tty; 895 struct screen *s = wp->screen; 896 struct colour_palette *palette = &wp->palette; 897 struct grid_cell defaults; 898 u_int i, j, top, x, y, width; 899 900 log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id); 901 902 if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx) 903 return; 904 if (ctx->statustop) 905 top = ctx->statuslines; 906 else 907 top = 0; 908 for (j = 0; j < wp->sy; j++) { 909 if (wp->yoff + j < ctx->oy || wp->yoff + j >= ctx->oy + ctx->sy) 910 continue; 911 y = top + wp->yoff + j - ctx->oy; 912 913 if (wp->xoff >= ctx->ox && 914 wp->xoff + wp->sx <= ctx->ox + ctx->sx) { 915 /* All visible. */ 916 i = 0; 917 x = wp->xoff - ctx->ox; 918 width = wp->sx; 919 } else if (wp->xoff < ctx->ox && 920 wp->xoff + wp->sx > ctx->ox + ctx->sx) { 921 /* Both left and right not visible. */ 922 i = ctx->ox; 923 x = 0; 924 width = ctx->sx; 925 } else if (wp->xoff < ctx->ox) { 926 /* Left not visible. */ 927 i = ctx->ox - wp->xoff; 928 x = 0; 929 width = wp->sx - i; 930 } else { 931 /* Right not visible. */ 932 i = 0; 933 x = wp->xoff - ctx->ox; 934 width = ctx->sx - x; 935 } 936 log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u", 937 __func__, c->name, wp->id, i, j, x, y, width); 938 939 tty_default_colours(&defaults, wp); 940 tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette); 941 } 942 943 #ifdef ENABLE_SIXEL 944 tty_draw_images(c, wp, s); 945 #endif 946 } 947 948 /* Draw the panes scrollbars */ 949 static void 950 screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *ctx) 951 { 952 struct client *c = ctx->c; 953 struct window *w = c->session->curw->window; 954 struct window_pane *wp; 955 956 log_debug("%s: %s @%u", __func__, c->name, w->id); 957 958 TAILQ_FOREACH(wp, &w->panes, entry) { 959 if (window_pane_show_scrollbar(wp, ctx->pane_scrollbars) && 960 window_pane_visible(wp)) 961 screen_redraw_draw_pane_scrollbar(ctx, wp); 962 } 963 } 964 965 /* Draw pane scrollbar. */ 966 void 967 screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *ctx, 968 struct window_pane *wp) 969 { 970 struct screen *s = wp->screen; 971 double percent_view; 972 u_int sb = ctx->pane_scrollbars, total_height, sb_h = wp->sy; 973 u_int sb_pos = ctx->pane_scrollbars_pos, slider_h, slider_y; 974 int sb_w = wp->scrollbar_style.width; 975 int sb_pad = wp->scrollbar_style.pad; 976 u_int cm_y, cm_size; 977 int xoff = wp->xoff, ox = ctx->ox; 978 int sb_x, sb_y = (int)(wp->yoff - ctx->oy); /* sb top */ 979 980 if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) { 981 if (sb == PANE_SCROLLBARS_MODAL) 982 return; 983 /* Show slider at the bottom of the scrollbar. */ 984 total_height = screen_size_y(s) + screen_hsize(s); 985 percent_view = (double)sb_h / total_height; 986 slider_h = (double)sb_h * percent_view; 987 slider_y = sb_h - slider_h; 988 } else { 989 if (TAILQ_FIRST(&wp->modes) == NULL) 990 return; 991 if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0) 992 return; 993 total_height = cm_size + sb_h; 994 percent_view = (double)sb_h / (cm_size + sb_h); 995 slider_h = (double)sb_h * percent_view; 996 slider_y = (sb_h + 1) * ((double)cm_y / total_height); 997 } 998 999 if (sb_pos == PANE_SCROLLBARS_LEFT) 1000 sb_x = xoff - sb_w - sb_pad - ox; 1001 else 1002 sb_x = xoff + wp->sx - ox; 1003 1004 if (slider_h < 1) 1005 slider_h = 1; 1006 if (slider_y >= sb_h) 1007 slider_y = sb_h - 1; 1008 1009 screen_redraw_draw_scrollbar(ctx, wp, sb_pos, sb_x, sb_y, sb_h, 1010 slider_h, slider_y); 1011 1012 /* Store current position and height of the slider */ 1013 wp->sb_slider_y = slider_y; /* top of slider y pos in scrollbar */ 1014 wp->sb_slider_h = slider_h; /* height of slider */ 1015 } 1016 1017 static void 1018 screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx, 1019 struct window_pane *wp, int sb_pos, int sb_x, int sb_y, u_int sb_h, 1020 u_int slider_h, u_int slider_y) 1021 { 1022 struct client *c = ctx->c; 1023 struct tty *tty = &c->tty; 1024 struct grid_cell gc, slgc, *gcp; 1025 struct style *sb_style = &wp->scrollbar_style; 1026 u_int i, j, imax, jmax; 1027 u_int sb_w = sb_style->width, sb_pad = sb_style->pad; 1028 int px, py, ox = ctx->ox, oy = ctx->oy; 1029 int sx = ctx->sx, sy = ctx->sy, xoff = wp->xoff; 1030 int yoff = wp->yoff; 1031 1032 if (ctx->statustop) { 1033 sb_y += ctx->statuslines; 1034 sy += ctx->statuslines; 1035 } 1036 1037 /* Set up style for slider. */ 1038 gc = sb_style->gc; 1039 memcpy(&slgc, &gc, sizeof slgc); 1040 slgc.fg = gc.bg; 1041 slgc.bg = gc.fg; 1042 1043 imax = sb_w + sb_pad; 1044 if ((int)imax + sb_x > sx) 1045 imax = sx - sb_x; 1046 jmax = sb_h; 1047 if ((int)jmax + sb_y > sy) 1048 jmax = sy - sb_y; 1049 1050 for (j = 0; j < jmax; j++) { 1051 py = sb_y + j; 1052 for (i = 0; i < imax; i++) { 1053 px = sb_x + i; 1054 if (px < xoff - ox - (int)sb_w - (int)sb_pad || 1055 px >= sx || px < 0 || 1056 py < yoff - oy - 1 || 1057 py >= sy || py < 0) 1058 continue; 1059 tty_cursor(tty, px, py); 1060 if ((sb_pos == PANE_SCROLLBARS_LEFT && 1061 i >= sb_w && i < sb_w + sb_pad) || 1062 (sb_pos == PANE_SCROLLBARS_RIGHT && 1063 i < sb_pad)) { 1064 tty_cell(tty, &grid_default_cell, 1065 &grid_default_cell, NULL, NULL); 1066 } else { 1067 if (j >= slider_y && j < slider_y + slider_h) 1068 gcp = &slgc; 1069 else 1070 gcp = &gc; 1071 tty_cell(tty, gcp, &grid_default_cell, NULL, 1072 NULL); 1073 } 1074 } 1075 } 1076 } 1077