xref: /openbsd-src/usr.bin/tmux/screen-redraw.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /* $OpenBSD: screen-redraw.c,v 1.12 2009/10/12 09:29:58 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicm@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_border(struct client *, u_int, u_int);
26 int	screen_redraw_check_cell(struct client *, u_int, u_int);
27 void	screen_redraw_draw_number(struct client *, struct window_pane *);
28 
29 #define CELL_INSIDE 0
30 #define CELL_LEFTRIGHT 1
31 #define CELL_TOPBOTTOM 2
32 #define CELL_TOPLEFT 3
33 #define CELL_TOPRIGHT 4
34 #define CELL_BOTTOMLEFT 5
35 #define CELL_BOTTOMRIGHT 6
36 #define CELL_TOPJOIN 7
37 #define CELL_BOTTOMJOIN 8
38 #define CELL_LEFTJOIN 9
39 #define CELL_RIGHTJOIN 10
40 #define CELL_JOIN 11
41 #define CELL_OUTSIDE 12
42 
43 /* Check if a cell is on the pane border. */
44 int
45 screen_redraw_cell_border(struct client *c, u_int px, u_int py)
46 {
47 	struct window		*w = c->session->curw->window;
48 	struct window_pane	*wp;
49 
50 	/* Check all the panes. */
51 	TAILQ_FOREACH(wp, &w->panes, entry) {
52 		if (!window_pane_visible(wp))
53 			continue;
54 
55 		/* Inside pane. */
56 		if (px >= wp->xoff && px < wp->xoff + wp->sx &&
57 		    py >= wp->yoff && py < wp->yoff + wp->sy)
58 			return (0);
59 
60 		/* Left/right borders. */
61 		if ((wp->yoff == 0 || py >= wp->yoff - 1) &&
62 		    py <= wp->yoff + wp->sy) {
63 			if (wp->xoff != 0 && px == wp->xoff - 1)
64 				return (1);
65 			if (px == wp->xoff + wp->sx)
66 				return (1);
67 		}
68 
69 		/* Top/bottom borders. */
70 		if ((wp->xoff == 0 || px >= wp->xoff - 1) &&
71 		    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 
79 	return (0);
80 }
81 
82 /* Check if cell inside a pane. */
83 int
84 screen_redraw_check_cell(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			 borders;
89 
90 	if (px > w->sx || py > w->sy)
91 		return (CELL_OUTSIDE);
92 
93 	TAILQ_FOREACH(wp, &w->panes, entry) {
94 		if (!window_pane_visible(wp))
95 			continue;
96 
97 		/* If outside the pane and its border, skip it. */
98 		if ((wp->xoff != 0 && px < wp->xoff - 1) ||
99 		    px > wp->xoff + wp->sx ||
100 		    (wp->yoff != 0 && py < wp->yoff - 1) ||
101 		    py > wp->yoff + wp->sy)
102 			continue;
103 
104 		/* If definitely inside, return so. */
105 		if (!screen_redraw_cell_border(c, px, py))
106 			return (CELL_INSIDE);
107 
108 		/*
109 		 * Construct a bitmask of whether the cells to the left (bit
110 		 * 4), right, top, and bottom (bit 1) of this cell are borders.
111 		 */
112 		borders = 0;
113 		if (px == 0 || screen_redraw_cell_border(c, px - 1, py))
114 			borders |= 8;
115 		if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py))
116 			borders |= 4;
117 		if (py == 0 || screen_redraw_cell_border(c, px, py - 1))
118 			borders |= 2;
119 		if (py <= w->sy && screen_redraw_cell_border(c, px, py + 1))
120 			borders |= 1;
121 
122 		/*
123 		 * Figure out what kind of border this cell is. Only one bit
124 		 * set doesn't make sense (can't have a border cell with no
125 		 * others connected).
126 		 */
127 		switch (borders) {
128 		case 15:	/* 1111, left right top bottom */
129 			return (CELL_JOIN);
130 		case 14:	/* 1110, left right top */
131 			return (CELL_BOTTOMJOIN);
132 		case 13:	/* 1101, left right bottom */
133 			return (CELL_TOPJOIN);
134 		case 12:	/* 1100, left right */
135 			return (CELL_TOPBOTTOM);
136 		case 11:	/* 1011, left top bottom */
137 			return (CELL_RIGHTJOIN);
138 		case 10:	/* 1010, left top */
139 			return (CELL_BOTTOMRIGHT);
140 		case 9:		/* 1001, left bottom */
141 			return (CELL_TOPRIGHT);
142 		case 7:		/* 0111, right top bottom */
143 			return (CELL_LEFTJOIN);
144 		case 6:		/* 0110, right top */
145 			return (CELL_BOTTOMLEFT);
146 		case 5:		/* 0101, right bottom */
147 			return (CELL_TOPLEFT);
148 		case 3:		/* 0011, top bottom */
149 			return (CELL_LEFTRIGHT);
150 		}
151 	}
152 
153 	return (CELL_OUTSIDE);
154 }
155 
156 /* Redraw entire screen. */
157 void
158 screen_redraw_screen(struct client *c, int status_only)
159 {
160 	struct window		*w = c->session->curw->window;
161 	struct tty		*tty = &c->tty;
162 	struct window_pane	*wp;
163 	u_int		 	 i, j, type;
164 	int		 	 status;
165 	const u_char		*base, *ptr;
166 	u_char		       	 ch, border[20];
167 
168 	/* Get status line, er, status. */
169 	if (c->message_string != NULL || c->prompt_string != NULL)
170 		status = 1;
171 	else
172 		status = options_get_number(&c->session->options, "status");
173 
174 	/* If only drawing status and it is present, don't need the rest. */
175 	if (status_only && status) {
176 		tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1);
177 		tty_reset(tty);
178 		return;
179 	}
180 
181 	/* Draw background and borders. */
182 	tty_reset(tty);
183 	strlcpy(border, " |-....--||+.", sizeof border);
184 	if (tty_term_has(tty->term, TTYC_ACSC)) {
185 		base = " xqlkmjwvtun~";
186 		for (ptr = base; *ptr != '\0'; ptr++) {
187 			if ((ch = tty_get_acs(tty, *ptr)) != '\0')
188 				border[ptr - base] = ch;
189 		}
190 		tty_putcode(tty, TTYC_SMACS);
191 	}
192 	for (j = 0; j < tty->sy - status; j++) {
193 		if (status_only && j != tty->sy - 1)
194 			continue;
195 		for (i = 0; i < tty->sx; i++) {
196 			type = screen_redraw_check_cell(c, i, j);
197 			if (type != CELL_INSIDE) {
198 				tty_cursor(tty, i, j);
199 				tty_putc(tty, border[type]);
200 			}
201 		}
202 	}
203 	tty_putcode(tty, TTYC_RMACS);
204 
205 	/* Draw the panes. */
206 	TAILQ_FOREACH(wp, &w->panes, entry) {
207 		if (!window_pane_visible(wp))
208 			continue;
209 		for (i = 0; i < wp->sy; i++) {
210 			if (status_only && wp->yoff + i != tty->sy - 1)
211 				continue;
212 			tty_draw_line(tty, wp->screen, i, wp->xoff, wp->yoff);
213 		}
214 		if (c->flags & CLIENT_IDENTIFY)
215 			screen_redraw_draw_number(c, wp);
216 	}
217 
218 	/* Draw the status line. */
219 	if (status)
220 		tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1);
221 	tty_reset(tty);
222 }
223 
224 /* Draw a single pane. */
225 void
226 screen_redraw_pane(struct client *c, struct window_pane *wp)
227 {
228 	u_int	i;
229 
230 	for (i = 0; i < wp->sy; i++)
231 		tty_draw_line(&c->tty, wp->screen, i, wp->xoff, wp->yoff);
232 	tty_reset(&c->tty);
233 }
234 
235 /* Draw number on a pane. */
236 void
237 screen_redraw_draw_number(struct client *c, struct window_pane *wp)
238 {
239 	struct tty		*tty = &c->tty;
240 	struct session		*s = c->session;
241 	struct grid_cell	 gc;
242 	u_int			 idx, px, py, i, j, xoff, yoff;
243 	int			 colour;
244 	char			 buf[16], *ptr;
245 	size_t			 len;
246 
247 	idx = window_pane_index(wp->window, wp);
248 	len = xsnprintf(buf, sizeof buf, "%u", idx);
249 
250 	if (wp->sx < len)
251 		return;
252 	colour = options_get_number(&s->options, "display-panes-colour");
253 
254 	px = wp->sx / 2; py = wp->sy / 2;
255 	xoff = wp->xoff; yoff = wp->yoff;
256 
257 	if (wp->sx < len * 6 || wp->sy < 5) {
258 		tty_cursor(tty, xoff + px - len / 2, yoff + py);
259 		memcpy(&gc, &grid_default_cell, sizeof gc);
260 		colour_set_fg(&gc, colour);
261 		tty_attributes(tty, &gc);
262 		tty_puts(tty, buf);
263 		return;
264 	}
265 
266 	px -= len * 3;
267 	py -= 2;
268 
269 	memcpy(&gc, &grid_default_cell, sizeof gc);
270 	colour_set_bg(&gc, colour);
271 	tty_attributes(tty, &gc);
272 	for (ptr = buf; *ptr != '\0'; ptr++) {
273 		if (*ptr < '0' || *ptr > '9')
274 			continue;
275 		idx = *ptr - '0';
276 
277 		for (j = 0; j < 5; j++) {
278 			for (i = px; i < px + 5; i++) {
279 				tty_cursor(tty, xoff + i, yoff + py + j);
280 				if (clock_table[idx][j][i - px])
281 					tty_putc(tty, ' ');
282 			}
283 		}
284 		px += 6;
285 	}
286 }
287