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