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