xref: /openbsd-src/usr.bin/tmux/screen.c (revision 78d51a749ccd7ebbcc40a3da6238cd32c6cc55cf)
1*78d51a74Snicm /* $OpenBSD: screen.c,v 1.14 2010/02/06 17:35:01 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
4311827fbSnicm  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm 
2114e0d2d2Snicm #include <stdlib.h>
22311827fbSnicm #include <string.h>
234d6bb6c6Snicm #include <vis.h>
24311827fbSnicm 
25311827fbSnicm #include "tmux.h"
26311827fbSnicm 
27311827fbSnicm void	screen_resize_x(struct screen *, u_int);
28311827fbSnicm void	screen_resize_y(struct screen *, u_int);
29311827fbSnicm 
30311827fbSnicm /* Create a new screen. */
31311827fbSnicm void
32311827fbSnicm screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
33311827fbSnicm {
34311827fbSnicm 	s->grid = grid_create(sx, sy, hlimit);
35311827fbSnicm 
36311827fbSnicm 	s->title = xstrdup("");
37311827fbSnicm 
3814e0d2d2Snicm 	s->tabs = NULL;
3914e0d2d2Snicm 
40311827fbSnicm 	screen_reinit(s);
41311827fbSnicm }
42311827fbSnicm 
43311827fbSnicm /* Reinitialise screen. */
44311827fbSnicm void
45311827fbSnicm screen_reinit(struct screen *s)
46311827fbSnicm {
47311827fbSnicm 	s->cx = 0;
48311827fbSnicm 	s->cy = 0;
49311827fbSnicm 
50311827fbSnicm 	s->rupper = 0;
51311827fbSnicm 	s->rlower = screen_size_y(s) - 1;
52311827fbSnicm 
53311827fbSnicm 	s->mode = MODE_CURSOR;
54311827fbSnicm 
5514e0d2d2Snicm 	screen_reset_tabs(s);
5614e0d2d2Snicm 
5709b0fd37Snicm 	grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy);
58311827fbSnicm 
59311827fbSnicm 	screen_clear_selection(s);
60311827fbSnicm }
61311827fbSnicm 
62311827fbSnicm /* Destroy a screen. */
63311827fbSnicm void
64311827fbSnicm screen_free(struct screen *s)
65311827fbSnicm {
6624da6ca1Snicm 	if (s->tabs != NULL)
6724da6ca1Snicm 		xfree(s->tabs);
68311827fbSnicm 	xfree(s->title);
69311827fbSnicm 	grid_destroy(s->grid);
70311827fbSnicm }
71311827fbSnicm 
7214e0d2d2Snicm /* Reset tabs to default, eight spaces apart. */
7314e0d2d2Snicm void
7414e0d2d2Snicm screen_reset_tabs(struct screen *s)
7514e0d2d2Snicm {
7614e0d2d2Snicm 	u_int	i;
7714e0d2d2Snicm 
7814e0d2d2Snicm 	if (s->tabs != NULL)
7914e0d2d2Snicm 		xfree(s->tabs);
8014e0d2d2Snicm 
8114e0d2d2Snicm 	if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL)
8214e0d2d2Snicm 		fatal("bit_alloc failed");
8314e0d2d2Snicm 	for (i = 8; i < screen_size_x(s); i += 8)
8414e0d2d2Snicm 		bit_set(s->tabs, i);
8514e0d2d2Snicm }
8614e0d2d2Snicm 
87311827fbSnicm /* Set screen title. */
88311827fbSnicm void
89311827fbSnicm screen_set_title(struct screen *s, const char *title)
90311827fbSnicm {
914d6bb6c6Snicm 	char	tmp[BUFSIZ];
924d6bb6c6Snicm 
934d6bb6c6Snicm 	strnvis(tmp, title, sizeof tmp, VIS_OCTAL|VIS_TAB|VIS_NL);
944d6bb6c6Snicm 
95311827fbSnicm 	xfree(s->title);
964d6bb6c6Snicm 	s->title = xstrdup(tmp);
97311827fbSnicm }
98311827fbSnicm 
99311827fbSnicm /* Resize screen. */
100311827fbSnicm void
101311827fbSnicm screen_resize(struct screen *s, u_int sx, u_int sy)
102311827fbSnicm {
103311827fbSnicm 	if (sx < 1)
104311827fbSnicm 		sx = 1;
105311827fbSnicm 	if (sy < 1)
106311827fbSnicm 		sy = 1;
107311827fbSnicm 
10814e0d2d2Snicm 	if (sx != screen_size_x(s)) {
109311827fbSnicm 		screen_resize_x(s, sx);
11014e0d2d2Snicm 
11114e0d2d2Snicm 		/*
11214e0d2d2Snicm 		 * It is unclear what should happen to tabs on resize. xterm
11314e0d2d2Snicm 		 * seems to try and maintain them, rxvt resets them. Resetting
11414e0d2d2Snicm 		 * is simpler and more reliable so let's do that.
11514e0d2d2Snicm 		 */
11614e0d2d2Snicm 		screen_reset_tabs(s);
11714e0d2d2Snicm 	}
11814e0d2d2Snicm 
119311827fbSnicm 	if (sy != screen_size_y(s))
120311827fbSnicm 		screen_resize_y(s, sy);
121311827fbSnicm }
122311827fbSnicm 
123311827fbSnicm void
124311827fbSnicm screen_resize_x(struct screen *s, u_int sx)
125311827fbSnicm {
126311827fbSnicm 	struct grid		*gd = s->grid;
127311827fbSnicm 
128311827fbSnicm 	if (sx == 0)
129311827fbSnicm 		fatalx("zero size");
130311827fbSnicm 
131311827fbSnicm 	/*
132e8739adcSnicm 	 * Treat resizing horizontally simply: just ensure the cursor is
133e8739adcSnicm 	 * on-screen and change the size. Don't bother to truncate any lines -
134e8739adcSnicm 	 * then the data should be accessible if the size is then incrased.
135e8739adcSnicm 	 *
136e8739adcSnicm 	 * The only potential wrinkle is if UTF-8 double-width characters are
137e8739adcSnicm 	 * left in the last column, but UTF-8 terminals should deal with this
138e8739adcSnicm 	 * sanely.
139311827fbSnicm 	 */
140311827fbSnicm 	if (s->cx >= sx)
141311827fbSnicm 		s->cx = sx - 1;
142311827fbSnicm 	gd->sx = sx;
143311827fbSnicm }
144311827fbSnicm 
145311827fbSnicm void
146311827fbSnicm screen_resize_y(struct screen *s, u_int sy)
147311827fbSnicm {
148311827fbSnicm 	struct grid	*gd = s->grid;
149516fb81dSnicm 	u_int		 needed, available, oldy, i;
150311827fbSnicm 
151311827fbSnicm 	if (sy == 0)
152311827fbSnicm 		fatalx("zero size");
153516fb81dSnicm 	oldy = screen_size_y(s);
154516fb81dSnicm 
155516fb81dSnicm 	/*
156516fb81dSnicm 	 * When resizing:
157516fb81dSnicm 	 *
158516fb81dSnicm 	 * If the height is decreasing, delete lines from the bottom until
159516fb81dSnicm 	 * hitting the cursor, then push lines from the top into the history.
160516fb81dSnicm 	 *
161516fb81dSnicm 	 * When increasing, pull as many lines as possible from the history to
162516fb81dSnicm 	 * the top, then fill the remaining with blanks at the bottom.
163516fb81dSnicm 	 */
164311827fbSnicm 
165311827fbSnicm 	/* Size decreasing. */
166516fb81dSnicm 	if (sy < oldy) {
167516fb81dSnicm 		needed = oldy - sy;
168311827fbSnicm 
169516fb81dSnicm 		/* Delete as many lines as possible from the bottom. */
170516fb81dSnicm 		available = oldy - 1 - s->cy;
171516fb81dSnicm 		if (available > 0) {
172516fb81dSnicm 			if (available > needed)
173516fb81dSnicm 				available = needed;
174516fb81dSnicm 			grid_view_delete_lines(gd, oldy - available, available);
175516fb81dSnicm 		}
176516fb81dSnicm 		needed -= available;
177516fb81dSnicm 
178311827fbSnicm 		/*
179953d773cSnicm 		 * Now just increase the history size, if possible, to take
180953d773cSnicm 		 * over the lines which are left. If history is off, delete
181953d773cSnicm 		 * lines from the top.
182953d773cSnicm 		 *
183953d773cSnicm 		 * XXX Should apply history limit?
184311827fbSnicm 		 */
185953d773cSnicm 		available = s->cy;
186953d773cSnicm 		if (gd->flags & GRID_HISTORY)
187516fb81dSnicm 			gd->hsize += needed;
1887e4d386bSnicm 		else if (needed > 0 && available > 0) {
189953d773cSnicm 			if (available > needed)
190953d773cSnicm 				available = needed;
191953d773cSnicm 			grid_view_delete_lines(gd, 0, available);
192953d773cSnicm 		}
193516fb81dSnicm 		s->cy -= needed;
194311827fbSnicm 	}
195311827fbSnicm 
196311827fbSnicm 	/* Resize line arrays. */
1979554a682Snicm 	gd->linedata = xrealloc(
1989554a682Snicm 	    gd->linedata, gd->hsize + sy, sizeof *gd->linedata);
199311827fbSnicm 
200311827fbSnicm 	/* Size increasing. */
201516fb81dSnicm 	if (sy > oldy) {
202516fb81dSnicm 		needed = sy - oldy;
203516fb81dSnicm 
204953d773cSnicm 		/*
205953d773cSnicm 		 * Try to pull as much as possible out of the history, if is
206953d773cSnicm 		 * is enabled.
207953d773cSnicm 		 */
208516fb81dSnicm 		available = gd->hsize;
209953d773cSnicm 		if (gd->flags & GRID_HISTORY && available > 0) {
210516fb81dSnicm 			if (available > needed)
211516fb81dSnicm 				available = needed;
212516fb81dSnicm 			gd->hsize -= available;
213516fb81dSnicm 			s->cy += available;
214953d773cSnicm 		} else
215953d773cSnicm 			available = 0;
216516fb81dSnicm 		needed -= available;
217516fb81dSnicm 
218516fb81dSnicm 		/* Then fill the rest in with blanks. */
2199554a682Snicm 		for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++)
2209554a682Snicm 			memset(&gd->linedata[i], 0, sizeof gd->linedata[i]);
221311827fbSnicm 	}
222311827fbSnicm 
223516fb81dSnicm 	/* Set the new size, and reset the scroll region. */
224311827fbSnicm 	gd->sy = sy;
225311827fbSnicm 	s->rupper = 0;
226311827fbSnicm 	s->rlower = screen_size_y(s) - 1;
227311827fbSnicm }
228311827fbSnicm 
229311827fbSnicm /* Set selection. */
230311827fbSnicm void
231*78d51a74Snicm screen_set_selection(struct screen *s, u_int sx, u_int sy,
232*78d51a74Snicm     u_int ex, u_int ey, u_int rectflag, struct grid_cell *gc)
233311827fbSnicm {
234311827fbSnicm 	struct screen_sel	*sel = &s->sel;
235311827fbSnicm 
236311827fbSnicm 	memcpy(&sel->cell, gc, sizeof sel->cell);
237311827fbSnicm 	sel->flag = 1;
238*78d51a74Snicm 	sel->rectflag = rectflag;
23969466ab0Snicm 
24069466ab0Snicm 	sel->sx = sx; sel->sy = sy;
24169466ab0Snicm 	sel->ex = ex; sel->ey = ey;
242311827fbSnicm }
243311827fbSnicm 
244311827fbSnicm /* Clear selection. */
245311827fbSnicm void
246311827fbSnicm screen_clear_selection(struct screen *s)
247311827fbSnicm {
248311827fbSnicm 	struct screen_sel	*sel = &s->sel;
249311827fbSnicm 
250311827fbSnicm 	sel->flag = 0;
251311827fbSnicm }
252311827fbSnicm 
253311827fbSnicm /* Check if cell in selection. */
254311827fbSnicm int
255311827fbSnicm screen_check_selection(struct screen *s, u_int px, u_int py)
256311827fbSnicm {
257311827fbSnicm 	struct screen_sel	*sel = &s->sel;
258311827fbSnicm 
259*78d51a74Snicm 	if (!sel->flag)
260311827fbSnicm 		return (0);
261311827fbSnicm 
262*78d51a74Snicm 	if (sel->rectflag) {
263*78d51a74Snicm 		if (sel->sy < sel->ey) {
264*78d51a74Snicm 			/* start line < end line -- downward selection. */
265*78d51a74Snicm 			if (py < sel->sy || py > sel->ey)
266311827fbSnicm 				return (0);
267*78d51a74Snicm 		} else if (sel->sy > sel->ey) {
268*78d51a74Snicm 			/* start line > end line -- upward selection. */
269*78d51a74Snicm 			if (py > sel->sy || py < sel->ey)
270*78d51a74Snicm 				return (0);
271*78d51a74Snicm 		} else {
272*78d51a74Snicm 			/* starting line == ending line. */
273*78d51a74Snicm 			if (py != sel->sy)
274*78d51a74Snicm 				return (0);
275311827fbSnicm 		}
276311827fbSnicm 
277*78d51a74Snicm 		/*
278*78d51a74Snicm 		 * Need to include the selection start row, but not the cursor
279*78d51a74Snicm 		 * row, which means the selection changes depending on which
280*78d51a74Snicm 		 * one is on the left.
281*78d51a74Snicm 		 */
282*78d51a74Snicm 		if (sel->ex < sel->sx) {
283*78d51a74Snicm 			/* Cursor (ex) is on the left. */
284*78d51a74Snicm 			if (px <= sel->ex)
285311827fbSnicm 				return (0);
286*78d51a74Snicm 
287*78d51a74Snicm 			if (px > sel->sx)
288*78d51a74Snicm 				return (0);
289*78d51a74Snicm 		} else {
290*78d51a74Snicm 			/* Selection start (sx) is on the left. */
291*78d51a74Snicm 			if (px < sel->sx)
292*78d51a74Snicm 				return (0);
293*78d51a74Snicm 
294*78d51a74Snicm 			if (px >= sel->ex)
295*78d51a74Snicm 				return (0);
296*78d51a74Snicm 		}
297*78d51a74Snicm 	} else {
298*78d51a74Snicm 		/*
299*78d51a74Snicm 		 * Like emacs, keep the top-left-most character, and drop the
300*78d51a74Snicm 		 * bottom-right-most, regardless of copy direction.
301*78d51a74Snicm 		 */
302*78d51a74Snicm 		if (sel->sy < sel->ey) {
303*78d51a74Snicm 			/* starting line < ending line -- downward selection. */
304*78d51a74Snicm 			if (py < sel->sy || py > sel->ey)
305*78d51a74Snicm 				return (0);
306*78d51a74Snicm 
307*78d51a74Snicm 			if ((py == sel->sy && px < sel->sx)
308*78d51a74Snicm 			    || (py == sel->ey && px > sel->ex))
309*78d51a74Snicm 				return (0);
310*78d51a74Snicm 		} else if (sel->sy > sel->ey) {
311*78d51a74Snicm 			/* starting line > ending line -- upward selection. */
312*78d51a74Snicm 			if (py > sel->sy || py < sel->ey)
313*78d51a74Snicm 				return (0);
314*78d51a74Snicm 
315*78d51a74Snicm 			if ((py == sel->sy && px >= sel->sx)
316*78d51a74Snicm 			    || (py == sel->ey && px < sel->ex))
317*78d51a74Snicm 				return (0);
318*78d51a74Snicm 		} else {
319*78d51a74Snicm 			/* starting line == ending line. */
320*78d51a74Snicm 			if (py != sel->sy)
321*78d51a74Snicm 				return (0);
322*78d51a74Snicm 
323*78d51a74Snicm 			if (sel->ex < sel->sx) {
324*78d51a74Snicm 				/* cursor (ex) is on the left */
325*78d51a74Snicm 				if (px > sel->sx || px < sel->ex)
326*78d51a74Snicm 					return (0);
327*78d51a74Snicm 			} else {
328*78d51a74Snicm 				/* selection start (sx) is on the left */
329*78d51a74Snicm 				if (px < sel->sx || px > sel->ex)
330*78d51a74Snicm 					return (0);
331*78d51a74Snicm 			}
332*78d51a74Snicm 		}
333*78d51a74Snicm 	}
334*78d51a74Snicm 
335311827fbSnicm 	return (1);
336311827fbSnicm }
337