xref: /original-bsd/usr.bin/window/wwscroll.c (revision 883fb6be)
1 #ifndef lint
2 static char sccsid[] = "@(#)wwscroll.c	3.14 05/18/87";
3 #endif
4 
5 /*
6  * Copyright (c) 1983 Regents of the University of California,
7  * All rights reserved.  Redistribution permitted subject to
8  * the terms of the Berkeley Software License Agreement.
9  */
10 
11 #include "ww.h"
12 #include "tt.h"
13 
14 wwscroll(w, n)
15 register struct ww *w;
16 int n;
17 {
18 	register dir;
19 	register top;
20 
21 	if (n == 0)
22 		return;
23 	dir = n < 0 ? -1 : 1;
24 	top = w->ww_b.t - n;
25 	if (top > w->ww_w.t)
26 		top = w->ww_w.t;
27 	else if (top + w->ww_b.nr < w->ww_w.b)
28 		top = w->ww_w.b - w->ww_b.nr;
29 	n = abs(top - w->ww_b.t);
30 	if (n < w->ww_i.nr) {
31 		while (--n >= 0) {
32 			(void) wwscroll1(w, w->ww_i.t, w->ww_i.b, dir, 0);
33 			w->ww_buf += dir;
34 			w->ww_b.t -= dir;
35 			w->ww_b.b -= dir;
36 		}
37 	} else {
38 		w->ww_buf -= top - w->ww_b.t;
39 		w->ww_b.t = top;
40 		w->ww_b.b = top + w->ww_b.nr;
41 		wwredrawwin(w);
42 	}
43 }
44 
45 /*
46  * Scroll one line, between 'row1' and 'row2', in direction 'dir'.
47  * Don't adjust ww_scroll.
48  * And don't redraw 'leaveit' lines.
49  */
50 wwscroll1(w, row1, row2, dir, leaveit)
51 register struct ww *w;
52 int row1, row2, dir;
53 int leaveit;
54 {
55 	register i;
56 	int row1x, row2x;
57 	int nvis;
58 	int nvismax;
59 	int deleted = 0;
60 
61 	/*
62 	 * See how many lines on the screen are affected.
63 	 * And calculate row1x, row2x, and left at the same time.
64 	 */
65 	for (i = row1; i < row2 && w->ww_nvis[i] == 0; i++)
66 		;
67 	if (i >= row2)			/* can't do any fancy stuff */
68 		goto out;
69 	row1x = i;
70 	for (i = row2 - 1; i >= row1 && w->ww_nvis[i] == 0; i--)
71 		;
72 	if (i <= row1x)
73 		goto out;		/* just one line is easy */
74 	row2x = i + 1;
75 
76 	/*
77 	 * See how much of this window is visible.
78 	 */
79 	nvismax = wwncol * (row2x - row1x);
80 	nvis = 0;
81 	for (i = row1x; i < row2x; i++)
82 		nvis += w->ww_nvis[i];
83 
84 	/*
85 	 * If it's a good idea to use delete and insert line
86 	 * and the terminal can, then do it.
87 	 */
88 	if (nvis > nvismax / 2 && tt.tt_delline && tt.tt_insline) {
89 		register union ww_char *tmp;
90 		register union ww_char **cpp, **cqq;
91 
92 		/*
93 		 * Don't worry about retain when scrolling down,
94 		 * but do worry when scrolling up, for hp2621.
95 		 */
96 		if (dir > 0) {
97 			/*
98 			 * We're going to assume that a line feed at the
99 			 * bottom of the screen will cause a scroll, unless
100 			 * "ns" is set.  This should work at least 99%
101 			 * of the time.  At any rate, vi seems to do it.
102 			 */
103 			if (tt.tt_noscroll || row1x != 0 || row2x != wwnrow) {
104 				(*tt.tt_move)(row1x, 0);
105 				(*tt.tt_delline)();
106 				if (row2x < wwnrow) {
107 					(*tt.tt_move)(row2x - 1, 0);
108 					(*tt.tt_insline)();
109 				}
110 			} else {
111 				if (tt.tt_row != wwnrow - 1)
112 					(*tt.tt_move)(wwnrow - 1, 0);
113 				ttputc('\n');
114 			}
115 			/*
116 			 * Fix up the old screen.
117 			 */
118 			cpp = &wwos[row1x];
119 			cqq = cpp + 1;
120 			tmp = *cpp;
121 			for (i = row2x - row1x; --i > 0;)
122 				*cpp++ = *cqq++;
123 			*cpp = tmp;
124 		} else {
125 			if (tt.tt_retain || row2x != wwnrow) {
126 				(*tt.tt_move)(row2x - 1, 0);
127 				(*tt.tt_delline)();
128 			}
129 			(*tt.tt_move)(row1x, 0);
130 			(*tt.tt_insline)();
131 			/*
132 			 * Fix up the old screen.
133 			 */
134 			cpp = &wwos[row2x];
135 			cqq = cpp - 1;
136 			tmp = *cqq;
137 			for (i = row2x - row1x; --i > 0;)
138 				*--cpp = *--cqq;
139 			*cqq = tmp;
140 		}
141 		for (i = wwncol; --i >= 0;)
142 			tmp++->c_w = ' ';
143 		deleted++;
144 	}
145 
146 	/*
147 	 * Fix the new screen.
148 	 */
149 	if (nvis == nvismax) {
150 		/*
151 		 * Can shift whole lines.
152 		 */
153 		if (dir > 0) {
154 			{
155 				register union ww_char *tmp;
156 				register union ww_char **cpp, **cqq;
157 
158 				cpp = &wwns[row1x];
159 				cqq = cpp + 1;
160 				tmp = *cpp;
161 				for (i = row2x - row1x; --i > 0;)
162 					*cpp++ = *cqq++;
163 				*cpp = tmp;
164 			}
165 			if (deleted) {
166 				register char *p, *q;
167 
168 				p = &wwtouched[row1x];
169 				q = p + 1;
170 				for (i = row2x - row1x; --i > 0;)
171 					*p++ = *q++;
172 				*p |= WWU_TOUCHED;
173 			} else {
174 				register char *p;
175 
176 				p = &wwtouched[row1x];
177 				for (i = row2x - row1x; --i >= 0;)
178 					*p++ |= WWU_MAJOR|WWU_TOUCHED;
179 			}
180 			wwredrawwin1(w, row1, row1x, dir);
181 			wwredrawwin1(w, row2x - 1, row2 - leaveit, dir);
182 		} else {
183 			{
184 				register union ww_char *tmp;
185 				register union ww_char **cpp, **cqq;
186 
187 				cpp = &wwns[row2x];
188 				cqq = cpp - 1;
189 				tmp = *cqq;
190 				for (i = row2x - row1x; --i > 0;)
191 					*--cpp = *--cqq;
192 				*cqq = tmp;
193 			}
194 			if (deleted) {
195 				register char *p, *q;
196 
197 				p = &wwtouched[row2x];
198 				q = p - 1;
199 				for (i = row2x - row1x; --i > 0;)
200 					*--p = *--q;
201 				*q |= WWU_MAJOR|WWU_TOUCHED;
202 			} else {
203 				register char *p;
204 
205 				p = &wwtouched[row1x];
206 				for (i = row2x - row1x; --i >= 0;)
207 					*p++ |= WWU_TOUCHED;
208 			}
209 			wwredrawwin1(w, row1 + leaveit, row1x + 1, dir);
210 			wwredrawwin1(w, row2x, row2, dir);
211 		}
212 	} else {
213 out:
214 		if (dir > 0)
215 			wwredrawwin1(w, row1, row2 - leaveit, dir);
216 		else
217 			wwredrawwin1(w, row1 + leaveit, row2, dir);
218 	}
219 	return deleted;
220 }
221