1: /* Cursor motion subroutines for GNU Emacs. 2: Copyright (C) 1985 Richard M. Stallman. 3: based primarily on public domain code written by Chris Torek 4: 5: This file is part of GNU Emacs. 6: 7: GNU Emacs is distributed in the hope that it will be useful, 8: but WITHOUT ANY WARRANTY. No author or distributor 9: accepts responsibility to anyone for the consequences of using it 10: or for whether it serves any particular purpose or works at all, 11: unless he says so in writing. Refer to the GNU Emacs General Public 12: License for full details. 13: 14: Everyone is granted permission to copy, modify and redistribute 15: GNU Emacs, but only under the conditions described in the 16: GNU Emacs General Public License. A copy of this license is 17: supposed to have been given to you along with GNU Emacs so you 18: can know your rights and responsibilities. It should be in a 19: file named COPYING. Among other things, the copyright notice 20: and this notice must be preserved on all copies. */ 21: 22: 23: #include "config.h" 24: #include <stdio.h> 25: #include "cm.h" 26: #include "termhooks.h" 27: 28: #define BIG 9999 /* 9999 good on VAXen. For 16 bit machines 29: use about 2000.... */ 30: 31: char *malloc (), *tgoto (), *getenv (); 32: 33: extern char *BC, *UP; 34: 35: int cost; /* sums up costs */ 36: 37: /* ARGSUSED */ 38: evalcost (c) 39: char c; 40: { 41: cost++; 42: } 43: 44: void 45: cmputc (c) 46: char c; 47: { 48: if (termscript) 49: fputc (c & 0177, termscript); 50: putchar (c & 0177); 51: } 52: 53: /* NEXT TWO ARE DONE WITH MACROS */ 54: #if 0 55: /* 56: * Assume the cursor is at row row, column col. Normally used only after 57: * clearing the screen, when the cursor is at (0, 0), but what the heck, 58: * let's let the guy put it anywhere. 59: */ 60: 61: static 62: at (row, col) { 63: curY = row; 64: curX = col; 65: } 66: 67: /* 68: * Add n columns to the current cursor position. 69: */ 70: 71: static 72: addcol (n) { 73: curX += n; 74: 75: /* 76: * If cursor hit edge of screen, what happened? 77: * N.B.: DO NOT!! write past edge of screen. If you do, you 78: * deserve what you get. Furthermore, on terminals with 79: * autowrap (but not magicwrap), don't write in the last column 80: * of the last line. 81: */ 82: 83: if (curX == Wcm.cm_cols) { 84: /* 85: * Well, if magicwrap, still there, past the edge of the 86: * screen (!). If autowrap, on the col 0 of the next line. 87: * Otherwise on last column. 88: */ 89: 90: if (Wcm.cm_magicwrap) 91: ; /* "limbo" */ 92: else if (Wcm.cm_autowrap) { 93: curX = 0; 94: curY++; /* Beware end of screen! */ 95: } 96: else 97: curX--; 98: } 99: } 100: #endif 101: 102: /* 103: * (Re)Initialize the cost factors, given the output speed of the terminal 104: * in the variable ospeed. (Note: this holds B300, B9600, etc -- ie stuff 105: * out of <sgtty.h>.) 106: */ 107: 108: cmcostinit () 109: { 110: char *p; 111: 112: #define COST(x,e) (x ? (cost = 0, tputs (x, 1, e), cost) : BIG) 113: #define CMCOST(x,e) ((x == 0) ? BIG : (p = tgoto(x, 0, 0), COST(p ,e))) 114: 115: Wcm.cc_up = COST (Wcm.cm_up, evalcost); 116: Wcm.cc_down = COST (Wcm.cm_down, evalcost); 117: Wcm.cc_left = COST (Wcm.cm_left, evalcost); 118: Wcm.cc_right = COST (Wcm.cm_right, evalcost); 119: Wcm.cc_home = COST (Wcm.cm_home, evalcost); 120: Wcm.cc_cr = COST (Wcm.cm_cr, evalcost); 121: Wcm.cc_ll = COST (Wcm.cm_ll, evalcost); 122: Wcm.cc_tab = Wcm.cm_tabwidth ? COST (Wcm.cm_tab, evalcost) : BIG; 123: 124: /* 125: * These last three are actually minimum costs. When (if) they are 126: * candidates for the least-cost motion, the real cost is computed. 127: * (Note that "0" is the assumed to generate the minimum cost. 128: * While this is not necessarily true, I have yet to see a terminal 129: * for which is not; all the terminals that have variable-cost 130: * cursor motion seem to take straight numeric values. --ACT) 131: */ 132: 133: Wcm.cc_abs = CMCOST (Wcm.cm_abs, evalcost); 134: Wcm.cc_habs = CMCOST (Wcm.cm_habs, evalcost); 135: Wcm.cc_vabs = CMCOST (Wcm.cm_vabs, evalcost); 136: 137: #undef CMCOST 138: #undef COST 139: } 140: 141: /* 142: * Calculate the cost to move from (srcy, srcx) to (dsty, dstx) using 143: * up and down, and left and right, motions, and tabs. If doit is set 144: * actually perform the motion. 145: */ 146: 147: static 148: calccost (srcy, srcx, dsty, dstx, doit) 149: { 150: register int deltay, 151: deltax, 152: c, 153: totalcost; 154: int ntabs, 155: n2tabs, 156: tabx, 157: tab2x, 158: tabcost; 159: register char *p; 160: 161: /* If have just wrapped on a terminal with xn, 162: don't believe the cursor position: give up here 163: and force use of absolute positioning. */ 164: 165: if (curX == Wcm.cm_cols) 166: goto fail; 167: 168: totalcost = 0; 169: if ((deltay = dsty - srcy) == 0) 170: goto x; 171: if (deltay < 0) 172: p = Wcm.cm_up, c = Wcm.cc_up, deltay = -deltay; 173: else 174: p = Wcm.cm_down, c = Wcm.cc_down; 175: if (c == BIG) { /* caint get thar from here */ 176: if (doit) 177: printf ("OOPS"); 178: return c; 179: } 180: totalcost = c * deltay; 181: if (doit) 182: while (--deltay >= 0) 183: tputs (p, 1, cmputc); 184: x: 185: if ((deltax = dstx - srcx) == 0) 186: goto done; 187: if (deltax < 0) { 188: p = Wcm.cm_left, c = Wcm.cc_left, deltax = -deltax; 189: goto dodelta; /* skip all the tab junk */ 190: } 191: /* Tabs (the toughie) */ 192: if (Wcm.cc_tab >= BIG || !Wcm.cm_usetabs) 193: goto olddelta; /* forget it! */ 194: 195: /* 196: * ntabs is # tabs towards but not past dstx; n2tabs is one more 197: * (ie past dstx), but this is only valid if that is not past the 198: * right edge of the screen. We can check that at the same time 199: * as we figure out where we would be if we use the tabs (which 200: * we will put into tabx (for ntabs) and tab2x (for n2tabs)). 201: */ 202: 203: ntabs = deltax / Wcm.cm_tabwidth; 204: n2tabs = ntabs + 1; 205: tabx = (srcx / Wcm.cm_tabwidth + ntabs) * Wcm.cm_tabwidth; 206: tab2x = tabx + Wcm.cm_tabwidth; 207: 208: if (tab2x >= Wcm.cm_cols) /* too far (past edge) */ 209: n2tabs = 0; 210: 211: /* 212: * Now set tabcost to the cost for using ntabs, and c to the cost 213: * for using n2tabs, then pick the minimum. 214: */ 215: 216: /* cost for ntabs + cost for right motion */ 217: tabcost = ntabs ? ntabs * Wcm.cc_tab + (dstx - tabx) * Wcm.cc_right 218: : BIG; 219: 220: /* cost for n2tabs + cost for left motion */ 221: c = n2tabs ? n2tabs * Wcm.cc_tab + (tab2x - dstx) * Wcm.cc_left 222: : BIG; 223: 224: if (c < tabcost) /* then cheaper to overshoot & back up */ 225: ntabs = n2tabs, tabcost = c, tabx = tab2x; 226: 227: if (tabcost >= BIG) /* caint use tabs */ 228: goto newdelta; 229: 230: /* 231: * See if tabcost is less than just moving right 232: */ 233: 234: if (tabcost < (deltax * Wcm.cc_right)) { 235: totalcost += tabcost; /* use the tabs */ 236: if (doit) 237: while (--ntabs >= 0) 238: tputs (Wcm.cm_tab, 1, cmputc); 239: srcx = tabx; 240: } 241: 242: /* 243: * Now might as well just recompute the delta. 244: */ 245: 246: newdelta: 247: if ((deltax = dstx - srcx) == 0) 248: goto done; 249: olddelta: 250: if (deltax > 0) 251: p = Wcm.cm_right, c = Wcm.cc_right; 252: else 253: p = Wcm.cm_left, c = Wcm.cc_left, deltax = -deltax; 254: 255: dodelta: 256: if (c == BIG) { /* caint get thar from here */ 257: fail: 258: if (doit) 259: printf ("OOPS"); 260: return BIG; 261: } 262: totalcost += c * deltax; 263: if (doit) 264: while (--deltax >= 0) 265: tputs (p, 1, cmputc); 266: done: 267: return totalcost; 268: } 269: 270: losecursor () 271: { 272: curY = -1; 273: } 274: 275: #define USEREL 0 276: #define USEHOME 1 277: #define USELL 2 278: #define USECR 3 279: 280: cmgoto (row, col) 281: { 282: int homecost, 283: crcost, 284: llcost, 285: relcost, 286: directcost; 287: int use; 288: char *p, 289: *dcm; 290: 291: /* First the degenerate case */ 292: if (row == curY && col == curX) /* already there */ 293: return; 294: 295: if (curY >= 0 && curX >= 0) 296: { 297: /* 298: * Pick least-cost motions 299: */ 300: 301: relcost = calccost (curY, curX, row, col, 0); 302: use = USEREL; 303: if ((homecost = Wcm.cc_home) < BIG) 304: homecost += calccost (0, 0, row, col, 0); 305: if (homecost < relcost) 306: relcost = homecost, use = USEHOME; 307: if ((llcost = Wcm.cc_ll) < BIG) 308: llcost += calccost (Wcm.cm_rows - 1, 0, row, col, 0); 309: if (llcost < relcost) 310: relcost = llcost, use = USELL; 311: if ((crcost = Wcm.cc_cr) < BIG) { 312: if (Wcm.cm_autolf) 313: if (curY + 1 >= Wcm.cm_rows) 314: crcost = BIG; 315: else 316: crcost += calccost (curY + 1, 0, row, col, 0); 317: else 318: crcost += calccost (curY, 0, row, col, 0); 319: } 320: if (crcost < relcost) 321: relcost = crcost, use = USECR; 322: directcost = Wcm.cc_abs, dcm = Wcm.cm_abs; 323: if (row == curY && Wcm.cc_habs < BIG) 324: directcost = Wcm.cc_habs, dcm = Wcm.cm_habs; 325: else if (col == curX && Wcm.cc_vabs < BIG) 326: directcost = Wcm.cc_vabs, dcm = Wcm.cm_vabs; 327: } 328: else 329: { 330: directcost = 0, relcost = 100000; 331: dcm = Wcm.cm_abs; 332: } 333: 334: /* 335: * In the following comparison, the = in <= is because when the costs 336: * are the same, it looks nicer (I think) to move directly there. 337: */ 338: if (directcost <= relcost) 339: { 340: /* compute REAL direct cost */ 341: cost = 0; 342: p = dcm == Wcm.cm_habs ? tgoto (dcm, row, col) : 343: tgoto (dcm, col, row); 344: tputs (p, 1, evalcost); 345: if (cost <= relcost) 346: { /* really is cheaper */ 347: tputs (p, 1, cmputc); 348: curY = row, curX = col; 349: return; 350: } 351: } 352: 353: switch (use) 354: { 355: case USEHOME: 356: tputs (Wcm.cm_home, 1, cmputc); 357: curY = 0, curX = 0; 358: break; 359: 360: case USELL: 361: tputs (Wcm.cm_ll, 1, cmputc); 362: curY = Wcm.cm_rows - 1, curX = 0; 363: break; 364: 365: case USECR: 366: tputs (Wcm.cm_cr, 1, cmputc); 367: if (Wcm.cm_autolf) 368: curY++; 369: curX = 0; 370: break; 371: } 372: 373: (void) calccost (curY, curX, row, col, 1); 374: curY = row, curX = col; 375: } 376: 377: /* Clear out all terminal info. 378: Used before copying into it the info on the actual terminal. 379: */ 380: 381: Wcm_clear () 382: { 383: bzero (&Wcm, sizeof Wcm); 384: UP = 0; 385: BC = 0; 386: } 387: 388: /* 389: * Initialized stuff 390: * Return 0 if can do CM. 391: */ 392: 393: Wcm_init () 394: { 395: /* Check that we know the size of the screen.... */ 396: if (Wcm.cm_rows <= 0 || Wcm.cm_cols <= 0) 397: return - 1; 398: if (Wcm.cm_abs && !Wcm.cm_ds) 399: return 0; 400: /* Require up and left, and, if no absolute, down and right */ 401: if (!Wcm.cm_up || !Wcm.cm_left) 402: return - 1; 403: if (!Wcm.cm_abs && (!Wcm.cm_down || !Wcm.cm_right)) 404: return - 1; 405: return 0; 406: }