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