Annotation of src/usr.bin/mg/tty.c, Revision 1.1
1.1 ! deraadt 1: /*
! 2: * Termcap/terminfo display driver
! 3: *
! 4: * Termcap is a terminal information database and routines to describe
! 5: * terminals on most UNIX systems. Many other systems have adopted
! 6: * this as a reasonable way to allow for widly varying and ever changing
! 7: * varieties of terminal types. This should be used where practical.
! 8: */
! 9: /* Known problems:
! 10: * If you have a terminal with no clear to end of screen and
! 11: * memory of lines below the ones visible on the screen, display
! 12: * will be wrong in some cases. I doubt that any such terminal
! 13: * was ever made, but I thought everyone with delete line would
! 14: * have clear to end of screen too...
! 15: *
! 16: * Code for terminals without clear to end of screen and/or clear
! 17: * to end of line has not been extensivly tested.
! 18: *
! 19: * Cost calculations are very rough. Costs of insert/delete line
! 20: * may be far from the truth. This is accentuated by display.c
! 21: * not knowing about multi-line insert/delete.
! 22: *
! 23: * Using scrolling region vs insert/delete line should probably
! 24: * be based on cost rather than the assuption that scrolling
! 25: * region operations look better.
! 26: */
! 27: #include "def.h"
! 28:
! 29: #define BEL 0x07 /* BEL character. */
! 30:
! 31: extern int ttrow;
! 32: extern int ttcol;
! 33: extern int tttop;
! 34: extern int ttbot;
! 35: extern int tthue;
! 36:
! 37: int tceeol; /* Costs are set later */
! 38: int tcinsl;
! 39: int tcdell;
! 40:
! 41: static int insdel; /* Do we have both insert & delete line? */
! 42:
! 43: #ifdef NO_RESIZE
! 44: static setttysize();
! 45: #endif
! 46:
! 47: char *tgetstr();
! 48: char *tgoto();
! 49: int ttputc();
! 50:
! 51: #define TCAPSLEN 1024
! 52:
! 53: char tcapbuf[TCAPSLEN];
! 54:
! 55: /* PC, UP, and BC are used by termlib, so must be extern and have these
! 56: * names unless you have a non-standard termlib.
! 57: */
! 58:
! 59: int LI; /* standard # lines */
! 60: char PC,
! 61: *CM,
! 62: *CE,
! 63: *UP,
! 64: *BC,
! 65: *IM, /* insert mode */
! 66: *IC, /* insert a single space */
! 67: *EI, /* end insert mode */
! 68: *DC,
! 69: *AL, /* add line */
! 70: *DL, /* del line */
! 71: *pAL, /* parameterized add line */
! 72: *pDL, /* parameterized delete line */
! 73: *TI, /* term init -- start using cursor motion */
! 74: *TE, /* term end --- end using cursor motion */
! 75: *SO,
! 76: *SE,
! 77: *CD,
! 78: *CS, /* set scroll region */
! 79: *SF, /* forw index (used with scroll region) */
! 80: *SR; /* back index (used with scroll region) */
! 81: #ifdef XKEYS
! 82: char *KS, *KE; /* enter keypad mode, exit keypad mode */
! 83: #endif
! 84: int SG; /* number of glitches, 0 for invisible, -1 for none */
! 85: /* (yes virginia, there are terminals with invisible glitches) */
! 86:
! 87: /*
! 88: * Initialize the terminal when the editor
! 89: * gets started up.
! 90: */
! 91: static char tcbuf[1024];
! 92:
! 93: ttinit() {
! 94: char *tv_stype;
! 95: char *t, *p, *tgetstr();
! 96: #ifndef gettermtype /* (avoid declaration if #define) */
! 97: char *gettermtype(); /* system dependent function to determin terminal type */
! 98: #endif
! 99:
! 100: if((tv_stype = gettermtype()) == NULL)
! 101: panic("Could not determine terminal type");
! 102: if((tgetent(tcbuf, tv_stype)) != 1) {
! 103: (VOID) strcpy(tcbuf, "Unknown terminal type ");
! 104: (VOID) strcat(tcbuf, tv_stype);
! 105: panic(tcbuf);
! 106: }
! 107:
! 108: p = tcapbuf;
! 109: t = tgetstr("pc", &p);
! 110: if(t) PC = *t;
! 111:
! 112: LI = tgetnum("li");
! 113: CD = tgetstr("cd", &p);
! 114: CM = tgetstr("cm", &p);
! 115: CE = tgetstr("ce", &p);
! 116: UP = tgetstr("up", &p);
! 117: BC = tgetstr("bc", &p);
! 118: IM = tgetstr("im", &p);
! 119: IC = tgetstr("ic", &p);
! 120: EI = tgetstr("ei", &p);
! 121: DC = tgetstr("dc", &p);
! 122: AL = tgetstr("al", &p);
! 123: DL = tgetstr("dl", &p);
! 124: pAL= tgetstr("AL", &p); /* parameterized insert and del. line */
! 125: pDL= tgetstr("DL", &p);
! 126: TI = tgetstr("ti", &p);
! 127: TE = tgetstr("te", &p);
! 128: SO = tgetstr("so", &p);
! 129: SE = tgetstr("se", &p);
! 130: CS = tgetstr("cs", &p); /* set scrolling region */
! 131: SF = tgetstr("sf", &p);
! 132: if(!SF || !*SF) { /* this is what GNU Emacs does */
! 133: SF = tgetstr("do", &p);
! 134: if(!SF || !*SF) {
! 135: SF = tgetstr("nl", &p);
! 136: if(!SF || !*SF) SF = "\n";
! 137: }
! 138: }
! 139: SR = tgetstr("sr", &p);
! 140: SG = tgetnum("sg"); /* standout glitch */
! 141: #ifdef XKEYS
! 142: KS = tgetstr("ks", &p); /* keypad start, keypad end */
! 143: KE = tgetstr("ke", &p);
! 144: #endif
! 145:
! 146: if(CM == NULL || UP == NULL)
! 147: panic("This terminal is to stupid to run MicroGnuEmacs\n");
! 148: ttresize(); /* set nrow & ncol */
! 149:
! 150: /* watch out for empty capabilities (sure to be wrong) */
! 151: if (CE && !*CE) CE = NULL;
! 152: if (CS && !*CS) CS = NULL;
! 153: if (SR && !*SR) SR = NULL;
! 154: if (AL && !*AL) AL = NULL;
! 155: if (DL && !*DL) DL = NULL;
! 156: if (pAL && !*pAL) pAL = NULL;
! 157: if (pDL && !*pDL) pDL = NULL;
! 158: if (CD && !*CD) CD = NULL;
! 159:
! 160: if(!CE) tceeol = ncol;
! 161: else tceeol = charcost(CE);
! 162:
! 163: /* Estimate cost of inserting a line */
! 164: if (CS && SR) tcinsl = charcost(CS)*2 + charcost(SR);
! 165: else if (pAL) tcinsl = charcost(pAL);
! 166: else if (AL) tcinsl = charcost(AL);
! 167: else tcinsl = NROW * NCOL; /* make this cost high enough */
! 168:
! 169: /* Estimate cost of deleting a line */
! 170: if (CS) tcdell = charcost(CS)*2 + charcost(SF);
! 171: else if (pDL) tcdell = charcost(pDL);
! 172: else if (DL) tcdell = charcost(DL);
! 173: else tcdell = NROW * NCOL; /* make this cost high enough */
! 174:
! 175: /* Flag to indicate that we can both insert and delete lines */
! 176: insdel = (AL || pAL) && (DL || pDL);
! 177:
! 178: if (p >= &tcapbuf[TCAPSLEN])
! 179: panic("Terminal description too big!\n");
! 180: if (TI && *TI) putpad(TI, 1); /* init the term */
! 181: }
! 182:
! 183: /*
! 184: * Clean up the terminal, in anticipation of
! 185: * a return to the command interpreter. This is a no-op
! 186: * on the ANSI display. On the SCALD display, it sets the
! 187: * window back to half screen scrolling. Perhaps it should
! 188: * query the display for the increment, and put it
! 189: * back to what it was.
! 190: */
! 191: tttidy() {
! 192: if (TE && *TE) putpad(TE, 1); /* set the term back to normal mode */
! 193: #ifdef XKEYS
! 194: ttykeymaptidy();
! 195: #endif
! 196: }
! 197:
! 198: /*
! 199: * Move the cursor to the specified
! 200: * origin 0 row and column position. Try to
! 201: * optimize out extra moves; redisplay may
! 202: * have left the cursor in the right
! 203: * location last time!
! 204: */
! 205: ttmove(row, col) {
! 206: char *tgoto();
! 207:
! 208: if (ttrow!=row || ttcol!=col) {
! 209: putpad(tgoto(CM, col, row), 1);
! 210: ttrow = row;
! 211: ttcol = col;
! 212: }
! 213: }
! 214:
! 215: /*
! 216: * Erase to end of line.
! 217: */
! 218: tteeol() {
! 219: if(CE) putpad(CE, 1);
! 220: else {
! 221: register int i=ncol-ttcol;
! 222: while(i--) ttputc(' ');
! 223: ttrow = ttcol = HUGE;
! 224: }
! 225: }
! 226:
! 227: /*
! 228: * Erase to end of page.
! 229: */
! 230: tteeop() {
! 231: if(CD) putpad(CD, nrow - ttrow);
! 232: else {
! 233: putpad(CE, 1);
! 234: if (insdel) ttdell(ttrow + 1, LI, LI - ttrow - 1);
! 235: else { /* do it by hand */
! 236: register int line;
! 237: for (line = ttrow + 1; line <= LI; ++line) {
! 238: ttmove(line, 0);
! 239: tteeol();
! 240: }
! 241: }
! 242: ttrow = ttcol = HUGE;
! 243: }
! 244: }
! 245:
! 246: /*
! 247: * Make a noise.
! 248: */
! 249: ttbeep() {
! 250: ttputc(BEL);
! 251: ttflush();
! 252: }
! 253:
! 254: /*
! 255: * Insert nchunk blank line(s) onto the
! 256: * screen, scrolling the last line on the
! 257: * screen off the bottom. Use the scrolling
! 258: * region if possible for a smoother display.
! 259: * If no scrolling region, use a set
! 260: * of insert and delete line sequences
! 261: */
! 262: ttinsl(row, bot, nchunk) {
! 263: register int i, nl;
! 264:
! 265: if (row == bot) { /* Case of one line insert is */
! 266: ttmove(row, 0); /* special */
! 267: tteeol();
! 268: return;
! 269: }
! 270: if (CS && SR) { /* Use scroll region and back index */
! 271: nl = bot - row;
! 272: ttwindow(row,bot);
! 273: ttmove(row, 0);
! 274: while (nchunk--) putpad(SR, nl);
! 275: ttnowindow();
! 276: return;
! 277: } else if (insdel) {
! 278: ttmove(1+bot-nchunk, 0);
! 279: nl = nrow - ttrow;
! 280: if (pDL) putpad(tgoto(pDL, 0, nchunk), nl);
! 281: else for (i=0; i<nchunk; i++) /* For all lines in the chunk */
! 282: putpad(DL, nl);
! 283: ttmove(row, 0);
! 284: nl = nrow - ttrow; /* ttmove() changes ttrow */
! 285: if (pAL) putpad(tgoto(pAL, 0, nchunk), nl);
! 286: else for (i=0; i<nchunk; i++) /* For all lines in the chunk */
! 287: putpad(AL, nl);
! 288: ttrow = HUGE;
! 289: ttcol = HUGE;
! 290: } else panic("ttinsl: Can't insert/delete line");
! 291: }
! 292:
! 293: /*
! 294: * Delete nchunk line(s) from "row", replacing the
! 295: * bottom line on the screen with a blank line.
! 296: * Unless we're using the scrolling region, this is
! 297: * done with a crafty sequences of insert and delete
! 298: * lines. The presence of the echo area makes a
! 299: * boundry condition go away.
! 300: */
! 301: ttdell(row, bot, nchunk)
! 302: {
! 303: register int i, nl;
! 304:
! 305: if (row == bot) { /* One line special case */
! 306: ttmove(row, 0);
! 307: tteeol();
! 308: return;
! 309: }
! 310: if (CS) { /* scrolling region */
! 311: nl = bot - row;
! 312: ttwindow(row, bot);
! 313: ttmove(bot, 0);
! 314: while (nchunk--) putpad(SF, nl);
! 315: ttnowindow();
! 316: }
! 317: else if(insdel) {
! 318: ttmove(row, 0); /* Else use insert/delete line */
! 319: nl = nrow - ttrow;
! 320: if (pDL) putpad(tgoto(pDL, 0, nchunk), nl);
! 321: else for (i=0; i<nchunk; i++) /* For all lines in the chunk */
! 322: putpad(DL, nl);
! 323: ttmove(1+bot-nchunk,0);
! 324: nl = nrow - ttrow; /* ttmove() changes ttrow */
! 325: if (pAL) putpad(tgoto(pAL, 0, nchunk), nl);
! 326: else for (i=0; i<nchunk; i++) /* For all lines in the chunk */
! 327: putpad(AL, nl);
! 328: ttrow = HUGE;
! 329: ttcol = HUGE;
! 330: } else panic("ttdell: Can't insert/delete line");
! 331: }
! 332:
! 333: /*
! 334: * This routine sets the scrolling window
! 335: * on the display to go from line "top" to line
! 336: * "bot" (origin 0, inclusive). The caller checks
! 337: * for the pathalogical 1 line scroll window that
! 338: * doesn't work right, and avoids it. The "ttrow"
! 339: * and "ttcol" variables are set to a crazy value
! 340: * to ensure that the next call to "ttmove" does
! 341: * not turn into a no-op (the window adjustment
! 342: * moves the cursor).
! 343: *
! 344: */
! 345: ttwindow(top, bot)
! 346: {
! 347: if (CS && (tttop!=top || ttbot!=bot)) {
! 348: putpad(tgoto(CS, bot, top), nrow - ttrow);
! 349: ttrow = HUGE; /* Unknown. */
! 350: ttcol = HUGE;
! 351: tttop = top; /* Remember region. */
! 352: ttbot = bot;
! 353: }
! 354: }
! 355:
! 356: /*
! 357: * Switch to full screen scroll. This is
! 358: * used by "spawn.c" just before is suspends the
! 359: * editor, and by "display.c" when it is getting ready
! 360: * to exit. This function gets to full screen scroll
! 361: * by telling the terminal to set a scrolling regin
! 362: * that is LI or nrow rows high, whichever is larger.
! 363: * This behavior seems to work right on systems
! 364: * where you can set your terminal size.
! 365: */
! 366: ttnowindow()
! 367: {
! 368: if (CS) {
! 369: putpad(tgoto(CS, (nrow > LI ? nrow : LI) - 1, 0), nrow - ttrow);
! 370: ttrow = HUGE; /* Unknown. */
! 371: ttcol = HUGE;
! 372: tttop = HUGE; /* No scroll region. */
! 373: ttbot = HUGE;
! 374: }
! 375: }
! 376:
! 377: /*
! 378: * Set the current writing color to the
! 379: * specified color. Watch for color changes that are
! 380: * not going to do anything (the color is already right)
! 381: * and don't send anything to the display.
! 382: * The rainbow version does this in putline.s on a
! 383: * line by line basis, so don't bother sending
! 384: * out the color shift.
! 385: */
! 386: ttcolor(color) register int color; {
! 387: if (color != tthue) {
! 388: if (color == CTEXT) { /* Normal video. */
! 389: putpad(SE, 1);
! 390: } else if (color == CMODE) { /* Reverse video. */
! 391: putpad(SO, 1);
! 392: }
! 393: tthue = color; /* Save the color. */
! 394: }
! 395: }
! 396:
! 397: /*
! 398: * This routine is called by the
! 399: * "refresh the screen" command to try and resize
! 400: * the display. The new size, which must be deadstopped
! 401: * to not exceed the NROW and NCOL limits, it stored
! 402: * back into "nrow" and "ncol". Display can always deal
! 403: * with a screen NROW by NCOL. Look in "window.c" to
! 404: * see how the caller deals with a change.
! 405: */
! 406: ttresize() {
! 407: setttysize(); /* found in "ttyio.c", */
! 408: /* ask OS for tty size */
! 409: if (nrow < 1) /* Check limits. */
! 410: nrow = 1;
! 411: else if (nrow > NROW)
! 412: nrow = NROW;
! 413: if (ncol < 1)
! 414: ncol = 1;
! 415: else if (ncol > NCOL)
! 416: ncol = NCOL;
! 417: }
! 418:
! 419: #ifdef NO_RESIZE
! 420: static setttysize() {
! 421: nrow = tgetnum("li");
! 422: ncol = tgetnum("co");
! 423: }
! 424: #endif
! 425:
! 426: static int cci;
! 427:
! 428: /*ARGSUSED*/
! 429: static int /* fake char output for charcost() */
! 430: fakec(c)
! 431: char c;
! 432: {
! 433: cci++;
! 434: }
! 435:
! 436: /* calculate the cost of doing string s */
! 437: charcost (s) char *s; {
! 438: cci = 0;
! 439:
! 440: tputs(s, nrow, fakec);
! 441: return cci;
! 442: }