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