OSDN Git Service

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