summaryrefslogtreecommitdiff
path: root/lib/libcurses/tty
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libcurses/tty')
-rw-r--r--lib/libcurses/tty/MKexpanded.sh104
-rw-r--r--lib/libcurses/tty/hardscroll.c331
-rw-r--r--lib/libcurses/tty/hashmap.c569
-rw-r--r--lib/libcurses/tty/lib_mvcur.c1212
-rw-r--r--lib/libcurses/tty/lib_tstp.c327
-rw-r--r--lib/libcurses/tty/lib_twait.c223
-rw-r--r--lib/libcurses/tty/lib_vidattr.c273
-rw-r--r--lib/libcurses/tty/tty_display.h148
-rw-r--r--lib/libcurses/tty/tty_input.h63
-rw-r--r--lib/libcurses/tty/tty_update.c1757
10 files changed, 5007 insertions, 0 deletions
diff --git a/lib/libcurses/tty/MKexpanded.sh b/lib/libcurses/tty/MKexpanded.sh
new file mode 100644
index 00000000000..11e9fc16878
--- /dev/null
+++ b/lib/libcurses/tty/MKexpanded.sh
@@ -0,0 +1,104 @@
+#! /bin/sh
+##############################################################################
+# Copyright (c) 1998 Free Software Foundation, Inc. #
+# #
+# 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: Thomas E. Dickey <dickey@clark.net> 1997
+#
+# $OpenBSD: MKexpanded.sh,v 1.1 1999/01/18 19:10:25 millert Exp $
+# $From: MKexpanded.sh,v 1.7 1998/11/11 20:15:39 Alexander.V.Lukyanov Exp $
+#
+# Script to generate 'expanded.c', a dummy source that contains functions
+# corresponding to complex macros used in this library. By making functions,
+# we simplify analysis and debugging.
+
+if test $# != 0; then
+preprocessor="$1"
+else
+preprocessor="cc -E"
+fi
+shift
+if test $# != 0 ; then
+ preprocessor="$preprocessor $*"
+else
+ preprocessor="$preprocessor -DHAVE_CONFIG_H -I. -I../include"
+fi
+
+TMP=gen$$.c
+trap "rm -f $TMP" 0 1 2 5 15
+
+cat <<EOF
+/* generated by MKexpanded.sh */
+#include <curses.priv.h>
+#include <term.h>
+#ifdef NCURSES_EXPANDED
+EOF
+
+cat >$TMP <<EOF
+#include <ncurses_cfg.h>
+#undef NCURSES_EXPANDED /* this probably is set in ncurses_cfg.h */
+#include <curses.priv.h>
+/* these are names we'd like to see */
+#undef ALL_BUT_COLOR
+#undef PAIR_NUMBER
+#undef TRUE
+#undef FALSE
+/* this is a marker */
+IGNORE
+void _nc_toggle_attr_on(attr_t *S, attr_t at)
+{
+ toggle_attr_on(*S,at);
+}
+void _nc_toggle_attr_off(attr_t *S, attr_t at)
+{
+ toggle_attr_off(*S,at);
+}
+int _nc_can_clear_with(chtype ch)
+{
+ return can_clear_with(ch);
+}
+int _nc_DelCharCost(int count)
+{
+ return DelCharCost(count);
+}
+int _nc_InsCharCost(int count)
+{
+ return InsCharCost(count);
+}
+void _nc_UpdateAttrs(chtype c)
+{
+ UpdateAttrs(c);
+}
+EOF
+
+$preprocessor $TMP 2>/dev/null | sed -e '1,/^IGNORE$/d'
+
+cat <<EOF
+#else /* ! NCURSES_EXPANDED */
+void _nc_expanded(void) { }
+#endif /* NCURSES_EXPANDED */
+EOF
diff --git a/lib/libcurses/tty/hardscroll.c b/lib/libcurses/tty/hardscroll.c
new file mode 100644
index 00000000000..f27ece1915b
--- /dev/null
+++ b/lib/libcurses/tty/hardscroll.c
@@ -0,0 +1,331 @@
+/* $OpenBSD: hardscroll.c,v 1.1 1999/01/18 19:10:25 millert Exp $ */
+
+/****************************************************************************
+ * Copyright (c) 1998 Free Software Foundation, Inc. *
+ * *
+ * 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> *
+ ****************************************************************************/
+
+
+/******************************************************************************
+
+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
+
+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)
+
+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
+ New algorithm by Alexander V. Lukyanov <lav@yars.free.net>, Aug 1997
+
+*****************************************************************************/
+
+#include <curses.priv.h>
+
+MODULE_ID("$From: hardscroll.c,v 1.32 1998/09/20 02:34:59 tom Exp $")
+
+#if defined(SCROLLDEBUG) || defined(HASHDEBUG)
+
+# undef screen_lines
+# define screen_lines MAXLINES
+int oldnums[MAXLINES];
+# define OLDNUM(n) oldnums[n]
+# define _tracef printf
+# undef TR
+# define TR(n, a) if (_nc_tracing & (n)) { _tracef a ; putchar('\n'); }
+
+#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. */
+# if USE_HASHMAP
+int *_nc_oldnums = 0;
+static int oldnums_allocated = 0;
+# define oldnums _nc_oldnums
+# define OLDNUM(n) oldnums[n]
+# else /* !USE_HASHMAP */
+# define OLDNUM(n) newscr->_line[n].oldindex
+# endif /* !USE_HASHMAP */
+
+#endif /* defined(SCROLLDEBUG) || defined(HASHDEBUG) */
+
+
+void _nc_scroll_optimize(void)
+/* scroll optimization to transform curscr to newscr */
+{
+ int i;
+ int start, end, shift;
+
+ TR(TRACE_ICALLS, ("_nc_scroll_optimize() begins"));
+
+#if !defined(SCROLLDEBUG) && !defined(HASHDEBUG)
+#if USE_HASHMAP
+ /* get enough storage */
+ if (oldnums_allocated < screen_lines)
+ {
+ size_t size = screen_lines * sizeof(*oldnums);
+ int *new_oldnums = oldnums ? realloc(oldnums, size) : malloc(size);
+ if (!new_oldnums)
+ return;
+ oldnums = new_oldnums;
+ oldnums_allocated = screen_lines;
+ }
+ /* calculate the indices */
+ _nc_hash_map();
+#endif
+#endif /* !defined(SCROLLDEBUG) && !defined(HASHDEBUG) */
+
+#ifdef TRACE
+ if (_nc_tracing & (TRACE_UPDATE | TRACE_MOVE))
+ _nc_linedump();
+#endif /* TRACE */
+
+ /* pass 1 - from top to bottom scrolling up */
+ for (i = 0; i < screen_lines; )
+ {
+ while (i < screen_lines && (OLDNUM(i) == _NEWINDEX || OLDNUM(i) <= i))
+ i++;
+ if (i >= screen_lines)
+ break;
+
+ shift = OLDNUM(i) - i; /* shift > 0 */
+ start = i;
+
+ i++;
+ while (i < screen_lines && OLDNUM(i) != _NEWINDEX && OLDNUM(i) - i == shift)
+ i++;
+ end = i-1 + shift;
+
+ TR(TRACE_UPDATE | TRACE_MOVE, ("scroll [%d, %d] by %d", start, end, shift));
+#if !defined(SCROLLDEBUG) && !defined(HASHDEBUG)
+ if (_nc_scrolln(shift, start, end, screen_lines - 1) == ERR)
+ {
+ TR(TRACE_UPDATE | TRACE_MOVE, ("unable to scroll"));
+ continue;
+ }
+#endif /* !defined(SCROLLDEBUG) && !defined(HASHDEBUG) */
+ }
+
+ /* pass 2 - from bottom to top scrolling down */
+ for (i = screen_lines-1; i >= 0; )
+ {
+ while (i >= 0 && (OLDNUM(i) == _NEWINDEX || OLDNUM(i) >= i))
+ i--;
+ if (i < 0)
+ break;
+
+ shift = OLDNUM(i) - i; /* shift < 0 */
+ end = i;
+
+ i--;
+ while (i >= 0 && OLDNUM(i) != _NEWINDEX && OLDNUM(i) - i == shift)
+ i--;
+ start = i+1 - (-shift);
+
+ TR(TRACE_UPDATE | TRACE_MOVE, ("scroll [%d, %d] by %d", start, end, shift));
+#if !defined(SCROLLDEBUG) && !defined(HASHDEBUG)
+ if (_nc_scrolln(shift, start, end, screen_lines - 1) == ERR)
+ {
+ TR(TRACE_UPDATE | TRACE_MOVE, ("unable to scroll"));
+ continue;
+ }
+#endif /* !defined(SCROLLDEBUG) && !defined(HASHDEBUG) */
+ }
+}
+
+#if defined(TRACE) || defined(SCROLLDEBUG) || defined(HASHDEBUG)
+void _nc_linedump(void)
+/* dump the state of the real and virtual oldnum fields */
+{
+ static size_t have;
+ static char *buf;
+
+ int n;
+ size_t want = (screen_lines + 1) * 4;
+
+ if (have < want)
+ buf = malloc(have = want);
+
+ (void) strcpy(buf, "virt");
+ for (n = 0; n < screen_lines; n++)
+ (void) sprintf(buf + strlen(buf), " %02d", OLDNUM(n));
+ TR(TRACE_UPDATE | TRACE_MOVE, (buf));
+#if NO_LEAKS
+ free(buf);
+ have = 0;
+#endif
+}
+#endif /* defined(TRACE) || defined(SCROLLDEBUG) */
+
+#ifdef SCROLLDEBUG
+
+int
+main(int argc GCC_UNUSED, char *argv[] GCC_UNUSED)
+{
+ char line[BUFSIZ], *st;
+
+#ifdef TRACE
+ _nc_tracing = TRACE_MOVE;
+#endif
+ for (;;)
+ {
+ int n;
+
+ for (n = 0; n < screen_lines; n++)
+ oldnums[n] = _NEWINDEX;
+
+ /* grab the test vector */
+ if (fgets(line, sizeof(line), stdin) == (char *)NULL)
+ exit(EXIT_SUCCESS);
+
+ /* parse it */
+ n = 0;
+ if (line[0] == '#')
+ {
+ (void) fputs(line, stderr);
+ continue;
+ }
+ st = strtok(line, " ");
+ do {
+ oldnums[n++] = atoi(st);
+ } while
+ ((st = strtok((char *)NULL, " ")) != 0);
+
+ /* display it */
+ (void) fputs("Initial input:\n", stderr);
+ _nc_linedump();
+
+ _nc_scroll_optimize();
+ }
+}
+
+#endif /* SCROLLDEBUG */
+
+/* hardscroll.c ends here */
diff --git a/lib/libcurses/tty/hashmap.c b/lib/libcurses/tty/hashmap.c
new file mode 100644
index 00000000000..77cb0e19158
--- /dev/null
+++ b/lib/libcurses/tty/hashmap.c
@@ -0,0 +1,569 @@
+/* $OpenBSD: hashmap.c,v 1.1 1999/01/18 19:10:26 millert Exp $ */
+
+/****************************************************************************
+ * Copyright (c) 1998 Free Software Foundation, Inc. *
+ * *
+ * 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> *
+ ****************************************************************************/
+
+/******************************************************************************
+
+NAME
+ hashmap.c -- fill in scramble vector based on text hashes
+
+SYNOPSIS
+ void _nc_hash_map(void)
+
+DESCRIPTION:
+ This code attempts to recognize pairs of old and new lines in the physical
+and virtual screens. When a line pair is recognized, the old line index is
+placed in the oldindex member of the virtual screen line, to be used by the
+vertical-motion optimizer portion of the update logic (see hardscroll.c).
+
+ Line pairs are recognized by applying a modified Heckel's algorithm,
+sped up by hashing. If a line hash is unique in both screens, those
+lines must be a pair. Then if the lines just before or after the pair
+are the same or similar, they are a pair too.
+
+ We don't worry about false pairs produced by hash collisions, on the
+assumption that such cases are rare and will only make the latter stages
+of update less efficient, not introduce errors.
+
+HOW TO TEST THIS:
+
+Use the following production:
+
+hashmap: hashmap.c
+ $(CC) -g -DHASHDEBUG hashmap.c hardscroll.c ../objects/lib_trace.o -o hashmap
+
+AUTHOR
+ Eric S. Raymond <esr@snark.thyrsus.com>, May 1996
+ Bug fixes and improvements by Alexander V. Lukyanov <lav@yars.free.net>, 1997
+
+*****************************************************************************/
+
+#include <curses.priv.h>
+#include <term.h> /* for back_color_erase */
+
+MODULE_ID("$From: hashmap.c,v 1.28 1998/09/20 02:35:15 tom Exp $")
+
+#ifdef HASHDEBUG
+
+# define _tracef printf
+# undef TR
+# define TR(n, a) if (_nc_tracing & (n)) { _tracef a ; putchar('\n'); }
+# undef screen_lines
+# define screen_lines MAXLINES
+# define TEXTWIDTH 1
+int oldnums[MAXLINES], reallines[MAXLINES];
+static chtype oldtext[MAXLINES][TEXTWIDTH], newtext[MAXLINES][TEXTWIDTH];
+# define OLDNUM(n) oldnums[n]
+# define OLDTEXT(n) oldtext[n]
+# define NEWTEXT(m) newtext[m]
+# define PENDING(n) 1
+
+#else /* !HASHDEBUG */
+
+# define OLDNUM(n) _nc_oldnums[n]
+# define OLDTEXT(n) curscr->_line[n].text
+# define NEWTEXT(m) newscr->_line[m].text
+# define TEXTWIDTH (curscr->_maxx+1)
+# define PENDING(n) (newscr->_line[n].firstchar != _NOCHANGE)
+
+#endif /* !HASHDEBUG */
+
+#define oldhash (SP->oldhash)
+#define newhash (SP->newhash)
+
+static inline unsigned long hash(chtype *text)
+{
+ int i;
+ chtype ch;
+ unsigned long result = 0;
+ for (i = TEXTWIDTH; i>0; i--)
+ {
+ ch = *text++;
+ result += (result<<5) + ch;
+ }
+ return result;
+}
+
+/* approximate update cost */
+static int update_cost(chtype *from,chtype *to)
+{
+ int cost=0;
+ int i;
+
+ for (i=TEXTWIDTH; i>0; i--)
+ if (*from++ != *to++)
+ cost++;
+
+ return cost;
+}
+static int update_cost_from_blank(chtype *to)
+{
+ int cost=0;
+ int i;
+ chtype blank = BLANK;
+
+ if (back_color_erase)
+ blank |= (stdscr->_bkgd & A_COLOR);
+
+ for (i=TEXTWIDTH; i>0; i--)
+ if (blank != *to++)
+ cost++;
+
+ return cost;
+}
+
+/*
+ * Returns true when moving line 'from' to line 'to' seems to be cost
+ * effective. 'blank' indicates whether the line 'to' would become blank.
+ */
+static inline bool cost_effective(const int from, const int to, const bool blank)
+{
+ int new_from;
+
+ if (from == to)
+ return FALSE;
+
+ new_from = OLDNUM(from);
+ if (new_from == _NEWINDEX)
+ new_from = from;
+
+ /*
+ * On the left side of >= is the cost before moving;
+ * on the right side -- cost after moving.
+ */
+ return (((blank ? update_cost_from_blank(NEWTEXT(to))
+ : update_cost(OLDTEXT(to),NEWTEXT(to)))
+ + update_cost(OLDTEXT(new_from),NEWTEXT(from)))
+ >= ((new_from==from ? update_cost_from_blank(NEWTEXT(from))
+ : update_cost(OLDTEXT(new_from),NEWTEXT(from)))
+ + update_cost(OLDTEXT(from),NEWTEXT(to)))) ? TRUE : FALSE;
+}
+
+
+typedef struct
+{
+ unsigned long hashval;
+ int oldcount, newcount;
+ int oldindex, newindex;
+}
+ sym;
+
+static sym *hashtab=0;
+static int lines_alloc=0;
+
+static void grow_hunks(void)
+{
+ int start, end, shift;
+ int back_limit, forward_limit; /* limits for cells to fill */
+ int back_ref_limit, forward_ref_limit; /* limits for refrences */
+ int i;
+ int next_hunk;
+
+ /*
+ * This is tricky part. We have unique pairs to use as anchors.
+ * Use these to deduce the presence of spans of identical lines.
+ */
+ back_limit = 0;
+ back_ref_limit = 0;
+
+ i = 0;
+ while (i < screen_lines && OLDNUM(i) == _NEWINDEX)
+ i++;
+ for ( ; i < screen_lines; i=next_hunk)
+ {
+ start = i;
+ shift = OLDNUM(i) - i;
+
+ /* get forward limit */
+ i = start+1;
+ while (i < screen_lines && OLDNUM(i) != _NEWINDEX && OLDNUM(i) - i == shift)
+ i++;
+ end = i;
+ while (i < screen_lines && OLDNUM(i) == _NEWINDEX)
+ i++;
+ next_hunk = i;
+ forward_limit = i;
+ if (i >= screen_lines || OLDNUM(i) >= i)
+ forward_ref_limit = i;
+ else
+ forward_ref_limit = OLDNUM(i);
+
+ i = start-1;
+ /* grow back */
+ if (shift < 0)
+ back_limit = back_ref_limit + (-shift);
+ while (i >= back_limit)
+ {
+ if(newhash[i] == oldhash[i+shift]
+ || cost_effective(i+shift, i, shift<0))
+ {
+ OLDNUM(i) = i+shift;
+ TR(TRACE_UPDATE | TRACE_MOVE,
+ ("connected new line %d to old line %d (backward continuation)",
+ i, i+shift));
+ }
+ else
+ {
+ TR(TRACE_UPDATE | TRACE_MOVE,
+ ("not connecting new line %d to old line %d (backward continuation)",
+ i, i+shift));
+ break;
+ }
+ i--;
+ }
+
+ i = end;
+ /* grow forward */
+ if (shift > 0)
+ forward_limit = forward_ref_limit - shift;
+ while (i < forward_limit)
+ {
+ if(newhash[i] == oldhash[i+shift]
+ || cost_effective(i+shift, i, shift>0))
+ {
+ OLDNUM(i) = i+shift;
+ TR(TRACE_UPDATE | TRACE_MOVE,
+ ("connected new line %d to old line %d (forward continuation)",
+ i, i+shift));
+ }
+ else
+ {
+ TR(TRACE_UPDATE | TRACE_MOVE,
+ ("not connecting new line %d to old line %d (forward continuation)",
+ i, i+shift));
+ break;
+ }
+ i++;
+ }
+
+ back_ref_limit = back_limit = i;
+ if (shift > 0)
+ back_ref_limit += shift;
+ }
+}
+
+void _nc_hash_map(void)
+{
+ sym *sp;
+ register int i;
+ int start, shift, size;
+
+
+ if (screen_lines > lines_alloc)
+ {
+ if (hashtab)
+ free (hashtab);
+ hashtab = malloc (sizeof(*hashtab)*(screen_lines+1)*2);
+ if (!hashtab)
+ {
+ if (oldhash)
+ FreeAndNull(oldhash);
+ lines_alloc = 0;
+ return;
+ }
+ lines_alloc = screen_lines;
+ }
+
+ if (oldhash && newhash)
+ {
+ /* re-hash only changed lines */
+ for (i = 0; i < screen_lines; i++)
+ {
+ if (PENDING(i))
+ newhash[i] = hash(NEWTEXT(i));
+ }
+ }
+ else
+ {
+ /* re-hash all */
+ if (oldhash == 0)
+ oldhash = typeCalloc (unsigned long, screen_lines);
+ if (newhash == 0)
+ newhash = typeCalloc (unsigned long, screen_lines);
+ if (!oldhash || !newhash)
+ return; /* malloc failure */
+ for (i = 0; i < screen_lines; i++)
+ {
+ newhash[i] = hash(NEWTEXT(i));
+ oldhash[i] = hash(OLDTEXT(i));
+ }
+ }
+
+#ifdef HASH_VERIFY
+ for (i = 0; i < screen_lines; i++)
+ {
+ if(newhash[i] != hash(NEWTEXT(i)))
+ fprintf(stderr,"error in newhash[%d]\n",i);
+ if(oldhash[i] != hash(OLDTEXT(i)))
+ fprintf(stderr,"error in oldhash[%d]\n",i);
+ }
+#endif
+
+ /*
+ * Set up and count line-hash values.
+ */
+ memset(hashtab, '\0', sizeof(*hashtab)*(screen_lines+1)*2);
+ for (i = 0; i < screen_lines; i++)
+ {
+ unsigned long hashval = oldhash[i];
+
+ for (sp = hashtab; sp->hashval; sp++)
+ if (sp->hashval == hashval)
+ break;
+ sp->hashval = hashval; /* in case this is a new entry */
+ sp->oldcount++;
+ sp->oldindex = i;
+ }
+ for (i = 0; i < screen_lines; i++)
+ {
+ unsigned long hashval = newhash[i];
+
+ for (sp = hashtab; sp->hashval; sp++)
+ if (sp->hashval == hashval)
+ break;
+ sp->hashval = hashval; /* in case this is a new entry */
+ sp->newcount++;
+ sp->newindex = i;
+
+ OLDNUM(i) = _NEWINDEX; /* initialize old indices array */
+ }
+
+ /*
+ * Mark line pairs corresponding to unique hash pairs.
+ *
+ * We don't mark lines with offset 0, because it can make fail
+ * extending hunks by cost_effective. Otherwise, it does not
+ * have any side effects.
+ */
+ for (sp = hashtab; sp->hashval; sp++)
+ if (sp->oldcount == 1 && sp->newcount == 1
+ && sp->oldindex != sp->newindex)
+ {
+ TR(TRACE_UPDATE | TRACE_MOVE,
+ ("new line %d is hash-identical to old line %d (unique)",
+ sp->newindex, sp->oldindex));
+ OLDNUM(sp->newindex) = sp->oldindex;
+ }
+
+ grow_hunks();
+
+ /*
+ * Eliminate bad or impossible shifts -- this includes removing
+ * those hunks which could not grow because of conflicts, as well
+ * those which are to be moved too far, they are likely to destroy
+ * more than carry.
+ */
+ for (i = 0; i < screen_lines; )
+ {
+ while (i < screen_lines && OLDNUM(i) == _NEWINDEX)
+ i++;
+ if (i >= screen_lines)
+ break;
+ start = i;
+ shift = OLDNUM(i) - i;
+ i++;
+ while (i < screen_lines && OLDNUM(i) != _NEWINDEX && OLDNUM(i) - i == shift)
+ i++;
+ size = i - start;
+ if (size <= abs(shift))
+ {
+ while (start < i)
+ {
+ OLDNUM(start) = _NEWINDEX;
+ start++;
+ }
+ }
+ }
+
+ /* After clearing invalid hunks, try grow the rest. */
+ grow_hunks();
+
+#if NO_LEAKS
+ FreeAndNull(hashtab);
+ lines_alloc = 0;
+#endif
+}
+
+void _nc_make_oldhash(int i)
+{
+ if (oldhash)
+ oldhash[i] = hash(OLDTEXT(i));
+}
+
+void _nc_scroll_oldhash(int n, int top, int bot)
+{
+ int size;
+ int i;
+
+ if (!oldhash)
+ return;
+
+ size = sizeof(*oldhash) * (bot-top+1-abs(n));
+ if (n > 0)
+ {
+ memmove (oldhash+top, oldhash+top+n, size);
+ for (i = bot; i > bot-n; i--)
+ oldhash[i] = hash(OLDTEXT(i));
+ }
+ else
+ {
+ memmove (oldhash+top-n, oldhash+top, size);
+ for (i = top; i < top-n; i++)
+ oldhash[i] = hash(OLDTEXT(i));
+ }
+}
+
+
+#ifdef HASHDEBUG
+static void
+usage(void)
+{
+ static const char *table[] = {
+ "hashmap test-driver",
+ "",
+ "# comment",
+ "l get initial line number vector",
+ "n use following letters as text of new lines",
+ "o use following letters as text of old lines",
+ "d dump state of test arrays",
+ "h apply hash mapper and see scroll optimization",
+ "? this message"
+ };
+ size_t n;
+ for (n = 0; n < sizeof(table)/sizeof(table[0]); n++)
+ fprintf(stderr, "%s\n", table[n]);
+}
+
+int
+main(int argc GCC_UNUSED, char *argv[] GCC_UNUSED)
+{
+ char line[BUFSIZ], *st;
+ int n;
+
+ SP = typeCalloc(SCREEN,1);
+ for (n = 0; n < screen_lines; n++)
+ {
+ reallines[n] = n;
+ oldnums[n] = _NEWINDEX;
+ oldtext[n][0] = newtext[n][0] = '.';
+ }
+
+ if (isatty(fileno(stdin)))
+ usage();
+
+#ifdef TRACE
+ _nc_tracing = TRACE_MOVE;
+#endif
+ for (;;)
+ {
+ /* grab a test command */
+ if (fgets(line, sizeof(line), stdin) == (char *)NULL)
+ exit(EXIT_SUCCESS);
+
+ switch(line[0])
+ {
+ case '#': /* comment */
+ (void) fputs(line, stderr);
+ break;
+
+ case 'l': /* get initial line number vector */
+ for (n = 0; n < screen_lines; n++)
+ {
+ reallines[n] = n;
+ oldnums[n] = _NEWINDEX;
+ }
+ n = 0;
+ st = strtok(line, " ");
+ do {
+ oldnums[n++] = atoi(st);
+ } while
+ ((st = strtok((char *)NULL, " ")) != 0);
+ break;
+
+ case 'n': /* use following letters as text of new lines */
+ for (n = 0; n < screen_lines; n++)
+ newtext[n][0] = '.';
+ for (n = 0; n < screen_lines; n++)
+ if (line[n+1] == '\n')
+ break;
+ else
+ newtext[n][0] = line[n+1];
+ break;
+
+ case 'o': /* use following letters as text of old lines */
+ for (n = 0; n < screen_lines; n++)
+ oldtext[n][0] = '.';
+ for (n = 0; n < screen_lines; n++)
+ if (line[n+1] == '\n')
+ break;
+ else
+ oldtext[n][0] = line[n+1];
+ break;
+
+ case 'd': /* dump state of test arrays */
+#ifdef TRACE
+ _nc_linedump();
+#endif
+ (void) fputs("Old lines: [", stdout);
+ for (n = 0; n < screen_lines; n++)
+ putchar(oldtext[n][0]);
+ putchar(']');
+ putchar('\n');
+ (void) fputs("New lines: [", stdout);
+ for (n = 0; n < screen_lines; n++)
+ putchar(newtext[n][0]);
+ putchar(']');
+ putchar('\n');
+ break;
+
+ case 'h': /* apply hash mapper and see scroll optimization */
+ _nc_hash_map();
+ (void) fputs("Result:\n", stderr);
+#ifdef TRACE
+ _nc_linedump();
+#endif
+ _nc_scroll_optimize();
+ (void) fputs("Done.\n", stderr);
+ break;
+ case '?':
+ usage();
+ break;
+ }
+ }
+ return EXIT_SUCCESS;
+}
+
+#endif /* HASHDEBUG */
+
+/* hashmap.c ends here */
diff --git a/lib/libcurses/tty/lib_mvcur.c b/lib/libcurses/tty/lib_mvcur.c
new file mode 100644
index 00000000000..9d15770f150
--- /dev/null
+++ b/lib/libcurses/tty/lib_mvcur.c
@@ -0,0 +1,1212 @@
+/* $OpenBSD: lib_mvcur.c,v 1.1 1999/01/18 19:10:26 millert Exp $ */
+
+/****************************************************************************
+ * Copyright (c) 1998 Free Software Foundation, Inc. *
+ * *
+ * 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> *
+ ****************************************************************************/
+
+
+/*
+** lib_mvcur.c
+**
+** The routines for moving the physical cursor and scrolling:
+**
+** void _nc_mvcur_init(void)
+**
+** void _nc_mvcur_resume(void)
+**
+** int mvcur(int old_y, int old_x, int new_y, int new_x)
+**
+** void _nc_mvcur_wrap(void)
+**
+** Comparisons with older movement optimizers:
+** SVr3 curses mvcur() can't use cursor_to_ll or auto_left_margin.
+** 4.4BSD curses can't use cuu/cud/cuf/cub/hpa/vpa/tab/cbt for local
+** motions. It doesn't use tactics based on auto_left_margin. Weirdly
+** enough, it doesn't use its own hardware-scrolling routine to scroll up
+** destination lines for out-of-bounds addresses!
+** old ncurses optimizer: less accurate cost computations (in fact,
+** it was broken and had to be commented out!).
+**
+** Compile with -DMAIN to build an interactive tester/timer for the movement
+** optimizer. You can use it to investigate the optimizer's behavior.
+** You can also use it for tuning the formulas used to determine whether
+** or not full optimization is attempted.
+**
+** This code has a nasty tendency to find bugs in terminfo entries, because it
+** exercises the non-cup movement capabilities heavily. If you think you've
+** found a bug, try deleting subsets of the following capabilities (arranged
+** in decreasing order of suspiciousness): it, tab, cbt, hpa, vpa, cuu, cud,
+** cuf, cub, cuu1, cud1, cuf1, cub1. It may be that one or more are wrong.
+**
+** Note: you should expect this code to look like a resource hog in a profile.
+** That's because it does a lot of I/O, through the tputs() calls. The I/O
+** cost swamps the computation overhead (and as machines get faster, this
+** will become even more true). Comments in the test exerciser at the end
+** go into detail about tuning and how you can gauge the optimizer's
+** effectiveness.
+**/
+
+/****************************************************************************
+ *
+ * Constants and macros for optimizer tuning.
+ *
+ ****************************************************************************/
+
+/*
+ * The average overhead of a full optimization computation in character
+ * transmission times. If it's too high, the algorithm will be a bit
+ * over-biased toward using cup rather than local motions; if it's too
+ * low, the algorithm may spend more time than is strictly optimal
+ * looking for non-cup motions. Profile the optimizer using the `t'
+ * command of the exerciser (see below), and round to the nearest integer.
+ *
+ * Yes, I (esr) thought about computing expected overhead dynamically, say
+ * by derivation from a running average of optimizer times. But the
+ * whole point of this optimization is to *decrease* the frequency of
+ * system calls. :-)
+ */
+#define COMPUTE_OVERHEAD 1 /* I use a 90MHz Pentium @ 9.6Kbps */
+
+/*
+ * LONG_DIST is the distance we consider to be just as costly to move over as a
+ * cup sequence is to emit. In other words, it's the length of a cup sequence
+ * adjusted for average computation overhead. The magic number is the length
+ * of "\033[yy;xxH", the typical cup sequence these days.
+ */
+#define LONG_DIST (8 - COMPUTE_OVERHEAD)
+
+/*
+ * Tell whether a motion is optimizable by local motions. Needs to be cheap to
+ * compute. In general, all the fast moves go to either the right or left edge
+ * of the screen. So any motion to a location that is (a) further away than
+ * LONG_DIST and (b) further inward from the right or left edge than LONG_DIST,
+ * we'll consider nonlocal.
+ */
+#define NOT_LOCAL(fy, fx, ty, tx) ((tx > LONG_DIST) && (tx < screen_lines - 1 - LONG_DIST) && (abs(ty-fy) + abs(tx-fx) > LONG_DIST))
+
+/****************************************************************************
+ *
+ * External interfaces
+ *
+ ****************************************************************************/
+
+/*
+ * For this code to work OK, the following components must live in the
+ * screen structure:
+ *
+ * int _char_padding; // cost of character put
+ * int _cr_cost; // cost of (carriage_return)
+ * int _cup_cost; // cost of (cursor_address)
+ * int _home_cost; // cost of (cursor_home)
+ * int _ll_cost; // cost of (cursor_to_ll)
+ *#if USE_HARD_TABS
+ * int _ht_cost; // cost of (tab)
+ * int _cbt_cost; // cost of (back_tab)
+ *#endif USE_HARD_TABS
+ * int _cub1_cost; // cost of (cursor_left)
+ * int _cuf1_cost; // cost of (cursor_right)
+ * int _cud1_cost; // cost of (cursor_down)
+ * int _cuu1_cost; // cost of (cursor_up)
+ * int _cub_cost; // cost of (parm_cursor_left)
+ * int _cuf_cost; // cost of (parm_cursor_right)
+ * int _cud_cost; // cost of (parm_cursor_down)
+ * int _cuu_cost; // cost of (parm_cursor_up)
+ * int _hpa_cost; // cost of (column_address)
+ * int _vpa_cost; // cost of (row_address)
+ * int _ech_cost; // cost of (erase_chars)
+ * int _rep_cost; // cost of (repeat_char)
+ *
+ * The USE_HARD_TABS switch controls whether it is reliable to use tab/backtabs
+ * for local motions. On many systems, it's not, due to uncertainties about
+ * tab delays and whether or not tabs will be expanded in raw mode. If you
+ * have parm_right_cursor, tab motions don't win you a lot anyhow.
+ */
+
+#include <curses.priv.h>
+#include <term.h>
+#include <ctype.h>
+
+MODULE_ID("$From: lib_mvcur.c,v 1.55 1998/12/05 02:04:48 tom Exp $")
+
+#define STRLEN(s) (s != 0) ? strlen(s) : 0
+
+#define CURRENT_ATTR SP->_current_attr /* current phys attribute */
+#define CURRENT_ROW SP->_cursrow /* phys cursor row */
+#define CURRENT_COLUMN SP->_curscol /* phys cursor column */
+#define REAL_ATTR SP->_current_attr /* phys current attribute */
+#define WANT_CHAR(y, x) SP->_newscr->_line[y].text[x] /* desired state */
+#define BAUDRATE cur_term->_baudrate /* bits per second */
+
+#if defined(MAIN) || defined(NCURSES_TEST)
+#include <sys/time.h>
+
+static bool profiling = FALSE;
+static float diff;
+#endif /* MAIN */
+
+#define OPT_SIZE 512
+
+static int normalized_cost(const char *const cap, int affcnt);
+
+/****************************************************************************
+ *
+ * Initialization/wrapup (including cost pre-computation)
+ *
+ ****************************************************************************/
+
+#ifdef TRACE
+static int
+trace_cost_of(const char *capname, const char *cap, int affcnt)
+{
+ int result = _nc_msec_cost(cap,affcnt);
+ TR(TRACE_CHARPUT|TRACE_MOVE, ("CostOf %s %d", capname, result));
+ return result;
+}
+#define CostOf(cap,affcnt) trace_cost_of(#cap,cap,affcnt);
+
+static int
+trace_normalized_cost(const char *capname, const char *cap, int affcnt)
+{
+ int result = normalized_cost(cap,affcnt);
+ TR(TRACE_CHARPUT|TRACE_MOVE, ("NormalizedCost %s %d", capname, result));
+ return result;
+}
+#define NormalizedCost(cap,affcnt) trace_normalized_cost(#cap,cap,affcnt);
+
+#else
+
+#define CostOf(cap,affcnt) _nc_msec_cost(cap,affcnt);
+#define NormalizedCost(cap,affcnt) normalized_cost(cap,affcnt);
+
+#endif
+
+int _nc_msec_cost(const char *const cap, int affcnt)
+/* compute the cost of a given operation */
+{
+ if (cap == 0)
+ return(INFINITY);
+ else
+ {
+ const char *cp;
+ float cum_cost = 0;
+
+ for (cp = cap; *cp; cp++)
+ {
+ /* extract padding, either mandatory or required */
+ if (cp[0] == '$' && cp[1] == '<' && strchr(cp, '>'))
+ {
+ float number = 0;
+
+ for (cp += 2; *cp != '>'; cp++)
+ {
+ if (isdigit(*cp))
+ number = number * 10 + (*cp - '0');
+ else if (*cp == '.')
+ number += (*++cp - 10) / 10.0;
+ else if (*cp == '*')
+ number *= affcnt;
+ }
+
+ cum_cost += number * 10;
+ }
+ else
+ cum_cost += SP->_char_padding;
+ }
+
+ return((int)cum_cost);
+ }
+}
+
+static int normalized_cost(const char *const cap, int affcnt)
+/* compute the effective character-count for an operation (round up) */
+{
+ int cost = _nc_msec_cost(cap, affcnt);
+ if (cost != INFINITY)
+ cost = (cost + SP->_char_padding - 1) / SP->_char_padding;
+ return cost;
+}
+
+static void reset_scroll_region(void)
+/* Set the scroll-region to a known state (the default) */
+{
+ if (change_scroll_region)
+ {
+ TPUTS_TRACE("change_scroll_region");
+ putp(tparm(change_scroll_region, 0, screen_lines - 1));
+ }
+}
+
+void _nc_mvcur_resume(void)
+/* what to do at initialization time and after each shellout */
+{
+ /* initialize screen for cursor access */
+ if (enter_ca_mode)
+ {
+ TPUTS_TRACE("enter_ca_mode");
+ putp(enter_ca_mode);
+ }
+
+ /*
+ * Doing this here rather than in _nc_mvcur_wrap() ensures that
+ * ncurses programs will see a reset scroll region even if a
+ * program that messed with it died ungracefully.
+ *
+ * This also undoes the effects of terminal init strings that assume
+ * they know the screen size. This is useful when you're running
+ * a vt100 emulation through xterm.
+ */
+ reset_scroll_region();
+ SP->_cursrow = SP->_curscol = -1;
+
+ /* restore cursor shape */
+ if (SP->_cursor != -1)
+ {
+ int cursor = SP->_cursor;
+ SP->_cursor = -1;
+ curs_set (cursor);
+ }
+}
+
+void _nc_mvcur_init(void)
+/* initialize the cost structure */
+{
+ /*
+ * 9 = 7 bits + 1 parity + 1 stop.
+ */
+ SP->_char_padding = (9 * 1000 * 10) / (BAUDRATE > 0 ? BAUDRATE : 9600);
+ if (SP->_char_padding <= 0)
+ SP->_char_padding = 1; /* must be nonzero */
+ TR(TRACE_CHARPUT|TRACE_MOVE, ("char_padding %d msecs", SP->_char_padding));
+
+ /* non-parameterized local-motion strings */
+ SP->_cr_cost = CostOf(carriage_return, 0);
+ SP->_home_cost = CostOf(cursor_home, 0);
+ SP->_ll_cost = CostOf(cursor_to_ll, 0);
+#if USE_HARD_TABS
+ SP->_ht_cost = CostOf(tab, 0);
+ SP->_cbt_cost = CostOf(back_tab, 0);
+#endif /* USE_HARD_TABS */
+ SP->_cub1_cost = CostOf(cursor_left, 0);
+ SP->_cuf1_cost = CostOf(cursor_right, 0);
+ SP->_cud1_cost = CostOf(cursor_down, 0);
+ SP->_cuu1_cost = CostOf(cursor_up, 0);
+
+ SP->_smir_cost = CostOf(enter_insert_mode, 0);
+ SP->_rmir_cost = CostOf(exit_insert_mode, 0);
+ SP->_ip_cost = 0;
+ if (insert_padding) {
+ SP->_ip_cost = CostOf(insert_padding, 0);
+ }
+
+ /*
+ * Assumption: if the terminal has memory_relative addressing, the
+ * initialization strings or smcup will set single-page mode so we
+ * can treat it like absolute screen addressing. This seems to be true
+ * for all cursor_mem_address terminal types in the terminfo database.
+ */
+ SP->_address_cursor = cursor_address ? cursor_address : cursor_mem_address;
+
+ /*
+ * Parametrized local-motion strings. This static cost computation
+ * depends on the following assumptions:
+ *
+ * (1) They never have * padding. In the entire master terminfo database
+ * as of March 1995, only the obsolete Zenith Z-100 pc violates this.
+ * (Proportional padding is found mainly in insert, delete and scroll
+ * capabilities).
+ *
+ * (2) The average case of cup has two two-digit parameters. Strictly,
+ * the average case for a 24 * 80 screen has ((10*10*(1 + 1)) +
+ * (14*10*(1 + 2)) + (10*70*(2 + 1)) + (14*70*4)) / (24*80) = 3.458
+ * digits of parameters. On a 25x80 screen the average is 3.6197.
+ * On larger screens the value gets much closer to 4.
+ *
+ * (3) The average case of cub/cuf/hpa/ech/rep has 2 digits of parameters
+ * (strictly, (((10 * 1) + (70 * 2)) / 80) = 1.8750).
+ *
+ * (4) The average case of cud/cuu/vpa has 2 digits of parameters
+ * (strictly, (((10 * 1) + (14 * 2)) / 24) = 1.5833).
+ *
+ * All these averages depend on the assumption that all parameter values
+ * are equally probable.
+ */
+ SP->_cup_cost = CostOf(tparm(SP->_address_cursor, 23, 23), 1);
+ SP->_cub_cost = CostOf(tparm(parm_left_cursor, 23), 1);
+ SP->_cuf_cost = CostOf(tparm(parm_right_cursor, 23), 1);
+ SP->_cud_cost = CostOf(tparm(parm_down_cursor, 23), 1);
+ SP->_cuu_cost = CostOf(tparm(parm_up_cursor, 23), 1);
+ SP->_hpa_cost = CostOf(tparm(column_address, 23), 1);
+ SP->_vpa_cost = CostOf(tparm(row_address, 23), 1);
+
+ /* non-parameterized screen-update strings */
+ SP->_ed_cost = NormalizedCost(clr_eos, 1);
+ SP->_el_cost = NormalizedCost(clr_eol, 1);
+ SP->_el1_cost = NormalizedCost(clr_bol, 1);
+ SP->_dch1_cost = NormalizedCost(delete_character, 1);
+ SP->_ich1_cost = NormalizedCost(insert_character, 1);
+
+ /* parameterized screen-update strings */
+ SP->_dch_cost = NormalizedCost(tparm(parm_dch, 23), 1);
+ SP->_ich_cost = NormalizedCost(tparm(parm_ich, 23), 1);
+ SP->_ech_cost = NormalizedCost(tparm(erase_chars, 23), 1);
+ SP->_rep_cost = NormalizedCost(tparm(repeat_char, ' ', 23), 1);
+
+ SP->_cup_ch_cost = NormalizedCost(tparm(SP->_address_cursor, 23, 23), 1);
+ SP->_hpa_ch_cost = NormalizedCost(tparm(column_address, 23), 1);
+
+ /* pre-compute some capability lengths */
+ SP->_carriage_return_length = STRLEN(carriage_return);
+ SP->_cursor_home_length = STRLEN(cursor_home);
+ SP->_cursor_to_ll_length = STRLEN(cursor_to_ll);
+
+ /*
+ * A different, possibly better way to arrange this would be to set
+ * SP->_endwin = TRUE at window initialization time and let this be
+ * called by doupdate's return-from-shellout code.
+ */
+ _nc_mvcur_resume();
+}
+
+void _nc_mvcur_wrap(void)
+/* wrap up cursor-addressing mode */
+{
+ /* leave cursor at screen bottom */
+ mvcur(-1, -1, screen_lines - 1, 0);
+
+ /* set cursor to normal mode */
+ if (SP->_cursor != -1)
+ curs_set(1);
+
+ if (exit_ca_mode)
+ {
+ TPUTS_TRACE("exit_ca_mode");
+ putp(exit_ca_mode);
+ }
+ /*
+ * Reset terminal's tab counter. There's a long-time bug that
+ * if you exit a "curses" program such as vi or more, tab
+ * forward, and then backspace, the cursor doesn't go to the
+ * right place. The problem is that the kernel counts the
+ * escape sequences that reset things as column positions.
+ * Utter a \r to reset this invisibly.
+ */
+ _nc_outch('\r');
+}
+
+/****************************************************************************
+ *
+ * Optimized cursor movement
+ *
+ ****************************************************************************/
+
+/*
+ * Perform repeated-append, returning cost
+ */
+static inline int
+repeated_append (int total, int num, int repeat, char *dst, const char *src)
+{
+ register size_t src_len = strlen(src);
+ register size_t dst_len = STRLEN(dst);
+
+ if ((dst_len + repeat * src_len) < OPT_SIZE-1) {
+ total += (num * repeat);
+ if (dst) {
+ dst += dst_len;
+ while (repeat-- > 0) {
+ (void) strcpy(dst, src);
+ dst += src_len;
+ }
+ }
+ } else {
+ total = INFINITY;
+ }
+ return total;
+}
+
+#ifndef NO_OPTIMIZE
+#define NEXTTAB(fr) (fr + init_tabs - (fr % init_tabs))
+
+/*
+ * Assume back_tab (CBT) does not wrap backwards at the left margin, return
+ * a negative value at that point to simplify the loop.
+ */
+#define LASTTAB(fr) ((fr > 0) ? ((fr - 1) / init_tabs) * init_tabs : -1)
+
+/* Note: we'd like to inline this for speed, but GNU C barfs on the attempt. */
+
+static int
+relative_move(char *result, int from_y,int from_x,int to_y,int to_x, bool ovw)
+/* move via local motions (cuu/cuu1/cud/cud1/cub1/cub/cuf1/cuf/vpa/hpa) */
+{
+ int n, vcost = 0, hcost = 0;
+
+ if (result)
+ result[0] = '\0';
+
+ if (to_y != from_y)
+ {
+ vcost = INFINITY;
+
+ if (row_address)
+ {
+ if (result)
+ (void) strcpy(result, tparm(row_address, to_y));
+ vcost = SP->_vpa_cost;
+ }
+
+ if (to_y > from_y)
+ {
+ n = (to_y - from_y);
+
+ if (parm_down_cursor && SP->_cud_cost < vcost)
+ {
+ if (result)
+ (void) strcpy(result, tparm(parm_down_cursor, n));
+ vcost = SP->_cud_cost;
+ }
+
+ if (cursor_down && (n * SP->_cud1_cost < vcost))
+ {
+ if (result)
+ result[0] = '\0';
+ vcost = repeated_append(0, SP->_cud1_cost, n, result, cursor_down);
+ }
+ }
+ else /* (to_y < from_y) */
+ {
+ n = (from_y - to_y);
+
+ if (parm_up_cursor && SP->_cup_cost < vcost)
+ {
+ if (result)
+ (void) strcpy(result, tparm(parm_up_cursor, n));
+ vcost = SP->_cup_cost;
+ }
+
+ if (cursor_up && (n * SP->_cuu1_cost < vcost))
+ {
+ if (result)
+ result[0] = '\0';
+ vcost = repeated_append(0, SP->_cuu1_cost, n, result, cursor_up);
+ }
+ }
+
+ if (vcost == INFINITY)
+ return(INFINITY);
+ }
+
+ if (result)
+ result += strlen(result);
+
+ if (to_x != from_x)
+ {
+ char str[OPT_SIZE];
+
+ hcost = INFINITY;
+
+ if (column_address)
+ {
+ if (result)
+ (void) strcpy(result, tparm(column_address, to_x));
+ hcost = SP->_hpa_cost;
+ }
+
+ if (to_x > from_x)
+ {
+ n = to_x - from_x;
+
+ if (parm_right_cursor && SP->_cuf_cost < hcost)
+ {
+ if (result)
+ (void) strcpy(result, tparm(parm_right_cursor, n));
+ hcost = SP->_cuf_cost;
+ }
+
+ if (cursor_right)
+ {
+ int lhcost = 0;
+
+ str[0] = '\0';
+
+#if USE_HARD_TABS
+ /* use hard tabs, if we have them, to do as much as possible */
+ if (init_tabs > 0 && tab)
+ {
+ int nxt, fr;
+
+ for (fr = from_x; (nxt = NEXTTAB(fr)) <= to_x; fr = nxt)
+ {
+ lhcost = repeated_append(lhcost, SP->_ht_cost, 1, str, tab);
+ if (lhcost == INFINITY)
+ break;
+ }
+
+ n = to_x - fr;
+ from_x = fr;
+ }
+#endif /* USE_HARD_TABS */
+
+#if defined(REAL_ATTR) && defined(WANT_CHAR)
+#ifdef BSD_TPUTS
+ /*
+ * If we're allowing BSD-style padding in tputs, don't generate
+ * a string with a leading digit. Otherwise, that will be
+ * interpreted as a padding value rather than sent to the
+ * screen.
+ */
+ if (ovw
+ && n > 0
+ && vcost == 0
+ && str[0] == '\0'
+ && isdigit(TextOf(WANT_CHAR(to_y, from_x))))
+ ovw = FALSE;
+#endif
+ /*
+ * If we have no attribute changes, overwrite is cheaper.
+ * Note: must suppress this by passing in ovw = FALSE whenever
+ * WANT_CHAR would return invalid data. In particular, this
+ * is true between the time a hardware scroll has been done
+ * and the time the structure WANT_CHAR would access has been
+ * updated.
+ */
+ if (ovw)
+ {
+ int i;
+
+ for (i = 0; i < n; i++)
+ if ((WANT_CHAR(to_y, from_x + i) & A_ATTRIBUTES) != CURRENT_ATTR)
+ {
+ ovw = FALSE;
+ break;
+ }
+ }
+ if (ovw)
+ {
+ char *sp;
+ int i;
+
+ sp = str + strlen(str);
+
+ for (i = 0; i < n; i++)
+ *sp++ = WANT_CHAR(to_y, from_x + i);
+ *sp = '\0';
+ lhcost += n * SP->_char_padding;
+ }
+ else
+#endif /* defined(REAL_ATTR) && defined(WANT_CHAR) */
+ {
+ lhcost = repeated_append(lhcost, SP->_cuf1_cost, n, str, cursor_right);
+ }
+
+ if (lhcost < hcost)
+ {
+ if (result)
+ (void) strcpy(result, str);
+ hcost = lhcost;
+ }
+ }
+ }
+ else /* (to_x < from_x) */
+ {
+ n = from_x - to_x;
+
+ if (parm_left_cursor && SP->_cub_cost < hcost)
+ {
+ if (result)
+ (void) strcpy(result, tparm(parm_left_cursor, n));
+ hcost = SP->_cub_cost;
+ }
+
+ if (cursor_left)
+ {
+ int lhcost = 0;
+
+ str[0] = '\0';
+
+#if USE_HARD_TABS
+ if (init_tabs > 0 && back_tab)
+ {
+ int nxt, fr;
+
+ for (fr = from_x; (nxt = LASTTAB(fr)) >= to_x; fr = nxt)
+ {
+ lhcost = repeated_append(lhcost, SP->_cbt_cost, 1, str, back_tab);
+ if (lhcost == INFINITY)
+ break;
+ }
+
+ n = fr - to_x;
+ }
+#endif /* USE_HARD_TABS */
+
+ lhcost = repeated_append(lhcost, SP->_cub1_cost, n, str, cursor_left);
+
+ if (lhcost < hcost)
+ {
+ if (result)
+ (void) strcpy(result, str);
+ hcost = lhcost;
+ }
+ }
+ }
+
+ if (hcost == INFINITY)
+ return(INFINITY);
+ }
+
+ return(vcost + hcost);
+}
+#endif /* !NO_OPTIMIZE */
+
+/*
+ * With the machinery set up above, it's conceivable that
+ * onscreen_mvcur could be modified into a recursive function that does
+ * an alpha-beta search of motion space, as though it were a chess
+ * move tree, with the weight function being boolean and the search
+ * depth equated to length of string. However, this would jack up the
+ * computation cost a lot, especially on terminals without a cup
+ * capability constraining the search tree depth. So we settle for
+ * the simpler method below.
+ */
+
+static inline int
+onscreen_mvcur(int yold,int xold,int ynew,int xnew, bool ovw)
+/* onscreen move from (yold, xold) to (ynew, xnew) */
+{
+ char use[OPT_SIZE], *sp;
+ int tactic = 0, newcost, usecost = INFINITY;
+
+#if defined(MAIN) || defined(NCURSES_TEST)
+ struct timeval before, after;
+
+ gettimeofday(&before, NULL);
+#endif /* MAIN */
+
+ /* tactic #0: use direct cursor addressing */
+ sp = tparm(SP->_address_cursor, ynew, xnew);
+ if (sp)
+ {
+ tactic = 0;
+ (void) strcpy(use, sp);
+ usecost = SP->_cup_cost;
+
+#if defined(TRACE) || defined(NCURSES_TEST)
+ if (!(_nc_optimize_enable & OPTIMIZE_MVCUR))
+ goto nonlocal;
+#endif /* TRACE */
+
+ /*
+ * We may be able to tell in advance that the full optimization
+ * will probably not be worth its overhead. Also, don't try to
+ * use local movement if the current attribute is anything but
+ * A_NORMAL...there are just too many ways this can screw up
+ * (like, say, local-movement \n getting mapped to some obscure
+ * character because A_ALTCHARSET is on).
+ */
+ if (yold == -1 || xold == -1 || NOT_LOCAL(yold, xold, ynew, xnew))
+ {
+#if defined(MAIN) || defined(NCURSES_TEST)
+ if (!profiling)
+ {
+ (void) fputs("nonlocal\n", stderr);
+ goto nonlocal; /* always run the optimizer if profiling */
+ }
+#else
+ goto nonlocal;
+#endif /* MAIN */
+ }
+ }
+
+#ifndef NO_OPTIMIZE
+ /* tactic #1: use local movement */
+ if (yold != -1 && xold != -1
+ && ((newcost=relative_move(NULL, yold, xold, ynew, xnew, ovw))!=INFINITY)
+ && newcost < usecost)
+ {
+ tactic = 1;
+ usecost = newcost;
+ }
+
+ /* tactic #2: use carriage-return + local movement */
+ if (yold != -1 && carriage_return
+ && ((newcost=relative_move(NULL, yold,0,ynew,xnew, ovw)) != INFINITY)
+ && SP->_cr_cost + newcost < usecost)
+ {
+ tactic = 2;
+ usecost = SP->_cr_cost + newcost;
+ }
+
+ /* tactic #3: use home-cursor + local movement */
+ if (cursor_home
+ && ((newcost=relative_move(NULL, 0, 0, ynew, xnew, ovw)) != INFINITY)
+ && SP->_home_cost + newcost < usecost)
+ {
+ tactic = 3;
+ usecost = SP->_home_cost + newcost;
+ }
+
+ /* tactic #4: use home-down + local movement */
+ if (cursor_to_ll
+ && ((newcost=relative_move(NULL, screen_lines-1, 0, ynew, xnew, ovw)) != INFINITY)
+ && SP->_ll_cost + newcost < usecost)
+ {
+ tactic = 4;
+ usecost = SP->_ll_cost + newcost;
+ }
+
+ /*
+ * tactic #5: use left margin for wrap to right-hand side,
+ * unless strange wrap behavior indicated by xenl might hose us.
+ */
+ if (auto_left_margin && !eat_newline_glitch
+ && yold > 0 && cursor_left
+ && ((newcost=relative_move(NULL, yold-1, screen_columns-1, ynew, xnew, ovw)) != INFINITY)
+ && SP->_cr_cost + SP->_cub1_cost + newcost + newcost < usecost)
+ {
+ tactic = 5;
+ usecost = SP->_cr_cost + SP->_cub1_cost + newcost;
+ }
+
+ /*
+ * These cases are ordered by estimated relative frequency.
+ */
+ if (tactic)
+ {
+ if (tactic == 1)
+ (void) relative_move(use, yold, xold, ynew, xnew, ovw);
+ else if (tactic == 2)
+ {
+ (void) strcpy(use, carriage_return);
+ (void) relative_move(use + SP->_carriage_return_length,
+ yold,0,ynew,xnew, ovw);
+ }
+ else if (tactic == 3)
+ {
+ (void) strcpy(use, cursor_home);
+ (void) relative_move(use + SP->_cursor_home_length,
+ 0, 0, ynew, xnew, ovw);
+ }
+ else if (tactic == 4)
+ {
+ (void) strcpy(use, cursor_to_ll);
+ (void) relative_move(use + SP->_cursor_to_ll_length,
+ screen_lines-1, 0, ynew, xnew, ovw);
+ }
+ else /* if (tactic == 5) */
+ {
+ use[0] = '\0';
+ if (xold > 0)
+ (void) strcat(use, carriage_return);
+ (void) strcat(use, cursor_left);
+ (void) relative_move(use + strlen(use),
+ yold-1, screen_columns-1, ynew, xnew, ovw);
+ }
+ }
+#endif /* !NO_OPTIMIZE */
+
+#if defined(MAIN) || defined(NCURSES_TEST)
+ gettimeofday(&after, NULL);
+ diff = after.tv_usec - before.tv_usec
+ + (after.tv_sec - before.tv_sec) * 1000000;
+ if (!profiling)
+ (void) fprintf(stderr, "onscreen: %d msec, %f 28.8Kbps char-equivalents\n",
+ (int)diff, diff/288);
+#endif /* MAIN */
+
+ nonlocal:
+ if (usecost != INFINITY)
+ {
+ TPUTS_TRACE("mvcur");
+ tputs(use, 1, _nc_outch);
+ return(OK);
+ }
+ else
+ return(ERR);
+}
+
+int mvcur(int yold, int xold, int ynew, int xnew)
+/* optimized cursor move from (yold, xold) to (ynew, xnew) */
+{
+ TR(TRACE_MOVE, ("mvcur(%d,%d,%d,%d) called", yold, xold, ynew, xnew));
+
+ if (yold == ynew && xold == xnew)
+ return(OK);
+
+ /*
+ * Most work here is rounding for terminal boundaries getting the
+ * column position implied by wraparound or the lack thereof and
+ * rolling up the screen to get ynew on the screen.
+ */
+
+ if (xnew >= screen_columns)
+ {
+ ynew += xnew / screen_columns;
+ xnew %= screen_columns;
+ }
+ if (xold >= screen_columns)
+ {
+ int l;
+
+ l = (xold + 1) / screen_columns;
+ yold += l;
+ if (yold >= screen_lines)
+ l -= (yold - screen_lines - 1);
+
+ while (l > 0) {
+ if (newline)
+ {
+ TPUTS_TRACE("newline");
+ tputs(newline, 0, _nc_outch);
+ }
+ else
+ putchar('\n');
+ l--;
+ if (xold > 0)
+ {
+ if (carriage_return)
+ {
+ TPUTS_TRACE("carriage_return");
+ tputs(carriage_return, 0, _nc_outch);
+ }
+ else
+ putchar('\r');
+ xold = 0;
+ }
+ }
+ }
+
+ if (yold > screen_lines - 1)
+ yold = screen_lines - 1;
+ if (ynew > screen_lines - 1)
+ ynew = screen_lines - 1;
+
+ /* destination location is on screen now */
+ return(onscreen_mvcur(yold, xold, ynew, xnew, TRUE));
+}
+
+#if defined(TRACE) || defined(NCURSES_TEST)
+int _nc_optimize_enable = OPTIMIZE_ALL;
+#endif
+
+#if defined(MAIN) || defined(NCURSES_TEST)
+/****************************************************************************
+ *
+ * Movement optimizer test code
+ *
+ ****************************************************************************/
+
+#include <tic.h>
+#include <dump_entry.h>
+
+const char *_nc_progname = "mvcur";
+
+static unsigned long xmits;
+
+int tputs(const char *string, int affcnt GCC_UNUSED, int (*outc)(int) GCC_UNUSED)
+/* stub tputs() that dumps sequences in a visible form */
+{
+ if (profiling)
+ xmits += strlen(string);
+ else
+ (void) fputs(_nc_visbuf(string), stdout);
+ return(OK);
+}
+
+int putp(const char *string)
+{
+ return(tputs(string, 1, _nc_outch));
+}
+
+int _nc_outch(int ch)
+{
+ putc(ch, stdout);
+ return OK;
+}
+
+static char tname[MAX_ALIAS];
+
+static void load_term(void)
+{
+ (void) setupterm(tname, STDOUT_FILENO, NULL);
+}
+
+static int roll(int n)
+{
+ int i, j;
+
+ i = (RAND_MAX / n) * n;
+ while ((j = rand()) >= i)
+ continue;
+ return (j % n);
+}
+
+int main(int argc GCC_UNUSED, char *argv[] GCC_UNUSED)
+{
+ (void) strcpy(tname, termname());
+ load_term();
+ _nc_setupscreen(lines, columns, stdout);
+ baudrate();
+
+ _nc_mvcur_init();
+ NC_BUFFERED(FALSE);
+
+ (void) puts("The mvcur tester. Type ? for help");
+
+ fputs("smcup:", stdout);
+ putchar('\n');
+
+ for (;;)
+ {
+ int fy, fx, ty, tx, n, i;
+ char buf[BUFSIZ], capname[BUFSIZ];
+
+ (void) fputs("> ", stdout);
+ (void) fgets(buf, sizeof(buf), stdin);
+
+ if (buf[0] == '?')
+ {
+(void) puts("? -- display this help message");
+(void) puts("fy fx ty tx -- (4 numbers) display (fy,fx)->(ty,tx) move");
+(void) puts("s[croll] n t b m -- display scrolling sequence");
+(void) printf("r[eload] -- reload terminal info for %s\n", termname());
+(void) puts("l[oad] <term> -- load terminal info for type <term>");
+(void) puts("d[elete] <cap> -- delete named capability");
+(void) puts("i[nspect] -- display terminal capabilities");
+(void) puts("c[ost] -- dump cursor-optimization cost table");
+(void) puts("o[optimize] -- toggle movement optimization");
+(void) puts("t[orture] <num> -- torture-test with <num> random moves");
+(void) puts("q[uit] -- quit the program");
+ }
+ else if (sscanf(buf, "%d %d %d %d", &fy, &fx, &ty, &tx) == 4)
+ {
+ struct timeval before, after;
+
+ putchar('"');
+
+ gettimeofday(&before, NULL);
+ mvcur(fy, fx, ty, tx);
+ gettimeofday(&after, NULL);
+
+ printf("\" (%ld msec)\n",
+ (long)(after.tv_usec - before.tv_usec + (after.tv_sec - before.tv_sec) * 1000000));
+ }
+ else if (sscanf(buf, "s %d %d %d %d", &fy, &fx, &ty, &tx) == 4)
+ {
+ struct timeval before, after;
+
+ putchar('"');
+
+ gettimeofday(&before, NULL);
+ _nc_scrolln(fy, fx, ty, tx);
+ gettimeofday(&after, NULL);
+
+ printf("\" (%ld msec)\n",
+ (long)(after.tv_usec - before.tv_usec + (after.tv_sec - before.tv_sec) * 1000000));
+ }
+ else if (buf[0] == 'r')
+ {
+ (void) strcpy(tname, termname());
+ load_term();
+ }
+ else if (sscanf(buf, "l %s", tname) == 1)
+ {
+ load_term();
+ }
+ else if (sscanf(buf, "d %s", capname) == 1)
+ {
+ struct name_table_entry const *np = _nc_find_entry(capname,
+ _nc_info_hash_table);
+
+ if (np == NULL)
+ (void) printf("No such capability as \"%s\"\n", capname);
+ else
+ {
+ switch(np->nte_type)
+ {
+ case BOOLEAN:
+ cur_term->type.Booleans[np->nte_index] = FALSE;
+ (void) printf("Boolean capability `%s' (%d) turned off.\n",
+ np->nte_name, np->nte_index);
+ break;
+
+ case NUMBER:
+ cur_term->type.Numbers[np->nte_index] = -1;
+ (void) printf("Number capability `%s' (%d) set to -1.\n",
+ np->nte_name, np->nte_index);
+ break;
+
+ case STRING:
+ cur_term->type.Strings[np->nte_index] = (char *)NULL;
+ (void) printf("String capability `%s' (%d) deleted.\n",
+ np->nte_name, np->nte_index);
+ break;
+ }
+ }
+ }
+ else if (buf[0] == 'i')
+ {
+ dump_init((char *)NULL, F_TERMINFO, S_TERMINFO, 70, 0, FALSE);
+ dump_entry(&cur_term->type, FALSE, TRUE, 0);
+ putchar('\n');
+ }
+ else if (buf[0] == 'o')
+ {
+ if (_nc_optimize_enable & OPTIMIZE_MVCUR)
+ {
+ _nc_optimize_enable &=~ OPTIMIZE_MVCUR;
+ (void) puts("Optimization is now off.");
+ }
+ else
+ {
+ _nc_optimize_enable |= OPTIMIZE_MVCUR;
+ (void) puts("Optimization is now on.");
+ }
+ }
+ /*
+ * You can use the `t' test to profile and tune the movement
+ * optimizer. Use iteration values in three digits or more.
+ * At above 5000 iterations the profile timing averages are stable
+ * to within a millisecond or three.
+ *
+ * The `overhead' field of the report will help you pick a
+ * COMPUTE_OVERHEAD figure appropriate for your processor and
+ * expected line speed. The `total estimated time' is
+ * computation time plus a character-transmission time
+ * estimate computed from the number of transmits and the baud
+ * rate.
+ *
+ * Use this together with the `o' command to get a read on the
+ * optimizer's effectiveness. Compare the total estimated times
+ * for `t' runs of the same length in both optimized and un-optimized
+ * modes. As long as the optimized times are less, the optimizer
+ * is winning.
+ */
+ else if (sscanf(buf, "t %d", &n) == 1)
+ {
+ float cumtime = 0, perchar;
+ int speeds[] = {2400, 9600, 14400, 19200, 28800, 38400, 0};
+
+ srand((unsigned)(getpid() + time((time_t *)0)));
+ profiling = TRUE;
+ xmits = 0;
+ for (i = 0; i < n; i++)
+ {
+ /*
+ * This does a move test between two random locations,
+ * Random moves probably short-change the optimizer,
+ * which will work better on the short moves probably
+ * typical of doupdate()'s usage pattern. Still,
+ * until we have better data...
+ */
+#ifdef FIND_COREDUMP
+ int from_y = roll(lines);
+ int to_y = roll(lines);
+ int from_x = roll(columns);
+ int to_x = roll(columns);
+
+ printf("(%d,%d) -> (%d,%d)\n", from_y, from_x, to_y, to_x);
+ mvcur(from_y, from_x, to_y, to_x);
+#else
+ mvcur(roll(lines), roll(columns), roll(lines), roll(columns));
+#endif /* FIND_COREDUMP */
+ if (diff)
+ cumtime += diff;
+ }
+ profiling = FALSE;
+
+ /*
+ * Average milliseconds per character optimization time.
+ * This is the key figure to watch when tuning the optimizer.
+ */
+ perchar = cumtime / n;
+
+ (void) printf("%d moves (%ld chars) in %d msec, %f msec each:\n",
+ n, xmits, (int)cumtime, perchar);
+
+ for (i = 0; speeds[i]; i++)
+ {
+ /*
+ * Total estimated time for the moves, computation and
+ * transmission both. Transmission time is an estimate
+ * assuming 9 bits/char, 8 bits + 1 stop bit.
+ */
+ float totalest = cumtime + xmits * 9 * 1e6 / speeds[i];
+
+ /*
+ * Per-character optimization overhead in character transmits
+ * at the current speed. Round this to the nearest integer
+ * to figure COMPUTE_OVERHEAD for the speed.
+ */
+ float overhead = speeds[i] * perchar / 1e6;
+
+ (void) printf("%6d bps: %3.2f char-xmits overhead; total estimated time %15.2f\n",
+ speeds[i], overhead, totalest);
+ }
+ }
+ else if (buf[0] == 'c')
+ {
+ (void) printf("char padding: %d\n", SP->_char_padding);
+ (void) printf("cr cost: %d\n", SP->_cr_cost);
+ (void) printf("cup cost: %d\n", SP->_cup_cost);
+ (void) printf("home cost: %d\n", SP->_home_cost);
+ (void) printf("ll cost: %d\n", SP->_ll_cost);
+#if USE_HARD_TABS
+ (void) printf("ht cost: %d\n", SP->_ht_cost);
+ (void) printf("cbt cost: %d\n", SP->_cbt_cost);
+#endif /* USE_HARD_TABS */
+ (void) printf("cub1 cost: %d\n", SP->_cub1_cost);
+ (void) printf("cuf1 cost: %d\n", SP->_cuf1_cost);
+ (void) printf("cud1 cost: %d\n", SP->_cud1_cost);
+ (void) printf("cuu1 cost: %d\n", SP->_cuu1_cost);
+ (void) printf("cub cost: %d\n", SP->_cub_cost);
+ (void) printf("cuf cost: %d\n", SP->_cuf_cost);
+ (void) printf("cud cost: %d\n", SP->_cud_cost);
+ (void) printf("cuu cost: %d\n", SP->_cuu_cost);
+ (void) printf("hpa cost: %d\n", SP->_hpa_cost);
+ (void) printf("vpa cost: %d\n", SP->_vpa_cost);
+ }
+ else if (buf[0] == 'x' || buf[0] == 'q')
+ break;
+ else
+ (void) puts("Invalid command.");
+ }
+
+ (void) fputs("rmcup:", stdout);
+ _nc_mvcur_wrap();
+ putchar('\n');
+
+ return(0);
+}
+
+#endif /* MAIN */
+
+/* lib_mvcur.c ends here */
diff --git a/lib/libcurses/tty/lib_tstp.c b/lib/libcurses/tty/lib_tstp.c
new file mode 100644
index 00000000000..709b7ce492a
--- /dev/null
+++ b/lib/libcurses/tty/lib_tstp.c
@@ -0,0 +1,327 @@
+/* $OpenBSD: lib_tstp.c,v 1.1 1999/01/18 19:10:26 millert Exp $ */
+
+/****************************************************************************
+ * Copyright (c) 1998 Free Software Foundation, Inc. *
+ * *
+ * 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> *
+ ****************************************************************************/
+
+
+/*
+** lib_tstp.c
+**
+** The routine _nc_signal_handler().
+**
+*/
+
+#include <curses.priv.h>
+
+#include <signal.h>
+#include <SigAction.h>
+
+#if defined(SVR4_ACTION) && !defined(_POSIX_SOURCE)
+#define _POSIX_SOURCE
+#endif
+
+MODULE_ID("$From: lib_tstp.c,v 1.15 1998/02/11 12:13:57 tom Exp $")
+
+/*
+ * Note: This code is fragile! Its problem is that different OSs
+ * handle restart of system calls interrupted by signals differently.
+ * The ncurses code needs signal-call restart to happen -- otherwise,
+ * interrupted wgetch() calls will return FAIL, probably making the
+ * application think the input stream has ended and it should
+ * terminate. In particular, you know you have this problem if, when
+ * you suspend an ncurses-using lynx with ^Z and resume, it dies
+ * immediately.
+ *
+ * Default behavior of POSIX sigaction(2) is not to restart
+ * interrupted system calls, but Linux's sigaction does it anyway (at
+ * least, on and after the 1.1.47 I (esr) use). Thus this code works
+ * OK under Linux. The 4.4BSD sigaction(2) supports a (non-portable)
+ * SA_RESTART flag that forces the right behavior. Thus, this code
+ * should work OK under BSD/OS, NetBSD, and FreeBSD (let us know if it
+ * does not).
+ *
+ * Stock System Vs (and anything else using a strict-POSIX
+ * sigaction(2) without SA_RESTART) may have a problem. Possible
+ * solutions:
+ *
+ * sigvec restarts by default (SV_INTERRUPT flag to not restart)
+ * signal restarts by default in SVr4 (assuming you link with -lucb)
+ * and BSD, but not SVr3.
+ * sigset restarts, but is only available under SVr4/Solaris.
+ *
+ * The signal(3) call is mandated by the ANSI standard, and its
+ * interaction with sigaction(2) is described in the POSIX standard
+ * (3.3.4.2, page 72,line 934). According to section 8.1, page 191,
+ * however, signal(3) itself is not required by POSIX.1. And POSIX is
+ * silent on whether it is required to restart signals.
+ *
+ * So. The present situation is, we use sigaction(2) with no
+ * guarantee of restart anywhere but on Linux and BSD. We could
+ * switch to signal(3) and collar Linux, BSD, and SVr4. Any way
+ * we slice it, System V UNIXes older than SVr4 will probably lose
+ * (this may include XENIX).
+ *
+ * This implementation will probably be changed to use signal(3) in
+ * the future. If nothing else, it's simpler...
+ */
+
+#ifdef SIGTSTP
+static void tstp(int dummy GCC_UNUSED)
+{
+ sigset_t mask, omask;
+ sigaction_t act, oact;
+
+ T(("tstp() called"));
+
+ /*
+ * The user may have changed the prog_mode tty bits, so save them.
+ */
+ def_prog_mode();
+
+ /*
+ * Block window change and timer signals. The latter
+ * is because applications use timers to decide when
+ * to repaint the screen.
+ */
+ (void)sigemptyset(&mask);
+ (void)sigaddset(&mask, SIGALRM);
+#if USE_SIGWINCH
+ (void)sigaddset(&mask, SIGWINCH);
+#endif
+ (void)sigprocmask(SIG_BLOCK, &mask, &omask);
+
+ /*
+ * End window mode, which also resets the terminal state to the
+ * original (pre-curses) modes.
+ */
+ endwin();
+
+ /* Unblock SIGTSTP. */
+ (void)sigemptyset(&mask);
+ (void)sigaddset(&mask, SIGTSTP);
+ (void)sigprocmask(SIG_UNBLOCK, &mask, NULL);
+
+ /* Now we want to resend SIGSTP to this process and suspend it */
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+#ifdef SA_RESTART
+ act.sa_flags |= SA_RESTART;
+#endif /* SA_RESTART */
+ sigaction(SIGTSTP, &act, &oact);
+ kill(getpid(), SIGTSTP);
+
+ /* Process gets suspended...time passes...process resumes */
+
+ T(("SIGCONT received"));
+ sigaction(SIGTSTP, &oact, NULL);
+ flushinp();
+
+ /*
+ * If the user modified the tty state while suspended, he wants
+ * those changes to stick. So save the new "default" terminal state.
+ */
+ def_shell_mode();
+
+ /*
+ * This relies on the fact that doupdate() will restore the
+ * program-mode tty state, and issue enter_ca_mode if need be.
+ */
+ doupdate();
+
+ /* Reset the signals. */
+ (void)sigprocmask(SIG_SETMASK, &omask, NULL);
+}
+#endif /* defined(SIGTSTP) */
+
+static void cleanup(int sig)
+{
+ /*
+ * Actually, doing any sort of I/O from within an signal handler is
+ * "unsafe". But we'll _try_ to clean up the screen and terminal
+ * settings on the way out.
+ */
+ if (sig == SIGINT
+ || sig == SIGQUIT) {
+#if HAVE_SIGACTION || HAVE_SIGVEC
+ sigaction_t act;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = SIG_IGN;
+ if (sigaction(sig, &act, (sigaction_t *)0) == 0)
+#else
+ if (signal(sig, SIG_IGN) != SIG_ERR)
+#endif
+ {
+ SCREEN *scan = _nc_screen_chain;
+ while(scan)
+ {
+ set_term(scan);
+ endwin();
+ SP->_endwin = FALSE; /* in case we have an atexit! */
+ scan = scan->_next_screen;
+ }
+ }
+ }
+ exit(EXIT_FAILURE);
+}
+
+#if USE_SIGWINCH
+static void sigwinch(int sig GCC_UNUSED)
+{
+ SCREEN *scan = _nc_screen_chain;
+ while(scan)
+ {
+ scan->_sig_winch = TRUE;
+ scan = scan->_next_screen;
+ }
+}
+#endif /* USE_SIGWINCH */
+
+/*
+ * If the given signal is still in its default state, set it to the given
+ * handler.
+ */
+#if HAVE_SIGACTION || HAVE_SIGVEC
+static int CatchIfDefault(int sig, sigaction_t *act)
+{
+ sigaction_t old_act;
+
+ if (sigaction(sig, (sigaction_t *)0, &old_act) == 0
+ && (old_act.sa_handler == SIG_DFL
+#if USE_SIGWINCH
+ || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN)
+#endif
+ )) {
+ (void)sigaction(sig, act, (sigaction_t *)0);
+ return TRUE;
+ }
+ return FALSE;
+}
+#else
+static int CatchIfDefault(int sig, RETSIGTYPE (*handler)())
+{
+ void (*ohandler)();
+
+ ohandler = signal(sig, SIG_IGN);
+ if (ohandler == SIG_DFL
+#if USE_SIGWINCH
+ || (sig == SIGWINCH && ohandler == SIG_IGN)
+#endif
+ ) {
+ signal(sig, handler);
+ return TRUE;
+ } else {
+ signal(sig, ohandler);
+ return FALSE;
+ }
+}
+#endif
+
+/*
+ * This is invoked once at the beginning (e.g., from 'initscr()'), to
+ * initialize the signal catchers, and thereafter when spawning a shell (and
+ * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher.
+ *
+ * If the application has already set one of the signals, we'll not modify it
+ * (during initialization).
+ *
+ * The XSI document implies that we shouldn't keep the SIGTSTP handler if
+ * the caller later changes its mind, but that doesn't seem correct.
+ */
+void _nc_signal_handler(bool enable)
+{
+#ifdef SIGTSTP /* Xenix 2.x doesn't have this */
+static sigaction_t act, oact;
+static int ignore;
+
+ if (!ignore)
+ {
+ if (!enable)
+ {
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGTSTP, &act, &oact);
+ }
+ else if (act.sa_handler)
+ {
+ sigaction(SIGTSTP, &oact, NULL);
+ }
+ else /*initialize */
+ {
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+#if USE_SIGWINCH
+ act.sa_handler = sigwinch;
+ CatchIfDefault(SIGWINCH, &act);
+#endif
+
+#ifdef SA_RESTART
+ act.sa_flags |= SA_RESTART;
+#endif /* SA_RESTART */
+ act.sa_handler = cleanup;
+ CatchIfDefault(SIGINT, &act);
+ CatchIfDefault(SIGTERM, &act);
+
+ act.sa_handler = tstp;
+ if (!CatchIfDefault(SIGTSTP, &act))
+ ignore = TRUE;
+ }
+ }
+#else /* !SIGTSTP */
+ if (enable)
+ {
+#if HAVE_SIGACTION || HAVE_SIGVEC
+ static sigaction_t act;
+ sigemptyset(&act.sa_mask);
+#if USE_SIGWINCH
+ act.sa_handler = sigwinch;
+ CatchIfDefault(SIGWINCH, &act);
+#endif
+#ifdef SA_RESTART
+ act.sa_flags |= SA_RESTART;
+#endif /* SA_RESTART */
+ act.sa_handler = cleanup;
+ CatchIfDefault(SIGINT, &act);
+ CatchIfDefault(SIGTERM, &act);
+
+#else /* !(HAVE_SIGACTION || HAVE_SIGVEC) */
+
+ CatchIfDefault(SIGINT, cleanup);
+ CatchIfDefault(SIGTERM, cleanup);
+#if USE_SIGWINCH
+ CatchIfDefault(SIGWINCH, sigwinch);
+#endif
+#endif /* !(HAVE_SIGACTION || HAVE_SIGVEC) */
+ }
+#endif /* !SIGTSTP */
+}
diff --git a/lib/libcurses/tty/lib_twait.c b/lib/libcurses/tty/lib_twait.c
new file mode 100644
index 00000000000..5a7cffeae2b
--- /dev/null
+++ b/lib/libcurses/tty/lib_twait.c
@@ -0,0 +1,223 @@
+/* $OpenBSD: lib_twait.c,v 1.1 1999/01/18 19:10:27 millert Exp $ */
+
+/****************************************************************************
+ * Copyright (c) 1998 Free Software Foundation, Inc. *
+ * *
+ * 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> *
+ ****************************************************************************/
+
+/*
+** lib_twait.c
+**
+** The routine _nc_timed_wait().
+**
+** (This file was originally written by Eric Raymond; however except for
+** comments, none of the original code remains - T.Dickey).
+*/
+
+#include <curses.priv.h>
+
+#if USE_FUNC_POLL
+# include <stropts.h>
+# include <poll.h>
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# endif
+#elif HAVE_SELECT
+# if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
+# include <sys/time.h>
+# endif
+# if HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# endif
+#endif
+
+#ifdef __BEOS__
+/* BeOS select() only works on sockets. Use the tty hack instead */
+#include <socket.h>
+#define select check_select
+#endif
+
+MODULE_ID("$From: lib_twait.c,v 1.32 1998/06/06 22:44:14 tom Exp $")
+
+static int _nc_gettime(void)
+{
+ int res;
+
+#if HAVE_GETTIMEOFDAY
+# define PRECISE_GETTIME 1
+ struct timeval t;
+ gettimeofday(&t, (struct timezone *)0);
+ res = t.tv_sec*1000 + t.tv_usec/1000;
+#else
+# define PRECISE_GETTIME 0
+ res = time(0)*1000;
+#endif
+ T(("time: %d msec", res));
+ return res;
+}
+
+/*
+ * Wait a specified number of milliseconds, returning nonzero if the timer
+ * didn't expire before there is activity on the specified file descriptors.
+ * The file-descriptors are specified by the mode:
+ * 0 - none (absolute time)
+ * 1 - ncurses' normal input-descriptor
+ * 2 - mouse descriptor, if any
+ * 3 - either input or mouse.
+ * We return a mask that corresponds to the mode (e.g., 2 for mouse activity).
+ *
+ * If the milliseconds given are -1, the wait blocks until activity on the
+ * descriptors.
+ */
+int _nc_timed_wait(int mode, int milliseconds, int *timeleft)
+{
+int fd;
+int count;
+
+int result;
+
+#if USE_FUNC_POLL
+struct pollfd fds[2];
+#elif HAVE_SELECT
+static fd_set set;
+#endif
+
+int starttime, returntime;
+
+ T(("start twait: %d milliseconds, mode: %d", milliseconds, mode));
+
+#if PRECISE_GETTIME
+retry:
+#endif
+ starttime = _nc_gettime();
+
+ count = 0;
+
+#if USE_FUNC_POLL
+ if (mode & 1) {
+ fds[count].fd = SP->_ifd;
+ fds[count].events = POLLIN;
+ count++;
+ }
+ if ((mode & 2)
+ && (fd = SP->_mouse_fd) >= 0) {
+ fds[count].fd = fd;
+ fds[count].events = POLLIN;
+ count++;
+ }
+ result = poll(fds, count, milliseconds);
+
+#elif HAVE_SELECT
+ /*
+ * select() modifies the fd_set arguments; do this in the
+ * loop.
+ */
+ FD_ZERO(&set);
+
+ if (mode & 1) {
+ FD_SET(SP->_ifd, &set);
+ count = SP->_ifd + 1;
+ }
+ if ((mode & 2)
+ && (fd = SP->_mouse_fd) >= 0) {
+ FD_SET(fd, &set);
+ count = max(fd, count) + 1;
+ }
+
+ if (milliseconds >= 0) {
+ struct timeval ntimeout;
+ ntimeout.tv_sec = milliseconds / 1000;
+ ntimeout.tv_usec = (milliseconds % 1000) * 1000;
+ result = select(count, &set, NULL, NULL, &ntimeout);
+ } else {
+ result = select(count, &set, NULL, NULL, NULL);
+ }
+#endif
+
+ returntime = _nc_gettime();
+
+ if (milliseconds >= 0)
+ milliseconds -= returntime-starttime;
+
+#if PRECISE_GETTIME
+ /*
+ * If the timeout hasn't expired, and we've gotten no data,
+ * this is probably a system where 'select()' needs to be left
+ * alone so that it can complete. Make this process sleep,
+ * then come back for more.
+ */
+ if (result == 0 && milliseconds > 100) {
+ napms(100);
+ milliseconds -= 100;
+ goto retry;
+ }
+#endif
+
+ /* return approximate time left in milliseconds */
+ if (timeleft)
+ *timeleft = milliseconds;
+
+ T(("end twait: returned %d (%d), remaining time %d msec",
+ result, errno, milliseconds));
+
+ /*
+ * Both 'poll()' and 'select()' return the number of file descriptors
+ * that are active. Translate this back to the mask that denotes which
+ * file-descriptors, so that we don't need all of this system-specific
+ * code everywhere.
+ */
+ if (result != 0) {
+ if (result > 0) {
+ result = 0;
+#if USE_FUNC_POLL
+ for (count = 0; count < 2; count++) {
+ if ((mode & (1 << count))
+ && (fds[count].revents & POLLIN)) {
+ result |= (1 << count);
+ count++;
+ }
+ }
+#elif HAVE_SELECT
+ if ((mode & 2)
+ && (fd = SP->_mouse_fd) >= 0
+ && FD_ISSET(fd, &set))
+ result |= 2;
+ if ((mode & 1)
+ && FD_ISSET(SP->_ifd, &set))
+ result |= 1;
+#endif
+ }
+ else
+ result = 0;
+ }
+
+ return (result);
+}
diff --git a/lib/libcurses/tty/lib_vidattr.c b/lib/libcurses/tty/lib_vidattr.c
new file mode 100644
index 00000000000..b148877d992
--- /dev/null
+++ b/lib/libcurses/tty/lib_vidattr.c
@@ -0,0 +1,273 @@
+/* $OpenBSD: lib_vidattr.c,v 1.1 1999/01/18 19:10:27 millert Exp $ */
+
+/****************************************************************************
+ * Copyright (c) 1998 Free Software Foundation, Inc. *
+ * *
+ * 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> *
+ ****************************************************************************/
+
+/*
+ * vidputs(newmode, outc)
+ *
+ * newmode is taken to be the logical 'or' of the symbols in curses.h
+ * representing graphic renditions. The terminal is set to be in all of
+ * the given modes, if possible.
+ *
+ * if the new attribute is normal
+ * if exit-alt-char-set exists
+ * emit it
+ * emit exit-attribute-mode
+ * else if set-attributes exists
+ * use it to set exactly what you want
+ * else
+ * if exit-attribute-mode exists
+ * turn off everything
+ * else
+ * turn off those which can be turned off and aren't in
+ * newmode.
+ * turn on each mode which should be on and isn't, one by one
+ *
+ * NOTE that this algorithm won't achieve the desired mix of attributes
+ * in some cases, but those are probably just those cases in which it is
+ * actually impossible, anyway, so...
+ *
+ * NOTE that we cannot assume that there's no interaction between color
+ * and other attribute resets. So each time we reset color (or other
+ * attributes) we'll have to be prepared to restore the other.
+ */
+
+#include <curses.priv.h>
+#include <term.h>
+
+MODULE_ID("$From: lib_vidattr.c,v 1.21 1998/08/01 22:21:19 tom Exp $")
+
+#define doPut(mode) TPUTS_TRACE(#mode); tputs(mode, 1, outc)
+
+#define TurnOn(mask,mode) \
+ if ((turn_on & mask) && mode) { doPut(mode); }
+
+#define TurnOff(mask,mode) \
+ if ((turn_off & mask) && mode) { doPut(mode); turn_off &= ~mask; }
+
+ /* if there is no current screen, assume we *can* do color */
+#define SetColorsIf(why,old_attr) \
+ if ((!SP || SP->_coloron) && (why)) { \
+ int old_pair = PAIR_NUMBER(old_attr); \
+ T(("old pair = %d -- new pair = %d", old_pair, pair)); \
+ if ((pair != old_pair) \
+ || (reverse ^ ((old_attr & A_REVERSE) != 0))) { \
+ _nc_do_color(pair, reverse, outc); \
+ } \
+ }
+
+int vidputs(attr_t newmode, int (*outc)(int))
+{
+static attr_t previous_attr = A_NORMAL;
+attr_t turn_on, turn_off;
+int pair;
+bool reverse = FALSE;
+bool used_ncv = FALSE;
+
+ T((T_CALLED("vidputs(%s)"), _traceattr(newmode)));
+
+ /* this allows us to go on whether or not newterm() has been called */
+ if (SP)
+ previous_attr = SP->_current_attr;
+
+ T(("previous attribute was %s", _traceattr(previous_attr)));
+
+#if !USE_XMC_SUPPORT
+ if (magic_cookie_glitch > 0)
+ newmode &= ~(SP->_xmc_suppress);
+#endif
+
+ /*
+ * If we have a terminal that cannot combine color with video
+ * attributes, use the colors in preference.
+ */
+ if ((newmode & A_COLOR)
+ && (no_color_video > 0)) {
+ static const struct {
+ attr_t video;
+ unsigned bit;
+ } table[] = {
+ { A_STANDOUT, 1 },
+ { A_UNDERLINE, 2 },
+ { A_REVERSE, 4 },
+ { A_BLINK, 8 },
+ { A_DIM, 16 },
+ { A_BOLD, 32 },
+ { A_INVIS, 64 },
+ { A_PROTECT, 128 },
+ { A_ALTCHARSET, 256 },
+ };
+ size_t n;
+ for (n = 0; n < SIZEOF(table); n++) {
+ if ((table[n].bit & no_color_video)
+ && (table[n].video & newmode)) {
+ used_ncv = TRUE;
+ if (table[n].video == A_REVERSE)
+ reverse = TRUE;
+ else
+ newmode &= ~table[n].video;
+ }
+ }
+ }
+
+ if (newmode == previous_attr)
+ returnCode(OK);
+
+ pair = PAIR_NUMBER(newmode);
+
+ if (reverse) {
+ newmode &= ~A_REVERSE;
+ }
+
+ turn_off = (~newmode & previous_attr) & ALL_BUT_COLOR;
+ turn_on = (newmode & ~previous_attr) & ALL_BUT_COLOR;
+
+ SetColorsIf(pair == 0, previous_attr);
+
+ if (newmode == A_NORMAL) {
+ if((previous_attr & A_ALTCHARSET) && exit_alt_charset_mode) {
+ doPut(exit_alt_charset_mode);
+ previous_attr &= ~A_ALTCHARSET;
+ }
+ if (previous_attr) {
+ doPut(exit_attribute_mode);
+ previous_attr &= ~A_COLOR;
+ }
+
+ SetColorsIf(pair != 0, previous_attr);
+ } else if (set_attributes && !used_ncv) {
+ if (turn_on || turn_off) {
+ TPUTS_TRACE("set_attributes");
+ tputs(tparm(set_attributes,
+ (newmode & A_STANDOUT) != 0,
+ (newmode & A_UNDERLINE) != 0,
+ (newmode & A_REVERSE) != 0,
+ (newmode & A_BLINK) != 0,
+ (newmode & A_DIM) != 0,
+ (newmode & A_BOLD) != 0,
+ (newmode & A_INVIS) != 0,
+ (newmode & A_PROTECT) != 0,
+ (newmode & A_ALTCHARSET) != 0), 1, outc);
+ previous_attr &= ~A_COLOR;
+ }
+ SetColorsIf(pair != 0, previous_attr);
+ } else {
+
+ T(("turning %s off", _traceattr(turn_off)));
+
+ TurnOff(A_ALTCHARSET, exit_alt_charset_mode);
+ TurnOff(A_UNDERLINE, exit_underline_mode);
+ TurnOff(A_STANDOUT, exit_standout_mode);
+
+ if (turn_off && exit_attribute_mode) {
+ doPut(exit_attribute_mode);
+ turn_on |= (newmode & (chtype)(~A_COLOR));
+ previous_attr &= ~A_COLOR;
+ }
+ SetColorsIf(pair != 0, previous_attr);
+
+ T(("turning %s on", _traceattr(turn_on)));
+
+ TurnOn (A_ALTCHARSET, enter_alt_charset_mode);
+ TurnOn (A_BLINK, enter_blink_mode);
+ TurnOn (A_BOLD, enter_bold_mode);
+ TurnOn (A_DIM, enter_dim_mode);
+ TurnOn (A_REVERSE, enter_reverse_mode);
+ TurnOn (A_STANDOUT, enter_standout_mode);
+ TurnOn (A_PROTECT, enter_protected_mode);
+ TurnOn (A_INVIS, enter_secure_mode);
+ TurnOn (A_UNDERLINE, enter_underline_mode);
+ TurnOn (A_HORIZONTAL, enter_horizontal_hl_mode);
+ TurnOn (A_LEFT, enter_left_hl_mode);
+ TurnOn (A_LOW, enter_low_hl_mode);
+ TurnOn (A_RIGHT, enter_right_hl_mode);
+ TurnOn (A_TOP, enter_top_hl_mode);
+ TurnOn (A_VERTICAL, enter_vertical_hl_mode);
+ }
+
+ if (reverse)
+ newmode |= A_REVERSE;
+
+ if (SP)
+ SP->_current_attr = newmode;
+ else
+ previous_attr = newmode;
+
+ returnCode(OK);
+}
+
+int vidattr(attr_t newmode)
+{
+ T((T_CALLED("vidattr(%s)"), _traceattr(newmode)));
+
+ returnCode(vidputs(newmode, _nc_outch));
+}
+
+chtype termattrs(void)
+{
+ chtype attrs = A_NORMAL;
+
+ if (enter_alt_charset_mode)
+ attrs |= A_ALTCHARSET;
+
+ if (enter_blink_mode)
+ attrs |= A_BLINK;
+
+ if (enter_bold_mode)
+ attrs |= A_BOLD;
+
+ if (enter_dim_mode)
+ attrs |= A_DIM;
+
+ if (enter_reverse_mode)
+ attrs |= A_REVERSE;
+
+ if (enter_standout_mode)
+ attrs |= A_STANDOUT;
+
+ if (enter_protected_mode)
+ attrs |= A_PROTECT;
+
+ if (enter_secure_mode)
+ attrs |= A_INVIS;
+
+ if (enter_underline_mode)
+ attrs |= A_UNDERLINE;
+
+ if (SP->_coloron)
+ attrs |= A_COLOR;
+
+ return(attrs);
+}
+
diff --git a/lib/libcurses/tty/tty_display.h b/lib/libcurses/tty/tty_display.h
new file mode 100644
index 00000000000..9aed32d8116
--- /dev/null
+++ b/lib/libcurses/tty/tty_display.h
@@ -0,0 +1,148 @@
+/* $OpenBSD: tty_display.h,v 1.1 1999/01/18 19:10:27 millert Exp $ */
+
+/****************************************************************************
+ * Copyright (c) 1998 Free Software Foundation, Inc. *
+ * *
+ * 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. *
+ ************************************************************************** */
+
+#ifndef TTY_DISPLAY_H
+#define TTY_DISPLAY_H 1
+
+extern bool _nc_tty_beep(void);
+extern bool _nc_tty_check_resize(void);
+extern bool _nc_tty_cursor(int);
+extern bool _nc_tty_flash(void);
+extern bool _nc_tty_init_color(int,int,int,int);
+extern bool _nc_tty_init_pair(int,int,int);
+extern bool _nc_tty_slk_hide(bool);
+extern bool _nc_tty_slk_update(int,const char *);
+extern bool _nc_tty_start_color(void);
+extern void _nc_tty_display_resume(void);
+extern void _nc_tty_display_suspend(void);
+extern void _nc_tty_dispose(void); /* frees SP->_term */
+extern void _nc_tty_switch_to(void);
+extern void _nc_tty_update(void);
+
+struct tty_display_data {
+ int _fifohold; /* set if breakout marked */
+ unsigned long _current_attr; /* terminal attribute current set */
+ int _cursrow; /* physical cursor row (-1=unknown) */
+ int _curscol; /* physical cursor column */
+
+ /* cursor movement costs; units are 10ths of milliseconds */
+ int _char_padding; /* cost of character put */
+ int _cr_cost; /* cost of (carriage_return) */
+ int _cup_cost; /* cost of (cursor_address) */
+ int _home_cost; /* cost of (cursor_home) */
+ int _ll_cost; /* cost of (cursor_to_ll) */
+#if USE_HARD_TABS
+ int _ht_cost; /* cost of (tab) */
+ int _cbt_cost; /* cost of (backtab) */
+#endif /* USE_HARD_TABS */
+ int _cub1_cost; /* cost of (cursor_left) */
+ int _cuf1_cost; /* cost of (cursor_right) */
+ int _cud1_cost; /* cost of (cursor_down) */
+ int _cuu1_cost; /* cost of (cursor_up) */
+ int _cub_cost; /* cost of (parm_cursor_left) */
+ int _cuf_cost; /* cost of (parm_cursor_right) */
+ int _cud_cost; /* cost of (parm_cursor_down) */
+ int _cuu_cost; /* cost of (parm_cursor_up) */
+ int _hpa_cost; /* cost of (column_address) */
+ int _vpa_cost; /* cost of (row_address) */
+ /* used in lib_doupdate.c, must be chars */
+ int _ed_cost; /* cost of (clr_eos) */
+ int _el_cost; /* cost of (clr_eol) */
+ int _el1_cost; /* cost of (clr_bol) */
+ int _dch1_cost; /* cost of (delete_character) */
+ int _ich1_cost; /* cost of (insert_character) */
+ int _dch_cost; /* cost of (parm_dch) */
+ int _ich_cost; /* cost of (parm_ich) */
+ int _ech_cost; /* cost of (erase_chars) */
+ int _rep_cost; /* cost of (repeat_char) */
+ int _hpa_ch_cost; /* cost of (column_address) */
+ int _cup_ch_cost; /* cost of (cursor_address) */
+ int _smir_cost; /* cost of (enter_insert_mode) */
+ int _rmir_cost; /* cost of (exit_insert_mode) */
+ int _ip_cost; /* cost of (insert_padding) */
+ /* used in lib_mvcur.c */
+ char * _address_cursor;
+ int _carriage_return_length;
+ int _cursor_home_length;
+ int _cursor_to_ll_length;
+
+ chtype _xmc_suppress; /* attributes to suppress if xmc */
+ chtype _xmc_triggers; /* attributes to process if xmc */
+
+ bool _sig_winch;
+};
+
+
+#define DelCharCost(count) \
+ ((parm_dch != 0) \
+ ? D->_dch_cost \
+ : ((delete_character != 0) \
+ ? (D->_dch1_cost * count) \
+ : INFINITY))
+
+#define InsCharCost(count) \
+ ((parm_ich != 0) \
+ ? D->_ich_cost \
+ : ((enter_insert_mode && exit_insert_mode) \
+ ? D->_smir_cost + D->_rmir_cost + (D->_ip_cost * count) \
+ : ((insert_character != 0) \
+ ? (D->_ich1_cost * count) \
+ : INFINITY)))
+
+#if USE_XMC_SUPPORT
+#define UpdateAttrs(c) if (D->_current_attr != AttrOf(c)) { \
+ attr_t chg = D->_current_attr; \
+ vidattr(AttrOf(c)); \
+ if (magic_cookie_glitch > 0 \
+ && XMC_CHANGES((chg ^ D->_current_attr))) { \
+ T(("%s @%d before glitch %d,%d", \
+ __FILE__, __LINE__, \
+ D->_cursrow, \
+ D->_curscol)); \
+ _nc_do_xmc_glitch(chg); \
+ } \
+ }
+#else
+#define UpdateAttrs(c) if (D->_current_attr != AttrOf(c)) \
+ vidattr(AttrOf(c));
+#endif
+
+/*
+ * Check whether the given character can be output by clearing commands. This
+ * includes test for being a space and not including any 'bad' attributes, such
+ * as A_REVERSE. All attribute flags which don't affect appearance of a space
+ * or can be output by clearing (A_COLOR in case of bce-terminal) are excluded.
+ */
+#define can_clear_with(ch) \
+ ((ch & ~(NONBLANK_ATTR|(back_color_erase ? A_COLOR:0))) == BLANK)
+
+#define XMC_CHANGES(c) ((c) & D->_xmc_suppress)
+
+#endif /* TTY_DISPLAY_H */
diff --git a/lib/libcurses/tty/tty_input.h b/lib/libcurses/tty/tty_input.h
new file mode 100644
index 00000000000..c42774be14b
--- /dev/null
+++ b/lib/libcurses/tty/tty_input.h
@@ -0,0 +1,63 @@
+/* $OpenBSD: tty_input.h,v 1.1 1999/01/18 19:10:27 millert Exp $ */
+
+/****************************************************************************
+ * Copyright (c) 1998 Free Software Foundation, Inc. *
+ * *
+ * 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. *
+ ****************************************************************************/
+
+/*
+ * $From: tty_input.h,v 1.1 1998/12/19 22:42:57 tom Exp $
+ */
+
+#ifndef TTY_INPUT_H
+#define TTY_INPUT_H 1
+
+extern bool _nc_tty_mouse_mask(mmask_t);
+extern bool _nc_tty_pending(void);
+extern int _nc_tty_next_event(int);
+extern void _nc_tty_flags_changed(void);
+extern void _nc_tty_flush(void);
+extern void _nc_tty_input_resume(void);
+extern void _nc_tty_input_suspend(void);
+
+struct tty_input_data {
+ int _ifd; /* input file ptr for screen */
+ int _keypad_xmit; /* current terminal state */
+ int _meta_on; /* current terminal state */
+
+ /*
+ * These are the data that support the mouse interface.
+ */
+ bool (*_mouse_event) (SCREEN *);
+ bool (*_mouse_inline)(SCREEN *);
+ bool (*_mouse_parse) (int);
+ void (*_mouse_resume)(SCREEN *);
+ void (*_mouse_wrap) (SCREEN *);
+ int _mouse_fd; /* file-descriptor, if any */
+ int mousetype;
+};
+
+#endif /* TTY_INPUT_H */
diff --git a/lib/libcurses/tty/tty_update.c b/lib/libcurses/tty/tty_update.c
new file mode 100644
index 00000000000..9fd81963b5e
--- /dev/null
+++ b/lib/libcurses/tty/tty_update.c
@@ -0,0 +1,1757 @@
+/* $OpenBSD: tty_update.c,v 1.1 1999/01/18 19:10:27 millert Exp $ */
+
+/****************************************************************************
+ * Copyright (c) 1998 Free Software Foundation, Inc. *
+ * *
+ * 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> *
+ ****************************************************************************/
+
+
+/*-----------------------------------------------------------------
+ *
+ * lib_doupdate.c
+ *
+ * The routine doupdate() and its dependents. Also _nc_outstr(),
+ * so all physical output is concentrated here (except _nc_outch()
+ * in lib_tputs.c).
+ *
+ *-----------------------------------------------------------------*/
+
+#include <curses.priv.h>
+
+#if defined(TRACE) && HAVE_SYS_TIMES_H && HAVE_TIMES
+#define USE_TRACE_TIMES 1
+#else
+#define USE_TRACE_TIMES 0
+#endif
+
+#if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
+#include <sys/time.h>
+#endif
+
+#if USE_TRACE_TIMES
+#include <sys/times.h>
+#endif
+
+#if USE_FUNC_POLL
+#include <stropts.h>
+#include <poll.h>
+#elif HAVE_SELECT
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#endif
+
+#ifdef __BEOS__
+/* BeOS select() only works on sockets. Use the tty hack instead */
+#include <socket.h>
+#define select check_select
+#endif
+
+#include <term.h>
+
+MODULE_ID("$From: tty_update.c,v 1.109 1998/10/03 19:08:33 tom Exp $")
+
+/*
+ * This define controls the line-breakout optimization. Every once in a
+ * while during screen refresh, we want to check for input and abort the
+ * update if there's some waiting. CHECK_INTERVAL controls the number of
+ * changed lines to be emitted between input checks.
+ *
+ * Note: Input-check-and-abort is no longer done if the screen is being
+ * updated from scratch. This is a feature, not a bug.
+ */
+#define CHECK_INTERVAL 5
+
+/*
+ * Enable checking to see if doupdate and friends are tracking the true
+ * cursor position correctly. NOTE: this is a debugging hack which will
+ * work ONLY on ANSI-compatible terminals!
+ */
+/* #define POSITION_DEBUG */
+
+static inline chtype ClrBlank ( WINDOW *win );
+static int ClrBottom(int total);
+static int InsStr( chtype *line, int count );
+static void ClearScreen( chtype blank );
+static void ClrUpdate( WINDOW *win );
+static void DelChar( int count );
+static void TransformLine( int const lineno );
+
+#ifdef POSITION_DEBUG
+/****************************************************************************
+ *
+ * Debugging code. Only works on ANSI-standard terminals.
+ *
+ ****************************************************************************/
+
+void position_check(int expected_y, int expected_x, char *legend)
+/* check to see if the real cursor position matches the virtual */
+{
+ static char buf[9];
+ int y, x;
+
+ if (_nc_tracing)
+ return;
+
+ memset(buf, '\0', sizeof(buf));
+ (void) write(1, "\033[6n", 4); /* only works on ANSI-compatibles */
+ (void) read(0, (void *)buf, 8);
+ _tracef("probe returned %s", _nc_visbuf(buf));
+
+ /* try to interpret as a position report */
+ if (sscanf(buf, "\033[%d;%dR", &y, &x) != 2)
+ _tracef("position probe failed in %s", legend);
+ else if (y - 1 != expected_y || x - 1 != expected_x)
+ _tracef("position seen (%d, %d) doesn't match expected one (%d, %d) in %s",
+ y-1, x-1, expected_y, expected_x, legend);
+ else
+ _tracef("position matches OK in %s", legend);
+}
+#endif /* POSITION_DEBUG */
+
+/****************************************************************************
+ *
+ * Optimized update code
+ *
+ ****************************************************************************/
+
+static inline void GoTo(int const row, int const col)
+{
+ chtype oldattr = SP->_current_attr;
+
+ TR(TRACE_MOVE, ("GoTo(%d, %d) from (%d, %d)",
+ row, col, SP->_cursrow, SP->_curscol));
+
+#ifdef POSITION_DEBUG
+ position_check(SP->_cursrow, SP->_curscol, "GoTo");
+#endif /* POSITION_DEBUG */
+
+ /*
+ * Force restore even if msgr is on when we're in an alternate
+ * character set -- these have a strong tendency to screw up the
+ * CR & LF used for local character motions!
+ */
+ if ((oldattr & A_ALTCHARSET)
+ || (oldattr && !move_standout_mode))
+ {
+ TR(TRACE_CHARPUT, ("turning off (%#lx) %s before move",
+ oldattr, _traceattr(oldattr)));
+ vidattr(A_NORMAL);
+ }
+
+ mvcur(SP->_cursrow, SP->_curscol, row, col);
+ SP->_cursrow = row;
+ SP->_curscol = col;
+}
+
+static inline void PutAttrChar(chtype ch)
+{
+ if (tilde_glitch && (TextOf(ch) == '~'))
+ ch = ('`' | AttrOf(ch));
+
+ TR(TRACE_CHARPUT, ("PutAttrChar(%s) at (%d, %d)",
+ _tracechtype(ch),
+ SP->_cursrow, SP->_curscol));
+ UpdateAttrs(ch);
+ putc((int)TextOf(ch), SP->_ofp);
+#ifdef TRACE
+ _nc_outchars++;
+#endif /* TRACE */
+ SP->_curscol++;
+ if (char_padding) {
+ TPUTS_TRACE("char_padding");
+ putp(char_padding);
+ }
+}
+
+static bool check_pending(void)
+/* check for pending input */
+{
+ bool have_pending = FALSE;
+
+ /*
+ * Only carry out this check when the flag is zero, otherwise we'll
+ * have the refreshing slow down drastically (or stop) if there's an
+ * unread character available.
+ */
+ if(SP->_fifohold != 0)
+ return FALSE;
+
+ if (SP->_checkfd >= 0) {
+#if USE_FUNC_POLL
+ struct pollfd fds[1];
+ fds[0].fd = SP->_checkfd;
+ fds[0].events = POLLIN;
+ if (poll(fds, 1, 0) > 0)
+ {
+ have_pending = TRUE;
+ }
+#elif HAVE_SELECT
+ fd_set fdset;
+ struct timeval ktimeout;
+
+ ktimeout.tv_sec =
+ ktimeout.tv_usec = 0;
+
+ FD_ZERO(&fdset);
+ FD_SET(SP->_checkfd, &fdset);
+ if (select(SP->_checkfd+1, &fdset, NULL, NULL, &ktimeout) != 0)
+ {
+ have_pending = TRUE;
+ }
+#endif
+ }
+ if (have_pending) {
+ SP->_fifohold = 5;
+ fflush(SP->_ofp);
+ }
+ return FALSE;
+}
+
+/*
+ * No one supports recursive inline functions. However, gcc is quieter if we
+ * instantiate the recursive part separately.
+ */
+#if CC_HAS_INLINE_FUNCS
+static void callPutChar(chtype const);
+#else
+#define callPutChar(ch) PutChar(ch)
+#endif
+
+static inline void PutChar(chtype const ch); /* forward declaration */
+
+/* put char at lower right corner */
+static void PutCharLR(chtype const ch)
+{
+ if (!auto_right_margin)
+ {
+ /* we can put the char directly */
+ PutAttrChar(ch);
+ }
+ else if (enter_am_mode && exit_am_mode)
+ {
+ /* we can suppress automargin */
+ TPUTS_TRACE("exit_am_mode");
+ putp(exit_am_mode);
+
+ PutAttrChar(ch);
+
+ TPUTS_TRACE("enter_am_mode");
+ putp(enter_am_mode);
+ }
+ else if ((enter_insert_mode && exit_insert_mode)
+ || insert_character || parm_ich)
+ {
+ GoTo(screen_lines-1,screen_columns-2);
+ callPutChar(ch);
+ GoTo(screen_lines-1,screen_columns-2);
+ InsStr(newscr->_line[screen_lines-1].text+screen_columns-2,1);
+ }
+}
+
+static void wrap_cursor(void)
+{
+ if (eat_newline_glitch)
+ {
+ /*
+ * xenl can manifest two different ways. The vt100
+ * way is that, when you'd expect the cursor to wrap,
+ * it stays hung at the right margin (on top of the
+ * character just emitted) and doesn't wrap until the
+ * *next* graphic char is emitted. The c100 way is
+ * to ignore LF received just after an am wrap.
+ *
+ * An aggressive way to handle this would be to
+ * emit CR/LF after the char and then assume the wrap
+ * is done, you're on the first position of the next
+ * line, and the terminal out of its weird state.
+ * Here it's safe to just tell the code that the
+ * cursor is in hyperspace and let the next mvcur()
+ * call straighten things out.
+ */
+ SP->_curscol = -1;
+ SP->_cursrow = -1;
+ }
+ else if (auto_right_margin)
+ {
+ SP->_curscol = 0;
+ SP->_cursrow++;
+ }
+ else
+ {
+ SP->_curscol--;
+ }
+}
+
+static inline void PutChar(chtype const ch)
+/* insert character, handling automargin stuff */
+{
+ if (SP->_cursrow == screen_lines-1 && SP->_curscol == screen_columns-1)
+ PutCharLR(ch);
+ else
+ PutAttrChar(ch);
+
+ if (SP->_curscol >= screen_columns)
+ wrap_cursor();
+
+#ifdef POSITION_DEBUG
+ position_check(SP->_cursrow, SP->_curscol, "PutChar");
+#endif /* POSITION_DEBUG */
+}
+
+/*
+ * Issue a given span of characters from an array.
+ * Must be functionally equivalent to:
+ * for (i = 0; i < num; i++)
+ * PutChar(ntext[i]);
+ * but can leave the cursor positioned at the middle of the interval.
+ *
+ * Returns: 0 - cursor is at the end of interval
+ * 1 - cursor is somewhere in the middle
+ *
+ * This code is optimized using ech and rep.
+ */
+static int EmitRange(const chtype *ntext, int num)
+{
+ int i;
+
+ if (erase_chars || repeat_char)
+ {
+ while (num > 0)
+ {
+ int runcount;
+ chtype ntext0;
+
+ while (num>1 && ntext[0]!=ntext[1])
+ {
+ PutChar(ntext[0]);
+ ntext++;
+ num--;
+ }
+ ntext0 = ntext[0];
+ if (num==1)
+ {
+ PutChar(ntext0);
+ return 0;
+ }
+ runcount = 2;
+
+ while (runcount < num && ntext[runcount] == ntext0)
+ runcount++;
+
+ /*
+ * The cost expression in the middle isn't exactly right.
+ * _cup_cost is an upper bound on the cost for moving to the
+ * end of the erased area, but not the cost itself (which we
+ * can't compute without emitting the move). This may result
+ * in erase_chars not getting used in some situations for
+ * which it would be marginally advantageous.
+ */
+ if (erase_chars
+ && runcount > SP->_ech_cost + SP->_cup_cost
+ && can_clear_with(ntext0))
+ {
+ UpdateAttrs(ntext0);
+ putp(tparm(erase_chars, runcount));
+
+ /*
+ * If this is the last part of the given interval,
+ * don't bother moving cursor, since it can be the
+ * last update on the line.
+ */
+ if (runcount < num)
+ GoTo(SP->_cursrow, SP->_curscol + runcount);
+ else
+ return 1; /* cursor stays in the middle */
+ }
+ else if (repeat_char && runcount > SP->_rep_cost)
+ {
+ bool wrap_possible = (SP->_curscol + runcount >= screen_columns);
+ int rep_count = runcount;
+
+ if (wrap_possible)
+ rep_count--;
+
+ UpdateAttrs(ntext0);
+ putp(tparm(repeat_char, TextOf(ntext0), rep_count));
+ SP->_curscol += rep_count;
+
+ if (wrap_possible)
+ PutChar(ntext0);
+ }
+ else
+ {
+ for (i = 0; i < runcount; i++)
+ PutChar(ntext[i]);
+ }
+ ntext += runcount;
+ num -= runcount;
+ }
+ return 0;
+ }
+
+ for (i = 0; i < num; i++)
+ PutChar(ntext[i]);
+ return 0;
+}
+
+/*
+ * Output the line in the given range [first .. last]
+ *
+ * If there's a run of identical characters that's long enough to justify
+ * cursor movement, use that also.
+ *
+ * Returns: same as EmitRange
+ */
+static int PutRange(
+ const chtype *otext,
+ const chtype *ntext,
+ int row,
+ int first, int last)
+{
+ int j, run;
+ int cost = min(SP->_cup_ch_cost, SP->_hpa_ch_cost);
+
+ TR(TRACE_CHARPUT, ("PutRange(%p, %p, %d, %d, %d)",
+ otext, ntext, row, first, last));
+
+ if (otext != ntext
+ && (last-first+1) > cost) {
+ for (j = first, run = 0; j <= last; j++) {
+ if (otext[j] == ntext[j]) {
+ run++;
+ } else {
+ if (run > cost) {
+ int before_run = (j - run);
+ EmitRange(ntext+first, before_run-first);
+ GoTo(row, first = j);
+ }
+ run = 0;
+ }
+ }
+ }
+ return EmitRange(ntext + first, last-first+1);
+}
+
+#if CC_HAS_INLINE_FUNCS
+static void callPutChar(chtype const ch)
+{
+ PutChar(ch);
+}
+#endif
+
+#define MARK_NOCHANGE(win,row) \
+ { \
+ win->_line[row].firstchar = _NOCHANGE; \
+ win->_line[row].lastchar = _NOCHANGE; \
+ if_USE_SCROLL_HINTS(win->_line[row].oldindex = row); \
+ }
+
+int doupdate(void)
+{
+int i;
+int nonempty;
+#if USE_TRACE_TIMES
+struct tms before, after;
+#endif /* USE_TRACE_TIMES */
+
+ T((T_CALLED("doupdate()")));
+
+#ifdef TRACE
+ if (_nc_tracing & TRACE_UPDATE)
+ {
+ if (curscr->_clear)
+ _tracef("curscr is clear");
+ else
+ _tracedump("curscr", curscr);
+ _tracedump("newscr", newscr);
+ }
+#endif /* TRACE */
+
+ _nc_signal_handler(FALSE);
+
+ if (SP->_fifohold)
+ SP->_fifohold--;
+
+#if USE_SIZECHANGE
+ if (SP->_endwin || SP->_sig_winch)
+ {
+ /*
+ * This is a transparent extension: XSI does not address it,
+ * and applications need not know that ncurses can do it.
+ *
+ * Check if the terminal size has changed while curses was off
+ * (this can happen in an xterm, for example), and resize the
+ * ncurses data structures accordingly.
+ */
+ _nc_update_screensize();
+ }
+#endif
+
+ if (SP->_endwin) {
+
+ T(("coming back from shell mode"));
+ reset_prog_mode();
+
+ _nc_mvcur_resume();
+ _nc_screen_resume();
+ SP->_mouse_resume(SP);
+
+ SP->_endwin = FALSE;
+ }
+
+#if USE_TRACE_TIMES
+ /* zero the metering machinery */
+ _nc_outchars = 0;
+ (void) times(&before);
+#endif /* USE_TRACE_TIMES */
+
+ /*
+ * This is the support for magic-cookie terminals. The
+ * theory: we scan the virtual screen looking for attribute
+ * turnons. Where we find one, check to make sure it's
+ * realizable by seeing if the required number of
+ * un-attributed blanks are present before and after the
+ * attributed range; try to shift the range boundaries over
+ * blanks (not changing the screen display) so this becomes
+ * true. If it is, shift the beginning attribute change
+ * appropriately (the end one, if we've gotten this far, is
+ * guaranteed room for its cookie). If not, nuke the added
+ * attributes out of the span.
+ */
+#if USE_XMC_SUPPORT
+ if (magic_cookie_glitch > 0) {
+ int j, k;
+ attr_t rattr = A_NORMAL;
+
+ for (i = 0; i < screen_lines; i++)
+ for (j = 0; j < screen_columns; j++)
+ {
+ bool failed = FALSE;
+ chtype turnon = AttrOf(newscr->_line[i].text[j]) & ~rattr;
+
+ /* is an attribute turned on here? */
+ if (turnon == 0) {
+ rattr = AttrOf(newscr->_line[i].text[j]);
+ continue;
+ }
+
+ T(("At (%d, %d): from %s...", i, j, _traceattr(rattr)));
+ T(("...to %s",_traceattr(turnon)));
+
+ /*
+ * If the attribute change location is a blank with a
+ * "safe" attribute, undo the attribute turnon. This may
+ * ensure there's enough room to set the attribute before
+ * the first non-blank in the run.
+ */
+#define SAFE(a) !((a) & (chtype)~NONBLANK_ATTR)
+ if (TextOf(newscr->_line[i].text[j])==' ' && SAFE(turnon))
+ {
+ newscr->_line[i].text[j] &= ~turnon;
+ continue;
+ }
+
+ /* check that there's enough room at start of span */
+ for (k = 1; k <= magic_cookie_glitch; k++)
+ if (j-k < 0
+ || TextOf(newscr->_line[i].text[j-k]) != ' '
+ || !SAFE(AttrOf(newscr->_line[i].text[j-k])))
+ failed = TRUE;
+ if (!failed)
+ {
+ bool end_onscreen = FALSE;
+ int m, n = j;
+
+ /* find end of span, if it's onscreen */
+ for (m = i; m < screen_lines; m++)
+ {
+ for ( ; n < screen_columns; n++)
+ {
+ if (AttrOf(newscr->_line[m].text[n]) == rattr)
+ {
+ end_onscreen = TRUE;
+ T(("Range attributed with %s ends at (%d, %d)",
+ _traceattr(turnon),m,n));
+ goto foundit;
+ }
+ }
+ n = 0;
+ }
+ T(("Range attributed with %s ends offscreen",
+ _traceattr(turnon)));
+ foundit:;
+
+ if (end_onscreen)
+ {
+ chtype *lastline = newscr->_line[m].text;
+
+ /*
+ * If there are safely-attributed blanks at the
+ * end of the range, shorten the range. This will
+ * help ensure that there is enough room at end
+ * of span.
+ */
+ while (n >= 0
+ && TextOf(lastline[n]) == ' '
+ && SAFE(AttrOf(lastline[n])))
+ lastline[n--] &= ~turnon;
+
+ /* check that there's enough room at end of span */
+ for (k = 1; k <= magic_cookie_glitch; k++)
+ if (n + k >= screen_columns
+ || TextOf(lastline[n + k]) != ' '
+ || !SAFE(AttrOf(lastline[n+k])))
+ failed = TRUE;
+ }
+ }
+
+ if (failed)
+ {
+ int p, q = j;
+
+ T(("Clearing %s beginning at (%d, %d)",
+ _traceattr(turnon), i, j));
+
+ /* turn off new attributes over span */
+ for (p = i; p < screen_lines; p++)
+ {
+ for ( ; q < screen_columns; q++)
+ {
+ if (AttrOf(newscr->_line[p].text[q]) == rattr)
+ goto foundend;
+ newscr->_line[p].text[q] &= ~turnon;
+ }
+ q = 0;
+ }
+ foundend:;
+ }
+ else
+ {
+ T(("Cookie space for %s found before (%d, %d)",
+ _traceattr(turnon), i, j));
+
+ /*
+ * back up the start of range so there's room
+ * for cookies before the first nonblank character
+ */
+ for (k = 1; k <= magic_cookie_glitch; k++)
+ newscr->_line[i].text[j-k] |= turnon;
+ }
+
+ rattr = AttrOf(newscr->_line[i].text[j]);
+ }
+
+#ifdef TRACE
+ /* show altered highlights after magic-cookie check */
+ if (_nc_tracing & TRACE_UPDATE)
+ {
+ _tracef("After magic-cookie check...");
+ _tracedump("newscr", newscr);
+ }
+#endif /* TRACE */
+ }
+#endif /* USE_XMC_SUPPORT */
+
+ nonempty = 0;
+ if (curscr->_clear) { /* force refresh ? */
+ /* yes, clear all & update */
+ T(("clearing and updating curscr"));
+ if (is_wintouched(newscr))
+ ClrUpdate(newscr);
+ else
+ ClrUpdate(curscr);
+ curscr->_clear = FALSE; /* reset flag */
+ newscr->_clear = FALSE; /* reset flag */
+ } else if (newscr->_clear) {
+ T(("clearing and updating newscr"));
+ ClrUpdate(newscr);
+ newscr->_clear = FALSE;
+ } else {
+ int changedlines = CHECK_INTERVAL;
+
+ if(check_pending())
+ goto cleanup;
+
+ nonempty = min(screen_lines, newscr->_maxy+1);
+
+ if (SP->_scrolling) {
+ _nc_scroll_optimize();
+ }
+
+ nonempty = ClrBottom(nonempty);
+
+ T(("Transforming lines, nonempty %d", nonempty));
+ for (i = 0; i < nonempty; i++) {
+ /*
+ * Here is our line-breakout optimization.
+ */
+ if (changedlines == CHECK_INTERVAL)
+ {
+ if (check_pending())
+ goto cleanup;
+ changedlines = 0;
+ }
+
+ /*
+ * newscr->line[i].firstchar is normally set
+ * by wnoutrefresh. curscr->line[i].firstchar
+ * is normally set by _nc_scroll_window in the
+ * vertical-movement optimization code,
+ */
+ if (newscr->_line[i].firstchar != _NOCHANGE
+ || curscr->_line[i].firstchar != _NOCHANGE)
+ {
+ TransformLine(i);
+ changedlines++;
+ }
+
+ /* mark line changed successfully */
+ if (i <= newscr->_maxy)
+ MARK_NOCHANGE(newscr,i)
+ if (i <= curscr->_maxy)
+ MARK_NOCHANGE(curscr,i)
+ }
+ }
+
+ /* put everything back in sync */
+ for (i = nonempty; i <= newscr->_maxy; i++)
+ MARK_NOCHANGE(newscr,i)
+ for (i = nonempty; i <= curscr->_maxy; i++)
+ MARK_NOCHANGE(curscr,i)
+
+ if (!newscr->_leaveok)
+ {
+ curscr->_curx = newscr->_curx;
+ curscr->_cury = newscr->_cury;
+
+ GoTo(curscr->_cury, curscr->_curx);
+ }
+
+ cleanup:
+ /*
+ * Keep the physical screen in normal mode in case we get other
+ * processes writing to the screen.
+ */
+ UpdateAttrs(A_NORMAL);
+
+ fflush(SP->_ofp);
+ curscr->_attrs = newscr->_attrs;
+/* curscr->_bkgd = newscr->_bkgd; */
+
+#if USE_TRACE_TIMES
+ (void) times(&after);
+ TR(TRACE_TIMES, ("Update cost: %ld chars, %ld clocks system time, %ld clocks user time",
+ _nc_outchars,
+ after.tms_stime-before.tms_stime,
+ after.tms_utime-before.tms_utime));
+#endif /* USE_TRACE_TIMES */
+
+ _nc_signal_handler(TRUE);
+
+ returnCode(OK);
+}
+
+/*
+ * ClrBlank(win)
+ *
+ * Returns the attributed character that corresponds to the "cleared"
+ * screen. If the terminal has the back-color-erase feature, this will be
+ * colored according to the wbkgd() call.
+ *
+ * We treat 'curscr' specially because it isn't supposed to be set directly
+ * in the wbkgd() call. Assume 'stdscr' for this case.
+ */
+#define BCE_ATTRS (A_NORMAL|A_COLOR)
+#define BCE_BKGD(win) (((win) == curscr ? stdscr : (win))->_bkgd)
+
+static inline chtype ClrBlank (WINDOW *win)
+{
+chtype blank = BLANK;
+ if (back_color_erase)
+ blank |= (BCE_BKGD(win) & BCE_ATTRS);
+ return blank;
+}
+
+/*
+** ClrUpdate(win)
+**
+** Update by clearing and redrawing the entire screen.
+**
+*/
+
+static void ClrUpdate(WINDOW *win)
+{
+ int i;
+ chtype blank = ClrBlank(win);
+ int nonempty = min(screen_lines, newscr->_maxy+1);
+
+ T(("ClrUpdate() called"));
+
+ if (win == curscr) {
+ /* discard updates */
+ for (i = 0; i < screen_lines ; i++) {
+ memcpy( newscr->_line[i].text,
+ curscr->_line[i].text,
+ screen_columns * sizeof(chtype));
+ }
+ }
+
+ ClearScreen(blank);
+
+ T(("updating screen from scratch"));
+
+ nonempty = ClrBottom(nonempty);
+
+ for (i = 0; i < nonempty; i++)
+ TransformLine(i);
+}
+
+/*
+** ClrToEOL(blank)
+**
+** Clear to end of current line, starting at the cursor position
+*/
+
+static void ClrToEOL(chtype blank)
+{
+int j;
+bool needclear = FALSE;
+
+ for (j = SP->_curscol; j < screen_columns; j++)
+ {
+ chtype *cp = &(curscr->_line[SP->_cursrow].text[j]);
+
+ if (*cp != blank)
+ {
+ *cp = blank;
+ needclear = TRUE;
+ }
+ }
+
+ if (needclear)
+ {
+ UpdateAttrs(blank);
+ TPUTS_TRACE("clr_eol");
+ if (SP->_el_cost > (screen_columns - SP->_curscol))
+ {
+ int count = (screen_columns - SP->_curscol);
+ while (count-- > 0)
+ PutChar(blank);
+ }
+ else
+ putp(clr_eol);
+ }
+}
+
+/*
+** ClrToEOS(blank)
+**
+** Clear to end of screen, starting at the cursor position
+*/
+
+static void ClrToEOS(chtype blank)
+{
+int row, col;
+
+ UpdateAttrs(blank);
+ TPUTS_TRACE("clr_eos");
+ row = SP->_cursrow;
+ tputs(clr_eos, screen_lines-row, _nc_outch);
+
+ for (col = SP->_curscol; col < screen_columns; col++)
+ curscr->_line[row].text[col] = blank;
+
+ for (row++; row < screen_lines; row++)
+ {
+ for (col = 0; col < screen_columns; col++)
+ curscr->_line[row].text[col] = blank;
+ }
+}
+
+/*
+ * ClrBottom(total)
+ *
+ * Test if clearing the end of the screen would satisfy part of the
+ * screen-update. Do this by scanning backwards through the lines in the
+ * screen, checking if each is blank, and one or more are changed.
+ */
+static int ClrBottom(int total)
+{
+static chtype *tstLine;
+static size_t lenLine;
+
+int row, col;
+int top = total;
+int last = min(screen_columns, newscr->_maxx+1);
+size_t length = sizeof(chtype) * last;
+chtype blank = newscr->_line[total-1].text[last-1]; /* lower right char */
+
+ if(!clr_eos || !can_clear_with(blank))
+ return total;
+
+ if (tstLine == 0 || length > lenLine) {
+ tstLine = (chtype *)_nc_doalloc(tstLine, length);
+ }
+
+ if (tstLine != 0) {
+ lenLine = length;
+ for (col = 0; col < last; col++)
+ tstLine[col] = blank;
+
+ for (row = total-1; row >= 0; row--) {
+ if (memcmp(tstLine, newscr->_line[row].text, length))
+ break;
+ if (memcmp(tstLine, curscr->_line[row].text, length))
+ top = row;
+ }
+
+ /* don't use clr_eos for just one line if clr_eol available */
+ if (top < total-1 || (top < total && !clr_eol && !clr_bol)) {
+ GoTo(top,0);
+ ClrToEOS(blank);
+ total = top;
+ if (SP->oldhash && SP->newhash)
+ {
+ for (row = top; row < screen_lines; row++)
+ SP->oldhash[row] = SP->newhash[row];
+ }
+ }
+ }
+#if NO_LEAKS
+ if (tstLine != 0)
+ FreeAndNull(tstLine);
+#endif
+ return total;
+}
+
+
+/*
+** TransformLine(lineno)
+**
+** Transform the given line in curscr to the one in newscr, using
+** Insert/Delete Character if _nc_idcok && has_ic().
+**
+** firstChar = position of first different character in line
+** oLastChar = position of last different character in old line
+** nLastChar = position of last different character in new line
+**
+** move to firstChar
+** overwrite chars up to min(oLastChar, nLastChar)
+** if oLastChar < nLastChar
+** insert newLine[oLastChar+1..nLastChar]
+** else
+** delete oLastChar - nLastChar spaces
+*/
+
+static void TransformLine(int const lineno)
+{
+int firstChar, oLastChar, nLastChar;
+chtype *newLine = newscr->_line[lineno].text;
+chtype *oldLine = curscr->_line[lineno].text;
+int n;
+bool attrchanged = FALSE;
+
+ T(("TransformLine(%d) called", lineno));
+
+ /* copy new hash value to old one */
+ if (SP->oldhash && SP->newhash)
+ SP->oldhash[lineno] = SP->newhash[lineno];
+
+ if(ceol_standout_glitch && clr_eol) {
+ firstChar = 0;
+ while(firstChar < screen_columns) {
+ if(AttrOf(newLine[firstChar]) != AttrOf(oldLine[firstChar]))
+ attrchanged = TRUE;
+ firstChar++;
+ }
+ }
+
+ firstChar = 0;
+
+ if (attrchanged) { /* we may have to disregard the whole line */
+ GoTo(lineno, firstChar);
+ ClrToEOL(ClrBlank(curscr));
+ PutRange(oldLine, newLine, lineno, 0, (screen_columns-1));
+#if USE_XMC_SUPPORT
+
+#define NEW(r,c) newscr->_line[r].text[c]
+#define xmc_turn_on(a,b) ((((a)^(b)) & ~(a) & SP->_xmc_triggers) != 0)
+#define xmc_turn_off(a,b) xmc_turn_on(b,a)
+
+ /*
+ * This is a very simple loop to paint characters which may have the
+ * magic cookie glitch embedded. It doesn't know much about video
+ * attributes which are continued from one line to the next. It
+ * assumes that we have filtered out requests for attribute changes
+ * that do not get mapped to blank positions.
+ *
+ * FIXME: we are not keeping track of where we put the cookies, so this
+ * will work properly only once, since we may overwrite a cookie in a
+ * following operation.
+ */
+ } else if (magic_cookie_glitch > 0) {
+ GoTo(lineno, firstChar);
+ for (n = 0; n < screen_columns; n++) {
+ int m = n + magic_cookie_glitch;
+
+ /* check for turn-on:
+ * If we are writing an attributed blank, where the
+ * previous cell is not attributed.
+ */
+ if (TextOf(newLine[n]) == ' '
+ && ((n > 0
+ && xmc_turn_on(newLine[n-1], newLine[n]))
+ || (n == 0
+ && lineno > 0
+ && xmc_turn_on(NEW(lineno-1,screen_columns-1), newLine[n])))) {
+ n = m;
+ }
+
+ PutChar(newLine[n]);
+
+ /* check for turn-off:
+ * If we are writing an attributed non-blank, where the
+ * next cell is blank, and not attributed.
+ */
+ if (TextOf(newLine[n]) != ' '
+ && ((n+1 < screen_columns
+ && xmc_turn_off(newLine[n], newLine[n+1]))
+ || (n+1 >= screen_columns
+ && lineno+1 < screen_lines
+ && xmc_turn_off(newLine[n], NEW(lineno+1,0))))) {
+ n = m;
+ }
+
+ }
+#undef NEW
+#endif
+ } else {
+ chtype blank;
+
+ /* find the first differing character */
+ while (firstChar < screen_columns &&
+ newLine[firstChar] == oldLine[firstChar])
+ firstChar++;
+
+ /* if there wasn't one, we're done */
+ if (firstChar >= screen_columns)
+ return;
+
+ /* it may be cheap to clear leading whitespace with clr_bol */
+ if (clr_bol && can_clear_with(blank=newLine[0]))
+ {
+ int oFirstChar, nFirstChar;
+
+ for (oFirstChar = 0; oFirstChar < screen_columns; oFirstChar++)
+ if (oldLine[oFirstChar] != blank)
+ break;
+ for (nFirstChar = 0; nFirstChar < screen_columns; nFirstChar++)
+ if (newLine[nFirstChar] != blank)
+ break;
+
+ if (nFirstChar > oFirstChar + SP->_el1_cost)
+ {
+ if (nFirstChar >= screen_columns && SP->_el_cost <= SP->_el1_cost)
+ {
+ GoTo(lineno, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("clr_eol");
+ putp(clr_eol);
+ }
+ else
+ {
+ GoTo(lineno, nFirstChar - 1);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("clr_bol");
+ putp(clr_bol);
+ }
+
+ while (firstChar < nFirstChar)
+ oldLine[firstChar++] = blank;
+
+ if (firstChar >= screen_columns)
+ return;
+ }
+ }
+
+ blank = newLine[screen_columns-1];
+
+ if(!can_clear_with(blank))
+ {
+ /* find the last differing character */
+ nLastChar = screen_columns - 1;
+
+ while (nLastChar > firstChar
+ && newLine[nLastChar] == oldLine[nLastChar])
+ nLastChar--;
+
+ if (nLastChar >= firstChar) {
+ GoTo(lineno, firstChar);
+ PutRange(oldLine, newLine, lineno, firstChar, nLastChar);
+ memcpy( oldLine + firstChar,
+ newLine + firstChar,
+ (nLastChar - firstChar + 1) * sizeof(chtype));
+ }
+ return;
+ }
+
+ /* find last non-blank character on old line */
+ oLastChar = screen_columns - 1;
+ while (oLastChar > firstChar && oldLine[oLastChar] == blank)
+ oLastChar--;
+
+ /* find last non-blank character on new line */
+ nLastChar = screen_columns - 1;
+ while (nLastChar > firstChar && newLine[nLastChar] == blank)
+ nLastChar--;
+
+ if((nLastChar == firstChar)
+ && (SP->_el_cost < (oLastChar - nLastChar))) {
+ GoTo(lineno, firstChar);
+ if(newLine[firstChar] != blank )
+ PutChar(newLine[firstChar]);
+ ClrToEOL(blank);
+ } else if( (nLastChar != oLastChar)
+ && (newLine[nLastChar] != oldLine[oLastChar]
+ || !(_nc_idcok && has_ic())) ) {
+ GoTo(lineno, firstChar);
+ if ((oLastChar - nLastChar) > SP->_el_cost) {
+ if(PutRange(oldLine, newLine, lineno, firstChar, nLastChar))
+ GoTo(lineno, nLastChar+1);
+ ClrToEOL(blank);
+ } else {
+ n = max( nLastChar , oLastChar );
+ PutRange(oldLine, newLine, lineno, firstChar, n);
+ }
+ } else {
+ int nLastNonblank = nLastChar;
+ int oLastNonblank = oLastChar;
+
+ /* find the last characters that really differ */
+ while (newLine[nLastChar] == oldLine[oLastChar]) {
+ if (nLastChar != 0
+ && oLastChar != 0) {
+ nLastChar--;
+ oLastChar--;
+ } else {
+ break;
+ }
+ }
+
+ n = min(oLastChar, nLastChar);
+ if (n >= firstChar) {
+ GoTo(lineno, firstChar);
+ PutRange(oldLine, newLine, lineno, firstChar, n);
+ }
+
+ if (oLastChar < nLastChar) {
+ int m = max(nLastNonblank, oLastNonblank);
+ GoTo(lineno, n+1);
+ if (InsCharCost(nLastChar - oLastChar)
+ > (m - n)) {
+ PutRange(oldLine, newLine, lineno, n+1, m);
+ } else {
+ InsStr(&newLine[n+1], nLastChar - oLastChar);
+ }
+ } else if (oLastChar > nLastChar ) {
+ GoTo(lineno, n+1);
+ if (DelCharCost(oLastChar - nLastChar)
+ > SP->_el_cost + nLastNonblank - (n+1)) {
+ if(PutRange(oldLine, newLine, lineno,
+ n+1, nLastNonblank))
+ GoTo(lineno, nLastNonblank+1);
+ ClrToEOL(blank);
+ } else {
+ /*
+ * The delete-char sequence will
+ * effectively shift in blanks from the
+ * right margin of the screen. Ensure
+ * that they are the right color by
+ * setting the video attributes from
+ * the last character on the row.
+ */
+ UpdateAttrs(blank);
+ DelChar(oLastChar - nLastChar);
+ }
+ }
+ }
+ }
+
+ /* update the code's internal representation */
+ if (screen_columns > firstChar)
+ memcpy( oldLine + firstChar,
+ newLine + firstChar,
+ (screen_columns - firstChar) * sizeof(chtype));
+}
+
+/*
+** ClearScreen(blank)
+**
+** Clear the physical screen and put cursor at home
+**
+*/
+
+static void ClearScreen(chtype blank)
+{
+ int i, j;
+
+ T(("ClearScreen() called"));
+
+ if (clear_screen) {
+ UpdateAttrs(blank);
+ TPUTS_TRACE("clear_screen");
+ putp(clear_screen);
+ SP->_cursrow = SP->_curscol = 0;
+#ifdef POSITION_DEBUG
+ position_check(SP->_cursrow, SP->_curscol, "ClearScreen");
+#endif /* POSITION_DEBUG */
+ } else if (clr_eos) {
+ SP->_cursrow = SP->_curscol = -1;
+ GoTo(0,0);
+
+ UpdateAttrs(blank);
+ TPUTS_TRACE("clr_eos");
+ putp(clr_eos);
+ } else if (clr_eol) {
+ SP->_cursrow = SP->_curscol = -1;
+
+ for (i = 0; i < screen_lines; i++) {
+ GoTo(i, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("clr_eol");
+ putp(clr_eol);
+ }
+ GoTo(0,0);
+ } else {
+ T(("cannot clear screen"));
+ return;
+ }
+
+ for (i = 0; i < screen_lines; i++) {
+ for (j = 0; j < screen_columns; j++)
+ curscr->_line[i].text[j] = blank;
+ }
+
+ T(("screen cleared"));
+}
+
+/*
+** InsStr(line, count)
+**
+** Insert the count characters pointed to by line.
+**
+*/
+
+static int InsStr(chtype *line, int count)
+{
+ T(("InsStr(%p,%d) called", line, count));
+
+ /* Prefer parm_ich as it has the smallest cost - no need to shift
+ * the whole line on each character. */
+ /* The order must match that of InsCharCost. */
+ if (parm_ich) {
+ TPUTS_TRACE("parm_ich");
+ tputs(tparm(parm_ich, count), count, _nc_outch);
+ while (count) {
+ PutAttrChar(*line);
+ line++;
+ count--;
+ }
+ return(OK);
+ } else if (enter_insert_mode && exit_insert_mode) {
+ TPUTS_TRACE("enter_insert_mode");
+ putp(enter_insert_mode);
+ while (count) {
+ PutAttrChar(*line);
+ if (insert_padding)
+ {
+ TPUTS_TRACE("insert_padding");
+ putp(insert_padding);
+ }
+ line++;
+ count--;
+ }
+ TPUTS_TRACE("exit_insert_mode");
+ putp(exit_insert_mode);
+ return(OK);
+ } else {
+ while (count) {
+ TPUTS_TRACE("insert_character");
+ putp(insert_character);
+ PutAttrChar(*line);
+ if (insert_padding)
+ {
+ TPUTS_TRACE("insert_padding");
+ putp(insert_padding);
+ }
+ line++;
+ count--;
+ }
+ return(OK);
+ }
+}
+
+/*
+** DelChar(count)
+**
+** Delete count characters at current position
+**
+*/
+
+static void DelChar(int count)
+{
+ T(("DelChar(%d) called, position = (%d,%d)", count, newscr->_cury, newscr->_curx));
+
+ if (parm_dch) {
+ TPUTS_TRACE("parm_dch");
+ tputs(tparm(parm_dch, count), count, _nc_outch);
+ } else {
+ while (count--)
+ {
+ TPUTS_TRACE("delete_character");
+ putp(delete_character);
+ }
+ }
+}
+
+/*
+** _nc_outstr(char *str)
+**
+** Emit a string without waiting for update.
+*/
+
+void _nc_outstr(const char *str)
+{
+ FILE *ofp = SP ? SP->_ofp : stdout;
+
+ (void) fputs(str, ofp);
+ (void) fflush(ofp);
+
+#ifdef TRACE
+ _nc_outchars += strlen(str);
+#endif /* TRACE */
+}
+
+/*
+ * Physical-scrolling support
+ *
+ * This code was adapted from Keith Bostic's hardware scrolling
+ * support for 4.4BSD curses. I (esr) translated it to use terminfo
+ * capabilities, narrowed the call interface slightly, and cleaned
+ * up some convoluted tests. I also added support for the memory_above
+ * memory_below, and non_dest_scroll_region capabilities.
+ *
+ * For this code to work, we must have either
+ * change_scroll_region and scroll forward/reverse commands, or
+ * insert and delete line capabilities.
+ * When the scrolling region has been set, the cursor has to
+ * be at the last line of the region to make the scroll up
+ * happen, or on the first line of region to scroll down.
+ *
+ * This code makes one aesthetic decision in the opposite way from
+ * BSD curses. BSD curses preferred pairs of il/dl operations
+ * over scrolls, allegedly because il/dl looked faster. We, on
+ * the other hand, prefer scrolls because (a) they're just as fast
+ * on many terminals and (b) using them avoids bouncing an
+ * unchanged bottom section of the screen up and down, which is
+ * visually nasty.
+ *
+ * (lav): added more cases, used dl/il when bot==maxy and in csr case.
+ *
+ * I used assumption that capabilities il/il1/dl/dl1 work inside
+ * changed scroll region not shifting screen contents outside of it.
+ * If there are any terminals behaving different way, it would be
+ * necessary to add some conditions to scroll_csr_forward/backward.
+ */
+
+/* Try to scroll up assuming given csr (miny, maxy). Returns ERR on failure */
+static int scroll_csr_forward(int n, int top, int bot, int miny, int maxy, chtype blank)
+{
+ int i;
+
+ if (n == 1 && scroll_forward && top == miny && bot == maxy)
+ {
+ GoTo(bot, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("scroll_forward");
+ tputs(scroll_forward, 0, _nc_outch);
+ }
+ else if (n == 1 && delete_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("delete_line");
+ tputs(delete_line, 0, _nc_outch);
+ }
+ else if (parm_index && top == miny && bot == maxy)
+ {
+ GoTo(bot, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("parm_index");
+ tputs(tparm(parm_index, n, 0), n, _nc_outch);
+ }
+ else if (parm_delete_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("parm_delete_line");
+ tputs(tparm(parm_delete_line, n, 0), n, _nc_outch);
+ }
+ else if (scroll_forward && top == miny && bot == maxy)
+ {
+ GoTo(bot, 0);
+ UpdateAttrs(blank);
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("scroll_forward");
+ tputs(scroll_forward, 0, _nc_outch);
+ }
+ }
+ else if (delete_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("delete_line");
+ tputs(delete_line, 0, _nc_outch);
+ }
+ }
+ else
+ return ERR;
+
+ return OK;
+}
+
+/* Try to scroll down assuming given csr (miny, maxy). Returns ERR on failure */
+/* n > 0 */
+static int scroll_csr_backward(int n, int top, int bot, int miny, int maxy, chtype blank)
+{
+ int i;
+
+ if (n == 1 && scroll_reverse && top == miny && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("scroll_reverse");
+ tputs(scroll_reverse, 0, _nc_outch);
+ }
+ else if (n == 1 && insert_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("insert_line");
+ tputs(insert_line, 0, _nc_outch);
+ }
+ else if (parm_rindex && top == miny && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("parm_rindex");
+ tputs(tparm(parm_rindex, n, 0), n, _nc_outch);
+ }
+ else if (parm_insert_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ TPUTS_TRACE("parm_insert_line");
+ tputs(tparm(parm_insert_line, n, 0), n, _nc_outch);
+ }
+ else if (scroll_reverse && top == miny && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("scroll_reverse");
+ tputs(scroll_reverse, 0, _nc_outch);
+ }
+ }
+ else if (insert_line && bot == maxy)
+ {
+ GoTo(top, 0);
+ UpdateAttrs(blank);
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("insert_line");
+ tputs(insert_line, 0, _nc_outch);
+ }
+ }
+ else
+ return ERR;
+
+ return OK;
+}
+
+/* scroll by using delete_line at del and insert_line at ins */
+/* n > 0 */
+static int scroll_idl(int n, int del, int ins, chtype blank)
+{
+ int i;
+
+ if(!((parm_delete_line || delete_line) && (parm_insert_line || insert_line)))
+ return ERR;
+
+ GoTo(del, 0);
+ UpdateAttrs(blank);
+ if (n == 1 && delete_line)
+ {
+ TPUTS_TRACE("delete_line");
+ tputs(delete_line, 0, _nc_outch);
+ }
+ else if (parm_delete_line)
+ {
+ TPUTS_TRACE("parm_delete_line");
+ tputs(tparm(parm_delete_line, n, 0), n, _nc_outch);
+ }
+ else /* if (delete_line) */
+ {
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("delete_line");
+ tputs(delete_line, 0, _nc_outch);
+ }
+ }
+
+ GoTo(ins, 0);
+ UpdateAttrs(blank);
+ if (n == 1 && insert_line)
+ {
+ TPUTS_TRACE("insert_line");
+ tputs(insert_line, 0, _nc_outch);
+ }
+ else if (parm_insert_line)
+ {
+ TPUTS_TRACE("parm_insert_line");
+ tputs(tparm(parm_insert_line, n, 0), n, _nc_outch);
+ }
+ else /* if (insert_line) */
+ {
+ for (i = 0; i < n; i++)
+ {
+ TPUTS_TRACE("insert_line");
+ tputs(insert_line, 0, _nc_outch);
+ }
+ }
+
+ return OK;
+}
+
+int _nc_scrolln(int n, int top, int bot, int maxy)
+/* scroll region from top to bot by n lines */
+{
+ chtype blank=ClrBlank(stdscr);
+ int i;
+ bool cursor_saved=FALSE;
+ int res;
+
+ TR(TRACE_MOVE, ("mvcur_scrolln(%d, %d, %d, %d)", n, top, bot, maxy));
+
+#if USE_XMC_SUPPORT
+ /*
+ * If we scroll, we might remove a cookie.
+ */
+ if (magic_cookie_glitch > 0) {
+ return (ERR);
+ }
+#endif
+
+ if (n > 0) /* scroll up (forward) */
+ {
+ /*
+ * Explicitly clear if stuff pushed off top of region might
+ * be saved by the terminal.
+ */
+ if (non_dest_scroll_region || (memory_above && top == 0)) {
+ for (i = 0; i < n; i++)
+ {
+ GoTo(i, 0);
+ ClrToEOL(BLANK);
+ }
+ }
+
+ res = scroll_csr_forward(n, top, bot, 0, maxy, blank);
+
+ if (res == ERR && change_scroll_region)
+ {
+ if ((((n==1 && scroll_forward) || parm_index)
+ && (SP->_cursrow == bot || SP->_cursrow == bot-1))
+ && save_cursor && restore_cursor)
+ {
+ cursor_saved=TRUE;
+ TPUTS_TRACE("save_cursor");
+ tputs(save_cursor, 0, _nc_outch);
+ }
+ TPUTS_TRACE("change_scroll_region");
+ tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch);
+ if (cursor_saved)
+ {
+ TPUTS_TRACE("restore_cursor");
+ tputs(restore_cursor, 0, _nc_outch);
+ }
+ else
+ {
+ SP->_cursrow = SP->_curscol = -1;
+ }
+
+ res = scroll_csr_forward(n, top, bot, top, bot, blank);
+
+ TPUTS_TRACE("change_scroll_region");
+ tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch);
+ SP->_cursrow = SP->_curscol = -1;
+ }
+
+ if (res == ERR && _nc_idlok)
+ res = scroll_idl(n, top, bot-n+1, blank);
+ }
+ else /* (n < 0) - scroll down (backward) */
+ {
+ /*
+ * Do explicit clear to end of region if it's possible that the
+ * terminal might hold on to stuff we push off the end.
+ */
+ if (non_dest_scroll_region || (memory_below && bot == maxy))
+ {
+ if (bot == maxy && clr_eos)
+ {
+ GoTo(maxy + n, 0);
+ ClrToEOS(BLANK);
+ }
+ else if (clr_eol)
+ {
+ for (i = 0; i < -n; i++)
+ {
+ GoTo(maxy + n + i, 0);
+ ClrToEOL(BLANK);
+ }
+ }
+ }
+
+ res = scroll_csr_backward(-n, top, bot, 0, maxy, blank);
+
+ if (res == ERR && change_scroll_region)
+ {
+ if (top != 0 && (SP->_cursrow == top || SP->_cursrow == top-1)
+ && save_cursor && restore_cursor)
+ {
+ cursor_saved=TRUE;
+ TPUTS_TRACE("save_cursor");
+ tputs(save_cursor, 0, _nc_outch);
+ }
+ TPUTS_TRACE("change_scroll_region");
+ tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch);
+ if (cursor_saved)
+ {
+ TPUTS_TRACE("restore_cursor");
+ tputs(restore_cursor, 0, _nc_outch);
+ }
+ else
+ {
+ SP->_cursrow = SP->_curscol = -1;
+ }
+
+ res = scroll_csr_backward(-n, top, bot, top, bot, blank);
+
+ TPUTS_TRACE("change_scroll_region");
+ tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch);
+ SP->_cursrow = SP->_curscol = -1;
+ }
+
+ if (res == ERR && _nc_idlok)
+ res = scroll_idl(-n, bot+n+1, top, blank);
+ }
+
+ if (res == ERR)
+ return(ERR);
+
+ _nc_scroll_window(curscr, n, top, bot, blank);
+
+ /* shift hash values too - they can be reused */
+ _nc_scroll_oldhash(n, top, bot);
+
+ return(OK);
+}
+
+
+void _nc_screen_resume()
+{
+ /* make sure terminal is in a sane known state */
+ SP->_current_attr = A_NORMAL;
+ newscr->_clear = TRUE;
+
+ if (SP->_coloron == TRUE && orig_pair)
+ putp(orig_pair);
+ if (exit_attribute_mode)
+ putp(exit_attribute_mode);
+ else
+ {
+ /* turn off attributes */
+ if (exit_alt_charset_mode)
+ putp(exit_alt_charset_mode);
+ if (exit_standout_mode)
+ putp(exit_standout_mode);
+ if (exit_underline_mode)
+ putp(exit_underline_mode);
+ }
+ if (exit_insert_mode)
+ putp(exit_insert_mode);
+ if (enter_am_mode && exit_am_mode)
+ putp(auto_right_margin ? enter_am_mode : exit_am_mode);
+}
+
+void _nc_screen_init()
+{
+ _nc_screen_resume();
+}
+
+/* wrap up screen handling */
+void _nc_screen_wrap()
+{
+ UpdateAttrs(A_NORMAL);
+}
+
+#if USE_XMC_SUPPORT
+void _nc_do_xmc_glitch(attr_t previous)
+{
+ attr_t chg = XMC_CHANGES(previous ^ SP->_current_attr);
+
+ while (chg != 0) {
+ if (chg & 1) {
+ SP->_curscol += magic_cookie_glitch;
+ if (SP->_curscol >= SP->_columns)
+ wrap_cursor();
+ T(("bumped to %d,%d after cookie", SP->_cursrow, SP->_curscol));
+ }
+ chg >>= 1;
+ }
+}
+#endif /* USE_XMC_SUPPORT */