[comp.unix] curses "untouch"?

dalegass@dalcsug.UUCP (Dale Gass) (10/14/87)

Could anyone tell the easiest way (or any way) to 'untouch' a window
in curses?

The problem is thus: I want to scroll a portion of my screen, and curses
does this very slowly be rewriting the whole portion.  I want to do a
hardware scroll by sending the proper code directly to my terminal, doing
a scroll(win), and then having curses be happy with the way things are
and do nothing on the next wrefresh(win).  However, since curses thinks
the whole window has to be refreshed due to the scroll(), it rewrites...

Is there any way to tell curses not to bother rewriting; things are just
fine on the screen?


Also, what are the flags ENDLINE,FULLWIN,SCROLLWIN,FLUSH,FULLLINE,IDLINE,
NOCHANGE, leave, clear, ch_off for?

-dalegass@dalcsug.uucp

usenet@mcdchg.UUCP (10/22/87)

[This is the first of three responses to the "untouch" article that I've
received.  In addition, help arrived from Steve Nuchia and Mike Stump.  -mod]

(Gawd, I hate curses!  I'm working with overlapping windows, and
they're a real pain!)

Each line in a window has an offset to the first and last characters
that are changed called _firstch[y] and _lastch[y].  If you set:
	window->_firstch[y] = _NOCHANGE
the line becomes untouched.

There is also an undocumented call touchline(window, y, startx, endx)
to touch a portion of a line in a window.

Hope I got this right.  Good luck
-- 
"You can't argue with a sick mind."

Bob Ankeney  ...!tektronix!reed!bob

usenet@mcdchg.UUCP (10/22/87)

There is a macro called idlok which is _supposed_ to tell curses to use
the hardware insert/delete line capability if possible.  This is implemented
in the ATT version of curses but isn't in the Berkely version.  I will assume
that you have the Berkeley version.  This assumption is valid because
AT&T's version does not use the IDLINE flag, therefore you must be using
BSD.

Berkely's version of scroll() basically moves to line 0 then calls
deleteln (which touches all lines on the current window) and then
restores the cursor to it's original position on the screen.  What you
need to do is the same thing except that you should send the DL termcap
sequence to the terminal by yourself and then 'untouch' all lines in the
window.  A simple (and untested) function to do this is:

hwscroll(win)
WINDOW *win;
{
	int ox,oy,y;

	getyx(win, oy, ox);     /* save current cursor position */
	wmove(win, 0, 0);
	_puts(DL);              /* delete top line of window */
	deleteln(win);          /* update internal data structures of win */
	wmove (win, oy, ox);    /* restore cursor */

	/* now 'untouch' all lines in the current window */

	for (y=0; y<win->_maxy; y++)
	        win->_firstch[y] = _NOCHANGE;
}

This of course works best for windows which are the same size as
the terminal screen.  If this isn't the case then you may need to
insert a line (capability AL) to get the other window back to it's
normal state.

>   
>  Also, what are the flags ENDLINE,FULLWIN,SCROLLWIN,FLUSH,FULLLINE,IDLINE,
>  NOCHANGE, leave, clear, ch_off for?
>   

ENDLINE:  This flag is used to indicate that this window has the same number
	  of columns as the physical terminal screen.  This is useful for
	  functions like clrtoeol.
FULLWIN:  This flag is used to indicate that this window has the same
	  dimensions as the physical terminal screen (ie stdscr).
	  This should allow easy scrolling of the whole window by simply
	  printing a linefeed in the lower right corner of the screen
	  (if the cursor doesn't wrap to top left).

SCROLLWIN:This flag is used to indicate that the window could scroll by
	  accident.  This would occur if you write a character to the
	  lower right corner of the screen and the am capability is
	  present.

NOCHANGE: Is a simple indicator of change.  Different variables are set
	  to this value to indicate that no change has occured.  If the
	  variable is then queried later and the value is other than
	  -1 (NOCHANGE) then it is assumed that a change has occurred
	  (therefore some updating would be necessary).

IDLINE:   Set by the idlok macro.  Unused by Berkeley curses.

FLUSH:    Set by flushok macro.  Unused by Berkeley curses.

leave:    This flag tells the update logic where to leave the cursor at
	  the end of an update cycle.  If it is true then the cursor may
	  be left anywhere after an update.  If it is false then the cursor
	  is left at the current position.

clear:    A flag that is used to tell the refresh logic to clear the
	  terminal screen before the next update.

ch_off:   The character offset of a subwin from the left hand side of
          the terminal screen.  This is used to correctly position characters
          within subwindows and windows whose begx coordinate is not 0.

Hope that this info helps you out.
-- 
------------------------------------------------------------------------
Scott L. Wiegel                          +--+    +--+
					 |  |    |  |
Harris Computer Systems Division         |  +----+  |
2101 W. Cypress Creek Rd.		 |_/-\_/-\_/|
Ft. Lauderdale, Fl  33309                |  +----+  |
                                         |  |    |  |
UUCP:slwiegel@hcx1.harris.com            |__|    |__|
------------------------------------------------------------------------

mb@ttidca.UUCP (Michael Bloom) (10/23/87)

 > The problem is thus: I want to scroll a portion of my screen, and curses
 > does this very slowly be rewriting the whole portion.  I want to do a
 > hardware scroll by sending the proper code directly to my terminal, doing
 > a scroll(win), and then having curses be happy with the way things are
 > and do nothing on the next wrefresh(win).  However, since curses thinks
 > the whole window has to be refreshed due to the scroll(), it rewrites...

I did this once a while back.  You can't use scroll() itself, however.

What you do instead is to use a combination if winsertln() and
wdeleteln() calls, doing the same operations that you did on stdscr
again on curscr AT THE SAME TIME as you handle the hardware scroll.
After which, it is safe to call refresh.  (System V curses is smarter;
for portability to system V, you can just ifdef out all the klugery).

With just the above, you need to do a lot of extra hardware operations.
It's better to put a wrapper around winsertln() and wdeleteln() to leave 
an audit of the operations you've performed on stdscr.

Then you can have a redisplay() routine that looks like

	redisplay()
	{
		if (optimize_scrollinfo(audit_info) < THRESHOLD)
			do_hardware_scroll(audit_info);

		(*application_specific_pre_work)(); /* optional */

		refresh();

		(*application_specific_post_work)(); /* also optional */
		clear_audit();
	}

My original application was fairly simple and always scrolled the same
sub-portion of the window, so my do_hardware_scroll used a global scroll
count instead of a more elaborate audit trail and looked like:

do_hardware_scroll() 
{
	int cnt = ins_del_cnt;
	int abscnt = (ins_del_cnt >=0 ) ?ins_del_cnt : -ins_del_cnt;
	int i;

	if (!abscnt || abscnt > SCROLLBOT-2) /* let curses do its own thing */
		return;

	if (ins_del_cnt <0) /* scroll up */
	{
		for (i = 0;i<abscnt;i++)
			wscr_up(curscr);
      
		if (sc_scroll_region && *sc_scroll_region)
			ansi_scroll(cnt);
		else
		{
			tputs(tgoto(CM,0,0),1,_putchar);
			while (cnt++) 
			{
				tputs(DL,LINES,_putchar);
			}
			tputs(tgoto(CM,0,(MODELINE)-abscnt),1,_putchar);
			while (abscnt--) 
			{
				tputs(AL,LINES,_putchar);
			}
		}
	}
	else
	{
		for (i = 0;i<abscnt;i++)
			wscr_down(curscr);

		tputs(tgoto(CM,0,SCROLLBOT-(abscnt-1)),1,_putchar);
		
		if (sc_scroll_region && *sc_scroll_region)
			ansi_scroll(cnt);
		else
		{
			while (cnt--) 
				tputs(DL,LINES,_putchar);

			scgoto(0,0);
			while (abscnt--) 
				tputs(AL,LINES,_putchar);
		}
	}
}

My ansi scroll always scrolled the same size region. If you aren't doing
that you'd need to replace the "SCROLLBOT,0" constants with appropriate
variables in the following:

ansi_scroll(cnt)
     int cnt;
{
	if (!cnt)
		return;

	tputs(tgoto(CS,SCROLLBOT,0),1,_putchar); /* set region */
	if (cnt <0)
	{
		scgoto(SCROLLBOT,0);
		while (cnt++)
			tputs(SF,SCROLLBOT+1,_putchar);
	}
	else
	{
		scgoto(0,0);
		while (cnt--)
			tputs(SR,SCROLLBOT+1,_putchar);	
	}
	tputs(tgoto(CS,LINES-1,0),1,_putchar); /* restore region*/
	scgoto(0,0);		/* scroll region moved the cursor here */
}

Here are some other routines you can use:

add_line()
{
	wscr_down(stdscr);	
	home();
}
				/* synchronized (curses and real world)
				   cursor motion */
scgoto(row,col)
{
	tputs(tgoto(CM, col, row),1,_putchar);
	wmove(curscr,row,col);
}
/*
 * clear a region of lines
 */

wclear_txt(win)
     WINDOW *win;
{
	register int i;
	for (i=SCROLLBOT;i>=0;i--)
	{
		wmove(win,i,0);
		wclrtoeol(win);
	}
	if (win == curscr) 
		scgoto(0,0);
}

clear_txt()
{
	wclear_txt(stdscr);
}

ins_line(scr) 
WINDOW *scr;
{
	winsertln(scr);
	if (scr == curscr)
		return;
	ins_del_cnt++;
	if (ins_cnt < ins_del_cnt)
		ins_cnt++;
}

del_line(scr)
WINDOW * scr;
{
	wdeleteln(scr);
	if (scr == curscr)
		return;
	ins_del_cnt--;
	if (del_cnt > ins_del_cnt)
		del_cnt--;
}

scrollup()
{
	wscr_up(stdscr);
}

wscr_up(scr)
WINDOW *scr;
{
	wmove(scr,0,0);
	del_line(scr);
	wmove(scr,SCROLLBOT,0);
	winsertln(scr);
}

scrolldown()
{
	wscr_down(stdscr);
}

wscr_down(scr)
WINDOW *scr;
{
	wmove(scr,SCROLLBOT,0);
	wdeleteln(scr);
	wmove(scr,0,0);
	ins_line(scr);
}
clr_cnt() 
{
	ins_del_cnt = del_cnt = ins_cnt = 0;
}

clr_lowest_line() 
{
	move(ECHOLINE,0);
	clrtoeol();
}

invalidate_screen()
{
	werase(curscr);
	clearok(curscr,1);
}
				/* flush: called by routines that dont
				   include curses.h */
flush()
{
	refresh();
}

to_scroll_bot()
{
	move(SCROLLBOT,0);
}
				/* called from routines that dont include
				   either curses.h or stdio.h */
std_flush()
{
	fflush(stdout);
}

katzung@laidbak.UUCP (Brian Katzung) (11/04/87)

Note that to be really complete, the hwscroll() routine should go to
the bottom of the screen and issue an insert line or erase (to end of)
line sequence if DB is set (display retained below).
-- Brian Katzung