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