Annotation of src/usr.bin/mg/tty.c, Revision 1.7
1.7 ! niklas 1: /* $OpenBSD$ */
! 2:
1.1 deraadt 3: /*
1.2 millert 4: * Terminfo display driver
1.1 deraadt 5: *
1.2 millert 6: * Terminfo is a terminal information database and routines to describe
7: * terminals on most modern UNIX systems. Many other systems have adopted
1.1 deraadt 8: * this as a reasonable way to allow for widly varying and ever changing
9: * varieties of terminal types. This should be used where practical.
10: */
1.5 millert 11: /*
12: * Known problems: If you have a terminal with no clear to end of screen and
13: * memory of lines below the ones visible on the screen, display will be
14: * wrong in some cases. I doubt that any such terminal was ever made, but I
15: * thought everyone with delete line would have clear to end of screen too...
1.1 deraadt 16: *
1.5 millert 17: * Code for terminals without clear to end of screen and/or clear to end of line
18: * has not been extensivly tested.
1.1 deraadt 19: *
1.5 millert 20: * Cost calculations are very rough. Costs of insert/delete line may be far
21: * from the truth. This is accentuated by display.c not knowing about
22: * multi-line insert/delete.
1.1 deraadt 23: *
1.5 millert 24: * Using scrolling region vs insert/delete line should probably be based on cost
25: * rather than the assuption that scrolling region operations look better.
1.1 deraadt 26: */
27:
1.6 millert 28: #include "def.h"
1.1 deraadt 29:
1.6 millert 30: #include <term.h>
1.5 millert 31:
1.6 millert 32: #ifdef NO_RESIZE
33: static int setttysize __P((void));
34: #endif /* NO_RESIZE */
1.1 deraadt 35:
1.6 millert 36: static int charcost __P((char *));
1.1 deraadt 37:
1.6 millert 38: static int cci;
39: static int insdel; /* Do we have both insert & delete line? */
40: static char *scroll_fwd; /* How to scroll forward. */
1.1 deraadt 41:
42: /*
43: * Initialize the terminal when the editor
44: * gets started up.
45: */
1.5 millert 46: VOID
47: ttinit()
48: {
1.6 millert 49: char *tv_stype, *p;
50:
51: /* system dependent function to determine terminal type, if necessary. */
52: #ifndef gettermtype
53: char *gettermtype();
54: #endif /* gettermtype */
1.1 deraadt 55:
1.2 millert 56: if ((tv_stype = gettermtype()) == NULL)
1.6 millert 57: panic("Could not determine terminal type!");
58:
1.5 millert 59: if (setupterm(tv_stype, 1, NULL)) {
1.6 millert 60: (void)asprintf(&p, "Unknown terminal type: %s", tv_stype);
1.2 millert 61: panic(p);
1.1 deraadt 62: }
1.6 millert 63:
1.2 millert 64: scroll_fwd = scroll_forward;
1.6 millert 65: if (scroll_fwd == NULL || *scroll_fwd == '\0') {
1.2 millert 66: /* this is what GNU Emacs does */
67: scroll_fwd = parm_down_cursor;
1.6 millert 68: if (scroll_fwd == NULL || *scroll_fwd == '\0')
1.2 millert 69: scroll_fwd = "\n";
1.1 deraadt 70: }
1.6 millert 71:
72: if (cursor_address == NULL || cursor_up == NULL)
73: panic("This terminal is too stupid to run mg");
74:
75: /* set nrow & ncol */
76: ttresize();
1.1 deraadt 77:
1.2 millert 78: if (!clr_eol)
79: tceeol = ncol;
80: else
81: tceeol = charcost(clr_eol);
1.1 deraadt 82:
83: /* Estimate cost of inserting a line */
1.2 millert 84: if (change_scroll_region && scroll_reverse)
1.5 millert 85: tcinsl = charcost(change_scroll_region) * 2 +
1.6 millert 86: charcost(scroll_reverse);
1.2 millert 87: else if (parm_insert_line)
88: tcinsl = charcost(parm_insert_line);
89: else if (insert_line)
90: tcinsl = charcost(insert_line);
91: else
1.6 millert 92: /* make this cost high enough */
93: tcinsl = NROW * NCOL;
1.1 deraadt 94:
95: /* Estimate cost of deleting a line */
1.2 millert 96: if (change_scroll_region)
1.5 millert 97: tcdell = charcost(change_scroll_region) * 2 +
1.6 millert 98: charcost(scroll_fwd);
1.2 millert 99: else if (parm_delete_line)
100: tcdell = charcost(parm_delete_line);
101: else if (delete_line)
102: tcdell = charcost(delete_line);
1.5 millert 103: else
1.6 millert 104: /* make this cost high enough */
105: tcdell = NROW * NCOL;
1.1 deraadt 106:
107: /* Flag to indicate that we can both insert and delete lines */
1.6 millert 108: insdel = (insert_line || parm_insert_line) &&
109: (delete_line || parm_delete_line);
1.1 deraadt 110:
1.2 millert 111: if (enter_ca_mode)
1.6 millert 112: /* enter application mode */
113: putpad(enter_ca_mode, 1);
1.3 millert 114:
115: setttysize();
1.1 deraadt 116: }
117:
118: /*
1.4 millert 119: * Re-initialize the terminal when the editor is resumed.
120: * The keypad_xmit doesn't really belong here but...
121: */
1.5 millert 122: VOID
123: ttreinit()
124: {
1.4 millert 125: if (enter_ca_mode)
1.6 millert 126: /* enter application mode */
127: putpad(enter_ca_mode, 1);
1.4 millert 128: if (keypad_xmit)
1.6 millert 129: /* turn on keypad */
130: putpad(keypad_xmit, 1);
1.4 millert 131:
132: setttysize();
133: }
134:
135: /*
1.6 millert 136: * Clean up the terminal, in anticipation of a return to the command
137: * interpreter. This is a no-op on the ANSI display. On the SCALD display,
138: * it sets the window back to half screen scrolling. Perhaps it should
139: * query the display for the increment, and put it back to what it was.
1.1 deraadt 140: */
1.5 millert 141: VOID
142: tttidy()
143: {
144: #ifdef XKEYS
145: ttykeymaptidy();
1.6 millert 146: #endif /* XKEYS */
147:
1.2 millert 148: /* set the term back to normal mode */
149: if (exit_ca_mode)
150: putpad(exit_ca_mode, 1);
1.1 deraadt 151: }
152:
153: /*
1.6 millert 154: * Move the cursor to the specified origin 0 row and column position. Try to
155: * optimize out extra moves; redisplay may have left the cursor in the right
1.1 deraadt 156: * location last time!
157: */
1.5 millert 158: VOID
159: ttmove(row, col)
1.6 millert 160: int row, col;
1.5 millert 161: {
162: if (ttrow != row || ttcol != col) {
163: putpad(tgoto(cursor_address, col, row), 1);
164: ttrow = row;
165: ttcol = col;
166: }
1.1 deraadt 167: }
168:
169: /*
170: * Erase to end of line.
171: */
1.5 millert 172: VOID
173: tteeol()
174: {
1.6 millert 175: int i;
1.2 millert 176:
177: if (clr_eol)
178: putpad(clr_eol, 1);
179: else {
1.6 millert 180: i = ncol - ttcol;
1.2 millert 181: while (i--)
182: ttputc(' ');
183: ttrow = ttcol = HUGE;
184: }
1.1 deraadt 185: }
186:
187: /*
188: * Erase to end of page.
189: */
1.5 millert 190: VOID
191: tteeop()
192: {
1.6 millert 193: int line;
1.2 millert 194:
195: if (clr_eos)
196: putpad(clr_eos, nrow - ttrow);
197: else {
198: putpad(clr_eol, 1);
199: if (insdel)
200: ttdell(ttrow + 1, lines, lines - ttrow - 1);
1.6 millert 201: else {
202: /* do it by hand */
1.2 millert 203: for (line = ttrow + 1; line <= lines; ++line) {
204: ttmove(line, 0);
205: tteeol();
206: }
207: }
208: ttrow = ttcol = HUGE;
1.1 deraadt 209: }
210: }
211:
212: /*
213: * Make a noise.
214: */
1.5 millert 215: VOID
216: ttbeep()
217: {
1.2 millert 218: putpad(bell, 1);
1.1 deraadt 219: ttflush();
220: }
221:
222: /*
1.6 millert 223: * Insert nchunk blank line(s) onto the screen, scrolling the last line on
224: * the screen off the bottom. Use the scrolling region if possible for a
225: * smoother display. If there is no scrolling region, use a set of insert
226: * and delete line sequences.
1.1 deraadt 227: */
1.5 millert 228: VOID
229: ttinsl(row, bot, nchunk)
1.6 millert 230: int row, bot, nchunk;
1.5 millert 231: {
1.6 millert 232: int i, nl;
1.1 deraadt 233:
1.6 millert 234: /* Case of one line insert is special. */
235: if (row == bot) {
236: ttmove(row, 0);
1.2 millert 237: tteeol();
238: return;
239: }
240: if (change_scroll_region && scroll_reverse) {
1.5 millert 241: /* Use scroll region and back index */
1.2 millert 242: nl = bot - row;
1.5 millert 243: ttwindow(row, bot);
1.2 millert 244: ttmove(row, 0);
245: while (nchunk--)
246: putpad(scroll_reverse, nl);
247: ttnowindow();
248: return;
249: } else if (insdel) {
1.5 millert 250: ttmove(1 + bot - nchunk, 0);
1.2 millert 251: nl = nrow - ttrow;
252: if (parm_delete_line)
253: putpad(tgoto(parm_delete_line, 0, nchunk), nl);
254: else
1.6 millert 255: /* For all lines in the chunk... */
1.5 millert 256: for (i = 0; i < nchunk; i++)
1.2 millert 257: putpad(delete_line, nl);
258: ttmove(row, 0);
1.6 millert 259:
260: /* ttmove() changes ttrow */
261: nl = nrow - ttrow;
262:
1.2 millert 263: if (parm_insert_line)
264: putpad(tgoto(parm_insert_line, 0, nchunk), nl);
265: else
266: /* For all lines in the chunk */
1.5 millert 267: for (i = 0; i < nchunk; i++)
1.2 millert 268: putpad(insert_line, nl);
269: ttrow = HUGE;
270: ttcol = HUGE;
271: } else
272: panic("ttinsl: Can't insert/delete line");
1.1 deraadt 273: }
274:
275: /*
1.6 millert 276: * Delete nchunk line(s) from "row", replacing the bottom line on the
277: * screen with a blank line. Unless we're using the scrolling region,
278: * this is done with crafty sequences of insert and delete lines. The
279: * presence of the echo area makes a boundry condition go away.
1.1 deraadt 280: */
1.5 millert 281: VOID
1.1 deraadt 282: ttdell(row, bot, nchunk)
1.6 millert 283: int row, bot, nchunk;
1.1 deraadt 284: {
1.6 millert 285: int i, nl;
1.1 deraadt 286:
1.6 millert 287: /* One line special cases */
288: if (row == bot) {
1.2 millert 289: ttmove(row, 0);
290: tteeol();
291: return;
292: }
1.6 millert 293: /* scrolling region */
294: if (change_scroll_region) {
1.2 millert 295: nl = bot - row;
296: ttwindow(row, bot);
297: ttmove(bot, 0);
298: while (nchunk--)
299: putpad(scroll_fwd, nl);
300: ttnowindow();
1.6 millert 301: /* else use insert/delete line */
1.5 millert 302: } else if (insdel) {
1.6 millert 303: ttmove(row, 0);
1.2 millert 304: nl = nrow - ttrow;
305: if (parm_delete_line)
306: putpad(tgoto(parm_delete_line, 0, nchunk), nl);
307: else
1.5 millert 308: /* For all lines in the chunk */
309: for (i = 0; i < nchunk; i++)
1.2 millert 310: putpad(delete_line, nl);
1.5 millert 311: ttmove(1 + bot - nchunk, 0);
1.6 millert 312:
313: /* ttmove() changes ttrow */
314: nl = nrow - ttrow;
1.2 millert 315: if (parm_insert_line)
316: putpad(tgoto(parm_insert_line, 0, nchunk), nl);
317: else
318: /* For all lines in the chunk */
1.5 millert 319: for (i = 0; i < nchunk; i++)
1.2 millert 320: putpad(insert_line, nl);
321: ttrow = HUGE;
322: ttcol = HUGE;
323: } else
324: panic("ttdell: Can't insert/delete line");
1.1 deraadt 325: }
326:
327: /*
1.6 millert 328: * This routine sets the scrolling window on the display to go from line
329: * "top" to line "bot" (origin 0, inclusive). The caller checks for the
330: * pathological 1-line scroll window which doesn't work right and avoids
331: * it. The "ttrow" and "ttcol" variables are set to a crazy value to
332: * ensure that the next call to "ttmove" does not turn into a no-op (the
333: * window adjustment moves the cursor).
1.1 deraadt 334: */
1.5 millert 335: VOID
1.1 deraadt 336: ttwindow(top, bot)
1.6 millert 337: int top, bot;
1.1 deraadt 338: {
1.2 millert 339: if (change_scroll_region && (tttop != top || ttbot != bot)) {
340: putpad(tgoto(change_scroll_region, bot, top), nrow - ttrow);
1.5 millert 341: ttrow = HUGE; /* Unknown. */
1.1 deraadt 342: ttcol = HUGE;
1.5 millert 343: tttop = top; /* Remember region. */
1.1 deraadt 344: ttbot = bot;
345: }
346: }
347:
348: /*
1.6 millert 349: * Switch to full screen scroll. This is used by "spawn.c" just before it
350: * suspends the editor and by "display.c" when it is getting ready to
351: * exit. This function does a full screen scroll by telling the terminal
352: * to set a scrolling region that is lines or nrow rows high, whichever is
353: * larger. This behavior seems to work right on systems where you can set
354: * your terminal size.
1.1 deraadt 355: */
1.5 millert 356: VOID
1.1 deraadt 357: ttnowindow()
358: {
1.2 millert 359: if (change_scroll_region) {
360: putpad(tgoto(change_scroll_region,
1.5 millert 361: (nrow > lines ? nrow : lines) - 1, 0), nrow - ttrow);
362: ttrow = HUGE; /* Unknown. */
1.2 millert 363: ttcol = HUGE;
1.5 millert 364: tttop = HUGE; /* No scroll region. */
1.2 millert 365: ttbot = HUGE;
366: }
1.1 deraadt 367: }
368:
369: /*
1.6 millert 370: * Set the current writing color to the specified color. Watch for color
371: * changes that are not going to do anything (the color is already right)
372: * and don't send anything to the display. The rainbow version does this
373: * in putline.s on a line by line basis, so don't bother sending out the
374: * color shift.
1.1 deraadt 375: */
1.5 millert 376: VOID
1.2 millert 377: ttcolor(color)
1.6 millert 378: int color;
1.2 millert 379: {
380: if (color != tthue) {
1.6 millert 381: if (color == CTEXT)
382: /* normal video */
1.5 millert 383: putpad(exit_standout_mode, 1);
1.6 millert 384: else if (color == CMODE)
385: /* reverse video */
1.5 millert 386: putpad(enter_standout_mode, 1);
1.6 millert 387: /* save the color */
388: tthue = color;
1.1 deraadt 389: }
390: }
391:
392: /*
1.6 millert 393: * This routine is called by the "refresh the screen" command to try
394: * to resize the display. The new size, which must not exceed the NROW
395: * and NCOL limits, is stored back into "nrow" and * "ncol". Display can
396: * always deal with a screen NROW by NCOL. Look in "window.c" to see how
397: * the caller deals with a change.
1.1 deraadt 398: */
1.5 millert 399: VOID
400: ttresize()
401: {
1.6 millert 402: /* found in "ttyio.c" */
403: setttysize();
1.2 millert 404:
1.6 millert 405: /* ask OS for tty size and check limits */
406: if (nrow < 1)
1.1 deraadt 407: nrow = 1;
408: else if (nrow > NROW)
409: nrow = NROW;
410: if (ncol < 1)
411: ncol = 1;
412: else if (ncol > NCOL)
413: ncol = NCOL;
414: }
415:
416: #ifdef NO_RESIZE
1.5 millert 417: static
418: setttysize()
419: {
1.3 millert 420: nrow = lines;
421: ncol = columns;
1.1 deraadt 422: }
1.6 millert 423: #endif /* NO_RESIZE */
1.1 deraadt 424:
1.6 millert 425: /*
426: * fake char output for charcost()
427: */
1.5 millert 428: /* ARGSUSED */
1.6 millert 429: static int
1.1 deraadt 430: fakec(c)
1.6 millert 431: char c;
1.1 deraadt 432: {
433: cci++;
1.6 millert 434: return 0;
1.1 deraadt 435: }
436:
437: /* calculate the cost of doing string s */
1.6 millert 438: static int
1.5 millert 439: charcost(s)
1.6 millert 440: 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: }