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