]> pd.if.org Git - pd_readline/blob - mg/tty.c
Added mg from an OpenBSD mirror site. Many thanks to the OpenBSD team and the mg...
[pd_readline] / mg / tty.c
1 /*      $OpenBSD: tty.c,v 1.30 2008/09/15 16:11:35 kjell Exp $  */
2
3 /* This file is in the public domain. */
4
5 /*
6  * Terminfo display driver
7  *
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.
12  */
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...
18  *
19  * Code for terminals without clear to end of screen and/or clear to end of line
20  * has not been extensively tested.
21  *
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.
25  *
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.
28  */
29
30 #include "def.h"
31
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include <sys/ioctl.h>
35
36 #include <term.h>
37
38 static int       charcost(char *);
39
40 static int       cci;
41 static int       insdel;        /* Do we have both insert & delete line? */
42 static char     *scroll_fwd;    /* How to scroll forward. */
43
44 static void      winchhandler(int);
45
46 /* ARGSUSED */
47 static void
48 winchhandler(int sig)
49 {
50         winch_flag = 1;
51 }
52
53 /*
54  * Initialize the terminal when the editor
55  * gets started up.
56  */
57 void
58 ttinit(void)
59 {
60         int errret;
61
62         if (setupterm(NULL, 1, &errret))
63                 panic("Terminal setup failed");
64
65         signal(SIGWINCH, winchhandler);
66         signal(SIGCONT, winchhandler);
67         siginterrupt(SIGWINCH, 1);
68
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')
74                         scroll_fwd = "\n";
75         }
76
77         if (cursor_address == NULL || cursor_up == NULL)
78                 panic("This terminal is too stupid to run mg");
79
80         /* set nrow & ncol */
81         ttresize();
82
83         if (!clr_eol)
84                 tceeol = ncol;
85         else
86                 tceeol = charcost(clr_eol);
87
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);
94         else if (insert_line)
95                 tcinsl = charcost(insert_line);
96         else
97                 /* make this cost high enough */
98                 tcinsl = nrow * ncol;
99
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);
108         else
109                 /* make this cost high enough */
110                 tcdell = nrow * ncol;
111
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);
115
116         if (enter_ca_mode)
117                 /* enter application mode */
118                 putpad(enter_ca_mode, 1);
119
120         ttresize();
121 }
122
123 /*
124  * Re-initialize the terminal when the editor is resumed.
125  * The keypad_xmit doesn't really belong here but...
126  */
127 void
128 ttreinit(void)
129 {
130         /* check if file was modified while we were gone */
131         if (fchecktime(curbp) != TRUE) {
132                 curbp->b_flag |= BFDIRTY;
133         }
134
135         if (enter_ca_mode)
136                 /* enter application mode */
137                 putpad(enter_ca_mode, 1);
138
139         if (keypad_xmit)
140                 /* turn on keypad */
141                 putpad(keypad_xmit, 1);
142
143         ttresize();
144 }
145
146 /*
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.
151  */
152 void
153 tttidy(void)
154 {
155 #ifdef  XKEYS
156         ttykeymaptidy();
157 #endif /* XKEYS */
158
159         /* set the term back to normal mode */
160         if (exit_ca_mode)
161                 putpad(exit_ca_mode, 1);
162 }
163
164 /*
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!
168  */
169 void
170 ttmove(int row, int col)
171 {
172         if (ttrow != row || ttcol != col) {
173                 putpad(tgoto(cursor_address, col, row), 1);
174                 ttrow = row;
175                 ttcol = col;
176         }
177 }
178
179 /*
180  * Erase to end of line.
181  */
182 void
183 tteeol(void)
184 {
185         int     i;
186
187         if (clr_eol)
188                 putpad(clr_eol, 1);
189         else {
190                 i = ncol - ttcol;
191                 while (i--)
192                         ttputc(' ');
193                 ttrow = ttcol = HUGE;
194         }
195 }
196
197 /*
198  * Erase to end of page.
199  */
200 void
201 tteeop(void)
202 {
203         int     line;
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);
211                 else {
212                         /* do it by hand */
213                         for (line = ttrow + 1; line <= lines; ++line) {
214                                 ttmove(line, 0);
215                                 tteeol();
216                         }
217                 }
218                 ttrow = ttcol = HUGE;
219         }
220 }
221
222 /*
223  * Make a noise.
224  */
225 void
226 ttbeep(void)
227 {
228         putpad(bell, 1);
229         ttflush();
230 }
231
232 /*
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.
237  */
238 void
239 ttinsl(int row, int bot, int nchunk)
240 {
241         int     i, nl;
242
243         /* Case of one line insert is special. */
244         if (row == bot) {
245                 ttmove(row, 0);
246                 tteeol();
247                 return;
248         }
249         if (change_scroll_region && scroll_reverse) {
250                 /* Use scroll region and back index      */
251                 nl = bot - row;
252                 ttwindow(row, bot);
253                 ttmove(row, 0);
254                 while (nchunk--)
255                         putpad(scroll_reverse, nl);
256                 ttnowindow();
257                 return;
258         } else if (insdel) {
259                 ttmove(1 + bot - nchunk, 0);
260                 nl = nrow - ttrow;
261                 if (parm_delete_line)
262                         putpad(tgoto(parm_delete_line, 0, nchunk), nl);
263                 else
264                         /* For all lines in the chunk... */
265                         for (i = 0; i < nchunk; i++)
266                                 putpad(delete_line, nl);
267                 ttmove(row, 0);
268
269                 /* ttmove() changes ttrow */
270                 nl = nrow - ttrow;
271
272                 if (parm_insert_line)
273                         putpad(tgoto(parm_insert_line, 0, nchunk), nl);
274                 else
275                         /* For all lines in the chunk */
276                         for (i = 0; i < nchunk; i++)
277                                 putpad(insert_line, nl);
278                 ttrow = HUGE;
279                 ttcol = HUGE;
280         } else
281                 panic("ttinsl: Can't insert/delete line");
282 }
283
284 /*
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.
289  */
290 void
291 ttdell(int row, int bot, int nchunk)
292 {
293         int     i, nl;
294
295         /* One line special cases */
296         if (row == bot) {
297                 ttmove(row, 0);
298                 tteeol();
299                 return;
300         }
301         /* scrolling region */
302         if (change_scroll_region) {
303                 nl = bot - row;
304                 ttwindow(row, bot);
305                 ttmove(bot, 0);
306                 while (nchunk--)
307                         putpad(scroll_fwd, nl);
308                 ttnowindow();
309         /* else use insert/delete line */
310         } else if (insdel) {
311                 ttmove(row, 0);
312                 nl = nrow - ttrow;
313                 if (parm_delete_line)
314                         putpad(tgoto(parm_delete_line, 0, nchunk), nl);
315                 else
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);
320
321                 /* ttmove() changes ttrow */
322                 nl = nrow - ttrow;
323                 if (parm_insert_line)
324                         putpad(tgoto(parm_insert_line, 0, nchunk), nl);
325                 else
326                         /* For all lines in the chunk */
327                         for (i = 0; i < nchunk; i++)
328                                 putpad(insert_line, nl);
329                 ttrow = HUGE;
330                 ttcol = HUGE;
331         } else
332                 panic("ttdell: Can't insert/delete line");
333 }
334
335 /*
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).
342  */
343 void
344 ttwindow(int top, int bot)
345 {
346         if (change_scroll_region && (tttop != top || ttbot != bot)) {
347                 putpad(tgoto(change_scroll_region, bot, top), nrow - ttrow);
348                 ttrow = HUGE;   /* Unknown.              */
349                 ttcol = HUGE;
350                 tttop = top;    /* Remember region.      */
351                 ttbot = bot;
352         }
353 }
354
355 /*
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.
362  */
363 void
364 ttnowindow(void)
365 {
366         if (change_scroll_region) {
367                 putpad(tgoto(change_scroll_region,
368                     (nrow > lines ? nrow : lines) - 1, 0), nrow - ttrow);
369                 ttrow = HUGE;   /* Unknown.              */
370                 ttcol = HUGE;
371                 tttop = HUGE;   /* No scroll region.     */
372                 ttbot = HUGE;
373         }
374 }
375
376 /*
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
381  * color shift.
382  */
383 void
384 ttcolor(int color)
385 {
386         if (color != tthue) {
387                 if (color == CTEXT)
388                         /* normal video */
389                         putpad(exit_standout_mode, 1);
390                 else if (color == CMODE)
391                         /* reverse video */
392                         putpad(enter_standout_mode, 1);
393                 /* save the color */
394                 tthue = color;
395         }
396 }
397
398 /*
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.
402  *
403  * We use `newrow' and `newcol' so vtresize() know the difference between the
404  * new and old settings.
405  */
406 void
407 ttresize(void)
408 {
409         int newrow = 0, newcol = 0;
410
411 #ifdef  TIOCGWINSZ
412         struct  winsize winsize;
413
414         if (ioctl(0, TIOCGWINSZ, &winsize) == 0) {
415                 newrow = winsize.ws_row;
416                 newcol = winsize.ws_col;
417         }
418 #endif
419         if ((newrow <= 0 || newcol <= 0) &&
420             ((newrow = lines) <= 0 || (newcol = columns) <= 0)) {
421                 newrow = 24;
422                 newcol = 80;
423         }
424         if (vtresize(1, newrow, newcol) != TRUE)
425                 panic("vtresize failed");
426 }
427
428 /*
429  * fake char output for charcost()
430  */
431 /* ARGSUSED */
432 static int
433 fakec(int c)
434 {
435         cci++;
436         return (0);
437 }
438
439 /* calculate the cost of doing string s */
440 static int
441 charcost(char *s)
442 {
443         cci = 0;
444
445         tputs(s, nrow, fakec);
446         return (cci);
447 }