#include "curses.ext" /* @(#) mvcur.c: 1.1 10/15/83 (1.14 3/16/83) */ /* * Cursor motion optimization routine. This routine takes as parameters * the screen positions that the cursor is currently at, and the position * you want it to be at, and it will move the cursor there very * efficiently. It isn't really optimal, since several approximations * are taken in the interests of efficiency and simplicity. The code * here considers directly addressing the cursor, and also considers * local motions using left, right, up, down, tabs, backtabs, vertical * and horizontal addressing, and parameterized motions. It does not * consider using home down, or taking advantage of automatic margins on * any of the four directions. (Two of these directions, left and right, * are well defined by the am and bw capabilities, but up and down are * not defined, nor are tab or backtab off the ends.) * * General strategies considered: * CA Direct Cursor Addressing * LM Local Motions from the old position * HDR Home + Local Motions from upper left corner * CR CR + Local Motions from left margin * * Local Motions can include * Up cuu, cuu1, vpa * Down cud, cud1, vpa * Left cul, cul1, hpa, bs, cbt * Right cuf, cuf1, hpa, tab, char moved over */ #ifdef DEBUG /* We don't want this debugging output now. */ # undef DEBUG #endif /* #define mvcurdebug */ #ifdef mvcurdebug # define DEBUG # define _outch oc FILE *outf; main() { int oldrow, oldcol, newrow, newcol, n; newterm(getenv("TERM"), stdout, stdin); echo(); nocrmode(); nl(); outf = stdout; setbuf(stdout, NULL); for (;;) { printf("oldrow, oldcol, newrow, newcol: "); n = scanf("%d %d %d %d", &oldrow, &oldcol, &newrow, &newcol); if (n < 0) { endwin(); exit(1); } mvcur(oldrow, oldcol, newrow, newcol); } } _outch(ch) { printf("_outch '%s'\n", unctrl(ch)); } #else int _outch(); #endif static int bare_lf_ok; /* bare_lf_ok is true if linefeed will work right */ char *tparm(); mvcur(oldrow, oldcol, newrow, newcol) int oldrow, oldcol, newrow, newcol; { /* temporary costs of various submotions */ int home_right; /* cost to move from col 0 right to newcol */ int cur_right; /* motion from current postition to right */ int cur_left; /* motion from current postition to left */ int home_down; /* motion from line 0 downward */ int cur_down; /* motion from current position downward */ int cur_up; /* motion from current position upward */ /* costs of various full motions we consider */ int cost_ca; /* cost of directly addressing the cursor */ int cost_hdr; /* going home, local motions down & right */ int cost_cr; /* return, local motions up/down & right */ int cost_lm; /* local motions up/down & left/right */ int sgf; #ifdef DEBUG if(outf) fprintf(outf, "mvcur(oldrow=%d, oldcol=%d, newrow=%d, newcol=%d)\n", oldrow, oldcol, newrow, newcol); #endif /* Quick check for nothing to do - very common case. */ if (oldrow == newrow && oldcol == newcol) return; #ifndef NONSTANDARD # ifdef USG bare_lf_ok = ((cur_term->Nttyb.c_oflag&ONLCR)==0); # else sgf = cur_term->Nttyb.sg_flags; bare_lf_ok = (sgf&RAW) ? 1 : (sgf&CRMOD)==0; # endif #else NONSTANDARD bare_lf_ok = 1; #endif NONSTANDARD /* Another common case: crlf */ if (newcol==0 && newrow==oldrow+1 && carriage_return && cursor_down) { /* * Just because bare_lf_ok is on doesn't mean cursor_down is * crlf. E.g. in the tty 40/2 it's escape B. The test below * will be wrong on a newline style terminal (!bare_lf_ok) * with no carriage_return where cursor_down is not crlf. * I have never seen such a terminal. */ if (oldcol > 0 && carriage_return) tputs(carriage_return, 1, _outch); tputs(cursor_down, 1, _outch); return; } /* Figure out costs of various component motions */ home_right = _loc_right(0 , newcol, 0, newcol); cur_right = _loc_right(oldcol, newcol, 0, newcol); cur_left = _loc_left (oldcol, newcol, 0); home_down = _loc_down (0 , newrow, 0, newcol); cur_down = _loc_down (oldrow, newrow, 0, newcol); cur_up = _loc_up (oldrow, newrow, 0); #ifdef DEBUG if(outf) fprintf(outf, "home_right %d, cur_right %d, cur_left %d, home_down %d, cur_down %d, cur_up %d\n", home_right, cur_right, cur_left, home_down, cur_down, cur_up); #endif /* 4 possible strategies: get costs for each */ cost_ca = _cost(Cursor_address); cost_hdr = _cost(Cursor_home) + home_down + home_right; /* 3rd and 4th strategies: local motions and with carriage return. */ if (newrow < oldrow) { if (newcol < oldcol) cost_lm = cur_left + cur_up; else cost_lm = cur_right + cur_up; cost_cr = _cost(Carriage_return) + cur_up + home_right; } else { if (newcol < oldcol) cost_lm = cur_left + cur_down; else cost_lm = cur_right + cur_down; if (cur_down >= INFINITY) cur_down = _loc_down(oldrow, newrow, 0, 0); cost_cr = _cost(Carriage_return) + cur_down + home_right; } #ifdef DEBUG if(outf) fprintf(outf, "cost_ca %d, cost_hdr %d, cost_cr %d, cost_lm %d\n", cost_ca, cost_hdr, cost_cr, cost_lm); #endif /* * Now we pick which one is cheapest and actually do it. * Note the ordering if they come out equal - this was * conciously chosen based on how visually distracting * it is to see the cursor bounce all over the screen, * I did not take into account approximation errors. */ if (cost_ca <= cost_hdr && cost_ca <= cost_cr && cost_ca <= cost_lm) { /* direct cursor addressing is cheapest */ #ifdef DEBUG if(outf) fprintf(outf, "chose absolute cursor addressing\n"); #endif tputs(tparm(cursor_address, newrow, newcol), 1, _outch); } else if (cost_lm <= cost_cr && cost_lm <= cost_hdr) { /* local motions are cheapest */ #ifdef DEBUG if(outf) fprintf(outf, "chose local motions\n"); #endif if (newcol > oldcol) { if (newrow > oldrow) (void) _loc_down(oldrow, newrow, 1, newcol); else (void) _loc_up(oldrow, newrow, 1); (void) _loc_right(oldcol, newcol, 1, newrow); } else { if (newrow > oldrow) (void) _loc_down(oldrow, newrow, 1, newcol); else (void) _loc_up(oldrow, newrow, 1); (void) _loc_left(oldcol, newcol, 1); } } else if (cost_cr <= cost_hdr) { /* carriage return + local motions are cheapest */ #ifdef DEBUG if(outf) fprintf(outf, "chose carriage return + local motions\n"); #endif tputs(carriage_return, 1, _outch); if (newrow > oldrow) (void) _loc_down(oldrow, newrow, 1, 0); else (void) _loc_up(oldrow, newrow, 1); (void) _loc_right(0, newcol, 1, newrow); } else { /* home + local motions are cheapest */ #ifdef DEBUG if(outf) fprintf(outf, "chose home + local motions\n"); #endif tputs(cursor_home, 1, _outch); (void) _loc_down(0, newrow, 1, newcol); (void) _loc_right(0, newcol, 1, newrow); } #ifdef DEBUG if(outf) fprintf(outf, "end of mvcur\n"); #endif } /* * These four routines figure out what the cost of the most efficient * kind of local motion from the given row or column to the other given * row or column is. They return the cost, in characters. If the third * argument is 1, they actually do the motion. The row number is useful * if we're going to actually do the motion - that way we can look in * the screen image (if we have it) and just output the characters that * are already on the screen - this usually saves 50% over cursor_right. */ static _loc_right(oldcol, newcol, domotion, row) { int c0, c1, c2, c3; int i, tabcol, ntabs, nright, nleft; /* notinsmode: we know for sure we aren't in insert char mode */ int notinsmode = SP && SP->phys_irm!=1; register struct line *rp; if (newcol < oldcol) /* can't go left with right motions */ return INFINITY; if (newcol == oldcol) return 0; /* already there - nothing to do */ #ifdef DEBUG fprintf(outf, "SP %x, phys_irm %d, notinsmode %d\n", SP, SP->phys_irm, notinsmode); #endif /* * Code here and further down attempts to output the character that * is already on the screen to move right. */ if (notinsmode) _cost(Cursor_right) = 1; else _cost(Cursor_right) = _cost(Right_base); /* figure out various costs */ tabcol = (newcol+4)/8 * 8; /* round to nearest 8 */ /* tab past right margin is undefined */ if (tabcol >= columns) tabcol = (columns-1)/8 * 8; ntabs = (tabcol-oldcol+7)/8; if (ntabs <= 0) tabcol = oldcol; if (tabcol < newcol) { /* some tabs plus some rights */ nright = newcol - tabcol; c1 = ntabs*_cost(Tab) + nright*_cost(Cursor_right); } else { /* some tabs plus some lefts */ nleft = tabcol - newcol; c1 = ntabs*_cost(Tab) + nleft*_cost(Cursor_left); } c0 = (newcol - oldcol) * _cost(Cursor_right); if (parm_right_cursor) c2 = _cost(Parm_right_cursor); else c2 = INFINITY; if (column_address) c3 = _cost(Column_address); else c3 = INFINITY; #ifdef DEBUG if(outf) fprintf(outf, "_loc_right(%d, %d, %d), chars %d, ri %d, RI %d, ch %d\n", oldcol, newcol, domotion, c0, c1, c2, c3); #endif /* Decide and maybe do them */ if (c3 <= c1 && c3 <= c2 && c3 <= c0) { /* cheapest to use column absolute cursor addressing */ #ifdef DEBUG if(outf) fprintf(outf, "chose column absolute cursor addressing\n"); #endif if (domotion) tputs(tparm(column_address, newcol), 1, _outch); return c3; } else if (c2 <= c1 && c2 <= c0) { /* cheapest to use column relative motion */ #ifdef DEBUG if(outf) fprintf(outf, "chose column relative motion\n"); #endif if (domotion) tputs(tparm(parm_right_cursor, newcol-oldcol), 1, _outch); return c2; } else { /* cheapest to use several right commands */ #ifdef DEBUG if(outf) fprintf(outf, "chose rights: ntabs %d, tabcol %d, nleft %d, nright %d\n", ntabs, tabcol, nleft, nright); #endif if (domotion) if (c1 < c0) { for (i=0; i<ntabs; i++) tputs(tab, 1, _outch); if (tabcol < newcol) { /* some tabs plus some rights */ for (i=0; i<nright; i++) { #ifdef DEBUG if (outf && SP->cur_body[row+1]) fprintf(outf, "nd1, row %d col %d+%d=%d, char '%c'\n", row, tabcol, i, tabcol+i, SP->cur_body[row+1]->body[tabcol+i]); #endif rp = SP->cur_body[row+1]; if (cursor_right && (!notinsmode || rp && SP->phys_gr != (rp->body[tabcol+i] & A_ATTRIBUTES))) /* dont know */ tputs(cursor_right,1,_outch); else if (rp && rp->length > tabcol+i) /* Note we assume dumb terminals without cursor_right don't have * standout either, otherwise we should go into right standout * mode here and in the essentially similar code below. */ _outch(rp->body[tabcol+i]&A_CHARTEXT); else /* off edge */ _outch(' '); } } else { /* some tabs plus some lefts */ for (i=0; i<nleft; i++) tputs(cursor_left,1,_outch); } } else { for (i=oldcol; i<newcol; i++) { #ifdef DEBUG if (outf && SP->cur_body[row+1]) fprintf(outf, "nd2, row %d col %d, char '%c'\n", row, i, SP->cur_body[row+1]->body[i]); #endif rp = SP->cur_body[row+1]; if (cursor_right && (!notinsmode || rp && SP->phys_gr != (rp->body[i] & A_ATTRIBUTES))) /* dont know */ tputs(cursor_right,1,_outch); else if (rp && rp->length > i) _outch(rp->body[i]&A_CHARTEXT); else /* off edge */ _outch(' '); } } return (c1 < c0) ? c1 : c0; } } static _loc_left(oldcol, newcol, domotion) { int c1, c2, c3; int i, tabcol, ntabs, nright, nleft; if (newcol > oldcol) /* can't go right with left motions */ return INFINITY; if (newcol == oldcol) return 0; /* already there - nothing to do */ /* figure out various costs */ if (cursor_left) { if (back_tab) { tabcol = (newcol+4)/8 * 8; /* round to nearest 8 */ /* tab past left margin is undefined */ if (tabcol < 8) tabcol = 8; ntabs = (oldcol-tabcol+7)/8; if (ntabs <= 0) tabcol = oldcol; if (tabcol < newcol) { /* some backtabs plus some rights */ nright = newcol - tabcol; c1 = ntabs*_cost(Back_tab) + nright*_cost(Cursor_right); } else { /* some tabs plus some lefts */ nleft = tabcol - newcol; c1 = ntabs*_cost(Back_tab) + nleft*_cost(Cursor_left); } } else { c1 = (oldcol - newcol) * _cost(Cursor_left); } } else c1 = INFINITY; if (parm_left_cursor) c2 = _cost(Parm_left_cursor); else c2 = INFINITY; if (column_address) c3 = _cost(Column_address); else c3 = INFINITY; #ifdef DEBUG if(outf) fprintf(outf, "_loc_left(%d, %d, %d), le %d, LE %d, ch %d\n", oldcol, newcol, domotion, c1, c2, c3); #endif /* Decide and maybe do them */ if (c3 <= c1 && c3 <= c2) { /* cheapest to use column absolute cursor addressing */ #ifdef DEBUG if(outf) fprintf(outf, "chose column absolute cursor addressing\n"); #endif if (domotion) tputs(tparm(column_address, newcol), 1, _outch); return c3; } else if (c2 <= c1) { /* cheapest to use column relative motion */ #ifdef DEBUG if(outf) fprintf(outf, "chose column relative motion\n"); #endif if (domotion) tputs(tparm(parm_left_cursor, oldcol-newcol), 1, _outch); return c2; } else { /* cheapest to use several left commands */ #ifdef DEBUG if(outf) fprintf(outf, "chose several left commands\n"); #endif if (domotion) if (back_tab) { for (i=0; i<ntabs; i++) tputs(back_tab, 1, _outch); if (tabcol < newcol) { /* some tabs plus some rights */ for (i=0; i<nright; i++) tputs(cursor_right,1, _outch); } else { /* some tabs plus some lefts */ for (i=0; i<nleft; i++) tputs(cursor_left,1,_outch); } } else { for (i=oldcol; i>newcol; i--) tputs(cursor_left, 1, _outch); } return c1; } } static _loc_up(oldrow, newrow, domotion) { int c1, c2, c3, i; if (newrow > oldrow) /* can't go down with up motions */ return INFINITY; if (newrow == oldrow) return 0; /* already there - nothing to do */ /* figure out various costs */ if (cursor_up) c1 = (oldrow - newrow) * _cost(Cursor_up); else c1 = INFINITY; if (parm_up_cursor) c2 = _cost(Parm_up_cursor); else c2 = INFINITY; if (row_address) c3 = _cost(Row_address); else c3 = INFINITY; #ifdef DEBUG if(outf) fprintf(outf, "_loc_up(%d, %d, %d), up %d, UP %d, cv %d\n", oldrow, newrow, domotion, c1, c2, c3); #endif /* Decide and maybe do them */ if (c3 <= c1 && c3 <= c2) { /* cheapest to use row absolute cursor addressing */ #ifdef DEBUG if(outf) fprintf(outf, "chose row absolute cursor addressing\n"); #endif if (domotion) tputs(tparm(row_address, newrow), 1, _outch); return c3; } else if (c2 <= c1) { /* cheapest to use row relative motion */ #ifdef DEBUG if(outf) fprintf(outf, "chose row relative motion\n"); #endif if (domotion) tputs(tparm(parm_up_cursor, oldrow-newrow), 1, _outch); return c2; } else { /* cheapest to use several up commands */ #ifdef DEBUG if(outf) fprintf(outf, "chose several up commands\n"); #endif if (domotion) for (i=oldrow; i>newrow; i--) tputs(cursor_up, 1, _outch); return c1; } } static _loc_down(oldrow, newrow, domotion, col) { int c1, c2, c3, i; if (newrow < oldrow) /* can't go up with down motions */ return INFINITY; if (newrow == oldrow) return 0; /* already there - nothing to do */ /* figure out various costs */ if (cursor_down && (col==0 || bare_lf_ok || *cursor_down!='\n')) c1 = (newrow - oldrow) * _cost(Cursor_down); else c1 = INFINITY; if (parm_down_cursor) c2 = _cost(Parm_down_cursor); else c2 = INFINITY; if (row_address) c3 = _cost(Row_address); else c3 = INFINITY; #ifdef DEBUG if(outf) fprintf(outf, "_loc_down(%d, %d, %d, %d), do %d, DO %d, cv %d\n", oldrow, newrow, domotion, col,c1, c2, c3); #endif /* Decide and maybe do them */ if (c3 <= c1 && c3 <= c2) { /* cheapest to use row absolute cursor addressing */ #ifdef DEBUG if(outf) fprintf(outf, "chose row absolute cursor addressing\n"); #endif if (domotion) tputs(tparm(row_address, newrow), 1, _outch); return c3; } else if (c2 <= c1) { /* cheapest to use row relative motion */ #ifdef DEBUG if(outf) fprintf(outf, "chose row relative motion\n"); #endif if (domotion) tputs(tparm(parm_down_cursor, newrow-oldrow), 1, _outch); return c2; } else { /* cheapest to use several down commands */ #ifdef DEBUG if(outf) fprintf(outf, "chose several down commands\n"); #endif if (domotion) for (i=oldrow; i<newrow; i++) tputs(cursor_down, 1, _outch); return c1; } }