Annotation of src/usr.bin/mg/display.c, Revision 1.1
1.1 ! deraadt 1: /*
! 2: * The functions in this file handle redisplay. The
! 3: * redisplay system knows almost nothing about the editing
! 4: * process; the editing functions do, however, set some
! 5: * hints to eliminate a lot of the grinding. There is more
! 6: * that can be done; the "vtputc" interface is a real
! 7: * pig. Two conditional compilation flags; the GOSLING
! 8: * flag enables dynamic programming redisplay, using the
! 9: * algorithm published by Jim Gosling in SIGOA. The MEMMAP
! 10: * changes things around for memory mapped video. With
! 11: * both off, the terminal is a VT52.
! 12: */
! 13: #include "def.h"
! 14: #include "kbd.h"
! 15:
! 16: /*
! 17: * You can change these back to the types
! 18: * implied by the name if you get tight for space. If you
! 19: * make both of them "int" you get better code on the VAX.
! 20: * They do nothing if this is not Gosling redisplay, except
! 21: * for change the size of a structure that isn't used.
! 22: * A bit of a cheat.
! 23: */
! 24: /* These defines really belong in sysdef.h */
! 25: #ifndef XCHAR
! 26: # define XCHAR int
! 27: # define XSHORT int
! 28: #endif
! 29:
! 30: #ifdef STANDOUT_GLITCH
! 31: extern int SG; /* number of standout glitches */
! 32: #endif
! 33:
! 34: /*
! 35: * A video structure always holds
! 36: * an array of characters whose length is equal to
! 37: * the longest line possible. Only some of this is
! 38: * used if "ncol" isn't the same as "NCOL".
! 39: */
! 40: typedef struct {
! 41: short v_hash; /* Hash code, for compares. */
! 42: short v_flag; /* Flag word. */
! 43: short v_color; /* Color of the line. */
! 44: XSHORT v_cost; /* Cost of display. */
! 45: char v_text[NCOL]; /* The actual characters. */
! 46: } VIDEO;
! 47:
! 48: #define VFCHG 0x0001 /* Changed. */
! 49: #define VFHBAD 0x0002 /* Hash and cost are bad. */
! 50: #define VFEXT 0x0004 /* extended line (beond ncol) */
! 51:
! 52: /*
! 53: * SCORE structures hold the optimal
! 54: * trace trajectory, and the cost of redisplay, when
! 55: * the dynamic programming redisplay code is used.
! 56: * If no fancy redisplay, this isn't used. The trace index
! 57: * fields can be "char", and the score a "short", but
! 58: * this makes the code worse on the VAX.
! 59: */
! 60: typedef struct {
! 61: XCHAR s_itrace; /* "i" index for track back. */
! 62: XCHAR s_jtrace; /* "j" index for trace back. */
! 63: XSHORT s_cost; /* Display cost. */
! 64: } SCORE;
! 65:
! 66: int sgarbf = TRUE; /* TRUE if screen is garbage. */
! 67: int vtrow = 0; /* Virtual cursor row. */
! 68: int vtcol = 0; /* Virtual cursor column. */
! 69: int tthue = CNONE; /* Current color. */
! 70: int ttrow = HUGE; /* Physical cursor row. */
! 71: int ttcol = HUGE; /* Physical cursor column. */
! 72: int tttop = HUGE; /* Top of scroll region. */
! 73: int ttbot = HUGE; /* Bottom of scroll region. */
! 74: int lbound = 0; /* leftmost bound of the current line */
! 75: /* being displayed */
! 76:
! 77: VIDEO *vscreen[NROW-1]; /* Edge vector, virtual. */
! 78: VIDEO *pscreen[NROW-1]; /* Edge vector, physical. */
! 79: VIDEO video[2*(NROW-1)]; /* Actual screen data. */
! 80: VIDEO blanks; /* Blank line image. */
! 81:
! 82: /*
! 83: * Some predeclerations to make ANSI compilers happy
! 84: */
! 85: VOID vtinit();
! 86: VOID vttidy();
! 87: VOID vtmove();
! 88: VOID vtputc();
! 89: VOID vtpute();
! 90: VOID vteeol();
! 91: VOID update();
! 92: VOID updext();
! 93: VOID ucopy();
! 94: VOID uline();
! 95: VOID modeline();
! 96: VOID hash();
! 97: VOID setscores();
! 98: VOID traceback();
! 99:
! 100: #ifdef GOSLING
! 101: /*
! 102: * This matrix is written as an array because
! 103: * we do funny things in the "setscores" routine, which
! 104: * is very compute intensive, to make the subscripts go away.
! 105: * It would be "SCORE score[NROW][NROW]" in old speak.
! 106: * Look at "setscores" to understand what is up.
! 107: */
! 108: SCORE score[NROW*NROW];
! 109: #endif
! 110:
! 111: /*
! 112: * Initialize the data structures used
! 113: * by the display code. The edge vectors used
! 114: * to access the screens are set up. The operating
! 115: * system's terminal I/O channel is set up. Fill the
! 116: * "blanks" array with ASCII blanks. The rest is done
! 117: * at compile time. The original window is marked
! 118: * as needing full update, and the physical screen
! 119: * is marked as garbage, so all the right stuff happens
! 120: * on the first call to redisplay.
! 121: */
! 122: VOID
! 123: vtinit() {
! 124: register VIDEO *vp;
! 125: register int i;
! 126:
! 127: ttopen();
! 128: ttinit();
! 129: vp = &video[0];
! 130: for (i=0; i<NROW-1; ++i) {
! 131: vscreen[i] = vp;
! 132: ++vp;
! 133: pscreen[i] = vp;
! 134: ++vp;
! 135: }
! 136: blanks.v_color = CTEXT;
! 137: for (i=0; i<NCOL; ++i)
! 138: blanks.v_text[i] = ' ';
! 139: }
! 140:
! 141: /*
! 142: * Tidy up the virtual display system
! 143: * in anticipation of a return back to the host
! 144: * operating system. Right now all we do is position
! 145: * the cursor to the last line, erase the line, and
! 146: * close the terminal channel.
! 147: */
! 148: VOID
! 149: vttidy() {
! 150: ttcolor(CTEXT);
! 151: ttnowindow(); /* No scroll window. */
! 152: ttmove(nrow-1, 0); /* Echo line. */
! 153: tteeol();
! 154: tttidy();
! 155: ttflush();
! 156: ttclose();
! 157: }
! 158:
! 159: /*
! 160: * Move the virtual cursor to an origin
! 161: * 0 spot on the virtual display screen. I could
! 162: * store the column as a character pointer to the spot
! 163: * on the line, which would make "vtputc" a little bit
! 164: * more efficient. No checking for errors.
! 165: */
! 166: VOID
! 167: vtmove(row, col) {
! 168: vtrow = row;
! 169: vtcol = col;
! 170: }
! 171:
! 172: /*
! 173: * Write a character to the virtual display,
! 174: * dealing with long lines and the display of unprintable
! 175: * things like control characters. Also expand tabs every 8
! 176: * columns. This code only puts printing characters into
! 177: * the virtual display image. Special care must be taken when
! 178: * expanding tabs. On a screen whose width is not a multiple
! 179: * of 8, it is possible for the virtual cursor to hit the
! 180: * right margin before the next tab stop is reached. This
! 181: * makes the tab code loop if you are not careful.
! 182: * Three guesses how we found this.
! 183: */
! 184: VOID
! 185: vtputc(c) register int c; {
! 186: register VIDEO *vp;
! 187:
! 188: vp = vscreen[vtrow];
! 189: if (vtcol >= ncol)
! 190: vp->v_text[ncol-1] = '$';
! 191: else if (c == '\t'
! 192: #ifdef NOTAB
! 193: && !(curbp->b_flag & BFNOTAB)
! 194: #endif
! 195: ) {
! 196: do {
! 197: vtputc(' ');
! 198: } while (vtcol<ncol && (vtcol&0x07)!=0);
! 199: } else if (ISCTRL(c)) {
! 200: vtputc('^');
! 201: vtputc(CCHR(c));
! 202: } else
! 203: vp->v_text[vtcol++] = c;
! 204: }
! 205:
! 206: /* Put a character to the virtual screen in an extended line. If we are
! 207: * not yet on left edge, don't print it yet. Check for overflow on
! 208: * the right margin.
! 209: */
! 210: VOID
! 211: vtpute(c)
! 212: int c;
! 213: {
! 214: register VIDEO *vp;
! 215:
! 216: vp = vscreen[vtrow];
! 217:
! 218: if (vtcol >= ncol) vp->v_text[ncol - 1] = '$';
! 219: else if (c == '\t'
! 220: #ifdef NOTAB
! 221: && !(curbp->b_flag & BFNOTAB)
! 222: #endif
! 223: ) {
! 224: do {
! 225: vtpute(' ');
! 226: }
! 227: while (((vtcol + lbound)&0x07) != 0 && vtcol < ncol);
! 228: } else if (ISCTRL(c) != FALSE) {
! 229: vtpute('^');
! 230: vtpute(CCHR(c));
! 231: } else {
! 232: if (vtcol >= 0) vp->v_text[vtcol] = c;
! 233: ++vtcol;
! 234: }
! 235: }
! 236:
! 237: /* Erase from the end of the
! 238: * software cursor to the end of the
! 239: * line on which the software cursor is
! 240: * located. The display routines will decide
! 241: * if a hardware erase to end of line command
! 242: * should be used to display this.
! 243: */
! 244: VOID
! 245: vteeol() {
! 246: register VIDEO *vp;
! 247:
! 248: vp = vscreen[vtrow];
! 249: while (vtcol < ncol)
! 250: vp->v_text[vtcol++] = ' ';
! 251: }
! 252:
! 253: /*
! 254: * Make sure that the display is
! 255: * right. This is a three part process. First,
! 256: * scan through all of the windows looking for dirty
! 257: * ones. Check the framing, and refresh the screen.
! 258: * Second, make sure that "currow" and "curcol" are
! 259: * correct for the current window. Third, make the
! 260: * virtual and physical screens the same.
! 261: */
! 262: VOID
! 263: update() {
! 264: register LINE *lp;
! 265: register WINDOW *wp;
! 266: register VIDEO *vp1;
! 267: VIDEO *vp2;
! 268: register int i;
! 269: register int j;
! 270: register int c;
! 271: register int hflag;
! 272: register int currow;
! 273: register int curcol;
! 274: register int offs;
! 275: register int size;
! 276: VOID traceback ();
! 277: VOID uline ();
! 278:
! 279: if (typeahead()) return;
! 280: if (sgarbf) { /* must update everything */
! 281: wp = wheadp;
! 282: while(wp != NULL) {
! 283: wp->w_flag |= WFMODE | WFHARD;
! 284: wp = wp->w_wndp;
! 285: }
! 286: }
! 287: hflag = FALSE; /* Not hard. */
! 288: wp = wheadp;
! 289: while (wp != NULL) {
! 290: if (wp->w_flag != 0) { /* Need update. */
! 291: if ((wp->w_flag&WFFORCE) == 0) {
! 292: lp = wp->w_linep;
! 293: for (i=0; i<wp->w_ntrows; ++i) {
! 294: if (lp == wp->w_dotp)
! 295: goto out;
! 296: if (lp == wp->w_bufp->b_linep)
! 297: break;
! 298: lp = lforw(lp);
! 299: }
! 300: }
! 301: i = wp->w_force; /* Reframe this one. */
! 302: if (i > 0) {
! 303: --i;
! 304: if (i >= wp->w_ntrows)
! 305: i = wp->w_ntrows-1;
! 306: } else if (i < 0) {
! 307: i += wp->w_ntrows;
! 308: if (i < 0)
! 309: i = 0;
! 310: } else
! 311: i = wp->w_ntrows/2;
! 312: lp = wp->w_dotp;
! 313: while (i!=0 && lback(lp)!=wp->w_bufp->b_linep) {
! 314: --i;
! 315: lp = lback(lp);
! 316: }
! 317: wp->w_linep = lp;
! 318: wp->w_flag |= WFHARD; /* Force full. */
! 319: out:
! 320: lp = wp->w_linep; /* Try reduced update. */
! 321: i = wp->w_toprow;
! 322: if ((wp->w_flag&~WFMODE) == WFEDIT) {
! 323: while (lp != wp->w_dotp) {
! 324: ++i;
! 325: lp = lforw(lp);
! 326: }
! 327: vscreen[i]->v_color = CTEXT;
! 328: vscreen[i]->v_flag |= (VFCHG|VFHBAD);
! 329: vtmove(i, 0);
! 330: for (j=0; j<llength(lp); ++j)
! 331: vtputc(lgetc(lp, j));
! 332: vteeol();
! 333: } else if ((wp->w_flag&(WFEDIT|WFHARD)) != 0) {
! 334: hflag = TRUE;
! 335: while (i < wp->w_toprow+wp->w_ntrows) {
! 336: vscreen[i]->v_color = CTEXT;
! 337: vscreen[i]->v_flag |= (VFCHG|VFHBAD);
! 338: vtmove(i, 0);
! 339: if (lp != wp->w_bufp->b_linep) {
! 340: for (j=0; j<llength(lp); ++j)
! 341: vtputc(lgetc(lp, j));
! 342: lp = lforw(lp);
! 343: }
! 344: vteeol();
! 345: ++i;
! 346: }
! 347: }
! 348: if ((wp->w_flag&WFMODE) != 0)
! 349: modeline(wp);
! 350: wp->w_flag = 0;
! 351: wp->w_force = 0;
! 352: }
! 353: wp = wp->w_wndp;
! 354: }
! 355: lp = curwp->w_linep; /* Cursor location. */
! 356: currow = curwp->w_toprow;
! 357: while (lp != curwp->w_dotp) {
! 358: ++currow;
! 359: lp = lforw(lp);
! 360: }
! 361: curcol = 0;
! 362: i = 0;
! 363: while (i < curwp->w_doto) {
! 364: c = lgetc(lp, i++);
! 365: if (c == '\t'
! 366: #ifdef NOTAB
! 367: && !(curbp->b_flag & BFNOTAB)
! 368: #endif
! 369: ) curcol |= 0x07;
! 370: else if (ISCTRL(c) != FALSE)
! 371: ++curcol;
! 372: ++curcol;
! 373: }
! 374: if (curcol >= ncol - 1) { /* extended line. */
! 375: /* flag we are extended and changed */
! 376: vscreen[currow]->v_flag |= VFEXT | VFCHG;
! 377: updext(currow, curcol); /* and output extended line */
! 378: } else lbound = 0; /* not extended line */
! 379:
! 380: /* make sure no lines need to be de-extended because the cursor is
! 381: no longer on them */
! 382:
! 383: wp = wheadp;
! 384:
! 385: while (wp != NULL) {
! 386: lp = wp->w_linep;
! 387: i = wp->w_toprow;
! 388: while (i < wp->w_toprow + wp->w_ntrows) {
! 389: if (vscreen[i]->v_flag & VFEXT) {
! 390: /* always flag extended lines as changed */
! 391: vscreen[i]->v_flag |= VFCHG;
! 392: if ((wp != curwp) || (lp != wp->w_dotp) ||
! 393: (curcol < ncol - 1)) {
! 394: vtmove(i, 0);
! 395: for (j = 0; j < llength(lp); ++j)
! 396: vtputc(lgetc(lp, j));
! 397: vteeol();
! 398: /* this line no longer is extended */
! 399: vscreen[i]->v_flag &= ~VFEXT;
! 400: }
! 401: }
! 402: lp = lforw(lp);
! 403: ++i;
! 404: }
! 405: /* if garbaged then fix up mode lines */
! 406: if (sgarbf != FALSE) vscreen[i]->v_flag |= VFCHG;
! 407: /* and onward to the next window */
! 408: wp = wp->w_wndp;
! 409: }
! 410:
! 411: if (sgarbf != FALSE) { /* Screen is garbage. */
! 412: sgarbf = FALSE; /* Erase-page clears */
! 413: epresf = FALSE; /* the message area. */
! 414: tttop = HUGE; /* Forget where you set */
! 415: ttbot = HUGE; /* scroll region. */
! 416: tthue = CNONE; /* Color unknown. */
! 417: ttmove(0, 0);
! 418: tteeop();
! 419: for (i=0; i<nrow-1; ++i) {
! 420: uline(i, vscreen[i], &blanks);
! 421: ucopy(vscreen[i], pscreen[i]);
! 422: }
! 423: ttmove(currow, curcol - lbound);
! 424: ttflush();
! 425: return;
! 426: }
! 427: #ifdef GOSLING
! 428: if (hflag != FALSE) { /* Hard update? */
! 429: for (i=0; i<nrow-1; ++i) { /* Compute hash data. */
! 430: hash(vscreen[i]);
! 431: hash(pscreen[i]);
! 432: }
! 433: offs = 0; /* Get top match. */
! 434: while (offs != nrow-1) {
! 435: vp1 = vscreen[offs];
! 436: vp2 = pscreen[offs];
! 437: if (vp1->v_color != vp2->v_color
! 438: || vp1->v_hash != vp2->v_hash)
! 439: break;
! 440: uline(offs, vp1, vp2);
! 441: ucopy(vp1, vp2);
! 442: ++offs;
! 443: }
! 444: if (offs == nrow-1) { /* Might get it all. */
! 445: ttmove(currow, curcol - lbound);
! 446: ttflush();
! 447: return;
! 448: }
! 449: size = nrow-1; /* Get bottom match. */
! 450: while (size != offs) {
! 451: vp1 = vscreen[size-1];
! 452: vp2 = pscreen[size-1];
! 453: if (vp1->v_color != vp2->v_color
! 454: || vp1->v_hash != vp2->v_hash)
! 455: break;
! 456: uline(size-1, vp1, vp2);
! 457: ucopy(vp1, vp2);
! 458: --size;
! 459: }
! 460: if ((size -= offs) == 0) /* Get screen size. */
! 461: panic("Illegal screen size in update");
! 462: setscores(offs, size); /* Do hard update. */
! 463: traceback(offs, size, size, size);
! 464: for (i=0; i<size; ++i)
! 465: ucopy(vscreen[offs+i], pscreen[offs+i]);
! 466: ttmove(currow, curcol - lbound);
! 467: ttflush();
! 468: return;
! 469: }
! 470: #endif
! 471: for (i=0; i<nrow-1; ++i) { /* Easy update. */
! 472: vp1 = vscreen[i];
! 473: vp2 = pscreen[i];
! 474: if ((vp1->v_flag&VFCHG) != 0) {
! 475: uline(i, vp1, vp2);
! 476: ucopy(vp1, vp2);
! 477: }
! 478: }
! 479: ttmove(currow, curcol - lbound);
! 480: ttflush();
! 481: }
! 482:
! 483: /*
! 484: * Update a saved copy of a line,
! 485: * kept in a VIDEO structure. The "vvp" is
! 486: * the one in the "vscreen". The "pvp" is the one
! 487: * in the "pscreen". This is called to make the
! 488: * virtual and physical screens the same when
! 489: * display has done an update.
! 490: */
! 491: VOID
! 492: ucopy(vvp, pvp) register VIDEO *vvp; register VIDEO *pvp; {
! 493:
! 494: vvp->v_flag &= ~VFCHG; /* Changes done. */
! 495: pvp->v_flag = vvp->v_flag; /* Update model. */
! 496: pvp->v_hash = vvp->v_hash;
! 497: pvp->v_cost = vvp->v_cost;
! 498: pvp->v_color = vvp->v_color;
! 499: bcopy(vvp->v_text, pvp->v_text, ncol);
! 500: }
! 501:
! 502: /* updext: update the extended line which the cursor is currently
! 503: * on at a column greater than the terminal width. The line
! 504: * will be scrolled right or left to let the user see where
! 505: * the cursor is
! 506: */
! 507: VOID
! 508: updext(currow, curcol)
! 509: int currow, curcol;
! 510: {
! 511: register LINE *lp; /* pointer to current line */
! 512: register int j; /* index into line */
! 513:
! 514: /* calculate what column the left bound should be */
! 515: /* (force cursor into middle half of screen) */
! 516: lbound = curcol - (curcol % (ncol>>1)) - (ncol>>2);
! 517: /* scan through the line outputing characters to the virtual screen */
! 518: /* once we reach the left edge */
! 519: vtmove(currow, -lbound); /* start scanning offscreen */
! 520: lp = curwp->w_dotp; /* line to output */
! 521: for (j=0; j<llength(lp); ++j) /* until the end-of-line */
! 522: vtpute(lgetc(lp, j));
! 523: vteeol(); /* truncate the virtual line */
! 524: vscreen[currow]->v_text[0] = '$'; /* and put a '$' in column 1 */
! 525: }
! 526:
! 527: /*
! 528: * Update a single line. This routine only
! 529: * uses basic functionality (no insert and delete character,
! 530: * but erase to end of line). The "vvp" points at the VIDEO
! 531: * structure for the line on the virtual screen, and the "pvp"
! 532: * is the same for the physical screen. Avoid erase to end of
! 533: * line when updating CMODE color lines, because of the way that
! 534: * reverse video works on most terminals.
! 535: */
! 536: VOID uline(row, vvp, pvp) VIDEO *vvp; VIDEO *pvp; {
! 537: #ifdef MEMMAP
! 538: putline(row+1, 1, &vvp->v_text[0]);
! 539: #else
! 540: register char *cp1;
! 541: register char *cp2;
! 542: register char *cp3;
! 543: char *cp4;
! 544: char *cp5;
! 545: register int nbflag;
! 546:
! 547: if (vvp->v_color != pvp->v_color) { /* Wrong color, do a */
! 548: ttmove(row, 0); /* full redraw. */
! 549: #ifdef STANDOUT_GLITCH
! 550: if (pvp->v_color != CTEXT && SG >= 0) tteeol();
! 551: #endif
! 552: ttcolor(vvp->v_color);
! 553: #ifdef STANDOUT_GLITCH
! 554: cp1 = &vvp->v_text[SG > 0 ? SG : 0];
! 555: /* the odd code for SG==0 is to avoid putting the invisable
! 556: * glitch character on the next line.
! 557: * (Hazeltine executive 80 model 30)
! 558: */
! 559: cp2 = &vvp->v_text[ncol - (SG >= 0 ? (SG!=0 ? SG : 1) : 0)];
! 560: #else
! 561: cp1 = &vvp->v_text[0];
! 562: cp2 = &vvp->v_text[ncol];
! 563: #endif
! 564: while (cp1 != cp2) {
! 565: ttputc(*cp1++);
! 566: ++ttcol;
! 567: }
! 568: #ifndef MOVE_STANDOUT
! 569: ttcolor(CTEXT);
! 570: #endif
! 571: return;
! 572: }
! 573: cp1 = &vvp->v_text[0]; /* Compute left match. */
! 574: cp2 = &pvp->v_text[0];
! 575: while (cp1!=&vvp->v_text[ncol] && cp1[0]==cp2[0]) {
! 576: ++cp1;
! 577: ++cp2;
! 578: }
! 579: if (cp1 == &vvp->v_text[ncol]) /* All equal. */
! 580: return;
! 581: nbflag = FALSE;
! 582: cp3 = &vvp->v_text[ncol]; /* Compute right match. */
! 583: cp4 = &pvp->v_text[ncol];
! 584: while (cp3[-1] == cp4[-1]) {
! 585: --cp3;
! 586: --cp4;
! 587: if (cp3[0] != ' ') /* Note non-blanks in */
! 588: nbflag = TRUE; /* the right match. */
! 589: }
! 590: cp5 = cp3; /* Is erase good? */
! 591: if (nbflag==FALSE && vvp->v_color==CTEXT) {
! 592: while (cp5!=cp1 && cp5[-1]==' ')
! 593: --cp5;
! 594: /* Alcyon hack */
! 595: if ((int)(cp3-cp5) <= tceeol)
! 596: cp5 = cp3;
! 597: }
! 598: /* Alcyon hack */
! 599: ttmove(row, (int)(cp1-&vvp->v_text[0]));
! 600: #ifdef STANDOUT_GLITCH
! 601: if (vvp->v_color != CTEXT && SG > 0) {
! 602: if(cp1 < &vvp->v_text[SG]) cp1 = &vvp->v_text[SG];
! 603: if(cp5 > &vvp->v_text[ncol-SG]) cp5 = &vvp->v_text[ncol-SG];
! 604: } else if (SG < 0)
! 605: #endif
! 606: ttcolor(vvp->v_color);
! 607: while (cp1 != cp5) {
! 608: ttputc(*cp1++);
! 609: ++ttcol;
! 610: }
! 611: if (cp5 != cp3) /* Do erase. */
! 612: tteeol();
! 613: #endif
! 614: }
! 615:
! 616: /*
! 617: * Redisplay the mode line for
! 618: * the window pointed to by the "wp".
! 619: * This is the only routine that has any idea
! 620: * of how the modeline is formatted. You can
! 621: * change the modeline format by hacking at
! 622: * this routine. Called by "update" any time
! 623: * there is a dirty window.
! 624: * Note that if STANDOUT_GLITCH is defined, first and last SG characters
! 625: * may never be seen.
! 626: */
! 627: VOID
! 628: modeline(wp) register WINDOW *wp; {
! 629: register int n;
! 630: register BUFFER *bp;
! 631: int mode;
! 632:
! 633: n = wp->w_toprow+wp->w_ntrows; /* Location. */
! 634: vscreen[n]->v_color = CMODE; /* Mode line color. */
! 635: vscreen[n]->v_flag |= (VFCHG|VFHBAD); /* Recompute, display. */
! 636: vtmove(n, 0); /* Seek to right line. */
! 637: bp = wp->w_bufp;
! 638: vtputc('-'); vtputc('-');
! 639: if ((bp->b_flag&BFCHG) != 0) { /* "*" if changed. */
! 640: vtputc('*'); vtputc('*');
! 641: } else {
! 642: vtputc('-'); vtputc('-');
! 643: }
! 644: vtputc('-');
! 645: n = 5;
! 646: n += vtputs("Mg: ");
! 647: if (bp->b_bname[0] != '\0')
! 648: n += vtputs(&(bp->b_bname[0]));
! 649: while (n < 42) { /* Pad out with blanks */
! 650: vtputc(' ');
! 651: ++n;
! 652: }
! 653: vtputc('(');
! 654: ++n;
! 655: for(mode=0;;) {
! 656: n += vtputs(bp->b_modes[mode]->p_name);
! 657: if(++mode > bp->b_nmodes) break;
! 658: vtputc('-');
! 659: ++n;
! 660: }
! 661: vtputc(')');
! 662: ++n;
! 663: while (n < ncol) { /* Pad out. */
! 664: vtputc('-');
! 665: ++n;
! 666: }
! 667: }
! 668: /*
! 669: * output a string to the mode line, report how long it was.
! 670: */
! 671: vtputs(s) register char *s; {
! 672: register int n = 0;
! 673:
! 674: while (*s != '\0') {
! 675: vtputc(*s++);
! 676: ++n;
! 677: }
! 678: return n;
! 679: }
! 680: #ifdef GOSLING
! 681: /*
! 682: * Compute the hash code for
! 683: * the line pointed to by the "vp". Recompute
! 684: * it if necessary. Also set the approximate redisplay
! 685: * cost. The validity of the hash code is marked by
! 686: * a flag bit. The cost understand the advantages
! 687: * of erase to end of line. Tuned for the VAX
! 688: * by Bob McNamara; better than it used to be on
! 689: * just about any machine.
! 690: */
! 691: VOID
! 692: hash(vp) register VIDEO *vp; {
! 693: register int i;
! 694: register int n;
! 695: register char *s;
! 696:
! 697: if ((vp->v_flag&VFHBAD) != 0) { /* Hash bad. */
! 698: s = &vp->v_text[ncol-1];
! 699: for (i=ncol; i!=0; --i, --s)
! 700: if (*s != ' ')
! 701: break;
! 702: n = ncol-i; /* Erase cheaper? */
! 703: if (n > tceeol)
! 704: n = tceeol;
! 705: vp->v_cost = i+n; /* Bytes + blanks. */
! 706: for (n=0; i!=0; --i, --s)
! 707: n = (n<<5) + n + *s;
! 708: vp->v_hash = n; /* Hash code. */
! 709: vp->v_flag &= ~VFHBAD; /* Flag as all done. */
! 710: }
! 711: }
! 712:
! 713: /*
! 714: * Compute the Insert-Delete
! 715: * cost matrix. The dynamic programming algorithm
! 716: * described by James Gosling is used. This code assumes
! 717: * that the line above the echo line is the last line involved
! 718: * in the scroll region. This is easy to arrange on the VT100
! 719: * because of the scrolling region. The "offs" is the origin 0
! 720: * offset of the first row in the virtual/physical screen that
! 721: * is being updated; the "size" is the length of the chunk of
! 722: * screen being updated. For a full screen update, use offs=0
! 723: * and size=nrow-1.
! 724: *
! 725: * Older versions of this code implemented the score matrix by
! 726: * a two dimensional array of SCORE nodes. This put all kinds of
! 727: * multiply instructions in the code! This version is written to
! 728: * use a linear array and pointers, and contains no multiplication
! 729: * at all. The code has been carefully looked at on the VAX, with
! 730: * only marginal checking on other machines for efficiency. In
! 731: * fact, this has been tuned twice! Bob McNamara tuned it even
! 732: * more for the VAX, which is a big issue for him because of
! 733: * the 66 line X displays.
! 734: *
! 735: * On some machines, replacing the "for (i=1; i<=size; ++i)" with
! 736: * i = 1; do { } while (++i <=size)" will make the code quite a
! 737: * bit better; but it looks ugly.
! 738: */
! 739: VOID
! 740: setscores(offs, size) {
! 741: register SCORE *sp;
! 742: SCORE *sp1;
! 743: register int tempcost;
! 744: register int bestcost;
! 745: register int j;
! 746: register int i;
! 747: register VIDEO **vp;
! 748: VIDEO **pp, **vbase, **pbase;
! 749:
! 750: vbase = &vscreen[offs-1]; /* By hand CSE's. */
! 751: pbase = &pscreen[offs-1];
! 752: score[0].s_itrace = 0; /* [0, 0] */
! 753: score[0].s_jtrace = 0;
! 754: score[0].s_cost = 0;
! 755: sp = &score[1]; /* Row 0, inserts. */
! 756: tempcost = 0;
! 757: vp = &vbase[1];
! 758: for (j=1; j<=size; ++j) {
! 759: sp->s_itrace = 0;
! 760: sp->s_jtrace = j-1;
! 761: tempcost += tcinsl;
! 762: tempcost += (*vp)->v_cost;
! 763: sp->s_cost = tempcost;
! 764: ++vp;
! 765: ++sp;
! 766: }
! 767: sp = &score[NROW]; /* Column 0, deletes. */
! 768: tempcost = 0;
! 769: for (i=1; i<=size; ++i) {
! 770: sp->s_itrace = i-1;
! 771: sp->s_jtrace = 0;
! 772: tempcost += tcdell;
! 773: sp->s_cost = tempcost;
! 774: sp += NROW;
! 775: }
! 776: sp1 = &score[NROW+1]; /* [1, 1]. */
! 777: pp = &pbase[1];
! 778: for (i=1; i<=size; ++i) {
! 779: sp = sp1;
! 780: vp = &vbase[1];
! 781: for (j=1; j<=size; ++j) {
! 782: sp->s_itrace = i-1;
! 783: sp->s_jtrace = j;
! 784: bestcost = (sp-NROW)->s_cost;
! 785: if (j != size) /* Cd(A[i])=0 @ Dis. */
! 786: bestcost += tcdell;
! 787: tempcost = (sp-1)->s_cost;
! 788: tempcost += (*vp)->v_cost;
! 789: if (i != size) /* Ci(B[j])=0 @ Dsj. */
! 790: tempcost += tcinsl;
! 791: if (tempcost < bestcost) {
! 792: sp->s_itrace = i;
! 793: sp->s_jtrace = j-1;
! 794: bestcost = tempcost;
! 795: }
! 796: tempcost = (sp-NROW-1)->s_cost;
! 797: if ((*pp)->v_color != (*vp)->v_color
! 798: || (*pp)->v_hash != (*vp)->v_hash)
! 799: tempcost += (*vp)->v_cost;
! 800: if (tempcost < bestcost) {
! 801: sp->s_itrace = i-1;
! 802: sp->s_jtrace = j-1;
! 803: bestcost = tempcost;
! 804: }
! 805: sp->s_cost = bestcost;
! 806: ++sp; /* Next column. */
! 807: ++vp;
! 808: }
! 809: ++pp;
! 810: sp1 += NROW; /* Next row. */
! 811: }
! 812: }
! 813:
! 814: /*
! 815: * Trace back through the dynamic programming cost
! 816: * matrix, and update the screen using an optimal sequence
! 817: * of redraws, insert lines, and delete lines. The "offs" is
! 818: * the origin 0 offset of the chunk of the screen we are about to
! 819: * update. The "i" and "j" are always started in the lower right
! 820: * corner of the matrix, and imply the size of the screen.
! 821: * A full screen traceback is called with offs=0 and i=j=nrow-1.
! 822: * There is some do-it-yourself double subscripting here,
! 823: * which is acceptable because this routine is much less compute
! 824: * intensive then the code that builds the score matrix!
! 825: */
! 826: VOID traceback(offs, size, i, j) {
! 827: register int itrace;
! 828: register int jtrace;
! 829: register int k;
! 830: register int ninsl;
! 831: register int ndraw;
! 832: register int ndell;
! 833:
! 834: if (i==0 && j==0) /* End of update. */
! 835: return;
! 836: itrace = score[(NROW*i) + j].s_itrace;
! 837: jtrace = score[(NROW*i) + j].s_jtrace;
! 838: if (itrace == i) { /* [i, j-1] */
! 839: ninsl = 0; /* Collect inserts. */
! 840: if (i != size)
! 841: ninsl = 1;
! 842: ndraw = 1;
! 843: while (itrace!=0 || jtrace!=0) {
! 844: if (score[(NROW*itrace) + jtrace].s_itrace != itrace)
! 845: break;
! 846: jtrace = score[(NROW*itrace) + jtrace].s_jtrace;
! 847: if (i != size)
! 848: ++ninsl;
! 849: ++ndraw;
! 850: }
! 851: traceback(offs, size, itrace, jtrace);
! 852: if (ninsl != 0) {
! 853: ttcolor(CTEXT);
! 854: ttinsl(offs+j-ninsl, offs+size-1, ninsl);
! 855: }
! 856: do { /* B[j], A[j] blank. */
! 857: k = offs+j-ndraw;
! 858: uline(k, vscreen[k], &blanks);
! 859: } while (--ndraw);
! 860: return;
! 861: }
! 862: if (jtrace == j) { /* [i-1, j] */
! 863: ndell = 0; /* Collect deletes. */
! 864: if (j != size)
! 865: ndell = 1;
! 866: while (itrace!=0 || jtrace!=0) {
! 867: if (score[(NROW*itrace) + jtrace].s_jtrace != jtrace)
! 868: break;
! 869: itrace = score[(NROW*itrace) + jtrace].s_itrace;
! 870: if (j != size)
! 871: ++ndell;
! 872: }
! 873: if (ndell != 0) {
! 874: ttcolor(CTEXT);
! 875: ttdell(offs+i-ndell, offs+size-1, ndell);
! 876: }
! 877: traceback(offs, size, itrace, jtrace);
! 878: return;
! 879: }
! 880: traceback(offs, size, itrace, jtrace);
! 881: k = offs+j-1;
! 882: uline(k, vscreen[k], pscreen[offs+i-1]);
! 883: }
! 884: #endif