Annotation of src/usr.bin/mg/tty.c, Revision 1.23
1.23 ! kjell 1: /* $OpenBSD: tty.c,v 1.22 2005/06/14 18:14:40 kjell 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.6 millert 35: #include <term.h>
1.10 art 36: #include <signal.h>
1.1 deraadt 37:
1.14 millert 38: static int charcost(char *);
1.1 deraadt 39:
1.6 millert 40: static int cci;
41: static int insdel; /* Do we have both insert & delete line? */
42: static char *scroll_fwd; /* How to scroll forward. */
1.1 deraadt 43:
1.21 db 44: static void winchhandler(int);
1.10 art 45:
1.23 ! kjell 46: /* ARGSUSED */
1.10 art 47: static void
48: winchhandler(int sig)
49: {
1.15 deraadt 50: winch_flag = 1;
1.10 art 51: }
52:
1.1 deraadt 53: /*
54: * Initialize the terminal when the editor
55: * gets started up.
56: */
1.8 art 57: void
1.18 vincent 58: ttinit(void)
1.5 millert 59: {
1.6 millert 60: char *tv_stype, *p;
61:
1.16 deraadt 62: if ((tv_stype = getenv("TERM")) == NULL)
1.6 millert 63: panic("Could not determine terminal type!");
64:
1.5 millert 65: if (setupterm(tv_stype, 1, NULL)) {
1.6 millert 66: (void)asprintf(&p, "Unknown terminal type: %s", tv_stype);
1.2 millert 67: panic(p);
1.1 deraadt 68: }
1.6 millert 69:
1.10 art 70: signal(SIGWINCH, winchhandler);
1.15 deraadt 71: siginterrupt(SIGWINCH, 1);
72:
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 */
1.11 art 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 */
1.11 art 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.18 vincent 132: ttreinit(void)
1.5 millert 133: {
1.21 db 134: if (enter_ca_mode)
1.6 millert 135: /* enter application mode */
136: putpad(enter_ca_mode, 1);
1.21 db 137:
138: if (keypad_xmit)
1.6 millert 139: /* turn on keypad */
140: putpad(keypad_xmit, 1);
1.4 millert 141:
1.10 art 142: ttresize();
1.4 millert 143: }
144:
145: /*
1.9 mickey 146: * Clean up the terminal, in anticipation of a return to the command
147: * interpreter. This is a no-op on the ANSI display. On the SCALD display,
1.6 millert 148: * it sets the window back to half screen scrolling. Perhaps it should
149: * query the display for the increment, and put it back to what it was.
1.1 deraadt 150: */
1.8 art 151: void
1.18 vincent 152: tttidy(void)
1.5 millert 153: {
154: #ifdef XKEYS
155: ttykeymaptidy();
1.6 millert 156: #endif /* XKEYS */
157:
1.2 millert 158: /* set the term back to normal mode */
159: if (exit_ca_mode)
160: putpad(exit_ca_mode, 1);
1.1 deraadt 161: }
162:
163: /*
1.6 millert 164: * Move the cursor to the specified origin 0 row and column position. Try to
165: * optimize out extra moves; redisplay may have left the cursor in the right
1.1 deraadt 166: * location last time!
167: */
1.8 art 168: void
1.18 vincent 169: ttmove(int row, int 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.18 vincent 182: tteeol(void)
1.5 millert 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.18 vincent 200: tteeop(void)
1.5 millert 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.20 deraadt 225: ttbeep(void)
1.5 millert 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.18 vincent 238: ttinsl(int row, int bot, int nchunk)
1.5 millert 239: {
1.6 millert 240: int i, nl;
1.1 deraadt 241:
1.6 millert 242: /* Case of one line insert is special. */
243: if (row == bot) {
244: ttmove(row, 0);
1.2 millert 245: tteeol();
246: return;
247: }
248: if (change_scroll_region && scroll_reverse) {
1.5 millert 249: /* Use scroll region and back index */
1.2 millert 250: nl = bot - row;
1.5 millert 251: ttwindow(row, bot);
1.2 millert 252: ttmove(row, 0);
253: while (nchunk--)
254: putpad(scroll_reverse, nl);
255: ttnowindow();
256: return;
257: } else if (insdel) {
1.5 millert 258: ttmove(1 + bot - nchunk, 0);
1.2 millert 259: nl = nrow - ttrow;
260: if (parm_delete_line)
261: putpad(tgoto(parm_delete_line, 0, nchunk), nl);
262: else
1.6 millert 263: /* For all lines in the chunk... */
1.5 millert 264: for (i = 0; i < nchunk; i++)
1.2 millert 265: putpad(delete_line, nl);
266: ttmove(row, 0);
1.6 millert 267:
268: /* ttmove() changes ttrow */
269: nl = nrow - ttrow;
270:
1.2 millert 271: if (parm_insert_line)
272: putpad(tgoto(parm_insert_line, 0, nchunk), nl);
273: else
274: /* For all lines in the chunk */
1.5 millert 275: for (i = 0; i < nchunk; i++)
1.2 millert 276: putpad(insert_line, nl);
277: ttrow = HUGE;
278: ttcol = HUGE;
279: } else
280: panic("ttinsl: Can't insert/delete line");
1.1 deraadt 281: }
282:
283: /*
1.9 mickey 284: * Delete nchunk line(s) from "row", replacing the bottom line on the
285: * screen with a blank line. Unless we're using the scrolling region,
286: * this is done with crafty sequences of insert and delete lines. The
1.21 db 287: * presence of the echo area makes a boundary condition go away.
1.1 deraadt 288: */
1.8 art 289: void
1.21 db 290: ttdell(int row, int bot, int nchunk)
1.1 deraadt 291: {
1.6 millert 292: int i, nl;
1.1 deraadt 293:
1.6 millert 294: /* One line special cases */
295: if (row == bot) {
1.2 millert 296: ttmove(row, 0);
297: tteeol();
298: return;
299: }
1.6 millert 300: /* scrolling region */
301: if (change_scroll_region) {
1.2 millert 302: nl = bot - row;
303: ttwindow(row, bot);
304: ttmove(bot, 0);
305: while (nchunk--)
306: putpad(scroll_fwd, nl);
307: ttnowindow();
1.6 millert 308: /* else use insert/delete line */
1.5 millert 309: } else if (insdel) {
1.6 millert 310: ttmove(row, 0);
1.2 millert 311: nl = nrow - ttrow;
312: if (parm_delete_line)
313: putpad(tgoto(parm_delete_line, 0, nchunk), nl);
314: else
1.5 millert 315: /* For all lines in the chunk */
316: for (i = 0; i < nchunk; i++)
1.2 millert 317: putpad(delete_line, nl);
1.5 millert 318: ttmove(1 + bot - nchunk, 0);
1.6 millert 319:
320: /* ttmove() changes ttrow */
321: nl = nrow - ttrow;
1.2 millert 322: if (parm_insert_line)
323: putpad(tgoto(parm_insert_line, 0, nchunk), nl);
324: else
325: /* For all lines in the chunk */
1.5 millert 326: for (i = 0; i < nchunk; i++)
1.2 millert 327: putpad(insert_line, nl);
328: ttrow = HUGE;
329: ttcol = HUGE;
330: } else
331: panic("ttdell: Can't insert/delete line");
1.1 deraadt 332: }
333:
334: /*
1.9 mickey 335: * This routine sets the scrolling window on the display to go from line
336: * "top" to line "bot" (origin 0, inclusive). The caller checks for the
337: * pathological 1-line scroll window which doesn't work right and avoids
338: * it. The "ttrow" and "ttcol" variables are set to a crazy value to
339: * ensure that the next call to "ttmove" does not turn into a no-op (the
1.6 millert 340: * window adjustment moves the cursor).
1.1 deraadt 341: */
1.8 art 342: void
1.21 db 343: ttwindow(int top, int 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.20 deraadt 363: ttnowindow(void)
1.1 deraadt 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.21 db 383: ttcolor(int color)
1.2 millert 384: {
385: if (color != tthue) {
1.6 millert 386: if (color == CTEXT)
387: /* normal video */
1.5 millert 388: putpad(exit_standout_mode, 1);
1.6 millert 389: else if (color == CMODE)
390: /* reverse video */
1.5 millert 391: putpad(enter_standout_mode, 1);
1.6 millert 392: /* save the color */
393: tthue = color;
1.1 deraadt 394: }
395: }
396:
397: /*
1.9 mickey 398: * This routine is called by the "refresh the screen" command to try
1.11 art 399: * to resize the display. Look in "window.c" to see how
1.6 millert 400: * the caller deals with a change.
1.11 art 401: *
402: * We use `newrow' and `newcol' so vtresize() know the difference between the
403: * new and old settings.
1.1 deraadt 404: */
1.8 art 405: void
1.18 vincent 406: ttresize(void)
1.5 millert 407: {
1.11 art 408: int newrow = 0, newcol = 0;
409:
1.10 art 410: #ifdef TIOCGWINSZ
411: struct winsize winsize;
412:
413: if (ioctl(0, TIOCGWINSZ, (char *) &winsize) == 0) {
1.11 art 414: newrow = winsize.ws_row;
415: newcol = winsize.ws_col;
416: }
1.10 art 417: #endif
1.11 art 418: if ((newrow <= 0 || newcol <= 0) &&
419: ((newrow = lines) <= 0 || (newcol = columns) <= 0)) {
420: newrow = 24;
421: newcol = 80;
422: }
1.17 vincent 423: if (vtresize(1, newrow, newcol) != TRUE)
1.11 art 424: panic("vtresize failed");
1.1 deraadt 425: }
426:
1.6 millert 427: /*
1.9 mickey 428: * fake char output for charcost()
1.6 millert 429: */
1.5 millert 430: /* ARGSUSED */
1.6 millert 431: static int
1.19 art 432: fakec(int c)
1.1 deraadt 433: {
434: cci++;
1.21 db 435: return (0);
1.1 deraadt 436: }
437:
438: /* calculate the cost of doing string s */
1.6 millert 439: static int
1.18 vincent 440: charcost(char *s)
1.5 millert 441: {
1.6 millert 442: int cci = 0;
1.1 deraadt 443:
1.2 millert 444: tputs(s, nrow, fakec);
445: return (cci);
1.1 deraadt 446: }