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