xref: /original-bsd/lib/libcurses/refresh.c (revision be1f24e8)
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.12 (Berkeley) 10/08/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 
122 	if (win == curscr)
123 		domvcur(ly, lx, win->cury, win->curx);
124 	else {
125 		if (win->flags & __LEAVEOK) {
126 			curscr->cury = ly;
127 			curscr->curx = lx;
128 			ly -= win->begy;
129 			lx -= win->begx;
130 			if (ly >= 0 && ly < win->maxy && lx >= 0 &&
131 			    lx < win->maxx) {
132 				win->cury = ly;
133 				win->curx = lx;
134 			} else
135 				win->cury = win->curx = 0;
136 		} else {
137 			domvcur(ly, lx, win->cury + win->begy,
138 			    win->curx + win->begx);
139 			curscr->cury = win->cury + win->begy;
140 			curscr->curx = win->curx + win->begx;
141 		}
142 	}
143 	retval = OK;
144 
145 	_win = NULL;
146 	(void)fflush(stdout);
147 	return (retval);
148 }
149 
150 /*
151  * makech --
152  *	Make a change on the screen.
153  */
154 static int
155 makech(win, wy)
156 	register WINDOW *win;
157 	int wy;
158 {
159 	register int nlsp, clsp;		/* Last space in lines. */
160 	register short wx, lch, y;
161 	register char *nsp, *csp, *ce;
162 
163 	/* Is the cursor still on the end of the last line? */
164 	if (wy > 0 && win->lines[wy - 1]->flags & __ISPASTEOL) {
165 			win->lines[wy - 1]->flags &= ~__ISPASTEOL;
166 			ly++;
167 			lx = 0;
168 	}
169 
170 	if (!(win->lines[wy]->flags & __ISDIRTY))
171 		return (OK);
172 	wx = win->lines[wy]->firstch - win->ch_off;
173 	if (wx >= win->maxx)
174 		return (OK);
175 	else if (wx < 0)
176 		wx = 0;
177 	lch = win->lines[wy]->lastch - win->ch_off;
178 	if (lch < 0)
179 		return (OK);
180 	else if (lch >= win->maxx)
181 		lch = win->maxx - 1;
182 	y = wy + win->begy;
183 
184 	if (curwin)
185 		csp = " ";
186 	else
187 		csp = &curscr->lines[wy + win->begy]->line[wx + win->begx];
188 
189 	nsp = &win->lines[wy]->line[wx];
190 	if (CE && !curwin) {
191 		for (ce = &win->lines[wy]->line[win->maxx - 1];
192 		     *ce == ' '; ce--)
193 			if (ce <= win->lines[wy]->line)
194 				break;
195 		nlsp = ce - win->lines[wy]->line;
196 	}
197 	if (!curwin)
198 		ce = CE;
199 	else
200 		ce = NULL;
201 
202 	while (wx <= lch) {
203 		if (*nsp == *csp) {
204 			if (wx <= lch) {
205 				while (*nsp == *csp && wx <= lch) {
206 					nsp++;
207 					if (!curwin)
208 						csp++;
209 					++wx;
210 				}
211 				continue;
212 			}
213 			break;
214 		}
215 		domvcur(ly, lx, y, wx + win->begx);
216 
217 #ifdef DEBUG
218 		__TRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d\n",
219 		    wx, ly, lx, y, wx + win->begx);
220 #endif
221 		ly = y;
222 		lx = wx + win->begx;
223 		while (*nsp != *csp && wx <= lch) {
224 #ifdef notdef
225 			/* XXX
226 			 * The problem with this code is that we can't count on
227 			 * terminals wrapping around after the
228 			 * last character on the previous line has been output
229 			 * In effect, what then could happen is that the CE
230 			 * clear the previous line and do nothing to the
231 			 * next line.
232 			 */
233 			if (ce != NULL && wx >= nlsp && *nsp == ' ') {
234 				/* Check for clear to end-of-line. */
235 				ce = &curscr->lines[ly]->line[COLS - 1];
236 				while (*ce == ' ')
237 					if (ce-- <= csp)
238 						break;
239 				clsp = ce - curscr->lines[ly]->line -
240 				       win->begx;
241 #ifdef DEBUG
242 			__TRACE("makech: clsp = %d, nlsp = %d\n", clsp, nlsp);
243 #endif
244 				if (clsp - nlsp >= strlen(CE) &&
245 				    clsp < win->maxx) {
246 #ifdef DEBUG
247 					__TRACE("makech: using CE\n");
248 #endif
249 					tputs(CE, 0, __cputchar);
250 					lx = wx + win->begx;
251 					while (wx++ <= clsp)
252 						*csp++ = ' ';
253 					return (OK);
254 				}
255 				ce = NULL;
256 			}
257 #endif
258 
259 			/* Enter/exit standout mode as appropriate. */
260 			if (SO && (*nsp & __STANDOUT) !=
261 			    (curscr->flags & __WSTANDOUT)) {
262 				if (*nsp & __STANDOUT) {
263 					tputs(SO, 0, __cputchar);
264 					curscr->flags |= __WSTANDOUT;
265 				} else {
266 					tputs(SE, 0, __cputchar);
267 					curscr->flags &= ~__WSTANDOUT;
268 				}
269 			}
270 
271 			wx++;
272 			if (wx >= win->maxx && wy == win->maxy - 1 && !curwin)
273 				if (win->flags & __SCROLLOK) {
274 					if (curscr->flags & __WSTANDOUT
275 					    && win->flags & __ENDLINE)
276 						if (!MS) {
277 							tputs(SE, 0,
278 							    __cputchar);
279 							curscr->flags &=
280 							    ~__WSTANDOUT;
281 						}
282 					if (!curwin)
283 						putchar((*csp = *nsp) & 0177);
284 					else
285 						putchar(*nsp & 0177);
286 #ifdef notdef
287 					if (win->flags & __FULLWIN && !curwin)
288 						scroll(curscr);
289 #endif
290 					ly = win->begy + win->maxy - 1;
291 					lx = win->begx + win->maxx - 1;
292 					return (OK);
293 				} else
294 					if (win->flags & __SCROLLWIN) {
295 						lx = --wx;
296 						return (ERR);
297 					}
298 			if (!curwin)
299 				putchar((*csp++ = *nsp) & 0177);
300 			else
301 				putchar(*nsp & 0177);
302 
303 #ifdef DEBUG
304 			__TRACE("makech: putchar(%c)\n", *nsp & 0177);
305 #endif
306 			if (UC && (*nsp & __STANDOUT)) {
307 				putchar('\b');
308 				tputs(UC, 0, __cputchar);
309 			}
310 			nsp++;
311 		}
312 #ifdef DEBUG
313 		__TRACE("makech: 2: wx = %d, lx = %d\n", wx, lx);
314 #endif
315 		if (lx == wx + win->begx)	/* If no change. */
316 			break;
317 		lx = wx + win->begx;
318 		if (lx >= COLS && AM) {
319 			/*
320 			 * xn glitch: chomps a newline after auto-wrap.
321 			 * we just feed it now and forget about it.
322 			 */
323 			if (XN) {
324 				lx = 0;
325 				ly++;
326 				putchar('\n');
327 				putchar('\r');
328 			} else {
329 				if (wy != LINES)
330 					win->lines[wy]->flags |= __ISPASTEOL;
331 				lx = COLS - 1;
332 			}
333 		}
334 #ifdef DEBUG
335 		__TRACE("makech: 3: wx = %d, lx = %d\n", wx, lx);
336 #endif
337 	}
338 	return (OK);
339 }
340 
341 /*
342  * domvcur --
343  *	Do a mvcur, leaving standout mode if necessary.
344  */
345 static void
346 domvcur(oy, ox, ny, nx)
347 	int oy, ox, ny, nx;
348 {
349 	if (curscr->flags & __WSTANDOUT && !MS) {
350 		tputs(SE, 0, __cputchar);
351 		curscr->flags &= ~__WSTANDOUT;
352 	}
353 #ifdef DEBUG
354 	__TRACE("domvcur: oy=%d, ox=%d, ny=%d, nx=%d\n", oy, ox, ny, nx);
355 #endif
356 	mvcur(oy, ox, ny, nx);
357 }
358 
359 
360 /*
361  * Quickch() attempts to detect a pattern in the change of the window
362  * inorder to optimize the change, e.g., scroll n lines as opposed to
363  * repainting the screen line by line.
364  */
365 
366 static void
367 quickch(win)
368 	WINDOW *win;
369 {
370 #define THRESH		win->maxy / 4
371 
372 	register LINE *clp, *tmp1, *tmp2;
373 	register int bsize, curs, curw, starts, startw, i, j;
374 	int n, target, remember;
375 	char buf[1024];
376 	u_int blank_hash;
377 
378 	for (bsize = win->maxy; bsize >= THRESH; bsize--)
379 		for (startw = 0; startw <= win->maxy - bsize; startw++)
380 			for (starts = 0; starts <= win->maxy - bsize;
381 			     starts++) {
382 				for (curw = startw, curs = starts;
383 				     curs < starts + bsize; curw++, curs++)
384 					if (win->lines[curw]->hash !=
385 					    curscr->lines[curs]->hash)
386 						break;
387 				if (curs == starts + bsize)
388 					goto done;
389 			}
390  done:
391 	/* Did not find anything or block is in correct place already. */
392 	if (bsize < THRESH || starts == startw)
393 		return;
394 
395 #ifdef DEBUG
396 	__TRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d\n",
397 		bsize, starts, startw, curw, curs);
398 #endif
399 	scrolln(win, starts, startw);
400 
401 	n = startw - starts;
402 
403 	/* So we don't have to call __hash() each time */
404 	(void)memset(buf, ' ', win->maxx);
405 	blank_hash = __hash(buf, win->maxx);
406 
407 	/*
408 	 * Perform the rotation to maintain the consistency of curscr.
409 	 */
410 	i = 0;
411 	tmp1 = curscr->lines[0];
412 	remember = 0;
413 	for (j = 0; j < win->maxy; j++) {
414 		target = (i + n + win->maxy) % win->maxy;
415 		tmp2 = curscr->lines[target];
416 		curscr->lines[target] = tmp1;
417 		/* Mark block as clean and blank out scrolled lines. */
418 		clp = curscr->lines[target];
419 #ifdef DEBUG
420 		__TRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ",
421 			n, startw, curw, i, target);
422 #endif
423 		if (target >= startw && target < curw) {
424 #ifdef DEBUG
425 			__TRACE("-- notdirty");
426 #endif
427 			win->lines[target]->flags &= ~__ISDIRTY;
428 		} else if ((n < 0 && target >= win->maxy + n) ||
429 			 (n > 0 && target < n)) {
430 			if (clp->hash != blank_hash) {
431 				(void)memset(clp->line, ' ', win->maxx);
432 #ifdef DEBUG
433 				__TRACE("-- memset");
434 #endif
435 				clp->hash = blank_hash;
436 			} else
437 #ifdef DEBUG
438 				__TRACE(" -- nonmemset");
439 #endif
440 			touchline(win, target, 0, win->maxx - 1);
441 		} else {
442 #ifdef DEBUG
443 			__TRACE(" -- just dirty");
444 #endif
445 			touchline(win, target, 0, win->maxx - 1);
446 		}
447 #ifdef DEBUG
448 		__TRACE("\n");
449 #endif
450 		if (target == remember) {
451 			i = target + 1;
452 			tmp1 = curscr->lines[i];
453 			remember = i;
454 		} else {
455 			tmp1 = tmp2;
456 			i = target;
457 		}
458 	}
459 #ifdef DEBUG
460 	__TRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
461 	for (i = 0; i < curscr->maxy; i++)
462 		__TRACE("Q: %d: %.70s\n", i,
463 	           curscr->lines[i]->line);
464 #endif
465 }
466 
467 static void
468 scrolln(win, starts, startw)
469 	WINDOW *win;
470 	int starts, startw;
471 {
472 	int i, oy, ox, n;
473 
474 	oy = curscr->cury;
475 	ox = curscr->curx;
476 	n = starts - startw;
477 
478 	if (n > 0) {
479 		mvcur(oy, ox, 0, 0);
480 		if (DL)
481 			tputs(tscroll(DL, n), 0, __cputchar);
482 		else
483 			for(i = 0; i < n; i++)
484 				tputs(dl, 0, __cputchar);
485 		mvcur(0, 0, oy, ox);
486 	} else {
487 		/* Delete the bottom lines */
488 		mvcur(oy, 0, win->maxy + n, 0);		/* n < 0 */
489 		if (DL)
490 			tputs(tscroll(DL, -n), 0, __cputchar);
491 		else
492 			for(i = n; i < 0; i++)
493 				tputs(dl, 0, __cputchar);
494 		mvcur(win->maxy + n, 0, starts,  0);
495 
496 		/* Scroll the block down */
497 		if (AL)
498 			tputs(tscroll(AL, -n), 0, __cputchar);
499 		else
500 			for(i = n; i < 0; i++)
501 				tputs(al, 0, __cputchar);
502 		mvcur(starts, 0, oy, ox);
503 	}
504 }
505 
506 
507