xref: /original-bsd/lib/libcurses/refresh.c (revision 29d8ef23)
1 /*
2  * Copyright (c) 1981 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)refresh.c	5.11 (Berkeley) 10/01/92";
10 #endif /* not lint */
11 
12 #include <curses.h>
13 #include <string.h>
14 
15 static int curwin;
16 static short ly, lx;
17 
18 int doqch = 1;
19 
20 WINDOW *_win;
21 
22 static void	domvcur __P((int, int, int, int));
23 static int	makech __P((WINDOW *, int));
24 static void	quickch __P((WINDOW *));
25 static void	scrolln __P((WINDOW *, int, int));
26 /*
27  * wrefresh --
28  *	Make the current screen look like "win" over the area coverd by
29  *	win.
30  */
31 int
32 wrefresh(win)
33 	register WINDOW *win;
34 {
35 	register LINE *wlp;
36 	register int retval;
37 	register short wy;
38 
39 	/* Make sure were in visual state. */
40 	if (__endwin) {
41 		tputs(VS, 0, __cputchar);
42 		tputs(TI, 0, __cputchar);
43 		__endwin = 0;
44 	}
45 
46 	/* Initialize loop parameters. */
47 
48 	ly = curscr->cury;
49 	lx = curscr->curx;
50 	wy = 0;
51 	_win = win;
52 	curwin = (win == curscr);
53 
54 	if (!curwin)
55 		for (wy = 0; wy < win->maxy; wy++) {
56 			wlp = win->lines[wy];
57 			if (wlp->flags & __ISDIRTY)
58 				wlp->hash = __hash(wlp->line, win->maxx);
59 		}
60 
61 	if (win->flags & __CLEAROK || curscr->flags & __CLEAROK || curwin) {
62 		if ((win->flags & __FULLWIN) || curscr->flags & __CLEAROK) {
63 			tputs(CL, 0, __cputchar);
64 			ly = 0;
65 			lx = 0;
66 			if (!curwin) {
67 				curscr->flags &= ~__CLEAROK;
68 				curscr->cury = 0;
69 				curscr->curx = 0;
70 				werase(curscr);
71 			}
72 			touchwin(win);
73 		}
74 		win->flags &= ~__CLEAROK;
75 	}
76 	if (!CA) {
77 		if (win->curx != 0)
78 			putchar('\n');
79 		if (!curwin)
80 			werase(curscr);
81 	}
82 #ifdef DEBUG
83 	__TRACE("wrefresh: (%0.2o): curwin = %d\n", win, curwin);
84 	__TRACE("wrefresh: \tfirstch\tlastch\n");
85 #endif
86 
87 #ifndef NOQCH
88 	if (!__noqch && (win->flags & __FULLWIN) && !curwin)
89     		quickch(win);
90 #endif
91 	for (wy = 0; wy < win->maxy; wy++) {
92 #ifdef DEBUG
93 		__TRACE("%d\t%d\t%d\n",
94 		    wy, win->lines[wy]->firstch, win->lines[wy]->lastch);
95 #endif
96 		if (!curwin)
97 			curscr->lines[wy]->hash = win->lines[wy]->hash;
98 		if (win->lines[wy]->flags & __ISDIRTY)
99 			if (makech(win, wy) == ERR)
100 				return (ERR);
101 			else {
102 				if (win->lines[wy]->firstch >= win->ch_off)
103 					win->lines[wy]->firstch = win->maxx +
104 					    win->ch_off;
105 				if (win->lines[wy]->lastch < win->maxx +
106 				    win->ch_off)
107 					win->lines[wy]->lastch = win->ch_off;
108 				if (win->lines[wy]->lastch <
109 				    win->lines[wy]->firstch)
110 					win->lines[wy]->flags &= ~__ISDIRTY;
111 			}
112 #ifdef DEBUG
113 		__TRACE("\t%d\t%d\n", win->lines[wy]->firstch,
114 			win->lines[wy]->lastch);
115 #endif
116 	}
117 
118 #ifdef DEBUG
119 	__TRACE("refresh: ly=%d, lx=%d\n", ly, lx);
120 #endif
121 	if (win == curscr)
122 		domvcur(ly, lx, win->cury, win->curx);
123 	else {
124 		if (win->flags & __LEAVEOK) {
125 			curscr->cury = ly;
126 			curscr->curx = lx;
127 			ly -= win->begy;
128 			lx -= win->begx;
129 			if (ly >= 0 && ly < win->maxy && lx >= 0 &&
130 			    lx < win->maxx) {
131 				win->cury = ly;
132 				win->curx = lx;
133 			} else
134 				win->cury = win->curx = 0;
135 		} else {
136 			domvcur(ly, lx, win->cury + win->begy,
137 			    win->curx + win->begx);
138 			curscr->cury = win->cury + win->begy;
139 			curscr->curx = win->curx + win->begx;
140 		}
141 	}
142 	retval = OK;
143 
144 	_win = NULL;
145 	(void)fflush(stdout);
146 	return (retval);
147 }
148 
149 /*
150  * makech --
151  *	Make a change on the screen.
152  */
153 static int
154 makech(win, wy)
155 	register WINDOW *win;
156 	int wy;
157 {
158 	register int nlsp, clsp;		/* Last space in lines. */
159 	register short wx, lch, y;
160 	register char *nsp, *csp, *ce;
161 
162 	if (!(win->lines[wy]->flags & __ISDIRTY))
163 		return (OK);
164 	wx = win->lines[wy]->firstch - win->ch_off;
165 	if (wx >= win->maxx)
166 		return (OK);
167 	else if (wx < 0)
168 		wx = 0;
169 	lch = win->lines[wy]->lastch - win->ch_off;
170 	if (lch < 0)
171 		return (OK);
172 	else if (lch >= win->maxx)
173 		lch = win->maxx - 1;
174 	y = wy + win->begy;
175 
176 	if (curwin)
177 		csp = " ";
178 	else
179 		csp = &curscr->lines[wy + win->begy]->line[wx + win->begx];
180 
181 	nsp = &win->lines[wy]->line[wx];
182 	if (CE && !curwin) {
183 		for (ce = &win->lines[wy]->line[win->maxx - 1];
184 		     *ce == ' '; ce--)
185 			if (ce <= win->lines[wy]->line)
186 				break;
187 		nlsp = ce - win->lines[wy]->line;
188 	}
189 	if (!curwin)
190 		ce = CE;
191 	else
192 		ce = NULL;
193 
194 	while (wx <= lch) {
195 		if (*nsp == *csp) {
196 			if (wx <= lch) {
197 				while (*nsp == *csp && wx <= lch) {
198 					nsp++;
199 					if (!curwin)
200 						csp++;
201 					++wx;
202 				}
203 				continue;
204 			}
205 			break;
206 		}
207 		domvcur(ly, lx, y, wx + win->begx);
208 
209 #ifdef DEBUG
210 		__TRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d\n",
211 		    wx, ly, lx, y, wx + win->begx);
212 #endif
213 		ly = y;
214 		lx = wx + win->begx;
215 		while (*nsp != *csp && wx <= lch) {
216 #ifdef notdef
217 			/* XXX
218 			 * The problem with this code is that we can't count on
219 			 * terminals wrapping around after the
220 			 * last character on the previous line has been output
221 			 * In effect, what then could happen is that the CE
222 			 * clear the previous line and do nothing to the
223 			 * next line.
224 			 */
225 			if (ce != NULL && wx >= nlsp && *nsp == ' ') {
226 				/* Check for clear to end-of-line. */
227 				ce = &curscr->lines[ly]->line[COLS - 1];
228 				while (*ce == ' ')
229 					if (ce-- <= csp)
230 						break;
231 				clsp = ce - curscr->lines[ly]->line -
232 				       win->begx;
233 #ifdef DEBUG
234 			__TRACE("makech: clsp = %d, nlsp = %d\n", clsp, nlsp);
235 #endif
236 				if (clsp - nlsp >= strlen(CE) &&
237 				    clsp < win->maxx) {
238 #ifdef DEBUG
239 					__TRACE("makech: using CE\n");
240 #endif
241 					tputs(CE, 0, __cputchar);
242 					lx = wx + win->begx;
243 					while (wx++ <= clsp)
244 						*csp++ = ' ';
245 					return (OK);
246 				}
247 				ce = NULL;
248 			}
249 #endif
250 
251 			/* Enter/exit standout mode as appropriate. */
252 			if (SO && (*nsp & __STANDOUT) !=
253 			    (curscr->flags & __WSTANDOUT)) {
254 				if (*nsp & __STANDOUT) {
255 					tputs(SO, 0, __cputchar);
256 					curscr->flags |= __WSTANDOUT;
257 				} else {
258 					tputs(SE, 0, __cputchar);
259 					curscr->flags &= ~__WSTANDOUT;
260 				}
261 			}
262 
263 			wx++;
264 			if (wx >= win->maxx && wy == win->maxy - 1)
265 				if (win->flags & __SCROLLOK) {
266 					if (curscr->flags & __WSTANDOUT
267 					    && win->flags & __ENDLINE)
268 						if (!MS) {
269 							tputs(SE, 0,
270 							    __cputchar);
271 							curscr->flags &=
272 							    ~__WSTANDOUT;
273 						}
274 					if (!curwin)
275 						putchar((*csp = *nsp) & 0177);
276 					else
277 						putchar(*nsp & 0177);
278 #ifdef notdef
279 					if (win->flags & __FULLWIN && !curwin){
280 						scroll(curscr);
281 #endif
282 					ly = win->begy + win->maxy - 1;
283 					lx = win->begx + win->maxx - 1;
284 					return (OK);
285 				} else
286 					if (win->flags & __SCROLLWIN) {
287 						lx = --wx;
288 						return (ERR);
289 					}
290 			if (!curwin)
291 				putchar((*csp++ = *nsp) & 0177);
292 			else
293 				putchar(*nsp & 0177);
294 #ifdef DEBUG
295 			__TRACE("makech: putchar(%c)\n", *nsp & 0177);
296 #endif
297 			if (UC && (*nsp & __STANDOUT)) {
298 				putchar('\b');
299 				tputs(UC, 0, __cputchar);
300 			}
301 			nsp++;
302 		}
303 #ifdef DEBUG
304 		__TRACE("makech: 2: wx = %d, lx = %d\n", wx, lx);
305 #endif
306 		if (lx == wx + win->begx)	/* If no change. */
307 			break;
308 		lx = wx + win->begx;
309 		if (lx >= COLS && AM) {
310 			lx = 0;
311 			ly++;
312 			/*
313 			 * xn glitch: chomps a newline after auto-wrap.
314 			 * we just feed it now and forget about it.
315 			 */
316 			if (XN) {
317 				putchar('\n');
318 				putchar('\r');
319 			}
320 		}
321 #ifdef DEBUG
322 		__TRACE("makech: 3: wx = %d, lx = %d\n", wx, lx);
323 #endif
324 	}
325 	return (OK);
326 }
327 
328 /*
329  * domvcur --
330  *	Do a mvcur, leaving standout mode if necessary.
331  */
332 static void
333 domvcur(oy, ox, ny, nx)
334 	int oy, ox, ny, nx;
335 {
336 	if (curscr->flags & __WSTANDOUT && !MS) {
337 		tputs(SE, 0, __cputchar);
338 		curscr->flags &= ~__WSTANDOUT;
339 	}
340 #ifdef DEBUG
341 	__TRACE("domvcur: oy=%d, ox=%d, ny=%d, nx=%d\n", oy, ox, ny, nx);
342 #endif
343 	mvcur(oy, ox, ny, nx);
344 }
345 
346 
347 /*
348  * Quickch() attempts to detect a pattern in the change of the window
349  * inorder to optimize the change, e.g., scroll n lines as opposed to
350  * repainting the screen line by line.
351  */
352 
353 static void
354 quickch(win)
355 	WINDOW *win;
356 {
357 #define THRESH		win->maxy / 4
358 
359 	register LINE *clp, *tmp1, *tmp2;
360 	register int bsize, curs, curw, starts, startw, i, j;
361 	int n, target, remember;
362 	char buf[1024];
363 	u_int blank_hash;
364 
365 	for (bsize = win->maxy; bsize >= THRESH; bsize--)
366 		for (startw = 0; startw <= win->maxy - bsize; startw++)
367 			for (starts = 0; starts <= win->maxy - bsize;
368 			     starts++) {
369 				for (curw = startw, curs = starts;
370 				     curs < starts + bsize; curw++, curs++)
371 					if (win->lines[curw]->hash !=
372 					    curscr->lines[curs]->hash)
373 						break;
374 				if (curs == starts + bsize)
375 					goto done;
376 			}
377  done:
378 	/* Did not find anything or block is in correct place already. */
379 	if (bsize < THRESH || starts == startw)
380 		return;
381 
382 #ifdef DEBUG
383 	__TRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d\n",
384 		bsize, starts, startw, curw, curs);
385 #endif
386 	scrolln(win, starts, startw);
387 
388 	n = startw - starts;
389 
390 	/* So we don't have to call __hash() each time */
391 	(void)memset(buf, ' ', win->maxx);
392 	blank_hash = __hash(buf, win->maxx);
393 
394 	/*
395 	 * Perform the rotation to maintain the consistency of curscr.
396 	 */
397 	i = 0;
398 	tmp1 = curscr->lines[0];
399 	remember = 0;
400 	for (j = 0; j < win->maxy; j++) {
401 		target = (i + n + win->maxy) % win->maxy;
402 		tmp2 = curscr->lines[target];
403 		curscr->lines[target] = tmp1;
404 		/* Mark block as clean and blank out scrolled lines. */
405 		clp = curscr->lines[target];
406 		__TRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ",
407 			n, startw, curw, i, target);
408 		if (target >= startw && target < curw) {
409 			__TRACE("-- notdirty");
410 			win->lines[target]->flags &= ~__ISDIRTY;
411 		} else if ((n < 0 && target >= win->maxy + n) ||
412 			 (n > 0 && target < n)) {
413 			if (clp->hash != blank_hash) {
414 				(void)memset(clp->line, ' ', win->maxx);
415 				__TRACE("-- memset");
416 				clp->hash = blank_hash;
417 			} else
418 				__TRACE(" -- nonmemset");
419 			touchline(win, target, 0, win->maxx - 1);
420 		} else {
421 			__TRACE(" -- just dirty");
422 			touchline(win, target, 0, win->maxx - 1);
423 		}
424 		__TRACE("\n");
425 		if (target == remember) {
426 			i = target + 1;
427 			tmp1 = curscr->lines[i];
428 			remember = i;
429 		} else {
430 			tmp1 = tmp2;
431 			i = target;
432 		}
433 	}
434 	__TRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
435 	for (i = 0; i < curscr->maxy; i++)
436 		__TRACE("Q: %d: %.70s\n", i,
437 	           curscr->lines[i]->line);
438 }
439 
440 static void
441 scrolln(win, starts, startw)
442 	WINDOW *win;
443 	int starts, startw;
444 {
445 	int i, oy, ox, n;
446 
447 	oy = curscr->cury;
448 	ox = curscr->curx;
449 	n = starts - startw;
450 
451 	if (n > 0) {
452 		mvcur(oy, ox, 0, 0);
453 		if (DL)
454 			tputs(tscroll(DL, n), 0, __cputchar);
455 		else
456 			for(i = 0; i < n; i++)
457 				tputs(dl, 0, __cputchar);
458 		mvcur(0, 0, oy, ox);
459 	} else {
460 		/* Delete the bottom lines */
461 		mvcur(oy, 0, win->maxy + n, 0);		/* n < 0 */
462 		if (DL)
463 			tputs(tscroll(DL, -n), 0, __cputchar);
464 		else
465 			for(i = n; i < 0; i++)
466 				tputs(dl, 0, __cputchar);
467 		mvcur(win->maxy + n, 0, starts,  0);
468 
469 		/* Scroll the block down */
470 		if (AL)
471 			tputs(tscroll(AL, -n), 0, __cputchar);
472 		else
473 			for(i = n; i < 0; i++)
474 				tputs(al, 0, __cputchar);
475 		mvcur(starts, 0, oy, ox);
476 	}
477 }
478 
479 
480