#ifdef M_RCSID
#ifndef lint
static char const rcsID[] = "$Header: /rd/src/libc/xcurses/rcs/doupdate.c 1.9 1995/07/26 17:45:06 ant Exp $";
#endif
#endif
#include <private.h>
#include <string.h>
#include <setjmp.h>
#include <signal.h>
#undef SIGTSTP
#ifdef MPE_STUB
#undef M_CURSES_TYPEAHEAD
#endif
#define JUMP_SIZE 4
#define CEOL_SIZE 2
#define GOTO(r,c) (__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);
static jmp_buf breakout;
#define LC(i,j) (lc[(i) * (LINES + 1) + (j)])
static lcost *lc = (lcost *) 0;
static unsigned long *nhash = (unsigned long *) 0;
static t_action *del = (t_action *) 0;
static t_action *ins_rep = (t_action *) 0;
static WINDOW *newscr;
STATIC void erase_bottom(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_dn(int);
STATIC int scroll_up(int);
STATIC void simple(void);
STATIC void text_replace(int);
STATIC void block_over(int, int, int);
int
__m_outc(ch)
int ch;
{
return putc(ch, __m_screen->_of);
}
int
__m_doupdate_init()
{
void *new;
static short nlines = 0;
if (lines <= 0)
return -1;
if (lines <= nlines)
return 0;
new = m_malloc((lines+1) * (lines+1) * sizeof *lc);
if (new == (void *) 0)
return -1;
if (lc != (lcost *) 0)
free(lc);
lc = (lcost *) new;
new = m_malloc((lines + lines) * sizeof *del);
if (new == (void *) 0)
return -1;
if (del != (t_action *) 0)
free(del);
del = (t_action *) new;
ins_rep = del + lines;
new = m_malloc(lines * sizeof *nhash);
if (new == (void *) 0)
return -1;
if (nhash != (unsigned long *) 0)
free(nhash);
nhash = (unsigned long *) new;
nlines = lines;
return 0;
}
STATIC void
erase_bottom(y)
int y;
{
int i;
for (i = y; i < LINES; ++i) {
(void) __m_cc_erase(curscr, i, 0, i, curscr->_maxx-1);
__m_cc_hash(curscr, __m_screen->_hash, i);
}
}
STATIC void
clear_bottom(y)
int y;
{
erase_bottom(y);
if (back_color_erase)
(void) vid_puts(WA_NORMAL, 0, (void *) 0, __m_outc);
if (y == 0 && clear_screen != (char *) 0) {
(void) tputs(clear_screen, 1, __m_outc);
} else {
(void) __m_mvcur(-1, -1, y, 0, __m_outc);
if (clr_eos != (char *) 0) {
(void) tputs(clr_eos, 1, __m_outc);
} else if (clr_eol != (char *) 0) {
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;
}
STATIC void
text_replace(row)
int row;
{
short npair;
attr_t cookie, nattr;
cchar_t *optr, *nptr;
int col, last, tail, jump, count;
#ifdef M_CURSES_TYPEAHEAD
if (__m_screen->_flags & S_ISATTY) {
unsigned char cc;
if (read(__m_screen->_kfd, &cc, sizeof cc) == sizeof cc) {
(void) ungetch(cc);
longjmp(breakout, 1);
}
}
#endif
col = newscr->_first[row];
if (col < 0)
col = 0;
last = newscr->_last[row];
if (COLS < last)
last = COLS;
if (clr_eol != (char *) 0) {
nptr = &newscr->_line[row][COLS];
for (tail = COLS; 0 < tail; --tail) {
if (!__m_cc_compare(--nptr, &newscr->_bg, 1))
break;
}
if (last < tail + CEOL_SIZE)
tail = COLS;
}
optr = &curscr->_line[row][col];
nptr = &newscr->_line[row][col];
for (jump = -1; col < last; ) {
for (count = 0; __m_cc_compare(optr, nptr, 1); ++count) {
++optr;
++nptr;
if (last <= ++col)
goto done;
}
if (jump < count) {
jump = JUMP_SIZE;
GOTO(row, col);
count = 0;
if (ceol_standout_glitch
&& (optr->_at != nptr->_at || optr->_co != nptr->_co))
ATTR_STATE |= WA_COOKIE;
} else {
optr -= count;
nptr -= count;
col -= count;
}
while (col < last
&& (!__m_cc_compare(optr, nptr, 1) || 0 < count--)) {
write_loop:
if (clr_eol != (char *) 0 && tail <= col) {
if (!ceol_standout_glitch
|| ATTR_STATE == WA_NORMAL) {
curscr->_curx = col;
goto done;
}
}
++col;
if (COLS <= col && LINES-1 <= row
&& auto_right_margin && !eat_newline_glitch) {
curscr->_curx = col;
goto done;
}
cookie = optr->_at & WA_COOKIE;
nattr = nptr->_at;
npair = nptr->_co;
if (ATTR_STATE != nattr
|| optr->_at != nattr || optr->_co != npair) {
(void) vid_puts(
nattr, npair, (void *) 0, __m_outc
);
cookie = WA_COOKIE;
}
if (nptr->_f)
(void) __m_cc_write(nptr);
*optr++ = *nptr++;
optr->_at |= cookie;
}
curscr->_curx = col;
if (ceol_standout_glitch && col < COLS
&& ATTR_STATE != (optr->_at & ~WA_COOKIE))
goto write_loop;
}
done:
if (!move_standout_mode && ATTR_STATE != WA_NORMAL) {
if (ceol_standout_glitch)
ATTR_STATE = A_NORMAL;
else
(void) vid_puts(WA_NORMAL, 0, (void *) 0, __m_outc);
}
if (clr_eol != (char *) 0 && tail <= col && col < last) {
for (tail = col; tail < COLS; ++tail, ++optr)
if (!__m_cc_compare(optr, &newscr->_bg, 1))
break;
if (tail < COLS) {
if (back_color_erase)
(void) vid_puts(
WA_NORMAL, 0, (void *) 0, __m_outc
);
(void) tputs(clr_eol, 1, __m_outc);
__m_cc_erase(curscr, row, tail, row, COLS-1);
}
}
if (COLS <= curscr->_curx) {
--curscr->_curx;
if (auto_right_margin && row < LINES-1) {
if (eat_newline_glitch) {
__m_outc('\r');
__m_outc('\n');
}
++curscr->_cury;
curscr->_curx = 0;
}
}
}
STATIC void
lines_replace(from, to_1)
int from, to_1;
{
for (; from < to_1; ++from)
text_replace(from);
}
STATIC void
lines_delete(from, to_1)
int from, to_1;
{
int count = to_1 - from;
if (LINES <= to_1) {
clear_bottom(from);
} else {
GOTO(from, 0);
(void) winsdelln(curscr, -count);
if (parm_delete_line != (char *) 0) {
(void) tputs(
tparm(
parm_delete_line, (long) count,
0, 0, 0, 0, 0, 0, 0, 0
), count, __m_outc
);
} else if (delete_line != (char *) 0) {
while (from++ < to_1)
(void) tputs(delete_line, 1, __m_outc);
} else {
return;
}
}
}
STATIC void
lines_insert(from, to_1)
int from, to_1;
{
int row, count = to_1 - from;
GOTO(from, 0);
(void) winsdelln(curscr, count);
if (parm_insert_line != (char *) 0) {
(void) tputs(
tparm(
parm_insert_line, (long) count,
0, 0, 0, 0, 0, 0, 0, 0
), count, __m_outc
);
} else if (insert_line != (char *) 0) {
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(n)
int n;
{
int count = n;
int start, finish, to, row;
if (scroll_forward != (char *) 0) {
GOTO(LINES-1, 0);
while (0 < n--)
(void) tputs(scroll_forward, 1, __m_outc);
} else if (parm_delete_line != (char *) 0 && 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 != (char *) 0) {
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;
}
STATIC int
scroll_dn(n)
int n;
{
int count = n;
int start, finish, to, row;
if (LINES < n)
return 0;
if (scroll_reverse != (char *) 0) {
GOTO(0, 0);
while (0 < n--)
(void) tputs(scroll_reverse, 1, __m_outc);
} else if (parm_insert_line != (char *) 0 && 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 != (char *) 0) {
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;
}
#ifdef NEVER
STATIC int
is_same_line(old, new, count)
cchar_t *old, *new;
int count;
{
while (0 < count--)
if (!__m_cc_compare(old, new, 1))
return 0;
return 1;
}
#endif
STATIC int
cost(fr, lr)
int fr, lr;
{
register lcost *lcp;
register int or, nr, cc;
register unsigned long *ohash = __m_screen->_hash;
cchar_t **oline = curscr->_line;
cchar_t **nline = newscr->_line;
int linesz = COLS * sizeof **oline;
LC(fr,fr).cost = 0;
for (cc = 1, ++lr, nr = fr+1; nr <= lr; ++nr, ++cc) {
LC(fr,nr).cost = cc * 3;
LC(fr,nr).op = 'i';
LC(nr,fr).cost = cc;
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]
#ifdef NEVER
|| !is_same_line(oline[or], nline[nr], linesz)
#endif
) {
lcp->cost += 2;
lcp->op = 'r';
}
if ((cc = LC(or+1,nr).cost + 3) < lcp->cost) {
lcp->cost = cc;
lcp->op = 'i';
}
if ((cc = LC(or,nr+1).cost + 1) < lcp->cost) {
lcp->cost = cc;
lcp->op = 'd';
}
}
}
return LC(lr+1,lr+1).cost;
}
STATIC void
script(fr, lr)
int fr, lr;
{
int i, j;
cchar_t *cp;
i = j = lr + 1;
memset(del, 0, sizeof *del * LINES);
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()
{
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) {
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()
{
int row;
for (row = 0; row < LINES; ++row) {
if (newscr->_first[row] < newscr->_last[row]) {
text_replace(row);
newscr->_first[row] = newscr->_maxx;
newscr->_last[row] = -1;
if (__m_screen->_flags & S_INS_DEL_LINE)
__m_cc_hash(newscr, nhash, row);
}
}
newscr->_flags &= ~W_REDRAW_WINDOW;
}
int
doupdate()
{
#ifdef SIGTSTP
int (*oldsig)(int) = signal(SIGTSTP, SIG_IGN);
#endif
#ifdef M_CURSES_TYPEAHEAD
unsigned char cc;
volatile int min, time, icanon;
if (__m_screen->_flags & S_ISATTY) {
min = cur_term->_prog.c_cc[VMIN];
time = cur_term->_prog.c_cc[VTIME];
icanon = cur_term->_prog.c_lflag & ICANON;
cur_term->_prog.c_cc[VMIN] = 0;
cur_term->_prog.c_cc[VTIME] = 0;
cur_term->_prog.c_lflag &= ~ICANON;
(void) tcsetattr(__m_screen->_kfd, TCSANOW, &cur_term->_prog);
}
#endif
#ifdef M_CURSES_TRACE
__m_trace(
"doupdate(void) using %s algorithm.",
(__m_screen->_flags & S_INS_DEL_LINE) ? "complex" : "simple"
);
#endif
newscr = __m_screen->_newscr;
if (__m_screen->_flags & S_ENDWIN) {
__m_screen->_flags &= ~S_ENDWIN;
(void) reset_prog_mode();
if (enter_ca_mode != (char *) 0)
(void) tputs(enter_ca_mode, 1, __m_outc);
if (keypad_xmit != (char *) 0)
(void) tputs(keypad_xmit, 1, __m_outc);
if (ena_acs != (char *) 0)
(void) tputs(ena_acs, 1, __m_outc);
newscr->_flags |= W_CLEAR_WINDOW;
}
#ifdef M_CURSES_TYPEAHEAD
if (setjmp(breakout) == 0) {
if ((__m_screen->_flags & S_ISATTY)
&& read(__m_screen->_kfd, &cc, sizeof cc) == sizeof cc) {
(void) ungetch(cc);
longjmp(breakout, 1);
}
#endif
if (newscr->_flags & (W_CLEAR_WINDOW | W_REDRAW_WINDOW)) {
clear_bottom(0);
newscr->_flags &= ~W_CLEAR_WINDOW;
(void) wtouchln(newscr, 0, newscr->_maxy, 1);
}
if (newscr->_flags & W_REDRAW_WINDOW)
simple();
#if 0
else if (newscr->_scroll < 0 && scroll_dn(-newscr->_scroll))
#else
else if (scroll_dn(-newscr->_scroll))
#endif
;
else if (0 < newscr->_scroll && scroll_up(newscr->_scroll))
;
else 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);
#ifdef M_CURSES_TYPEAHEAD
}
if (__m_screen->_flags & S_ISATTY) {
cur_term->_prog.c_cc[VMIN] = min;
cur_term->_prog.c_cc[VTIME] = time;
cur_term->_prog.c_lflag |= icanon;
(void) tcsetattr(__m_screen->_kfd,TCSANOW,&cur_term->_prog);
}
#endif
newscr->_scroll = curscr->_scroll = 0;
#ifdef SIGTSTP
signal(SIGTSTP, oldsig);
#endif
return __m_return_code("doupdate", OK);
}
void
idcok(WINDOW *w, bool bf)
{
#ifdef M_CURSES_TRACE
__m_trace("idcok(%p, %d)", w, bf);
#endif
__m_screen->_flags &= ~S_INS_DEL_CHAR;
if (bf)
__m_screen->_flags |= S_INS_DEL_CHAR;
__m_return_void("idcok");
}
int
idlok(WINDOW *w, bool bf)
{
#ifdef M_CURSES_TRACE
__m_trace("idlok(%p, %d)", w, bf);
#endif
__m_screen->_flags &= ~S_INS_DEL_LINE;
if (bf && has_il())
__m_screen->_flags |= S_INS_DEL_LINE;
return __m_return_code("idlok", OK);
}
void
__m_cc_hash(w, array, y)
WINDOW *w;
unsigned long *array;
int y;
{
array[y] = 0;
m_crcposix(
&array[y], (unsigned char *) w->_line[y],
(size_t) (w->_maxx * sizeof **w->_line)
);
}