1    | /*	SCCS Id: @(#)termcap.c	3.3	2000/07/10	*/
2    | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3    | /* NetHack may be freely redistributed.  See license for details. */
4    | 
5    | #include "hack.h"
6    | 
7    | #if defined (TTY_GRAPHICS) && !defined(NO_TERMS)
8    | 
9    | #include "wintty.h"
10   | 
11   | #include "tcap.h"
12   | 
13   | 
14   | #ifdef MICROPORT_286_BUG
15   | #define Tgetstr(key) (tgetstr(key,tbuf))
16   | #else
17   | #define Tgetstr(key) (tgetstr(key,&tbufptr))
18   | #endif /* MICROPORT_286_BUG **/
19   | 
20   | static char * FDECL(s_atr2str, (int));
21   | static char * FDECL(e_atr2str, (int));
22   | 
23   | void FDECL(cmov, (int, int));
24   | void FDECL(nocmov, (int, int));
25   | #if defined(TEXTCOLOR) && defined(TERMLIB)
26   | # ifdef OVLB
27   | #  if !defined(UNIX) || !defined(TERMINFO)
28   | #   ifndef TOS
29   | static void FDECL(analyze_seq, (char *, int *, int *));
30   | #   endif
31   | #  endif
32   | static void NDECL(init_hilite);
33   | static void NDECL(kill_hilite);
34   | # endif /* OVLB */
35   | #endif
36   | 
37   | #ifdef OVLB
38   | 	/* (see tcap.h) -- nh_CM, nh_ND, nh_CD, nh_HI,nh_HE, nh_US,nh_UE,
39   | 				ul_hack */
40   | struct tc_lcl_data tc_lcl_data = { 0, 0, 0, 0,0, 0,0, FALSE };
41   | #endif /* OVLB */
42   | 
43   | STATIC_VAR char *HO, *CL, *CE, *UP, *XD, *BC, *SO, *SE, *TI, *TE;
44   | STATIC_VAR char *VS, *VE;
45   | STATIC_VAR char *ME;
46   | STATIC_VAR char *MR;
47   | #if 0
48   | STATIC_VAR char *MB, *MH;
49   | STATIC_VAR char *MD;     /* may already be in use below */
50   | #endif
51   | #ifdef TERMLIB
52   | # ifdef TEXTCOLOR
53   | STATIC_VAR char *MD;
54   | # endif
55   | STATIC_VAR int SG;
56   | #ifdef OVLB
57   | STATIC_OVL char PC = '\0';
58   | #else /* OVLB */
59   | STATIC_DCL char PC;
60   | #endif /* OVLB */
61   | STATIC_VAR char tbuf[512];
62   | #endif
63   | 
64   | #ifdef TEXTCOLOR
65   | # ifdef TOS
66   | const char *hilites[CLR_MAX];	/* terminal escapes for the various colors */
67   | # else
68   | char NEARDATA *hilites[CLR_MAX]; /* terminal escapes for the various colors */
69   | # endif
70   | #endif
71   | 
72   | #ifdef OVLB
73   | static char *KS = (char *)0, *KE = (char *)0;	/* keypad sequences */
74   | static char nullstr[] = "";
75   | #endif /* OVLB */
76   | 
77   | #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
78   | extern boolean HE_resets_AS;
79   | #endif
80   | 
81   | #ifndef TERMLIB
82   | STATIC_VAR char tgotobuf[20];
83   | # ifdef TOS
84   | #define tgoto(fmt, x, y)	(Sprintf(tgotobuf, fmt, y+' ', x+' '), tgotobuf)
85   | # else
86   | #define tgoto(fmt, x, y)	(Sprintf(tgotobuf, fmt, y+1, x+1), tgotobuf)
87   | # endif
88   | #endif /* TERMLIB */
89   | 
90   | #ifdef OVLB
91   | 
92   | void
93   | tty_startup(wid, hgt)
94   | int *wid, *hgt;
95   | {
96   | 	register int i;
97   | #ifdef TERMLIB
98   | 	register const char *term;
99   | 	register char *tptr;
100  | 	char *tbufptr, *pc;
101  | 
102  | # ifdef VMS
103  | 	term = verify_termcap();
104  | 	if (!term)
105  | # endif
106  | 		term = getenv("TERM");
107  | 
108  | # if defined(TOS) && defined(__GNUC__)
109  | 	if (!term)
110  | 		term = "builtin";		/* library has a default */
111  | # endif
112  | 	if (!term)
113  | #endif
114  | #ifndef ANSI_DEFAULT
115  | 		error("Can't get TERM.");
116  | #else
117  | # ifdef TOS
118  | 	{
119  | 		CO = 80; LI = 25;
120  | 		TI = VS = VE = TE = nullstr;
121  | 		HO = "\033H";
122  | 		CE = "\033K";		/* the VT52 termcap */
123  | 		UP = "\033A";
124  | 		nh_CM = "\033Y%c%c";	/* used with function tgoto() */
125  | 		nh_ND = "\033C";
126  | 		XD = "\033B";
127  | 		BC = "\033D";
128  | 		SO = "\033p";
129  | 		SE = "\033q";
130  | 	/* HI and HE will be updated in init_hilite if we're using color */
131  | 		nh_HI = "\033p";
132  | 		nh_HE = "\033q";
133  | 		*wid = CO;
134  | 		*hgt = LI;
135  | 		CL = "\033E";		/* last thing set */
136  | 		return;
137  | 	}
138  | # else /* TOS */
139  | 	{
140  | #  ifdef MICRO
141  | 		get_scr_size();
142  | #   ifdef CLIPPING
143  | 		if(CO < COLNO || LI < ROWNO+3)
144  | 			setclipped();
145  | #   endif
146  | #  endif
147  | 		HO = "\033[H";
148  | /*		nh_CD = "\033[J"; */
149  | 		CE = "\033[K";		/* the ANSI termcap */
150  | #  ifndef TERMLIB
151  | 		nh_CM = "\033[%d;%dH";
152  | #  else
153  | 		nh_CM = "\033[%i%d;%dH";
154  | #  endif
155  | 		UP = "\033[A";
156  | 		nh_ND = "\033[C";
157  | 		XD = "\033[B";
158  | #  ifdef MICRO	/* backspaces are non-destructive */
159  | 		BC = "\b";
160  | #  else
161  | 		BC = "\033[D";
162  | #  endif
163  | 		nh_HI = SO = "\033[1m";
164  | 		nh_US = "\033[4m";
165  | 		MR = "\033[7m";
166  | 		TI = nh_HE = ME = SE = nh_UE = "\033[0m";
167  | 		/* strictly, SE should be 2, and nh_UE should be 24,
168  | 		   but we can't trust all ANSI emulators to be
169  | 		   that complete.  -3. */
170  | #  ifndef MICRO
171  | 		AS = "\016";
172  | 		AE = "\017";
173  | #  endif
174  | 		TE = VS = VE = nullstr;
175  | #  ifdef TEXTCOLOR
176  | 		for (i = 0; i < CLR_MAX / 2; i++)
177  | 		    if (i != CLR_BLACK) {
178  | 			hilites[i|BRIGHT] = (char *) alloc(sizeof("\033[1;3%dm"));
179  | 			Sprintf(hilites[i|BRIGHT], "\033[1;3%dm", i);
180  | 			if (i != CLR_GRAY)
181  | #   ifdef MICRO
182  | 			    if (i == CLR_BLUE) hilites[CLR_BLUE] = hilites[CLR_BLUE|BRIGHT];
183  | 			    else
184  | #   endif
185  | 			    {
186  | 				hilites[i] = (char *) alloc(sizeof("\033[0;3%dm"));
187  | 				Sprintf(hilites[i], "\033[0;3%dm", i);
188  | 			    }
189  | 		    }
190  | #  endif
191  | 		*wid = CO;
192  | 		*hgt = LI;
193  | 		CL = "\033[2J";		/* last thing set */
194  | 		return;
195  | 	}
196  | # endif /* TOS */
197  | #endif /* ANSI_DEFAULT */
198  | 
199  | #ifdef TERMLIB
200  | 	tptr = (char *) alloc(1024);
201  | 
202  | 	tbufptr = tbuf;
203  | 	if(!strncmp(term, "5620", 4))
204  | 		flags.null = FALSE;	/* this should be a termcap flag */
205  | 	if(tgetent(tptr, term) < 1) {
206  | 		char buf[BUFSZ];
207  | 		(void) strncpy(buf, term,
208  | 				(BUFSZ - 1) - (sizeof("Unknown terminal type: .  ")));
209  | 		buf[BUFSZ-1] = '\0';
210  | 		error("Unknown terminal type: %s.", term);
211  | 	}
212  | 	if ((pc = Tgetstr("pc")) != 0)
213  | 		PC = *pc;
214  | 
215  | 	if(!(BC = Tgetstr("le")))	/* both termcap and terminfo use le */
216  | # ifdef TERMINFO
217  | 	    error("Terminal must backspace.");
218  | # else
219  | 	    if(!(BC = Tgetstr("bc"))) {	/* termcap also uses bc/bs */
220  | #  ifndef MINIMAL_TERM
221  | 		if(!tgetflag("bs"))
222  | 			error("Terminal must backspace.");
223  | #  endif
224  | 		BC = tbufptr;
225  | 		tbufptr += 2;
226  | 		*BC = '\b';
227  | 	    }
228  | # endif
229  | 
230  | # ifdef MINIMAL_TERM
231  | 	HO = (char *)0;
232  | # else
233  | 	HO = Tgetstr("ho");
234  | # endif
235  | 	/*
236  | 	 * LI and CO are set in ioctl.c via a TIOCGWINSZ if available.  If
237  | 	 * the kernel has values for either we should use them rather than
238  | 	 * the values from TERMCAP ...
239  | 	 */
240  | # ifndef MICRO
241  | 	if (!CO) CO = tgetnum("co");
242  | 	if (!LI) LI = tgetnum("li");
243  | # else
244  | #  if defined(TOS) && defined(__GNUC__)
245  | 	if (!strcmp(term, "builtin"))
246  | 		get_scr_size();
247  | 	else {
248  | #  endif
249  | 		CO = tgetnum("co");
250  | 		LI = tgetnum("li");
251  | 		if (!LI || !CO)			/* if we don't override it */
252  | 			get_scr_size();
253  | #  if defined(TOS) && defined(__GNUC__)
254  | 	}
255  | #  endif
256  | # endif
257  | # ifdef CLIPPING
258  | 	if(CO < COLNO || LI < ROWNO+3)
259  | 		setclipped();
260  | # endif
261  | 	nh_ND = Tgetstr("nd");
262  | 	if(tgetflag("os"))
263  | 		error("NetHack can't have OS.");
264  | 	if(tgetflag("ul"))
265  | 		ul_hack = TRUE;
266  | 	CE = Tgetstr("ce");
267  | 	UP = Tgetstr("up");
268  | 	/* It seems that xd is no longer supported, and we should use
269  | 	   a linefeed instead; unfortunately this requires resetting
270  | 	   CRMOD, and many output routines will have to be modified
271  | 	   slightly. Let's leave that till the next release. */
272  | 	XD = Tgetstr("xd");
273  | /* not:		XD = Tgetstr("do"); */
274  | 	if(!(nh_CM = Tgetstr("cm"))) {
275  | 	    if(!UP && !HO)
276  | 		error("NetHack needs CM or UP or HO.");
277  | 	    tty_raw_print("Playing NetHack on terminals without CM is suspect.");
278  | 	    tty_wait_synch();
279  | 	}
280  | 	SO = Tgetstr("so");
281  | 	SE = Tgetstr("se");
282  | 	nh_US = Tgetstr("us");
283  | 	nh_UE = Tgetstr("ue");
284  | 	SG = tgetnum("sg");	/* -1: not fnd; else # of spaces left by so */
285  | 	if(!SO || !SE || (SG > 0)) SO = SE = nh_US = nh_UE = nullstr;
286  | 	TI = Tgetstr("ti");
287  | 	TE = Tgetstr("te");
288  | 	VS = VE = nullstr;
289  | # ifdef TERMINFO
290  | 	VS = Tgetstr("eA");	/* enable graphics */
291  | # endif
292  | 	KS = Tgetstr("ks");	/* keypad start (special mode) */
293  | 	KE = Tgetstr("ke");	/* keypad end (ordinary mode [ie, digits]) */
294  | 	MR = Tgetstr("mr");	/* reverse */
295  | # if 0
296  | 	MB = Tgetstr("mb");	/* blink */
297  | 	MD = Tgetstr("md");	/* boldface */
298  | 	MH = Tgetstr("mh");	/* dim */
299  | # endif
300  | 	ME = Tgetstr("me");	/* turn off all attributes */
301  | 	if (!ME || (SE == nullstr)) ME = SE;	/* default to SE value */
302  | 
303  | 	/* Get rid of padding numbers for nh_HI and nh_HE.  Hope they
304  | 	 * aren't really needed!!!  nh_HI and nh_HE are outputted to the
305  | 	 * pager as a string - so how can you send it NULs???
306  | 	 *  -jsb
307  | 	 */
308  | 	nh_HI = (char *) alloc((unsigned)(strlen(SO)+1));
309  | 	nh_HE = (char *) alloc((unsigned)(strlen(ME)+1));
310  | 	i = 0;
311  | 	while (digit(SO[i])) i++;
312  | 	Strcpy(nh_HI, &SO[i]);
313  | 	i = 0;
314  | 	while (digit(ME[i])) i++;
315  | 	Strcpy(nh_HE, &ME[i]);
316  | 	AS = Tgetstr("as");
317  | 	AE = Tgetstr("ae");
318  | 	nh_CD = Tgetstr("cd");
319  | # ifdef TEXTCOLOR
320  | 	MD = Tgetstr("md");
321  | # endif
322  | # ifdef TEXTCOLOR
323  | #  if defined(TOS) && defined(__GNUC__)
324  | 	if (!strcmp(term, "builtin") || !strcmp(term, "tw52") ||
325  | 	    !strcmp(term, "st52")) {
326  | 		init_hilite();
327  | 	}
328  | #  else
329  | 	init_hilite();
330  | #  endif
331  | # endif
332  | 	*wid = CO;
333  | 	*hgt = LI;
334  | 	if (!(CL = Tgetstr("cl")))	/* last thing set */
335  | 		error("NetHack needs CL.");
336  | 	if ((int)(tbufptr - tbuf) > (int)(sizeof tbuf))
337  | 		error("TERMCAP entry too big...\n");
338  | 	free((genericptr_t)tptr);
339  | #endif /* TERMLIB */
340  | }
341  | 
342  | /* note: at present, this routine is not part of the formal window interface */
343  | /* deallocate resources prior to final termination */
344  | void
345  | tty_shutdown()
346  | {
347  | #if defined(TEXTCOLOR) && defined(TERMLIB)
348  | 	kill_hilite();
349  | #endif
350  | 	/* we don't attempt to clean up individual termcap variables [yet?] */
351  | 	return;
352  | }
353  | 
354  | void
355  | tty_number_pad(state)
356  | int state;
357  | {
358  | 	switch (state) {
359  | 	    case -1:	/* activate keypad mode (escape sequences) */
360  | 		    if (KS && *KS) xputs(KS);
361  | 		    break;
362  | 	    case  1:	/* activate numeric mode for keypad (digits) */
363  | 		    if (KE && *KE) xputs(KE);
364  | 		    break;
365  | 	    case  0:	/* don't need to do anything--leave terminal as-is */
366  | 	    default:
367  | 		    break;
368  | 	}
369  | }
370  | 
371  | #ifdef TERMLIB
372  | extern void NDECL((*decgraphics_mode_callback));    /* defined in drawing.c */
373  | static void NDECL(tty_decgraphics_termcap_fixup);
374  | 
375  | /*
376  |    We call this routine whenever DECgraphics mode is enabled, even if it
377  |    has been previously set, in case the user manages to reset the fonts.
378  |    The actual termcap fixup only needs to be done once, but we can't
379  |    call xputs() from the option setting or graphics assigning routines,
380  |    so this is a convenient hook.
381  |  */
382  | static void
383  | tty_decgraphics_termcap_fixup()
384  | {
385  | 	static char ctrlN[]   = "\016";
386  | 	static char ctrlO[]   = "\017";
387  | 	static char appMode[] = "\033=";
388  | 	static char numMode[] = "\033>";
389  | 
390  | 	/* these values are missing from some termcaps */
391  | 	if (!AS) AS = ctrlN;	/* ^N (shift-out [graphics font]) */
392  | 	if (!AE) AE = ctrlO;	/* ^O (shift-in  [regular font])  */
393  | 	if (!KS) KS = appMode;	/* ESC= (application keypad mode) */
394  | 	if (!KE) KE = numMode;	/* ESC> (numeric keypad mode)	  */
395  | 	/*
396  | 	 * Select the line-drawing character set as the alternate font.
397  | 	 * Do not select NA ASCII as the primary font since people may
398  | 	 * reasonably be using the UK character set.
399  | 	 */
400  | 	if (iflags.DECgraphics) xputs("\033)0");
401  | #ifdef PC9800
402  | 	init_hilite();
403  | #endif
404  | 
405  | #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
406  | 	/* some termcaps suffer from the bizarre notion that resetting
407  | 	   video attributes should also reset the chosen character set */
408  |     {
409  | 	const char *nh_he = nh_HE, *ae = AE;
410  | 	int he_limit, ae_length;
411  | 
412  | 	if (digit(*ae)) {	/* skip over delay prefix, if any */
413  | 	    do ++ae; while (digit(*ae));
414  | 	    if (*ae == '.') { ++ae; if (digit(*ae)) ++ae; }
415  | 	    if (*ae == '*') ++ae;
416  | 	}
417  | 	/* can't use nethack's case-insensitive strstri() here, and some old
418  | 	   systems don't have strstr(), so use brute force substring search */
419  | 	ae_length = strlen(ae), he_limit = strlen(nh_he);
420  | 	while (he_limit >= ae_length) {
421  | 	    if (strncmp(nh_he, ae, ae_length) == 0) {
422  | 		HE_resets_AS = TRUE;
423  | 		break;
424  | 	    }
425  | 	    ++nh_he, --he_limit;
426  | 	}
427  |     }
428  | #endif
429  | }
430  | #endif	/* TERMLIB */
431  | 
432  | #if defined(ASCIIGRAPH) && defined(PC9800)
433  | extern void NDECL((*ibmgraphics_mode_callback));    /* defined in drawing.c */
434  | #endif
435  | 
436  | #ifdef PC9800
437  | extern void NDECL((*ascgraphics_mode_callback));    /* defined in drawing.c */
438  | static void NDECL(tty_ascgraphics_hilite_fixup);
439  | 
440  | static void
441  | tty_ascgraphics_hilite_fixup()
442  | {
443  |     register int c;
444  | 
445  |     for (c = 0; c < CLR_MAX / 2; c++)
446  | 	if (c != CLR_BLACK) {
447  | 	    hilites[c|BRIGHT] = (char *) alloc(sizeof("\033[1;3%dm"));
448  | 	    Sprintf(hilites[c|BRIGHT], "\033[1;3%dm", c);
449  | 	    if (c != CLR_GRAY) {
450  | 		    hilites[c] = (char *) alloc(sizeof("\033[0;3%dm"));
451  | 		    Sprintf(hilites[c], "\033[0;3%dm", c);
452  | 	    }
453  | 	}
454  | }
455  | #endif /* PC9800 */
456  | 
457  | void
458  | tty_start_screen()
459  | {
460  | 	xputs(TI);
461  | 	xputs(VS);
462  | #ifdef PC9800
463  |     if (!iflags.IBMgraphics && !iflags.DECgraphics)
464  | 	    tty_ascgraphics_hilite_fixup();
465  |     /* set up callback in case option is not set yet but toggled later */
466  |     ascgraphics_mode_callback = tty_ascgraphics_hilite_fixup;
467  | # ifdef ASCIIGRAPH
468  |     if (iflags.IBMgraphics) init_hilite();
469  |     /* set up callback in case option is not set yet but toggled later */
470  |     ibmgraphics_mode_callback = init_hilite;
471  | # endif
472  | #endif /* PC9800 */
473  | 
474  | #ifdef TERMLIB
475  | 	if (iflags.DECgraphics) tty_decgraphics_termcap_fixup();
476  | 	/* set up callback in case option is not set yet but toggled later */
477  | 	decgraphics_mode_callback = tty_decgraphics_termcap_fixup;
478  | #endif
479  | 	if (iflags.num_pad) tty_number_pad(1);	/* make keypad send digits */
480  | }
481  | 
482  | void
483  | tty_end_screen()
484  | {
485  | 	clear_screen();
486  | 	xputs(VE);
487  | 	xputs(TE);
488  | }
489  | 
490  | /* Cursor movements */
491  | 
492  | #endif /* OVLB */
493  | 
494  | #ifdef OVL0
495  | /* Note to OVLx tinkerers.  The placement of this overlay controls the location
496  |    of the function xputc().  This function is not currently in trampoli.[ch]
497  |    files for what is deemed to be performance reasons.  If this define is moved
498  |    and or xputc() is taken out of the ROOT overlay, then action must be taken
499  |    in trampoli.[ch]. */
500  | 
501  | void
502  | nocmov(x, y)
503  | int x,y;
504  | {
505  | 	if ((int) ttyDisplay->cury > y) {
506  | 		if(UP) {
507  | 			while ((int) ttyDisplay->cury > y) {	/* Go up. */
508  | 				xputs(UP);
509  | 				ttyDisplay->cury--;
510  | 			}
511  | 		} else if(nh_CM) {
512  | 			cmov(x, y);
513  | 		} else if(HO) {
514  | 			home();
515  | 			tty_curs(BASE_WINDOW, x+1, y);
516  | 		} /* else impossible("..."); */
517  | 	} else if ((int) ttyDisplay->cury < y) {
518  | 		if(XD) {
519  | 			while((int) ttyDisplay->cury < y) {
520  | 				xputs(XD);
521  | 				ttyDisplay->cury++;
522  | 			}
523  | 		} else if(nh_CM) {
524  | 			cmov(x, y);
525  | 		} else {
526  | 			while((int) ttyDisplay->cury < y) {
527  | 				xputc('\n');
528  | 				ttyDisplay->curx = 0;
529  | 				ttyDisplay->cury++;
530  | 			}
531  | 		}
532  | 	}
533  | 	if ((int) ttyDisplay->curx < x) {		/* Go to the right. */
534  | 		if(!nh_ND) cmov(x, y); else	/* bah */
535  | 			/* should instead print what is there already */
536  | 		while ((int) ttyDisplay->curx < x) {
537  | 			xputs(nh_ND);
538  | 			ttyDisplay->curx++;
539  | 		}
540  | 	} else if ((int) ttyDisplay->curx > x) {
541  | 		while ((int) ttyDisplay->curx > x) {	/* Go to the left. */
542  | 			xputs(BC);
543  | 			ttyDisplay->curx--;
544  | 		}
545  | 	}
546  | }
547  | 
548  | void
549  | cmov(x, y)
550  | register int x, y;
551  | {
552  | 	xputs(tgoto(nh_CM, x, y));
553  | 	ttyDisplay->cury = y;
554  | 	ttyDisplay->curx = x;
555  | }
556  | 
557  | /* See note at OVLx ifdef above.   xputc() is a special function. */
558  | void
559  | xputc(c)
560  | #if defined(apollo)
561  | int c;
562  | #else
563  | char c;
564  | #endif
565  | {
566  | 	(void) putchar(c);
567  | }
568  | 
569  | void
570  | xputs(s)
571  | const char *s;
572  | {
573  | # ifndef TERMLIB
574  | 	(void) fputs(s, stdout);
575  | # else
576  | #  if defined(NHSTDC) || defined(ULTRIX_PROTO)
577  | 	tputs(s, 1, (int (*)())xputc);
578  | #  else
579  | 	tputs(s, 1, xputc);
580  | #  endif
581  | # endif
582  | }
583  | 
584  | void
585  | cl_end()
586  | {
587  | 	if(CE)
588  | 		xputs(CE);
589  | 	else {	/* no-CE fix - free after Harold Rynes */
590  | 		/* this looks terrible, especially on a slow terminal
591  | 		   but is better than nothing */
592  | 		register int cx = ttyDisplay->curx+1;
593  | 
594  | 		while(cx < CO) {
595  | 			xputc(' ');
596  | 			cx++;
597  | 		}
598  | 		tty_curs(BASE_WINDOW, (int)ttyDisplay->curx+1,
599  | 						(int)ttyDisplay->cury);
600  | 	}
601  | }
602  | 
603  | #endif /* OVL0 */
604  | #ifdef OVLB
605  | 
606  | void
607  | clear_screen()
608  | {
609  | 	/* note: if CL is null, then termcap initialization failed,
610  | 		so don't attempt screen-oriented I/O during final cleanup.
611  | 	 */
612  | 	if (CL) {
613  | 		xputs(CL);
614  | 		home();
615  | 	}
616  | }
617  | 
618  | #endif /* OVLB */
619  | #ifdef OVL0
620  | 
621  | void
622  | home()
623  | {
624  | 	if(HO)
625  | 		xputs(HO);
626  | 	else if(nh_CM)
627  | 		xputs(tgoto(nh_CM, 0, 0));
628  | 	else
629  | 		tty_curs(BASE_WINDOW, 1, 0);	/* using UP ... */
630  | 	ttyDisplay->curx = ttyDisplay->cury = 0;
631  | }
632  | 
633  | void
634  | standoutbeg()
635  | {
636  | 	if(SO) xputs(SO);
637  | }
638  | 
639  | void
640  | standoutend()
641  | {
642  | 	if(SE) xputs(SE);
643  | }
644  | 
645  | #if 0	/* if you need one of these, uncomment it (here and in extern.h) */
646  | void
647  | revbeg()
648  | {
649  | 	if(MR) xputs(MR);
650  | }
651  | 
652  | void
653  | boldbeg()
654  | {
655  | 	if(MD) xputs(MD);
656  | }
657  | 
658  | void
659  | blinkbeg()
660  | {
661  | 	if(MB) xputs(MB);
662  | }
663  | 
664  | void
665  | dimbeg()
666  | /* not in most termcap entries */
667  | {
668  | 	if(MH) xputs(MH);
669  | }
670  | 
671  | void
672  | m_end()
673  | {
674  | 	if(ME) xputs(ME);
675  | }
676  | #endif
677  | 
678  | #endif /* OVL0 */
679  | #ifdef OVLB
680  | 
681  | void
682  | backsp()
683  | {
684  | 	xputs(BC);
685  | }
686  | 
687  | void
688  | tty_nhbell()
689  | {
690  | 	if (flags.silent) return;
691  | 	(void) putchar('\007');		/* curx does not change */
692  | 	(void) fflush(stdout);
693  | }
694  | 
695  | #endif /* OVLB */
696  | #ifdef OVL0
697  | 
698  | #ifdef ASCIIGRAPH
699  | void
700  | graph_on() {
701  | 	if (AS) xputs(AS);
702  | }
703  | 
704  | void
705  | graph_off() {
706  | 	if (AE) xputs(AE);
707  | }
708  | #endif
709  | 
710  | #endif /* OVL0 */
711  | #ifdef OVL1
712  | 
713  | #if !defined(MICRO)
714  | # ifdef VMS
715  | static const short tmspc10[] = {		/* from termcap */
716  | 	0, 2000, 1333, 909, 743, 666, 333, 166, 83, 55, 50, 41, 27, 20, 13, 10,
717  | 	5
718  | };
719  | # else
720  | static const short tmspc10[] = {		/* from termcap */
721  | 	0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5
722  | };
723  | # endif
724  | #endif
725  | 
726  | /* delay 50 ms */
727  | void
728  | tty_delay_output()
729  | {
730  | #if defined(MICRO)
731  | 	register int i;
732  | #endif
733  | #ifdef TIMED_DELAY
734  | 	if (flags.nap) {
735  | 		(void) fflush(stdout);
736  | 		msleep(50);		/* sleep for 50 milliseconds */
737  | 		return;
738  | 	}
739  | #endif
740  | #if defined(MICRO)
741  | 	/* simulate the delay with "cursor here" */
742  | 	for (i = 0; i < 3; i++) {
743  | 		cmov(ttyDisplay->curx, ttyDisplay->cury);
744  | 		(void) fflush(stdout);
745  | 	}
746  | #else /* MICRO */
747  | 	/* BUG: if the padding character is visible, as it is on the 5620
748  | 	   then this looks terrible. */
749  | 	if(flags.null)
750  | # ifdef TERMINFO
751  | 		/* cbosgd!cbcephus!pds for SYS V R2 */
752  | #  ifdef NHSTDC
753  | 		tputs("$<50>", 1, (int (*)())xputc);
754  | #  else
755  | 		tputs("$<50>", 1, xputc);
756  | #  endif
757  | # else
758  | #  if defined(NHSTDC) || defined(ULTRIX_PROTO)
759  | 		tputs("50", 1, (int (*)())xputc);
760  | #  else
761  | 		tputs("50", 1, xputc);
762  | #  endif
763  | # endif
764  | 
765  | 	else if(ospeed > 0 && ospeed < SIZE(tmspc10) && nh_CM) {
766  | 		/* delay by sending cm(here) an appropriate number of times */
767  | 		register int cmlen = strlen(tgoto(nh_CM, ttyDisplay->curx,
768  | 							ttyDisplay->cury));
769  | 		register int i = 500 + tmspc10[ospeed]/2;
770  | 
771  | 		while(i > 0) {
772  | 			cmov((int)ttyDisplay->curx, (int)ttyDisplay->cury);
773  | 			i -= cmlen*tmspc10[ospeed];
774  | 		}
775  | 	}
776  | #endif /* MICRO */
777  | }
778  | 
779  | #endif /* OVL1 */
780  | #ifdef OVLB
781  | 
782  | void
783  | cl_eos()			/* free after Robert Viduya */
784  | {				/* must only be called with curx = 1 */
785  | 
786  | 	if(nh_CD)
787  | 		xputs(nh_CD);
788  | 	else {
789  | 		register int cy = ttyDisplay->cury+1;
790  | 		while(cy <= LI-2) {
791  | 			cl_end();
792  | 			xputc('\n');
793  | 			cy++;
794  | 		}
795  | 		cl_end();
796  | 		tty_curs(BASE_WINDOW, (int)ttyDisplay->curx+1,
797  | 						(int)ttyDisplay->cury);
798  | 	}
799  | }
800  | 
801  | #if defined(TEXTCOLOR) && defined(TERMLIB)
802  | # if defined(UNIX) && defined(TERMINFO)
803  | /*
804  |  * Sets up color highlighting, using terminfo(4) escape sequences.
805  |  *
806  |  * Having never seen a terminfo system without curses, we assume this
807  |  * inclusion is safe.  On systems with color terminfo, it should define
808  |  * the 8 COLOR_FOOs, and avoid us having to guess whether this particular
809  |  * terminfo uses BGR or RGB for its indexes.
810  |  *
811  |  * If we don't get the definitions, then guess.  Original color terminfos
812  |  * used BGR for the original Sf (setf, Standard foreground) codes, but
813  |  * there was a near-total lack of user documentation, so some subsequent
814  |  * terminfos, such as early Linux ncurses and SCO UNIX, used RGB.  Possibly
815  |  * as a result of the confusion, AF (setaf, ANSI Foreground) codes were
816  |  * introduced, but this caused yet more confusion.  Later Linux ncurses
817  |  * have BGR Sf, RGB AF, and RGB COLOR_FOO, which appears to be the SVR4
818  |  * standard.  We could switch the colors around when using Sf with ncurses,
819  |  * which would help things on later ncurses and hurt things on early ncurses.
820  |  * We'll try just preferring AF and hoping it always agrees with COLOR_FOO,
821  |  * and falling back to Sf if AF isn't defined.
822  |  *
823  |  * In any case, treat black specially so we don't try to display black
824  |  * characters on the assumed black background.
825  |  */
826  | 
827  | 	/* `curses' is aptly named; various versions don't like these
828  | 	    macros used elsewhere within nethack; fortunately they're
829  | 	    not needed beyond this point, so we don't need to worry
830  | 	    about reconstructing them after the header file inclusion. */
831  | #undef delay_output
832  | #undef TRUE
833  | #undef FALSE
834  | #define m_move curses_m_move	/* Some curses.h decl m_move(), not used here */
835  | 
836  | #include <curses.h>
837  | 
838  | #ifndef LINUX
839  | extern char *tparm();
840  | #endif
841  | 
842  | #  ifdef COLOR_BLACK	/* trust include file */
843  | #undef COLOR_BLACK
844  | #  else
845  | #   ifndef _M_UNIX	/* guess BGR */
846  | #define COLOR_BLUE    1
847  | #define COLOR_GREEN   2
848  | #define COLOR_CYAN    3
849  | #define COLOR_RED     4
850  | #define COLOR_MAGENTA 5
851  | #define COLOR_YELLOW  6
852  | #define COLOR_WHITE   7
853  | #   else		/* guess RGB */
854  | #define COLOR_RED     1
855  | #define COLOR_GREEN   2
856  | #define COLOR_YELLOW  3
857  | #define COLOR_BLUE    4
858  | #define COLOR_MAGENTA 5
859  | #define COLOR_CYAN    6
860  | #define COLOR_WHITE   7
861  | #   endif
862  | #  endif
863  | #define COLOR_BLACK COLOR_BLUE
864  | 
865  | const int ti_map[8] = {
866  | 	COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW,
867  | 	COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE };
868  | 
869  | static void
870  | init_hilite()
871  | {
872  | 	register int c;
873  | 	char *setf, *scratch;
874  | 
875  | 	for (c = 0; c < SIZE(hilites); c++)
876  | 		hilites[c] = nh_HI;
877  | 	hilites[CLR_GRAY] = hilites[NO_COLOR] = (char *)0;
878  | 
879  | 	if (tgetnum("Co") < 8
880  | 	    || ((setf = tgetstr("AF", (char **)0)) == (char *)0
881  | 		 && (setf = tgetstr("Sf", (char **)0)) == (char *)0))
882  | 		return;
883  | 
884  | 	for (c = 0; c < CLR_MAX / 2; c++) {
885  | 	    scratch = tparm(setf, ti_map[c]);
886  | 	    if (c != CLR_GRAY) {
887  | 		hilites[c] = (char *) alloc(strlen(scratch) + 1);
888  | 		Strcpy(hilites[c], scratch);
889  | 	    }
890  | 	    if (c != CLR_BLACK) {
891  | 		hilites[c|BRIGHT] = (char*) alloc(strlen(scratch)+strlen(MD)+1);
892  | 		Strcpy(hilites[c|BRIGHT], MD);
893  | 		Strcat(hilites[c|BRIGHT], scratch);
894  | 	    }
895  | 
896  | 	}
897  | }
898  | 
899  | # else /* UNIX && TERMINFO */
900  | 
901  | #  ifndef TOS
902  | /* find the foreground and background colors set by nh_HI or nh_HE */
903  | static void
904  | analyze_seq (str, fg, bg)
905  | char *str;
906  | int *fg, *bg;
907  | {
908  | 	register int c, code;
909  | 	int len;
910  | 
911  | #   ifdef MICRO
912  | 	*fg = CLR_GRAY; *bg = CLR_BLACK;
913  | #   else
914  | 	*fg = *bg = NO_COLOR;
915  | #   endif
916  | 
917  | 	c = (str[0] == '\233') ? 1 : 2;	 /* index of char beyond esc prefix */
918  | 	len = strlen(str) - 1;		 /* length excluding attrib suffix */
919  | 	if ((c != 1 && (str[0] != '\033' || str[1] != '[')) ||
920  | 	    (len - c) < 1 || str[len] != 'm')
921  | 		return;
922  | 
923  | 	while (c < len) {
924  | 	    if ((code = atoi(&str[c])) == 0) { /* reset */
925  | 		/* this also catches errors */
926  | #   ifdef MICRO
927  | 		*fg = CLR_GRAY; *bg = CLR_BLACK;
928  | #   else
929  | 		*fg = *bg = NO_COLOR;
930  | #   endif
931  | 	    } else if (code == 1) { /* bold */
932  | 		*fg |= BRIGHT;
933  | #   if 0
934  | 	/* I doubt we'll ever resort to using blinking characters,
935  | 	   unless we want a pulsing glow for something.  But, in case
936  | 	   we do... - 3. */
937  | 	    } else if (code == 5) { /* blinking */
938  | 		*fg |= BLINK;
939  | 	    } else if (code == 25) { /* stop blinking */
940  | 		*fg &= ~BLINK;
941  | #   endif
942  | 	    } else if (code == 7 || code == 27) { /* reverse */
943  | 		code = *fg & ~BRIGHT;
944  | 		*fg = *bg | (*fg & BRIGHT);
945  | 		*bg = code;
946  | 	    } else if (code >= 30 && code <= 37) { /* hi_foreground RGB */
947  | 		*fg = code - 30;
948  | 	    } else if (code >= 40 && code <= 47) { /* hi_background RGB */
949  | 		*bg = code - 40;
950  | 	    }
951  | 	    while (digit(str[++c]));
952  | 	    c++;
953  | 	}
954  | }
955  | #  endif
956  | 
957  | /*
958  |  * Sets up highlighting sequences, using ANSI escape sequences (highlight code
959  |  * found in print.c).  The nh_HI and nh_HE sequences (usually from SO) are
960  |  * scanned to find foreground and background colors.
961  |  */
962  | 
963  | static void
964  | init_hilite()
965  | {
966  | 	register int c;
967  | #  ifdef TOS
968  | 	extern unsigned long tos_numcolors;	/* in tos.c */
969  | 	static char NOCOL[] = "\033b0", COLHE[] = "\033q\033b0";
970  | 
971  | 	if (tos_numcolors <= 2) {
972  | 		return;
973  | 	}
974  | /* Under TOS, the "bright" and "dim" colors are reversed. Moreover,
975  |  * on the Falcon the dim colors are *really* dim; so we make most
976  |  * of the colors the bright versions, with a few exceptions where
977  |  * the dim ones look OK.
978  |  */
979  | 	hilites[0] = NOCOL;
980  | 	for (c = 1; c < SIZE(hilites); c++) {
981  | 		char *foo;
982  | 		foo = (char *) alloc(sizeof("\033b0"));
983  | 		if (tos_numcolors > 4)
984  | 			Sprintf(foo, "\033b%c", (c&~BRIGHT)+'0');
985  | 		else
986  | 			Strcpy(foo, "\033b0");
987  | 		hilites[c] = foo;
988  | 	}
989  | 
990  | 	if (tos_numcolors == 4) {
991  | 		TI = "\033b0\033c3\033E\033e";
992  | 		TE = "\033b3\033c0\033J";
993  | 		nh_HE = COLHE;
994  | 		hilites[CLR_GREEN] = hilites[CLR_GREEN|BRIGHT] = "\033b2";
995  | 		hilites[CLR_RED] = hilites[CLR_RED|BRIGHT] = "\033b1";
996  | 	} else {
997  | 		sprintf(hilites[CLR_BROWN], "\033b%c", (CLR_BROWN^BRIGHT)+'0');
998  | 		sprintf(hilites[CLR_GREEN], "\033b%c", (CLR_GREEN^BRIGHT)+'0');
999  | 
1000 | 		TI = "\033b0\033c\017\033E\033e";
1001 | 		TE = "\033b\017\033c0\033J";
1002 | 		nh_HE = COLHE;
1003 | 		hilites[CLR_WHITE] = hilites[CLR_BLACK] = NOCOL;
1004 | 		hilites[NO_COLOR] = hilites[CLR_GRAY];
1005 | 	}
1006 | 
1007 | #  else /* TOS */
1008 | 
1009 | 	int backg, foreg, hi_backg, hi_foreg;
1010 | 
1011 | 	for (c = 0; c < SIZE(hilites); c++)
1012 | 	    hilites[c] = nh_HI;
1013 | 	hilites[CLR_GRAY] = hilites[NO_COLOR] = (char *)0;
1014 | 
1015 | 	analyze_seq(nh_HI, &hi_foreg, &hi_backg);
1016 | 	analyze_seq(nh_HE, &foreg, &backg);
1017 | 
1018 | 	for (c = 0; c < SIZE(hilites); c++)
1019 | 	    /* avoid invisibility */
1020 | 	    if ((backg & ~BRIGHT) != c) {
1021 | #   ifdef MICRO
1022 | 		if (c == CLR_BLUE) continue;
1023 | #   endif
1024 | 		if (c == foreg)
1025 | 		    hilites[c] = (char *)0;
1026 | 		else if (c != hi_foreg || backg != hi_backg) {
1027 | 		    hilites[c] = (char *) alloc(sizeof("\033[%d;3%d;4%dm"));
1028 | 		    Sprintf(hilites[c], "\033[%d", !!(c & BRIGHT));
1029 | 		    if ((c | BRIGHT) != (foreg | BRIGHT))
1030 | 			Sprintf(eos(hilites[c]), ";3%d", c & ~BRIGHT);
1031 | 		    if (backg != CLR_BLACK)
1032 | 			Sprintf(eos(hilites[c]), ";4%d", backg & ~BRIGHT);
1033 | 		    Strcat(hilites[c], "m");
1034 | 		}
1035 | 	    }
1036 | 
1037 | #   ifdef MICRO
1038 | 	/* brighten low-visibility colors */
1039 | 	hilites[CLR_BLUE] = hilites[CLR_BLUE|BRIGHT];
1040 | #   endif
1041 | #  endif /* TOS */
1042 | }
1043 | # endif /* UNIX */
1044 | 
1045 | static void
1046 | kill_hilite()
1047 | {
1048 | # ifndef TOS
1049 | 	register int c;
1050 | 
1051 | 	for (c = 0; c < CLR_MAX / 2; c++) {
1052 | 	    if (hilites[c|BRIGHT] == hilites[c])  hilites[c|BRIGHT] = 0;
1053 | 	    if (hilites[c] && (hilites[c] != nh_HI))
1054 | 		free((genericptr_t) hilites[c]),  hilites[c] = 0;
1055 | 	    if (hilites[c|BRIGHT] && (hilites[c|BRIGHT] != nh_HI))
1056 | 		free((genericptr_t) hilites[c|BRIGHT]),  hilites[c|BRIGHT] = 0;
1057 | 	}
1058 | # endif
1059 | 	return;
1060 | }
1061 | #endif /* TEXTCOLOR */
1062 | 
1063 | 
1064 | static char nulstr[] = "";
1065 | 
1066 | static char *
1067 | s_atr2str(n)
1068 | int n;
1069 | {
1070 |     switch (n) {
1071 | 	    case ATR_ULINE:
1072 | 		    if(nh_US) return nh_US;
1073 | 	    case ATR_BOLD:
1074 | 	    case ATR_BLINK:
1075 | 		    return nh_HI;
1076 | 	    case ATR_INVERSE:
1077 | 		    return MR;
1078 |     }
1079 |     return nulstr;
1080 | }
1081 | 
1082 | static char *
1083 | e_atr2str(n)
1084 | int n;
1085 | {
1086 |     switch (n) {
1087 | 	    case ATR_ULINE:
1088 | 		    if(nh_UE) return nh_UE;
1089 | 	    case ATR_BOLD:
1090 | 	    case ATR_BLINK:
1091 | 		    return nh_HE;
1092 | 	    case ATR_INVERSE:
1093 | 		    return ME;
1094 |     }
1095 |     return nulstr;
1096 | }
1097 | 
1098 | 
1099 | void
1100 | term_start_attr(attr)
1101 | int attr;
1102 | {
1103 | 	if (attr) {
1104 | 		xputs(s_atr2str(attr));
1105 | 	}
1106 | }
1107 | 
1108 | 
1109 | void
1110 | term_end_attr(attr)
1111 | int attr;
1112 | {
1113 | 	if(attr) {
1114 | 		xputs(e_atr2str(attr));
1115 | 	}
1116 | }
1117 | 
1118 | 
1119 | void
1120 | term_start_raw_bold()
1121 | {
1122 | 	xputs(nh_HI);
1123 | }
1124 | 
1125 | 
1126 | void
1127 | term_end_raw_bold()
1128 | {
1129 | 	xputs(nh_HE);
1130 | }
1131 | 
1132 | 
1133 | #ifdef TEXTCOLOR
1134 | 
1135 | void
1136 | term_end_color()
1137 | {
1138 | 	xputs(nh_HE);
1139 | }
1140 | 
1141 | 
1142 | void
1143 | term_start_color(color)
1144 | int color;
1145 | {
1146 | 	xputs(hilites[color]);
1147 | }
1148 | 
1149 | 
1150 | int
1151 | has_color(color)
1152 | int color;
1153 | {
1154 | 	return hilites[color] != (char *)0;
1155 | }
1156 | 
1157 | #endif /* TEXTCOLOR */
1158 | 
1159 | #endif /* OVLB */
1160 | 
1161 | #endif /* TTY_GRAPHICS */
1162 | 
1163 | /*termcap.c*/