xref: /netbsd-src/lib/libcurses/refresh.c (revision 9616dacfef448e70e3fbbd865bddf60d54b656c5)
1 /*	$NetBSD: refresh.c,v 1.85 2017/01/10 10:33:49 roy Exp $	*/
2 
3 /*
4  * Copyright (c) 1981, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)refresh.c	8.7 (Berkeley) 8/13/94";
36 #else
37 __RCSID("$NetBSD: refresh.c,v 1.85 2017/01/10 10:33:49 roy Exp $");
38 #endif
39 #endif				/* not lint */
40 
41 #include <poll.h>
42 #include <stdlib.h>
43 #include <string.h>
44 
45 #include "curses.h"
46 #include "curses_private.h"
47 
48 static void	domvcur(const WINDOW *, int, int, int, int);
49 static int	makech(int);
50 static void	quickch(void);
51 static void	scrolln(int, int, int, int, int);
52 
53 static int _cursesi_wnoutrefresh(SCREEN *, WINDOW *,
54 				 int, int, int, int, int, int);
55 
56 #ifdef HAVE_WCHAR
57 int cellcmp( __LDATA *, __LDATA * );
58 int linecmp( __LDATA *, __LDATA *, size_t );
59 #endif /* HAVE_WCHAR */
60 
61 #define	CHECK_INTERVAL		5 /* Change N lines before checking typeahead */
62 
63 #ifndef _CURSES_USE_MACROS
64 
65 /*
66  * refresh --
67  *	Make the current screen look like "stdscr" over the area covered by
68  *	stdscr.
69  */
70 int
71 refresh(void)
72 {
73 
74 	return wrefresh(stdscr);
75 }
76 
77 #endif
78 
79 /*
80  * wnoutrefresh --
81  *	Add the contents of "win" to the virtual window.
82  */
83 int
84 wnoutrefresh(WINDOW *win)
85 {
86 
87 #ifdef DEBUG
88 	__CTRACE(__CTRACE_REFRESH, "wnoutrefresh: win %p\n", win);
89 #endif
90 
91 	return _cursesi_wnoutrefresh(_cursesi_screen, win, 0, 0, win->begy,
92 	    win->begx, win->maxy, win->maxx);
93 }
94 
95 /*
96  * pnoutrefresh --
97  *	Add the contents of "pad" to the virtual window.
98  */
99 int
100 pnoutrefresh(WINDOW *pad, int pbegy, int pbegx, int sbegy, int sbegx,
101 	     int smaxy, int smaxx)
102 {
103 	int pmaxy, pmaxx;
104 
105 #ifdef DEBUG
106 	__CTRACE(__CTRACE_REFRESH, "pnoutrefresh: pad %p, flags 0x%08x\n",
107 	    pad, pad->flags);
108 	__CTRACE(__CTRACE_REFRESH,
109 	    "pnoutrefresh: (%d, %d), (%d, %d), (%d, %d)\n",
110 	    pbegy, pbegx, sbegy, sbegx, smaxy, smaxx);
111 #endif
112 
113 	/* SUS says if these are negative, they should be treated as zero */
114 	if (pbegy < 0)
115 		pbegy = 0;
116 	if (pbegx < 0)
117 		pbegx = 0;
118 	if (sbegy < 0)
119 		sbegy = 0;
120 	if (sbegx < 0)
121 		sbegx = 0;
122 
123 	/* Calculate rectangle on pad - used by _cursesi_wnoutrefresh */
124 	pmaxy = pbegy + smaxy - sbegy + 1;
125 	pmaxx = pbegx + smaxx - sbegx + 1;
126 
127 	/* Check rectangle fits in pad */
128 	if (pmaxy > pad->maxy - pad->begy)
129 		pmaxy = pad->maxy - pad->begy;
130 	if (pmaxx > pad->maxx - pad->begx)
131 		pmaxx = pad->maxx - pad->begx;
132 
133 	if (smaxy - sbegy < 0 || smaxx - sbegx < 0 )
134 		return ERR;
135 
136 	return _cursesi_wnoutrefresh(_cursesi_screen, pad,
137 	    pad->begy + pbegy, pad->begx + pbegx, pad->begy + sbegy,
138 	    pad->begx + sbegx, pmaxy, pmaxx);
139 }
140 
141 /*
142  * _cursesi_wnoutrefresh --
143  *	Does the grunt work for wnoutrefresh to the given screen.
144  *	Copies the part of the window given by the rectangle
145  *	(begy, begx) to (maxy, maxx) at screen position (wbegy, wbegx).
146  */
147 int
148 _cursesi_wnoutrefresh(SCREEN *screen, WINDOW *win, int begy, int begx,
149 		      int wbegy, int wbegx, int maxy, int maxx)
150 {
151 
152 	short	sy, wy, wx, y_off, x_off, mx, dy_off, dx_off, endy;
153 	__LINE	*wlp, *vlp, *dwlp;
154 	WINDOW	*sub_win, *orig, *swin, *dwin;
155 
156 #ifdef DEBUG
157 	__CTRACE(__CTRACE_REFRESH, "_wnoutrefresh: win %p, flags 0x%08x\n",
158 	    win, win->flags);
159 	__CTRACE(__CTRACE_REFRESH,
160 	    "_wnoutrefresh: (%d, %d), (%d, %d), (%d, %d)\n",
161 	    begy, begx, wbegy, wbegx, maxy, maxx);
162 #endif
163 
164 	if (screen->curwin)
165 		return OK;
166 
167 	swin = dwin = win;
168 	if (win->flags & __ISDERWIN)
169 		swin = win->orig;
170 
171 	/*
172 	 * Recurse through any sub-windows, mark as dirty lines on the parent
173 	 * window that are dirty on the sub-window and clear the dirty flag on
174 	 * the sub-window.
175 	 */
176 	if (dwin->orig == 0) {
177 		orig = dwin;
178 		for (sub_win = dwin->nextp; sub_win != orig;
179 		    sub_win = sub_win->nextp) {
180 			if (sub_win->flags & __ISDERWIN)
181 				continue;
182 #ifdef DEBUG
183 			__CTRACE(__CTRACE_REFRESH,
184 			    "wnout_refresh: win %p, sub_win %p\n",
185 			    orig, sub_win);
186 #endif
187 			for (sy = 0; sy < sub_win->maxy; sy++) {
188 				if (sub_win->alines[sy]->flags & __ISDIRTY) {
189 					orig->alines[sy + sub_win->begy - orig->begy]->flags
190 					    |= __ISDIRTY;
191 					sub_win->alines[sy]->flags
192 					    &= ~__ISDIRTY;
193 				}
194 				if (sub_win->alines[sy]->flags & __ISFORCED) {
195 					orig->alines[sy + sub_win->begy - orig->begy]->flags
196 					    |= __ISFORCED;
197 					sub_win->alines[sy]->flags
198 					    &= ~__ISFORCED;
199 				}
200 			}
201 		}
202 	}
203 
204 	/* Check that cursor position on "win" is valid for "__virtscr" */
205 	if (dwin->cury + wbegy - begy < screen->__virtscr->maxy &&
206 	    dwin->cury + wbegy - begy >= 0 && dwin->cury < maxy - begy)
207 		screen->__virtscr->cury = dwin->cury + wbegy - begy;
208 	if (dwin->curx + wbegx - begx < screen->__virtscr->maxx &&
209 	    dwin->curx + wbegx - begx >= 0 && dwin->curx < maxx - begx)
210 		screen->__virtscr->curx = dwin->curx + wbegx - begx;
211 
212 	/* Copy the window flags from "win" to "__virtscr" */
213 	if (dwin->flags & __CLEAROK) {
214 		if (dwin->flags & __FULLWIN)
215 			screen->__virtscr->flags |= __CLEAROK;
216 		dwin->flags &= ~__CLEAROK;
217 	}
218 	screen->__virtscr->flags &= ~__LEAVEOK;
219 	screen->__virtscr->flags |= dwin->flags;
220 
221 	if ((dwin->flags & __ISDERWIN) != 0)
222 		endy = begy + maxy;
223 	else
224 		endy = maxy;
225 
226 	for (wy = begy, y_off = wbegy, dy_off = 0; wy < endy &&
227 	    y_off < screen->__virtscr->maxy; wy++, y_off++, dy_off++)
228 	{
229 		wlp = swin->alines[wy];
230 		dwlp = dwin->alines[dy_off];
231 #ifdef DEBUG
232 		__CTRACE(__CTRACE_REFRESH,
233 		    "_wnoutrefresh: wy %d\tf %d\tl %d\tflags %x\n",
234 		    wy, *wlp->firstchp, *wlp->lastchp, wlp->flags);
235 
236 		if ((dwin->flags & __ISDERWIN) != 0) {
237 			__CTRACE(__CTRACE_REFRESH,
238 			"_wnoutrefresh: derwin wy %d\tf %d\tl %d\tflags %x\n",
239 			dy_off, *dwlp->firstchp, *dwlp->lastchp, dwlp->flags);
240 			__CTRACE(__CTRACE_REFRESH,
241 			"_wnoutrefresh: derwin maxx %d\tch_off %d\n",
242 			dwin->maxx, dwin->ch_off);
243 		}
244 #endif
245 		if (((wlp->flags & (__ISDIRTY | __ISFORCED)) == 0) &&
246 		    ((dwlp->flags & (__ISDIRTY | __ISFORCED)) == 0))
247 			continue;
248 		vlp = screen->__virtscr->alines[y_off];
249 
250 		if ((*wlp->firstchp < maxx + swin->ch_off &&
251 		    *wlp->lastchp >= swin->ch_off) ||
252 		    ((((dwin->flags & __ISDERWIN) != 0) &&
253 		     (*dwlp->firstchp < dwin->maxx + dwin->ch_off &&
254 		      *dwlp->lastchp >= dwin->ch_off))))
255 		{
256 			/* Set start column */
257 			wx = begx;
258 			x_off = wbegx;
259 			dx_off = 0;
260 			/*
261 			 * if a derwin then source change pointers aren't
262 			 * relevant.
263 			 */
264 			if ((dwin->flags & __ISDERWIN) != 0)
265 				mx = wx + maxx;
266 			else {
267 				if (*wlp->firstchp - swin->ch_off > 0) {
268 					wx += *wlp->firstchp - swin->ch_off;
269 					x_off += *wlp->firstchp - swin->ch_off;
270 				}
271 				mx = maxx;
272 				if (mx > *wlp->lastchp - swin->ch_off + 1)
273 					mx = *dwlp->lastchp - dwin->ch_off + 1;
274 				if (x_off + (mx - wx) > __virtscr->maxx)
275 					mx -= (x_off + maxx) - __virtscr->maxx;
276 			}
277 
278 			/* Copy line from "win" to "__virtscr". */
279 			while (wx < mx) {
280 #ifdef DEBUG
281 				__CTRACE(__CTRACE_REFRESH,
282 				    "_wnoutrefresh: copy from %d, "
283 				    "%d to %d, %d: %s, 0x%x",
284 				    wy, wx, y_off, x_off,
285 				    unctrl(wlp->line[wx].ch),
286 				    wlp->line[wx].attr);
287 #endif
288 				/* Copy character */
289 				vlp->line[x_off].ch = wlp->line[wx].ch;
290 				/* Copy attributes  */
291 				vlp->line[x_off].attr = wlp->line[wx].attr;
292 				/* Check for nca conflict with colour */
293 				if ((vlp->line[x_off].attr & __COLOR) &&
294 				    (vlp->line[x_off].attr &
295 				    _cursesi_screen->nca))
296 					vlp->line[x_off].attr &= ~__COLOR;
297 				if (win->flags & __ISDERWIN) {
298 					dwlp->line[dx_off].ch =
299 						wlp->line[wx].ch;
300 					dwlp->line[dx_off].attr =
301 						wlp->line[wx].attr;
302 				}
303 
304 #ifdef HAVE_WCHAR
305 				if (wlp->line[wx].ch
306 				    == (wchar_t)btowc((int) win->bch)) {
307 					vlp->line[x_off].ch = win->bch;
308 					SET_WCOL( vlp->line[x_off], 1 );
309 					if (_cursesi_copy_nsp(win->bnsp,
310 							      &vlp->line[x_off])
311 					    == ERR)
312 						return ERR;
313 					if (win->flags & __ISDERWIN) {
314 						dwlp->line[dx_off].ch =
315 							win->bch;
316 						SET_WCOL(dwlp->line[dx_off], 1);
317 						if (_cursesi_copy_nsp(win->bnsp,
318 						     &dwlp->line[dx_off])
319 						    == ERR)
320 							return ERR;
321 					}
322 				}
323 #endif /* HAVE_WCHAR */
324 #ifdef DEBUG
325 				__CTRACE(__CTRACE_REFRESH, " = %s, 0x%x\n",
326 				    unctrl(vlp->line[x_off].ch),
327 				    vlp->line[x_off].attr);
328 #endif
329 				wx++;
330 				x_off++;
331 				dx_off++;
332 			}
333 
334 			/* Set flags on "__virtscr" and unset on "win". */
335 			if (wlp->flags & __ISPASTEOL)
336 				vlp->flags |= __ISPASTEOL;
337 			else
338 				vlp->flags &= ~__ISPASTEOL;
339 			if (wlp->flags & __ISDIRTY)
340 				vlp->flags |= __ISDIRTY;
341 			if (wlp->flags & __ISFORCED)
342 				vlp->flags |= __ISFORCED;
343 
344 #ifdef DEBUG
345 			__CTRACE(__CTRACE_REFRESH,
346 			    "win: firstch = %d, lastch = %d\n",
347 			    *wlp->firstchp, *wlp->lastchp);
348 			if (win->flags & __ISDERWIN) {
349 				__CTRACE(__CTRACE_REFRESH,
350 				    "derwin: fistch = %d, lastch = %d\n",
351 				    *dwlp->firstchp, *dwlp->lastchp);
352 			}
353 #endif
354 			/* Set change pointers on "__virtscr". */
355 			if (*vlp->firstchp >
356 				*wlp->firstchp + wbegx - win->ch_off)
357 					*vlp->firstchp =
358 					    *wlp->firstchp + wbegx - win->ch_off;
359 			if (*vlp->lastchp <
360 				*wlp->lastchp + wbegx - win->ch_off)
361 					*vlp->lastchp =
362 					    *wlp->lastchp + wbegx - win->ch_off;
363 
364 			if (win->flags & __ISDERWIN) {
365 				if (*vlp->firstchp >
366 				    *dwlp->firstchp + wbegx - dwin->ch_off)
367 				{
368 					*vlp->firstchp =
369 					    *dwlp->firstchp + wbegx
370 						- dwin->ch_off;
371 					vlp->flags |= __ISDIRTY;
372 				}
373 
374 				if (*vlp->lastchp <
375 				    *dwlp->lastchp + wbegx - dwin->ch_off)
376 				{
377 					*vlp->lastchp = *dwlp->lastchp
378 					    + wbegx - dwin->ch_off;
379 					vlp->flags |= __ISDIRTY;
380 				}
381 			}
382 
383 #ifdef DEBUG
384 			__CTRACE(__CTRACE_REFRESH,
385 			    "__virtscr: firstch = %d, lastch = %d\n",
386 			    *vlp->firstchp, *vlp->lastchp);
387 #endif
388 			/*
389 			 * Unset change pointers only if a window and we
390 			 * are not forcing a redraw. A pad can be displayed
391 			 * again without any of the contents changing.
392 			 */
393 			if ((!(win->flags & __ISPAD)) ||
394 			    ((wlp->flags & __ISFORCED) == __ISFORCED))
395 			{
396 				/* Set change pointers on "win". */
397 				if (*wlp->firstchp >= win->ch_off)
398 					*wlp->firstchp = maxx + win->ch_off;
399 				if (*wlp->lastchp < maxx + win->ch_off)
400 					*wlp->lastchp = win->ch_off;
401 				if ((*wlp->lastchp < *wlp->firstchp) ||
402 				    (*wlp->firstchp >= maxx + win->ch_off) ||
403 				    (*wlp->lastchp <= win->ch_off)) {
404 #ifdef DEBUG
405 					__CTRACE(__CTRACE_REFRESH,
406 					    "_wnoutrefresh: "
407 					    "line %d notdirty\n", wy);
408 #endif
409 					wlp->flags &= ~(__ISDIRTY | __ISFORCED);
410 				}
411 			}
412 		}
413 	}
414 	return OK;
415 }
416 
417 /*
418  * wrefresh --
419  *	Make the current screen look like "win" over the area covered by
420  *	win.
421  */
422 int
423 wrefresh(WINDOW *win)
424 {
425 	int retval;
426 	int pbegx, pbegy;
427 
428 #ifdef DEBUG
429 	__CTRACE(__CTRACE_REFRESH, "wrefresh: win %p\n", win);
430 #endif
431 
432 	_cursesi_screen->curwin = (win == _cursesi_screen->curscr);
433 	if (!_cursesi_screen->curwin) {
434 		pbegx = pbegy = 0;
435 		if ((win->flags & __ISDERWIN) == __ISDERWIN) {
436 			pbegx = win->derx;
437 			pbegy = win->dery;
438 #ifdef DEBUG
439 	__CTRACE(__CTRACE_REFRESH, "wrefresh: derwin, begy = %d, begx = %x\n",
440 		pbegy, pbegx);
441 #endif
442 		}
443 		retval = _cursesi_wnoutrefresh(_cursesi_screen, win, pbegy,
444 		    pbegx, win->begy, win->begx, win->maxy, win->maxx);
445 	} else
446 		retval = OK;
447 	if (retval == OK) {
448 		retval = doupdate();
449 		if (!(win->flags & __LEAVEOK)) {
450 			win->cury = max(0, curscr->cury - win->begy);
451 			win->curx = max(0, curscr->curx - win->begx);
452 		}
453 	}
454 	_cursesi_screen->curwin = 0;
455 	return retval;
456 }
457 
458  /*
459  * prefresh --
460  *	Make the current screen look like "pad" over the area coverd by
461  *	the specified area of pad.
462  */
463 int
464 prefresh(WINDOW *pad, int pbegy, int pbegx, int sbegy, int sbegx,
465 	int smaxy, int smaxx)
466 {
467 	int retval;
468 
469 #ifdef DEBUG
470 	__CTRACE(__CTRACE_REFRESH, "prefresh: pad %p, flags 0x%08x\n",
471 	    pad, pad->flags);
472 #endif
473 	/* Retain values in case pechochar() is called. */
474 	pad->pbegy = pbegy;
475 	pad->pbegx = pbegx;
476 	pad->sbegy = sbegy;
477 	pad->sbegx = sbegx;
478 	pad->smaxy = smaxy;
479 	pad->smaxx = smaxx;
480 
481 	/* Use pnoutrefresh() to avoid duplicating code here */
482 	retval = pnoutrefresh(pad, pbegy, pbegx, sbegy, sbegx, smaxy, smaxx);
483 	if (retval == OK) {
484 		retval = doupdate();
485 		if (!(pad->flags & __LEAVEOK)) {
486 			pad->cury = max(0, curscr->cury - pad->begy);
487 			pad->curx = max(0, curscr->curx - pad->begx);
488 		}
489 	}
490 	return retval;
491 }
492 
493 /*
494  * doupdate --
495  *	Make the current screen look like the virtual window "__virtscr".
496  */
497 int
498 doupdate(void)
499 {
500 	WINDOW	*win;
501 	__LINE	*wlp, *vlp;
502 	short	 wy;
503 	int	 dnum, was_cleared, changed;
504 #ifdef HAVE_WCHAR
505 	__LDATA *lp;
506 	nschar_t *np;
507 	int x;
508 #endif /* HAVE_WCHAR */
509 
510 	/* Check if we need to restart ... */
511 	if (_cursesi_screen->endwin)
512 		__restartwin();
513 
514 	if (_cursesi_screen->curwin)
515 		win = curscr;
516 	else
517 		win = _cursesi_screen->__virtscr;
518 
519 	/* Initialize loop parameters. */
520 	_cursesi_screen->ly = curscr->cury;
521 	_cursesi_screen->lx = curscr->curx;
522 	wy = 0;
523 
524 	if (!_cursesi_screen->curwin) {
525 		for (wy = 0; wy < win->maxy; wy++) {
526 			wlp = win->alines[wy];
527 			if (wlp->flags & __ISDIRTY) {
528 #ifndef HAVE_WCHAR
529 				wlp->hash = __hash(wlp->line,
530 				    (size_t)(win->maxx * __LDATASIZE));
531 #else
532 				wlp->hash = 0;
533 				for ( x = 0; x < win->maxx; x++ ) {
534 					lp = &wlp->line[ x ];
535 					wlp->hash = __hash_more( &lp->ch,
536 						sizeof(wchar_t), wlp->hash );
537 					wlp->hash = __hash_more( &lp->attr,
538 						sizeof(attr_t), wlp->hash );
539 					np = lp->nsp;
540 					if (np) {
541 						while (np) {
542 							wlp->hash
543 							    = __hash_more(
544 								&np->ch,
545 								sizeof(wchar_t),
546 								wlp->hash);
547 							np = np->next;
548 						}
549 					}
550 				}
551 #endif /* HAVE_WCHAR */
552 			}
553 		}
554 	}
555 
556 	was_cleared = 0;
557 	if ((win->flags & __CLEAROK) || (curscr->flags & __CLEAROK) ||
558 	    _cursesi_screen->curwin)
559 	{
560 		if (curscr->wattr & __COLOR)
561 			__unsetattr(0);
562 		tputs(clear_screen, 0, __cputchar);
563 		_cursesi_screen->ly = 0;
564 		_cursesi_screen->lx = 0;
565 		if (!_cursesi_screen->curwin) {
566 			curscr->flags &= ~__CLEAROK;
567 			curscr->cury = 0;
568 			curscr->curx = 0;
569 			werase(curscr);
570 		}
571 		__touchwin(win);
572 		win->flags &= ~__CLEAROK;
573 		/* note we cleared for later */
574 		was_cleared = 1;
575 	}
576 	if (!cursor_address) {
577 		if (win->curx != 0)
578 			__cputchar('\n');
579 		if (!_cursesi_screen->curwin)
580 			werase(curscr);
581 	}
582 #ifdef DEBUG
583 	__CTRACE(__CTRACE_REFRESH, "doupdate: (%p): curwin = %d\n", win,
584 	    _cursesi_screen->curwin);
585 	__CTRACE(__CTRACE_REFRESH, "doupdate: \tfirstch\tlastch\n");
586 #endif
587 
588 	if (!_cursesi_screen->curwin) {
589 		/*
590 		 * Invoke quickch() only if more than a quarter of the lines
591 		 * in the window are dirty.
592 		 */
593 		for (wy = 0, dnum = 0; wy < win->maxy; wy++)
594 			if (win->alines[wy]->flags & __ISDIRTY)
595 				dnum++;
596 		if (!__noqch && dnum > (int) win->maxy / 4)
597 			quickch();
598 	}
599 
600 #ifdef DEBUG
601 	{
602 		int	i, j;
603 
604 		__CTRACE(__CTRACE_REFRESH,
605 		    "#####################################\n");
606 		__CTRACE(__CTRACE_REFRESH,
607 		    "stdscr(%p)-curscr(%p)-__virtscr(%p)\n",
608 		    stdscr, curscr, _cursesi_screen->__virtscr);
609 		for (i = 0; i < curscr->maxy; i++) {
610 			__CTRACE(__CTRACE_REFRESH, "C: %d:", i);
611 			__CTRACE(__CTRACE_REFRESH, " 0x%x \n",
612 			    curscr->alines[i]->hash);
613 			for (j = 0; j < curscr->maxx; j++)
614 				__CTRACE(__CTRACE_REFRESH, "%c",
615 				    curscr->alines[i]->line[j].ch);
616 			__CTRACE(__CTRACE_REFRESH, "\n");
617 			__CTRACE(__CTRACE_REFRESH, " attr:");
618 			for (j = 0; j < curscr->maxx; j++)
619 				__CTRACE(__CTRACE_REFRESH, " %x",
620 				    curscr->alines[i]->line[j].attr);
621 			__CTRACE(__CTRACE_REFRESH, "\n");
622 			__CTRACE(__CTRACE_REFRESH, "W: %d:", i);
623 			__CTRACE(__CTRACE_REFRESH, " 0x%x \n",
624 			    win->alines[i]->hash);
625 			__CTRACE(__CTRACE_REFRESH, " 0x%x ",
626 			    win->alines[i]->flags);
627 			for (j = 0; j < win->maxx; j++)
628 				__CTRACE(__CTRACE_REFRESH, "%c",
629 				    win->alines[i]->line[j].ch);
630 			__CTRACE(__CTRACE_REFRESH, "\n");
631 			__CTRACE(__CTRACE_REFRESH, " attr:");
632 			for (j = 0; j < win->maxx; j++)
633 				__CTRACE(__CTRACE_REFRESH, " %x",
634 				    win->alines[i]->line[j].attr);
635 			__CTRACE(__CTRACE_REFRESH, "\n");
636 #ifdef HAVE_WCHAR
637 			__CTRACE(__CTRACE_REFRESH, " nsp:");
638 			for (j = 0; j < curscr->maxx; j++)
639 				__CTRACE(__CTRACE_REFRESH, " %p",
640 				    win->alines[i]->line[j].nsp);
641 			__CTRACE(__CTRACE_REFRESH, "\n");
642 			__CTRACE(__CTRACE_REFRESH, " bnsp:");
643 			for (j = 0; j < curscr->maxx; j++)
644 				__CTRACE(__CTRACE_REFRESH, " %p",
645 				    win->bnsp);
646 			__CTRACE(__CTRACE_REFRESH, "\n");
647 #endif /* HAVE_WCHAR */
648 		}
649 	}
650 #endif /* DEBUG */
651 
652 	changed = 0;
653 	for (wy = 0; wy < win->maxy; wy++) {
654 		wlp = win->alines[wy];
655 		vlp = _cursesi_screen->__virtscr->alines[win->begy + wy];
656 /* XXX: remove this debug */
657 #ifdef DEBUG
658 		__CTRACE(__CTRACE_REFRESH,
659 		    "doupdate: wy %d\tf: %d\tl:%d\tflags %x\n",
660 		    wy, *wlp->firstchp, *wlp->lastchp, wlp->flags);
661 #endif /* DEBUG */
662 		if (!_cursesi_screen->curwin)
663 			curscr->alines[wy]->hash = wlp->hash;
664 		if (wlp->flags & __ISDIRTY || wlp->flags & __ISFORCED) {
665 #ifdef DEBUG
666 			__CTRACE(__CTRACE_REFRESH,
667 			    "doupdate: [ISDIRTY]wy:%d\tf:%d\tl:%d\n", wy,
668 			    *wlp->firstchp, *wlp->lastchp);
669 #endif /* DEBUG */
670 			/*
671 			 * We have just cleared so don't force an update
672 			 * otherwise we spray neeedless blanks to a cleared
673 			 * screen.
674 			 */
675 			if (was_cleared == 1)
676 				win->alines[wy]->flags &= ~__ISFORCED;
677 
678 			if (makech(wy) == ERR)
679 				return ERR;
680 			else {
681 				if (*wlp->firstchp >= 0)
682 					*wlp->firstchp = win->maxx;
683 				if (*wlp->lastchp < win->maxx)
684 					*wlp->lastchp = win->ch_off;
685 				if (*wlp->lastchp < *wlp->firstchp) {
686 #ifdef DEBUG
687 					__CTRACE(__CTRACE_REFRESH,
688 					    "doupdate: line %d notdirty\n", wy);
689 #endif /* DEBUG */
690 					wlp->flags &= ~(__ISDIRTY | __ISFORCED);
691 				}
692 
693 				/* Check if we have input after
694 				 * changing N lines. */
695 				if (_cursesi_screen->checkfd != -1 &&
696 				    ++changed == CHECK_INTERVAL)
697 				{
698 					struct pollfd fds[1];
699 
700 					/* If we have input, abort. */
701 					fds[0].fd = _cursesi_screen->checkfd;
702 					fds[0].events = POLLIN;
703 					if (poll(fds, 1, 0) > 0)
704 						goto cleanup;
705 					changed = 0;
706 				}
707 			}
708 		}
709 
710 		/*
711 		 * virtscr is now synced for the line, unset the change
712 		 * pointers.
713 		 */
714 		if (*vlp->firstchp >= 0)
715 			*vlp->firstchp = _cursesi_screen->__virtscr->maxx;
716 		if (*vlp->lastchp <= _cursesi_screen->__virtscr->maxx)
717 			*vlp->lastchp = 0;
718 
719 #ifdef DEBUG
720 		__CTRACE(__CTRACE_REFRESH, "\t%d\t%d\n",
721 		    *wlp->firstchp, *wlp->lastchp);
722 #endif /* DEBUG */
723 	}
724 
725 #ifdef DEBUG
726 	__CTRACE(__CTRACE_REFRESH, "doupdate: ly=%d, lx=%d\n",
727 	    _cursesi_screen->ly, _cursesi_screen->lx);
728 #endif /* DEBUG */
729 
730 	if (_cursesi_screen->curwin)
731 		domvcur(win, _cursesi_screen->ly, _cursesi_screen->lx,
732 			win->cury, win->curx);
733 	else {
734 		if (win->flags & __LEAVEOK) {
735 			curscr->cury = _cursesi_screen->ly;
736 			curscr->curx = _cursesi_screen->lx;
737 		} else {
738 			domvcur(win, _cursesi_screen->ly, _cursesi_screen->lx,
739 				win->cury, win->curx);
740 			curscr->cury = win->cury;
741 			curscr->curx = win->curx;
742 		}
743 	}
744 
745 cleanup:
746 	/* Don't leave the screen with attributes set. */
747 	__unsetattr(0);
748 #ifdef DEBUG
749 #ifdef HAVE_WCHAR
750 	{
751 		int	i, j;
752 
753 		__CTRACE(__CTRACE_REFRESH,
754 		    "***********after*****************\n");
755 		__CTRACE(__CTRACE_REFRESH,
756 		    "stdscr(%p)-curscr(%p)-__virtscr(%p)\n",
757 		    stdscr, curscr, _cursesi_screen->__virtscr);
758 		for (i = 0; i < curscr->maxy; i++) {
759 			for (j = 0; j < curscr->maxx; j++)
760 				__CTRACE(__CTRACE_REFRESH,
761 				    "[%d,%d](%x,%x,%p)-(%x,%x,%p)\n",
762 				    i, j,
763 				    curscr->alines[i]->line[j].ch,
764 				    curscr->alines[i]->line[j].attr,
765 				    curscr->alines[i]->line[j].nsp,
766 				    _cursesi_screen->__virtscr->alines[i]->line[j].ch,
767 				    _cursesi_screen->__virtscr->alines[i]->line[j].attr,
768 				    _cursesi_screen->__virtscr->alines[i]->line[j].nsp);
769 		}
770 	}
771 #endif /* HAVE_WCHAR */
772 #endif /* DEBUG */
773 	return fflush(_cursesi_screen->outfd) == EOF ? ERR : OK;
774 }
775 
776 /*
777  * makech --
778  *	Make a change on the screen.
779  */
780 static int
781 makech(int wy)
782 {
783 	WINDOW	*win;
784 	static __LDATA blank;
785 	__LDATA *nsp, *csp, *cp, *cep;
786 	__LINE *wlp;
787 	size_t	clsp, nlsp;	/* Last space in lines. */
788 	int	lch, wx;
789 	const char	*ce;
790 	attr_t	lspc;		/* Last space colour */
791 	attr_t	off, on;
792 
793 #ifdef __GNUC__
794 	nlsp = lspc = 0;	/* XXX gcc -Wuninitialized */
795 #endif
796 	if (_cursesi_screen->curwin)
797 		win = curscr;
798 	else
799 		win = __virtscr;
800 #ifdef HAVE_WCHAR
801 	blank.ch = (wchar_t)btowc((int) win->bch);
802 	blank.attr = 0;
803 	if (_cursesi_copy_nsp(win->bnsp, &blank) == ERR)
804 		return ERR;
805 	SET_WCOL(blank, 1);
806 #endif /* HAVE_WCHAR */
807 #ifdef DEBUG
808 #if HAVE_WCHAR
809 	{
810 		int x;
811 		__LDATA *lp, *vlp;
812 
813 		__CTRACE(__CTRACE_REFRESH,
814 		    "[makech-before]wy=%d,curscr(%p)-__virtscr(%p)\n",
815 		    wy, curscr, __virtscr);
816 		for (x = 0; x < curscr->maxx; x++) {
817 			lp = &curscr->alines[wy]->line[x];
818 			vlp = &__virtscr->alines[wy]->line[x];
819 			__CTRACE(__CTRACE_REFRESH,
820 			    "[%d,%d](%x,%x,%x,%x,%p)-"
821 			    "(%x,%x,%x,%x,%p)\n",
822 			    wy, x, lp->ch, lp->attr,
823 			    win->bch, win->battr, lp->nsp,
824 			    vlp->ch, vlp->attr,
825 			    win->bch, win->battr, vlp->nsp);
826 		}
827 	}
828 #endif /* HAVE_WCHAR */
829 #endif /* DEBUG */
830 	/* Is the cursor still on the end of the last line? */
831 	if (wy > 0 && curscr->alines[wy - 1]->flags & __ISPASTEOL) {
832 		domvcur(win, _cursesi_screen->ly, _cursesi_screen->lx,
833 			_cursesi_screen->ly + 1, 0);
834 		_cursesi_screen->ly++;
835 		_cursesi_screen->lx = 0;
836 	}
837 	wlp = win->alines[wy];
838 	wx = *win->alines[wy]->firstchp;
839 	if (wx < 0)
840 		wx = 0;
841 	else
842 		if (wx >= win->maxx)
843 			return (OK);
844 	lch = *win->alines[wy]->lastchp;
845 	if (lch < 0)
846 		return OK;
847 	else
848 		if (lch >= (int) win->maxx)
849 			lch = win->maxx - 1;
850 
851 	if (_cursesi_screen->curwin) {
852 		csp = &blank;
853 #ifdef DEBUG
854 		__CTRACE(__CTRACE_REFRESH, "makech: csp is blank\n");
855 #endif /* DEBUG */
856 	} else {
857 		csp = &curscr->alines[wy]->line[wx];
858 #ifdef DEBUG
859 		__CTRACE(__CTRACE_REFRESH,
860 		    "makech: csp is on curscr:(%d,%d)\n", wy, wx);
861 #endif /* DEBUG */
862 	}
863 
864 	nsp = &win->alines[wy]->line[wx];
865 #ifdef DEBUG
866 	if (_cursesi_screen->curwin)
867 		__CTRACE(__CTRACE_REFRESH,
868 		    "makech: nsp is at curscr:(%d,%d)\n", wy, wx);
869 	else
870 		__CTRACE(__CTRACE_REFRESH,
871 		    "makech: nsp is at __virtscr:(%d,%d)\n", wy, wx);
872 #endif /* DEBUG */
873 	if (clr_eol && !_cursesi_screen->curwin) {
874 		cp = &win->alines[wy]->line[win->maxx - 1];
875 		lspc = cp->attr & __COLOR;
876 #ifndef HAVE_WCHAR
877 		while (cp->ch == ' ' && cp->attr == lspc) /* XXX */
878 			if (cp-- <= win->alines[wy]->line)
879 				break;
880 #else
881 		while (cp->ch == ( wchar_t )btowc(( int )' ' )
882 				&& ( cp->attr & WA_ATTRIBUTES ) == lspc)
883 			if (cp-- <= win->alines[wy]->line)
884 				break;
885 #endif /* HAVE_WCHAR */
886 		if (win->alines[wy]->line > cp)
887 			nlsp = 0;
888 		else
889 			nlsp = cp - win->alines[wy]->line;
890 	}
891 	if (!_cursesi_screen->curwin)
892 		ce = clr_eol;
893 	else
894 		ce = NULL;
895 
896 	while (wx <= lch) {
897 #ifdef DEBUG
898 		__CTRACE(__CTRACE_REFRESH, "makech: wx=%d,lch=%d\n", wx, lch);
899 #endif /* DEBUG */
900 #ifndef HAVE_WCHAR
901 		if (!(wlp->flags & __ISFORCED) &&
902 		    (memcmp(nsp, csp, sizeof(__LDATA)) == 0))
903 		{
904 			if (wx <= lch) {
905 				while (wx <= lch &&
906 				    memcmp(nsp, csp, sizeof(__LDATA)) == 0)
907 				{
908 					nsp++;
909 					if (!_cursesi_screen->curwin)
910 						++csp;
911 					++wx;
912 				}
913 				continue;
914 			}
915 			break;
916 		}
917 #else
918 #ifdef DEBUG
919 		__CTRACE(__CTRACE_REFRESH, "makech: nsp=(%x,%x,%x,%x,%p)\n",
920 			nsp->ch, nsp->attr, win->bch, win->battr, nsp->nsp);
921 		__CTRACE(__CTRACE_REFRESH, "makech: csp=(%x,%x,%x,%x,%p)\n",
922 			csp->ch, csp->attr, win->bch, win->battr, csp->nsp);
923 #endif /* DEBUG */
924 		if (!(wlp->flags & __ISFORCED) &&
925 		     (((nsp->attr & __WCWIDTH) != __WCWIDTH) &&
926 		       cellcmp(nsp, csp)))
927 		{
928 			if (wx <= lch) {
929 				while (wx <= lch && cellcmp( csp, nsp )) {
930 					nsp++;
931 					if (!_cursesi_screen->curwin)
932 						++csp;
933 					++wx;
934 				}
935 				continue;
936 			}
937 			break;
938 		}
939 #endif /* HAVE_WCHAR */
940 		domvcur(win, _cursesi_screen->ly, _cursesi_screen->lx, wy, wx);
941 
942 #ifdef DEBUG
943 		__CTRACE(__CTRACE_REFRESH, "makech: 1: wx = %d, ly= %d, "
944 		    "lx = %d, newy = %d, newx = %d\n",
945 		    wx, _cursesi_screen->ly, _cursesi_screen->lx, wy, wx);
946 #endif
947 		_cursesi_screen->ly = wy;
948 		_cursesi_screen->lx = wx;
949 #ifndef HAVE_WCHAR
950 		while (wx <= lch && (memcmp(nsp, csp, sizeof(__LDATA)) != 0) ||
951 			(wlp->flags & __ISFORCED))
952 		{
953 			if (ce != NULL &&
954 			    wx >= nlsp && nsp->ch == ' ' && nsp->attr == lspc)
955 			{
956 #else
957 		while ((!cellcmp(nsp, csp) || (wlp->flags & __ISFORCED)) &&
958 			wx <= lch)
959 		{
960 			if (ce != NULL && wx >= nlsp
961 			   && nsp->ch == (wchar_t)btowc((int)' ') /* XXX */
962 			   && (nsp->attr & WA_ATTRIBUTES) == lspc)
963 			{
964 
965 #endif
966 				/* Check for clear to end-of-line. */
967 				cep = &curscr->alines[wy]->line[win->maxx - 1];
968 #ifndef HAVE_WCHAR
969 				while (cep->ch == ' ' && cep->attr == lspc) /* XXX */
970 #else
971 				while (cep->ch == (wchar_t)btowc((int)' ')
972 				       && (cep->attr & WA_ATTRIBUTES) == lspc)
973 #endif /* HAVE_WCHAR */
974 					if (cep-- <= csp)
975 						break;
976 				if (cep > (curscr->alines[wy]->line + win->begx * __LDATASIZE))
977 					clsp = cep - curscr->alines[wy]->line -
978 					win->begx * __LDATASIZE;
979 				else
980 					clsp = 0;
981 #ifdef DEBUG
982 				__CTRACE(__CTRACE_REFRESH,
983 				    "makech: clsp = %zu, nlsp = %zu\n",
984 				    clsp, nlsp);
985 				__CTRACE(__CTRACE_REFRESH,
986 				    "makech: line = %p, cep = %p, begx = %u\n",
987 				    curscr->alines[wy]->line, cep, win->begx);
988 #endif
989 				if (((clsp - nlsp >= strlen(clr_eol) &&
990 				    clsp < win->maxx * __LDATASIZE) ||
991 				    wy == win->maxy - 1) &&
992 				    (!(lspc & __COLOR) ||
993 				    ((lspc & __COLOR) && back_color_erase)))
994 				{
995 					__unsetattr(0);
996 					if (__using_color &&
997 					    ((lspc & __COLOR) !=
998 					    (curscr->wattr & __COLOR)))
999 						__set_color(curscr, lspc &
1000 						    __COLOR);
1001 					tputs(clr_eol, 0, __cputchar);
1002 					_cursesi_screen->lx = wx + win->begx;
1003 					while (wx++ <= clsp) {
1004 						csp->attr = lspc;
1005 #ifndef HAVE_WCHAR
1006 						csp->ch = ' '; /* XXX */
1007 #else
1008 						csp->ch = (wchar_t)btowc((int)' ');
1009 						SET_WCOL( *csp, 1 );
1010 #endif /* HAVE_WCHAR */
1011 						csp++;
1012 					}
1013 					return OK;
1014 				}
1015 				ce = NULL;
1016 			}
1017 
1018 #ifdef DEBUG
1019 				__CTRACE(__CTRACE_REFRESH,
1020 				    "makech: have attr %08x, need attr %08x\n",
1021 				    curscr->wattr
1022 #ifndef HAVE_WCHAR
1023 				 & __ATTRIBUTES
1024 #else
1025 				 & WA_ATTRIBUTES
1026 #endif
1027 					 ,  nsp->attr
1028 #ifndef HAVE_WCHAR
1029 				 & __ATTRIBUTES
1030 #else
1031 				 & WA_ATTRIBUTES
1032 #endif
1033 					);
1034 #endif
1035 
1036 			off = (~nsp->attr & curscr->wattr)
1037 #ifndef HAVE_WCHAR
1038 				 & __ATTRIBUTES
1039 #else
1040 				 & WA_ATTRIBUTES
1041 #endif
1042 				;
1043 
1044 			/*
1045 			 * Unset attributes as appropriate.  Unset first
1046 			 * so that the relevant attributes can be reset
1047 			 * (because 'me' unsets 'mb', 'md', 'mh', 'mk',
1048 			 * 'mp' and 'mr').  Check to see if we also turn off
1049 			 * standout, attributes and colour.
1050 			 */
1051 			if (off & __TERMATTR && exit_attribute_mode != NULL) {
1052 				tputs(exit_attribute_mode, 0, __cputchar);
1053 				curscr->wattr &= __mask_me;
1054 				off &= __mask_me;
1055 			}
1056 
1057 			/*
1058 			 * Exit underscore mode if appropriate.
1059 			 * Check to see if we also turn off standout,
1060 			 * attributes and colour.
1061 			 */
1062 			if (off & __UNDERSCORE && exit_underline_mode != NULL) {
1063 				tputs(exit_underline_mode, 0, __cputchar);
1064 				curscr->wattr &= __mask_ue;
1065 				off &= __mask_ue;
1066 			}
1067 
1068 			/*
1069 			 * Exit standout mode as appropriate.
1070 			 * Check to see if we also turn off underscore,
1071 			 * attributes and colour.
1072 			 * XXX
1073 			 * Should use uc if so/se not available.
1074 			 */
1075 			if (off & __STANDOUT && exit_standout_mode != NULL) {
1076 				tputs(exit_standout_mode, 0, __cputchar);
1077 				curscr->wattr &= __mask_se;
1078 				off &= __mask_se;
1079 			}
1080 
1081 			if (off & __ALTCHARSET && exit_alt_charset_mode != NULL) {
1082 				tputs(exit_alt_charset_mode, 0, __cputchar);
1083 				curscr->wattr &= ~__ALTCHARSET;
1084 			}
1085 
1086 			/* Set/change colour as appropriate. */
1087 			if (__using_color)
1088 				__set_color(curscr, nsp->attr & __COLOR);
1089 
1090 			on = (nsp->attr & ~curscr->wattr)
1091 #ifndef HAVE_WCHAR
1092 				 & __ATTRIBUTES
1093 #else
1094 				 & WA_ATTRIBUTES
1095 #endif
1096 				 ;
1097 
1098 			/*
1099 			 * Enter standout mode if appropriate.
1100 			 */
1101 			if (on & __STANDOUT &&
1102 			    enter_standout_mode != NULL &&
1103 			    exit_standout_mode != NULL)
1104 			{
1105 				tputs(enter_standout_mode, 0, __cputchar);
1106 				curscr->wattr |= __STANDOUT;
1107 			}
1108 
1109 			/*
1110 			 * Enter underscore mode if appropriate.
1111 			 * XXX
1112 			 * Should use uc if us/ue not available.
1113 			 */
1114 			if (on & __UNDERSCORE &&
1115 			    enter_underline_mode != NULL &&
1116 			    exit_underline_mode != NULL)
1117 			{
1118 				tputs(enter_underline_mode, 0, __cputchar);
1119 				curscr->wattr |= __UNDERSCORE;
1120 			}
1121 
1122 			/*
1123 			 * Set other attributes as appropriate.
1124 			 */
1125 			if (exit_attribute_mode != NULL) {
1126 				if (on & __BLINK &&
1127 				    enter_blink_mode != NULL)
1128 				{
1129 					tputs(enter_blink_mode, 0, __cputchar);
1130 					curscr->wattr |= __BLINK;
1131 				}
1132 				if (on & __BOLD &&
1133 				    enter_bold_mode != NULL)
1134 				{
1135 					tputs(enter_bold_mode, 0, __cputchar);
1136 					curscr->wattr |= __BOLD;
1137 				}
1138 				if (on & __DIM &&
1139 				    enter_dim_mode != NULL)
1140 				{
1141 					tputs(enter_dim_mode, 0, __cputchar);
1142 					curscr->wattr |= __DIM;
1143 				}
1144 				if (on & __BLANK &&
1145 				    enter_secure_mode != NULL)
1146 				{
1147 					tputs(enter_secure_mode, 0, __cputchar);
1148 					curscr->wattr |= __BLANK;
1149 				}
1150 				if (on & __PROTECT &&
1151 				    enter_protected_mode != NULL)
1152 				{
1153 					tputs(enter_protected_mode, 0, __cputchar);
1154 					curscr->wattr |= __PROTECT;
1155 				}
1156 				if (on & __REVERSE &&
1157 				    enter_reverse_mode != NULL)
1158 				{
1159 					tputs(enter_reverse_mode, 0, __cputchar);
1160 					curscr->wattr |= __REVERSE;
1161 				}
1162 #ifdef HAVE_WCHAR
1163 				if (on & WA_TOP &&
1164 				    enter_top_hl_mode != NULL)
1165 				{
1166 					tputs(enter_top_hl_mode, 0, __cputchar);
1167 					curscr->wattr |= WA_TOP;
1168 				}
1169 				if (on & WA_LOW &&
1170 				    enter_low_hl_mode != NULL)
1171 				{
1172 					tputs(enter_low_hl_mode, 0, __cputchar);
1173 					curscr->wattr |= WA_LOW;
1174 				}
1175 				if (on & WA_LEFT &&
1176 				    enter_left_hl_mode != NULL)
1177 				{
1178 					tputs(enter_left_hl_mode, 0, __cputchar);
1179 					curscr->wattr |= WA_LEFT;
1180 				}
1181 				if (on & WA_RIGHT &&
1182 				    enter_right_hl_mode != NULL)
1183 				{
1184 					tputs(enter_right_hl_mode, 0, __cputchar);
1185 					curscr->wattr |= WA_RIGHT;
1186 				}
1187 				if (on & WA_HORIZONTAL &&
1188 				    enter_horizontal_hl_mode != NULL)
1189 				{
1190 					tputs(enter_horizontal_hl_mode, 0, __cputchar);
1191 					curscr->wattr |= WA_HORIZONTAL;
1192 				}
1193 				if (on & WA_VERTICAL &&
1194 				    enter_vertical_hl_mode != NULL)
1195 				{
1196 					tputs(enter_vertical_hl_mode, 0, __cputchar);
1197 					curscr->wattr |= WA_VERTICAL;
1198 				}
1199 #endif /* HAVE_WCHAR */
1200 			}
1201 
1202 			/* Enter/exit altcharset mode as appropriate. */
1203 			if (on & __ALTCHARSET && enter_alt_charset_mode != NULL &&
1204 			    exit_alt_charset_mode != NULL) {
1205 				tputs(enter_alt_charset_mode, 0, __cputchar);
1206 				curscr->wattr |= __ALTCHARSET;
1207 			}
1208 
1209 			wx++;
1210 			if (wx >= win->maxx &&
1211 			    wy == win->maxy - 1 && !_cursesi_screen->curwin) {
1212 				if (win->flags & __SCROLLOK) {
1213 					if (win->flags & __ENDLINE)
1214 						__unsetattr(1);
1215 					if (!(win->flags & __SCROLLWIN)) {
1216 						if (!_cursesi_screen->curwin) {
1217 							csp->attr = nsp->attr;
1218 							csp->ch = nsp->ch;
1219 #ifdef HAVE_WCHAR
1220 							if (_cursesi_copy_nsp(nsp->nsp, csp) == ERR)
1221 								return ERR;
1222 #endif /* HAVE_WCHAR */
1223 						}
1224 #ifndef HAVE_WCHAR
1225 						__cputchar((int) nsp->ch);
1226 #else
1227 						if ( WCOL( *nsp ) > 0 ) {
1228 							__cputwchar((int)nsp->ch);
1229 #ifdef DEBUG
1230 							__CTRACE(__CTRACE_REFRESH,
1231 							    "makech: (%d,%d)putwchar(0x%x)\n",
1232 								wy, wx - 1,
1233 								nsp->ch );
1234 #endif /* DEBUG */
1235 							/*
1236 							 * Output non-spacing
1237 							 * characters for the
1238 							 * cell.
1239 							 */
1240 							__cursesi_putnsp(nsp->nsp,
1241 									 wy, wx);
1242 
1243 						}
1244 #endif /* HAVE_WCHAR */
1245 					}
1246 					if (wx < curscr->maxx) {
1247 						domvcur(win,
1248 						    _cursesi_screen->ly, wx,
1249 						    (int)(win->maxy - 1),
1250 						    (int)(win->maxx - 1));
1251 					}
1252 					_cursesi_screen->ly = win->maxy - 1;
1253 					_cursesi_screen->lx = win->maxx - 1;
1254 					return (OK);
1255 				}
1256 			}
1257 			if (wx < win->maxx || wy < win->maxy - 1 ||
1258 			    !(win->flags & __SCROLLWIN))
1259 			{
1260 				if (!_cursesi_screen->curwin) {
1261 					csp->attr = nsp->attr;
1262 					csp->ch = nsp->ch;
1263 #ifdef HAVE_WCHAR
1264 					if (_cursesi_copy_nsp(nsp->nsp,
1265 							      csp) == ERR)
1266 						return ERR;
1267 #endif /* HAVE_WCHAR */
1268 					csp++;
1269 				}
1270 #ifndef HAVE_WCHAR
1271 				__cputchar((int)nsp->ch);
1272 #ifdef DEBUG
1273 				__CTRACE(__CTRACE_REFRESH,
1274 				    "makech: putchar(%c)\n", nsp->ch & 0177);
1275 #endif
1276 #else
1277 				if (WCOL(*nsp) > 0) {
1278 					__cputwchar((int)nsp->ch);
1279 #ifdef DEBUG
1280 					__CTRACE(__CTRACE_REFRESH,
1281 					    "makech:(%d,%d) putwchar(%x)\n",
1282 					    wy, wx - 1, nsp->ch);
1283 					__cursesi_putnsp(nsp->nsp, wy, wx);
1284 #endif /* DEBUG */
1285 				}
1286 #endif /* HAVE_WCHAR */
1287 			}
1288 			if (underline_char && ((nsp->attr & __STANDOUT) ||
1289 			    (nsp->attr & __UNDERSCORE))) {
1290 				__cputchar('\b');
1291 				tputs(underline_char, 0, __cputchar);
1292 			}
1293 			nsp++;
1294 #ifdef DEBUG
1295 			__CTRACE(__CTRACE_REFRESH,
1296 			    "makech: 2: wx = %d, lx = %d\n",
1297 			    wx, _cursesi_screen->lx);
1298 #endif
1299 		}
1300 		if (_cursesi_screen->lx == wx)	/* If no change. */
1301 			break;
1302 		_cursesi_screen->lx = wx;
1303 		if (_cursesi_screen->lx >= COLS && auto_right_margin)
1304 			_cursesi_screen->lx = COLS - 1;
1305 		else
1306 			if (wx >= win->maxx) {
1307 				domvcur(win,
1308 					_cursesi_screen->ly,
1309 					_cursesi_screen->lx,
1310 					_cursesi_screen->ly,
1311 					(int)(win->maxx - 1));
1312 				_cursesi_screen->lx = win->maxx - 1;
1313 			}
1314 #ifdef DEBUG
1315 		__CTRACE(__CTRACE_REFRESH, "makech: 3: wx = %d, lx = %d\n",
1316 		    wx, _cursesi_screen->lx);
1317 #endif
1318 	}
1319 #ifdef DEBUG
1320 #if HAVE_WCHAR
1321 	{
1322 		int x;
1323 		__LDATA *lp, *vlp;
1324 
1325 		__CTRACE(__CTRACE_REFRESH,
1326 		    "makech-after: curscr(%p)-__virtscr(%p)\n",
1327 		    curscr, __virtscr );
1328 		for (x = 0; x < curscr->maxx; x++) {
1329 			lp = &curscr->alines[wy]->line[x];
1330 			vlp = &__virtscr->alines[wy]->line[x];
1331 			__CTRACE(__CTRACE_REFRESH,
1332 			    "[%d,%d](%x,%x,%x,%x,%p)-"
1333 			    "(%x,%x,%x,%x,%p)\n",
1334 			    wy, x, lp->ch, lp->attr,
1335 			    win->bch, win->battr, lp->nsp,
1336 			    vlp->ch, vlp->attr,
1337 			    win->bch, win->battr, vlp->nsp);
1338 		}
1339 	}
1340 #endif /* HAVE_WCHAR */
1341 #endif /* DEBUG */
1342 
1343 	return OK;
1344 }
1345 
1346 /*
1347  * domvcur --
1348  *	Do a mvcur, leaving attributes if necessary.
1349  */
1350 static void
1351 domvcur(const WINDOW *win, int oy, int ox, int ny, int nx)
1352 {
1353 
1354 #ifdef DEBUG
1355 	__CTRACE(__CTRACE_REFRESH, "domvcur: (%x,%d)=>(%d,%d)\n",
1356 	    oy, ox, ny, nx );
1357 #endif /* DEBUG */
1358 
1359 	__unsetattr(1);
1360 
1361 	/* Don't move the cursor unless we need to. */
1362 	if (oy == ny && ox == nx) {
1363 		/* Check EOL. */
1364 		if (!(win->alines[oy]->flags & __ISPASTEOL))
1365 			return;
1366 	}
1367 
1368 	/* Clear EOL flags. */
1369 	win->alines[oy]->flags &= ~__ISPASTEOL;
1370 	win->alines[ny]->flags &= ~__ISPASTEOL;
1371 
1372 	__mvcur(oy, ox, ny, nx, 1);
1373 }
1374 
1375 /*
1376  * Quickch() attempts to detect a pattern in the change of the window
1377  * in order to optimize the change, e.g., scroll n lines as opposed to
1378  * repainting the screen line by line.
1379  */
1380 
1381 static __LDATA buf[128];
1382 static  unsigned int last_hash;
1383 static  size_t last_hash_len;
1384 #define BLANKSIZE (sizeof(buf) / sizeof(buf[0]))
1385 
1386 static void
1387 quickch(void)
1388 {
1389 #define THRESH		(int) __virtscr->maxy / 4
1390 
1391 	__LINE *clp, *tmp1, *tmp2;
1392 	int	bsize, curs, curw, starts, startw, i, j;
1393 	int	n, target, cur_period, bot, top, sc_region;
1394 	unsigned int	blank_hash;
1395 	attr_t	bcolor;
1396 
1397 #ifdef __GNUC__
1398 	curs = curw = starts = startw = 0;	/* XXX gcc -Wuninitialized */
1399 #endif
1400 	/*
1401 	 * Find how many lines from the top of the screen are unchanged.
1402 	 */
1403 	for (top = 0; top < __virtscr->maxy; top++) {
1404 #ifndef HAVE_WCHAR
1405 		if (__virtscr->alines[top]->flags & __ISDIRTY &&
1406 		    (__virtscr->alines[top]->hash != curscr->alines[top]->hash ||
1407 		    memcmp(__virtscr->alines[top]->line,
1408 		    curscr->alines[top]->line,
1409 		    (size_t) __virtscr->maxx * __LDATASIZE)
1410 		    != 0))
1411 			break;
1412 #else
1413 		if (__virtscr->alines[top]->flags & __ISDIRTY &&
1414 		    (__virtscr->alines[top]->hash != curscr->alines[top]->hash ||
1415 		    !linecmp(__virtscr->alines[top]->line,
1416 		    curscr->alines[top]->line,
1417 	(size_t) __virtscr->maxx )))
1418 			break;
1419 #endif /* HAVE_WCHAR */
1420 		else
1421 			__virtscr->alines[top]->flags &= ~__ISDIRTY;
1422 	}
1423 	/*
1424 	 * Find how many lines from bottom of screen are unchanged.
1425 	 */
1426 	for (bot = __virtscr->maxy - 1; bot >= 0; bot--) {
1427 #ifndef HAVE_WCHAR
1428 		if (__virtscr->alines[bot]->flags & __ISDIRTY &&
1429 		    (__virtscr->alines[bot]->hash != curscr->alines[bot]->hash ||
1430 		    memcmp(__virtscr->alines[bot]->line,
1431 		    curscr->alines[bot]->line,
1432 		    (size_t) __virtscr->maxx * __LDATASIZE)
1433 		    != 0))
1434 			break;
1435 #else
1436 		if (__virtscr->alines[bot]->flags & __ISDIRTY &&
1437 		    (__virtscr->alines[bot]->hash != curscr->alines[bot]->hash ||
1438 		    !linecmp(__virtscr->alines[bot]->line,
1439 		    curscr->alines[bot]->line,
1440 		    (size_t) __virtscr->maxx )))
1441 			break;
1442 #endif /* HAVE_WCHAR */
1443 		else
1444 			__virtscr->alines[bot]->flags &= ~__ISDIRTY;
1445 	}
1446 
1447 	/*
1448 	 * Work round an xterm bug where inserting lines causes all the
1449 	 * inserted lines to be covered with the background colour we
1450 	 * set on the first line (even if we unset it for subsequent
1451 	 * lines).
1452 	 */
1453 	bcolor = __virtscr->alines[min(top,
1454 	    __virtscr->maxy - 1)]->line[0].attr & __COLOR;
1455 	for (i = top + 1, j = 0; i < bot; i++) {
1456 		if ((__virtscr->alines[i]->line[0].attr & __COLOR) != bcolor) {
1457 			bcolor = __virtscr->alines[i]->line[__virtscr->maxx].
1458 			    attr & __COLOR;
1459 			j = i - top;
1460 		} else
1461 			break;
1462 	}
1463 	top += j;
1464 
1465 #ifdef NO_JERKINESS
1466 	/*
1467 	 * If we have a bottom unchanged region return.  Scrolling the
1468 	 * bottom region up and then back down causes a screen jitter.
1469 	 * This will increase the number of characters sent to the screen
1470 	 * but it looks better.
1471 	 */
1472 	if (bot < __virtscr->maxy - 1)
1473 		return;
1474 #endif				/* NO_JERKINESS */
1475 
1476 	/*
1477 	 * Search for the largest block of text not changed.
1478 	 * Invariants of the loop:
1479 	 * - Startw is the index of the beginning of the examined block in
1480 	 *   __virtscr.
1481 	 * - Starts is the index of the beginning of the examined block in
1482 	 *   curscr.
1483 	 * - Curw is the index of one past the end of the exmined block in
1484 	 *   __virtscr.
1485 	 * - Curs is the index of one past the end of the exmined block in
1486 	 *   curscr.
1487 	 * - bsize is the current size of the examined block.
1488 	*/
1489 
1490 	for (bsize = bot - top; bsize >= THRESH; bsize--) {
1491 		for (startw = top; startw <= bot - bsize; startw++)
1492 			for (starts = top; starts <= bot - bsize; starts++) {
1493 				for (curw = startw, curs = starts;
1494 				    curs < starts + bsize; curw++, curs++)
1495 					if (__virtscr->alines[curw]->hash !=
1496 					    curscr->alines[curs]->hash)
1497 						break;
1498 				if (curs != starts + bsize)
1499 					continue;
1500 				for (curw = startw, curs = starts;
1501 					curs < starts + bsize; curw++, curs++)
1502 #ifndef HAVE_WCHAR
1503 					if (memcmp(__virtscr->alines[curw]->line,
1504 					    curscr->alines[curs]->line,
1505 					    (size_t) __virtscr->maxx *
1506 					    __LDATASIZE) != 0)
1507 						break;
1508 #else
1509 					if (!linecmp(__virtscr->alines[curw]->line,
1510 					    curscr->alines[curs]->line,
1511 					    (size_t) __virtscr->maxx))
1512 						break;
1513 #endif /* HAVE_WCHAR */
1514 				if (curs == starts + bsize)
1515 					goto done;
1516 			}
1517 	}
1518 done:
1519 
1520 	/* Did not find anything */
1521 	if (bsize < THRESH)
1522 		return;
1523 
1524 #ifdef DEBUG
1525 	__CTRACE(__CTRACE_REFRESH, "quickch:bsize=%d, starts=%d, startw=%d, "
1526 	    "curw=%d, curs=%d, top=%d, bot=%d\n",
1527 	    bsize, starts, startw, curw, curs, top, bot);
1528 #endif
1529 
1530 	/*
1531 	 * Make sure that there is no overlap between the bottom and top
1532 	 * regions and the middle scrolled block.
1533 	 */
1534 	if (bot < curs)
1535 		bot = curs - 1;
1536 	if (top > starts)
1537 		top = starts;
1538 
1539 	n = startw - starts;
1540 
1541 #ifdef DEBUG
1542 	__CTRACE(__CTRACE_REFRESH, "#####################################\n");
1543 	for (i = 0; i < curscr->maxy; i++) {
1544 		__CTRACE(__CTRACE_REFRESH, "C: %d:", i);
1545 		__CTRACE(__CTRACE_REFRESH, " 0x%x \n", curscr->alines[i]->hash);
1546 		for (j = 0; j < curscr->maxx; j++)
1547 			__CTRACE(__CTRACE_REFRESH, "%c",
1548 			    curscr->alines[i]->line[j].ch);
1549 		__CTRACE(__CTRACE_REFRESH, "\n");
1550 		__CTRACE(__CTRACE_REFRESH, " attr:");
1551 		for (j = 0; j < curscr->maxx; j++)
1552 			__CTRACE(__CTRACE_REFRESH, " %x",
1553 			    curscr->alines[i]->line[j].attr);
1554 		__CTRACE(__CTRACE_REFRESH, "\n");
1555 		__CTRACE(__CTRACE_REFRESH, "W: %d:", i);
1556 		__CTRACE(__CTRACE_REFRESH, " 0x%x \n",
1557 		    __virtscr->alines[i]->hash);
1558 		__CTRACE(__CTRACE_REFRESH, " 0x%x ",
1559 		    __virtscr->alines[i]->flags);
1560 		for (j = 0; j < __virtscr->maxx; j++)
1561 			__CTRACE(__CTRACE_REFRESH, "%c",
1562 			    __virtscr->alines[i]->line[j].ch);
1563 		__CTRACE(__CTRACE_REFRESH, "\n");
1564 		__CTRACE(__CTRACE_REFRESH, " attr:");
1565 		for (j = 0; j < __virtscr->maxx; j++)
1566 			__CTRACE(__CTRACE_REFRESH, " %x",
1567 			    __virtscr->alines[i]->line[j].attr);
1568 		__CTRACE(__CTRACE_REFRESH, "\n");
1569 	}
1570 #endif
1571 
1572 #ifndef HAVE_WCHAR
1573 	if (buf[0].ch != ' ') {
1574 		for (i = 0; i < BLANKSIZE; i++) {
1575 			buf[i].ch = ' ';
1576 			buf[i].attr = 0;
1577 		}
1578 	}
1579 #else
1580 	if (buf[0].ch != (wchar_t)btowc((int)curscr->bch )) {
1581 		for (i = 0; i < BLANKSIZE; i++) {
1582 			buf[i].ch = (wchar_t)btowc((int)curscr->bch);
1583 			if (_cursesi_copy_nsp(curscr->bnsp, &buf[i]) == ERR)
1584 				return;
1585 			buf[i].attr = 0;
1586 			SET_WCOL(buf[i], 1);
1587 		}
1588 	}
1589 #endif /* HAVE_WCHAR */
1590 
1591 	if (__virtscr->maxx != last_hash_len) {
1592 		blank_hash = 0;
1593 		for (i = __virtscr->maxx; i > BLANKSIZE; i -= BLANKSIZE) {
1594 			blank_hash = __hash_more(buf, sizeof(buf), blank_hash);
1595 		}
1596 		blank_hash = __hash_more((char *)(void *)buf,
1597 		    i * sizeof(buf[0]), blank_hash);
1598 		/* cache result in static data - screen width doesn't change often */
1599 		last_hash_len = __virtscr->maxx;
1600 		last_hash = blank_hash;
1601 	} else
1602 		blank_hash = last_hash;
1603 
1604 	/*
1605 	 * Perform the rotation to maintain the consistency of curscr.
1606 	 * This is hairy since we are doing an *in place* rotation.
1607 	 * Invariants of the loop:
1608 	 * - I is the index of the current line.
1609 	 * - Target is the index of the target of line i.
1610 	 * - Tmp1 points to current line (i).
1611 	 * - Tmp2 and points to target line (target);
1612 	 * - Cur_period is the index of the end of the current period.
1613 	 *   (see below).
1614 	 *
1615 	 * There are 2 major issues here that make this rotation non-trivial:
1616 	 * 1.  Scrolling in a scrolling region bounded by the top
1617 	 *     and bottom regions determined (whose size is sc_region).
1618 	 * 2.  As a result of the use of the mod function, there may be a
1619 	 *     period introduced, i.e., 2 maps to 4, 4 to 6, n-2 to 0, and
1620 	 *     0 to 2, which then causes all odd lines not to be rotated.
1621 	 *     To remedy this, an index of the end ( = beginning) of the
1622 	 *     current 'period' is kept, cur_period, and when it is reached,
1623 	 *     the next period is started from cur_period + 1 which is
1624 	 *     guaranteed not to have been reached since that would mean that
1625 	 *     all records would have been reached. (think about it...).
1626 	 *
1627 	 * Lines in the rotation can have 3 attributes which are marked on the
1628 	 * line so that curscr is consistent with the visual screen.
1629 	 * 1.  Not dirty -- lines inside the scrolled block, top region or
1630 	 *                  bottom region.
1631 	 * 2.  Blank lines -- lines in the differential of the scrolling
1632 	 *		      region adjacent to top and bot regions
1633 	 *                    depending on scrolling direction.
1634 	 * 3.  Dirty line -- all other lines are marked dirty.
1635 	 */
1636 	sc_region = bot - top + 1;
1637 	i = top;
1638 	tmp1 = curscr->alines[top];
1639 	cur_period = top;
1640 	for (j = top; j <= bot; j++) {
1641 		target = (i - top + n + sc_region) % sc_region + top;
1642 		tmp2 = curscr->alines[target];
1643 		curscr->alines[target] = tmp1;
1644 		/* Mark block as clean and blank out scrolled lines. */
1645 		clp = curscr->alines[target];
1646 #ifdef DEBUG
1647 		__CTRACE(__CTRACE_REFRESH,
1648 		    "quickch: n=%d startw=%d curw=%d i = %d target=%d ",
1649 		    n, startw, curw, i, target);
1650 #endif
1651 		if ((target >= startw && target < curw) || target < top
1652 		    || target > bot)
1653 		{
1654 #ifdef DEBUG
1655 			__CTRACE(__CTRACE_REFRESH, " notdirty\n");
1656 #endif
1657 			__virtscr->alines[target]->flags &= ~__ISDIRTY;
1658 		} else
1659 			if ((n > 0 && target >= top && target < top + n) ||
1660 			    (n < 0 && target <= bot && target > bot + n))
1661 			{
1662 #ifndef HAVE_WCHAR
1663 				if (clp->hash != blank_hash ||
1664 				    memcmp(clp->line, clp->line + 1,
1665 				    (__virtscr->maxx - 1)
1666 				    * __LDATASIZE) ||
1667 				    memcmp(clp->line, buf, __LDATASIZE))
1668 				{
1669 #else
1670 				if (clp->hash != blank_hash
1671 				    || linecmp(clp->line, clp->line + 1,
1672 				    (unsigned int) (__virtscr->maxx - 1))
1673 				    || cellcmp(clp->line, buf))
1674 				{
1675 #endif /* HAVE_WCHAR */
1676 					for (i = __virtscr->maxx;
1677 					    i > BLANKSIZE;
1678 					    i -= BLANKSIZE) {
1679 						(void) memcpy(clp->line + i -
1680 						    BLANKSIZE, buf, sizeof(buf));
1681 					}
1682 					(void) memcpy(clp->line , buf, i *
1683 					    sizeof(buf[0]));
1684 #ifdef DEBUG
1685 					__CTRACE(__CTRACE_REFRESH,
1686 					    " blanked out: dirty\n");
1687 #endif
1688 					clp->hash = blank_hash;
1689 					__touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1);
1690 				} else {
1691 #ifdef DEBUG
1692 					__CTRACE(__CTRACE_REFRESH,
1693 					    " -- blank line already: dirty\n");
1694 #endif
1695 					__touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1);
1696 				}
1697 			} else {
1698 #ifdef DEBUG
1699 				__CTRACE(__CTRACE_REFRESH, " -- dirty\n");
1700 #endif
1701 				__touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1);
1702 			}
1703 		if (target == cur_period) {
1704 			i = target + 1;
1705 			tmp1 = curscr->alines[i];
1706 			cur_period = i;
1707 		} else {
1708 			tmp1 = tmp2;
1709 			i = target;
1710 		}
1711 	}
1712 #ifdef DEBUG
1713 	__CTRACE(__CTRACE_REFRESH, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
1714 	for (i = 0; i < curscr->maxy; i++) {
1715 		__CTRACE(__CTRACE_REFRESH, "C: %d:", i);
1716 		for (j = 0; j < curscr->maxx; j++)
1717 			__CTRACE(__CTRACE_REFRESH, "%c",
1718 			    curscr->alines[i]->line[j].ch);
1719 		__CTRACE(__CTRACE_REFRESH, "\n");
1720 		__CTRACE(__CTRACE_REFRESH, "W: %d:", i);
1721 		for (j = 0; j < __virtscr->maxx; j++)
1722 			__CTRACE(__CTRACE_REFRESH, "%c",
1723 			    __virtscr->alines[i]->line[j].ch);
1724 		__CTRACE(__CTRACE_REFRESH, "\n");
1725 	}
1726 #endif
1727 	if (n != 0)
1728 		scrolln(starts, startw, curs, bot, top);
1729 }
1730 
1731 /*
1732  * scrolln --
1733  *	Scroll n lines, where n is starts - startw.
1734  */
1735 static void /* ARGSUSED */
1736 scrolln(int starts, int startw, int curs, int bot, int top)
1737 {
1738 	int	i, oy, ox, n;
1739 
1740 	oy = curscr->cury;
1741 	ox = curscr->curx;
1742 	n = starts - startw;
1743 
1744 	/*
1745 	 * XXX
1746 	 * The initial tests that set __noqch don't let us reach here unless
1747 	 * we have either cs + ho + SF/sf/SR/sr, or AL + DL.  SF/sf and SR/sr
1748 	 * scrolling can only shift the entire scrolling region, not just a
1749 	 * part of it, which means that the quickch() routine is going to be
1750 	 * sadly disappointed in us if we don't have cs as well.
1751 	 *
1752 	 * If cs, ho and SF/sf are set, can use the scrolling region.  Because
1753 	 * the cursor position after cs is undefined, we need ho which gives us
1754 	 * the ability to move to somewhere without knowledge of the current
1755 	 * location of the cursor.  Still call __mvcur() anyway, to update its
1756 	 * idea of where the cursor is.
1757 	 *
1758 	 * When the scrolling region has been set, the cursor has to be at the
1759 	 * last line of the region to make the scroll happen.
1760 	 *
1761 	 * Doing SF/SR or AL/DL appears faster on the screen than either sf/sr
1762 	 * or AL/DL, and, some terminals have AL/DL, sf/sr, and cs, but not
1763 	 * SF/SR.  So, if we're scrolling almost all of the screen, try and use
1764 	 * AL/DL, otherwise use the scrolling region.  The "almost all" is a
1765 	 * shameless hack for vi.
1766 	 */
1767 	if (n > 0) {
1768 		if (change_scroll_region != NULL && cursor_home != NULL &&
1769 		    (parm_index != NULL ||
1770 		    ((parm_insert_line == NULL || parm_delete_line == NULL ||
1771 		    top > 3 || bot + 3 < __virtscr->maxy) &&
1772 		    scroll_forward != NULL)))
1773 		{
1774 			tputs(tiparm(change_scroll_region, top, bot),
1775 			    0, __cputchar);
1776 			__mvcur(oy, ox, 0, 0, 1);
1777 			tputs(cursor_home, 0, __cputchar);
1778 			__mvcur(0, 0, bot, 0, 1);
1779 			if (parm_index != NULL)
1780 				tputs(tiparm(parm_index, n),
1781 				    0, __cputchar);
1782 			else
1783 				for (i = 0; i < n; i++)
1784 					tputs(scroll_forward, 0, __cputchar);
1785 			tputs(tiparm(change_scroll_region,
1786 			    0, (int)__virtscr->maxy - 1), 0, __cputchar);
1787 			__mvcur(bot, 0, 0, 0, 1);
1788 			tputs(cursor_home, 0, __cputchar);
1789 			__mvcur(0, 0, oy, ox, 1);
1790 			return;
1791 		}
1792 
1793 		/* Scroll up the block. */
1794 		if (parm_index != NULL && top == 0) {
1795 			__mvcur(oy, ox, bot, 0, 1);
1796 			tputs(tiparm(parm_index, n), 0, __cputchar);
1797 		} else
1798 			if (parm_delete_line != NULL) {
1799 				__mvcur(oy, ox, top, 0, 1);
1800 				tputs(tiparm(parm_delete_line, n),
1801 				    0, __cputchar);
1802 			} else
1803 				if (delete_line != NULL) {
1804 					__mvcur(oy, ox, top, 0, 1);
1805 					for (i = 0; i < n; i++)
1806 						tputs(delete_line, 0,
1807 						    __cputchar);
1808 				} else
1809 					if (scroll_forward != NULL && top == 0) {
1810 						__mvcur(oy, ox, bot, 0, 1);
1811 						for (i = 0; i < n; i++)
1812 							tputs(scroll_forward, 0,
1813 							    __cputchar);
1814 					} else
1815 						abort();
1816 
1817 		/* Push down the bottom region. */
1818 		__mvcur(top, 0, bot - n + 1, 0, 1);
1819 		if (parm_insert_line != NULL)
1820 			tputs(tiparm(parm_insert_line, n), 0, __cputchar);
1821 		else {
1822 			if (insert_line != NULL) {
1823 				for (i = 0; i < n; i++)
1824 					tputs(insert_line, 0, __cputchar);
1825 			} else
1826 				abort();
1827 		}
1828 		__mvcur(bot - n + 1, 0, oy, ox, 1);
1829 	} else {
1830 		/*
1831 		 * !!!
1832 		 * n < 0
1833 		 *
1834 		 * If cs, ho and SR/sr are set, can use the scrolling region.
1835 		 * See the above comments for details.
1836 		 */
1837 		if (change_scroll_region != NULL && cursor_home != NULL &&
1838 		    (parm_rindex != NULL ||
1839 		    ((parm_insert_line == NULL || parm_delete_line == NULL ||
1840 		    top > 3 ||
1841 		    bot + 3 < __virtscr->maxy) && scroll_reverse != NULL)))
1842 		{
1843 			tputs(tiparm(change_scroll_region, top, bot),
1844 			    0, __cputchar);
1845 			__mvcur(oy, ox, 0, 0, 1);
1846 			tputs(cursor_home, 0, __cputchar);
1847 			__mvcur(0, 0, top, 0, 1);
1848 
1849 			if (parm_rindex != NULL)
1850 				tputs(tiparm(parm_rindex, -n),
1851 				    0, __cputchar);
1852 			else
1853 				for (i = n; i < 0; i++)
1854 					tputs(scroll_reverse, 0, __cputchar);
1855 			tputs(tiparm(change_scroll_region,
1856 			    0, (int) __virtscr->maxy - 1), 0, __cputchar);
1857 			__mvcur(top, 0, 0, 0, 1);
1858 			tputs(cursor_home, 0, __cputchar);
1859 			__mvcur(0, 0, oy, ox, 1);
1860 			return;
1861 		}
1862 
1863 		/* Preserve the bottom lines. */
1864 		__mvcur(oy, ox, bot + n + 1, 0, 1);
1865 		if (parm_rindex != NULL && bot == __virtscr->maxy)
1866 			tputs(tiparm(parm_rindex, -n), 0, __cputchar);
1867 		else {
1868 			if (parm_delete_line != NULL)
1869 				tputs(tiparm(parm_delete_line, -n),
1870 				    0, __cputchar);
1871 			else {
1872 				if (delete_line != NULL)
1873 					for (i = n; i < 0; i++)
1874 						tputs(delete_line,
1875 						    0, __cputchar);
1876 				else {
1877 					if (scroll_reverse != NULL &&
1878 					    bot == __virtscr->maxy)
1879 						for (i = n; i < 0; i++)
1880 							tputs(scroll_reverse, 0,
1881 							    __cputchar);
1882 					else
1883 						abort();
1884 				}
1885 			}
1886 		}
1887 		/* Scroll the block down. */
1888 		__mvcur(bot + n + 1, 0, top, 0, 1);
1889 		if (parm_insert_line != NULL)
1890 			tputs(tiparm(parm_insert_line, -n), 0, __cputchar);
1891 		else
1892 			if (insert_line != NULL)
1893 				for (i = n; i < 0; i++)
1894 					tputs(insert_line, 0, __cputchar);
1895 			else
1896 				abort();
1897 		__mvcur(top, 0, oy, ox, 1);
1898 	}
1899 }
1900 
1901 /*
1902  * __unsetattr --
1903  *	Unset attributes on curscr.  Leave standout, attribute and colour
1904  *	modes if necessary (!ms).  Always leave altcharset (xterm at least
1905  *	ignores a cursor move if we don't).
1906  */
1907 void /* ARGSUSED */
1908 __unsetattr(int checkms)
1909 {
1910 	int	isms;
1911 
1912 	if (checkms) {
1913 		if (!move_standout_mode)
1914 			isms = 1;
1915 		else
1916 			isms = 0;
1917 	} else
1918 		isms = 1;
1919 #ifdef DEBUG
1920 	__CTRACE(__CTRACE_REFRESH,
1921 	    "__unsetattr: checkms = %d, ms = %s, wattr = %08x\n",
1922 	    checkms, move_standout_mode ? "TRUE" : "FALSE", curscr->wattr);
1923 #endif
1924 
1925 	/*
1926 	 * Don't leave the screen in standout mode (check against ms).  Check
1927 	 * to see if we also turn off underscore, attributes and colour.
1928 	 */
1929 	if (curscr->wattr & __STANDOUT && isms) {
1930 		tputs(exit_standout_mode, 0, __cputchar);
1931 		curscr->wattr &= __mask_se;
1932 	}
1933 	/*
1934 	 * Don't leave the screen in underscore mode (check against ms).
1935 	 * Check to see if we also turn off attributes.  Assume that we
1936 	 * also turn off colour.
1937 	 */
1938 	if (curscr->wattr & __UNDERSCORE && isms) {
1939 		tputs(exit_underline_mode, 0, __cputchar);
1940 		curscr->wattr &= __mask_ue;
1941 	}
1942 	/*
1943 	 * Don't leave the screen with attributes set (check against ms).
1944 	 * Assume that also turn off colour.
1945 	 */
1946 	if (curscr->wattr & __TERMATTR && isms) {
1947 		tputs(exit_attribute_mode, 0, __cputchar);
1948 		curscr->wattr &= __mask_me;
1949 	}
1950 	/* Don't leave the screen with altcharset set (don't check ms). */
1951 	if (curscr->wattr & __ALTCHARSET) {
1952 		tputs(exit_alt_charset_mode, 0, __cputchar);
1953 		curscr->wattr &= ~__ALTCHARSET;
1954 	}
1955 	/* Don't leave the screen with colour set (check against ms). */
1956 	if (__using_color && isms)
1957 		__unset_color(curscr);
1958 }
1959 
1960 #ifdef HAVE_WCHAR
1961 /* compare two cells on screen, must have the same forground/background,
1962  * and the same sequence of non-spacing characters */
1963 int
1964 cellcmp( __LDATA *x, __LDATA *y )
1965 {
1966 	nschar_t *xnp = x->nsp, *ynp = y->nsp;
1967 	int ret = ( x->ch == y->ch ) & ( x->attr == y->attr );
1968 
1969 	if (!ret)
1970 		return 0;
1971 	if (!xnp && !ynp)
1972 		return 1;
1973 	if ((xnp && !ynp) || (!xnp && ynp))
1974 		return 0;
1975 
1976 	while (xnp && ynp) {
1977 		if (xnp->ch != ynp->ch)
1978 			return 0;
1979 		xnp = xnp->next;
1980 		ynp = ynp->next;
1981 	}
1982 	return !xnp && !ynp;
1983 }
1984 
1985 /* compare two line segments */
1986 int
1987 linecmp( __LDATA *xl, __LDATA *yl, size_t len )
1988 {
1989 	int i = 0;
1990 	__LDATA *xp = xl, *yp = yl;
1991 
1992 	for (i = 0; i < len; i++, xp++, yp++) {
1993 		if (!cellcmp(xp, yp))
1994 			return 0;
1995 	}
1996 	return 1;
1997 }
1998 
1999 /*
2000  * Output the non-spacing characters associated with the given character
2001  * cell to the screen.
2002  */
2003 
2004 void
2005 __cursesi_putnsp(nschar_t *nsp, const int wy, const int wx)
2006 {
2007 	nschar_t *p;
2008 
2009 	/* this shuts up gcc warnings about wx and wy not being used */
2010 	if (wx > wy) {
2011 	}
2012 
2013 	p = nsp;
2014 	while (p != NULL) {
2015 		__cputwchar((int)p->ch);
2016 #ifdef DEBUG
2017 		__CTRACE(__CTRACE_REFRESH,
2018 		       "_cursesi_putnsp: (%d,%d) non-spacing putwchar(0x%x)\n",
2019 			 wy, wx - 1, p->ch);
2020 #endif
2021 		p = p->next;
2022 	}
2023 }
2024 
2025 #endif /* HAVE_WCHAR */
2026