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