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