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