OSDN Git Service

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