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