Home | History | Annotate | Line # | Download | only in dist
screen-redraw.c revision 1.1.1.2.8.1
      1 /* Id */
      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 int	screen_redraw_cell_border1(struct window_pane *, u_int, u_int);
     26 int	screen_redraw_cell_border(struct client *, u_int, u_int);
     27 int	screen_redraw_check_cell(struct client *, u_int, u_int,
     28 	    struct window_pane **);
     29 int	screen_redraw_check_active(u_int, u_int, int, struct window *,
     30 	    struct window_pane *);
     31 
     32 void	screen_redraw_draw_borders(struct client *, int, u_int);
     33 void	screen_redraw_draw_panes(struct client *, u_int);
     34 void	screen_redraw_draw_status(struct client *, u_int);
     35 void	screen_redraw_draw_number(struct client *, struct window_pane *);
     36 
     37 #define CELL_INSIDE 0
     38 #define CELL_LEFTRIGHT 1
     39 #define CELL_TOPBOTTOM 2
     40 #define CELL_TOPLEFT 3
     41 #define CELL_TOPRIGHT 4
     42 #define CELL_BOTTOMLEFT 5
     43 #define CELL_BOTTOMRIGHT 6
     44 #define CELL_TOPJOIN 7
     45 #define CELL_BOTTOMJOIN 8
     46 #define CELL_LEFTJOIN 9
     47 #define CELL_RIGHTJOIN 10
     48 #define CELL_JOIN 11
     49 #define CELL_OUTSIDE 12
     50 
     51 #define CELL_BORDERS " xqlkmjwvtun~"
     52 
     53 /* Check if cell is on the border of a particular pane. */
     54 int
     55 screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py)
     56 {
     57 	/* Inside pane. */
     58 	if (px >= wp->xoff && px < wp->xoff + wp->sx &&
     59 	    py >= wp->yoff && py < wp->yoff + wp->sy)
     60 		return (0);
     61 
     62 	/* Left/right borders. */
     63 	if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= wp->yoff + wp->sy) {
     64 		if (wp->xoff != 0 && px == wp->xoff - 1)
     65 			return (1);
     66 		if (px == wp->xoff + wp->sx)
     67 			return (1);
     68 	}
     69 
     70 	/* Top/bottom borders. */
     71 	if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= wp->xoff + wp->sx) {
     72 		if (wp->yoff != 0 && py == wp->yoff - 1)
     73 			return (1);
     74 		if (py == wp->yoff + wp->sy)
     75 			return (1);
     76 	}
     77 
     78 	/* Outside pane. */
     79 	return (-1);
     80 }
     81 
     82 /* Check if a cell is on the pane border. */
     83 int
     84 screen_redraw_cell_border(struct client *c, u_int px, u_int py)
     85 {
     86 	struct window		*w = c->session->curw->window;
     87 	struct window_pane	*wp;
     88 	int			 retval;
     89 
     90 	/* Check all the panes. */
     91 	TAILQ_FOREACH(wp, &w->panes, entry) {
     92 		if (!window_pane_visible(wp))
     93 			continue;
     94 		if ((retval = screen_redraw_cell_border1(wp, px, py)) != -1)
     95 			return (retval);
     96 	}
     97 
     98 	return (0);
     99 }
    100 
    101 /* Check if cell inside a pane. */
    102 int
    103 screen_redraw_check_cell(struct client *c, u_int px, u_int py,
    104     struct window_pane **wpp)
    105 {
    106 	struct window		*w = c->session->curw->window;
    107 	struct window_pane	*wp;
    108 	int			 borders;
    109 
    110 	if (px > w->sx || py > w->sy)
    111 		return (CELL_OUTSIDE);
    112 
    113 	TAILQ_FOREACH(wp, &w->panes, entry) {
    114 		if (!window_pane_visible(wp))
    115 			continue;
    116 		*wpp = wp;
    117 
    118 		/* If outside the pane and its border, skip it. */
    119 		if ((wp->xoff != 0 && px < wp->xoff - 1) ||
    120 		    px > wp->xoff + wp->sx ||
    121 		    (wp->yoff != 0 && py < wp->yoff - 1) ||
    122 		    py > wp->yoff + wp->sy)
    123 			continue;
    124 
    125 		/* If definitely inside, return so. */
    126 		if (!screen_redraw_cell_border(c, px, py))
    127 			return (CELL_INSIDE);
    128 
    129 		/*
    130 		 * Construct a bitmask of whether the cells to the left (bit
    131 		 * 4), right, top, and bottom (bit 1) of this cell are borders.
    132 		 */
    133 		borders = 0;
    134 		if (px == 0 || screen_redraw_cell_border(c, px - 1, py))
    135 			borders |= 8;
    136 		if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py))
    137 			borders |= 4;
    138 		if (py == 0 || screen_redraw_cell_border(c, px, py - 1))
    139 			borders |= 2;
    140 		if (py <= w->sy && screen_redraw_cell_border(c, px, py + 1))
    141 			borders |= 1;
    142 
    143 		/*
    144 		 * Figure out what kind of border this cell is. Only one bit
    145 		 * set doesn't make sense (can't have a border cell with no
    146 		 * others connected).
    147 		 */
    148 		switch (borders) {
    149 		case 15:	/* 1111, left right top bottom */
    150 			return (CELL_JOIN);
    151 		case 14:	/* 1110, left right top */
    152 			return (CELL_BOTTOMJOIN);
    153 		case 13:	/* 1101, left right bottom */
    154 			return (CELL_TOPJOIN);
    155 		case 12:	/* 1100, left right */
    156 			return (CELL_TOPBOTTOM);
    157 		case 11:	/* 1011, left top bottom */
    158 			return (CELL_RIGHTJOIN);
    159 		case 10:	/* 1010, left top */
    160 			return (CELL_BOTTOMRIGHT);
    161 		case 9:		/* 1001, left bottom */
    162 			return (CELL_TOPRIGHT);
    163 		case 7:		/* 0111, right top bottom */
    164 			return (CELL_LEFTJOIN);
    165 		case 6:		/* 0110, right top */
    166 			return (CELL_BOTTOMLEFT);
    167 		case 5:		/* 0101, right bottom */
    168 			return (CELL_TOPLEFT);
    169 		case 3:		/* 0011, top bottom */
    170 			return (CELL_LEFTRIGHT);
    171 		}
    172 	}
    173 
    174 	*wpp = NULL;
    175 	return (CELL_OUTSIDE);
    176 }
    177 
    178 /* Check active pane indicator. */
    179 int
    180 screen_redraw_check_active(u_int px, u_int py, int type, struct window *w,
    181     struct window_pane *wp)
    182 {
    183 	/* Is this off the active pane border? */
    184 	if (screen_redraw_cell_border1(w->active, px, py) != 1)
    185 		return (0);
    186 
    187 	/* If there are more than two panes, that's enough. */
    188 	if (window_count_panes(w) != 2)
    189 		return (1);
    190 
    191 	/* Else if the cell is not a border cell, forget it. */
    192 	if (wp == NULL || (type == CELL_OUTSIDE || type == CELL_INSIDE))
    193 		return (1);
    194 
    195 	/* Check if the pane covers the whole width. */
    196 	if (wp->xoff == 0 && wp->sx == w->sx) {
    197 		/* This can either be the top pane or the bottom pane. */
    198 		if (wp->yoff == 0) { /* top pane */
    199 			if (wp == w->active)
    200 				return (px <= wp->sx / 2);
    201 			return (px > wp->sx / 2);
    202 		}
    203 		return (0);
    204 	}
    205 
    206 	/* Check if the pane covers the whole height. */
    207 	if (wp->yoff == 0 && wp->sy == w->sy) {
    208 		/* This can either be the left pane or the right pane. */
    209 		if (wp->xoff == 0) { /* left pane */
    210 			if (wp == w->active)
    211 				return (py <= wp->sy / 2);
    212 			return (py > wp->sy / 2);
    213 		}
    214 		return (0);
    215 	}
    216 
    217 	return (type);
    218 }
    219 
    220 /* Redraw entire screen. */
    221 void
    222 screen_redraw_screen(struct client *c, int draw_panes, int draw_status,
    223     int draw_borders)
    224 {
    225 	struct options	*oo = &c->session->options;
    226 	struct tty	*tty = &c->tty;
    227 	u_int		 top;
    228 	int	 	 status, spos;
    229 
    230 	/* Suspended clients should not be updated. */
    231 	if (c->flags & CLIENT_SUSPENDED)
    232 		return;
    233 
    234 	/* Get status line, er, status. */
    235 	spos = options_get_number(oo, "status-position");
    236 	if (c->message_string != NULL || c->prompt_string != NULL)
    237 		status = 1;
    238 	else
    239 		status = options_get_number(oo, "status");
    240 	top = 0;
    241 	if (status && spos == 0)
    242 		top = 1;
    243 	if (!status)
    244 		draw_status = 0;
    245 
    246 	if (draw_borders)
    247 		screen_redraw_draw_borders(c, status, top);
    248 	if (draw_panes)
    249 		screen_redraw_draw_panes(c, top);
    250 	if (draw_status)
    251 		screen_redraw_draw_status(c, top);
    252 	tty_reset(tty);
    253 }
    254 
    255 /* Draw a single pane. */
    256 void
    257 screen_redraw_pane(struct client *c, struct window_pane *wp)
    258 {
    259 	u_int	i, yoff;
    260 
    261 	if (!window_pane_visible(wp))
    262 		return;
    263 
    264 	yoff = wp->yoff;
    265 	if (status_at_line(c) == 0)
    266 		yoff++;
    267 
    268 	for (i = 0; i < wp->sy; i++)
    269 		tty_draw_line(&c->tty, wp->screen, i, wp->xoff, yoff);
    270 	tty_reset(&c->tty);
    271 }
    272 
    273 /* Draw the borders. */
    274 void
    275 screen_redraw_draw_borders(struct client *c, int status, u_int top)
    276 {
    277 	struct window		*w = c->session->curw->window;
    278 	struct options		*oo = &c->session->options;
    279 	struct tty		*tty = &c->tty;
    280 	struct window_pane	*wp;
    281 	struct grid_cell	 active_gc, other_gc;
    282 	u_int		 	 i, j, type;
    283 
    284 	style_apply(&other_gc, oo, "pane-border-style");
    285 	style_apply(&active_gc, oo, "pane-active-border-style");
    286 	active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET;
    287 
    288 	for (j = 0; j < tty->sy - status; j++) {
    289 		for (i = 0; i < tty->sx; i++) {
    290 			type = screen_redraw_check_cell(c, i, j, &wp);
    291 			if (type == CELL_INSIDE)
    292 				continue;
    293 			if (screen_redraw_check_active(i, j, type, w, wp))
    294 				tty_attributes(tty, &active_gc);
    295 			else
    296 				tty_attributes(tty, &other_gc);
    297 			tty_cursor(tty, i, top + j);
    298 			tty_putc(tty, CELL_BORDERS[type]);
    299 		}
    300 	}
    301 }
    302 
    303 /* Draw the panes. */
    304 void
    305 screen_redraw_draw_panes(struct client *c, u_int top)
    306 {
    307 	struct window		*w = c->session->curw->window;
    308 	struct tty		*tty = &c->tty;
    309 	struct window_pane	*wp;
    310 	struct screen		*s;
    311 	u_int		 	 i;
    312 
    313 	TAILQ_FOREACH(wp, &w->panes, entry) {
    314 		if (!window_pane_visible(wp))
    315 			continue;
    316 		s = wp->screen;
    317 		for (i = 0; i < wp->sy; i++)
    318 			tty_draw_line(tty, s, i, wp->xoff, top + wp->yoff);
    319 		if (c->flags & CLIENT_IDENTIFY)
    320 			screen_redraw_draw_number(c, wp);
    321 	}
    322 }
    323 
    324 /* Draw the status line. */
    325 void
    326 screen_redraw_draw_status(struct client *c, u_int top)
    327 {
    328 	struct tty	*tty = &c->tty;
    329 
    330 	if (top)
    331 		tty_draw_line(tty, &c->status, 0, 0, 0);
    332 	else
    333 		tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1);
    334 }
    335 
    336 /* Draw number on a pane. */
    337 void
    338 screen_redraw_draw_number(struct client *c, struct window_pane *wp)
    339 {
    340 	struct tty		*tty = &c->tty;
    341 	struct session		*s = c->session;
    342 	struct options		*oo = &s->options;
    343 	struct window		*w = wp->window;
    344 	struct grid_cell	 gc;
    345 	u_int			 idx, px, py, i, j, xoff, yoff;
    346 	int			 colour, active_colour;
    347 	char			 buf[16], *ptr;
    348 	size_t			 len;
    349 
    350 	if (window_pane_index(wp, &idx) != 0)
    351 		fatalx("index not found");
    352 	len = xsnprintf(buf, sizeof buf, "%u", idx);
    353 
    354 	if (wp->sx < len)
    355 		return;
    356 	colour = options_get_number(oo, "display-panes-colour");
    357 	active_colour = options_get_number(oo, "display-panes-active-colour");
    358 
    359 	px = wp->sx / 2; py = wp->sy / 2;
    360 	xoff = wp->xoff; yoff = wp->yoff;
    361 
    362 	if (wp->sx < len * 6 || wp->sy < 5) {
    363 		tty_cursor(tty, xoff + px - len / 2, yoff + py);
    364 		goto draw_text;
    365 	}
    366 
    367 	px -= len * 3;
    368 	py -= 2;
    369 
    370 	memcpy(&gc, &grid_default_cell, sizeof gc);
    371 	if (w->active == wp)
    372 		colour_set_bg(&gc, active_colour);
    373 	else
    374 		colour_set_bg(&gc, colour);
    375 	tty_attributes(tty, &gc);
    376 	for (ptr = buf; *ptr != '\0'; ptr++) {
    377 		if (*ptr < '0' || *ptr > '9')
    378 			continue;
    379 		idx = *ptr - '0';
    380 
    381 		for (j = 0; j < 5; j++) {
    382 			for (i = px; i < px + 5; i++) {
    383 				tty_cursor(tty, xoff + i, yoff + py + j);
    384 				if (clock_table[idx][j][i - px])
    385 					tty_putc(tty, ' ');
    386 			}
    387 		}
    388 		px += 6;
    389 	}
    390 
    391 	len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy);
    392 	if (wp->sx < len || wp->sy < 6)
    393 		return;
    394 	tty_cursor(tty, xoff + wp->sx - len, yoff);
    395 
    396 draw_text:
    397 	memcpy(&gc, &grid_default_cell, sizeof gc);
    398 	if (w->active == wp)
    399 		colour_set_fg(&gc, active_colour);
    400 	else
    401 		colour_set_fg(&gc, colour);
    402 	tty_attributes(tty, &gc);
    403 	tty_puts(tty, buf);
    404 
    405 	tty_cursor(tty, 0, 0);
    406 }
    407