[net.bugs.4bsd] curses scrolling

mark@munnari.OZ (Mark Davoren) (06/06/84)

[]

	I didn't get the original question, only the replies.
	I hope this is of interest to those who are trying to
	beat curses into some sense of sanity.

	I have recently finished developing a screen based version of
	the CAI program "learn". It consists of two windows.
	A set of instructions is displayed in the top window
	then the user is put into the shell, editor, etc.
	operating in the bottom window (like a ~12 line screen)
	To do this properly the bottom window must scroll.

	The following are some fixes I had to make to get everything
	working.

	All the programs displayed characters by using waddch
	or derivatives like waddstr etc. This is then followed by a wrefresh.
	I let waddch decide when to scroll the screen.

	1) The following are two fixes to refresh.c version 1.8
	   This will fix the bug which stopped windows of width less than
	   the screen scrolling properly.

diff refresh.c $S/refresh.c

198c198
<                                           /* scroll(win); */   /* mark */
---
>                                           scroll(win);
201,202c201,202
<                                           ly = win->_begy+win->_maxy-1; /* mark */
<                                           lx = win->_begx+win->_maxx; /* mark */
---
>                                           ly = win->_begy+win->_cury;
>                                           lx = win->_begx+win->_curx;

	2) Here is my copy of scroll.c (easier to give the source than a diff)
	   This version fixes the scrolling of curscr
	   and includes a quicky to see whether the delete line and insert line
	   facilities of the terminal can be used (if they exist).
	   Without this addition, scrolling windows is amazingly slow
	   since the whole window is redrawn. Refresh does not have enough
	   information to know when it can simply scroll a window. In addition
	   things would get very confused if refresh had to scroll and make
	   changes as well.

================================================================================
# include	"curses.ext"

/*
 *	This routine scrolls the window up a line.
 *
 * 6/1/83 (Berkeley) @(#)scroll.c	1.3
 *
 *	with bug fixes	20/5/84	    Mark Davoren (Uni of Melbourne, Australia)
 *	with quicky using add line and delete line  1/6/84 "
 */
scroll(win)
reg WINDOW	*win;
{

	reg char	*sp;
	reg int		i;
	reg char	*temp;
	reg int		tx;

	if (!win->_scroll)
		return ERR;

	temp = win->_y[0];
	for (i = 1; i < win->_maxy; i++)
		win->_y[i - 1] = win->_y[i];
	for (sp = temp; sp < &temp[win->_maxx]; )
		*sp++ = ' ';
	win->_y[win->_maxy - 1] = temp;

	if (win == curscr) {
		/*	BUG FIX so that scrolling curscr works */

		mvcur(win->_cury, win->_curx, win->_maxy-1, win->_curx);
		/*
		 *	Doing a newline whilst in the middle of the screen
		 *	is not going to scroll the screen very much!
		 */
		putchar('\n');	/*
				 * Should make use of scroll forwards functions
				 * but only some terminals support it
				 * and I need it to work on all our terminals
				 */

		tx = win->_curx;
		if (!NONL)
			tx = 0;

		/*
		 *	If we were at the top of the screen
		 *	don't go into hyperspace.
		 */
		if (win->_cury > 0)
			win->_cury--;

		mvcur(win->_maxy-1, tx, win->_cury, win->_curx);
# ifdef DEBUG
		fprintf(outf, "SCROLL: win == curscr\n");
# endif
	} else {
#ifdef DEBUG
		fprintf(outf, "SCROLL: win [0%o] != curscr [0%o]\n",win,curscr);
#endif
		if (AL && DL && win->_maxx == curscr->_maxx) {
# ifdef DEBUG
			fprintf(outf, "SCROLL: shifting %d - %d (%d)\n"
				, win->_begy
				, win->_begy + win->_maxy
				, curscr->_maxy
			);
# endif
			temp = curscr->_y[win->_begy];
			for (i = win->_begy; i < win->_begy + win->_maxy; i++)
				curscr->_y[i - 1] = curscr->_y[i];
			for (sp = temp; sp < &temp[curscr->_maxx]; )
				*sp++ = ' ';
			curscr->_y[win->_begy + win->_maxy - 1] = temp;

			mvcur(curscr->_cury, curscr->_curx, win->_begy, 0);
			tputs(DL, 1, _putchar);

			if (curscr->_cury > win->_begy
			 && curscr->_cury <  win->_begy + win->_maxy)
				curscr->_cury--;

			if (win->_begy + win->_maxy != curscr->_maxy) {
				mvcur(win->_begy, 0, win->_begy+win->_maxy - 1, 0);
				tputs(AL, 1, _putchar);
				mvcur(win->_begy+win->_maxy - 1, 0, curscr->_cury, curscr->_curx);
			} else
				mvcur(win->_begy, 0, curscr->_cury, curscr->_curx);

		}
	}
	touchwin(win);
	return OK;
}
================================================================================

	3) The following is a fix to addch.c (1.5) The call to refresh and
	   scroll are swapped. This is to avoid the situation where a character
	   has been placed in the bottom right hand corner of a window
	   which corresponds to the bottom right hand corner of the screen.
	   If refresh is called first then the char would be printed
	   and the terminal will automatically scroll (all the terminals we
	   use do) this of course upsets everything else on the screen, such
	   as the top window. By calling scroll first, no chars are ever written
	   into the bottom right hand corner and thus the terminal doesn't
	   do anything dumb.

diff addch.c $S/addch.c

48d47
< 					scroll(win);
49a49
> 					scroll(win);

	4) Curses seems to have an identity crisis with cbreak mode
	   crmode() exists as separate to raw(), yet it still sets _rawmode.
	   This mucks up the innards of cr_put.c which use _rawmode
	   to decide whether it can do a newline without the ensuing carriage
	   return taking the cursor to column one.
	   This is a problem since whilst in cbreak mode, one can still
	   have newline mapping turned on, but not so in raw mode.
	   (This will only occur if it is quicker to do a newline than
	    do a cursor movement command)
	   So here are fixes to allow a proper crmode.
	   curses.c (1.2) curses.h (1.14) curses.ext (1.3)

diff curses.c $S/curses.c

10d9
<       _crmode         = FALSE,/* set if stty indicates CRMODE mode    */

diff curses.h $S/curses.h

60,66c60
< extern bool   My_term, _echoit, _crmode, _rawmode, _endwin;
< /*
<  *    CRMOD is not the same as RAW, therefore needs a different variable
<  *    to hold state info
<  *    In particular nl mapping is off in raw mode, but not necessarily
<  *    so in crmode                            mark.
<  */
---
> extern bool   My_term, _echoit, _rawmode, _endwin;
139,140c133,134
< #define crmode() (_tty.sg_flags |= CBREAK, _crmode = TRUE, stty(_tty_ch,&_tty))
< #define nocrmode() (_tty.sg_flags &= ~CBREAK,_crmode=FALSE,stty(_tty_ch,&_tty))
---
> #define crmode() (_tty.sg_flags |= CBREAK, _rawmode = TRUE, stty(_tty_ch,&_tty))
> #define nocrmode() (_tty.sg_flags &= ~CBREAK,_rawmode=FALSE,stty(_tty_ch,&_tty))

diff curses.ext $S/curses.ext

11c11
< extern bool   _echoit, _rawmode, My_term, _endwin, _crmode;
---
> extern bool   _echoit, _rawmode, My_term, _endwin;

	I hope this has been of some use to all you out there in curses land

						Mark Davoren
						decvax!mulga!mark