xref: /original-bsd/lib/libcurses/refresh.c (revision f4a18198)
1 /*
2  * Copyright (c) 1981, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)refresh.c	8.2 (Berkeley) 05/04/94";
10 #endif /* not lint */
11 
12 #include <string.h>
13 
14 #include "curses.h"
15 
16 static int curwin;
17 static short ly, lx;
18 
19 static void	domvcur __P((int, int, int, int));
20 static int	makech __P((WINDOW *, int));
21 static void	quickch __P((WINDOW *));
22 static void	scrolln __P((WINDOW *, int, int, int, int, int));
23 
24 /*
25  * wrefresh --
26  *	Make the current screen look like "win" over the area coverd by
27  *	win.
28  */
29 int
30 wrefresh(win)
31 	register WINDOW *win;
32 {
33 	register __LINE *wlp;
34 	register int retval;
35 	register short wy;
36 	int dnum;
37 
38 	/* Initialize loop parameters. */
39 	ly = curscr->cury;
40 	lx = curscr->curx;
41 	wy = 0;
42 	curwin = (win == curscr);
43 
44 	if (!curwin)
45 		for (wy = 0; wy < win->maxy; wy++) {
46 			wlp = win->lines[wy];
47 			if (wlp->flags & __ISDIRTY)
48 				wlp->hash =
49 				   __hash((char *) wlp->line, win->maxx * __LDATASIZE);
50 		}
51 
52 	if (win->flags & __CLEAROK || curscr->flags & __CLEAROK || curwin) {
53 		if ((win->flags & __FULLWIN) || curscr->flags & __CLEAROK) {
54 			tputs(CL, 0, __cputchar);
55 			ly = 0;
56 			lx = 0;
57 			if (!curwin) {
58 				curscr->flags &= ~__CLEAROK;
59 				curscr->cury = 0;
60 				curscr->curx = 0;
61 				werase(curscr);
62 			}
63 			__touchwin(win);
64 		}
65 		win->flags &= ~__CLEAROK;
66 	}
67 	if (!CA) {
68 		if (win->curx != 0)
69 			putchar('\n');
70 		if (!curwin)
71 			werase(curscr);
72 	}
73 #ifdef DEBUG
74 	__CTRACE("wrefresh: (%0.2o): curwin = %d\n", win, curwin);
75 	__CTRACE("wrefresh: \tfirstch\tlastch\n");
76 #endif
77 
78 #ifndef NOQCH
79 	if ((win->flags & __FULLWIN) && !curwin) {
80 		/*
81 		 * Invoke quickch() only if more than a quarter of the lines
82 		 * in the window are dirty.
83 		 */
84 		for (wy = 0, dnum = 0; wy < win->maxy; wy++)
85 			if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT))
86 				dnum++;
87 		if (!__noqch && dnum > (int) win->maxy / 4)
88 			quickch(win);
89 	}
90 #endif
91 
92 #ifdef DEBUG
93 { int i, j;
94 		__CTRACE("#####################################\n");
95 		for (i = 0; i < curscr->maxy; i++) {
96 			__CTRACE("C: %d:", i);
97 			__CTRACE(" 0x%x \n", curscr->lines[i]->hash);
98 			for (j = 0; j < curscr->maxx; j++)
99 				__CTRACE("%c",
100 			           curscr->lines[i]->line[j].ch);
101 			__CTRACE("\n");
102 			for (j = 0; j < curscr->maxx; j++)
103 				__CTRACE("%x",
104 			           curscr->lines[i]->line[j].attr);
105 			__CTRACE("\n");
106 			__CTRACE("W: %d:", i);
107 			__CTRACE(" 0x%x \n", win->lines[i]->hash);
108 			__CTRACE(" 0x%x ", win->lines[i]->flags);
109 			for (j = 0; j < win->maxx; j++)
110 				__CTRACE("%c",
111 			           win->lines[i]->line[j].ch);
112 			__CTRACE("\n");
113 			for (j = 0; j < win->maxx; j++)
114 				__CTRACE("%x",
115 			           win->lines[i]->line[j].attr);
116 			__CTRACE("\n");
117 		}
118 }
119 #endif /* DEBUG */
120 
121 	for (wy = 0; wy < win->maxy; wy++) {
122 #ifdef DEBUG
123 		__CTRACE("%d\t%d\t%d\n",
124 		    wy, *win->lines[wy]->firstchp, *win->lines[wy]->lastchp);
125 #endif
126 		if (!curwin)
127 			curscr->lines[wy]->hash = win->lines[wy]->hash;
128 		if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT)) {
129 			if (makech(win, wy) == ERR)
130 				return (ERR);
131 			else {
132 				if (*win->lines[wy]->firstchp >= win->ch_off)
133 					*win->lines[wy]->firstchp = win->maxx +
134 					    win->ch_off;
135 				if (*win->lines[wy]->lastchp < win->maxx +
136 				    win->ch_off)
137 					*win->lines[wy]->lastchp = win->ch_off;
138 				if (*win->lines[wy]->lastchp <
139 				    *win->lines[wy]->firstchp) {
140 #ifdef DEBUG
141 					__CTRACE("wrefresh: line %d notdirty \n", wy);
142 #endif
143 					win->lines[wy]->flags &= ~__ISDIRTY;
144 				}
145 			}
146 
147 		}
148 #ifdef DEBUG
149 		__CTRACE("\t%d\t%d\n", *win->lines[wy]->firstchp,
150 			*win->lines[wy]->lastchp);
151 #endif
152 	}
153 
154 #ifdef DEBUG
155 	__CTRACE("refresh: ly=%d, lx=%d\n", ly, lx);
156 #endif
157 
158 	if (win == curscr)
159 		domvcur(ly, lx, win->cury, win->curx);
160 	else {
161 		if (win->flags & __LEAVEOK) {
162 			curscr->cury = ly;
163 			curscr->curx = lx;
164 			ly -= win->begy;
165 			lx -= win->begx;
166 			if (ly >= 0 && ly < win->maxy && lx >= 0 &&
167 			    lx < win->maxx) {
168 				win->cury = ly;
169 				win->curx = lx;
170 			} else
171 				win->cury = win->curx = 0;
172 		} else {
173 			domvcur(ly, lx, win->cury + win->begy,
174 			    win->curx + win->begx);
175 			curscr->cury = win->cury + win->begy;
176 			curscr->curx = win->curx + win->begx;
177 		}
178 	}
179 	retval = OK;
180 
181 	(void)fflush(stdout);
182 	return (retval);
183 }
184 
185 /*
186  * makech --
187  *	Make a change on the screen.
188  */
189 static int
190 makech(win, wy)
191 	register WINDOW *win;
192 	int wy;
193 {
194 	static __LDATA blank = {' ', 0};
195 	register int nlsp, clsp;		/* Last space in lines. */
196 	register int wx, lch, y;
197 	register __LDATA *nsp, *csp, *cp, *cep;
198 	u_int force;
199 	char *ce;
200 
201 	/* Is the cursor still on the end of the last line? */
202 	if (wy > 0 && win->lines[wy - 1]->flags & __ISPASTEOL) {
203 		domvcur(ly, lx, ly + 1, 0);
204 		ly++;
205 		lx = 0;
206 	}
207 	wx = *win->lines[wy]->firstchp - win->ch_off;
208 	if (wx < 0)
209 		wx = 0;
210 	else if (wx >= win->maxx)
211 		return (OK);
212 	lch = *win->lines[wy]->lastchp - win->ch_off;
213 	if (lch < 0)
214 		return (OK);
215 	else if (lch >= (int) win->maxx)
216 		lch = win->maxx - 1;
217 	y = wy + win->begy;
218 
219 	if (curwin)
220 		csp = &blank;
221 	else
222 		csp = &curscr->lines[wy + win->begy]->line[wx + win->begx];
223 
224 	nsp = &win->lines[wy]->line[wx];
225 	force = win->lines[wy]->flags & __FORCEPAINT;
226 	win->lines[wy]->flags &= ~__FORCEPAINT;
227 	if (CE && !curwin) {
228 		for (cp = &win->lines[wy]->line[win->maxx - 1];
229 		     cp->ch == ' ' && cp->attr == 0; cp--)
230 			if (cp <= win->lines[wy]->line)
231 				break;
232 		nlsp = cp - win->lines[wy]->line;
233 	}
234 	if (!curwin)
235 		ce = CE;
236 	else
237 		ce = NULL;
238 
239 	if (force) {
240 		if (CM)
241 			tputs(tgoto(CM, lx, ly), 0, __cputchar);
242 		else {
243 			tputs(HO, 0, __cputchar);
244 			__mvcur(0, 0, ly, lx, 1);
245 		}
246 	}
247 	while (wx <= lch) {
248 		if (!force && memcmp(nsp, csp, sizeof(__LDATA)) == 0) {
249 			if (wx <= lch) {
250 				while (wx <= lch &&
251 				       memcmp(nsp, csp, sizeof(__LDATA)) == 0) {
252 					    nsp++;
253 					    if (!curwin)
254 						    csp++;
255 					    ++wx;
256 				    }
257 				continue;
258 			}
259 			break;
260 		}
261 		domvcur(ly, lx, y, wx + win->begx);
262 
263 #ifdef DEBUG
264 		__CTRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d, force =%d\n",
265 		    wx, ly, lx, y, wx + win->begx, force);
266 #endif
267 		ly = y;
268 		lx = wx + win->begx;
269 		while ((force || memcmp(nsp, csp, sizeof(__LDATA)) != 0)
270 		    && wx <= lch) {
271 
272 			if (ce != NULL && win->maxx + win->begx ==
273 			    curscr->maxx && wx >= nlsp && nsp->ch == ' ') {
274 				/* Check for clear to end-of-line. */
275 				cep = &curscr->lines[wy]->line[win->maxx - 1];
276 				while (cep->ch == ' ' && cep->attr == 0)
277 					if (cep-- <= csp)
278 						break;
279 				clsp = cep - curscr->lines[wy]->line -
280 				       win->begx * __LDATASIZE;
281 #ifdef DEBUG
282 			__CTRACE("makech: clsp = %d, nlsp = %d\n", clsp, nlsp);
283 #endif
284 				if ((clsp - nlsp >= strlen(CE)
285 				    && clsp < win->maxx * __LDATASIZE) ||
286 				    wy == win->maxy - 1) {
287 #ifdef DEBUG
288 					__CTRACE("makech: using CE\n");
289 #endif
290 					tputs(CE, 0, __cputchar);
291 					lx = wx + win->begx;
292 					while (wx++ <= clsp) {
293 						csp->ch = ' ';
294 						csp->attr = 0;
295 						csp++;
296 					}
297 					return (OK);
298 				}
299 				ce = NULL;
300 			}
301 
302 			/* Enter/exit standout mode as appropriate. */
303 			if (SO && (nsp->attr & __STANDOUT) !=
304 			    (curscr->flags & __WSTANDOUT)) {
305 				if (nsp->attr & __STANDOUT) {
306 					tputs(SO, 0, __cputchar);
307 					curscr->flags |= __WSTANDOUT;
308 				} else {
309 					tputs(SE, 0, __cputchar);
310 					curscr->flags &= ~__WSTANDOUT;
311 				}
312 			}
313 
314 			wx++;
315 			if (wx >= win->maxx && wy == win->maxy - 1 && !curwin)
316 				if (win->flags & __SCROLLOK) {
317 					if (curscr->flags & __WSTANDOUT
318 					    && win->flags & __ENDLINE)
319 						if (!MS) {
320 							tputs(SE, 0,
321 							    __cputchar);
322 							curscr->flags &=
323 							    ~__WSTANDOUT;
324 						}
325 					if (!(win->flags & __SCROLLWIN)) {
326 						if (!curwin) {
327 							csp->attr = nsp->attr;
328 							putchar(csp->ch = nsp->ch);
329 						} else
330 							putchar(nsp->ch);
331 					}
332 					if (wx + win->begx < curscr->maxx) {
333 						domvcur(ly, wx + win->begx,
334 						    win->begy + win->maxy - 1,
335 						    win->begx + win->maxx - 1);
336 					}
337 					ly = win->begy + win->maxy - 1;
338 					lx = win->begx + win->maxx - 1;
339 					return (OK);
340 				}
341 			if (wx < win->maxx || wy < win->maxy - 1 ||
342 			    !(win->flags & __SCROLLWIN)) {
343 				if (!curwin) {
344 					csp->attr = nsp->attr;
345 					putchar(csp->ch = nsp->ch);
346 					csp++;
347 				} else
348 					putchar(nsp->ch);
349 			}
350 #ifdef DEBUG
351 			__CTRACE("makech: putchar(%c)\n", nsp->ch & 0177);
352 #endif
353 			if (UC && (nsp->attr & __STANDOUT)) {
354 				putchar('\b');
355 				tputs(UC, 0, __cputchar);
356 			}
357 			nsp++;
358 #ifdef DEBUG
359 		__CTRACE("makech: 2: wx = %d, lx = %d\n", wx, lx);
360 #endif
361 		}
362 		if (lx == wx + win->begx)	/* If no change. */
363 			break;
364 		lx = wx + win->begx;
365 		if (lx >= COLS && AM)
366 			lx = COLS - 1;
367 		else if (wx >= win->maxx) {
368 			domvcur(ly, lx, ly, win->maxx + win->begx - 1);
369 			lx = win->maxx + win->begx - 1;
370 		}
371 
372 #ifdef DEBUG
373 		__CTRACE("makech: 3: wx = %d, lx = %d\n", wx, lx);
374 #endif
375 	}
376 	return (OK);
377 }
378 
379 /*
380  * domvcur --
381  *	Do a mvcur, leaving standout mode if necessary.
382  */
383 static void
384 domvcur(oy, ox, ny, nx)
385 	int oy, ox, ny, nx;
386 {
387 	if (curscr->flags & __WSTANDOUT && !MS) {
388 		tputs(SE, 0, __cputchar);
389 		curscr->flags &= ~__WSTANDOUT;
390 	}
391 
392 	__mvcur(oy, ox, ny, nx, 1);
393 }
394 
395 /*
396  * Quickch() attempts to detect a pattern in the change of the window
397  * in order to optimize the change, e.g., scroll n lines as opposed to
398  * repainting the screen line by line.
399  */
400 
401 static void
402 quickch(win)
403 	WINDOW *win;
404 {
405 #define THRESH		(int) win->maxy / 4
406 
407 	register __LINE *clp, *tmp1, *tmp2;
408 	register int bsize, curs, curw, starts, startw, i, j;
409 	int n, target, cur_period, bot, top, sc_region;
410 	__LDATA buf[1024];
411 	u_int blank_hash;
412 
413 	/*
414 	 * Find how many lines from the top of the screen are unchanged.
415 	 */
416 	for (top = 0; top < win->maxy; top++)
417 		if (win->lines[top]->flags & __FORCEPAINT ||
418 		    win->lines[top]->hash != curscr->lines[top]->hash
419 		    || memcmp(win->lines[top]->line,
420 		    curscr->lines[top]->line,
421 		    win->maxx * __LDATASIZE) != 0)
422 			break;
423 		else
424 			win->lines[top]->flags &= ~__ISDIRTY;
425        /*
426 	* Find how many lines from bottom of screen are unchanged.
427 	*/
428 	for (bot = win->maxy - 1; bot >= 0; bot--)
429 		if (win->lines[bot]->flags & __FORCEPAINT ||
430 		    win->lines[bot]->hash != curscr->lines[bot]->hash
431 		    || memcmp(win->lines[bot]->line,
432 		    curscr->lines[bot]->line,
433 		    win->maxx * __LDATASIZE) != 0)
434 			break;
435 		else
436 			win->lines[bot]->flags &= ~__ISDIRTY;
437 
438 #ifdef NO_JERKINESS
439 	/*
440 	 * If we have a bottom unchanged region return.  Scrolling the
441 	 * bottom region up and then back down causes a screen jitter.
442 	 * This will increase the number of characters sent to the screen
443 	 * but it looks better.
444 	 */
445 	if (bot < win->maxy - 1)
446 		return;
447 #endif /* NO_JERKINESS */
448 
449 	/*
450 	 * Search for the largest block of text not changed.
451 	 * Invariants of the loop:
452 	 * - Startw is the index of the beginning of the examined block in win.
453          * - Starts is the index of the beginning of the examined block in
454 	 *    curscr.
455 	 * - Curs is the index of one past the end of the exmined block in win.
456 	 * - Curw is the index of one past the end of the exmined block in
457 	 *   curscr.
458 	 * - bsize is the current size of the examined block.
459          */
460 	for (bsize = bot - top; bsize >= THRESH; bsize--) {
461 		for (startw = top; startw <= bot - bsize; startw++)
462 			for (starts = top; starts <= bot - bsize;
463 			     starts++) {
464 				for (curw = startw, curs = starts;
465 				     curs < starts + bsize; curw++, curs++)
466 					if (win->lines[curw]->flags &
467 					    __FORCEPAINT ||
468 					    (win->lines[curw]->hash !=
469 					    curscr->lines[curs]->hash ||
470 				            memcmp(win->lines[curw]->line,
471 					    curscr->lines[curs]->line,
472 					    win->maxx * __LDATASIZE) != 0))
473 						break;
474 				if (curs == starts + bsize)
475 					goto done;
476 			}
477 	}
478  done:
479 	/* Did not find anything */
480 	if (bsize < THRESH)
481 		return;
482 
483 #ifdef DEBUG
484 	__CTRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n",
485 		bsize, starts, startw, curw, curs, top, bot);
486 #endif
487 
488 	/*
489 	 * Make sure that there is no overlap between the bottom and top
490 	 * regions and the middle scrolled block.
491 	 */
492 	if (bot < curs)
493 		bot = curs - 1;
494 	if (top > starts)
495 		top = starts;
496 
497 	n = startw - starts;
498 
499 #ifdef DEBUG
500 		__CTRACE("#####################################\n");
501 		for (i = 0; i < curscr->maxy; i++) {
502 			__CTRACE("C: %d:", i);
503 			__CTRACE(" 0x%x \n", curscr->lines[i]->hash);
504 			for (j = 0; j < curscr->maxx; j++)
505 				__CTRACE("%c",
506 			           curscr->lines[i]->line[j].ch);
507 			__CTRACE("\n");
508 			for (j = 0; j < curscr->maxx; j++)
509 				__CTRACE("%x",
510 			           curscr->lines[i]->line[j].attr);
511 			__CTRACE("\n");
512 			__CTRACE("W: %d:", i);
513 			__CTRACE(" 0x%x \n", win->lines[i]->hash);
514 			__CTRACE(" 0x%x ", win->lines[i]->flags);
515 			for (j = 0; j < win->maxx; j++)
516 				__CTRACE("%c",
517 			           win->lines[i]->line[j].ch);
518 			__CTRACE("\n");
519 			for (j = 0; j < win->maxx; j++)
520 				__CTRACE("%x",
521 			           win->lines[i]->line[j].attr);
522 			__CTRACE("\n");
523 		}
524 #endif
525 
526 	/* So we don't have to call __hash() each time */
527 	for (i = 0; i < win->maxx; i++) {
528 		buf[i].ch = ' ';
529 		buf[i].attr = 0;
530 	}
531 	blank_hash = __hash((char *) buf, win->maxx * __LDATASIZE);
532 
533 	/*
534 	 * Perform the rotation to maintain the consistency of curscr.
535 	 * This is hairy since we are doing an *in place* rotation.
536 	 * Invariants of the loop:
537 	 * - I is the index of the current line.
538 	 * - Target is the index of the target of line i.
539 	 * - Tmp1 points to current line (i).
540 	 * - Tmp2 and points to target line (target);
541 	 * - Cur_period is the index of the end of the current period.
542 	 *   (see below).
543 	 *
544 	 * There are 2 major issues here that make this rotation non-trivial:
545 	 * 1.  Scrolling in a scrolling region bounded by the top
546 	 *     and bottom regions determined (whose size is sc_region).
547 	 * 2.  As a result of the use of the mod function, there may be a
548 	 *     period introduced, i.e., 2 maps to 4, 4 to 6, n-2 to 0, and
549 	 *     0 to 2, which then causes all odd lines not to be rotated.
550 	 *     To remedy this, an index of the end ( = beginning) of the
551 	 *     current 'period' is kept, cur_period, and when it is reached,
552 	 *     the next period is started from cur_period + 1 which is
553 	 *     guaranteed not to have been reached since that would mean that
554 	 *     all records would have been reached. (think about it...).
555 	 *
556 	 * Lines in the rotation can have 3 attributes which are marked on the
557 	 * line so that curscr is consistent with the visual screen.
558 	 * 1.  Not dirty -- lines inside the scrolled block, top region or
559 	 *                  bottom region.
560 	 * 2.  Blank lines -- lines in the differential of the scrolling
561 	 *		      region adjacent to top and bot regions
562 	 *                    depending on scrolling direction.
563 	 * 3.  Dirty line -- all other lines are marked dirty.
564 	 */
565 	sc_region = bot - top + 1;
566 	i = top;
567 	tmp1 = curscr->lines[top];
568 	cur_period = top;
569 	for (j = top; j <= bot; j++) {
570 		target = (i - top + n + sc_region) % sc_region + top;
571 		tmp2 = curscr->lines[target];
572 		curscr->lines[target] = tmp1;
573 		/* Mark block as clean and blank out scrolled lines. */
574 		clp = curscr->lines[target];
575 #ifdef DEBUG
576 		__CTRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ",
577 			n, startw, curw, i, target);
578 #endif
579 		if ((target >= startw && target < curw) || target < top
580 		    || target > bot) {
581 #ifdef DEBUG
582 			__CTRACE("-- notdirty");
583 #endif
584 			win->lines[target]->flags &= ~__ISDIRTY;
585 		} else if ((n > 0 && target >= top && target < top + n) ||
586 		           (n < 0 && target <= bot && target > bot + n)) {
587 			if (clp->hash != blank_hash ||  memcmp(clp->line,
588 			    buf, win->maxx * __LDATASIZE) !=0) {
589 				(void)memcpy(clp->line,  buf,
590 				    win->maxx * __LDATASIZE);
591 #ifdef DEBUG
592 				__CTRACE("-- blanked out: dirty");
593 #endif
594 				clp->hash = blank_hash;
595 				__touchline(win, target, 0, win->maxx - 1, 0);
596 			} else {
597 				__touchline(win, target, 0, win->maxx - 1, 0);
598 #ifdef DEBUG
599 				__CTRACE(" -- blank line already: dirty");
600 #endif
601 			}
602 		} else {
603 #ifdef DEBUG
604 			__CTRACE(" -- dirty");
605 #endif
606 			__touchline(win, target, 0, win->maxx - 1, 0);
607 		}
608 #ifdef DEBUG
609 		__CTRACE("\n");
610 #endif
611 		if (target == cur_period) {
612 			i = target + 1;
613 			tmp1 = curscr->lines[i];
614 			cur_period = i;
615 		} else {
616 			tmp1 = tmp2;
617 			i = target;
618 		}
619 	}
620 #ifdef DEBUG
621 		__CTRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
622 		for (i = 0; i < curscr->maxy; i++) {
623 			__CTRACE("C: %d:", i);
624 			for (j = 0; j < curscr->maxx; j++)
625 				__CTRACE("%c",
626 			           curscr->lines[i]->line[j].ch);
627 			__CTRACE("\n");
628 			__CTRACE("W: %d:", i);
629 			for (j = 0; j < win->maxx; j++)
630 				__CTRACE("%c",
631 			           win->lines[i]->line[j].ch);
632 			__CTRACE("\n");
633 		}
634 #endif
635 	if (n != 0) {
636 		WINDOW *wp;
637 		scrolln(win, starts, startw, curs, bot, top);
638 		/*
639 		 * Need to repoint any subwindow lines to the rotated
640 		 * line structured.
641 		 */
642 		for (wp = win->nextp; wp != win; wp = wp->nextp)
643 			__set_subwin(win, wp);
644 	}
645 }
646 
647 /*
648  * Scrolln performs the scroll by n lines, where n is starts - startw.
649  */
650 static void
651 scrolln(win, starts, startw, curs, bot, top)
652 	WINDOW *win;
653 	int starts, startw, curs, bot, top;
654 {
655 	int i, oy, ox, n;
656 
657 	oy = curscr->cury;
658 	ox = curscr->curx;
659 	n = starts - startw;
660 
661 	if (n > 0) {
662 		__mvcur(oy, ox, top, 0, 1);
663 		/* Scroll up the block */
664 		if (DL)
665 			tputs(__tscroll(DL, n), 0, __cputchar);
666 		else
667 			for(i = 0; i < n; i++)
668 				tputs(dl, 0, __cputchar);
669 
670 		/*
671 		 * Push down the bottom region.
672 		 */
673 		__mvcur(top, 0, bot - n + 1, 0, 1);
674 		if (AL)
675 			tputs(__tscroll(AL, n), 0, __cputchar);
676 		else
677 			for(i = 0; i < n; i++)
678 				tputs(al, 0, __cputchar);
679 		__mvcur(bot - n + 1, 0, oy, ox, 1);
680 	} else {
681 		/* Preserve the bottom lines */
682 		__mvcur(oy, ox, bot + n + 1, 0, 1);	/* n < 0 */
683 		if (DL)
684 			tputs(__tscroll(DL, -n), 0, __cputchar);
685 		else
686 		       	for(i = n; i < 0; i++)
687 				tputs(dl, 0, __cputchar);
688 		__mvcur(bot + n + 1, 0, top, 0, 1);
689 
690 		/* Scroll the block down */
691 		if (AL)
692 			tputs(__tscroll(AL, -n), 0, __cputchar);
693 		else
694 			for(i = n; i < 0; i++)
695 				tputs(al, 0, __cputchar);
696 		__mvcur(top, 0, oy, ox, 1);
697 	}
698 }
699