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