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