1 /* $OpenBSD: tty.c,v 1.30 2008/09/15 16:11:35 kjell Exp $ */
3 /* This file is in the public domain. */
6 * Terminfo display driver
8 * Terminfo is a terminal information database and routines to describe
9 * terminals on most modern UNIX systems. Many other systems have adopted
10 * this as a reasonable way to allow for widely varying and ever changing
11 * varieties of terminal types. This should be used where practical.
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...
19 * Code for terminals without clear to end of screen and/or clear to end of line
20 * has not been extensively tested.
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.
26 * Using scrolling region vs insert/delete line should probably be based on cost
27 * rather than the assumption that scrolling region operations look better.
32 #include <sys/types.h>
34 #include <sys/ioctl.h>
38 static int charcost(char *);
41 static int insdel; /* Do we have both insert & delete line? */
42 static char *scroll_fwd; /* How to scroll forward. */
44 static void winchhandler(int);
54 * Initialize the terminal when the editor
62 if (setupterm(NULL, 1, &errret))
63 panic("Terminal setup failed");
65 signal(SIGWINCH, winchhandler);
66 signal(SIGCONT, winchhandler);
67 siginterrupt(SIGWINCH, 1);
69 scroll_fwd = scroll_forward;
70 if (scroll_fwd == NULL || *scroll_fwd == '\0') {
71 /* this is what GNU Emacs does */
72 scroll_fwd = parm_down_cursor;
73 if (scroll_fwd == NULL || *scroll_fwd == '\0')
77 if (cursor_address == NULL || cursor_up == NULL)
78 panic("This terminal is too stupid to run mg");
86 tceeol = charcost(clr_eol);
88 /* Estimate cost of inserting a line */
89 if (change_scroll_region && scroll_reverse)
90 tcinsl = charcost(change_scroll_region) * 2 +
91 charcost(scroll_reverse);
92 else if (parm_insert_line)
93 tcinsl = charcost(parm_insert_line);
95 tcinsl = charcost(insert_line);
97 /* make this cost high enough */
100 /* Estimate cost of deleting a line */
101 if (change_scroll_region)
102 tcdell = charcost(change_scroll_region) * 2 +
103 charcost(scroll_fwd);
104 else if (parm_delete_line)
105 tcdell = charcost(parm_delete_line);
106 else if (delete_line)
107 tcdell = charcost(delete_line);
109 /* make this cost high enough */
110 tcdell = nrow * ncol;
112 /* Flag to indicate that we can both insert and delete lines */
113 insdel = (insert_line || parm_insert_line) &&
114 (delete_line || parm_delete_line);
117 /* enter application mode */
118 putpad(enter_ca_mode, 1);
124 * Re-initialize the terminal when the editor is resumed.
125 * The keypad_xmit doesn't really belong here but...
130 /* check if file was modified while we were gone */
131 if (fchecktime(curbp) != TRUE) {
132 curbp->b_flag |= BFDIRTY;
136 /* enter application mode */
137 putpad(enter_ca_mode, 1);
141 putpad(keypad_xmit, 1);
147 * Clean up the terminal, in anticipation of a return to the command
148 * interpreter. This is a no-op on the ANSI display. On the SCALD display,
149 * it sets the window back to half screen scrolling. Perhaps it should
150 * query the display for the increment, and put it back to what it was.
159 /* set the term back to normal mode */
161 putpad(exit_ca_mode, 1);
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
167 * location last time!
170 ttmove(int row, int col)
172 if (ttrow != row || ttcol != col) {
173 putpad(tgoto(cursor_address, col, row), 1);
180 * Erase to end of line.
193 ttrow = ttcol = HUGE;
198 * Erase to end of page.
206 putpad(clr_eos, nrow - ttrow);
210 ttdell(ttrow + 1, lines, lines - ttrow - 1);
213 for (line = ttrow + 1; line <= lines; ++line) {
218 ttrow = ttcol = HUGE;
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
236 * and delete line sequences.
239 ttinsl(int row, int bot, int nchunk)
243 /* Case of one line insert is special. */
249 if (change_scroll_region && scroll_reverse) {
250 /* Use scroll region and back index */
255 putpad(scroll_reverse, nl);
259 ttmove(1 + bot - nchunk, 0);
261 if (parm_delete_line)
262 putpad(tgoto(parm_delete_line, 0, nchunk), nl);
264 /* For all lines in the chunk... */
265 for (i = 0; i < nchunk; i++)
266 putpad(delete_line, nl);
269 /* ttmove() changes ttrow */
272 if (parm_insert_line)
273 putpad(tgoto(parm_insert_line, 0, nchunk), nl);
275 /* For all lines in the chunk */
276 for (i = 0; i < nchunk; i++)
277 putpad(insert_line, nl);
281 panic("ttinsl: Can't insert/delete line");
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
288 * presence of the echo area makes a boundary condition go away.
291 ttdell(int row, int bot, int nchunk)
295 /* One line special cases */
301 /* scrolling region */
302 if (change_scroll_region) {
307 putpad(scroll_fwd, nl);
309 /* else use insert/delete line */
313 if (parm_delete_line)
314 putpad(tgoto(parm_delete_line, 0, nchunk), nl);
316 /* For all lines in the chunk */
317 for (i = 0; i < nchunk; i++)
318 putpad(delete_line, nl);
319 ttmove(1 + bot - nchunk, 0);
321 /* ttmove() changes ttrow */
323 if (parm_insert_line)
324 putpad(tgoto(parm_insert_line, 0, nchunk), nl);
326 /* For all lines in the chunk */
327 for (i = 0; i < nchunk; i++)
328 putpad(insert_line, nl);
332 panic("ttdell: Can't insert/delete line");
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
341 * window adjustment moves the cursor).
344 ttwindow(int top, int bot)
346 if (change_scroll_region && (tttop != top || ttbot != bot)) {
347 putpad(tgoto(change_scroll_region, bot, top), nrow - ttrow);
348 ttrow = HUGE; /* Unknown. */
350 tttop = top; /* Remember region. */
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
361 * your terminal size.
366 if (change_scroll_region) {
367 putpad(tgoto(change_scroll_region,
368 (nrow > lines ? nrow : lines) - 1, 0), nrow - ttrow);
369 ttrow = HUGE; /* Unknown. */
371 tttop = HUGE; /* No scroll region. */
377 * Set the current writing color to the specified color. Watch for color
378 * changes that are not going to do anything (the color is already right)
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
386 if (color != tthue) {
389 putpad(exit_standout_mode, 1);
390 else if (color == CMODE)
392 putpad(enter_standout_mode, 1);
399 * This routine is called by the "refresh the screen" command to try
400 * to resize the display. Look in "window.c" to see how
401 * the caller deals with a change.
403 * We use `newrow' and `newcol' so vtresize() know the difference between the
404 * new and old settings.
409 int newrow = 0, newcol = 0;
412 struct winsize winsize;
414 if (ioctl(0, TIOCGWINSZ, &winsize) == 0) {
415 newrow = winsize.ws_row;
416 newcol = winsize.ws_col;
419 if ((newrow <= 0 || newcol <= 0) &&
420 ((newrow = lines) <= 0 || (newcol = columns) <= 0)) {
424 if (vtresize(1, newrow, newcol) != TRUE)
425 panic("vtresize failed");
429 * fake char output for charcost()
439 /* calculate the cost of doing string s */
445 tputs(s, nrow, fakec);