Home | History | Annotate | Line # | Download | only in dist
screen-redraw.c revision 1.1.1.16
      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 	int		 cm_y, cm_size, xoff = wp->xoff, ox = ctx->ox;
    977 	int		 sb_x, sb_y = (int)(wp->yoff - ctx->oy); /* sb top */
    978 
    979 	if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) {
    980 		if (sb == PANE_SCROLLBARS_MODAL)
    981 			return;
    982 		/* Show slider at the bottom of the scrollbar. */
    983 		total_height = screen_size_y(s) + screen_hsize(s);
    984 		percent_view = (double)sb_h / total_height;
    985 		slider_h = (double)sb_h * percent_view;
    986 		slider_y = sb_h - slider_h;
    987 	} else {
    988 		if (TAILQ_FIRST(&wp->modes) == NULL)
    989 			return;
    990 		if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0)
    991 			return;
    992 		total_height = cm_size + sb_h;
    993 		percent_view = (double)sb_h / (cm_size + sb_h);
    994 		slider_h = (double)sb_h * percent_view;
    995 		slider_y = (sb_h + 1) * ((double)cm_y / total_height);
    996 	}
    997 
    998 	if (sb_pos == PANE_SCROLLBARS_LEFT)
    999 		sb_x = xoff - sb_w - sb_pad - ox;
   1000 	else
   1001 		sb_x = xoff + wp->sx - ox;
   1002 
   1003 	if (slider_h < 1)
   1004 		slider_h = 1;
   1005 	if (slider_y >= sb_h)
   1006 		slider_y = sb_h - 1;
   1007 
   1008 	screen_redraw_draw_scrollbar(ctx, wp, sb_pos, sb_x, sb_y, sb_h,
   1009 	    slider_h, slider_y);
   1010 
   1011 	/* Store current position and height of the slider */
   1012 	wp->sb_slider_y = slider_y;  /* top of slider y pos in scrollbar */
   1013 	wp->sb_slider_h = slider_h;  /* height of slider */
   1014 }
   1015 
   1016 static void
   1017 screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
   1018     struct window_pane *wp, int sb_pos, int sb_x, int sb_y, u_int sb_h,
   1019     u_int slider_h, u_int slider_y)
   1020 {
   1021 	struct client		*c = ctx->c;
   1022 	struct tty		*tty = &c->tty;
   1023 	struct grid_cell	 gc, slgc, *gcp;
   1024 	struct style		*sb_style = &wp->scrollbar_style;
   1025 	u_int			 i, j, imax, jmax;
   1026 	u_int			 sb_w = sb_style->width, sb_pad = sb_style->pad;
   1027 	int			 px, py, ox = ctx->ox, oy = ctx->oy;
   1028 	int			 sx = ctx->sx, sy = ctx->sy, xoff = wp->xoff;
   1029 	int			 yoff = wp->yoff;
   1030 
   1031 	if (ctx->statustop) {
   1032 		sb_y += ctx->statuslines;
   1033 		sy += ctx->statuslines;
   1034 	}
   1035 
   1036 	/* Set up style for slider. */
   1037 	gc = sb_style->gc;
   1038 	memcpy(&slgc, &gc, sizeof slgc);
   1039 	slgc.fg = gc.bg;
   1040 	slgc.bg = gc.fg;
   1041 
   1042 	imax = sb_w + sb_pad;
   1043 	if ((int)imax + sb_x > sx)
   1044 		imax = sx - sb_x;
   1045 	jmax = sb_h;
   1046 	if ((int)jmax + sb_y > sy)
   1047 		jmax = sy - sb_y;
   1048 
   1049 	for (j = 0; j < jmax; j++) {
   1050 		py = sb_y + j;
   1051 		for (i = 0; i < imax; i++) {
   1052 			px = sb_x + i;
   1053 			if (px < xoff - ox - (int)sb_w - (int)sb_pad ||
   1054 			    px >= sx || px < 0 ||
   1055 			    py < yoff - oy - 1 ||
   1056 			    py >= sy || py < 0)
   1057 				continue;
   1058 			tty_cursor(tty, px, py);
   1059 			if ((sb_pos == PANE_SCROLLBARS_LEFT &&
   1060 			    i >= sb_w && i < sb_w + sb_pad) ||
   1061 			    (sb_pos == PANE_SCROLLBARS_RIGHT &&
   1062 			     i < sb_pad)) {
   1063 				tty_cell(tty, &grid_default_cell,
   1064 				    &grid_default_cell, NULL, NULL);
   1065 			} else {
   1066 				if (j >= slider_y && j < slider_y + slider_h)
   1067 					gcp = &slgc;
   1068 				else
   1069 					gcp = &gc;
   1070 				tty_cell(tty, gcp, &grid_default_cell, NULL,
   1071 				    NULL);
   1072 			}
   1073 		}
   1074 	}
   1075 }
   1076