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