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