/*--------------------------------------------------------------------------- * * lib_mvcur.c * * The routine mvcur() etc. * * last edit-date: [Wed Jun 16 14:13:22 1993] * * -hm conversion from termcap -> terminfo * -hm optimization debugging * -hm zeyd's ncurses 0.7 update * -hm eat_newline_glitch bugfix * -hm hpux lint'ing .. * *---------------------------------------------------------------------------*/ /* This work is copyrighted. See COPYRIGHT.OLD & COPYRIGHT.NEW for * * details. If they are missing then this copy is in violation of * * the copyright conditions. */ #include #include #include "terminfo.h" #include "curses.priv.h" #ifndef OPT_MVCUR /* ** ** mvcur(oldrow, oldcol, newrow, newcol) ** A hack for terminals that are smart enough ** to know how to move cursor. ** There is still a bug in the alternative long- ** winded code. ** */ int mvcur(int oldrow, int oldcol, int newrow, int newcol) { T(("mvcur(%d,%d,%d,%d) called", oldrow, oldcol, newrow, newcol)); if(!cursor_address) return ERR; newrow %= lines; newcol %= columns; if (cursor_address) putp(tparm(cursor_address, newrow, newcol)); return OK; } #else #define BUFSIZE 128 /* size of strategy buffer */ struct Sequence { int vec[BUFSIZE]; /* vector of operations */ int *end; /* end of vector */ int cost; /* cost of vector */ }; static void row(struct Sequence *outseq, int orow, int nrow); static void column(struct Sequence *outseq, int ocol, int ncol); static void simp_col(struct Sequence *outseq, int oc, int nc); static void zero_seq(struct Sequence *seq); static void add_seq(struct Sequence *seq1, struct Sequence *seq2); static void out_seq(struct Sequence *seq); static void update_ops(void); static void init_costs(int costs[]); static int countc(char ch); static void add_op(struct Sequence *seq, int op, ...); static char *sequence(int op); static int c_count; /* used for counting tputs output */ #define INFINITY 1000 /* biggest, impossible sequence cost */ #define NUM_OPS 16 /* num. term. control sequences */ #define NUM_NPARM 9 /* num. ops wo/ parameters */ /* operator indexes into op_info */ #define CARRIAGE_RETURN 0 /* watch out for nl mapping */ #define CURS_DOWN 1 #define CURS_HOME 2 #define CURS_LEFT 3 #define CURS_RIGHT 4 #define CURS_TO_LL 5 #define CURS_UP 6 #define TAB 7 #define BACK_TAB 8 #define ROW_ADDR 9 #define COL_ADDR 10 #define P_DOWN_CURS 11 #define P_LEFT_CURS 12 #define P_RIGHT_CURS 13 #define P_UP_CURS 14 #define CURS_ADDR 15 static bool loc_init = FALSE; /* set if op_info is init'ed */ static bool rel_ok; /* set if we really know where we are */ /* * op_info[NUM_OPS] * * op_info[] contains for operations with no parameters * the cost of the operation. These ops should be first in the array. * For operations with parameters, op_info[] contains * the negative of the number of parameters. */ static int op_info[NUM_OPS] = { 0, /* carriage_return */ 0, /* cursor_down */ 0, /* cursor_home */ 0, /* cursor_left */ 0, /* cursor_right */ 0, /* cursor_to_ll */ 0, /* cursor_up */ 0, /* tab */ 0, /* back_tab */ -1, /* row_address */ -1, /* column_address */ -1, /* parm_down_cursor */ -1, /* parm_left_cursor */ -1, /* parm_right_cursor */ -1, /* parm_up_cursor */ -2 /* cursor_address */ }; /* ** Make_seq_best(best, try) ** ** Make_seq_best() copies try to best if try->cost < best->cost ** ** fixed the old version, now it really runs .. (-hm/08.04.93) ** */ inline void Make_seq_best(struct Sequence *best, struct Sequence *try) { if (best->cost > try->cost) { register int *sptr; sptr = try->vec; /* src ptr */ best->end = best->vec; /* dst ptr */ while(sptr != try->end) /* copy src -> dst */ *(best->end++) = *(sptr++); best->cost = try->cost; /* copy cost */ } } /* ** ** mvcur(oldrow, oldcol, newrow, newcol) ** ** mvcur() optimally moves the cursor from the position ** specified by (oldrow, oldcol) to (newrow, newcol). If ** (oldrow, oldcol) == (-1, -1), mvcur() does not use relative ** cursor motions. If the coordinates are otherwise ** out of bounds, it mods them into range. ** ** Revisions needed: ** eat_newline_glitch, auto_right_margin */ int mvcur(int oldrow, int oldcol, int newrow, int newcol) { struct Sequence seqA, seqB, /* allocate work structures */ col0seq, /* sequence to get from col0 to nc */ *best, /* best sequence so far */ *try; /* next try */ bool nlstat = SP->_nl; /* nl-output-mapping in effect ?*/ T(("=============================\nmvcur(%d,%d,%d,%d) called", oldrow, oldcol, newrow, newcol)); if ((oldrow == newrow) && (oldcol == newcol)) return OK; if (oldcol == columns-1 && eat_newline_glitch && auto_right_margin) { putp(tparm(cursor_address, newrow, newcol)); return OK; } #if 0 if (nlstat) nonl(); #endif update_ops(); /* make sure op_info[] is current */ if (oldrow < 0 || oldcol < 0 || (eat_newline_glitch && oldcol == 0 )) { rel_ok = FALSE; /* relative ops ok? */ } else { rel_ok = TRUE; oldrow %= lines; /* mod values into range */ oldcol %= columns; } newrow %= lines; newcol %= columns; best = &seqA; try = &seqB; /* try out direct cursor addressing */ zero_seq(best); add_op(best, CURS_ADDR, newrow, newcol); /* try out independent row/column addressing */ if(rel_ok) { zero_seq(try); row(try, oldrow, newrow); column(try, oldcol, newcol); Make_seq_best(best, try); } zero_seq(&col0seq); /* store seq. to get from c0 to nc */ column(&col0seq, 0, newcol); if(col0seq.cost < INFINITY) { /* can get from col0 to newcol */ /* try out homing and then row/column */ if (! rel_ok || newcol < oldcol || newrow < oldrow) { zero_seq(try); add_op(try, CURS_HOME, 1); row(try, 0, newrow); add_seq(try, &col0seq); Make_seq_best(best, try); } /* try out homing to last line and then row/column */ if (! rel_ok || newcol < oldcol || newrow > oldrow) { zero_seq(try); add_op(try, CURS_TO_LL, 1); row(try, lines - 1, newrow); add_seq(try, &col0seq); Make_seq_best(best, try); } } out_seq(best); #if 0 if(nlstat) nl(); #endif T(("===================================")); return OK; } /* ** row(outseq, oldrow, newrow) ** ** row() adds the best sequence for moving ** the cursor from oldrow to newrow to seq. ** row() considers row_address, parm_up/down_cursor ** and cursor_up/down. */ static void row(struct Sequence *outseq, /* where to put the output */ int orow, int nrow) /* old, new cursor locations */ { struct Sequence seqA, seqB, *best, /* best sequence so far */ *try; /* next try */ int parm_cursor, one_step; best = &seqA; try = &seqB; if (nrow == orow) return; if (nrow < orow) { parm_cursor = P_UP_CURS; one_step = CURS_UP; } else { parm_cursor = P_DOWN_CURS; one_step = CURS_DOWN; } /* try out direct row addressing */ zero_seq(best); add_op(best, ROW_ADDR, nrow); /* try out paramaterized up or down motion */ if (rel_ok) { zero_seq(try); add_op(try, parm_cursor, abs(orow - nrow)); Make_seq_best(best, try); } /* try getting there one step at a time... */ if (rel_ok) { zero_seq(try); add_op(try, one_step, abs(orow-nrow)); Make_seq_best(best, try); } add_seq(outseq, best); } /* ** column(outseq, oldcol, newcol) ** ** column() adds the best sequence for moving ** the cursor from oldcol to newcol to outseq. ** column() considers column_address, parm_left/right_cursor, ** simp_col(), and carriage_return followed by simp_col(). */ static void column(struct Sequence *outseq, /* where to put the output */ int ocol, int ncol) /* old, new cursor column */ { struct Sequence seqA, seqB, *best, *try; int parm_cursor; /* set to either parm_up/down_cursor */ best = &seqA; try = &seqB; if (ncol == ocol) return; if (ncol < ocol) parm_cursor = P_LEFT_CURS; else parm_cursor = P_RIGHT_CURS; /* try out direct column addressing */ zero_seq(best); add_op(best, COL_ADDR, ncol); /* try carriage_return then simp_col() */ if(! rel_ok || (ncol < ocol)) { zero_seq(try); add_op(try, CARRIAGE_RETURN, 1); simp_col(try, 0, ncol); Make_seq_best(best, try); } if(rel_ok) { /* try out paramaterized left or right motion */ zero_seq(try); add_op(try, parm_cursor, abs(ocol - ncol)); Make_seq_best(best, try); /* try getting there with simp_col() */ zero_seq(try); simp_col(try, ocol, ncol); Make_seq_best(best, try); } add_seq(outseq, best); } /* ** simp_col(outseq, oldcol, newcol) ** ** simp_col() adds the best simple sequence for getting ** from oldcol to newcol to outseq. ** simp_col() considers (back_)tab and cursor_left/right. ** ** Revisions needed: ** Simp_col asssumes that the cost of a (back_)tab ** is less then the cost of one-stepping to get to the same column. ** Should sometimes use overprinting instead of cursor_right. */ static void simp_col( struct Sequence *outseq, /* place to put sequence */ int oc, int nc) /* old column, new column */ { struct Sequence seqA, seqB, tabseq, *best, *try; int mytab, tabs, onepast, one_step, opp_step; onepast = -1; if (oc == nc) return; if(! rel_ok) { outseq->cost = INFINITY; return; } best = &seqA; try = &seqB; if(oc < nc) { mytab = TAB; if (init_tabs > 0 && op_info[TAB] < INFINITY) { tabs = (nc / init_tabs) - (oc / init_tabs); onepast = ((nc / init_tabs) + 1) * init_tabs; if (tabs) oc = onepast - init_tabs; /* consider it done */ } else { tabs = 0; } one_step = CURS_RIGHT; opp_step = CURS_LEFT; } else { mytab = BACK_TAB; if (init_tabs > 0 && op_info[BACK_TAB] < INFINITY) { tabs = (oc / init_tabs) - (nc / init_tabs); onepast = ((nc - 1) / init_tabs) * init_tabs; if (tabs) oc = onepast + init_tabs; /* consider it done */ } else { tabs = 0; } one_step = CURS_LEFT; opp_step = CURS_RIGHT; } /* tab as close as possible to nc */ zero_seq(&tabseq); add_op(&tabseq, mytab, tabs); /* try extra tab and backing up */ zero_seq(best); if (onepast >= 0 && onepast < columns) { add_op(best, mytab, 1); add_op(best, opp_step, abs(onepast - nc)); } else { best->cost = INFINITY; /* make sure of next swap */ } /* try stepping to nc */ zero_seq(try); add_op(try, one_step, abs(nc - oc)); Make_seq_best(best, try); if (tabseq.cost < INFINITY) add_seq(outseq, &tabseq); add_seq(outseq, best); } /* ** zero_seq(seq) empties seq. ** add_seq(seq1, seq2) adds seq1 to seq2. ** out_seq(seq) outputs a sequence. */ static void zero_seq(seq) struct Sequence *seq; { seq->end = seq->vec; seq->cost = 0; } static void add_seq(struct Sequence *seq1, struct Sequence *seq2) { int *vptr; T(("add_seq(%x, %x)", seq1, seq2)); if(seq1->cost >= INFINITY || seq2->cost >= INFINITY) seq1->cost = INFINITY; else { vptr = seq2->vec; while (vptr != seq2->end) *(seq1->end++) = *(vptr++); seq1->cost += seq2->cost; } } static void out_seq(struct Sequence *seq) { int *opptr, prm[9], ps, p, op; int count; char *sequence(); T(("out_seq(%x)", seq)); if (seq->cost >= INFINITY) return; for (opptr = seq->vec; opptr < seq->end; opptr++) { op = *opptr; /* grab operator */ ps = -op_info[op]; if(ps > 0) { /* parameterized */ for (p = 0; p < ps; p++) /* fill in needed parms */ prm[p] = *(++opptr); putp(tparm(sequence(op), prm[0], prm[1], prm[2], prm[3], prm[4], prm[5], prm[6], prm[7], prm[8])); } else { count = *(++opptr); /*rev should save tputs output instead of mult calls */ while (count--) /* do count times */ putp(sequence(op)); } } } /* ** update_ops() ** ** update_ops() makes sure that ** the op_info[] array is updated and initializes ** the cost array for SP if needed. */ static void update_ops() { T(("update_ops()")); if (SP) { /* SP structure exists */ int op; if (! SP->_costinit) { /* this term not yet assigned costs */ loc_init = FALSE; /* if !SP in the future, new term */ init_costs(SP->_costs); /* fill term costs */ SP->_costinit = TRUE; } for (op = 0; op < NUM_NPARM; op++) op_info[op] = SP->_costs[op]; /* set up op_info */ /* check for newline that might be mapped... */ if (SP->_nlmapping && index(sequence(CURS_DOWN), '\n')) op_info[CURS_DOWN] = INFINITY; } else { if (! loc_init) { /* using local costs */ loc_init = TRUE; init_costs(op_info); /* set up op_info */ } /* check for newline that might be mapped... */ if (index(sequence(CURS_DOWN), '\n')) op_info[CURS_DOWN] = INFINITY; } } /* ** init_costs(costs) ** ** init_costs() fills the array costs[NUM_NPARM] ** with costs calculated by doing tputs() calls. */ static void init_costs(int costs[]) { int i; for (i = 0; i < NUM_NPARM; i++) { if (sequence(i) != (char *) 0) { c_count = 0; tputs(sequence(i), 1, countc); costs[i] = c_count; } else costs[i] = INFINITY; } } /* ** countc() increments global var c_count. */ static int countc(char ch) { return(c_count++); } /* ** add_op(seq, op, p0, p1, ... , p8) ** ** add_op() adds the operator op and the appropriate ** number of paramaters to seq. It also increases the ** cost appropriately. ** if op has no parameters, p0 is taken to be a count. */ static void add_op(struct Sequence *seq, int op, ...) { va_list argp; int num_ps, p; T(("adding op %d to sequence", op)); va_start(argp, op); num_ps = - op_info[op]; /* get parms or -cost */ *(seq->end++) = op; if (num_ps == (- INFINITY) || sequence(op) == (char *) 0) { seq->cost = INFINITY; } else if (num_ps <= 0) { /* no parms, -cost */ int i = va_arg(argp, int); seq->cost -= i * num_ps; /* ADD count * cost */ *(seq->end++) = i; } else { int prm[9]; for (p = 0; p < num_ps; p++) *(seq->end++) = prm[p] = va_arg(argp, int); c_count = 0; tputs(tparm(sequence(op), prm[0], prm[1], prm[2], prm[3], prm[4], prm[5], prm[6], prm[7], prm[8]), 1, countc); seq->cost += c_count; } va_end(argp); } /* ** char *sequence(op) ** ** sequence() returns a pointer to the op's ** terminal control sequence. */ static char *sequence(int op) { T(("sequence(%d)", op)); switch(op) { case CARRIAGE_RETURN: return (carriage_return); case CURS_DOWN: return (cursor_down); case CURS_HOME: return (cursor_home); case CURS_LEFT: return (cursor_left); case CURS_RIGHT: return (cursor_right); case CURS_TO_LL: return (cursor_to_ll); case CURS_UP: return (cursor_up); case TAB: return (tab); case BACK_TAB: return (back_tab); case ROW_ADDR: return (row_address); case COL_ADDR: return (column_address); case P_DOWN_CURS: return (parm_down_cursor); case P_LEFT_CURS: return (parm_left_cursor); case P_RIGHT_CURS: return (parm_right_cursor); case P_UP_CURS: return (parm_up_cursor); case CURS_ADDR: return (cursor_address); default: return ((char *) 0); } } #endif