1*dd0df669Snicm /* $OpenBSD: screen-redraw.c,v 1.103 2024/11/15 13:12:20 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 498ca8272Snicm * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5311827fbSnicm * 6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 8311827fbSnicm * copyright notice and this permission notice appear in all copies. 9311827fbSnicm * 10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17311827fbSnicm */ 18311827fbSnicm 19311827fbSnicm #include <sys/types.h> 20311827fbSnicm 212afe06f2Snicm #include <stdlib.h> 22311827fbSnicm #include <string.h> 23311827fbSnicm 24311827fbSnicm #include "tmux.h" 25311827fbSnicm 26fac4c2d1Snicm static void screen_redraw_draw_borders(struct screen_redraw_ctx *); 27fac4c2d1Snicm static void screen_redraw_draw_panes(struct screen_redraw_ctx *); 28fac4c2d1Snicm static void screen_redraw_draw_status(struct screen_redraw_ctx *); 297b470e93Snicm static void screen_redraw_draw_pane(struct screen_redraw_ctx *, 307b470e93Snicm struct window_pane *); 31236e997dSnicm static void screen_redraw_set_context(struct client *, 32236e997dSnicm struct screen_redraw_ctx *); 33d2117533Snicm static void screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *); 34d2117533Snicm static void screen_redraw_draw_scrollbar(struct screen_redraw_ctx *, 35d2117533Snicm struct window_pane *, int, int, int, u_int, u_int, u_int); 36d2117533Snicm static void screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *, 37d2117533Snicm struct window_pane *); 38311827fbSnicm 399a569e26Snicm #define START_ISOLATE "\342\201\246" 409a569e26Snicm #define END_ISOLATE "\342\201\251" 419a569e26Snicm 42dea11c3cSnicm /* Border in relation to a pane. */ 4301c0c428Snicm enum screen_redraw_border_type { 4401c0c428Snicm SCREEN_REDRAW_OUTSIDE, 4501c0c428Snicm SCREEN_REDRAW_INSIDE, 46dea11c3cSnicm SCREEN_REDRAW_BORDER_LEFT, 47dea11c3cSnicm SCREEN_REDRAW_BORDER_RIGHT, 48dea11c3cSnicm SCREEN_REDRAW_BORDER_TOP, 49dea11c3cSnicm SCREEN_REDRAW_BORDER_BOTTOM 5001c0c428Snicm }; 51dea11c3cSnicm #define BORDER_MARKERS " +,.-" 5201c0c428Snicm 53523d1daeSnicm /* Get cell border character. */ 54523d1daeSnicm static void 552c25e4b4Snicm screen_redraw_border_set(struct window *w, struct window_pane *wp, 562c25e4b4Snicm enum pane_lines pane_lines, int cell_type, struct grid_cell *gc) 57523d1daeSnicm { 58523d1daeSnicm u_int idx; 59523d1daeSnicm 602c25e4b4Snicm if (cell_type == CELL_OUTSIDE && w->fill_character != NULL) { 612c25e4b4Snicm utf8_copy(&gc->data, &w->fill_character[0]); 622c25e4b4Snicm return; 632c25e4b4Snicm } 642c25e4b4Snicm 65523d1daeSnicm switch (pane_lines) { 66523d1daeSnicm case PANE_LINES_NUMBER: 67523d1daeSnicm if (cell_type == CELL_OUTSIDE) { 68523d1daeSnicm gc->attr |= GRID_ATTR_CHARSET; 69523d1daeSnicm utf8_set(&gc->data, CELL_BORDERS[CELL_OUTSIDE]); 70523d1daeSnicm break; 71523d1daeSnicm } 72523d1daeSnicm gc->attr &= ~GRID_ATTR_CHARSET; 73523d1daeSnicm if (wp != NULL && window_pane_index(wp, &idx) == 0) 74523d1daeSnicm utf8_set(&gc->data, '0' + (idx % 10)); 75523d1daeSnicm else 76523d1daeSnicm utf8_set(&gc->data, '*'); 77523d1daeSnicm break; 78523d1daeSnicm case PANE_LINES_DOUBLE: 79523d1daeSnicm gc->attr &= ~GRID_ATTR_CHARSET; 80ccb627cdSnicm utf8_copy(&gc->data, tty_acs_double_borders(cell_type)); 81523d1daeSnicm break; 82523d1daeSnicm case PANE_LINES_HEAVY: 83523d1daeSnicm gc->attr &= ~GRID_ATTR_CHARSET; 84ccb627cdSnicm utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type)); 85523d1daeSnicm break; 86523d1daeSnicm case PANE_LINES_SIMPLE: 87523d1daeSnicm gc->attr &= ~GRID_ATTR_CHARSET; 88ccb627cdSnicm utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]); 89523d1daeSnicm break; 90523d1daeSnicm default: 91523d1daeSnicm gc->attr |= GRID_ATTR_CHARSET; 92523d1daeSnicm utf8_set(&gc->data, CELL_BORDERS[cell_type]); 93523d1daeSnicm break; 94523d1daeSnicm } 95523d1daeSnicm } 96523d1daeSnicm 9701c0c428Snicm /* Return if window has only two panes. */ 989883b791Snicm static int 9901c0c428Snicm screen_redraw_two_panes(struct window *w, int direction) 100e10f7d3dSnicm { 10101c0c428Snicm struct window_pane *wp; 10201c0c428Snicm 10301c0c428Snicm wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); 10401c0c428Snicm if (wp == NULL) 10501c0c428Snicm return (0); /* one pane */ 10601c0c428Snicm if (TAILQ_NEXT(wp, entry) != NULL) 10701c0c428Snicm return (0); /* more than two panes */ 10801c0c428Snicm if (direction == 0 && wp->xoff == 0) 109e10f7d3dSnicm return (0); 11001c0c428Snicm if (direction == 1 && wp->yoff == 0) 11101c0c428Snicm return (0); 11201c0c428Snicm return (1); 11301c0c428Snicm } 11401c0c428Snicm 11501c0c428Snicm /* Check if cell is on the border of a pane. */ 11601c0c428Snicm static enum screen_redraw_border_type 1177a4fa660Snicm screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, 1187a4fa660Snicm u_int px, u_int py) 11901c0c428Snicm { 120dea11c3cSnicm struct options *oo = wp->window->options; 12101c0c428Snicm u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy; 122d2117533Snicm int hsplit = 0, vsplit = 0, pane_status = ctx->pane_status; 123d2117533Snicm int pane_scrollbars = ctx->pane_scrollbars, sb_w = 0; 124d2117533Snicm int sb_pos = ctx->pane_scrollbars_pos; 12501c0c428Snicm 12601c0c428Snicm /* Inside pane. */ 12701c0c428Snicm if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey) 12801c0c428Snicm return (SCREEN_REDRAW_INSIDE); 129e10f7d3dSnicm 130dea11c3cSnicm /* Get pane indicator. */ 131dea11c3cSnicm switch (options_get_number(oo, "pane-border-indicators")) { 132dea11c3cSnicm case PANE_BORDER_COLOUR: 133dea11c3cSnicm case PANE_BORDER_BOTH: 134d2117533Snicm hsplit = screen_redraw_two_panes(wp->window, 0); 135d2117533Snicm vsplit = screen_redraw_two_panes(wp->window, 1); 136dea11c3cSnicm break; 137dea11c3cSnicm } 138dea11c3cSnicm 139d2117533Snicm /* Are scrollbars enabled? */ 1408b458060Snicm if (window_pane_show_scrollbar(wp, pane_scrollbars)) 141*dd0df669Snicm sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; 142d2117533Snicm 143d2117533Snicm /* 144d2117533Snicm * Left/right borders. The wp->sy / 2 test is to colour only half the 145d2117533Snicm * active window's border when there are two panes. 146d2117533Snicm */ 14701c0c428Snicm if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) { 148d2117533Snicm if (sb_pos == PANE_SCROLLBARS_LEFT) { 149d2117533Snicm if (wp->xoff - sb_w == 0 && px == wp->sx + sb_w) 150d2117533Snicm if (!hsplit || (hsplit && py <= wp->sy / 2)) 151dea11c3cSnicm return (SCREEN_REDRAW_BORDER_RIGHT); 152d2117533Snicm if (wp->xoff - sb_w != 0 && px == wp->xoff - sb_w - 1) 153d2117533Snicm if (!hsplit || (hsplit && py > wp->sy / 2)) 154dea11c3cSnicm return (SCREEN_REDRAW_BORDER_LEFT); 155d2117533Snicm } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */ 156d2117533Snicm if (wp->xoff == 0 && px == wp->sx + sb_w) 157d2117533Snicm if (!hsplit || (hsplit && py <= wp->sy / 2)) 158dea11c3cSnicm return (SCREEN_REDRAW_BORDER_RIGHT); 159d2117533Snicm if (wp->xoff != 0 && px == wp->xoff - 1) 160d2117533Snicm if (!hsplit || (hsplit && py > wp->sy / 2)) 161d2117533Snicm return (SCREEN_REDRAW_BORDER_LEFT); 16201c0c428Snicm } 163e10f7d3dSnicm } 164e10f7d3dSnicm 165e10f7d3dSnicm /* Top/bottom borders. */ 166d2117533Snicm if (vsplit && pane_status == PANE_STATUS_OFF && sb_w == 0) { 16701c0c428Snicm if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2) 168dea11c3cSnicm return (SCREEN_REDRAW_BORDER_BOTTOM); 169d2117533Snicm if (wp->yoff != 0 && py == wp->yoff - 1 && px > wp->sx / 2) 170dea11c3cSnicm return (SCREEN_REDRAW_BORDER_TOP); 17101c0c428Snicm } else { 172d2117533Snicm if (sb_pos == PANE_SCROLLBARS_LEFT) { 173d2117533Snicm if ((wp->xoff - sb_w == 0 || px >= wp->xoff - sb_w) && 174*dd0df669Snicm (px <= ex || (sb_w != 0 && px < ex + sb_w))) { 175e10f7d3dSnicm if (wp->yoff != 0 && py == wp->yoff - 1) 176dea11c3cSnicm return (SCREEN_REDRAW_BORDER_TOP); 17701c0c428Snicm if (py == ey) 178dea11c3cSnicm return (SCREEN_REDRAW_BORDER_BOTTOM); 17901c0c428Snicm } 180d2117533Snicm } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */ 181d2117533Snicm if ((wp->xoff == 0 || px >= wp->xoff) && 182*dd0df669Snicm (px <= ex || (sb_w != 0 && px < ex + sb_w))) { 18301c0c428Snicm if (wp->yoff != 0 && py == wp->yoff - 1) 184dea11c3cSnicm return (SCREEN_REDRAW_BORDER_TOP); 18501c0c428Snicm if (py == ey) 186dea11c3cSnicm return (SCREEN_REDRAW_BORDER_BOTTOM); 18701c0c428Snicm } 188e10f7d3dSnicm } 189d2117533Snicm } 190b9b22aa8Snicm 191b9b22aa8Snicm /* Outside pane. */ 19201c0c428Snicm return (SCREEN_REDRAW_OUTSIDE); 193b9b22aa8Snicm } 194b9b22aa8Snicm 19501c0c428Snicm /* Check if a cell is on a border. */ 1969883b791Snicm static int 1977a4fa660Snicm screen_redraw_cell_border(struct screen_redraw_ctx *ctx, u_int px, u_int py) 198b9b22aa8Snicm { 1997a4fa660Snicm struct client *c = ctx->c; 200b9b22aa8Snicm struct window *w = c->session->curw->window; 201b9b22aa8Snicm struct window_pane *wp; 202122218f2Snicm u_int sy = w->sy; 203122218f2Snicm 204122218f2Snicm if (ctx->pane_status == PANE_STATUS_BOTTOM) 205122218f2Snicm sy--; 20601c0c428Snicm 20701c0c428Snicm /* Outside the window? */ 208122218f2Snicm if (px > w->sx || py > sy) 20901c0c428Snicm return (0); 21001c0c428Snicm 21101c0c428Snicm /* On the window border? */ 212122218f2Snicm if (px == w->sx || py == sy) 21301c0c428Snicm return (1); 214b9b22aa8Snicm 215b9b22aa8Snicm /* Check all the panes. */ 216b9b22aa8Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 217b9b22aa8Snicm if (!window_pane_visible(wp)) 218b9b22aa8Snicm continue; 2197a4fa660Snicm switch (screen_redraw_pane_border(ctx, wp, px, py)) { 22001c0c428Snicm case SCREEN_REDRAW_INSIDE: 22101c0c428Snicm return (0); 22201c0c428Snicm case SCREEN_REDRAW_OUTSIDE: 22301c0c428Snicm break; 224dea11c3cSnicm default: 225dea11c3cSnicm return (1); 22601c0c428Snicm } 227e10f7d3dSnicm } 228e10f7d3dSnicm 229e10f7d3dSnicm return (0); 230e10f7d3dSnicm } 231a49a2704Snicm 23201c0c428Snicm /* Work out type of border cell from surrounding cells. */ 2339883b791Snicm static int 2347a4fa660Snicm screen_redraw_type_of_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py) 235311827fbSnicm { 2367a4fa660Snicm struct client *c = ctx->c; 2377a4fa660Snicm int pane_status = ctx->pane_status; 238311827fbSnicm struct window *w = c->session->curw->window; 23901c0c428Snicm u_int sx = w->sx, sy = w->sy; 24001c0c428Snicm int borders = 0; 241311827fbSnicm 242122218f2Snicm if (pane_status == PANE_STATUS_BOTTOM) 243122218f2Snicm sy--; 244122218f2Snicm 245236e997dSnicm /* Is this outside the window? */ 24685b81c84Snicm if (px > sx || py > sy) 247236e997dSnicm return (CELL_OUTSIDE); 248236e997dSnicm 249e10f7d3dSnicm /* 250d2117533Snicm * Construct a bitmask of whether the cells to the left (bit 8), right, 25101c0c428Snicm * top, and bottom (bit 1) of this cell are borders. 252d2117533Snicm * 253d2117533Snicm * bits 8 4 2 1: 2 254d2117533Snicm * 8 + 4 255d2117533Snicm * 1 256e10f7d3dSnicm */ 2577a4fa660Snicm if (px == 0 || screen_redraw_cell_border(ctx, px - 1, py)) 258e10f7d3dSnicm borders |= 8; 2597a4fa660Snicm if (px <= sx && screen_redraw_cell_border(ctx, px + 1, py)) 260e10f7d3dSnicm borders |= 4; 261c1cd4700Snicm if (pane_status == PANE_STATUS_TOP) { 26201c0c428Snicm if (py != 0 && 2637a4fa660Snicm screen_redraw_cell_border(ctx, px, py - 1)) 264bc3b19faSnicm borders |= 2; 2657a4fa660Snicm if (screen_redraw_cell_border(ctx, px, py + 1)) 266236e997dSnicm borders |= 1; 267b4edb009Snicm } else if (pane_status == PANE_STATUS_BOTTOM) { 26801c0c428Snicm if (py == 0 || 2697a4fa660Snicm screen_redraw_cell_border(ctx, px, py - 1)) 270e10f7d3dSnicm borders |= 2; 271122218f2Snicm if (py != sy && 2727a4fa660Snicm screen_redraw_cell_border(ctx, px, py + 1)) 273e10f7d3dSnicm borders |= 1; 274b4edb009Snicm } else { 275b4edb009Snicm if (py == 0 || 2767a4fa660Snicm screen_redraw_cell_border(ctx, px, py - 1)) 277b4edb009Snicm borders |= 2; 2787a4fa660Snicm if (screen_redraw_cell_border(ctx, px, py + 1)) 279b4edb009Snicm borders |= 1; 280236e997dSnicm } 281311827fbSnicm 282e10f7d3dSnicm /* 28301c0c428Snicm * Figure out what kind of border this cell is. Only one bit set 28401c0c428Snicm * doesn't make sense (can't have a border cell with no others 28501c0c428Snicm * connected). 286e10f7d3dSnicm */ 287e10f7d3dSnicm switch (borders) { 288e10f7d3dSnicm case 15: /* 1111, left right top bottom */ 289e10f7d3dSnicm return (CELL_JOIN); 290e10f7d3dSnicm case 14: /* 1110, left right top */ 291e10f7d3dSnicm return (CELL_BOTTOMJOIN); 292e10f7d3dSnicm case 13: /* 1101, left right bottom */ 293e10f7d3dSnicm return (CELL_TOPJOIN); 294e10f7d3dSnicm case 12: /* 1100, left right */ 2959a569e26Snicm return (CELL_LEFTRIGHT); 296e10f7d3dSnicm case 11: /* 1011, left top bottom */ 297e10f7d3dSnicm return (CELL_RIGHTJOIN); 298e10f7d3dSnicm case 10: /* 1010, left top */ 299e10f7d3dSnicm return (CELL_BOTTOMRIGHT); 300e10f7d3dSnicm case 9: /* 1001, left bottom */ 301e10f7d3dSnicm return (CELL_TOPRIGHT); 302e10f7d3dSnicm case 7: /* 0111, right top bottom */ 303e10f7d3dSnicm return (CELL_LEFTJOIN); 304e10f7d3dSnicm case 6: /* 0110, right top */ 305e10f7d3dSnicm return (CELL_BOTTOMLEFT); 306e10f7d3dSnicm case 5: /* 0101, right bottom */ 307e10f7d3dSnicm return (CELL_TOPLEFT); 308e10f7d3dSnicm case 3: /* 0011, top bottom */ 3099a569e26Snicm return (CELL_TOPBOTTOM); 310311827fbSnicm } 31101c0c428Snicm return (CELL_OUTSIDE); 312311827fbSnicm } 313311827fbSnicm 31401c0c428Snicm /* Check if cell inside a pane. */ 31501c0c428Snicm static int 3167a4fa660Snicm screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py, 31701c0c428Snicm struct window_pane **wpp) 31801c0c428Snicm { 3197a4fa660Snicm struct client *c = ctx->c; 32001c0c428Snicm struct window *w = c->session->curw->window; 321249e1654Snicm struct window_pane *wp, *active; 3227a4fa660Snicm int pane_status = ctx->pane_status; 323122218f2Snicm u_int sx = w->sx, sy = w->sy; 324d2117533Snicm int border, pane_scrollbars = ctx->pane_scrollbars; 32501c0c428Snicm u_int right, line; 326d2117533Snicm int sb_pos = ctx->pane_scrollbars_pos; 327*dd0df669Snicm int sb_w; 32801c0c428Snicm 32901c0c428Snicm *wpp = NULL; 33001c0c428Snicm 331122218f2Snicm if (px > sx || py > sy) 33201c0c428Snicm return (CELL_OUTSIDE); 333122218f2Snicm if (px == sx || py == sy) /* window border */ 3347a4fa660Snicm return (screen_redraw_type_of_cell(ctx, px, py)); 33501c0c428Snicm 33601c0c428Snicm if (pane_status != PANE_STATUS_OFF) { 337249e1654Snicm active = wp = server_client_get_pane(c); 33801c0c428Snicm do { 33901c0c428Snicm if (!window_pane_visible(wp)) 34001c0c428Snicm goto next1; 34101c0c428Snicm 34201c0c428Snicm if (pane_status == PANE_STATUS_TOP) 34301c0c428Snicm line = wp->yoff - 1; 34401c0c428Snicm else 345122218f2Snicm line = wp->yoff + sy; 34601c0c428Snicm right = wp->xoff + 2 + wp->status_size - 1; 34701c0c428Snicm 34801c0c428Snicm if (py == line && px >= wp->xoff + 2 && px <= right) 34901c0c428Snicm return (CELL_INSIDE); 35001c0c428Snicm 35101c0c428Snicm next1: 35201c0c428Snicm wp = TAILQ_NEXT(wp, entry); 35301c0c428Snicm if (wp == NULL) 35401c0c428Snicm wp = TAILQ_FIRST(&w->panes); 355249e1654Snicm } while (wp != active); 35601c0c428Snicm } 35701c0c428Snicm 358249e1654Snicm active = wp = server_client_get_pane(c); 35901c0c428Snicm do { 36001c0c428Snicm if (!window_pane_visible(wp)) 36101c0c428Snicm goto next2; 36201c0c428Snicm *wpp = wp; 36301c0c428Snicm 364d2117533Snicm /* Check if CELL_SCROLLBAR */ 3658b458060Snicm if (window_pane_show_scrollbar(wp, pane_scrollbars)) { 366d2117533Snicm 367d2117533Snicm if (pane_status == PANE_STATUS_TOP) 368d2117533Snicm line = wp->yoff - 1; 369d2117533Snicm else 370d2117533Snicm line = wp->yoff + wp->sy; 371d2117533Snicm 372d2117533Snicm /* 373d2117533Snicm * Check if py could lie within a scrollbar. If the 374d2117533Snicm * pane is at the top then py == 0 to sy; if the pane 375d2117533Snicm * is not at the top, then yoff to yoff + sy. 376d2117533Snicm */ 377*dd0df669Snicm sb_w = wp->scrollbar_style.width + 378*dd0df669Snicm wp->scrollbar_style.pad; 379d2117533Snicm if ((pane_status && py != line) || 380d2117533Snicm (wp->yoff == 0 && py < wp->sy) || 381d2117533Snicm (py >= wp->yoff && py < wp->yoff + wp->sy)) { 382d2117533Snicm /* Check if px lies within a scrollbar. */ 383d2117533Snicm if ((sb_pos == PANE_SCROLLBARS_RIGHT && 384d2117533Snicm (px >= wp->xoff + wp->sx && 385d2117533Snicm px < wp->xoff + wp->sx + sb_w)) || 386d2117533Snicm (sb_pos == PANE_SCROLLBARS_LEFT && 387d2117533Snicm (px >= wp->xoff - sb_w && 388d2117533Snicm px < wp->xoff))) 389d2117533Snicm return (CELL_SCROLLBAR); 390d2117533Snicm } 391d2117533Snicm } 392d2117533Snicm 39301c0c428Snicm /* 39401c0c428Snicm * If definitely inside, return. If not on border, skip. 39501c0c428Snicm * Otherwise work out the cell. 39601c0c428Snicm */ 3977a4fa660Snicm border = screen_redraw_pane_border(ctx, wp, px, py); 39801c0c428Snicm if (border == SCREEN_REDRAW_INSIDE) 39901c0c428Snicm return (CELL_INSIDE); 40001c0c428Snicm if (border == SCREEN_REDRAW_OUTSIDE) 40101c0c428Snicm goto next2; 4027a4fa660Snicm return (screen_redraw_type_of_cell(ctx, px, py)); 40301c0c428Snicm 40401c0c428Snicm next2: 40501c0c428Snicm wp = TAILQ_NEXT(wp, entry); 40601c0c428Snicm if (wp == NULL) 40701c0c428Snicm wp = TAILQ_FIRST(&w->panes); 408249e1654Snicm } while (wp != active); 40901c0c428Snicm 410a49a2704Snicm return (CELL_OUTSIDE); 411311827fbSnicm } 412311827fbSnicm 4132986c025Snicm /* Check if the border of a particular pane. */ 4149883b791Snicm static int 4157a4fa660Snicm screen_redraw_check_is(struct screen_redraw_ctx *ctx, u_int px, u_int py, 41601c0c428Snicm struct window_pane *wp) 417288ea757Snicm { 41801c0c428Snicm enum screen_redraw_border_type border; 419bc3b19faSnicm 4207a4fa660Snicm border = screen_redraw_pane_border(ctx, wp, px, py); 421dea11c3cSnicm if (border != SCREEN_REDRAW_INSIDE && border != SCREEN_REDRAW_OUTSIDE) 422288ea757Snicm return (1); 423288ea757Snicm return (0); 424288ea757Snicm } 425288ea757Snicm 426bc3b19faSnicm /* Update pane status. */ 4279883b791Snicm static int 428236e997dSnicm screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, 429ccb627cdSnicm struct screen_redraw_ctx *rctx, enum pane_lines pane_lines) 430bc3b19faSnicm { 431236e997dSnicm struct window *w = wp->window; 432bc3b19faSnicm struct grid_cell gc; 433bc3b19faSnicm const char *fmt; 434bc3b19faSnicm struct format_tree *ft; 4354ffcb1c8Snicm char *expanded; 436236e997dSnicm int pane_status = rctx->pane_status; 43737b90971Snicm u_int width, i, cell_type, px, py; 438bc3b19faSnicm struct screen_write_ctx ctx; 439be29cab1Snicm struct screen old; 440bc3b19faSnicm 4415a5ea52dSnicm ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); 44201c0c428Snicm format_defaults(ft, c, c->session, c->session->curw, wp); 44301c0c428Snicm 444249e1654Snicm if (wp == server_client_get_pane(c)) 44501c0c428Snicm style_apply(&gc, w->options, "pane-active-border-style", ft); 44601c0c428Snicm else 44701c0c428Snicm style_apply(&gc, w->options, "pane-border-style", ft); 4486df1256bSnicm fmt = options_get_string(wp->options, "pane-border-format"); 449bc3b19faSnicm 4504ffcb1c8Snicm expanded = format_expand_time(ft, fmt); 451af947beaSnicm if (wp->sx < 4) 452af947beaSnicm wp->status_size = width = 0; 453af947beaSnicm else 4544ffcb1c8Snicm wp->status_size = width = wp->sx - 4; 4554ffcb1c8Snicm 456be29cab1Snicm memcpy(&old, &wp->status_screen, sizeof old); 4574ffcb1c8Snicm screen_init(&wp->status_screen, width, 1, 0); 458bc3b19faSnicm wp->status_screen.mode = 0; 459bc3b19faSnicm 46083e83a91Snicm screen_write_start(&ctx, &wp->status_screen); 4614ffcb1c8Snicm 462236e997dSnicm for (i = 0; i < width; i++) { 463236e997dSnicm px = wp->xoff + 2 + i; 4647a4fa660Snicm if (pane_status == PANE_STATUS_TOP) 46537b90971Snicm py = wp->yoff - 1; 466236e997dSnicm else 46737b90971Snicm py = wp->yoff + wp->sy; 4687a4fa660Snicm cell_type = screen_redraw_type_of_cell(rctx, px, py); 4692c25e4b4Snicm screen_redraw_border_set(w, wp, pane_lines, cell_type, &gc); 470523d1daeSnicm screen_write_cell(&ctx, &gc); 471236e997dSnicm } 4724ffcb1c8Snicm gc.attr &= ~GRID_ATTR_CHARSET; 4734ffcb1c8Snicm 4745c6c7001Snicm screen_write_cursormove(&ctx, 0, 0, 0); 475173e8225Snicm format_draw(&ctx, &gc, width, expanded, NULL, 0); 476bc3b19faSnicm screen_write_stop(&ctx); 477bc3b19faSnicm 4784ffcb1c8Snicm free(expanded); 479bc3b19faSnicm format_free(ft); 480bc3b19faSnicm 481be29cab1Snicm if (grid_compare(wp->status_screen.grid, old.grid) == 0) { 482be29cab1Snicm screen_free(&old); 483be29cab1Snicm return (0); 484be29cab1Snicm } 485be29cab1Snicm screen_free(&old); 486be29cab1Snicm return (1); 487bc3b19faSnicm } 488bc3b19faSnicm 489bc3b19faSnicm /* Draw pane status. */ 4909883b791Snicm static void 49184cde37cSnicm screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) 492bc3b19faSnicm { 49384cde37cSnicm struct client *c = ctx->c; 494bc3b19faSnicm struct window *w = c->session->curw->window; 495bc3b19faSnicm struct tty *tty = &c->tty; 496bc3b19faSnicm struct window_pane *wp; 4977b470e93Snicm struct screen *s; 4987b470e93Snicm u_int i, x, width, xoff, yoff, size; 499bc3b19faSnicm 5007b470e93Snicm log_debug("%s: %s @%u", __func__, c->name, w->id); 5017b470e93Snicm 502bc3b19faSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 503be29cab1Snicm if (!window_pane_visible(wp)) 504be29cab1Snicm continue; 5057b470e93Snicm s = &wp->status_screen; 5067b470e93Snicm 5077b470e93Snicm size = wp->status_size; 508c1cd4700Snicm if (ctx->pane_status == PANE_STATUS_TOP) 509bc3b19faSnicm yoff = wp->yoff - 1; 510bc3b19faSnicm else 511bc3b19faSnicm yoff = wp->yoff + wp->sy; 5127b470e93Snicm xoff = wp->xoff + 2; 513bc3b19faSnicm 5147b470e93Snicm if (xoff + size <= ctx->ox || 5157b470e93Snicm xoff >= ctx->ox + ctx->sx || 5167b470e93Snicm yoff < ctx->oy || 5177b470e93Snicm yoff >= ctx->oy + ctx->sy) 5187b470e93Snicm continue; 5197b470e93Snicm 5207b470e93Snicm if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) { 5217b470e93Snicm /* All visible. */ 5227b470e93Snicm i = 0; 5237b470e93Snicm x = xoff - ctx->ox; 5247b470e93Snicm width = size; 5257b470e93Snicm } else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) { 5267b470e93Snicm /* Both left and right not visible. */ 5277b470e93Snicm i = ctx->ox; 5287b470e93Snicm x = 0; 5297b470e93Snicm width = ctx->sx; 5307b470e93Snicm } else if (xoff < ctx->ox) { 5317b470e93Snicm /* Left not visible. */ 5327b470e93Snicm i = ctx->ox - xoff; 5337b470e93Snicm x = 0; 5347b470e93Snicm width = size - i; 5357b470e93Snicm } else { 5367b470e93Snicm /* Right not visible. */ 5377b470e93Snicm i = 0; 5387b470e93Snicm x = xoff - ctx->ox; 5397b470e93Snicm width = size - x; 5407b470e93Snicm } 5417b470e93Snicm 54270c21bfbSnicm if (ctx->statustop) 54370c21bfbSnicm yoff += ctx->statuslines; 54483e83a91Snicm tty_draw_line(tty, s, i, 0, width, x, yoff - ctx->oy, 54583e83a91Snicm &grid_default_cell, NULL); 546bc3b19faSnicm } 547bc3b19faSnicm tty_cursor(tty, 0, 0); 548288ea757Snicm } 549288ea757Snicm 5501bf9bcefSnicm /* Update status line and change flags if unchanged. */ 551ab0a70a7Snicm static uint64_t 552d2117533Snicm screen_redraw_update(struct screen_redraw_ctx *ctx, uint64_t flags) 5531bf9bcefSnicm { 554d2117533Snicm struct client *c = ctx->c; 5551bf9bcefSnicm struct window *w = c->session->curw->window; 5561bf9bcefSnicm struct window_pane *wp; 557ccb627cdSnicm int redraw; 558ccb627cdSnicm enum pane_lines lines; 5591bf9bcefSnicm 5601bf9bcefSnicm if (c->message_string != NULL) 5611bf9bcefSnicm redraw = status_message_redraw(c); 5621bf9bcefSnicm else if (c->prompt_string != NULL) 5631bf9bcefSnicm redraw = status_prompt_redraw(c); 5641bf9bcefSnicm else 5651bf9bcefSnicm redraw = status_redraw(c); 5662c46c1fbSnicm if (!redraw && (~flags & CLIENT_REDRAWSTATUSALWAYS)) 5672c46c1fbSnicm flags &= ~CLIENT_REDRAWSTATUS; 5681bf9bcefSnicm 5697b61bc16Snicm if (c->overlay_draw != NULL) 5707b61bc16Snicm flags |= CLIENT_REDRAWOVERLAY; 5717b61bc16Snicm 572d2117533Snicm if (ctx->pane_status != PANE_STATUS_OFF) { 573d2117533Snicm lines = ctx->pane_lines; 5741bf9bcefSnicm redraw = 0; 5751bf9bcefSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 576d2117533Snicm if (screen_redraw_make_pane_status(c, wp, ctx, lines)) 5771bf9bcefSnicm redraw = 1; 5781bf9bcefSnicm } 5791bf9bcefSnicm if (redraw) 5802c46c1fbSnicm flags |= CLIENT_REDRAWBORDERS; 5811bf9bcefSnicm } 582d2117533Snicm 5832c46c1fbSnicm return (flags); 5841bf9bcefSnicm } 5851bf9bcefSnicm 58684cde37cSnicm /* Set up redraw context. */ 58784cde37cSnicm static void 58884cde37cSnicm screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx) 58984cde37cSnicm { 59084cde37cSnicm struct session *s = c->session; 59184cde37cSnicm struct options *oo = s->options; 59284cde37cSnicm struct window *w = s->curw->window; 59384cde37cSnicm struct options *wo = w->options; 59470c21bfbSnicm u_int lines; 59584cde37cSnicm 59684cde37cSnicm memset(ctx, 0, sizeof *ctx); 59784cde37cSnicm ctx->c = c; 59884cde37cSnicm 59970c21bfbSnicm lines = status_line_size(c); 6007b470e93Snicm if (c->message_string != NULL || c->prompt_string != NULL) 60170c21bfbSnicm lines = (lines == 0) ? 1 : lines; 60270c21bfbSnicm if (lines != 0 && options_get_number(oo, "status-position") == 0) 60370c21bfbSnicm ctx->statustop = 1; 60470c21bfbSnicm ctx->statuslines = lines; 60570c21bfbSnicm 60684cde37cSnicm ctx->pane_status = options_get_number(wo, "pane-border-status"); 607523d1daeSnicm ctx->pane_lines = options_get_number(wo, "pane-border-lines"); 60884cde37cSnicm 609d2117533Snicm ctx->pane_scrollbars = options_get_number(wo, "pane-scrollbars"); 610d2117533Snicm ctx->pane_scrollbars_pos = options_get_number(wo, 611d2117533Snicm "pane-scrollbars-position"); 612d2117533Snicm 6137b470e93Snicm tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); 6147b470e93Snicm 6157b470e93Snicm log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name, 61670c21bfbSnicm w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines, 61770c21bfbSnicm ctx->statustop); 61884cde37cSnicm } 61984cde37cSnicm 62079fc95a8Snicm /* Redraw entire screen. */ 621311827fbSnicm void 622e7808201Snicm screen_redraw_screen(struct client *c) 623311827fbSnicm { 624fac4c2d1Snicm struct screen_redraw_ctx ctx; 625ab0a70a7Snicm uint64_t flags; 626311827fbSnicm 62758dca43aSnicm if (c->flags & CLIENT_SUSPENDED) 62858dca43aSnicm return; 62958dca43aSnicm 630d2117533Snicm screen_redraw_set_context(c, &ctx); 631d2117533Snicm 632d2117533Snicm flags = screen_redraw_update(&ctx, c->flags); 63362b632c9Snicm if ((flags & CLIENT_ALLREDRAWFLAGS) == 0) 63462b632c9Snicm return; 63562b632c9Snicm 63676b45294Snicm tty_sync_start(&c->tty); 637565ae8f7Snicm tty_update_mode(&c->tty, c->tty.mode, NULL); 638a9e6426eSnicm 6392c46c1fbSnicm if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { 6406d02ba13Snicm log_debug("%s: redrawing borders", c->name); 641122218f2Snicm screen_redraw_draw_borders(&ctx); 642c1cd4700Snicm if (ctx.pane_status != PANE_STATUS_OFF) 64384cde37cSnicm screen_redraw_draw_pane_status(&ctx); 644d2117533Snicm screen_redraw_draw_pane_scrollbars(&ctx); 645e7808201Snicm } 6466d02ba13Snicm if (flags & CLIENT_REDRAWWINDOW) { 6476d02ba13Snicm log_debug("%s: redrawing panes", c->name); 648fac4c2d1Snicm screen_redraw_draw_panes(&ctx); 649d2117533Snicm screen_redraw_draw_pane_scrollbars(&ctx); 6506d02ba13Snicm } 65170c21bfbSnicm if (ctx.statuslines != 0 && 6526d02ba13Snicm (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) { 6536d02ba13Snicm log_debug("%s: redrawing status", c->name); 654fac4c2d1Snicm screen_redraw_draw_status(&ctx); 6556d02ba13Snicm } 6566d02ba13Snicm if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) { 6576d02ba13Snicm log_debug("%s: redrawing overlay", c->name); 658f38784aeSnicm c->overlay_draw(c, c->overlay_data, &ctx); 6596d02ba13Snicm } 66076b45294Snicm 66184cde37cSnicm tty_reset(&c->tty); 662311827fbSnicm } 663311827fbSnicm 664d2117533Snicm /* Redraw a single pane and its scrollbar. */ 665311827fbSnicm void 666d2117533Snicm screen_redraw_pane(struct client *c, struct window_pane *wp, 667d2117533Snicm int redraw_scrollbar_only) 668311827fbSnicm { 6697b470e93Snicm struct screen_redraw_ctx ctx; 670be5b7d79Snicm 6716bf571acSnicm if (!window_pane_visible(wp)) 6723aa2715fSnicm return; 6733aa2715fSnicm 6747b470e93Snicm screen_redraw_set_context(c, &ctx); 67576b45294Snicm tty_sync_start(&c->tty); 676565ae8f7Snicm tty_update_mode(&c->tty, c->tty.mode, NULL); 677311827fbSnicm 678d2117533Snicm if (!redraw_scrollbar_only) 6797b470e93Snicm screen_redraw_draw_pane(&ctx, wp); 68076b45294Snicm 6818b458060Snicm if (window_pane_show_scrollbar(wp, ctx.pane_scrollbars)) 682d2117533Snicm screen_redraw_draw_pane_scrollbar(&ctx, wp); 683d2117533Snicm 684dde47eafSnicm tty_reset(&c->tty); 685311827fbSnicm } 686669c539cSnicm 68701c0c428Snicm /* Get border cell style. */ 68801c0c428Snicm static const struct grid_cell * 68901c0c428Snicm screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, 69001c0c428Snicm u_int y, struct window_pane *wp) 691fac4c2d1Snicm { 692fac4c2d1Snicm struct client *c = ctx->c; 693fac4c2d1Snicm struct session *s = c->session; 694fac4c2d1Snicm struct window *w = s->curw->window; 695249e1654Snicm struct window_pane *active = server_client_get_pane(c); 69601c0c428Snicm struct options *oo = w->options; 69701c0c428Snicm struct format_tree *ft; 69801c0c428Snicm 69901c0c428Snicm if (wp->border_gc_set) 70001c0c428Snicm return (&wp->border_gc); 70101c0c428Snicm wp->border_gc_set = 1; 70201c0c428Snicm 70301c0c428Snicm ft = format_create_defaults(NULL, c, s, s->curw, wp); 7047a4fa660Snicm if (screen_redraw_check_is(ctx, x, y, active)) 705523d1daeSnicm style_apply(&wp->border_gc, oo, "pane-active-border-style", ft); 706523d1daeSnicm else 707523d1daeSnicm style_apply(&wp->border_gc, oo, "pane-border-style", ft); 70801c0c428Snicm format_free(ft); 709523d1daeSnicm 710523d1daeSnicm return (&wp->border_gc); 71101c0c428Snicm } 71201c0c428Snicm 71301c0c428Snicm /* Draw a border cell. */ 71401c0c428Snicm static void 71501c0c428Snicm screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) 71601c0c428Snicm { 71701c0c428Snicm struct client *c = ctx->c; 71801c0c428Snicm struct session *s = c->session; 7195ae1fde0Snicm struct window *w = s->curw->window; 7205ae1fde0Snicm struct options *oo = w->options; 721fac4c2d1Snicm struct tty *tty = &c->tty; 7225ae1fde0Snicm struct format_tree *ft; 723dea11c3cSnicm struct window_pane *wp, *active = server_client_get_pane(c); 724523d1daeSnicm struct grid_cell gc; 725523d1daeSnicm const struct grid_cell *tmp; 7267f16de32Snicm struct overlay_ranges r; 7277f16de32Snicm u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; 728d2117533Snicm int arrows = 0, border, isolates; 729fac4c2d1Snicm 7307f16de32Snicm if (c->overlay_check != NULL) { 7317f16de32Snicm c->overlay_check(c, c->overlay_data, x, y, 1, &r); 7327f16de32Snicm if (r.nx[0] + r.nx[1] == 0) 7330cac3d2dSnicm return; 7347f16de32Snicm } 73501c0c428Snicm 7367a4fa660Snicm cell_type = screen_redraw_check_cell(ctx, x, y, &wp); 737d2117533Snicm if (cell_type == CELL_INSIDE || cell_type == CELL_SCROLLBAR) 738fac4c2d1Snicm return; 73901c0c428Snicm 7405ae1fde0Snicm if (wp == NULL) { 7415ae1fde0Snicm if (!ctx->no_pane_gc_set) { 7425ae1fde0Snicm ft = format_create_defaults(NULL, c, s, s->curw, NULL); 7435ae1fde0Snicm memcpy(&ctx->no_pane_gc, &grid_default_cell, sizeof gc); 7445ae1fde0Snicm style_add(&ctx->no_pane_gc, oo, "pane-border-style", 7455ae1fde0Snicm ft); 7465ae1fde0Snicm format_free(ft); 7475ae1fde0Snicm ctx->no_pane_gc_set = 1; 7485ae1fde0Snicm } 7495ae1fde0Snicm memcpy(&gc, &ctx->no_pane_gc, sizeof gc); 7505ae1fde0Snicm } else { 751523d1daeSnicm tmp = screen_redraw_draw_borders_style(ctx, x, y, wp); 752523d1daeSnicm if (tmp == NULL) 75301c0c428Snicm return; 754523d1daeSnicm memcpy(&gc, tmp, sizeof gc); 755fac4c2d1Snicm 756fac4c2d1Snicm if (server_is_marked(s, s->curw, marked_pane.wp) && 7577a4fa660Snicm screen_redraw_check_is(ctx, x, y, marked_pane.wp)) 758523d1daeSnicm gc.attr ^= GRID_ATTR_REVERSE; 75901c0c428Snicm } 7602c25e4b4Snicm screen_redraw_border_set(w, wp, ctx->pane_lines, cell_type, &gc); 76101c0c428Snicm 7629a569e26Snicm if (cell_type == CELL_TOPBOTTOM && 7639a569e26Snicm (c->flags & CLIENT_UTF8) && 7649a569e26Snicm tty_term_has(tty->term, TTYC_BIDI)) 7659a569e26Snicm isolates = 1; 7669a569e26Snicm else 7679a569e26Snicm isolates = 0; 7689a569e26Snicm 76970c21bfbSnicm if (ctx->statustop) 77070c21bfbSnicm tty_cursor(tty, i, ctx->statuslines + j); 771fac4c2d1Snicm else 7727b470e93Snicm tty_cursor(tty, i, j); 7739a569e26Snicm if (isolates) 7749a569e26Snicm tty_puts(tty, END_ISOLATE); 775dea11c3cSnicm 776dea11c3cSnicm switch (options_get_number(oo, "pane-border-indicators")) { 777dea11c3cSnicm case PANE_BORDER_ARROWS: 778dea11c3cSnicm case PANE_BORDER_BOTH: 779dea11c3cSnicm arrows = 1; 780dea11c3cSnicm break; 781dea11c3cSnicm } 782dea11c3cSnicm 783dea11c3cSnicm if (wp != NULL && arrows) { 7847a4fa660Snicm border = screen_redraw_pane_border(ctx, active, x, y); 785dea11c3cSnicm if (((i == wp->xoff + 1 && 786dea11c3cSnicm (cell_type == CELL_LEFTRIGHT || 787dea11c3cSnicm (cell_type == CELL_TOPJOIN && 788dea11c3cSnicm border == SCREEN_REDRAW_BORDER_BOTTOM) || 789dea11c3cSnicm (cell_type == CELL_BOTTOMJOIN && 790dea11c3cSnicm border == SCREEN_REDRAW_BORDER_TOP))) || 791dea11c3cSnicm (j == wp->yoff + 1 && 792dea11c3cSnicm (cell_type == CELL_TOPBOTTOM || 793dea11c3cSnicm (cell_type == CELL_LEFTJOIN && 794dea11c3cSnicm border == SCREEN_REDRAW_BORDER_RIGHT) || 795dea11c3cSnicm (cell_type == CELL_RIGHTJOIN && 796dea11c3cSnicm border == SCREEN_REDRAW_BORDER_LEFT)))) && 7977a4fa660Snicm screen_redraw_check_is(ctx, x, y, active)) { 798896d80b6Snicm gc.attr |= GRID_ATTR_CHARSET; 799dea11c3cSnicm utf8_set(&gc.data, BORDER_MARKERS[border]); 800dea11c3cSnicm } 801896d80b6Snicm } 802dea11c3cSnicm 8032df6775cSnicm tty_cell(tty, &gc, &grid_default_cell, NULL, NULL); 8049a569e26Snicm if (isolates) 8059a569e26Snicm tty_puts(tty, START_ISOLATE); 806fac4c2d1Snicm } 807fac4c2d1Snicm 808890c4956Snicm /* Draw the borders. */ 8099883b791Snicm static void 810fac4c2d1Snicm screen_redraw_draw_borders(struct screen_redraw_ctx *ctx) 811890c4956Snicm { 812fac4c2d1Snicm struct client *c = ctx->c; 8132986c025Snicm struct session *s = c->session; 8142986c025Snicm struct window *w = s->curw->window; 81501c0c428Snicm struct window_pane *wp; 8167b470e93Snicm u_int i, j; 817d3ba9ba2Snicm 8187b470e93Snicm log_debug("%s: %s @%u", __func__, c->name, w->id); 819890c4956Snicm 82001c0c428Snicm TAILQ_FOREACH(wp, &w->panes, entry) 82101c0c428Snicm wp->border_gc_set = 0; 822890c4956Snicm 82301c0c428Snicm for (j = 0; j < c->tty.sy - ctx->statuslines; j++) { 82401c0c428Snicm for (i = 0; i < c->tty.sx; i++) 82501c0c428Snicm screen_redraw_draw_borders_cell(ctx, i, j); 826890c4956Snicm } 827890c4956Snicm } 828890c4956Snicm 829890c4956Snicm /* Draw the panes. */ 8309883b791Snicm static void 831fac4c2d1Snicm screen_redraw_draw_panes(struct screen_redraw_ctx *ctx) 832890c4956Snicm { 833fac4c2d1Snicm struct client *c = ctx->c; 834890c4956Snicm struct window *w = c->session->curw->window; 835890c4956Snicm struct window_pane *wp; 836d75a8bcfSnicm 8377b470e93Snicm log_debug("%s: %s @%u", __func__, c->name, w->id); 8387b470e93Snicm 839890c4956Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 84070c21bfbSnicm if (window_pane_visible(wp)) 8417b470e93Snicm screen_redraw_draw_pane(ctx, wp); 842890c4956Snicm } 843890c4956Snicm } 844890c4956Snicm 845890c4956Snicm /* Draw the status line. */ 8469883b791Snicm static void 847fac4c2d1Snicm screen_redraw_draw_status(struct screen_redraw_ctx *ctx) 848890c4956Snicm { 849fac4c2d1Snicm struct client *c = ctx->c; 8507b470e93Snicm struct window *w = c->session->curw->window; 851890c4956Snicm struct tty *tty = &c->tty; 8528e497f9eSnicm struct screen *s = c->status.active; 853d75a8bcfSnicm u_int i, y; 854890c4956Snicm 8557b470e93Snicm log_debug("%s: %s @%u", __func__, c->name, w->id); 8567b470e93Snicm 85770c21bfbSnicm if (ctx->statustop) 858d75a8bcfSnicm y = 0; 859890c4956Snicm else 86070c21bfbSnicm y = c->tty.sy - ctx->statuslines; 86183e83a91Snicm for (i = 0; i < ctx->statuslines; i++) { 86283e83a91Snicm tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i, 86383e83a91Snicm &grid_default_cell, NULL); 86483e83a91Snicm } 8657b470e93Snicm } 8667b470e93Snicm 8677b470e93Snicm /* Draw one pane. */ 8687b470e93Snicm static void 8697b470e93Snicm screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) 8707b470e93Snicm { 8717b470e93Snicm struct client *c = ctx->c; 8727b470e93Snicm struct window *w = c->session->curw->window; 8737b470e93Snicm struct tty *tty = &c->tty; 87433a1e283Snicm struct screen *s = wp->screen; 87533a1e283Snicm struct colour_palette *palette = &wp->palette; 87683e83a91Snicm struct grid_cell defaults; 8777b470e93Snicm u_int i, j, top, x, y, width; 8787b470e93Snicm 8797b470e93Snicm log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id); 8807b470e93Snicm 8817b470e93Snicm if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx) 8827b470e93Snicm return; 88370c21bfbSnicm if (ctx->statustop) 88470c21bfbSnicm top = ctx->statuslines; 8857b470e93Snicm else 8867b470e93Snicm top = 0; 8877b470e93Snicm for (j = 0; j < wp->sy; j++) { 8887b470e93Snicm if (wp->yoff + j < ctx->oy || wp->yoff + j >= ctx->oy + ctx->sy) 8897b470e93Snicm continue; 8907b470e93Snicm y = top + wp->yoff + j - ctx->oy; 8917b470e93Snicm 8927b470e93Snicm if (wp->xoff >= ctx->ox && 8937b470e93Snicm wp->xoff + wp->sx <= ctx->ox + ctx->sx) { 8947b470e93Snicm /* All visible. */ 8957b470e93Snicm i = 0; 8967b470e93Snicm x = wp->xoff - ctx->ox; 8977b470e93Snicm width = wp->sx; 8987b470e93Snicm } else if (wp->xoff < ctx->ox && 8997b470e93Snicm wp->xoff + wp->sx > ctx->ox + ctx->sx) { 9007b470e93Snicm /* Both left and right not visible. */ 9017b470e93Snicm i = ctx->ox; 9027b470e93Snicm x = 0; 9037b470e93Snicm width = ctx->sx; 9047b470e93Snicm } else if (wp->xoff < ctx->ox) { 9057b470e93Snicm /* Left not visible. */ 9067b470e93Snicm i = ctx->ox - wp->xoff; 9077b470e93Snicm x = 0; 9087b470e93Snicm width = wp->sx - i; 9097b470e93Snicm } else { 9107b470e93Snicm /* Right not visible. */ 9117b470e93Snicm i = 0; 9127b470e93Snicm x = wp->xoff - ctx->ox; 9137b470e93Snicm width = ctx->sx - x; 9147b470e93Snicm } 9157b470e93Snicm log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u", 9167b470e93Snicm __func__, c->name, wp->id, i, j, x, y, width); 9177b470e93Snicm 91883e83a91Snicm tty_default_colours(&defaults, wp); 91933a1e283Snicm tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette); 9207b470e93Snicm } 921890c4956Snicm } 922d2117533Snicm 923d2117533Snicm /* Draw the panes scrollbars */ 924d2117533Snicm static void 925d2117533Snicm screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *ctx) 926d2117533Snicm { 927d2117533Snicm struct client *c = ctx->c; 928d2117533Snicm struct window *w = c->session->curw->window; 929d2117533Snicm struct window_pane *wp; 930d2117533Snicm 931d2117533Snicm log_debug("%s: %s @%u", __func__, c->name, w->id); 932d2117533Snicm 933d2117533Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 9348b458060Snicm if (window_pane_show_scrollbar(wp, ctx->pane_scrollbars) && 9358b458060Snicm window_pane_visible(wp)) 936d2117533Snicm screen_redraw_draw_pane_scrollbar(ctx, wp); 937d2117533Snicm } 938d2117533Snicm } 939d2117533Snicm 940d2117533Snicm /* Draw pane scrollbar. */ 941d2117533Snicm void 942d2117533Snicm screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *ctx, 943d2117533Snicm struct window_pane *wp) 944d2117533Snicm { 945d2117533Snicm struct screen *s = wp->screen; 946d2117533Snicm double percent_view; 947d2117533Snicm u_int sb = ctx->pane_scrollbars, total_height, sb_h = wp->sy; 948d2117533Snicm u_int sb_pos = ctx->pane_scrollbars_pos, slider_h, slider_y; 949*dd0df669Snicm int sb_w = wp->scrollbar_style.width; 950*dd0df669Snicm int sb_pad = wp->scrollbar_style.pad; 951*dd0df669Snicm int cm_y, cm_size, xoff = wp->xoff, ox = ctx->ox; 952d2117533Snicm int sb_x, sb_y = (int)(wp->yoff - ctx->oy); /* sb top */ 953d2117533Snicm 954d2117533Snicm if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) { 955d2117533Snicm if (sb == PANE_SCROLLBARS_MODAL) 956d2117533Snicm return; 957d2117533Snicm /* Show slider at the bottom of the scrollbar. */ 958d2117533Snicm total_height = screen_size_y(s) + screen_hsize(s); 959d2117533Snicm percent_view = (double)sb_h / total_height; 960d2117533Snicm slider_h = (double)sb_h * percent_view; 961d2117533Snicm slider_y = sb_h - slider_h; 962d2117533Snicm } else { 963d2117533Snicm if (TAILQ_FIRST(&wp->modes) == NULL) 964d2117533Snicm return; 965d2117533Snicm if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0) 966d2117533Snicm return; 967d2117533Snicm total_height = cm_size + sb_h; 968d2117533Snicm percent_view = (double)sb_h / (cm_size + sb_h); 969d2117533Snicm slider_h = (double)sb_h * percent_view; 970d2117533Snicm slider_y = (sb_h + 1) * ((double)cm_y / total_height); 971d2117533Snicm } 972d2117533Snicm 973d2117533Snicm if (sb_pos == PANE_SCROLLBARS_LEFT) 974*dd0df669Snicm sb_x = xoff - sb_w - sb_pad - ox; 975d2117533Snicm else 976*dd0df669Snicm sb_x = xoff + wp->sx - ox; 977d2117533Snicm 978d2117533Snicm if (slider_h < 1) 979d2117533Snicm slider_h = 1; 980d2117533Snicm if (slider_y >= sb_h) 981d2117533Snicm slider_y = sb_h - 1; 982d2117533Snicm 983d2117533Snicm screen_redraw_draw_scrollbar(ctx, wp, sb_pos, sb_x, sb_y, sb_h, 984d2117533Snicm slider_h, slider_y); 985afeb706aSnicm 986afeb706aSnicm /* Store current position and height of the slider */ 987afeb706aSnicm wp->sb_slider_y = slider_y; /* top of slider y pos in scrollbar */ 988afeb706aSnicm wp->sb_slider_h = slider_h; /* height of slider */ 989d2117533Snicm } 990d2117533Snicm 991d2117533Snicm static void 992d2117533Snicm screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx, 993d2117533Snicm struct window_pane *wp, int sb_pos, int sb_x, int sb_y, u_int sb_h, 994d2117533Snicm u_int slider_h, u_int slider_y) 995d2117533Snicm { 996d2117533Snicm struct client *c = ctx->c; 997d2117533Snicm struct tty *tty = &c->tty; 998d2117533Snicm struct grid_cell gc, slgc, *gcp; 999*dd0df669Snicm struct style *sb_style = &wp->scrollbar_style; 1000*dd0df669Snicm u_int i, j, imax, jmax; 1001*dd0df669Snicm u_int sb_w = sb_style->width, sb_pad = sb_style->pad; 1002d2117533Snicm int px, py, ox = ctx->ox, oy = ctx->oy; 1003*dd0df669Snicm int sx = ctx->sx, sy = ctx->sy, xoff = wp->xoff; 1004*dd0df669Snicm int yoff = wp->yoff; 1005d2117533Snicm 1006d2117533Snicm /* Set up style for slider. */ 1007*dd0df669Snicm gc = sb_style->gc; 1008d2117533Snicm memcpy(&slgc, &gc, sizeof slgc); 1009*dd0df669Snicm slgc.fg = gc.bg; 1010d2117533Snicm slgc.bg = gc.fg; 1011d2117533Snicm 1012*dd0df669Snicm imax = sb_w + sb_pad; 1013*dd0df669Snicm if ((int)imax + sb_x > sx) 1014*dd0df669Snicm imax = sx - sb_x; 1015*dd0df669Snicm jmax = sb_h; 1016*dd0df669Snicm if ((int)jmax + sb_y > sy) 1017*dd0df669Snicm jmax = sy - sb_y; 1018d2117533Snicm 1019*dd0df669Snicm for (j = 0; j < jmax; j++) { 1020d2117533Snicm py = sb_y + j; 1021*dd0df669Snicm for (i = 0; i < imax; i++) { 1022*dd0df669Snicm px = sb_x + i; 1023*dd0df669Snicm if (px < xoff - ox - (int)sb_w - (int)sb_pad || 1024*dd0df669Snicm px >= sx || px < 0 || 1025*dd0df669Snicm py < yoff - oy - 1 || 1026*dd0df669Snicm py >= sy || py < 0) 1027d2117533Snicm continue; 1028d2117533Snicm tty_cursor(tty, px, py); 1029*dd0df669Snicm if ((sb_pos == PANE_SCROLLBARS_LEFT && 1030*dd0df669Snicm i >= sb_w && i < sb_w + sb_pad) || 1031*dd0df669Snicm (sb_pos == PANE_SCROLLBARS_RIGHT && 1032*dd0df669Snicm i < sb_pad)) { 1033d2117533Snicm tty_cell(tty, &grid_default_cell, 1034d2117533Snicm &grid_default_cell, NULL, NULL); 1035d2117533Snicm } else { 1036d2117533Snicm if (j >= slider_y && j < slider_y + slider_h) 1037d2117533Snicm gcp = &slgc; 1038d2117533Snicm else 1039d2117533Snicm gcp = &gc; 1040d2117533Snicm tty_cell(tty, gcp, &grid_default_cell, NULL, 1041d2117533Snicm NULL); 1042d2117533Snicm } 1043d2117533Snicm } 1044d2117533Snicm } 1045d2117533Snicm } 1046