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