Fix another impossible no_charge object
[NetHack.git] / win / tty / termcap.c
blob3af6333f9ba7e9ae1706d999306c07c9421de65b
1 /* NetHack 3.7 termcap.c $NHDT-Date: 1701946349 2023/12/07 10:52:29 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.60 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Pasi Kallinen, 2018. */
4 /* NetHack may be freely redistributed. See license for details. */
6 #include "hack.h"
8 #if defined(TTY_GRAPHICS) && !defined(NO_TERMS)
10 /* leave this undefined; it produces bad screen output with rxvt-unicode */
11 /*#define DECgraphicsOptimization*/
13 #include "wintty.h"
14 #include "tcap.h"
16 #define Tgetstr(key) (tgetstr(key, &tbufptr))
18 static char *s_atr2str(int);
19 static char *e_atr2str(int);
21 void cmov(int, int);
22 void nocmov(int, int);
23 void term_start_extracolor(uint32, uint16);
24 void term_end_extracolor(void);
26 #if defined(TERMLIB)
27 #if (!defined(UNIX) || !defined(TERMINFO)) && !defined(TOS)
28 static void analyze_seq(char *, int *, int *);
29 #endif
30 #endif
31 #if (defined(TERMLIB) || defined(ANSI_DEFAULT))
32 static void init_hilite(void);
33 static void kill_hilite(void);
34 #endif
36 /* (see tcap.h) -- nh_CM, nh_ND, nh_CD, nh_HI,nh_HE, nh_US,nh_UE, ul_hack */
37 struct tc_lcl_data tc_lcl_data = { 0, 0, 0, 0, 0, 0, 0, FALSE };
39 static char *nh_VI = (char *) 0; /* cursor_invisible */
40 static char *nh_VE = (char *) 0; /* cursor_normal */
41 /*static char *nh_VS = (char *) 0;*/ /* cursor_visible (highlighted cursor) */
43 static char *HO, *CL, *CE, *UP, *XD, *BC, *SO, *SE, *TI, *TE;
44 static char *VS, *VE;
45 static char *ME, *MR, *MB, *MH, *MD;
46 static char *ZH, *ZR;
48 #ifdef TERMLIB
49 boolean dynamic_HIHE = FALSE;
50 static int SG;
51 static char PC = '\0';
52 static char tbuf[512];
53 #endif /*TERMLIB*/
55 #ifdef TOS
56 const char *hilites[CLR_MAX]; /* terminal escapes for the various colors */
57 #else
58 char NEARDATA *hilites[CLR_MAX]; /* terminal escapes for the various colors */
59 #endif
61 static char *KS = (char *) 0, *KE = (char *) 0; /* keypad sequences */
62 static char nullstr[] = "";
64 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
65 extern boolean HE_resets_AS;
66 #endif
68 #ifndef TERMLIB
69 static char tgotobuf[20];
70 #ifdef TOS
71 #define tgoto(fmt, x, y) (Sprintf(tgotobuf, fmt, y + ' ', x + ' '), tgotobuf)
72 #else
73 #define tgoto(fmt, x, y) (Sprintf(tgotobuf, fmt, y + 1, x + 1), tgotobuf)
74 #endif
75 #endif /* TERMLIB */
77 /* these don't need to be part of 'struct instance_globals g' */
78 static char tty_standout_on[16], tty_standout_off[16];
80 void
81 term_startup(int *wid, int *hgt)
83 #ifdef TERMLIB
84 const char *term;
85 char *tptr;
86 char *tbufptr, *pc;
87 int i;
89 #ifdef VMS
90 term = verify_termcap();
91 if (!term)
92 #endif
93 term = getenv("TERM");
95 #if defined(TOS) && defined(__GNUC__)
96 if (!term)
97 term = "builtin"; /* library has a default */
98 #endif
99 if (!term)
100 #endif /* TERMLIB */
101 #ifndef ANSI_DEFAULT
102 error("Can't get TERM.");
103 #else
104 #ifdef TOS
106 CO = 80;
107 LI = 25;
108 TI = VS = VE = TE = nullstr;
110 * FIXME: These variables ought to be declared 'const' (instead
111 * of using nhStr() to cast away const) to avoid '-Wwrite-sttings'
112 * warnings about assigning string literals to them.
114 HO = nhStr("\033H");
115 CE = nhStr("\033K"); /* the VT52 termcap */
116 UP = nhStr("\033A");
117 nh_CM = nhStr("\033Y%c%c"); /* used with function tgoto() */
118 nh_ND = nhStr("\033C");
119 XD = nhStr("\033B");
120 BC = nhStr("\033D");
121 SO = nhStr("\033p");
122 SE = nhStr("\033q");
123 /* HI and HE will be updated in init_hilite if we're using color */
124 nh_HI = nhStr("\033p");
125 nh_HE = nhStr("\033q");
126 *wid = CO;
127 *hgt = LI;
128 CL = nhStr("\033E"); /* last thing set */
129 return;
131 #else /* TOS */
133 #ifdef MICRO
134 get_scr_size();
135 #ifdef CLIPPING
136 if (CO < COLNO || LI < ROWNO + 3)
137 setclipped();
138 #endif
139 #endif
140 HO = nhStr("\033[H");
141 /* nh_CD = nhStr("\033[J"); */
142 CE = nhStr("\033[K"); /* the ANSI termcap */
143 #ifndef TERMLIB
144 nh_CM = nhStr("\033[%d;%dH");
145 #else
146 nh_CM = nhStr("\033[%i%d;%dH");
147 #endif
148 UP = nhStr("\033[A");
149 nh_ND = nhStr("\033[C");
150 XD = nhStr("\033[B");
151 #ifdef MICRO /* backspaces are non-destructive */
152 BC = nhStr("\b");
153 #else
154 BC = nhStr("\033[D");
155 #endif
156 nh_HI = SO = nhStr("\033[1m");
157 nh_US = nhStr("\033[4m");
158 MR = nhStr("\033[7m");
159 TI = nh_HE = ME = SE = nh_UE = nhStr("\033[0m");
160 /* strictly, SE should be 2, and nh_UE should be 24,
161 but we can't trust all ANSI emulators to be
162 that complete. -3. */
163 #ifndef MICRO
164 AS = nhStr("\016");
165 AE = nhStr("\017");
166 #endif
167 TE = VS = VE = nullstr;
168 init_hilite();
169 *wid = CO;
170 *hgt = LI;
171 CL = nhStr("\033[2J"); /* last thing set */
172 return;
174 #endif /* TOS */
175 #endif /* ANSI_DEFAULT */
177 #ifdef TERMLIB
178 tptr = (char *) alloc(1024);
180 tbufptr = tbuf;
181 if (!strncmp(term, "5620", 4))
182 flags.null = FALSE; /* this should be a termcap flag */
183 if (tgetent(tptr, term) < 1) {
184 char buf[BUFSZ];
185 (void) strncpy(buf, term,
186 (BUFSZ - 1) - (sizeof("Unknown terminal type: . ")));
187 buf[BUFSZ - 1] = '\0';
188 error("Unknown terminal type: %s.", term);
190 if ((pc = Tgetstr(nhStr("pc"))) != 0)
191 PC = *pc;
193 if (!(BC = Tgetstr(nhStr("le")))) { /* both termcap and terminfo use le */
194 #ifdef TERMINFO
195 error("Terminal must backspace.");
196 #else
197 if (!(BC = Tgetstr(nhStr("bc")))) { /* termcap also uses bc/bs */
198 #ifndef MINIMAL_TERM
199 if (!tgetflag(nhStr("bs")))
200 error("Terminal must backspace.");
201 #endif
202 BC = tbufptr;
203 tbufptr += 2;
204 *BC = '\b';
206 #endif
209 #ifdef MINIMAL_TERM
210 HO = (char *) 0;
211 #else
212 HO = Tgetstr(nhStr("ho"));
213 #endif
215 * LI and CO are set in ioctl.c via a TIOCGWINSZ if available. If
216 * the kernel has values for either we should use them rather than
217 * the values from TERMCAP ...
219 #ifndef MICRO
220 if (!CO)
221 CO = tgetnum(nhStr("co"));
222 if (!LI)
223 LI = tgetnum(nhStr("li"));
224 #else
225 #if defined(TOS) && defined(__GNUC__)
226 if (!strcmp(term, "builtin")) {
227 get_scr_size();
228 } else
229 #endif
231 CO = tgetnum(nhStr("co"));
232 LI = tgetnum(nhStr("li"));
233 if (!LI || !CO) /* if we don't override it */
234 get_scr_size();
236 #endif /* ?MICRO */
237 #ifdef CLIPPING
238 if (CO < COLNO || LI < ROWNO + 3)
239 setclipped();
240 #endif
241 nh_ND = Tgetstr(nhStr("nd")); /* move cursor right 1 column */
242 if (tgetflag(nhStr("os"))) /* term can overstrike */
243 error("NetHack can't have OS.");
244 if (tgetflag(nhStr("ul"))) /* underline by overstrike w/ underscore */
245 ul_hack = TRUE;
246 CE = Tgetstr(nhStr("ce")); /* clear line from cursor to eol */
247 UP = Tgetstr(nhStr("up")); /* move cursor up 1 line */
248 /* It seems that xd is no longer supported, and we should use
249 a linefeed instead; unfortunately this requires resetting
250 CRMOD, and many output routines will have to be modified
251 slightly. Let's leave that till the next release. */
252 XD = Tgetstr(nhStr("xd"));
253 /* not: XD = Tgetstr("do"); */
254 if (!(nh_CM = Tgetstr(nhStr("cm")))) { /* cm: move cursor */
255 if (!UP && !HO)
256 error("NetHack needs CM or UP or HO.");
257 tty_raw_print("Playing NetHack on terminals without CM is suspect.");
258 tty_wait_synch();
260 SO = Tgetstr(nhStr("so")); /* standout start */
261 SE = Tgetstr(nhStr("se")); /* standout end */
262 nh_US = Tgetstr(nhStr("us")); /* underline start */
263 nh_UE = Tgetstr(nhStr("ue")); /* underline end */
264 ZH = Tgetstr(nhStr("ZH")); /* italic start */
265 ZR = Tgetstr(nhStr("ZR")); /* italic end */
266 SG = tgetnum(nhStr("sg")); /* -1: not fnd; else # of spaces left by so */
267 if (!SO || !SE || (SG > 0))
268 SO = SE = nh_US = nh_UE = nullstr;
269 TI = Tgetstr(nhStr("ti")); /* nonconsequential cursor movement start */
270 TE = Tgetstr(nhStr("te")); /* nonconsequential cursor movement end */
271 VS = VE = nullstr;
272 #ifdef TERMINFO
273 VS = Tgetstr(nhStr("eA")); /* enable graphics */
274 #endif
275 KS = Tgetstr(nhStr("ks")); /* keypad start (special mode) */
276 KE = Tgetstr(nhStr("ke")); /* keypad end (ordinary mode [ie, digits]) */
277 MR = Tgetstr(nhStr("mr")); /* reverse */
278 MB = Tgetstr(nhStr("mb")); /* blink */
279 MD = Tgetstr(nhStr("md")); /* boldface */
280 if (!SO)
281 SO = MD;
282 MH = Tgetstr(nhStr("mh")); /* dim */
283 ME = Tgetstr(nhStr("me")); /* turn off all attributes */
284 if (!ME)
285 ME = SE ? SE : nullstr; /* default to SE value */
287 nh_VI = Tgetstr(nhStr("vi"));
288 nh_VE = Tgetstr(nhStr("ve"));
289 /*nh_VS = Tgetstr(nhStr("vs"));*/
290 if (!nh_VI || !nh_VE /*|| !nh_VS*/ )
291 nh_VI = nh_VE = /*nh_VS =*/ (char *) 0;
293 /* Get rid of padding numbers for nh_HI and nh_HE. Hope they
294 * aren't really needed!!! nh_HI and nh_HE are outputted to the
295 * pager as a string - so how can you send it NULs???
296 * -jsb
298 for (i = 0; digit(SO[i]); ++i)
299 continue;
300 nh_HI = dupstr(&SO[i]);
301 for (i = 0; digit(ME[i]); ++i)
302 continue;
303 nh_HE = dupstr(&ME[i]);
304 dynamic_HIHE = TRUE;
306 AS = Tgetstr(nhStr("as")); /* alt charset start */
307 AE = Tgetstr(nhStr("ae")); /* alt charset end */
308 nh_CD = Tgetstr(nhStr("cd")); /* clear lines from cursor and down */
309 #if defined(TOS) && defined(__GNUC__)
310 if (!strcmp(term, "builtin") || !strcmp(term, "tw52")
311 || !strcmp(term, "st52")) {
312 init_hilite();
314 #else
315 init_hilite();
316 #endif
317 *wid = CO;
318 *hgt = LI;
319 /* cl: clear screen, set cursor to upper left */
320 if (!(CL = Tgetstr(nhStr("cl")))) /* last thing set */
321 error("NetHack needs CL.");
322 if ((int) (tbufptr - tbuf) > (int) (sizeof tbuf))
323 error("TERMCAP entry too big...\n");
324 free((genericptr_t) tptr);
325 #endif /* TERMLIB */
326 /* keep static copies of these so that raw_print_bold() will work
327 after exit_nhwindows(); if the sequences are too long, then bold
328 won't work after that--it will be rendered as ordinary text */
329 if (nh_HI && strlen(nh_HI) < sizeof tty_standout_on)
330 Strcpy(tty_standout_on, nh_HI);
331 if (nh_HE && strlen(nh_HE) < sizeof tty_standout_off)
332 Strcpy(tty_standout_off, nh_HE);
335 /* note: at present, this routine is not part of the formal window interface
337 /* deallocate resources prior to final termination */
338 void
339 term_shutdown(void)
341 /* we only attempt to clean up a few individual termcap variables */
342 #if defined(TERMLIB) || defined(ANSI_DEFAULT)
343 kill_hilite();
344 #endif
345 #ifdef TERMLIB
346 if (dynamic_HIHE) {
347 free((genericptr_t) nh_HI), nh_HI = (char *) 0;
348 free((genericptr_t) nh_HE), nh_HE = (char *) 0;
349 dynamic_HIHE = FALSE;
351 #endif
352 return;
355 void
356 tty_number_pad(int state)
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;
373 #ifdef TERMLIB
374 extern void (*decgraphics_mode_callback)(void); /* defined in symbols.c */
375 static void tty_decgraphics_termcap_fixup(void);
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.
384 static void
385 tty_decgraphics_termcap_fixup(void)
387 static char ctrlN[] = "\016";
388 static char ctrlO[] = "\017";
389 static char appMode[] = "\033=";
390 static char numMode[] = "\033>";
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) */
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.
406 if (SYMHANDLING(H_DEC)) {
407 xputs("\033)0"); /* "\e)0" load line drawing chars as secondary set */
408 /* TI doesn't necessarily do this; explicitly switch to primary
409 font in case previous program (either before starting nethack
410 or during a shell escape) left the alternate font active */
411 xputs(AE);
414 #ifdef PC9800
415 init_hilite();
416 #endif
418 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
419 #if DECgraphicsOptimization
420 /* some termcaps suffer from the bizarre notion that resetting
421 video attributes should also reset the chosen character set */
422 if (dynamic_HIHE) {
423 assert(nh_HE != NULL);
424 xputs(nh_HE); /* turn off any active highlighting (before maybe
425 * changing HE or AE) */
426 (void) strsubst(nh_HE, AE, "");
427 (void) strsubst(nh_HE, ctrlO, "");
428 /* if AE has prefixing, substituting an empty string for it in HE
429 would only work if it is a leading prefix of HE; remove the
430 magic sequences that loads US set ("\e(B") or UK set ("\e(A")
431 into the primary character set since we don't want HE to do that */
432 (void) strsubst(nh_HE, "\033(B", "");
433 (void) strsubst(nh_HE, "\033(A", "");
435 #endif
437 /* if AE is still present in HE, set a flag so that glyph writing
438 code will know that AS needs to be refreshed for consecutive
439 line drawing characters */
440 const char *ae = AE;
442 if (digit(*ae)) { /* skip over delay prefix, if any */
444 ++ae;
445 while (digit(*ae));
446 if (*ae == '.') {
447 ++ae;
448 if (digit(*ae))
449 ++ae;
451 if (*ae == '*')
452 ++ae;
454 /* stdc strstr(), not nethack's strstri(); HE ends color, ME ends
455 inverse video; they might have the same value; sequences to end
456 other attributes aren't known to sometimes contain AE */
457 if ((nh_HE && strstr(nh_HE, ae)) || (ME && strstr(ME, ae)))
458 HE_resets_AS = TRUE;
460 #ifdef DECgraphicsOptimization
461 /* some termcaps have AS load the line-drawing character set as
462 primary instead of having initialization load it as secondary
463 (we've already done that init) and then having AS simply switch
464 to secondary (change to do that now); they also have AE load
465 the US character set, which we avoid by not touching primary;
466 if HE_resets_AS, we can't simplify AS/AE due to the risk that
467 HE is changing the primary set rather than just toggling to it */
468 if (!HE_resets_AS && !strcmp(AS, "\033(0") && !strcmp(AE, "\033(B")) {
469 /* first output old AE to make sure we aren't about to leave
470 primary set with line drawing chars */
471 xputs(AE);
472 AS = ctrlN;
473 AE = ctrlO;
475 #endif /* DECgraphicsOptimization */
476 xputs(AE);
477 #endif /* ASCIIGRAPH && !NO_TERMS */
479 #endif /* TERMLIB */
481 #if defined(ASCIIGRAPH) && defined(PC9800)
482 extern void (*ibmgraphics_mode_callback)(void); /* defined in symbols.c */
483 #endif
484 extern void (*utf8graphics_mode_callback)(void); /* defined in symbols.c */
486 #ifdef PC9800
487 extern void (*ascgraphics_mode_callback)(void); /* defined in symbols.c */
488 static void tty_ascgraphics_hilite_fixup(void);
490 static void
491 tty_ascgraphics_hilite_fixup(void)
493 int c;
495 for (c = 0; c < CLR_MAX / 2; c++)
496 if (c != CLR_BLACK) {
497 hilites[c | BRIGHT] = (char *) alloc(sizeof "\033[1;3%dm");
498 Sprintf(hilites[c | BRIGHT], "\033[1;3%dm", c);
499 if (c != CLR_GRAY) {
500 hilites[c] = (char *) alloc(sizeof "\033[0;3%dm");
501 Sprintf(hilites[c], "\033[0;3%dm", c);
505 #endif /* PC9800 */
507 void
508 term_start_screen(void)
510 xputs(TI);
511 xputs(VS);
512 #ifdef PC9800
513 if (!SYMHANDLING(H_IBM))
514 tty_ascgraphics_hilite_fixup();
515 /* set up callback in case option is not set yet but toggled later */
516 ascgraphics_mode_callback = tty_ascgraphics_hilite_fixup;
517 #ifdef ASCIIGRAPH
518 if (SYMHANDLING(H_IBM))
519 init_hilite();
520 /* set up callback in case option is not set yet but toggled later */
521 ibmgraphics_mode_callback = init_hilite;
522 #endif
523 #endif /* PC9800 */
525 #ifdef TERMLIB
526 if (SYMHANDLING(H_DEC))
527 tty_decgraphics_termcap_fixup();
528 /* set up callback in case option is not set yet but toggled later */
529 decgraphics_mode_callback = tty_decgraphics_termcap_fixup;
530 #endif
531 #ifdef ENHANCED_SYMBOLS
532 utf8graphics_mode_callback = tty_utf8graphics_fixup;
533 #endif
535 if (gc.Cmd.num_pad)
536 tty_number_pad(1); /* make keypad send digits */
539 void
540 term_end_screen(void)
542 term_clear_screen();
543 xputs(VE);
544 xputs(TE);
547 /* Cursor movements */
549 /* Note to overlay tinkerers. The placement of this overlay controls the
550 location of the function xputc(). This function is not currently in
551 trampoli.[ch] files for what is deemed to be performance reasons. If
552 this define is moved and or xputc() is taken out of the ROOT overlay,
553 then action must be taken in trampoli.[ch]. */
555 void
556 nocmov(int x, int y)
558 if ((int) ttyDisplay->cury > y) {
559 if (UP) {
560 while ((int) ttyDisplay->cury > y) { /* Go up. */
561 xputs(UP);
562 ttyDisplay->cury--;
564 } else if (nh_CM) {
565 cmov(x, y);
566 } else if (HO) {
567 home();
568 tty_curs(BASE_WINDOW, x + 1, y);
569 } /* else impossible("..."); */
570 } else if ((int) ttyDisplay->cury < y) {
571 if (XD) {
572 while ((int) ttyDisplay->cury < y) {
573 xputs(XD);
574 ttyDisplay->cury++;
576 } else if (nh_CM) {
577 cmov(x, y);
578 } else {
579 while ((int) ttyDisplay->cury < y) {
580 (void) xputc('\n');
581 ttyDisplay->curx = 0;
582 ttyDisplay->cury++;
586 if ((int) ttyDisplay->curx < x) { /* Go to the right. */
587 if (!nh_ND) {
588 cmov(x, y);
589 } else { /* bah */
590 /* should instead print what is there already */
591 while ((int) ttyDisplay->curx < x) {
592 xputs(nh_ND);
593 ttyDisplay->curx++;
596 } else if ((int) ttyDisplay->curx > x) {
597 while ((int) ttyDisplay->curx > x) { /* Go to the left. */
598 xputs(BC);
599 ttyDisplay->curx--;
604 void
605 cmov(int x, int y)
607 xputs(tgoto(nh_CM, x, y));
608 ttyDisplay->cury = y;
609 ttyDisplay->curx = x;
612 /* See note above. xputc() is a special function for overlays. */
614 xputc(int c) /* actually char, but explicitly specify its widened type */
617 * Note: xputc() as a direct all to putchar() doesn't make any
618 * sense _if_ putchar() is a function. But if it is a macro, an
619 * overlay configuration would want to avoid hidden code bloat
620 * from multiple putchar() expansions. And it gets passed as an
621 * argument to tputs() so we have to guarantee an actual function
622 * (while possibly lacking ANSI's (func) syntax to override macro).
624 * xputc() used to be declared as 'void xputc(c) char c; {}' but
625 * avoiding the proper type 'int' just to avoid (void) casts when
626 * ignoring the result can't have been sufficient reason to add it.
627 * It also had '#if apollo' conditional to have the arg be int.
628 * Matching putchar()'s declaration and using explicit casts where
629 * warranted is more robust, so we're just a jacket around that.
631 return putchar(c);
634 void
635 xputs(const char *s)
637 #ifndef TERMLIB
638 (void) fputs(s, stdout);
639 #else
640 tputs(s, 1, xputc);
641 #endif
644 void
645 cl_end(void)
647 if (CE) {
648 xputs(CE);
649 } else { /* no-CE fix - free after Harold Rynes */
650 int cx = ttyDisplay->curx + 1;
652 /* this looks terrible, especially on a slow terminal
653 but is better than nothing */
654 while (cx < CO) {
655 (void) xputc(' ');
656 cx++;
658 tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1,
659 (int) ttyDisplay->cury);
663 void
664 term_clear_screen(void)
666 /* note: if CL is null, then termcap initialization failed,
667 * so don't attempt screen-oriented I/O during final cleanup.
669 if (CL) {
670 xputs(CL);
671 home();
672 /* set remembered data to all spaces */
673 erase_tty_screen();
677 void
678 home(void)
680 if (HO)
681 xputs(HO);
682 else if (nh_CM)
683 xputs(tgoto(nh_CM, 0, 0));
684 else
685 tty_curs(BASE_WINDOW, 1, 0); /* using UP ... */
686 ttyDisplay->curx = ttyDisplay->cury = 0;
689 void
690 standoutbeg(void)
692 if (SO)
693 xputs(SO);
696 void
697 standoutend(void)
699 if (SE)
700 xputs(SE);
703 #if 0 /* if you need one of these, uncomment it (here and in extern.h) */
704 void
705 revbeg(void)
707 if (MR)
708 xputs(MR);
711 void
712 boldbeg(void)
714 if (MD)
715 xputs(MD);
718 void
719 blinkbeg(void)
721 if (MB)
722 xputs(MB);
725 void
726 dimbeg(void)
728 /* not in most termcap entries */
729 if (MH)
730 xputs(MH);
733 void
734 m_end(void)
736 if (ME)
737 xputs(ME);
739 #endif /*0*/
741 void
742 backsp(void)
744 xputs(BC);
747 void
748 tty_nhbell(void)
750 if (flags.silent)
751 return;
752 (void) putchar('\007'); /* curx does not change */
753 (void) fflush(stdout);
756 #ifdef ASCIIGRAPH
757 void
758 graph_on(void)
760 if (AS)
761 xputs(AS);
764 void
765 graph_off(void)
767 if (AE)
768 xputs(AE);
770 #endif
772 #if !defined(MICRO)
773 #ifdef VMS
774 static const short tmspc10[] = { /* from termcap */
775 0, 2000, 1333, 909, 743, 666, 333, 166, 83,
776 55, 50, 41, 27, 20, 13, 10, 5
778 #else
779 static const short tmspc10[] = { /* from termcap */
780 0, 2000, 1333, 909, 743, 666, 500, 333, 166,
781 83, 55, 41, 20, 10, 5
783 #endif
784 #endif
786 /* delay 50 ms */
787 void
788 tty_delay_output(void)
790 #if defined(MICRO)
791 int i;
792 #endif
793 if (iflags.debug_fuzzer)
794 return;
795 #ifdef TIMED_DELAY
796 if (flags.nap) {
797 (void) fflush(stdout);
798 msleep(50); /* sleep for 50 milliseconds */
799 return;
801 #endif
802 #if defined(MICRO)
803 /* simulate the delay with "cursor here" */
804 for (i = 0; i < 3; i++) {
805 cmov(ttyDisplay->curx, ttyDisplay->cury);
806 (void) fflush(stdout);
808 #else /* MICRO */
809 /* BUG: if the padding character is visible, as it is on the 5620
810 then this looks terrible. */
811 if (flags.null) {
812 tputs(
813 #ifdef TERMINFO
814 "$<50>",
815 #else
816 "50",
817 #endif
818 1, xputc);
820 } else if (ospeed > 0 && ospeed < SIZE(tmspc10) && nh_CM) {
821 /* delay by sending cm(here) an appropriate number of times */
822 int cmlen =
823 (int) strlen(tgoto(nh_CM, ttyDisplay->curx, ttyDisplay->cury));
824 int i = 500 + tmspc10[ospeed] / 2;
826 while (i > 0) {
827 cmov((int) ttyDisplay->curx, (int) ttyDisplay->cury);
828 i -= cmlen * tmspc10[ospeed];
831 #endif /* MICRO */
834 /* must only be called with curx = 1 */
835 void
836 cl_eos(void) /* free after Robert Viduya */
838 if (nh_CD) {
839 xputs(nh_CD);
840 } else {
841 int cy = ttyDisplay->cury + 1;
843 while (cy <= LI - 2) {
844 cl_end();
845 (void) xputc('\n');
846 cy++;
848 cl_end();
849 tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1,
850 (int) ttyDisplay->cury);
854 #if defined(TERMLIB)
855 #if defined(UNIX) && defined(TERMINFO)
857 * Sets up color highlighting, using terminfo(4) escape sequences.
859 * Having never seen a terminfo system without curses, we assume this
860 * inclusion is safe. On systems with color terminfo, it should define
861 * the 8 COLOR_FOOs, and avoid us having to guess whether this particular
862 * terminfo uses BGR or RGB for its indexes.
864 * If we don't get the definitions, then guess. Original color terminfos
865 * used BGR for the original Sf (setf, Standard foreground) codes, but
866 * there was a near-total lack of user documentation, so some subsequent
867 * terminfos, such as early Linux ncurses and SCO UNIX, used RGB. Possibly
868 * as a result of the confusion, AF (setaf, ANSI Foreground) codes were
869 * introduced, but this caused yet more confusion. Later Linux ncurses
870 * have BGR Sf, RGB AF, and RGB COLOR_FOO, which appears to be the SVR4
871 * standard. We could switch the colors around when using Sf with ncurses,
872 * which would help things on later ncurses and hurt things on early ncurses.
873 * We'll try just preferring AF and hoping it always agrees with COLOR_FOO,
874 * and falling back to Sf if AF isn't defined.
876 * In any case, treat black specially so we don't try to display black
877 * characters on the assumed black background.
880 /* `curses' is aptly named; various versions don't like these
881 macros used elsewhere within nethack; fortunately they're
882 not needed beyond this point, so we don't need to worry
883 about reconstructing them after the header file inclusion. */
884 #undef TRUE
885 #undef FALSE
886 #define m_move curses_m_move /* some curses.h decl m_move(), not used here */
888 #include <curses.h>
890 #if !defined(LINUX) && !defined(__FreeBSD__) && !defined(NOTPARMDECL)
891 extern char *tparm();
892 #endif
894 #ifndef COLOR_BLACK /* trust include file */
895 #ifndef _M_UNIX /* guess BGR */
896 #define COLOR_BLACK 0
897 #define COLOR_BLUE 1
898 #define COLOR_GREEN 2
899 #define COLOR_CYAN 3
900 #define COLOR_RED 4
901 #define COLOR_MAGENTA 5
902 #define COLOR_YELLOW 6
903 #define COLOR_WHITE 7
904 #else /* guess RGB */
905 #define COLOR_BLACK 0
906 #define COLOR_RED 1
907 #define COLOR_GREEN 2
908 #define COLOR_YELLOW 3
909 #define COLOR_BLUE 4
910 #define COLOR_MAGENTA 5
911 #define COLOR_CYAN 6
912 #define COLOR_WHITE 7
913 #endif
914 #endif
916 /* Mapping data for the six terminfo colors that resolve to pairs of nethack
917 * colors. Black and white are handled specially.
919 const struct {
920 int ti_color, nh_color, nh_bright_color;
921 } ti_map[6] = { { COLOR_RED, CLR_RED, CLR_ORANGE },
922 { COLOR_GREEN, CLR_GREEN, CLR_BRIGHT_GREEN },
923 { COLOR_YELLOW, CLR_BROWN, CLR_YELLOW },
924 { COLOR_BLUE, CLR_BLUE, CLR_BRIGHT_BLUE },
925 { COLOR_MAGENTA, CLR_MAGENTA, CLR_BRIGHT_MAGENTA },
926 { COLOR_CYAN, CLR_CYAN, CLR_BRIGHT_CYAN } };
928 typedef struct {
929 unsigned char r, g, b;
930 } RGB;
932 static char nilstring[] = "";
934 static void
935 init_hilite(void)
937 int c, colors;
938 char *setf, *scratch;
940 colors = tgetnum(nhStr("Co"));
941 iflags.colorcount = colors;
942 int md_len = 0;
944 if (colors < 8 || !MD || !*MD
945 || ((setf = tgetstr(nhStr("AF"), (char **) 0)) == (char *) 0
946 && (setf = tgetstr(nhStr("Sf"), (char **) 0)) == (char *) 0)) {
947 /* Fallback when colors not available
948 * It's arbitrary to collapse all colors except gray
949 * together, but that's what the previous code did.
951 hilites[CLR_BLACK] = nh_HI;
952 hilites[CLR_RED] = nh_HI;
953 hilites[CLR_GREEN] = nh_HI;
954 hilites[CLR_BROWN] = nh_HI;
955 hilites[CLR_BLUE] = nh_HI;
956 hilites[CLR_MAGENTA] = nh_HI;
957 hilites[CLR_CYAN] = nh_HI;
958 hilites[CLR_GRAY] = nilstring;
959 hilites[NO_COLOR] = nilstring;
960 hilites[CLR_ORANGE] = nh_HI;
961 hilites[CLR_BRIGHT_GREEN] = nh_HI;
962 hilites[CLR_YELLOW] = nh_HI;
963 hilites[CLR_BRIGHT_BLUE] = nh_HI;
964 hilites[CLR_BRIGHT_MAGENTA] = nh_HI;
965 hilites[CLR_BRIGHT_CYAN] = nh_HI;
966 hilites[CLR_WHITE] = nh_HI;
967 return;
970 if (colors >= 16) {
971 for (c = 0; c < SIZE(ti_map); c++) {
972 /* system colors */
973 scratch = tparm(setf, ti_map[c].nh_color);
974 hilites[ti_map[c].nh_color] = dupstr(scratch);
975 /* bright colors */
976 scratch = tparm(setf, ti_map[c].nh_bright_color);
977 hilites[ti_map[c].nh_bright_color] = dupstr(scratch);
979 } else {
980 /* 8 system colors */
981 md_len = (int) strlen(MD);
983 c = 6;
984 while (c--) {
985 char *work;
987 scratch = tparm(setf, ti_map[c].ti_color);
988 work = (char *) alloc(strlen(scratch) + md_len + 1);
989 Strcpy(work, MD);
990 hilites[ti_map[c].nh_bright_color] = work;
991 work += md_len;
992 Strcpy(work, scratch);
993 hilites[ti_map[c].nh_color] = work;
997 if (colors >= 16) {
998 scratch = tparm(setf, COLOR_WHITE | BRIGHT);
999 hilites[CLR_WHITE] = dupstr(scratch);
1000 } else {
1001 scratch = tparm(setf, COLOR_WHITE);
1002 hilites[CLR_WHITE] = (char *) alloc(strlen(scratch) + md_len + 1);
1003 Strcpy(hilites[CLR_WHITE], MD);
1004 Strcat(hilites[CLR_WHITE], scratch);
1007 hilites[CLR_GRAY] = nilstring;
1008 hilites[NO_COLOR] = nilstring;
1010 if (iflags.wc2_darkgray) {
1011 if (colors >= 16) {
1012 scratch = tparm(setf, COLOR_BLACK|BRIGHT);
1013 hilites[CLR_BLACK] = dupstr(scratch);
1014 } else {
1015 /* On many terminals, esp. those using classic PC CGA/EGA/VGA
1016 * textmode, specifying "hilight" and "black" simultaneously
1017 * produces a dark shade of gray that is visible against a
1018 * black background. We can use it to represent black objects.
1020 scratch = tparm(setf, COLOR_BLACK);
1021 hilites[CLR_BLACK] = (char *) alloc(strlen(scratch) + md_len + 1);
1022 Strcpy(hilites[CLR_BLACK], MD);
1023 Strcat(hilites[CLR_BLACK], scratch);
1025 } else {
1026 /* But it's conceivable that hilighted black-on-black could
1027 * still be invisible on many others. We substitute blue for
1028 * black.
1030 hilites[CLR_BLACK] = hilites[CLR_BLUE];
1034 static void
1035 kill_hilite(void)
1037 int c;
1039 /* if colors weren't available, no freeing needed */
1040 if (hilites[CLR_BLACK] == nh_HI)
1041 return;
1043 /* hilites[] will be set to NULL below, whether freed here or not */
1045 if (hilites[CLR_BLACK]) {
1046 if (hilites[CLR_BLACK] != hilites[CLR_BLUE])
1047 free(hilites[CLR_BLACK]), hilites[CLR_BLACK] = NULL;
1049 if (tgetnum(nhStr("Co")) >= 16) {
1050 if (hilites[CLR_BLUE])
1051 free(hilites[CLR_BLUE]);
1052 if (hilites[CLR_GREEN])
1053 free(hilites[CLR_GREEN]);
1054 if (hilites[CLR_CYAN])
1055 free(hilites[CLR_CYAN]);
1056 if (hilites[CLR_MAGENTA])
1057 free(hilites[CLR_MAGENTA]);
1058 if (hilites[CLR_RED])
1059 free(hilites[CLR_RED]);
1060 if (hilites[CLR_BROWN])
1061 free(hilites[CLR_BROWN]);
1062 } else {
1063 /* CLR_BLUE overlaps CLR_BRIGHT_BLUE, do not free */
1064 /* CLR_GREEN overlaps CLR_BRIGHT_GREEN, do not free */
1065 /* CLR_CYAN overlaps CLR_BRIGHT_CYAN, do not free */
1066 /* CLR_MAGENTA overlaps CLR_BRIGHT_MAGENTA, do not free */
1067 /* CLR_RED overlaps CLR_ORANGE, do not free */
1068 /* CLR_BROWN overlaps CLR_YELLOW, do not free */
1070 /* CLR_GRAY is static 'nilstring', do not free */
1071 /* NO_COLOR is static 'nilstring', do not free */
1072 if (hilites[CLR_BRIGHT_BLUE])
1073 free(hilites[CLR_BRIGHT_BLUE]);
1074 if (hilites[CLR_BRIGHT_GREEN])
1075 free(hilites[CLR_BRIGHT_GREEN]);
1076 if (hilites[CLR_BRIGHT_CYAN])
1077 free(hilites[CLR_BRIGHT_CYAN]);
1078 if (hilites[CLR_BRIGHT_MAGENTA])
1079 free(hilites[CLR_BRIGHT_MAGENTA]);
1080 if (hilites[CLR_ORANGE])
1081 free(hilites[CLR_ORANGE]);
1082 if (hilites[CLR_YELLOW])
1083 free(hilites[CLR_YELLOW]);
1084 if (hilites[CLR_WHITE])
1085 free(hilites[CLR_WHITE]);
1087 for (c = 0; c < CLR_MAX; c++)
1088 hilites[c] = NULL;
1091 #else /* UNIX && TERMINFO */
1093 #ifndef TOS
1094 /* find the foreground and background colors set by nh_HI or nh_HE */
1095 static void
1096 analyze_seq(char *str, int *fg, int *bg)
1098 int c, code;
1099 int len;
1101 #ifdef MICRO
1102 *fg = CLR_GRAY;
1103 *bg = CLR_BLACK;
1104 #else
1105 *fg = *bg = NO_COLOR;
1106 #endif
1108 c = (str[0] == '\233') ? 1 : 2; /* index of char beyond esc prefix */
1109 len = strlen(str) - 1; /* length excluding attrib suffix */
1110 if ((c != 1 && (str[0] != '\033' || str[1] != '[')) || (len - c) < 1
1111 || str[len] != 'm')
1112 return;
1114 while (c < len) {
1115 if ((code = atoi(&str[c])) == 0) { /* reset */
1116 /* this also catches errors */
1117 #ifdef MICRO
1118 *fg = CLR_GRAY;
1119 *bg = CLR_BLACK;
1120 #else
1121 *fg = *bg = NO_COLOR;
1122 #endif
1123 } else if (code == 1) { /* bold */
1124 *fg |= BRIGHT;
1125 #if 0
1126 /* I doubt we'll ever resort to using blinking characters,
1127 unless we want a pulsing glow for something. But, in case
1128 we do... -3. */
1129 } else if (code == 5) { /* blinking */
1130 *fg |= BLINK;
1131 } else if (code == 25) { /* stop blinking */
1132 *fg &= ~BLINK;
1133 #endif
1134 } else if (code == 7 || code == 27) { /* reverse */
1135 code = *fg & ~BRIGHT;
1136 *fg = *bg | (*fg & BRIGHT);
1137 *bg = code;
1138 } else if (code >= 30 && code <= 37) { /* hi_foreground RGB */
1139 *fg = code - 30;
1140 } else if (code >= 40 && code <= 47) { /* hi_background RGB */
1141 *bg = code - 40;
1143 while (digit(str[++c]))
1145 c++;
1148 #endif
1151 * Sets up highlighting sequences, using ANSI escape sequences (highlight code
1152 * found in wintty.c). The nh_HI and nh_HE sequences (usually from SO) are
1153 * scanned to find foreground and background colors.
1156 static void
1157 init_hilite(void)
1159 int c;
1160 #ifdef TOS
1161 extern unsigned long tos_numcolors; /* in tos.c */
1162 static char NOCOL[] = "\033b0", COLHE[] = "\033q\033b0";
1164 if (tos_numcolors <= 2) {
1165 return;
1167 /* Under TOS, the "bright" and "dim" colors are reversed. Moreover,
1168 * on the Falcon the dim colors are *really* dim; so we make most
1169 * of the colors the bright versions, with a few exceptions where
1170 * the dim ones look OK.
1172 hilites[0] = NOCOL;
1173 for (c = 1; c < SIZE(hilites); c++) {
1174 char *foo;
1176 foo = (char *) alloc(sizeof "\033b0");
1177 if (tos_numcolors > 4)
1178 Sprintf(foo, "\033b%c", (c & ~BRIGHT) + '0');
1179 else
1180 Strcpy(foo, "\033b0");
1181 hilites[c] = foo;
1184 if (tos_numcolors == 4) {
1185 TI = nhStr("\033b0\033c3\033E\033e");
1186 TE = nhStr("\033b3\033c0\033J");
1187 nh_HE = COLHE;
1188 hilites[CLR_GREEN] = hilites[CLR_GREEN | BRIGHT] = "\033b2";
1189 hilites[CLR_RED] = hilites[CLR_RED | BRIGHT] = "\033b1";
1190 } else {
1191 Sprintf(hilites[CLR_BROWN], "\033b%c", (CLR_BROWN ^ BRIGHT) + '0');
1192 Sprintf(hilites[CLR_GREEN], "\033b%c", (CLR_GREEN ^ BRIGHT) + '0');
1194 TI = nhStr("\033b0\033c\017\033E\033e");
1195 TE = nhStr("\033b\017\033c0\033J");
1196 nh_HE = COLHE;
1197 hilites[CLR_WHITE] = hilites[CLR_BLACK] = NOCOL;
1198 hilites[NO_COLOR] = hilites[CLR_GRAY];
1201 #else /* TOS */
1203 int backg, foreg, hi_backg, hi_foreg;
1205 for (c = 0; c < SIZE(hilites); c++)
1206 hilites[c] = nh_HI;
1207 hilites[CLR_GRAY] = hilites[NO_COLOR] = (char *) 0;
1209 analyze_seq(nh_HI, &hi_foreg, &hi_backg);
1210 analyze_seq(nh_HE, &foreg, &backg);
1212 for (c = 0; c < SIZE(hilites); c++)
1213 /* avoid invisibility */
1214 if ((backg & ~BRIGHT) != c) {
1215 #ifdef MICRO
1216 if (c == CLR_BLUE)
1217 continue;
1218 #endif
1219 if (c == foreg) {
1220 hilites[c] = (char *) 0;
1221 } else if (c != hi_foreg || backg != hi_backg) {
1222 hilites[c] = (char *) alloc(sizeof "\033[%d;3%d;4%dm");
1223 Sprintf(hilites[c], "\033[%d", !!(c & BRIGHT));
1224 if ((c | BRIGHT) != (foreg | BRIGHT))
1225 Sprintf(eos(hilites[c]), ";3%d", c & ~BRIGHT);
1226 if (backg != CLR_BLACK)
1227 Sprintf(eos(hilites[c]), ";4%d", backg & ~BRIGHT);
1228 Strcat(hilites[c], "m");
1232 #ifdef MICRO
1233 /* brighten low-visibility colors */
1234 hilites[CLR_BLUE] = hilites[CLR_BLUE | BRIGHT];
1235 #endif
1236 #endif /* TOS */
1239 static void
1240 kill_hilite(void)
1242 #ifndef TOS
1243 int c;
1245 for (c = 0; c < CLR_MAX / 2; c++) {
1246 if (hilites[c | BRIGHT] == hilites[c])
1247 hilites[c | BRIGHT] = 0;
1248 if (hilites[c] && hilites[c] != nh_HI)
1249 free((genericptr_t) hilites[c]), hilites[c] = 0;
1250 if (hilites[c | BRIGHT] && hilites[c | BRIGHT] != nh_HI)
1251 free((genericptr_t) hilites[c | BRIGHT]), hilites[c | BRIGHT] = 0;
1253 #endif
1254 return;
1256 #endif /* UNIX && TERMINFO */
1257 #endif /* TERMLIB */
1259 #if !defined(TERMLIB) && defined(ANSI_DEFAULT)
1260 static char adef_nilstring[] = "";
1262 static void
1263 init_hilite(void)
1265 int c;
1267 if (!hilites[CLR_BLACK])
1268 hilites[CLR_BLACK] = adef_nilstring;
1269 if (!hilites[CLR_BLACK | BRIGHT])
1270 hilites[CLR_BLACK | BRIGHT] = hilites[CLR_BLACK];
1272 if (!hilites[CLR_GRAY])
1273 hilites[CLR_GRAY] = adef_nilstring;
1274 if (!hilites[NO_COLOR])
1275 hilites[NO_COLOR] = hilites[CLR_GRAY];
1277 for (c = 0; c < CLR_MAX / 2; c++) {
1278 if (c == CLR_BLACK)
1279 continue;
1280 hilites[c | BRIGHT] = (char *) alloc(sizeof "\033[1;3%dm");
1281 Sprintf(hilites[c | BRIGHT], "\033[1;3%dm", c);
1282 if (c == CLR_GRAY)
1283 continue;
1284 #ifdef MICRO
1285 if (c == CLR_BLUE) {
1286 hilites[CLR_BLUE] = hilites[CLR_BLUE | BRIGHT];
1287 } else
1288 #endif
1290 hilites[c] = (char *) alloc(sizeof "\033[0;3%dm");
1291 Sprintf(hilites[c], "\033[0;3%dm", c);
1295 /* See TERMLIB && UNIX && TERMINFO code above. */
1296 if (iflags.wc2_darkgray) {
1297 /* Bright black is dark gray. */
1298 hilites[CLR_BLACK] = (char *) alloc(sizeof "\033[1;30m");
1299 Sprintf(hilites[CLR_BLACK], "\033[1;30m");
1300 } else {
1301 /* Use blue for black. */
1302 hilites[CLR_BLACK] = hilites[CLR_BLUE];
1306 static void
1307 kill_hilite(void)
1309 int c;
1311 for (c = 0; c < CLR_MAX / 2; c++) {
1312 if (c == CLR_GRAY || hilites[c] == adef_nilstring)
1313 hilites[c] = 0;
1314 if (hilites[c | BRIGHT] == adef_nilstring)
1315 hilites[c] = 0;
1316 if (c == CLR_BLACK)
1317 continue;
1318 if (hilites[c | BRIGHT] == hilites[c]) /* for blue */
1319 hilites[c | BRIGHT] = 0;
1320 if (hilites[c] && hilites[c] != nh_HI)
1321 free((genericptr_t) hilites[c]), hilites[c] = 0;
1322 if (hilites[c | BRIGHT] && hilites[c | BRIGHT] != nh_HI)
1323 free((genericptr_t) hilites[c | BRIGHT]), hilites[c | BRIGHT] = 0;
1326 if (hilites[CLR_BLACK]) {
1327 if (hilites[CLR_BLACK] != hilites[CLR_BLUE])
1328 free(hilites[CLR_BLACK]);
1329 hilites[CLR_BLACK] = 0;
1332 #endif /* !TERMLIB && ANSI_DEFAULT */
1334 static char nulstr[] = "";
1336 static char *
1337 s_atr2str(int n)
1339 switch (n) {
1340 case ATR_ITALIC:
1341 /* if italic isn't available, fall through to underline */
1342 if (ZH && *ZH)
1343 return ZH;
1344 FALLTHROUGH;
1345 /*FALLTHRU*/
1346 case ATR_BLINK:
1347 case ATR_ULINE:
1348 if (n == ATR_BLINK) {
1349 if (MB && *MB)
1350 return MB;
1351 } else { /* Underline */
1352 if (nh_US && *nh_US)
1353 return nh_US;
1355 FALLTHROUGH;
1356 /*FALLTHRU*/
1357 case ATR_BOLD:
1358 if (MD && *MD)
1359 return MD;
1360 if (nh_HI && *nh_HI)
1361 return nh_HI;
1362 break;
1363 case ATR_INVERSE:
1364 if (MR && *MR)
1365 return MR;
1366 break;
1367 case ATR_DIM:
1368 if (MH && *MH)
1369 return MH;
1370 break;
1372 return nulstr;
1375 static char *
1376 e_atr2str(int n)
1378 switch (n) {
1379 case ATR_ITALIC:
1380 /* send ZR unless we didn't have ZH and substituted US */
1381 if (ZR && *ZR && ZH && *ZH)
1382 return ZR;
1383 FALLTHROUGH;
1384 /*FALLTHRU*/
1385 case ATR_ULINE:
1386 if (nh_UE && *nh_UE)
1387 return nh_UE;
1388 FALLTHROUGH;
1389 /*FALLTHRU*/
1390 case ATR_BOLD:
1391 case ATR_BLINK:
1392 if (nh_HE && *nh_HE)
1393 return nh_HE;
1394 FALLTHROUGH;
1395 /*FALLTHRU*/
1396 case ATR_DIM:
1397 case ATR_INVERSE:
1398 if (ME && *ME)
1399 return ME;
1400 break;
1402 return nulstr;
1405 /* suppress nonfunctional highlights so render_status() might be able to
1406 optimize more; keep this in sync with s_atr2str() */
1408 term_attr_fixup(int msk)
1410 /* underline is converted to bold if its start sequence isn't available */
1411 if ((msk & HL_ULINE) && (!nh_US || !*nh_US)) {
1412 msk |= HL_BOLD;
1413 msk &= ~HL_ULINE;
1415 /* blink used to be converted to bold unconditionally; now depends on MB */
1416 if ((msk & HL_BLINK) && (!MB || !*MB)) {
1417 msk |= HL_BOLD;
1418 msk &= ~HL_BLINK;
1420 /* dim is ignored if its start sequence isn't available */
1421 if ((msk & HL_DIM) && (!MH || !*MH)) {
1422 msk &= ~HL_DIM;
1424 return msk;
1427 void
1428 term_start_attr(int attr)
1430 if (attr) {
1431 const char *astr = s_atr2str(attr);
1433 if (astr && *astr)
1434 xputs(astr);
1438 void
1439 term_end_attr(int attr)
1441 if (attr) {
1442 const char *astr = e_atr2str(attr);
1444 if (astr && *astr)
1445 xputs(astr);
1449 /* this is called 'start bold' but HI is derived from SO (standout) rather
1450 than from MD (start bold attribute) */
1451 void
1452 term_start_raw_bold(void)
1454 const char *soOn = nh_HI ? nh_HI : tty_standout_on;
1456 if (*soOn)
1457 xputs(soOn);
1460 /* this is called 'end bold' but HE is derived from ME (end all attributes) */
1461 void
1462 term_end_raw_bold(void)
1464 const char *soOff = nh_HE ? nh_HE : tty_standout_off;
1466 if (*soOff)
1467 xputs(soOff);
1470 void
1471 term_end_color(void)
1473 xputs(nh_HE);
1476 void
1477 term_start_color(int color)
1479 if (color == NO_COLOR)
1480 xputs(nh_HE); /* inline term_end_color() */
1481 else if (color < CLR_MAX && hilites[color] && *hilites[color])
1482 xputs(hilites[color]);
1485 void
1486 term_start_bgcolor(int color)
1488 char tmp[8];
1489 Sprintf(tmp, "\033[%dm", ((color % 8) + 40));
1490 xputs(tmp);
1493 /* hide or show cursor */
1494 void
1495 term_curs_set(int visibility)
1497 static int vis = -1;
1499 if (vis == visibility)
1500 return;
1502 if (!visibility && nh_VI)
1503 xputs(nh_VI);
1504 else if (visibility && nh_VE)
1505 xputs(nh_VE);
1506 vis = visibility;
1509 #ifdef CHANGE_COLOR
1510 void
1511 tty_change_color(int color UNUSED, long rgb UNUSED, int reverse UNUSED)
1513 return;
1515 #endif /* CHANGE_COLOR */
1518 #ifndef SEP2
1519 #define tcfmtstr "\033[38;2;%ld;%ld;%ldm"
1520 #ifdef UNIX
1521 #define tcfmtstr24bit "\033[38;2;%u;%u;%um"
1522 #define tcfmtstr256 "\033[38;5;%dm"
1523 #else
1524 #define tcfmtstr24bit "\033[38;2;%lu;%lu;%lum"
1525 #define tcfmtstr256 "\033[38:5:%lum"
1526 #endif
1527 #endif
1529 static void emit24bit(long mcolor);
1530 static void emit256(int u256coloridx);
1532 static void emit24bit(long mcolor)
1534 static char tcolorbuf[QBUFSZ];
1536 Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr,
1537 ((mcolor >> 16) & 0xFF), /* red */
1538 ((mcolor >> 8) & 0xFF), /* green */
1539 ((mcolor >> 0) & 0xFF)); /* blue */
1540 xputs(tcolorbuf);
1543 static void emit256(int color256idx)
1545 static char tcolorbuf[QBUFSZ];
1547 Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr256,
1548 color256idx);
1549 xputs(tcolorbuf);
1552 void
1553 term_start_extracolor(uint32 customcolor, uint16 color256idx)
1555 /* color 0 has bit 0x1000000 set */
1556 long mcolor = (customcolor & 0xFFFFFF);
1557 if (iflags.colorcount == 256)
1558 emit256(color256idx);
1559 else
1560 emit24bit(mcolor);
1563 void
1564 term_end_extracolor(void)
1566 xputs("\033[0m");
1568 #endif /* TTY_GRAPHICS && !NO_TERMS */
1570 /*termcap.c*/