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