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