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