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