Annotation of src/usr.bin/mg/tty.c, Revision 1.16
1.16 ! deraadt 1: /* $OpenBSD: tty.c,v 1.15 2002/02/21 00:02:04 deraadt 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.5 millert 55: ttinit()
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.5 millert 129: ttreinit()
130: {
1.4 millert 131: if (enter_ca_mode)
1.6 millert 132: /* enter application mode */
133: putpad(enter_ca_mode, 1);
1.4 millert 134: if (keypad_xmit)
1.6 millert 135: /* turn on keypad */
136: putpad(keypad_xmit, 1);
1.4 millert 137:
1.10 art 138: ttresize();
1.4 millert 139: }
140:
141: /*
1.9 mickey 142: * Clean up the terminal, in anticipation of a return to the command
143: * interpreter. This is a no-op on the ANSI display. On the SCALD display,
1.6 millert 144: * it sets the window back to half screen scrolling. Perhaps it should
145: * query the display for the increment, and put it back to what it was.
1.1 deraadt 146: */
1.8 art 147: void
1.5 millert 148: tttidy()
149: {
150: #ifdef XKEYS
151: ttykeymaptidy();
1.6 millert 152: #endif /* XKEYS */
153:
1.2 millert 154: /* set the term back to normal mode */
155: if (exit_ca_mode)
156: putpad(exit_ca_mode, 1);
1.1 deraadt 157: }
158:
159: /*
1.6 millert 160: * Move the cursor to the specified origin 0 row and column position. Try to
161: * optimize out extra moves; redisplay may have left the cursor in the right
1.1 deraadt 162: * location last time!
163: */
1.8 art 164: void
1.5 millert 165: ttmove(row, col)
1.6 millert 166: int row, col;
1.5 millert 167: {
168: if (ttrow != row || ttcol != col) {
169: putpad(tgoto(cursor_address, col, row), 1);
170: ttrow = row;
171: ttcol = col;
172: }
1.1 deraadt 173: }
174:
175: /*
176: * Erase to end of line.
177: */
1.8 art 178: void
1.5 millert 179: tteeol()
180: {
1.6 millert 181: int i;
1.2 millert 182:
183: if (clr_eol)
184: putpad(clr_eol, 1);
185: else {
1.6 millert 186: i = ncol - ttcol;
1.2 millert 187: while (i--)
188: ttputc(' ');
189: ttrow = ttcol = HUGE;
190: }
1.1 deraadt 191: }
192:
193: /*
194: * Erase to end of page.
195: */
1.8 art 196: void
1.5 millert 197: tteeop()
198: {
1.6 millert 199: int line;
1.2 millert 200:
201: if (clr_eos)
202: putpad(clr_eos, nrow - ttrow);
203: else {
204: putpad(clr_eol, 1);
205: if (insdel)
206: ttdell(ttrow + 1, lines, lines - ttrow - 1);
1.6 millert 207: else {
208: /* do it by hand */
1.2 millert 209: for (line = ttrow + 1; line <= lines; ++line) {
210: ttmove(line, 0);
211: tteeol();
212: }
213: }
214: ttrow = ttcol = HUGE;
1.1 deraadt 215: }
216: }
217:
218: /*
219: * Make a noise.
220: */
1.8 art 221: void
1.5 millert 222: ttbeep()
223: {
1.2 millert 224: putpad(bell, 1);
1.1 deraadt 225: ttflush();
226: }
227:
228: /*
1.9 mickey 229: * Insert nchunk blank line(s) onto the screen, scrolling the last line on
230: * the screen off the bottom. Use the scrolling region if possible for a
231: * smoother display. If there is no scrolling region, use a set of insert
1.6 millert 232: * and delete line sequences.
1.1 deraadt 233: */
1.8 art 234: void
1.5 millert 235: ttinsl(row, bot, nchunk)
1.6 millert 236: int row, bot, 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.5 millert 407: ttresize()
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: }
424: if (vtresize(1, newrow, newcol))
425: panic("vtresize failed");
426: nrow = newrow;
427: ncol = newcol;
1.1 deraadt 428: }
429:
1.6 millert 430: /*
1.9 mickey 431: * fake char output for charcost()
1.6 millert 432: */
1.5 millert 433: /* ARGSUSED */
1.6 millert 434: static int
1.1 deraadt 435: fakec(c)
1.6 millert 436: char c;
1.1 deraadt 437: {
438: cci++;
1.6 millert 439: return 0;
1.1 deraadt 440: }
441:
442: /* calculate the cost of doing string s */
1.6 millert 443: static int
1.5 millert 444: charcost(s)
1.6 millert 445: char *s;
1.5 millert 446: {
1.6 millert 447: int cci = 0;
1.1 deraadt 448:
1.2 millert 449: tputs(s, nrow, fakec);
450: return (cci);
1.1 deraadt 451: }