1998-03-01 12:21:12 +08:00
|
|
|
/****************************************************************************
|
2007-04-29 08:13:28 +08:00
|
|
|
* Copyright (c) 1998-2006,2007 Free Software Foundation, Inc. *
|
1998-03-01 12:21:12 +08:00
|
|
|
* *
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a *
|
|
|
|
* copy of this software and associated documentation files (the *
|
|
|
|
* "Software"), to deal in the Software without restriction, including *
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish, *
|
|
|
|
* distribute, distribute with modifications, sublicense, and/or sell *
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is *
|
|
|
|
* furnished to do so, subject to the following conditions: *
|
|
|
|
* *
|
|
|
|
* The above copyright notice and this permission notice shall be included *
|
|
|
|
* in all copies or substantial portions of the Software. *
|
|
|
|
* *
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
|
|
|
|
* IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
|
|
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
|
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
|
|
|
|
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
|
|
* *
|
|
|
|
* Except as contained in this notice, the name(s) of the above copyright *
|
|
|
|
* holders shall not be used in advertising or otherwise to promote the *
|
|
|
|
* sale, use or other dealings in this Software without prior written *
|
|
|
|
* authorization. *
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
|
|
|
|
* and: Eric S. Raymond <esr@snark.thyrsus.com> *
|
|
|
|
****************************************************************************/
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
|
|
|
|
NAME
|
|
|
|
hardscroll.c -- hardware-scrolling optimization for ncurses
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
void _nc_scroll_optimize(void)
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
OVERVIEW
|
|
|
|
|
|
|
|
This algorithm for computes optimum hardware scrolling to transform an
|
|
|
|
old screen (curscr) into a new screen (newscr) via vertical line moves.
|
|
|
|
|
|
|
|
Because the screen has a `grain' (there are insert/delete/scroll line
|
|
|
|
operations but no insert/delete/scroll column operations), it is efficient
|
|
|
|
break the update algorithm into two pieces: a first stage that does only line
|
|
|
|
moves, optimizing the end product of user-invoked insertions, deletions, and
|
|
|
|
scrolls; and a second phase (corresponding to the present doupdate code in
|
|
|
|
ncurses) that does only line transformations.
|
|
|
|
|
|
|
|
The common case we want hardware scrolling for is to handle line insertions
|
|
|
|
and deletions in screen-oriented text-editors. This two-stage approach will
|
|
|
|
accomplish that at a low computation and code-size cost.
|
|
|
|
|
|
|
|
LINE-MOVE COMPUTATION
|
|
|
|
|
|
|
|
Now, to a discussion of the line-move computation.
|
|
|
|
|
|
|
|
For expository purposes, consider the screen lines to be represented by
|
|
|
|
integers 0..23 (with the understanding that the value of 23 may vary).
|
|
|
|
Let a new line introduced by insertion, scrolling, or at the bottom of
|
|
|
|
the screen following a line delete be given the index -1.
|
|
|
|
|
|
|
|
Assume that the real screen starts with lines 0..23. Now, we have
|
|
|
|
the following possible line-oriented operations on the screen:
|
|
|
|
|
|
|
|
Insertion: inserts a line at a given screen row, forcing all lines below
|
|
|
|
to scroll forward. The last screen line is lost. For example, an insertion
|
|
|
|
at line 5 would produce: 0..4 -1 5..23.
|
|
|
|
|
|
|
|
Deletion: deletes a line at a given screen row, forcing all lines below
|
|
|
|
to scroll forward. The last screen line is made new. For example, a deletion
|
|
|
|
at line 7 would produce: 0..6 8..23 -1.
|
|
|
|
|
|
|
|
Scroll up: move a range of lines up 1. The bottom line of the range
|
|
|
|
becomes new. For example, scrolling up the region from 9 to 14 will
|
|
|
|
produce 0..8 10..14 -1 15..23.
|
|
|
|
|
|
|
|
Scroll down: move a range of lines down 1. The top line of the range
|
|
|
|
becomes new. For example, scrolling down the region from 12 to 16 will produce
|
|
|
|
0..11 -1 12..15 17..23.
|
|
|
|
|
|
|
|
Now, an obvious property of all these operations is that they preserve the
|
|
|
|
order of old lines, though not their position in the sequence.
|
|
|
|
|
|
|
|
The key trick of this algorithm is that the original line indices described
|
|
|
|
above are actually maintained as _line[].oldindex fields in the window
|
|
|
|
structure, and stick to each line through scroll and insert/delete operations.
|
|
|
|
|
|
|
|
Thus, it is possible at update time to look at the oldnum fields and compute
|
|
|
|
an optimal set of il/dl/scroll operations that will take the real screen
|
|
|
|
lines to the virtual screen lines. Once these vertical moves have been done,
|
|
|
|
we can hand off to the second stage of the update algorithm, which does line
|
|
|
|
transformations.
|
|
|
|
|
|
|
|
Note that the move computation does not need to have the full generality
|
|
|
|
of a diff algorithm (which it superficially resembles) because lines cannot
|
|
|
|
be moved out of order.
|
|
|
|
|
|
|
|
THE ALGORITHM
|
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
The scrolling is done in two passes. The first pass is from top to bottom
|
|
|
|
scroling hunks UP. The second one is from bottom to top scrolling hunks DOWN.
|
|
|
|
Obviously enough, no lines to be scrolled will be destroyed. (lav)
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
HOW TO TEST THIS:
|
|
|
|
|
|
|
|
Use the following production:
|
|
|
|
|
|
|
|
hardscroll: hardscroll.c
|
|
|
|
$(CC) -g -DSCROLLDEBUG hardscroll.c -o hardscroll
|
|
|
|
|
|
|
|
Then just type scramble vectors and watch. The following test loads are
|
|
|
|
a representative sample of cases:
|
|
|
|
|
|
|
|
----------------------------- CUT HERE ------------------------------------
|
|
|
|
# No lines moved
|
|
|
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
|
|
|
#
|
|
|
|
# A scroll up
|
|
|
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 -1
|
|
|
|
#
|
|
|
|
# A scroll down
|
|
|
|
-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
|
|
|
#
|
|
|
|
# An insertion (after line 12)
|
|
|
|
0 1 2 3 4 5 6 7 8 9 10 11 12 -1 13 14 15 16 17 18 19 20 21 22
|
|
|
|
#
|
|
|
|
# A simple deletion (line 10)
|
|
|
|
0 1 2 3 4 5 6 7 8 9 11 12 13 14 15 16 17 18 19 20 21 22 23 -1
|
|
|
|
#
|
|
|
|
# A more complex case
|
|
|
|
-1 -1 -1 -1 -1 3 4 5 6 7 -1 -1 8 9 10 11 12 13 14 15 16 17 -1 -1
|
|
|
|
----------------------------- CUT HERE ------------------------------------
|
|
|
|
|
|
|
|
AUTHOR
|
|
|
|
Eric S. Raymond <esr@snark.thyrsus.com>, November 1994
|
1998-03-01 12:21:12 +08:00
|
|
|
New algorithm by Alexander V. Lukyanov <lav@yars.free.net>, Aug 1997
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include <curses.priv.h>
|
|
|
|
|
2007-04-29 08:13:28 +08:00
|
|
|
MODULE_ID("$Id: hardscroll.c,v 1.39 2007/04/28 20:14:08 tom Exp $")
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
#if defined(SCROLLDEBUG) || defined(HASHDEBUG)
|
1999-10-24 12:32:42 +08:00
|
|
|
|
|
|
|
# undef screen_lines
|
|
|
|
# define screen_lines MAXLINES
|
2002-10-13 11:35:53 +08:00
|
|
|
NCURSES_EXPORT_VAR(int)
|
|
|
|
oldnums[MAXLINES];
|
1999-10-24 12:32:42 +08:00
|
|
|
# define OLDNUM(n) oldnums[n]
|
|
|
|
# define _tracef printf
|
|
|
|
# undef TR
|
|
|
|
# define TR(n, a) if (_nc_tracing & (n)) { _tracef a ; putchar('\n'); }
|
|
|
|
|
2007-04-29 08:13:28 +08:00
|
|
|
extern NCURSES_EXPORT_VAR(unsigned) _nc_tracing;
|
|
|
|
|
1999-10-24 12:32:42 +08:00
|
|
|
#else /* no debug */
|
|
|
|
|
|
|
|
/* OLDNUM(n) indicates which line will be shifted to the position n.
|
|
|
|
if OLDNUM(n) == _NEWINDEX, then the line n in new, not shifted from
|
|
|
|
somewhere. */
|
2002-10-13 11:35:53 +08:00
|
|
|
NCURSES_EXPORT_VAR(int *)
|
2006-12-24 10:11:39 +08:00
|
|
|
_nc_oldnums = 0; /* obsolete: keep for ABI compat */
|
2002-10-13 11:35:53 +08:00
|
|
|
|
1999-10-24 12:32:42 +08:00
|
|
|
# if USE_HASHMAP
|
2006-12-24 10:11:39 +08:00
|
|
|
# define oldnums SP->_oldnum_list
|
1999-10-24 12:32:42 +08:00
|
|
|
# define OLDNUM(n) oldnums[n]
|
2002-10-13 11:35:53 +08:00
|
|
|
# else /* !USE_HASHMAP */
|
1999-10-24 12:32:42 +08:00
|
|
|
# define OLDNUM(n) newscr->_line[n].oldindex
|
2002-10-13 11:35:53 +08:00
|
|
|
# endif /* !USE_HASHMAP */
|
1999-10-24 12:32:42 +08:00
|
|
|
|
2006-12-24 10:11:39 +08:00
|
|
|
#define OLDNUM_SIZE SP->_oldnum_size
|
|
|
|
|
1997-05-15 12:00:00 +08:00
|
|
|
#endif /* defined(SCROLLDEBUG) || defined(HASHDEBUG) */
|
|
|
|
|
2002-10-13 11:35:53 +08:00
|
|
|
NCURSES_EXPORT(void)
|
|
|
|
_nc_scroll_optimize(void)
|
1997-05-15 12:00:00 +08:00
|
|
|
/* scroll optimization to transform curscr to newscr */
|
|
|
|
{
|
1998-03-01 12:21:12 +08:00
|
|
|
int i;
|
|
|
|
int start, end, shift;
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
TR(TRACE_ICALLS, ("_nc_scroll_optimize() begins"));
|
|
|
|
|
1999-10-24 12:32:42 +08:00
|
|
|
#if !defined(SCROLLDEBUG) && !defined(HASHDEBUG)
|
|
|
|
#if USE_HASHMAP
|
|
|
|
/* get enough storage */
|
2006-12-24 10:11:39 +08:00
|
|
|
if (OLDNUM_SIZE < screen_lines) {
|
1999-10-24 12:32:42 +08:00
|
|
|
int *new_oldnums = typeRealloc(int, screen_lines, oldnums);
|
|
|
|
if (!new_oldnums)
|
|
|
|
return;
|
|
|
|
oldnums = new_oldnums;
|
2006-12-24 10:11:39 +08:00
|
|
|
OLDNUM_SIZE = screen_lines;
|
1999-10-24 12:32:42 +08:00
|
|
|
}
|
|
|
|
/* calculate the indices */
|
|
|
|
_nc_hash_map();
|
|
|
|
#endif
|
|
|
|
#endif /* !defined(SCROLLDEBUG) && !defined(HASHDEBUG) */
|
|
|
|
|
1997-05-15 12:00:00 +08:00
|
|
|
#ifdef TRACE
|
|
|
|
if (_nc_tracing & (TRACE_UPDATE | TRACE_MOVE))
|
|
|
|
_nc_linedump();
|
|
|
|
#endif /* TRACE */
|
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
/* pass 1 - from top to bottom scrolling up */
|
2002-10-13 11:35:53 +08:00
|
|
|
for (i = 0; i < screen_lines;) {
|
1998-03-01 12:21:12 +08:00
|
|
|
while (i < screen_lines && (OLDNUM(i) == _NEWINDEX || OLDNUM(i) <= i))
|
|
|
|
i++;
|
|
|
|
if (i >= screen_lines)
|
|
|
|
break;
|
1999-10-24 12:32:42 +08:00
|
|
|
|
2002-10-13 11:35:53 +08:00
|
|
|
shift = OLDNUM(i) - i; /* shift > 0 */
|
1998-03-01 12:21:12 +08:00
|
|
|
start = i;
|
1999-10-24 12:32:42 +08:00
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
i++;
|
2002-10-13 11:35:53 +08:00
|
|
|
while (i < screen_lines && OLDNUM(i) != _NEWINDEX && OLDNUM(i) - i
|
|
|
|
== shift)
|
1998-03-01 12:21:12 +08:00
|
|
|
i++;
|
2002-10-13 11:35:53 +08:00
|
|
|
end = i - 1 + shift;
|
1998-03-01 12:21:12 +08:00
|
|
|
|
|
|
|
TR(TRACE_UPDATE | TRACE_MOVE, ("scroll [%d, %d] by %d", start, end, shift));
|
1997-05-15 12:00:00 +08:00
|
|
|
#if !defined(SCROLLDEBUG) && !defined(HASHDEBUG)
|
2002-10-13 11:35:53 +08:00
|
|
|
if (_nc_scrolln(shift, start, end, screen_lines - 1) == ERR) {
|
1998-03-01 12:21:12 +08:00
|
|
|
TR(TRACE_UPDATE | TRACE_MOVE, ("unable to scroll"));
|
|
|
|
continue;
|
|
|
|
}
|
1997-05-15 12:00:00 +08:00
|
|
|
#endif /* !defined(SCROLLDEBUG) && !defined(HASHDEBUG) */
|
1998-03-01 12:21:12 +08:00
|
|
|
}
|
1997-05-15 12:00:00 +08:00
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
/* pass 2 - from bottom to top scrolling down */
|
2002-10-13 11:35:53 +08:00
|
|
|
for (i = screen_lines - 1; i >= 0;) {
|
1998-03-01 12:21:12 +08:00
|
|
|
while (i >= 0 && (OLDNUM(i) == _NEWINDEX || OLDNUM(i) >= i))
|
|
|
|
i--;
|
|
|
|
if (i < 0)
|
|
|
|
break;
|
1999-10-24 12:32:42 +08:00
|
|
|
|
2002-10-13 11:35:53 +08:00
|
|
|
shift = OLDNUM(i) - i; /* shift < 0 */
|
1998-03-01 12:21:12 +08:00
|
|
|
end = i;
|
1999-10-24 12:32:42 +08:00
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
i--;
|
|
|
|
while (i >= 0 && OLDNUM(i) != _NEWINDEX && OLDNUM(i) - i == shift)
|
|
|
|
i--;
|
2002-10-13 11:35:53 +08:00
|
|
|
start = i + 1 - (-shift);
|
1998-03-01 12:21:12 +08:00
|
|
|
|
|
|
|
TR(TRACE_UPDATE | TRACE_MOVE, ("scroll [%d, %d] by %d", start, end, shift));
|
1997-05-15 12:00:00 +08:00
|
|
|
#if !defined(SCROLLDEBUG) && !defined(HASHDEBUG)
|
2002-10-13 11:35:53 +08:00
|
|
|
if (_nc_scrolln(shift, start, end, screen_lines - 1) == ERR) {
|
1998-03-01 12:21:12 +08:00
|
|
|
TR(TRACE_UPDATE | TRACE_MOVE, ("unable to scroll"));
|
|
|
|
continue;
|
1997-05-15 12:00:00 +08:00
|
|
|
}
|
1998-03-01 12:21:12 +08:00
|
|
|
#endif /* !defined(SCROLLDEBUG) && !defined(HASHDEBUG) */
|
|
|
|
}
|
1997-05-15 12:00:00 +08:00
|
|
|
}
|
|
|
|
|
1999-10-24 12:32:42 +08:00
|
|
|
#if defined(TRACE) || defined(SCROLLDEBUG) || defined(HASHDEBUG)
|
2002-10-13 11:35:53 +08:00
|
|
|
NCURSES_EXPORT(void)
|
|
|
|
_nc_linedump(void)
|
1997-05-15 12:00:00 +08:00
|
|
|
/* dump the state of the real and virtual oldnum fields */
|
|
|
|
{
|
2002-10-13 11:35:53 +08:00
|
|
|
int n;
|
2006-12-24 10:11:39 +08:00
|
|
|
char *buf = 0;
|
2002-10-13 11:35:53 +08:00
|
|
|
size_t want = (screen_lines + 1) * 4;
|
1997-05-15 12:00:00 +08:00
|
|
|
|
2006-12-24 10:11:39 +08:00
|
|
|
buf = typeMalloc(char, want);
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
(void) strcpy(buf, "virt");
|
1998-03-01 12:21:12 +08:00
|
|
|
for (n = 0; n < screen_lines; n++)
|
1997-05-15 12:00:00 +08:00
|
|
|
(void) sprintf(buf + strlen(buf), " %02d", OLDNUM(n));
|
|
|
|
TR(TRACE_UPDATE | TRACE_MOVE, (buf));
|
1998-03-01 12:21:12 +08:00
|
|
|
free(buf);
|
1997-05-15 12:00:00 +08:00
|
|
|
}
|
|
|
|
#endif /* defined(TRACE) || defined(SCROLLDEBUG) */
|
|
|
|
|
|
|
|
#ifdef SCROLLDEBUG
|
|
|
|
|
|
|
|
int
|
2002-10-13 11:35:53 +08:00
|
|
|
main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
|
1997-05-15 12:00:00 +08:00
|
|
|
{
|
2002-10-13 11:35:53 +08:00
|
|
|
char line[BUFSIZ], *st;
|
1997-05-15 12:00:00 +08:00
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
#ifdef TRACE
|
1997-05-15 12:00:00 +08:00
|
|
|
_nc_tracing = TRACE_MOVE;
|
1998-03-01 12:21:12 +08:00
|
|
|
#endif
|
2002-10-13 11:35:53 +08:00
|
|
|
for (;;) {
|
|
|
|
int n;
|
1997-05-15 12:00:00 +08:00
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
for (n = 0; n < screen_lines; n++)
|
1997-05-15 12:00:00 +08:00
|
|
|
oldnums[n] = _NEWINDEX;
|
|
|
|
|
|
|
|
/* grab the test vector */
|
2002-10-13 11:35:53 +08:00
|
|
|
if (fgets(line, sizeof(line), stdin) == (char *) NULL)
|
1997-05-15 12:00:00 +08:00
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
|
|
|
|
/* parse it */
|
|
|
|
n = 0;
|
2002-10-13 11:35:53 +08:00
|
|
|
if (line[0] == '#') {
|
1997-05-15 12:00:00 +08:00
|
|
|
(void) fputs(line, stderr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
st = strtok(line, " ");
|
|
|
|
do {
|
|
|
|
oldnums[n++] = atoi(st);
|
|
|
|
} while
|
2002-10-13 11:35:53 +08:00
|
|
|
((st = strtok((char *) NULL, " ")) != 0);
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
/* display it */
|
|
|
|
(void) fputs("Initial input:\n", stderr);
|
|
|
|
_nc_linedump();
|
|
|
|
|
|
|
|
_nc_scroll_optimize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* SCROLLDEBUG */
|
|
|
|
|
|
|
|
/* hardscroll.c ends here */
|