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