#ifdef M_RCSID
#ifndef lint
static char const rcsID[] =
"$Header: /team/ps/sun_xcurses/archive/local_changes/xcurses/src/lib/"
"libxcurses/src/libc/xcurses/rcs/doupdate.c 1.22 1998/06/04 12:13:38 "
"cbates Exp $";
#endif
#endif
#include <sys/isa_defs.h>
#include <private.h>
#include <string.h>
#include <signal.h>
#undef SIGTSTP
#define JUMP_SIZE 4
#define CEOL_SIZE 2
#define GOTO(r, c) ((void) __m_mvcur(curscr->_cury, curscr->_curx,\
r, c, __m_outc), curscr->_cury = r, curscr->_curx = c)
typedef struct cost_op {
short cost;
short op;
} lcost;
typedef void (*t_action)(int, int);
#define LC(i, j) (lc[(i) * (LINES + 1) + (j)])
static lcost *lc = NULL;
#if defined(_LP64)
static unsigned int *nhash = NULL;
#else
static unsigned long *nhash = NULL;
#endif
static t_action *del = NULL;
static t_action *ins_rep = NULL;
static WINDOW *newscr;
static void erase_bottom(int, int);
static void clear_bottom(int);
static void complex(void);
static int cost(int, int);
static void lines_delete(int, int);
static void lines_insert(int, int);
static void lines_replace(int, int);
static void script(int, int);
static int scroll_up(int);
static void simple(void);
static void text_replace(int);
#if 0
static int scroll_dn(int);
#endif
int
__m_outc(int ch)
{
return (putc(ch, __m_screen->_of));
}
int
__m_doupdate_init(void)
{
void *new;
static short nlines = 0;
if (lines <= 0)
return (-1);
if (lines <= nlines)
return (0);
new = malloc((lines + 1) * (lines + 1) * sizeof (*lc));
if (new == NULL)
return (-1);
if (lc != NULL)
free(lc);
lc = (lcost *) new;
new = malloc((lines + lines) * sizeof (*del));
if (new == NULL)
return (-1);
if (del != NULL)
free(del);
del = (t_action *) new;
ins_rep = del + lines;
new = malloc(lines * sizeof (*nhash));
if (new == NULL)
return (-1);
if (nhash != NULL)
free(nhash);
#if defined(_LP64)
nhash = (unsigned int *) new;
#else
nhash = (unsigned long *) new;
#endif
nlines = lines;
return (0);
}
static void
erase_bottom(int start, int end)
{
int i;
for (i = start; i < end; ++i) {
(void) __m_cc_erase(curscr, i, 0, i, curscr->_maxx - 1);
__m_cc_hash(curscr, __m_screen->_hash, i);
}
}
static void
clear_bottom(int y)
{
if (back_color_erase)
(void) vid_puts(WA_NORMAL, 0, (void *) 0, __m_outc);
if (y == 0 && clear_screen != NULL) {
(void) TPUTS(clear_screen, 1, __m_outc);
} else {
(void) __m_mvcur(-1, -1, y, 0, __m_outc);
if (clr_eos != NULL) {
(void) TPUTS(clr_eos, 1, __m_outc);
} else if (clr_eol != NULL) {
for (;;) {
(void) TPUTS(clr_eol, 1, __m_outc);
if (LINES <= y)
break;
(void) __m_mvcur(y, 0, y + 1, 0, __m_outc);
++y;
}
}
}
curscr->_cury = y;
curscr->_curx = 0;
}
typedef struct LineRegion {
int col;
int size;
int type;
} LineRegion;
#define REGION_DIFFERENT 0
#define REGION_COMMON 1
#define REGION_DELETE 2
#define DELETE_SEARCH_LIMIT 4
#define DELETE_THRESHOLD 10
static LineRegion regions[1024];
int nRegions = 0;
static int
_find_blank_tail(int row)
{
cchar_t *nptr;
int tail = COLS;
if (!clr_eol)
return (COLS);
nptr = &newscr->_line[row][COLS];
for (; 0 < tail; --tail) {
if (!__m_cc_compare(--nptr, &newscr->_bg, 1))
break;
}
return (tail);
}
static void
_writeRegion(int row, LineRegion region)
{
short npair;
attr_t nattr;
int i;
cchar_t *optr = &curscr->_line[row][region.col];
cchar_t *nptr = &newscr->_line[row][region.col];
for (i = 0; i < region.size; i++, nptr++, optr++) {
nattr = nptr->_at;
npair = nptr->_co;
if ((ATTR_STATE != nattr) || (optr->_at != nattr) ||
(cur_term->_co != npair)) {
(void) vid_puts(nattr, npair, NULL, __m_outc);
}
if (nptr->_f)
(void) __m_cc_write(nptr);
*optr = *nptr;
curscr->_curx = region.col + i + 1;
}
}
static void
_deleteRegion(int row, LineRegion region)
{
int i;
cchar_t *optr = &curscr->_line[row][region.col];
if ((region.size <= 1) || !parm_dch) {
for (i = 0; i < region.size; i++)
(void) TPUTS(delete_character, 1, __m_outc);
} else {
(void) TPUTS(tparm(parm_dch, (long)region.size,
0, 0, 0, 0, 0, 0, 0, 0), region.size, __m_outc);
}
for (i = region.col; i < COLS - region.size; i++) {
*optr = *(optr + region.size);
optr++;
}
}
static void
_clearToEOL(int row, int tail)
{
if (tail < COLS) {
GOTO(row, tail);
if (back_color_erase)
(void) vid_puts(WA_NORMAL, 0, NULL, __m_outc);
(void) TPUTS(clr_eol, 1, __m_outc);
(void) __m_cc_erase(curscr, row, tail, row, COLS - 1);
}
}
static void
_normalizeRegions1(void)
{
int iRegion;
if (regions[0].type == REGION_COMMON) {
nRegions--;
for (iRegion = 0; iRegion < nRegions; iRegion++) {
regions[iRegion] = regions[iRegion + 1];
}
}
}
static void
_normalizeRegions2(void)
{
int iRegion;
for (iRegion = 0; iRegion < nRegions - 1; iRegion++) {
regions[iRegion].size = regions[iRegion + 1].col -
regions[iRegion].col;
}
regions[nRegions - 1].size = COLS - regions[nRegions - 1].col;
while (regions[nRegions - 1].type == REGION_COMMON)
nRegions--;
}
static void
_mergeTinyRegions(void)
{
int from;
int to;
for (from = 1, to = 1; from < nRegions; ) {
if ((regions[from].type == REGION_COMMON) &&
(regions[from].size < JUMP_SIZE)) {
regions[to - 1].size += regions[from].size;
if (++from < nRegions)
regions[to - 1].size += regions[from++].size;
} else {
regions[to++] = regions[from++];
}
}
nRegions = to;
}
static int
_findRegions(int row)
{
int cmp;
int old_cmp;
int col;
int bestDeleteCount;
cchar_t *nptr = &newscr->_line[row][0];
cchar_t *optr = &curscr->_line[row][0];
col = 0;
nRegions = 0;
bestDeleteCount = 0;
if ((__m_screen->_flags & S_INS_DEL_CHAR) &&
(parm_dch || delete_character)) {
int bestFit = 0;
int deletePoint;
int deleteCount;
int matches;
for (col = 0; col < COLS; col++) {
if (!__m_cc_compare(&optr[col], &nptr[col], 1))
break;
}
deletePoint = col;
for (deleteCount = 1; deleteCount < DELETE_SEARCH_LIMIT;
deleteCount++) {
matches = 0;
for (col = deletePoint; col < COLS - deleteCount;
col++) {
if (__m_cc_compare(&optr[col + deleteCount],
&nptr[col], 1))
matches++;
else
break;
}
if (matches > bestFit) {
bestFit = matches;
bestDeleteCount = deleteCount;
}
}
if (bestFit > DELETE_THRESHOLD) {
regions[nRegions].type = REGION_DELETE;
regions[nRegions].col = deletePoint;
regions[nRegions].size = bestDeleteCount;
nRegions++;
col = deletePoint + bestDeleteCount;
} else {
col = 0;
nRegions = 0;
bestDeleteCount = 0;
}
}
for (old_cmp = -1; col + bestDeleteCount < COLS; col++) {
cmp = __m_cc_compare(&optr[col + bestDeleteCount],
&nptr[col], 1);
if (cmp != old_cmp) {
regions[nRegions].type = cmp ? REGION_COMMON :
REGION_DIFFERENT;
regions[nRegions].col = col;
regions[nRegions].size = 0;
nRegions++;
old_cmp = cmp;
}
}
if (bestDeleteCount) {
regions[nRegions].type = REGION_DIFFERENT;
regions[nRegions].col = col;
regions[nRegions].size = 0;
nRegions++;
}
_normalizeRegions1();
if (nRegions == 0)
return (0);
_normalizeRegions2();
return (1);
}
static int
_ceolAdjustRegions(int row)
{
int iRegion;
int blankEolStart = _find_blank_tail(row);
for (iRegion = 0; iRegion < nRegions; iRegion++) {
switch (regions[iRegion].type) {
case REGION_DIFFERENT:
if (regions[iRegion].col >= blankEolStart) {
nRegions = iRegion;
return (blankEolStart);
}
if (regions[iRegion].col + regions[iRegion].size >
blankEolStart) {
regions[iRegion].size = blankEolStart -
regions[iRegion].col;
nRegions = iRegion + 1;
return (blankEolStart);
}
break;
case REGION_COMMON:
break;
case REGION_DELETE:
return (COLS);
}
}
return (COLS);
}
static void
_updateRegions(int row)
{
int ceolStart;
int iRegion;
ceolStart = _ceolAdjustRegions(row);
if (nRegions) {
for (iRegion = 0; iRegion < nRegions; iRegion++) {
switch (regions[iRegion].type) {
case REGION_COMMON:
break;
case REGION_DELETE:
GOTO(row, regions[iRegion].col);
_deleteRegion(row, regions[iRegion]);
break;
case REGION_DIFFERENT:
GOTO(row, regions[iRegion].col);
_writeRegion(row, regions[iRegion]);
break;
}
}
}
if (ceolStart != COLS) {
_clearToEOL(row, ceolStart);
}
}
static void
text_replace(int row)
{
if (!_findRegions(row))
return;
_mergeTinyRegions();
_updateRegions(row);
if (COLS <= curscr->_curx) {
--curscr->_curx;
if (auto_right_margin && (row < LINES - 1)) {
if (eat_newline_glitch) {
(void) __m_outc('\r');
(void) __m_outc('\n');
}
++curscr->_cury;
curscr->_curx = 0;
}
}
}
static void
lines_replace(int from, int to_1)
{
for (; from < to_1; ++from)
text_replace(from);
}
static void
lines_delete(int from, int to_1)
{
int count = to_1 - from;
if (LINES <= to_1) {
erase_bottom(from, LINES);
clear_bottom(from);
} else {
GOTO(from, 0);
(void) winsdelln(curscr, -count);
if (parm_delete_line != NULL) {
(void) TPUTS(tparm(parm_delete_line, (long)count,
0, 0, 0, 0, 0, 0, 0, 0), count, __m_outc);
} else if (delete_line != NULL) {
while (from++ < to_1)
(void) TPUTS(delete_line, 1, __m_outc);
} else {
return;
}
}
}
static void
lines_insert(int from, int to_1)
{
int row;
int count = to_1 - from;
GOTO(from, 0);
(void) winsdelln(curscr, count);
if (parm_insert_line != NULL) {
(void) TPUTS(tparm(parm_insert_line, (long)count,
0, 0, 0, 0, 0, 0, 0, 0), count, __m_outc);
} else if (insert_line != NULL) {
for (row = from; row < to_1; ++row)
(void) TPUTS(insert_line, 1, __m_outc);
} else {
return;
}
for (row = from; row < to_1; ++row)
text_replace(row);
}
static int
scroll_up(int n)
{
int count = n;
int start, finish, to;
if (scroll_forward != NULL) {
GOTO(LINES-1, 0);
while (0 < n--)
(void) TPUTS(scroll_forward, 1, __m_outc);
} else if (parm_delete_line != NULL && 1 < n) {
GOTO(0, 0);
(void) TPUTS(tparm(parm_delete_line, (long)n,
0, 0, 0, 0, 0, 0, 0, 0), n, __m_outc);
} else if (delete_line != NULL) {
GOTO(0, 0);
while (0 < n--)
(void) TPUTS(delete_line, 1, __m_outc);
} else {
return (0);
}
start = 0;
finish = count-1;
to = lines;
(void) __m_cc_erase(curscr, start, 0, finish, curscr->_maxx-1);
(void) __m_ptr_move((void **) curscr->_line,
curscr->_maxy, start, finish, to);
simple();
return (1);
}
#if 0
static int
scroll_dn(int n)
{
int count = n;
int start, finish, to;
if (LINES < n)
return (0);
if (scroll_reverse != NULL) {
GOTO(0, 0);
while (0 < n--)
(void) TPUTS(scroll_reverse, 1, __m_outc);
} else if (parm_insert_line != NULL && 1 < n) {
GOTO(0, 0);
(void) TPUTS(tparm(parm_insert_line, (long)n,
0, 0, 0, 0, 0, 0, 0, 0), n, __m_outc);
} else if (insert_line != NULL) {
GOTO(0, 0);
while (0 < n--)
(void) TPUTS(insert_line, 1, __m_outc);
} else {
return (0);
}
start = lines - count;
finish = lines - 1;
to = 0;
(void) __m_cc_erase(curscr, start, 0, finish, curscr->_maxx-1);
(void) __m_ptr_move((void **) curscr->_line,
curscr->_maxy, start, finish, to);
simple();
return (1);
}
#endif
#define MOVE_COST 0
#define REPLACE_COST 10
#define INSERT_COST 12
#define DELETE_COST 1
static int
cost(int fr, int lr)
{
lcost *lcp;
int or, nr, cc;
#if defined(_LP64)
unsigned int *ohash = __m_screen->_hash;
#else
unsigned long *ohash = __m_screen->_hash;
#endif
LC(fr, fr).cost = MOVE_COST;
for (cc = 1, ++lr, nr = fr+1; nr <= lr; ++nr, ++cc) {
LC(fr, nr).cost = cc * INSERT_COST;
LC(fr, nr).op = 'i';
LC(nr, fr).cost = cc * DELETE_COST;
LC(nr, fr).op = 'd';
}
for (--lr, or = fr; or <= lr; ++or) {
for (nr = fr; nr <= lr; ++nr) {
lcp = &LC(or + 1, nr + 1);
lcp->cost = LC(or, nr).cost;
lcp->op = 'm';
if (ohash[or] != nhash[nr]) {
lcp->cost += REPLACE_COST;
lcp->op = 'r';
}
if ((cc = LC(or + 1, nr).cost + INSERT_COST) <
lcp->cost) {
lcp->cost = cc;
lcp->op = 'i';
}
if ((cc = LC(or, nr + 1).cost + DELETE_COST) <
lcp->cost) {
lcp->cost = cc;
lcp->op = 'd';
}
}
}
return (LC(lr + 1, lr + 1).cost);
}
static void
script(int fr, int lr)
{
int i, j;
cchar_t *cp;
i = j = lr + 1;
(void) memset(del, 0, sizeof (*del) * LINES);
(void) memset(ins_rep, 0, sizeof (*ins_rep) * LINES);
do {
switch (LC(i, j).op) {
case 'i':
--j;
ins_rep[j] = lines_insert;
break;
case 'd':
--i;
del[i] = lines_delete;
break;
case 'm':
--i;
--j;
break;
case 'r':
--i;
--j;
ins_rep[j] = lines_replace;
break;
}
} while (fr < i || fr < j);
for (i = LINES-1; 0 <= i && ins_rep[i] == lines_insert; --i) {
for (cp = curscr->_line[i], j = 0; j < COLS; ++j, ++cp)
cp->_n = -1;
ins_rep[i] = lines_replace;
}
}
static void
complex(void)
{
int fr = -1;
int i, j, lr;
t_action func;
for (i = 0; i < LINES; ++i) {
if (newscr->_first[i] < newscr->_last[i]) {
__m_cc_hash(newscr, nhash, i);
if (fr == -1)
fr = i;
lr = i;
} else {
nhash[i] = __m_screen->_hash[i];
}
}
if (fr != -1) {
(void) cost(fr, lr);
script(fr, lr);
for (j = lr; fr <= j; --j) {
if (del[j] != (t_action) 0) {
for (i = j-1; fr <= i; --i)
if (del[i] == (t_action) 0)
break;
lines_delete(i+1, j+1);
j = i;
}
}
for (i = fr; i <= lr; ++i) {
if ((func = ins_rep[i]) != (t_action) 0) {
for (j = i; j <= lr && ins_rep[j] == func; ++j)
;
(*func)(i, j);
i = j - 1;
}
}
for (i = fr; i <= lr; ++i) {
__m_screen->_hash[i] = nhash[i];
newscr->_first[i] = newscr->_maxx;
newscr->_last[i] = -1;
}
}
}
static void
simple(void)
{
int row;
for (row = 0; row < newscr->_maxy; ++row) {
if (newscr->_first[row] < newscr->_last[row]) {
text_replace(row);
newscr->_first[row] = newscr->_maxx;
newscr->_last[row] = -1;
__m_cc_hash(curscr, __m_screen->_hash, row);
}
}
newscr->_flags &= ~W_REDRAW_WINDOW;
}
void
wtouchln_hard(WINDOW *w, int y, int n)
{
int last;
last = w->_maxx;
for (; (y < w->_maxy) && (0 < n); ++y, --n) {
(void) memset(&__m_screen->_curscr->_line[w->_begy + y][w->_begx],
0xff, last * sizeof (cchar_t));
}
}
int
doupdate(void)
{
#ifdef SIGTSTP
int (*oldsig)(int) = signal(SIGTSTP, SIG_IGN);
#endif
if (pollTypeahead()) {
return (OK);
}
newscr = __m_screen->_newscr;
if (__m_screen->_flags & S_ENDWIN) {
__m_screen->_flags &= ~S_ENDWIN;
(void) reset_prog_mode();
if (enter_ca_mode != NULL)
(void) TPUTS(enter_ca_mode, 1, __m_outc);
if (keypad_xmit != NULL)
(void) TPUTS(keypad_xmit, 1, __m_outc);
if (ena_acs != NULL)
(void) TPUTS(ena_acs, 1, __m_outc);
newscr->_flags |= W_CLEAR_WINDOW;
}
if ((newscr->_flags & (W_CLEAR_WINDOW | W_REDRAW_WINDOW)) ||
(curscr->_flags & W_CLEAR_WINDOW)) {
erase_bottom(0, newscr->_maxy);
clear_bottom(0);
(void) wtouchln(newscr, 0, newscr->_maxy, 1);
newscr->_flags &= ~W_CLEAR_WINDOW;
curscr->_flags &= ~W_CLEAR_WINDOW;
}
if (newscr->_scroll) {
int y;
for (y = 0; y < newscr->_maxy; ++y) {
if (0 <= newscr->_last[y]) {
newscr->_scroll = 0;
}
}
newscr->_scroll = 0;
}
if (newscr->_flags & W_REDRAW_WINDOW) {
simple();
} else {
if (newscr->_scroll == 0) {
if (__m_screen->_flags & S_INS_DEL_LINE) {
complex();
} else {
simple();
}
} else {
if (!scroll_up(newscr->_scroll)) {
if (__m_screen->_flags & S_INS_DEL_LINE) {
complex();
} else {
simple();
}
}
}
}
if (!(newscr->_flags & W_LEAVE_CURSOR)) {
GOTO(newscr->_cury, newscr->_curx);
}
if (!(curscr->_flags & W_FLUSH)) {
(void) fflush(__m_screen->_of);
}
newscr->_scroll = curscr->_scroll = 0;
__m_slk_doupdate();
#ifdef SIGTSTP
signal(SIGTSTP, oldsig);
#endif
return (OK);
}
void
idcok(WINDOW *w, bool bf)
{
__m_screen->_flags &= ~S_INS_DEL_CHAR;
if (bf)
__m_screen->_flags |= S_INS_DEL_CHAR;
}
int
idlok(WINDOW *w, bool bf)
{
__m_screen->_flags &= ~S_INS_DEL_LINE;
if (bf && has_il())
__m_screen->_flags |= S_INS_DEL_LINE;
return (OK);
}
void
#if defined(_LP64)
__m_cc_hash(WINDOW *w, unsigned int *array, int y)
#else
__m_cc_hash(WINDOW *w, unsigned long *array, int y)
#endif
{
array[y] = 0;
m_crcposix(&array[y], (unsigned char *) w->_line[y],
(size_t)(w->_maxx * sizeof (**w->_line)));
}