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