OSDN Git Service

update year to 2020
[jnethack/source.git] / win / tty / wintty.c
1 /* NetHack 3.6  wintty.c        $NHDT-Date: 1575245194 2019/12/02 00:06:34 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.227 $ */
2 /* Copyright (c) David Cohrs, 1991                                */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /* JNetHack Copyright */
6 /* (c) Issei Numata 1994-2000                                      */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020            */
8 /* JNetHack may be freely redistributed.  See license for details. */
9
10 /*
11  * Neither a standard out nor character-based control codes should be
12  * part of the "tty look" windowing implementation.
13  * h+ 930227
14  */
15
16 /* It's still not clear I've caught all the cases for H2344.  #undef this
17  * to back out the changes. */
18 #define H2344_BROKEN
19
20 #include "hack.h"
21
22 #ifdef TTY_GRAPHICS
23 #include "dlb.h"
24
25 #ifdef MAC
26 #define MICRO /* The Mac is a MICRO only for this file, not in general! */
27 #ifdef THINK_C
28 extern void msmsg(const char *, ...);
29 #endif
30 #endif
31
32 #ifndef NO_TERMS
33 #include "tcap.h"
34 #endif
35
36 #include "wintty.h"
37
38 #ifdef CLIPPING /* might want SIGWINCH */
39 #if defined(BSD) || defined(ULTRIX) || defined(AIX_31) || defined(_BULL_SOURCE)
40 #include <signal.h>
41 #endif
42 #endif
43
44 #ifdef TTY_TILES_ESCCODES
45 extern short glyph2tile[];
46 #define TILE_ANSI_COMMAND 'z'
47 #define AVTC_GLYPH_START   0
48 #define AVTC_GLYPH_END     1
49 #define AVTC_SELECT_WINDOW 2
50 #define AVTC_INLINE_SYNC   3
51 #endif
52
53 #ifdef HANGUP_HANDLING
54 /*
55  * NetHack's core switches to a dummy windowing interface when it
56  * detects SIGHUP, but that's no help for any interface routine which
57  * is already in progress at the time, and there have been reports of
58  * runaway disconnected processes which use up all available CPU time.
59  * HUPSKIP() and HUPSKIP_RETURN(x) are used to try to cut them off so
60  * that they return to the core instead attempting more terminal I/O.
61  */
62 #define HUPSKIP() \
63     do {                                        \
64         if (program_state.done_hup) {           \
65             morc = '\033';                      \
66             return;                             \
67         }                                       \
68     } while (0)
69     /* morc=ESC - in case we bypass xwaitforspace() which sets that */
70 #define HUPSKIP_RESULT(RES) \
71     do {                                        \
72         if (program_state.done_hup)             \
73             return (RES);                       \
74     } while (0)
75 #else /* !HANGUP_HANDLING */
76 #define HUPSKIP() /*empty*/
77 #define HUPSKIP_RESULT(RES) /*empty*/
78 #endif /* ?HANGUP_HANDLING */
79
80 extern char mapped_menu_cmds[]; /* from options.c */
81
82 /* Interface definition, for windows.c */
83 struct window_procs tty_procs = {
84     "tty",
85     (0
86 #ifdef MSDOS
87      | WC_TILED_MAP | WC_ASCII_MAP
88 #endif
89 #if defined(WIN32CON)
90      | WC_MOUSE_SUPPORT
91 #endif
92      | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN),
93     (0
94 #if defined(SELECTSAVED)
95      | WC2_SELECTSAVED
96 #endif
97 #if defined(STATUS_HILITES)
98      | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS
99      | WC2_RESET_STATUS
100 #endif
101      | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_STATUSLINES),
102 #ifdef TEXTCOLOR
103     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},   /* color availability */
104 #else
105     {1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1},
106 #endif
107     tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event,
108     tty_exit_nhwindows, tty_suspend_nhwindows, tty_resume_nhwindows,
109     tty_create_nhwindow, tty_clear_nhwindow, tty_display_nhwindow,
110     tty_destroy_nhwindow, tty_curs, tty_putstr, genl_putmixed,
111     tty_display_file, tty_start_menu, tty_add_menu, tty_end_menu,
112     tty_select_menu, tty_message_menu, tty_update_inventory, tty_mark_synch,
113     tty_wait_synch,
114 #ifdef CLIPPING
115     tty_cliparound,
116 #endif
117 #ifdef POSITIONBAR
118     tty_update_positionbar,
119 #endif
120     tty_print_glyph, tty_raw_print, tty_raw_print_bold, tty_nhgetch,
121     tty_nh_poskey, tty_nhbell, tty_doprev_message, tty_yn_function,
122     tty_getlin, tty_get_ext_cmd, tty_number_pad, tty_delay_output,
123 #ifdef CHANGE_COLOR /* the Mac uses a palette device */
124     tty_change_color,
125 #ifdef MAC
126     tty_change_background, set_tty_font_name,
127 #endif
128     tty_get_color_string,
129 #endif
130
131     /* other defs that really should go away (they're tty specific) */
132     tty_start_screen, tty_end_screen, genl_outrip,
133     tty_preference_update,
134     tty_getmsghistory, tty_putmsghistory,
135     tty_status_init,
136     genl_status_finish, tty_status_enablefield,
137 #ifdef STATUS_HILITES
138     tty_status_update,
139 #else
140     genl_status_update,
141 #endif
142     genl_can_suspend_yes,
143 };
144
145 winid BASE_WINDOW;
146 struct WinDesc *wins[MAXWIN];
147 struct DisplayDesc *ttyDisplay; /* the tty display descriptor */
148
149 extern void FDECL(cmov, (int, int));   /* from termcap.c */
150 extern void FDECL(nocmov, (int, int)); /* from termcap.c */
151 #if defined(UNIX) || defined(VMS)
152 static char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
153 #endif
154
155 static char winpanicstr[] = "Bad window id %d";
156 char defmorestr[] = "--More--";
157
158 #ifdef CLIPPING
159 #if defined(USE_TILES) && defined(MSDOS)
160 boolean clipping = FALSE; /* clipping on? */
161 int clipx = 0, clipxmax = 0;
162 #else
163 static boolean clipping = FALSE; /* clipping on? */
164 static int clipx = 0, clipxmax = 0;
165 #endif
166 static int clipy = 0, clipymax = 0;
167 #endif /* CLIPPING */
168
169 #if defined(USE_TILES) && defined(MSDOS)
170 extern void FDECL(adjust_cursor_flags, (struct WinDesc *));
171 #endif
172
173 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
174 boolean GFlag = FALSE;
175 boolean HE_resets_AS; /* see termcap.c */
176 #endif
177
178 #if defined(MICRO) || defined(WIN32CON)
179 static const char to_continue[] = "to continue";
180 #define getret() getreturn(to_continue)
181 #else
182 STATIC_DCL void NDECL(getret);
183 #endif
184 STATIC_DCL void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */
185 STATIC_DCL void NDECL(new_status_window);
186 STATIC_DCL void FDECL(erase_menu_or_text, (winid, struct WinDesc *,
187                                            BOOLEAN_P));
188 STATIC_DCL void FDECL(free_window_info, (struct WinDesc *, BOOLEAN_P));
189 STATIC_DCL void FDECL(dmore, (struct WinDesc *, const char *));
190 STATIC_DCL void FDECL(set_item_state, (winid, int, tty_menu_item *));
191 STATIC_DCL void FDECL(set_all_on_page, (winid, tty_menu_item *,
192                                         tty_menu_item *));
193 STATIC_DCL void FDECL(unset_all_on_page, (winid, tty_menu_item *,
194                                           tty_menu_item *));
195 STATIC_DCL void FDECL(invert_all_on_page, (winid, tty_menu_item *,
196                                            tty_menu_item *, CHAR_P));
197 STATIC_DCL void FDECL(invert_all, (winid, tty_menu_item *,
198                                    tty_menu_item *, CHAR_P));
199 STATIC_DCL void FDECL(toggle_menu_attr, (BOOLEAN_P, int, int));
200 STATIC_DCL void FDECL(process_menu_window, (winid, struct WinDesc *));
201 STATIC_DCL void FDECL(process_text_window, (winid, struct WinDesc *));
202 STATIC_DCL tty_menu_item *FDECL(reverse, (tty_menu_item *));
203 STATIC_DCL const char *FDECL(compress_str, (const char *));
204 STATIC_DCL void FDECL(tty_putsym, (winid, int, int, CHAR_P));
205 STATIC_DCL void FDECL(setup_rolemenu, (winid, BOOLEAN_P, int, int, int));
206 STATIC_DCL void FDECL(setup_racemenu, (winid, BOOLEAN_P, int, int, int));
207 STATIC_DCL void FDECL(setup_gendmenu, (winid, BOOLEAN_P, int, int, int));
208 STATIC_DCL void FDECL(setup_algnmenu, (winid, BOOLEAN_P, int, int, int));
209 STATIC_DCL boolean NDECL(reset_role_filtering);
210 #ifdef STATUS_HILITES
211 STATIC_DCL boolean FDECL(check_fields, (BOOLEAN_P, int *));
212 STATIC_DCL void NDECL(render_status);
213 STATIC_DCL void FDECL(tty_putstatusfield, (const char *, int, int));
214 STATIC_DCL boolean NDECL(check_windowdata);
215 STATIC_DCL void NDECL(set_condition_length);
216 STATIC_DCL int FDECL(make_things_fit, (BOOLEAN_P));
217 STATIC_DCL void FDECL(shrink_enc, (int));
218 STATIC_DCL void FDECL(shrink_dlvl, (int));
219 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
220 STATIC_DCL void NDECL(status_sanity_check);
221 #endif /* NH_DEVEL_STATUS */
222 #endif
223
224 /*
225  * A string containing all the default commands -- to add to a list
226  * of acceptable inputs.
227  */
228 static const char default_menu_cmds[] = {
229     MENU_FIRST_PAGE,    MENU_LAST_PAGE,   MENU_NEXT_PAGE,
230     MENU_PREVIOUS_PAGE, MENU_SELECT_ALL,  MENU_UNSELECT_ALL,
231     MENU_INVERT_ALL,    MENU_SELECT_PAGE, MENU_UNSELECT_PAGE,
232     MENU_INVERT_PAGE,   MENU_SEARCH,      0 /* null terminator */
233 };
234
235 #ifdef TTY_TILES_ESCCODES
236 static int vt_tile_current_window = -2;
237
238 void
239 print_vt_code(i, c, d)
240 int i, c, d;
241 {
242     HUPSKIP();
243     if (iflags.vt_tiledata) {
244         if (c >= 0) {
245             if (i == AVTC_SELECT_WINDOW) {
246                 if (c == vt_tile_current_window)
247                     return;
248                 vt_tile_current_window = c;
249             }
250             if (d >= 0)
251                 printf("\033[1;%d;%d;%d%c", i, c, d, TILE_ANSI_COMMAND);
252             else
253                 printf("\033[1;%d;%d%c", i, c, TILE_ANSI_COMMAND);
254         } else {
255             printf("\033[1;%d%c", i, TILE_ANSI_COMMAND);
256         }
257     }
258 }
259 #else
260 # define print_vt_code(i, c, d) ;
261 #endif /* !TTY_TILES_ESCCODES */
262 #define print_vt_code1(i)     print_vt_code((i), -1, -1)
263 #define print_vt_code2(i,c)   print_vt_code((i), (c), -1)
264 #define print_vt_code3(i,c,d) print_vt_code((i), (c), (d))
265
266
267 /* clean up and quit */
268 STATIC_OVL void
269 bail(mesg)
270 const char *mesg;
271 {
272     clearlocks();
273     tty_exit_nhwindows(mesg);
274     nh_terminate(EXIT_SUCCESS);
275     /*NOTREACHED*/
276 }
277
278 #if defined(SIGWINCH) && defined(CLIPPING)
279 STATIC_DCL void FDECL(winch_handler, (int));
280
281     /*
282      * This really ought to just set a flag like the hangup handler does,
283      * then check the flag at "safe" times, in case the signal arrives
284      * while something fragile is executing.  Better to have a brief period
285      * where display updates don't fit the new size than for tty internals
286      * to become corrupted.
287      *
288      * 'winch_seen' has been "notyet" for a long time....
289      */
290 /*ARGUSED*/
291 STATIC_OVL void
292 winch_handler(sig_unused) /* signal handler is called with at least 1 arg */
293 int sig_unused UNUSED;
294 {
295     int oldLI = LI, oldCO = CO, i;
296     register struct WinDesc *cw;
297
298 #ifdef WINCHAIN
299     {
300 #define WINCH_MESSAGE "(SIGWINCH)"
301         if (wc_tracelogf)
302             (void) write(fileno(wc_tracelogf), WINCH_MESSAGE,
303                          strlen(WINCH_MESSAGE));
304 #undef WINCH_MESSAGE
305     }
306 #endif
307     getwindowsz();
308     /* For long running events such as multi-page menus and
309      * display_file(), we note the signal's occurance and
310      * hope the code there decides to handle the situation
311      * and reset the flag. There will be race conditions
312      * when handling this - code handlers so it doesn't matter.
313      */
314 #ifdef notyet
315     winch_seen = TRUE;
316 #endif
317     if ((oldLI != LI || oldCO != CO) && ttyDisplay) {
318         ttyDisplay->rows = LI;
319         ttyDisplay->cols = CO;
320
321         cw = wins[BASE_WINDOW];
322         cw->rows = ttyDisplay->rows;
323         cw->cols = ttyDisplay->cols;
324
325         if (iflags.window_inited) {
326             cw = wins[WIN_MESSAGE];
327             cw->curx = cw->cury = 0;
328
329             new_status_window();
330             if (u.ux) {
331                 i = ttyDisplay->toplin;
332                 ttyDisplay->toplin = 0;
333                 docrt();
334                 bot();
335                 ttyDisplay->toplin = i;
336                 flush_screen(1);
337                 if (i) {
338                     addtopl(toplines);
339                 } else
340                     for (i = WIN_INVEN; i < MAXWIN; i++)
341                         if (wins[i] && wins[i]->active) {
342                             /* cop-out */
343                             addtopl("Press Return to continue: ");
344                             break;
345                         }
346                 (void) fflush(stdout);
347                 if (i < 2)
348                     flush_screen(1);
349             }
350         }
351     }
352 }
353 #endif
354
355 /* destroy and recreate status window; extracted from winch_handler()
356    and augmented for use by tty_preference_update() */
357 STATIC_OVL void
358 new_status_window()
359 {
360     if (WIN_STATUS != WIN_ERR) {
361         /* if it's shrinking, clear it before destroying so that
362            dropped portion won't show anything that's now becoming stale */
363         if (wins[WIN_STATUS]->maxrow > iflags.wc2_statuslines)
364             tty_clear_nhwindow(WIN_STATUS);
365
366         tty_destroy_nhwindow(WIN_STATUS), WIN_STATUS = WIN_ERR;
367     }
368     /* frees some status tracking data */
369     genl_status_finish();
370     /* creates status window and allocates tracking data */
371     tty_status_init();
372     tty_clear_nhwindow(WIN_STATUS); /* does some init, sets context.botlx */
373 #ifdef STATUS_HILITES
374     status_initialize(REASSESS_ONLY);
375 #endif
376
377 #ifdef CLIPPING
378     if (u.ux) {
379         if (LI < 1 + ROWNO + iflags.wc2_statuslines) {
380             setclipped();
381             tty_cliparound(u.ux, u.uy);
382         } else {
383             clipping = FALSE;
384             clipx = clipy = 0;
385         }
386     }
387 #endif
388 }
389
390 /*ARGSUSED*/
391 void
392 tty_init_nhwindows(argcp, argv)
393 int *argcp UNUSED;
394 char **argv UNUSED;
395 {
396     int wid, hgt, i;
397
398     /* options aren't processed yet so wc2_statuslines might be 0;
399        make sure that it has a reasonable value during tty setup */
400     iflags.wc2_statuslines = (iflags.wc2_statuslines < 3) ? 2 : 3;
401     /*
402      *  Remember tty modes, to be restored on exit.
403      *
404      *  gettty() must be called before tty_startup()
405      *    due to ordering of LI/CO settings
406      *  tty_startup() must be called before initoptions()
407      *    due to ordering of graphics settings
408      */
409 #if defined(UNIX) || defined(VMS)
410     setbuf(stdout, obuf);
411 #endif
412     gettty();
413
414     /* to port dependant tty setup */
415     tty_startup(&wid, &hgt);
416     setftty(); /* calls start_screen */
417
418     /* set up tty descriptor */
419     ttyDisplay = (struct DisplayDesc *) alloc(sizeof (struct DisplayDesc));
420     ttyDisplay->toplin = 0;
421     ttyDisplay->rows = hgt;
422     ttyDisplay->cols = wid;
423     ttyDisplay->curx = ttyDisplay->cury = 0;
424     ttyDisplay->inmore = ttyDisplay->inread = ttyDisplay->intr = 0;
425     ttyDisplay->dismiss_more = 0;
426 #ifdef TEXTCOLOR
427     ttyDisplay->color = NO_COLOR;
428 #endif
429     ttyDisplay->attrs = 0;
430
431     /* set up the default windows */
432     BASE_WINDOW = tty_create_nhwindow(NHW_BASE);
433     wins[BASE_WINDOW]->active = 1;
434
435     ttyDisplay->lastwin = WIN_ERR;
436
437 #if defined(SIGWINCH) && defined(CLIPPING) && !defined(NO_SIGNAL)
438     (void) signal(SIGWINCH, (SIG_RET_TYPE) winch_handler);
439 #endif
440
441     tty_clear_nhwindow(BASE_WINDOW);
442
443     tty_putstr(BASE_WINDOW, 0, "");
444     for (i = 1; i <= 4; ++i)
445         tty_putstr(BASE_WINDOW, 0, copyright_banner_line(i));
446     tty_putstr(BASE_WINDOW, 0, "");
447 #if 0 /*JP*//*JPTB reversed*/
448     tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_A);
449     tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_B);
450     tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_C);
451     tty_putstr(BASE_WINDOW, 0, JA_COPYRIGHT_BANNER_D);
452     tty_putstr(BASE_WINDOW, 0, "");
453 #endif
454     tty_display_nhwindow(BASE_WINDOW, FALSE);
455
456     /* 'statuslines' defaults to SET_IN_FILE, allowed but invisible;
457        make it dynamically settable if feasible, otherwise visible */
458     if (tty_procs.wincap2 & WC2_STATUSLINES)
459         set_wc2_option_mod_status(WC2_STATUSLINES,
460 #ifndef CLIPPING
461                                   (LI < 1 + ROWNO + 2) ? DISP_IN_GAME :
462 #endif
463                                    SET_IN_GAME);
464 }
465
466 void
467 tty_preference_update(pref)
468 const char *pref;
469 {
470     if (!strcmp(pref, "statuslines") && iflags.window_inited) {
471         new_status_window();
472     }
473
474 #if defined(WIN32)
475     nttty_preference_update(pref);
476 #else
477     genl_preference_update(pref);
478 #endif
479     return;
480 }
481
482 /* try to reduce clutter in the code below... */
483 #define ROLE flags.initrole
484 #define RACE flags.initrace
485 #define GEND flags.initgend
486 #define ALGN flags.initalign
487
488 void
489 tty_player_selection()
490 {
491     int i, k, n, choice, nextpick;
492     boolean getconfirmation, picksomething;
493     char pick4u = 'n';
494     char pbuf[QBUFSZ], plbuf[QBUFSZ];
495     winid win;
496     anything any;
497     menu_item *selected = 0;
498
499     /* Used to avoid "Is this ok?" if player has already specified all
500      * four facets of role.
501      * Note that rigid_role_checks might force any unspecified facets to
502      * have a specific value, but that will still require confirmation;
503      * player can specify the forced ones if avoiding that is demanded.
504      */
505     picksomething = (ROLE == ROLE_NONE || RACE == ROLE_NONE
506                      || GEND == ROLE_NONE || ALGN == ROLE_NONE);
507     /* Used for '-@';
508      * choose randomly without asking for all unspecified facets.
509      */
510     if (flags.randomall && picksomething) {
511         if (ROLE == ROLE_NONE)
512             ROLE = ROLE_RANDOM;
513         if (RACE == ROLE_NONE)
514             RACE = ROLE_RANDOM;
515         if (GEND == ROLE_NONE)
516             GEND = ROLE_RANDOM;
517         if (ALGN == ROLE_NONE)
518             ALGN = ROLE_RANDOM;
519     }
520
521     /* prevent unnecessary prompting if role forces race (samurai) or gender
522        (valkyrie) or alignment (rogue), or race forces alignment (orc), &c */
523     rigid_role_checks();
524
525     /* Should we randomly pick for the player? */
526     if (ROLE == ROLE_NONE || RACE == ROLE_NONE || GEND == ROLE_NONE
527         || ALGN == ROLE_NONE) {
528         int echoline;
529         char *prompt = build_plselection_prompt(pbuf, QBUFSZ,
530                                                 ROLE, RACE, GEND, ALGN);
531
532         /* this prompt string ends in "[ynaq]?":
533            y - game picks role,&c then asks player to confirm;
534            n - player manually chooses via menu selections;
535            a - like 'y', but skips confirmation and starts game;
536            q - quit
537          */
538         tty_putstr(BASE_WINDOW, 0, "");
539         echoline = wins[BASE_WINDOW]->cury;
540         tty_putstr(BASE_WINDOW, 0, prompt);
541         do {
542             pick4u = lowc(readchar());
543             if (index(quitchars, pick4u))
544                 pick4u = 'y';
545         } while (!index(ynaqchars, pick4u));
546         if ((int) strlen(prompt) + 1 < CO) {
547             /* Echo choice and move back down line */
548             tty_putsym(BASE_WINDOW, (int) strlen(prompt) + 1, echoline,
549                        pick4u);
550             tty_putstr(BASE_WINDOW, 0, "");
551         } else
552             /* Otherwise it's hard to tell where to echo, and things are
553              * wrapping a bit messily anyway, so (try to) make sure the next
554              * question shows up well and doesn't get wrapped at the
555              * bottom of the window.
556              */
557             tty_clear_nhwindow(BASE_WINDOW);
558
559         if (pick4u != 'y' && pick4u != 'a' && pick4u != 'n')
560             goto give_up;
561     }
562
563  makepicks:
564     nextpick = RS_ROLE;
565     do {
566         if (nextpick == RS_ROLE) {
567             nextpick = RS_RACE;
568             /* Select a role, if necessary;
569                we'll try to be compatible with pre-selected
570                race/gender/alignment, but may not succeed. */
571             if (ROLE < 0) {
572                 /* Process the choice */
573                 if (pick4u == 'y' || pick4u == 'a' || ROLE == ROLE_RANDOM) {
574                     /* Pick a random role */
575                     k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
576                     if (k < 0) {
577                         tty_putstr(BASE_WINDOW, 0, "Incompatible role!");
578                         k = randrole(FALSE);
579                     }
580                 } else {
581                     /* Prompt for a role */
582                     tty_clear_nhwindow(BASE_WINDOW);
583                     role_selection_prolog(RS_ROLE, BASE_WINDOW);
584                     win = create_nhwindow(NHW_MENU);
585                     start_menu(win);
586                     /* populate the menu with role choices */
587                     setup_rolemenu(win, TRUE, RACE, GEND, ALGN);
588                     /* add miscellaneous menu entries */
589                     role_menu_extra(ROLE_RANDOM, win, TRUE);
590                     any = zeroany; /* separator, not a choice */
591                     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
592                              MENU_UNSELECTED);
593                     role_menu_extra(RS_RACE, win, FALSE);
594                     role_menu_extra(RS_GENDER, win, FALSE);
595                     role_menu_extra(RS_ALGNMNT, win, FALSE);
596                     if (gotrolefilter())
597                         role_menu_extra(RS_filter, win, FALSE);
598                     role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
599 /*JP
600                     Strcpy(pbuf, "Pick a role or profession");
601 */
602                     Strcpy(pbuf, "\90E\8bÆ\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢");
603                     end_menu(win, pbuf);
604                     n = select_menu(win, PICK_ONE, &selected);
605                     /*
606                      * PICK_ONE with preselected choice behaves strangely:
607                      *  n == -1 -- <escape>, so use quit choice;
608                      *  n ==  0 -- explicitly chose preselected entry,
609                      *             toggling it off, so use it;
610                      *  n ==  1 -- implicitly chose preselected entry
611                      *             with <space> or <return>;
612                      *  n ==  2 -- explicitly chose a different entry, so
613                      *             both it and preselected one are in list.
614                      */
615                     if (n > 0) {
616                         choice = selected[0].item.a_int;
617                         if (n > 1 && choice == ROLE_RANDOM)
618                             choice = selected[1].item.a_int;
619                     } else
620                         choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
621                     if (selected)
622                         free((genericptr_t) selected), selected = 0;
623                     destroy_nhwindow(win);
624
625                     if (choice == ROLE_NONE) {
626                         goto give_up; /* Selected quit */
627                     } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
628                         ALGN = k = ROLE_NONE;
629                         nextpick = RS_ALGNMNT;
630                     } else if (choice == RS_menu_arg(RS_GENDER)) {
631                         GEND = k = ROLE_NONE;
632                         nextpick = RS_GENDER;
633                     } else if (choice == RS_menu_arg(RS_RACE)) {
634                         RACE = k = ROLE_NONE;
635                         nextpick = RS_RACE;
636                     } else if (choice == RS_menu_arg(RS_filter)) {
637                         ROLE = k = ROLE_NONE;
638                         (void) reset_role_filtering();
639                         nextpick = RS_ROLE;
640                     } else if (choice == ROLE_RANDOM) {
641                         k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
642                         if (k < 0)
643                             k = randrole(FALSE);
644                     } else {
645                         k = choice - 1;
646                     }
647                 }
648                 ROLE = k;
649             } /* needed role */
650         }     /* picking role */
651
652         if (nextpick == RS_RACE) {
653             nextpick = (ROLE < 0) ? RS_ROLE : RS_GENDER;
654             /* Select a race, if necessary;
655                force compatibility with role, try for compatibility
656                with pre-selected gender/alignment. */
657             if (RACE < 0 || !validrace(ROLE, RACE)) {
658                 /* no race yet, or pre-selected race not valid */
659                 if (pick4u == 'y' || pick4u == 'a' || RACE == ROLE_RANDOM) {
660                     k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM);
661                     if (k < 0) {
662                         tty_putstr(BASE_WINDOW, 0, "Incompatible race!");
663                         k = randrace(ROLE);
664                     }
665                 } else { /* pick4u == 'n' */
666                     /* Count the number of valid races */
667                     n = 0; /* number valid */
668                     k = 0; /* valid race */
669                     for (i = 0; races[i].noun; i++)
670                         if (ok_race(ROLE, i, GEND, ALGN)) {
671                             n++;
672                             k = i;
673                         }
674                     if (n == 0) {
675                         for (i = 0; races[i].noun; i++)
676                             if (validrace(ROLE, i)) {
677                                 n++;
678                                 k = i;
679                             }
680                     }
681                     /* Permit the user to pick, if there is more than one */
682                     if (n > 1) {
683                         tty_clear_nhwindow(BASE_WINDOW);
684                         role_selection_prolog(RS_RACE, BASE_WINDOW);
685                         win = create_nhwindow(NHW_MENU);
686                         start_menu(win);
687                         any = zeroany; /* zero out all bits */
688                         /* populate the menu with role choices */
689                         setup_racemenu(win, TRUE, ROLE, GEND, ALGN);
690                         /* add miscellaneous menu entries */
691                         role_menu_extra(ROLE_RANDOM, win, TRUE);
692                         any.a_int = 0; /* separator, not a choice */
693                         add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
694                                  MENU_UNSELECTED);
695                         role_menu_extra(RS_ROLE, win, FALSE);
696                         role_menu_extra(RS_GENDER, win, FALSE);
697                         role_menu_extra(RS_ALGNMNT, win, FALSE);
698                         if (gotrolefilter())
699                             role_menu_extra(RS_filter, win, FALSE);
700                         role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
701 /*JP
702                         Strcpy(pbuf, "Pick a race or species");
703 */
704                         Strcpy(pbuf, "\8eí\91°\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢");
705                         end_menu(win, pbuf);
706                         n = select_menu(win, PICK_ONE, &selected);
707                         if (n > 0) {
708                             choice = selected[0].item.a_int;
709                             if (n > 1 && choice == ROLE_RANDOM)
710                                 choice = selected[1].item.a_int;
711                         } else
712                             choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
713                         if (selected)
714                             free((genericptr_t) selected), selected = 0;
715                         destroy_nhwindow(win);
716
717                         if (choice == ROLE_NONE) {
718                             goto give_up; /* Selected quit */
719                         } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
720                             ALGN = k = ROLE_NONE;
721                             nextpick = RS_ALGNMNT;
722                         } else if (choice == RS_menu_arg(RS_GENDER)) {
723                             GEND = k = ROLE_NONE;
724                             nextpick = RS_GENDER;
725                         } else if (choice == RS_menu_arg(RS_ROLE)) {
726                             ROLE = k = ROLE_NONE;
727                             nextpick = RS_ROLE;
728                         } else if (choice == RS_menu_arg(RS_filter)) {
729                             RACE = k = ROLE_NONE;
730                             if (reset_role_filtering())
731                                 nextpick = RS_ROLE;
732                             else
733                                 nextpick = RS_RACE;
734                         } else if (choice == ROLE_RANDOM) {
735                             k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM);
736                             if (k < 0)
737                                 k = randrace(ROLE);
738                         } else {
739                             k = choice - 1;
740                         }
741                     }
742                 }
743                 RACE = k;
744             } /* needed race */
745         }     /* picking race */
746
747         if (nextpick == RS_GENDER) {
748             nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE
749                        : RS_ALGNMNT;
750             /* Select a gender, if necessary;
751                force compatibility with role/race, try for compatibility
752                with pre-selected alignment. */
753             if (GEND < 0 || !validgend(ROLE, RACE, GEND)) {
754                 /* no gender yet, or pre-selected gender not valid */
755                 if (pick4u == 'y' || pick4u == 'a' || GEND == ROLE_RANDOM) {
756                     k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM);
757                     if (k < 0) {
758                         tty_putstr(BASE_WINDOW, 0, "Incompatible gender!");
759                         k = randgend(ROLE, RACE);
760                     }
761                 } else { /* pick4u == 'n' */
762                     /* Count the number of valid genders */
763                     n = 0; /* number valid */
764                     k = 0; /* valid gender */
765                     for (i = 0; i < ROLE_GENDERS; i++)
766                         if (ok_gend(ROLE, RACE, i, ALGN)) {
767                             n++;
768                             k = i;
769                         }
770                     if (n == 0) {
771                         for (i = 0; i < ROLE_GENDERS; i++)
772                             if (validgend(ROLE, RACE, i)) {
773                                 n++;
774                                 k = i;
775                             }
776                     }
777                     /* Permit the user to pick, if there is more than one */
778                     if (n > 1) {
779                         tty_clear_nhwindow(BASE_WINDOW);
780                         role_selection_prolog(RS_GENDER, BASE_WINDOW);
781                         win = create_nhwindow(NHW_MENU);
782                         start_menu(win);
783                         any = zeroany; /* zero out all bits */
784                         /* populate the menu with gender choices */
785                         setup_gendmenu(win, TRUE, ROLE, RACE, ALGN);
786                         /* add miscellaneous menu entries */
787                         role_menu_extra(ROLE_RANDOM, win, TRUE);
788                         any.a_int = 0; /* separator, not a choice */
789                         add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
790                                  MENU_UNSELECTED);
791                         role_menu_extra(RS_ROLE, win, FALSE);
792                         role_menu_extra(RS_RACE, win, FALSE);
793                         role_menu_extra(RS_ALGNMNT, win, FALSE);
794                         if (gotrolefilter())
795                             role_menu_extra(RS_filter, win, FALSE);
796                         role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
797 /*JP
798                         Strcpy(pbuf, "Pick a gender or sex");
799 */
800                         Strcpy(pbuf, "\90«\95Ê\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢");
801                         end_menu(win, pbuf);
802                         n = select_menu(win, PICK_ONE, &selected);
803                         if (n > 0) {
804                             choice = selected[0].item.a_int;
805                             if (n > 1 && choice == ROLE_RANDOM)
806                                 choice = selected[1].item.a_int;
807                         } else
808                             choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
809                         if (selected)
810                             free((genericptr_t) selected), selected = 0;
811                         destroy_nhwindow(win);
812
813                         if (choice == ROLE_NONE) {
814                             goto give_up; /* Selected quit */
815                         } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
816                             ALGN = k = ROLE_NONE;
817                             nextpick = RS_ALGNMNT;
818                         } else if (choice == RS_menu_arg(RS_RACE)) {
819                             RACE = k = ROLE_NONE;
820                             nextpick = RS_RACE;
821                         } else if (choice == RS_menu_arg(RS_ROLE)) {
822                             ROLE = k = ROLE_NONE;
823                             nextpick = RS_ROLE;
824                         } else if (choice == RS_menu_arg(RS_filter)) {
825                             GEND = k = ROLE_NONE;
826                             if (reset_role_filtering())
827                                 nextpick = RS_ROLE;
828                             else
829                                 nextpick = RS_GENDER;
830                         } else if (choice == ROLE_RANDOM) {
831                             k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM);
832                             if (k < 0)
833                                 k = randgend(ROLE, RACE);
834                         } else {
835                             k = choice - 1;
836                         }
837                     }
838                 }
839                 GEND = k;
840             } /* needed gender */
841         }     /* picking gender */
842
843         if (nextpick == RS_ALGNMNT) {
844             nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE : RS_GENDER;
845             /* Select an alignment, if necessary;
846                force compatibility with role/race/gender. */
847             if (ALGN < 0 || !validalign(ROLE, RACE, ALGN)) {
848                 /* no alignment yet, or pre-selected alignment not valid */
849                 if (pick4u == 'y' || pick4u == 'a' || ALGN == ROLE_RANDOM) {
850                     k = pick_align(ROLE, RACE, GEND, PICK_RANDOM);
851                     if (k < 0) {
852                         tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!");
853                         k = randalign(ROLE, RACE);
854                     }
855                 } else { /* pick4u == 'n' */
856                     /* Count the number of valid alignments */
857                     n = 0; /* number valid */
858                     k = 0; /* valid alignment */
859                     for (i = 0; i < ROLE_ALIGNS; i++)
860                         if (ok_align(ROLE, RACE, GEND, i)) {
861                             n++;
862                             k = i;
863                         }
864                     if (n == 0) {
865                         for (i = 0; i < ROLE_ALIGNS; i++)
866                             if (validalign(ROLE, RACE, i)) {
867                                 n++;
868                                 k = i;
869                             }
870                     }
871                     /* Permit the user to pick, if there is more than one */
872                     if (n > 1) {
873                         tty_clear_nhwindow(BASE_WINDOW);
874                         role_selection_prolog(RS_ALGNMNT, BASE_WINDOW);
875                         win = create_nhwindow(NHW_MENU);
876                         start_menu(win);
877                         any = zeroany; /* zero out all bits */
878                         setup_algnmenu(win, TRUE, ROLE, RACE, GEND);
879                         role_menu_extra(ROLE_RANDOM, win, TRUE);
880                         any.a_int = 0; /* separator, not a choice */
881                         add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
882                                  MENU_UNSELECTED);
883                         role_menu_extra(RS_ROLE, win, FALSE);
884                         role_menu_extra(RS_RACE, win, FALSE);
885                         role_menu_extra(RS_GENDER, win, FALSE);
886                         if (gotrolefilter())
887                             role_menu_extra(RS_filter, win, FALSE);
888                         role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
889 /*JP
890                         Strcpy(pbuf, "Pick an alignment or creed");
891 */
892                         Strcpy(pbuf, "\91®\90«\82ð\91I\82ñ\82Å\82­\82¾\82³\82¢");
893                         end_menu(win, pbuf);
894                         n = select_menu(win, PICK_ONE, &selected);
895                         if (n > 0) {
896                             choice = selected[0].item.a_int;
897                             if (n > 1 && choice == ROLE_RANDOM)
898                                 choice = selected[1].item.a_int;
899                         } else
900                             choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
901                         if (selected)
902                             free((genericptr_t) selected), selected = 0;
903                         destroy_nhwindow(win);
904
905                         if (choice == ROLE_NONE) {
906                             goto give_up; /* Selected quit */
907                         } else if (choice == RS_menu_arg(RS_GENDER)) {
908                             GEND = k = ROLE_NONE;
909                             nextpick = RS_GENDER;
910                         } else if (choice == RS_menu_arg(RS_RACE)) {
911                             RACE = k = ROLE_NONE;
912                             nextpick = RS_RACE;
913                         } else if (choice == RS_menu_arg(RS_ROLE)) {
914                             ROLE = k = ROLE_NONE;
915                             nextpick = RS_ROLE;
916                         } else if (choice == RS_menu_arg(RS_filter)) {
917                             ALGN = k = ROLE_NONE;
918                             if (reset_role_filtering())
919                                 nextpick = RS_ROLE;
920                             else
921                                 nextpick = RS_ALGNMNT;
922                         } else if (choice == ROLE_RANDOM) {
923                             k = pick_align(ROLE, RACE, GEND, PICK_RANDOM);
924                             if (k < 0)
925                                 k = randalign(ROLE, RACE);
926                         } else {
927                             k = choice - 1;
928                         }
929                     }
930                 }
931                 ALGN = k;
932             } /* needed alignment */
933         }     /* picking alignment */
934
935     } while (ROLE < 0 || RACE < 0 || GEND < 0 || ALGN < 0);
936
937     /*
938      *  Role, race, &c have now been determined;
939      *  ask for confirmation and maybe go back to choose all over again.
940      *
941      *  Uses ynaq for familiarity, although 'a' is usually a
942      *  superset of 'y' but here is an alternate form of 'n'.
943      *  Menu layout:
944      *   title:  Is this ok? [ynaq]
945      *   blank:
946      *    text:  $name, $alignment $gender $race $role
947      *   blank:
948      *    menu:  y + yes; play
949      *           n - no; pick again
950      *   maybe:  a - no; rename hero
951      *           q - quit
952      *           (end)
953      */
954     getconfirmation = (picksomething && pick4u != 'a' && !flags.randomall);
955     while (getconfirmation) {
956         tty_clear_nhwindow(BASE_WINDOW);
957         role_selection_prolog(ROLE_NONE, BASE_WINDOW);
958         win = create_nhwindow(NHW_MENU);
959         start_menu(win);
960         any = zeroany; /* zero out all bits */
961         any.a_int = 0;
962         if (!roles[ROLE].name.f
963             && (roles[ROLE].allow & ROLE_GENDMASK)
964                    == (ROLE_MALE | ROLE_FEMALE))
965 /*JP
966             Sprintf(plbuf, " %s", genders[GEND].adj);
967 */
968             Sprintf(plbuf, "%s\82Ì", genders[GEND].adj);
969         else
970             *plbuf = '\0'; /* omit redundant gender */
971 #if 0 /*JP*/
972         Sprintf(pbuf, "%s, %s%s %s %s", plname, aligns[ALGN].adj, plbuf,
973                 races[RACE].adj,
974                 (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f
975                                                   : roles[ROLE].name.m);
976 #else
977         Sprintf(pbuf, "%s, %s%s%s%s", plname, aligns[ALGN].adj, plbuf,
978                 races[RACE].adj,
979                 (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f
980                                                   : roles[ROLE].name.m);
981 #endif
982         add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, pbuf,
983                  MENU_UNSELECTED);
984         /* blank separator */
985         any.a_int = 0;
986         add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
987         /* [ynaq] menu choices */
988         any.a_int = 1;
989 /*JP
990         add_menu(win, NO_GLYPH, &any, 'y', 0, ATR_NONE, "Yes; start game",
991 */
992         add_menu(win, NO_GLYPH, &any, 'y', 0, ATR_NONE, "\82Í\82¢; \83Q\81[\83\80\82ð\8en\82ß\82é",
993                  MENU_SELECTED);
994         any.a_int = 2;
995         add_menu(win, NO_GLYPH, &any, 'n', 0, ATR_NONE,
996 /*JP
997                  "No; choose role again", MENU_UNSELECTED);
998 */
999                  "\82¢\82¢\82¦; \90E\8bÆ\82ð\91I\82Ñ\92¼\82·", MENU_UNSELECTED);
1000         if (iflags.renameallowed) {
1001             any.a_int = 3;
1002             add_menu(win, NO_GLYPH, &any, 'a', 0, ATR_NONE,
1003 /*JP
1004                      "Not yet; choose another name", MENU_UNSELECTED);
1005 */
1006                      "\82Ü\82¾; \96¼\91O\82ð\95Ï\82¦\82é", MENU_UNSELECTED);
1007         }
1008         any.a_int = -1;
1009 /*JP
1010         add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
1011 */
1012         add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "\94²\82¯\82é",
1013                  MENU_UNSELECTED);
1014 /*JP
1015         Sprintf(pbuf, "Is this ok? [yn%sq]", iflags.renameallowed ? "a" : "");
1016 */
1017         Sprintf(pbuf, "\82±\82ê\82Å\82æ\82¢\81H [yn%sq]", iflags.renameallowed ? "a" : "");
1018         end_menu(win, pbuf);
1019         n = select_menu(win, PICK_ONE, &selected);
1020         /* [pick-one menus with a preselected entry behave oddly...] */
1021         choice = (n > 0) ? selected[n - 1].item.a_int : (n == 0) ? 1 : -1;
1022         if (selected)
1023             free((genericptr_t) selected), selected = 0;
1024         destroy_nhwindow(win);
1025
1026         switch (choice) {
1027         default:          /* 'q' or ESC */
1028             goto give_up; /* quit */
1029             break;
1030         case 3: { /* 'a' */
1031             /*
1032              * TODO: what, if anything, should be done if the name is
1033              * changed to or from "wizard" after port-specific startup
1034              * code has set flags.debug based on the original name?
1035              */
1036             int saveROLE, saveRACE, saveGEND, saveALGN;
1037
1038             iflags.renameinprogress = TRUE;
1039             /* plnamesuffix() can change any or all of ROLE, RACE,
1040                GEND, ALGN; we'll override that and honor only the name */
1041             saveROLE = ROLE, saveRACE = RACE, saveGEND = GEND,
1042                 saveALGN = ALGN;
1043             *plname = '\0';
1044             plnamesuffix(); /* calls askname() when plname[] is empty */
1045             ROLE = saveROLE, RACE = saveRACE, GEND = saveGEND,
1046                 ALGN = saveALGN;
1047             break; /* getconfirmation is still True */
1048         }
1049         case 2:    /* 'n' */
1050             /* start fresh, but bypass "shall I pick everything for you?"
1051                step; any partial role selection via config file, command
1052                line, or name suffix is discarded this time */
1053             pick4u = 'n';
1054             ROLE = RACE = GEND = ALGN = ROLE_NONE;
1055             goto makepicks;
1056             break;
1057         case 1: /* 'y' or Space or Return/Enter */
1058             /* success; drop out through end of function */
1059             getconfirmation = FALSE;
1060             break;
1061         }
1062     }
1063
1064     /* Success! */
1065     tty_display_nhwindow(BASE_WINDOW, FALSE);
1066     return;
1067
1068  give_up:
1069     /* Quit */
1070     if (selected)
1071         free((genericptr_t) selected); /* [obsolete] */
1072     bail((char *) 0);
1073     /*NOTREACHED*/
1074     return;
1075 }
1076
1077 STATIC_OVL boolean
1078 reset_role_filtering()
1079 {
1080     winid win;
1081     anything any;
1082     int i, n;
1083     menu_item *selected = 0;
1084
1085     win = create_nhwindow(NHW_MENU);
1086     start_menu(win);
1087     any = zeroany;
1088
1089     /* no extra blank line preceding this entry; end_menu supplies one */
1090     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
1091              "Unacceptable roles", MENU_UNSELECTED);
1092     setup_rolemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1093
1094     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1095     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
1096              "Unacceptable races", MENU_UNSELECTED);
1097     setup_racemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1098
1099     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1100     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
1101              "Unacceptable genders", MENU_UNSELECTED);
1102     setup_gendmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1103
1104     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1105     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
1106              "Unacceptable alignments", MENU_UNSELECTED);
1107     setup_algnmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
1108
1109     end_menu(win, "Pick all that apply");
1110     n = select_menu(win, PICK_ANY, &selected);
1111
1112     if (n > 0) {
1113         clearrolefilter();
1114         for (i = 0; i < n; i++)
1115             setrolefilter(selected[i].item.a_string);
1116
1117         ROLE = RACE = GEND = ALGN = ROLE_NONE;
1118     }
1119     if (selected)
1120         free((genericptr_t) selected), selected = 0;
1121     destroy_nhwindow(win);
1122     return (n > 0) ? TRUE : FALSE;
1123 }
1124
1125 #undef ROLE
1126 #undef RACE
1127 #undef GEND
1128 #undef ALGN
1129
1130 /* add entries a-Archeologist, b-Barbarian, &c to menu being built in 'win' */
1131 STATIC_OVL void
1132 setup_rolemenu(win, filtering, race, gend, algn)
1133 winid win;
1134 boolean filtering; /* True => exclude filtered roles; False => filter reset */
1135 int race, gend, algn; /* all ROLE_NONE for !filtering case */
1136 {
1137     anything any;
1138     int i;
1139     boolean role_ok;
1140     char thisch, lastch = '\0', rolenamebuf[50];
1141
1142     any = zeroany; /* zero out all bits */
1143     for (i = 0; roles[i].name.m; i++) {
1144         role_ok = ok_role(i, race, gend, algn);
1145         if (filtering && !role_ok)
1146             continue;
1147         if (filtering)
1148             any.a_int = i + 1;
1149         else
1150             any.a_string = roles[i].name.m;
1151 /*JP
1152         thisch = lowc(*roles[i].name.m);
1153 */
1154         thisch = lowc(*roles[i].filecode);
1155         if (thisch == lastch)
1156             thisch = highc(thisch);
1157         Strcpy(rolenamebuf, roles[i].name.m);
1158         if (roles[i].name.f) {
1159             /* role has distinct name for female (C,P) */
1160             if (gend == 1) {
1161                 /* female already chosen; replace male name */
1162                 Strcpy(rolenamebuf, roles[i].name.f);
1163             } else if (gend < 0) {
1164                 /* not chosen yet; append slash+female name */
1165                 Strcat(rolenamebuf, "/");
1166                 Strcat(rolenamebuf, roles[i].name.f);
1167             }
1168         }
1169         /* !filtering implies reset_role_filtering() where we want to
1170            mark this role as preseleted if current filter excludes it */
1171         add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE, an(rolenamebuf),
1172                  (!filtering && !role_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1173         lastch = thisch;
1174     }
1175 }
1176
1177 STATIC_OVL void
1178 setup_racemenu(win, filtering, role, gend, algn)
1179 winid win;
1180 boolean filtering;
1181 int role, gend, algn;
1182 {
1183     anything any;
1184     boolean race_ok;
1185     int i;
1186     char this_ch;
1187
1188     any = zeroany;
1189     for (i = 0; races[i].noun; i++) {
1190         race_ok = ok_race(role, i, gend, algn);
1191         if (filtering && !race_ok)
1192             continue;
1193         if (filtering)
1194             any.a_int = i + 1;
1195         else
1196             any.a_string = races[i].noun;
1197 /*JP
1198         this_ch = *races[i].noun;
1199 */
1200         this_ch = lowc(*races[i].filecode);
1201         /* filtering: picking race, so choose by first letter, with
1202            capital letter as unseen accelerator;
1203            !filtering: resetting filter rather than picking, choose by
1204            capital letter since lowercase role letters will be present */
1205         add_menu(win, NO_GLYPH, &any,
1206                  filtering ? this_ch : highc(this_ch),
1207                  filtering ? highc(this_ch) : 0,
1208                  ATR_NONE, races[i].noun,
1209                  (!filtering && !race_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1210     }
1211 }
1212
1213 STATIC_DCL void
1214 setup_gendmenu(win, filtering, role, race, algn)
1215 winid win;
1216 boolean filtering;
1217 int role, race, algn;
1218 {
1219     anything any;
1220     boolean gend_ok;
1221     int i;
1222     char this_ch;
1223
1224     any = zeroany;
1225     for (i = 0; i < ROLE_GENDERS; i++) {
1226         gend_ok = ok_gend(role, race, i, algn);
1227         if (filtering && !gend_ok)
1228             continue;
1229         if (filtering)
1230             any.a_int = i + 1;
1231         else
1232             any.a_string = genders[i].adj;
1233 /*JP
1234         this_ch = *genders[i].adj;
1235 */
1236         this_ch = lowc(*genders[i].filecode);
1237         /* (see setup_racemenu for explanation of selector letters
1238            and setup_rolemenu for preselection) */
1239         add_menu(win, NO_GLYPH, &any,
1240                  filtering ? this_ch : highc(this_ch),
1241                  filtering ? highc(this_ch) : 0,
1242                  ATR_NONE, genders[i].adj,
1243                  (!filtering && !gend_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1244     }
1245 }
1246
1247 STATIC_DCL void
1248 setup_algnmenu(win, filtering, role, race, gend)
1249 winid win;
1250 boolean filtering;
1251 int role, race, gend;
1252 {
1253     anything any;
1254     boolean algn_ok;
1255     int i;
1256     char this_ch;
1257
1258     any = zeroany;
1259     for (i = 0; i < ROLE_ALIGNS; i++) {
1260         algn_ok = ok_align(role, race, gend, i);
1261         if (filtering && !algn_ok)
1262             continue;
1263         if (filtering)
1264             any.a_int = i + 1;
1265         else
1266 /*JP
1267             any.a_string = aligns[i].adj;
1268 */
1269             any.a_string = aligns[i].noun;
1270 /*JP
1271         this_ch = *aligns[i].adj;
1272 */
1273         this_ch = lowc(*aligns[i].filecode);
1274         /* (see setup_racemenu for explanation of selector letters
1275            and setup_rolemenu for preselection) */
1276 #if 0 /*JP*/
1277         add_menu(win, NO_GLYPH, &any,
1278                  filtering ? this_ch : highc(this_ch),
1279                  filtering ? highc(this_ch) : 0,
1280                  ATR_NONE, aligns[i].adj,
1281                  (!filtering && !algn_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1282 #else
1283         add_menu(win, NO_GLYPH, &any,
1284                  filtering ? this_ch : highc(this_ch),
1285                  filtering ? highc(this_ch) : 0,
1286                  ATR_NONE, aligns[i].noun,
1287                  (!filtering && !algn_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1288 #endif
1289     }
1290 }
1291
1292 /*
1293  * plname is filled either by an option (-u Player  or  -uPlayer) or
1294  * explicitly (by being the wizard) or by askname.
1295  * It may still contain a suffix denoting the role, etc.
1296  * Always called after init_nhwindows() and before display_gamewindows().
1297  */
1298 void
1299 tty_askname()
1300 {
1301 /*JP
1302     static const char who_are_you[] = "Who are you? ";
1303 */
1304     static const char who_are_you[] = "\82 \82È\82½\82Í\92N\81H ";
1305     register int c, ct, tryct = 0;
1306 #if 1 /*JP*/
1307     char ptmpname[PL_NSIZ];
1308 #endif
1309
1310 #ifdef SELECTSAVED
1311     if (iflags.wc2_selectsaved && !iflags.renameinprogress)
1312         switch (restore_menu(BASE_WINDOW)) {
1313         case -1:
1314             bail("Until next time then..."); /* quit */
1315             /*NOTREACHED*/
1316         case 0:
1317             break; /* no game chosen; start new game */
1318         case 1:
1319             return; /* plname[] has been set */
1320         }
1321 #endif /* SELECTSAVED */
1322
1323     tty_putstr(BASE_WINDOW, 0, "");
1324     do {
1325         if (++tryct > 1) {
1326             if (tryct > 10)
1327                 bail("Giving up after 10 tries.\n");
1328             tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury - 1);
1329 #if 0 /*JP*/
1330             tty_putstr(BASE_WINDOW, 0, "Enter a name for your character...");
1331 #else
1332             tty_putstr(BASE_WINDOW, 0, "\82 \82È\82½\82Ì\83L\83\83\83\89\83N\83^\82Ì\96¼\91O\82Í\81H");
1333 #endif
1334             /* erase previous prompt (in case of ESC after partial response) */
1335             tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury), cl_end();
1336         }
1337         tty_putstr(BASE_WINDOW, 0, who_are_you);
1338         tty_curs(BASE_WINDOW, (int) (sizeof who_are_you),
1339                  wins[BASE_WINDOW]->cury - 1);
1340         ct = 0;
1341         while ((c = tty_nhgetch()) != '\n') {
1342             if (c == EOF)
1343                 c = '\033';
1344             if (c == '\r')
1345                 break;
1346             if (c == '\033') {
1347                 ct = 0;
1348                 break;
1349             } /* continue outer loop */
1350 #if defined(WIN32CON)
1351             if (c == '\003')
1352                 bail("^C abort.\n");
1353 #endif
1354             /* some people get confused when their erase char is not ^H */
1355             if (c == '\b' || c == '\177') {
1356 #if 1 /*JP*/
1357             moreback:
1358 #endif
1359                 if (ct) {
1360                     ct--;
1361 #ifdef WIN32CON
1362                     ttyDisplay->curx--;
1363 #endif
1364 #if defined(MICRO) || defined(WIN32CON)
1365 #if defined(WIN32CON) || defined(MSDOS)
1366                     backsp(); /* \b is visible on NT */
1367                     (void) putchar(' ');
1368                     backsp();
1369 #else
1370                     msmsg("\b \b");
1371 #endif
1372 #else
1373                     (void) putchar('\b');
1374                     (void) putchar(' ');
1375                     (void) putchar('\b');
1376 #endif
1377                 }
1378 #if 1 /*JP*/
1379                 if(is_kanji2(ptmpname, ct))
1380                   goto moreback;
1381 #endif
1382                 continue;
1383             }
1384 #if defined(UNIX) || defined(VMS)
1385 # if 0 /*JP*/
1386             if (c != '-' && c != '@')
1387                 if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z')
1388                     /* reject leading digit but allow digits elsewhere
1389                        (avoids ambiguity when character name gets
1390                        appended to uid to construct save file name) */
1391                     && !(c >= '0' && c <= '9' && ct > 0))
1392                     c = '_';
1393 # endif /*JP*/
1394 #endif
1395             if (ct < (int) (sizeof plname) - 1) {
1396 #if defined(MICRO)
1397 #if defined(MSDOS)
1398                 if (iflags.grmode) {
1399 #  if 0 /*JP*/
1400                     (void) putchar(c);
1401 #  else
1402                     (void) cputchar(c);
1403 #   ifdef NO_TERMS
1404                     ttyDisplay->curx++;
1405                     wins[BASE_WINDOW]->curx++;
1406 #   endif
1407 #  endif /*JP*/
1408                 } else
1409 #endif
1410 /*JP
1411                     msmsg("%c", c);
1412 */
1413                     (void) putchar(c);
1414 #else
1415                 (void) putchar(c);
1416 #endif
1417 #if 0 /*JP*/
1418                 plname[ct++] = c;
1419 #else
1420                 ptmpname[ct++] = c;
1421 #endif
1422 #ifdef WIN32CON
1423                 ttyDisplay->curx++;
1424 #endif
1425             }
1426         }
1427 #if 0 /*JP*/
1428         plname[ct] = 0;
1429 #else
1430         ptmpname[ct] = 0;
1431 #endif
1432     } while (ct == 0);
1433
1434     /* move to next line to simulate echo of user's <return> */
1435     tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury + 1);
1436 #if 1 /*JP*/
1437     Strcpy(plname, str2ic(ptmpname));
1438 #endif
1439
1440     /* since we let user pick an arbitrary name now, he/she can pick
1441        another one during role selection */
1442     iflags.renameallowed = TRUE;
1443 }
1444
1445 void
1446 tty_get_nh_event()
1447 {
1448     return;
1449 }
1450
1451 #if !defined(MICRO) && !defined(WIN32CON)
1452 STATIC_OVL void
1453 getret()
1454 {
1455     HUPSKIP();
1456 #if 1 /*JP*/
1457     jputchar('\0');
1458 #endif
1459     xputs("\n");
1460     if (flags.standout)
1461         standoutbeg();
1462     xputs("Hit ");
1463     xputs(iflags.cbreak ? "space" : "return");
1464     xputs(" to continue: ");
1465     if (flags.standout)
1466         standoutend();
1467     xwaitforspace(" ");
1468 }
1469 #endif
1470
1471 void
1472 tty_suspend_nhwindows(str)
1473 const char *str;
1474 {
1475     settty(str); /* calls end_screen, perhaps raw_print */
1476     if (!str)
1477         tty_raw_print(""); /* calls fflush(stdout) */
1478 }
1479
1480 void
1481 tty_resume_nhwindows()
1482 {
1483     gettty();
1484     setftty(); /* calls start_screen */
1485     docrt();
1486 }
1487
1488 void
1489 tty_exit_nhwindows(str)
1490 const char *str;
1491 {
1492     winid i;
1493
1494     tty_suspend_nhwindows(str);
1495     /*
1496      * Disable windows to avoid calls to window routines.
1497      */
1498     free_pickinv_cache(); /* reset its state as well as tear down window */
1499     for (i = 0; i < MAXWIN; i++) {
1500         if (i == BASE_WINDOW)
1501             continue; /* handle wins[BASE_WINDOW] last */
1502         if (wins[i]) {
1503 #ifdef FREE_ALL_MEMORY
1504             free_window_info(wins[i], TRUE);
1505             free((genericptr_t) wins[i]);
1506 #endif
1507             wins[i] = (struct WinDesc *) 0;
1508         }
1509     }
1510     WIN_MAP = WIN_MESSAGE = WIN_INVEN = WIN_ERR; /* these are all gone now */
1511     WIN_STATUS = WIN_ERR;
1512 #ifdef FREE_ALL_MEMORY
1513     if (BASE_WINDOW != WIN_ERR && wins[BASE_WINDOW]) {
1514         free_window_info(wins[BASE_WINDOW], TRUE);
1515         free((genericptr_t) wins[BASE_WINDOW]);
1516         wins[BASE_WINDOW] = (struct WinDesc *) 0;
1517         BASE_WINDOW = WIN_ERR;
1518     }
1519     free((genericptr_t) ttyDisplay);
1520     ttyDisplay = (struct DisplayDesc *) 0;
1521 #endif
1522
1523 #ifndef NO_TERMS    /*(until this gets added to the window interface)*/
1524     tty_shutdown(); /* cleanup termcap/terminfo/whatever */
1525 #endif
1526 #ifdef WIN32
1527     nttty_exit();
1528 #endif
1529     iflags.window_inited = 0;
1530 }
1531
1532 winid
1533 tty_create_nhwindow(type)
1534 int type;
1535 {
1536     struct WinDesc *newwin;
1537     int i, rowoffset;
1538     int newid;
1539
1540     for (newid = 0; newid < MAXWIN; ++newid)
1541         if (wins[newid] == 0)
1542             break;
1543     if (newid == MAXWIN) {
1544         panic("No window slots!");
1545         /*NOTREACHED*/
1546         return WIN_ERR;
1547     }
1548
1549     newwin = (struct WinDesc *) alloc(sizeof (struct WinDesc));
1550     wins[newid] = newwin;
1551
1552     newwin->type = type;
1553     newwin->flags = 0;
1554     newwin->active = FALSE;
1555     newwin->curx = newwin->cury = 0;
1556     newwin->morestr = 0;
1557     newwin->mlist = (tty_menu_item *) 0;
1558     newwin->plist = (tty_menu_item **) 0;
1559     newwin->npages = newwin->plist_size = newwin->nitems = newwin->how = 0;
1560     switch (type) {
1561     case NHW_BASE:
1562         /* base window, used for absolute movement on the screen */
1563         newwin->offx = newwin->offy = 0;
1564         newwin->rows = ttyDisplay->rows;
1565         newwin->cols = ttyDisplay->cols;
1566         newwin->maxrow = newwin->maxcol = 0;
1567         break;
1568     case NHW_MESSAGE:
1569         /* message window, 1 line long, very wide, top of screen */
1570         newwin->offx = newwin->offy = 0;
1571         /* sanity check */
1572         if (iflags.msg_history < 20)
1573             iflags.msg_history = 20;
1574         else if (iflags.msg_history > 60)
1575             iflags.msg_history = 60;
1576         newwin->maxrow = newwin->rows = iflags.msg_history;
1577         newwin->maxcol = newwin->cols = 0;
1578         break;
1579     case NHW_STATUS:
1580         /* status window, 2 or 3 lines long, full width, bottom of screen */
1581         if (iflags.wc2_statuslines < 2
1582 #ifndef CLIPPING
1583             || (LI < 1 + ROWNO + 3)
1584 #endif
1585             || iflags.wc2_statuslines > 3)
1586             iflags.wc2_statuslines = 2;
1587         newwin->offx = 0;
1588         rowoffset = ttyDisplay->rows - iflags.wc2_statuslines;
1589 #if defined(USE_TILES) && defined(MSDOS)
1590         if (iflags.grmode) {
1591             newwin->offy = rowoffset;
1592         } else
1593 #endif
1594             newwin->offy = min(rowoffset, ROWNO + 1);
1595         newwin->rows = newwin->maxrow = iflags.wc2_statuslines;
1596         newwin->cols = newwin->maxcol = ttyDisplay->cols;
1597         break;
1598     case NHW_MAP:
1599         /* map window, ROWNO lines long, full width, below message window */
1600         newwin->offx = 0;
1601         newwin->offy = 1;
1602         newwin->rows = ROWNO;
1603         newwin->cols = COLNO;
1604         newwin->maxrow = 0; /* no buffering done -- let gbuf do it */
1605         newwin->maxcol = 0;
1606         break;
1607     case NHW_MENU:
1608     case NHW_TEXT:
1609         /* inventory/menu window, variable length, full width, top of screen;
1610            help window, the same, different semantics for display, etc */
1611         newwin->offx = newwin->offy = 0;
1612         newwin->rows = 0;
1613         newwin->cols = ttyDisplay->cols;
1614         newwin->maxrow = newwin->maxcol = 0;
1615         break;
1616     default:
1617         panic("Tried to create window type %d\n", (int) type);
1618         /*NOTREACHED*/
1619         return WIN_ERR;
1620     }
1621
1622     if (newwin->maxrow) {
1623         newwin->data = (char **) alloc(
1624                                (unsigned) (newwin->maxrow * sizeof (char *)));
1625         newwin->datlen = (short *) alloc(
1626                                 (unsigned) (newwin->maxrow * sizeof (short)));
1627         for (i = 0; i < newwin->maxrow; i++) {
1628             if (newwin->maxcol) { /* WIN_STATUS */
1629                 newwin->data[i] = (char *) alloc((unsigned) newwin->maxcol);
1630                 newwin->datlen[i] = (short) newwin->maxcol;
1631             } else {
1632                 newwin->data[i] = (char *) 0;
1633                 newwin->datlen[i] = 0;
1634             }
1635         }
1636         if (newwin->type == NHW_MESSAGE)
1637             newwin->maxrow = 0;
1638     } else {
1639         newwin->data = (char **) 0;
1640         newwin->datlen = (short *) 0;
1641     }
1642
1643     return newid;
1644 }
1645
1646 STATIC_OVL void
1647 erase_menu_or_text(window, cw, clear)
1648 winid window;
1649 struct WinDesc *cw;
1650 boolean clear;
1651 {
1652     if (cw->offx == 0) {
1653         if (cw->offy) {
1654             tty_curs(window, 1, 0);
1655             cl_eos();
1656         } else if (clear) {
1657             clear_screen();
1658         } else {
1659             docrt();
1660         }
1661     } else {
1662         docorner((int) cw->offx, cw->maxrow + 1);
1663     }
1664 }
1665
1666 STATIC_OVL void
1667 free_window_info(cw, free_data)
1668 struct WinDesc *cw;
1669 boolean free_data;
1670 {
1671     int i;
1672
1673     if (cw->data) {
1674         if (cw == wins[WIN_MESSAGE] && cw->rows > cw->maxrow)
1675             cw->maxrow = cw->rows; /* topl data */
1676         for (i = 0; i < cw->maxrow; i++)
1677             if (cw->data[i]) {
1678                 free((genericptr_t) cw->data[i]);
1679                 cw->data[i] = (char *) 0;
1680                 if (cw->datlen)
1681                     cw->datlen[i] = 0;
1682             }
1683         if (free_data) {
1684             free((genericptr_t) cw->data);
1685             cw->data = (char **) 0;
1686             if (cw->datlen)
1687                 free((genericptr_t) cw->datlen);
1688             cw->datlen = (short *) 0;
1689             cw->rows = 0;
1690         }
1691     }
1692     cw->maxrow = cw->maxcol = 0;
1693     if (cw->mlist) {
1694         tty_menu_item *temp;
1695
1696         while ((temp = cw->mlist) != 0) {
1697             cw->mlist = temp->next;
1698             if (temp->str)
1699                 free((genericptr_t) temp->str);
1700             free((genericptr_t) temp);
1701         }
1702     }
1703     if (cw->plist) {
1704         free((genericptr_t) cw->plist);
1705         cw->plist = 0;
1706     }
1707     cw->plist_size = cw->npages = cw->nitems = cw->how = 0;
1708     if (cw->morestr) {
1709         free((genericptr_t) cw->morestr);
1710         cw->morestr = 0;
1711     }
1712 }
1713
1714 void
1715 tty_clear_nhwindow(window)
1716 winid window;
1717 {
1718     int i, j, m, n;
1719     register struct WinDesc *cw = 0;
1720
1721     HUPSKIP();
1722     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1723         panic(winpanicstr, window);
1724     ttyDisplay->lastwin = window;
1725
1726     print_vt_code2(AVTC_SELECT_WINDOW, window);
1727
1728     switch (cw->type) {
1729     case NHW_MESSAGE:
1730         if (ttyDisplay->toplin) {
1731             home();
1732             cl_end();
1733             if (cw->cury)
1734                 docorner(1, cw->cury + 1);
1735             ttyDisplay->toplin = 0;
1736         }
1737         break;
1738     case NHW_STATUS:
1739         m = cw->maxrow;
1740         n = cw->cols;
1741         for (i = 0; i < m; ++i) {
1742             tty_curs(window, 1, i);
1743             cl_end();
1744
1745             for (j = 0; j < n - 1; ++j)
1746                 cw->data[i][j] = ' ';
1747             cw->data[i][n - 1] = '\0';
1748             /*finalx[i][NOW] = finalx[i][BEFORE] = 0;*/
1749         }
1750         context.botlx = 1;
1751         break;
1752     case NHW_MAP:
1753         /* cheap -- clear the whole thing and tell nethack to redraw botl */
1754         context.botlx = 1;
1755         /*FALLTHRU*/
1756     case NHW_BASE:
1757         clear_screen();
1758         /*for (i = 0; i < cw->maxrow; ++i)           */
1759         /*    finalx[i][NOW] = finalx[i][BEFORE] = 0;*/
1760         break;
1761     case NHW_MENU:
1762     case NHW_TEXT:
1763         if (cw->active)
1764             erase_menu_or_text(window, cw, TRUE);
1765         free_window_info(cw, FALSE);
1766         break;
1767     }
1768     cw->curx = cw->cury = 0;
1769 }
1770
1771 boolean
1772 toggle_menu_curr(window, curr, lineno, in_view, counting, count)
1773 winid window;
1774 tty_menu_item *curr;
1775 int lineno;
1776 boolean in_view, counting;
1777 long count;
1778 {
1779     if (curr->selected) {
1780         if (counting && count > 0) {
1781             curr->count = count;
1782             if (in_view)
1783                 set_item_state(window, lineno, curr);
1784             return TRUE;
1785         } else { /* change state */
1786             curr->selected = FALSE;
1787             curr->count = -1L;
1788             if (in_view)
1789                 set_item_state(window, lineno, curr);
1790             return TRUE;
1791         }
1792     } else { /* !selected */
1793         if (counting && count > 0) {
1794             curr->count = count;
1795             curr->selected = TRUE;
1796             if (in_view)
1797                 set_item_state(window, lineno, curr);
1798             return TRUE;
1799         } else if (!counting) {
1800             curr->selected = TRUE;
1801             if (in_view)
1802                 set_item_state(window, lineno, curr);
1803             return TRUE;
1804         }
1805         /* do nothing counting&&count==0 */
1806     }
1807     return FALSE;
1808 }
1809
1810 STATIC_OVL void
1811 dmore(cw, s)
1812 register struct WinDesc *cw;
1813 const char *s; /* valid responses */
1814 {
1815     const char *prompt = cw->morestr ? cw->morestr : defmorestr;
1816     int offset = (cw->type == NHW_TEXT) ? 1 : 2;
1817
1818     HUPSKIP();
1819     tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + offset,
1820              (int) ttyDisplay->cury);
1821     if (flags.standout)
1822         standoutbeg();
1823 #if 0 /*JP*/
1824     xputs(prompt);
1825     ttyDisplay->curx += strlen(prompt);
1826 #else
1827     jputchar('\0'); /* reset */
1828     {
1829       const char *p;
1830       p = prompt;
1831         while(*p){
1832             jputchar(*(p++));
1833             ttyDisplay->curx++;
1834         }
1835     }
1836 /*    jputs(prompt);*/
1837 #endif
1838     if (flags.standout)
1839         standoutend();
1840
1841     xwaitforspace(s);
1842 }
1843
1844 STATIC_OVL void
1845 set_item_state(window, lineno, item)
1846 winid window;
1847 int lineno;
1848 tty_menu_item *item;
1849 {
1850     char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-';
1851
1852     HUPSKIP();
1853     tty_curs(window, 4, lineno);
1854     term_start_attr(item->attr);
1855 /*JP
1856     (void) putchar(ch);
1857 */
1858     (void) jputchar(ch);
1859     ttyDisplay->curx++;
1860     term_end_attr(item->attr);
1861 }
1862
1863 STATIC_OVL void
1864 set_all_on_page(window, page_start, page_end)
1865 winid window;
1866 tty_menu_item *page_start, *page_end;
1867 {
1868     tty_menu_item *curr;
1869     int n;
1870
1871     for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1872         if (curr->identifier.a_void && !curr->selected) {
1873             curr->selected = TRUE;
1874             set_item_state(window, n, curr);
1875         }
1876 }
1877
1878 STATIC_OVL void
1879 unset_all_on_page(window, page_start, page_end)
1880 winid window;
1881 tty_menu_item *page_start, *page_end;
1882 {
1883     tty_menu_item *curr;
1884     int n;
1885
1886     for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1887         if (curr->identifier.a_void && curr->selected) {
1888             curr->selected = FALSE;
1889             curr->count = -1L;
1890             set_item_state(window, n, curr);
1891         }
1892 }
1893
1894 STATIC_OVL void
1895 invert_all_on_page(window, page_start, page_end, acc)
1896 winid window;
1897 tty_menu_item *page_start, *page_end;
1898 char acc; /* group accelerator, 0 => all */
1899 {
1900     tty_menu_item *curr;
1901     int n;
1902
1903     for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1904         if (curr->identifier.a_void && (acc == 0 || curr->gselector == acc)) {
1905             if (curr->selected) {
1906                 curr->selected = FALSE;
1907                 curr->count = -1L;
1908             } else
1909                 curr->selected = TRUE;
1910             set_item_state(window, n, curr);
1911         }
1912 }
1913
1914 /*
1915  * Invert all entries that match the give group accelerator (or all if zero).
1916  */
1917 STATIC_OVL void
1918 invert_all(window, page_start, page_end, acc)
1919 winid window;
1920 tty_menu_item *page_start, *page_end;
1921 char acc; /* group accelerator, 0 => all */
1922 {
1923     tty_menu_item *curr;
1924     boolean on_curr_page;
1925     struct WinDesc *cw = wins[window];
1926
1927     invert_all_on_page(window, page_start, page_end, acc);
1928
1929     /* invert the rest */
1930     for (on_curr_page = FALSE, curr = cw->mlist; curr; curr = curr->next) {
1931         if (curr == page_start)
1932             on_curr_page = TRUE;
1933         else if (curr == page_end)
1934             on_curr_page = FALSE;
1935
1936         if (!on_curr_page && curr->identifier.a_void
1937             && (acc == 0 || curr->gselector == acc)) {
1938             if (curr->selected) {
1939                 curr->selected = FALSE;
1940                 curr->count = -1;
1941             } else
1942                 curr->selected = TRUE;
1943         }
1944     }
1945 }
1946
1947 /* support menucolor in addition to caller-supplied attribute */
1948 STATIC_OVL void
1949 toggle_menu_attr(on, color, attr)
1950 boolean on;
1951 int color, attr;
1952 {
1953     if (on) {
1954         term_start_attr(attr);
1955 #ifdef TEXTCOLOR
1956         if (color != NO_COLOR)
1957             term_start_color(color);
1958 #endif
1959     } else {
1960 #ifdef TEXTCOLOR
1961         if (color != NO_COLOR)
1962             term_end_color();
1963 #endif
1964         term_end_attr(attr);
1965     }
1966
1967 #ifndef TEXTCOLOR
1968     nhUse(color);
1969 #endif
1970 }
1971
1972 STATIC_OVL void
1973 process_menu_window(window, cw)
1974 winid window;
1975 struct WinDesc *cw;
1976 {
1977     tty_menu_item *page_start, *page_end, *curr;
1978     long count;
1979     int n, attr_n, curr_page, page_lines, resp_len;
1980     boolean finished, counting, reset_count;
1981     char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ], *msave, *morestr, really_morc;
1982 #define MENU_EXPLICIT_CHOICE 0x7f /* pseudo menu manipulation char */
1983
1984     curr_page = page_lines = 0;
1985     page_start = page_end = 0;
1986     msave = cw->morestr; /* save the morestr */
1987     cw->morestr = morestr = (char *) alloc((unsigned) QBUFSZ);
1988     counting = FALSE;
1989     count = 0L;
1990     reset_count = TRUE;
1991     finished = FALSE;
1992
1993     /* collect group accelerators; for PICK_NONE, they're ignored;
1994        for PICK_ONE, only those which match exactly one entry will be
1995        accepted; for PICK_ANY, those which match any entry are okay */
1996     gacc[0] = '\0';
1997     if (cw->how != PICK_NONE) {
1998         int i, gcnt[128];
1999 #define GSELIDX(c) (c & 127) /* guard against `signed char' */
2000
2001         for (i = 0; i < SIZE(gcnt); i++)
2002             gcnt[i] = 0;
2003         for (n = 0, curr = cw->mlist; curr; curr = curr->next)
2004             if (curr->gselector && curr->gselector != curr->selector) {
2005                 ++n;
2006                 ++gcnt[GSELIDX(curr->gselector)];
2007             }
2008
2009         if (n > 0) /* at least one group accelerator found */
2010             for (rp = gacc, curr = cw->mlist; curr; curr = curr->next)
2011                 if (curr->gselector && curr->gselector != curr->selector
2012                     && !index(gacc, curr->gselector)
2013                     && (cw->how == PICK_ANY
2014                         || gcnt[GSELIDX(curr->gselector)] == 1)) {
2015                     *rp++ = curr->gselector;
2016                     *rp = '\0'; /* re-terminate for index() */
2017                 }
2018     }
2019     resp_len = 0; /* lint suppression */
2020
2021     /* loop until finished */
2022     while (!finished) {
2023         HUPSKIP();
2024         if (reset_count) {
2025             counting = FALSE;
2026             count = 0;
2027         } else
2028             reset_count = TRUE;
2029
2030         if (!page_start) {
2031             /* new page to be displayed */
2032             if (curr_page < 0 || (cw->npages > 0 && curr_page >= cw->npages))
2033                 panic("bad menu screen page #%d", curr_page);
2034
2035             /* clear screen */
2036             if (!cw->offx) { /* if not corner, do clearscreen */
2037                 if (cw->offy) {
2038                     tty_curs(window, 1, 0);
2039                     cl_eos();
2040                 } else
2041                     clear_screen();
2042             }
2043
2044             rp = resp;
2045             if (cw->npages > 0) {
2046                 /* collect accelerators */
2047                 page_start = cw->plist[curr_page];
2048                 page_end = cw->plist[curr_page + 1];
2049                 for (page_lines = 0, curr = page_start; curr != page_end;
2050                      page_lines++, curr = curr->next) {
2051                     int attr, color = NO_COLOR;
2052
2053                     if (curr->selector)
2054                         *rp++ = curr->selector;
2055
2056                     tty_curs(window, 1, page_lines);
2057                     if (cw->offx)
2058                         cl_end();
2059
2060 #if 0 /*JP*/
2061                     (void) putchar(' ');
2062 #else
2063                     (void) jputchar(' ');
2064 #endif
2065                     ++ttyDisplay->curx;
2066
2067                     if (!iflags.use_menu_color
2068                         || !get_menu_coloring(curr->str, &color, &attr))
2069                         attr = curr->attr;
2070
2071                     /* which character to start attribute highlighting;
2072                        whole line for headers and such, after the selector
2073                        character and space and selection indicator for menu
2074                        lines (including fake ones that simulate grayed-out
2075                        entries, so we don't rely on curr->identifier here) */
2076                     attr_n = 0; /* whole line */
2077                     if (curr->str[0] && curr->str[1] == ' '
2078                         && curr->str[2] && index("-+#", curr->str[2])
2079                         && curr->str[3] == ' ')
2080                         /* [0]=letter, [1]==space, [2]=[-+#], [3]=space */
2081                         attr_n = 4; /* [4:N]=entry description */
2082
2083                     /*
2084                      * Don't use xputs() because (1) under unix it calls
2085                      * tputstr() which will interpret a '*' as some kind
2086                      * of padding information and (2) it calls xputc to
2087                      * actually output the character.  We're faster doing
2088                      * this.
2089                      */
2090                     for (n = 0, cp = curr->str;
2091                          *cp &&
2092 #ifndef WIN32CON
2093                             (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
2094 #else
2095                             (int) ttyDisplay->curx < (int) ttyDisplay->cols;
2096                          ttyDisplay->curx++,
2097 #endif
2098                          cp++, n++) {
2099                         if (n == attr_n && (color != NO_COLOR
2100                                             || attr != ATR_NONE))
2101                             toggle_menu_attr(TRUE, color, attr);
2102                         if (n == 2
2103                             && curr->identifier.a_void != 0
2104                             && curr->selected) {
2105                             if (curr->count == -1L)
2106 #if 0 /*JP*/
2107                                 (void) putchar('+'); /* all selected */
2108 #else
2109                                 (void) jputchar('+'); /* all selected */
2110 #endif
2111                             else
2112 #if 0 /*JP*/
2113                                 (void) putchar('#'); /* count selected */
2114 #else
2115                                 (void) jputchar('#'); /* count selected */
2116 #endif
2117                         } else
2118 #if 0 /*JP*/
2119                             (void) putchar(*cp);
2120 #else
2121                             (void) jputchar(*cp);
2122 #endif
2123                     } /* for *cp */
2124                     if (n > attr_n && (color != NO_COLOR || attr != ATR_NONE))
2125                         toggle_menu_attr(FALSE, color, attr);
2126                 } /* if npages > 0 */
2127             } else {
2128                 page_start = 0;
2129                 page_end = 0;
2130                 page_lines = 0;
2131             }
2132             *rp = 0;
2133             /* remember how many explicit menu choices there are */
2134             resp_len = (int) strlen(resp);
2135
2136             /* corner window - clear extra lines from last page */
2137             if (cw->offx) {
2138                 for (n = page_lines + 1; n < cw->maxrow; n++) {
2139                     tty_curs(window, 1, n);
2140                     cl_end();
2141                 }
2142             }
2143
2144             /* set extra chars.. */
2145             Strcat(resp, default_menu_cmds);
2146             Strcat(resp, " ");                  /* next page or end */
2147             Strcat(resp, "0123456789\033\n\r"); /* counts, quit */
2148             Strcat(resp, gacc);                 /* group accelerators */
2149             Strcat(resp, mapped_menu_cmds);
2150
2151             if (cw->npages > 1)
2152 #if 0 /*JP*/
2153                 Sprintf(cw->morestr, "(%d of %d)", curr_page + 1,
2154                         (int) cw->npages);
2155 #else
2156                 Sprintf(cw->morestr, "(%d/%d)", curr_page + 1,
2157                         (int) cw->npages);
2158 #endif
2159             else if (msave)
2160                 Strcpy(cw->morestr, msave);
2161             else
2162                 Strcpy(cw->morestr, defmorestr);
2163
2164             tty_curs(window, 1, page_lines);
2165             cl_end();
2166             dmore(cw, resp);
2167         } else {
2168             /* just put the cursor back... */
2169             tty_curs(window, (int) strlen(cw->morestr) + 2, page_lines);
2170             xwaitforspace(resp);
2171         }
2172
2173         really_morc = morc; /* (only used with MENU_EXPLICIT_CHOICE */
2174         if ((rp = index(resp, morc)) != 0 && rp < resp + resp_len)
2175             /* explicit menu selection; don't override it if it also
2176                happens to match a mapped menu command (such as ':' to
2177                look inside a container vs ':' to search) */
2178             morc = MENU_EXPLICIT_CHOICE;
2179         else
2180             morc = map_menu_cmd(morc);
2181
2182         switch (morc) {
2183         case '0':
2184             /* special case: '0' is also the default ball class */
2185             if (!counting && index(gacc, morc))
2186                 goto group_accel;
2187             /* fall through to count the zero */
2188             /*FALLTHRU*/
2189         case '1':
2190         case '2':
2191         case '3':
2192         case '4':
2193         case '5':
2194         case '6':
2195         case '7':
2196         case '8':
2197         case '9':
2198             count = (count * 10L) + (long) (morc - '0');
2199             /*
2200              * It is debatable whether we should allow 0 to
2201              * start a count.  There is no difference if the
2202              * item is selected.  If not selected, then
2203              * "0b" could mean:
2204              *
2205              *  count starting zero:    "zero b's"
2206              *  ignore starting zero:   "select b"
2207              *
2208              * At present I don't know which is better.
2209              */
2210             if (count != 0L) { /* ignore leading zeros */
2211                 counting = TRUE;
2212                 reset_count = FALSE;
2213             }
2214             break;
2215         case '\033': /* cancel - from counting or loop */
2216             if (!counting) {
2217                 /* deselect everything */
2218                 for (curr = cw->mlist; curr; curr = curr->next) {
2219                     curr->selected = FALSE;
2220                     curr->count = -1L;
2221                 }
2222                 cw->flags |= WIN_CANCELLED;
2223                 finished = TRUE;
2224             }
2225             /* else only stop count */
2226             break;
2227         case '\0': /* finished (commit) */
2228         case '\n':
2229         case '\r':
2230             /* only finished if we are actually picking something */
2231             if (cw->how != PICK_NONE) {
2232                 finished = TRUE;
2233                 break;
2234             }
2235         /* else fall through */
2236         case ' ':
2237         case MENU_NEXT_PAGE:
2238             if (cw->npages > 0 && curr_page != cw->npages - 1) {
2239                 curr_page++;
2240                 page_start = 0;
2241             } else if (morc == ' ') {
2242                 /* ' ' finishes menus here, but stop '>' doing the same. */
2243                 finished = TRUE;
2244             }
2245             break;
2246         case MENU_PREVIOUS_PAGE:
2247             if (cw->npages > 0 && curr_page != 0) {
2248                 --curr_page;
2249                 page_start = 0;
2250             }
2251             break;
2252         case MENU_FIRST_PAGE:
2253             if (cw->npages > 0 && curr_page != 0) {
2254                 page_start = 0;
2255                 curr_page = 0;
2256             }
2257             break;
2258         case MENU_LAST_PAGE:
2259             if (cw->npages > 0 && curr_page != cw->npages - 1) {
2260                 page_start = 0;
2261                 curr_page = cw->npages - 1;
2262             }
2263             break;
2264         case MENU_SELECT_PAGE:
2265             if (cw->how == PICK_ANY)
2266                 set_all_on_page(window, page_start, page_end);
2267             break;
2268         case MENU_UNSELECT_PAGE:
2269             unset_all_on_page(window, page_start, page_end);
2270             break;
2271         case MENU_INVERT_PAGE:
2272             if (cw->how == PICK_ANY)
2273                 invert_all_on_page(window, page_start, page_end, 0);
2274             break;
2275         case MENU_SELECT_ALL:
2276             if (cw->how == PICK_ANY) {
2277                 set_all_on_page(window, page_start, page_end);
2278                 /* set the rest */
2279                 for (curr = cw->mlist; curr; curr = curr->next)
2280                     if (curr->identifier.a_void && !curr->selected)
2281                         curr->selected = TRUE;
2282             }
2283             break;
2284         case MENU_UNSELECT_ALL:
2285             unset_all_on_page(window, page_start, page_end);
2286             /* unset the rest */
2287             for (curr = cw->mlist; curr; curr = curr->next)
2288                 if (curr->identifier.a_void && curr->selected) {
2289                     curr->selected = FALSE;
2290                     curr->count = -1;
2291                 }
2292             break;
2293         case MENU_INVERT_ALL:
2294             if (cw->how == PICK_ANY)
2295                 invert_all(window, page_start, page_end, 0);
2296             break;
2297         case MENU_SEARCH:
2298             if (cw->how == PICK_NONE) {
2299                 tty_nhbell();
2300                 break;
2301             } else {
2302                 char searchbuf[BUFSZ + 2], tmpbuf[BUFSZ] = DUMMY;
2303                 boolean on_curr_page = FALSE;
2304                 int lineno = 0;
2305
2306                 tty_getlin("Search for:", tmpbuf);
2307                 if (!tmpbuf[0] || tmpbuf[0] == '\033')
2308                     break;
2309                 Sprintf(searchbuf, "*%s*", tmpbuf);
2310
2311                 for (curr = cw->mlist; curr; curr = curr->next) {
2312                     if (on_curr_page)
2313                         lineno++;
2314                     if (curr == page_start)
2315                         on_curr_page = TRUE;
2316                     else if (curr == page_end)
2317                         on_curr_page = FALSE;
2318                     if (curr->identifier.a_void
2319                         && pmatchi(searchbuf, curr->str)) {
2320                         toggle_menu_curr(window, curr, lineno, on_curr_page,
2321                                          counting, count);
2322                         if (cw->how == PICK_ONE) {
2323                             finished = TRUE;
2324                             break;
2325                         }
2326                     }
2327                 }
2328             }
2329             break;
2330         case MENU_EXPLICIT_CHOICE:
2331             morc = really_morc;
2332         /*FALLTHRU*/
2333         default:
2334             if (cw->how == PICK_NONE || !index(resp, morc)) {
2335                 /* unacceptable input received */
2336                 tty_nhbell();
2337                 break;
2338             } else if (index(gacc, morc)) {
2339  group_accel:
2340                 /* group accelerator; for the PICK_ONE case, we know that
2341                    it matches exactly one item in order to be in gacc[] */
2342                 invert_all(window, page_start, page_end, morc);
2343                 if (cw->how == PICK_ONE)
2344                     finished = TRUE;
2345                 break;
2346             }
2347             /* find, toggle, and possibly update */
2348             for (n = 0, curr = page_start; curr != page_end;
2349                  n++, curr = curr->next)
2350                 if (morc == curr->selector) {
2351                     toggle_menu_curr(window, curr, n, TRUE, counting, count);
2352                     if (cw->how == PICK_ONE)
2353                         finished = TRUE;
2354                     break; /* from `for' loop */
2355                 }
2356             break;
2357         }
2358
2359     } /* while */
2360     cw->morestr = msave;
2361     free((genericptr_t) morestr);
2362 }
2363
2364 STATIC_OVL void
2365 process_text_window(window, cw)
2366 winid window;
2367 struct WinDesc *cw;
2368 {
2369     int i, n, attr;
2370 #if 0 /*JP*/
2371     boolean linestart;
2372 #endif
2373     register char *cp;
2374
2375     for (n = 0, i = 0; i < cw->maxrow; i++) {
2376         HUPSKIP();
2377         if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) {
2378             tty_curs(window, 1, n);
2379             cl_end();
2380             dmore(cw, quitchars);
2381             if (morc == '\033') {
2382                 cw->flags |= WIN_CANCELLED;
2383                 break;
2384             }
2385             if (cw->offy) {
2386                 tty_curs(window, 1, 0);
2387                 cl_eos();
2388             } else
2389                 clear_screen();
2390             n = 0;
2391         }
2392         tty_curs(window, 1, n++);
2393 #ifdef H2344_BROKEN
2394         cl_end();
2395 #else
2396         if (cw->offx)
2397             cl_end();
2398 #endif
2399         if (cw->data[i]) {
2400             attr = cw->data[i][0] - 1;
2401             if (cw->offx) {
2402 #if 0 /*JP*/
2403                 (void) putchar(' ');
2404 #else
2405                 (void) jputchar(' ');
2406 #endif
2407                 ++ttyDisplay->curx;
2408             }
2409             term_start_attr(attr);
2410 #if 0 /*JP*/
2411             for (cp = &cw->data[i][1], linestart = TRUE;
2412 #else
2413             for (cp = &cw->data[i][1];
2414 #endif
2415 #ifndef WIN32CON
2416                  *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
2417                  cp++
2418 #else
2419                  *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
2420                  cp++, ttyDisplay->curx++
2421 #endif
2422                  ) {
2423                 /* message recall for msg_window:full/combination/reverse
2424                    might have output from '/' in it (see redotoplin()) */
2425 #if 0 /*JP*/
2426                 if (linestart && (*cp & 0x80) != 0) {
2427                     g_putch(*cp);
2428                     end_glyphout();
2429                     linestart = FALSE;
2430                 } else {
2431                     (void) putchar(*cp);
2432                 }
2433 #else
2434                 (void) jputchar(*cp);
2435 #endif
2436             }
2437             term_end_attr(attr);
2438         }
2439     }
2440     if (i == cw->maxrow) {
2441 #ifdef H2344_BROKEN
2442         if (cw->type == NHW_TEXT) {
2443             tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->cury + 1);
2444             cl_eos();
2445         }
2446 #endif
2447         tty_curs(BASE_WINDOW, (int) cw->offx + 1,
2448                  (cw->type == NHW_TEXT) ? (int) ttyDisplay->rows - 1 : n);
2449         cl_end();
2450         dmore(cw, quitchars);
2451         if (morc == '\033')
2452             cw->flags |= WIN_CANCELLED;
2453     }
2454 }
2455
2456 /*ARGSUSED*/
2457 void
2458 tty_display_nhwindow(window, blocking)
2459 winid window;
2460 boolean blocking; /* with ttys, all windows are blocking */
2461 {
2462     register struct WinDesc *cw = 0;
2463     short s_maxcol;
2464
2465     HUPSKIP();
2466     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2467         panic(winpanicstr, window);
2468     if (cw->flags & WIN_CANCELLED)
2469         return;
2470     ttyDisplay->lastwin = window;
2471     ttyDisplay->rawprint = 0;
2472
2473     print_vt_code2(AVTC_SELECT_WINDOW, window);
2474
2475     switch (cw->type) {
2476     case NHW_MESSAGE:
2477         if (ttyDisplay->toplin == 1) {
2478             more();
2479             ttyDisplay->toplin = 1; /* more resets this */
2480             tty_clear_nhwindow(window);
2481         } else
2482             ttyDisplay->toplin = 0;
2483         cw->curx = cw->cury = 0;
2484         if (!cw->active)
2485             iflags.window_inited = TRUE;
2486         break;
2487     case NHW_MAP:
2488         end_glyphout();
2489         if (blocking) {
2490             if (!ttyDisplay->toplin)
2491                 ttyDisplay->toplin = 1;
2492             tty_display_nhwindow(WIN_MESSAGE, TRUE);
2493             return;
2494         }
2495         /*FALLTHRU*/
2496     case NHW_BASE:
2497         (void) fflush(stdout);
2498         break;
2499     case NHW_TEXT:
2500         cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2501         /*FALLTHRU*/
2502     case NHW_MENU:
2503         cw->active = 1;
2504         /* cw->maxcol is a long, but its value is constrained to
2505            be <= ttyDisplay->cols, so is sure to fit within a short */
2506         s_maxcol = (short) cw->maxcol;
2507 #ifdef H2344_BROKEN
2508         cw->offx = (cw->type == NHW_TEXT)
2509                        ? 0
2510                        : min(min(82, ttyDisplay->cols / 2),
2511                              ttyDisplay->cols - s_maxcol - 1);
2512 #else
2513         /* avoid converting to uchar before calculations are finished */
2514         cw->offx = (uchar) max((int) 10,
2515                                (int) (ttyDisplay->cols - s_maxcol - 1));
2516 #endif
2517         if (cw->offx < 0)
2518             cw->offx = 0;
2519         if (cw->type == NHW_MENU)
2520             cw->offy = 0;
2521         if (ttyDisplay->toplin == 1)
2522             tty_display_nhwindow(WIN_MESSAGE, TRUE);
2523 #ifdef H2344_BROKEN
2524         if (cw->maxrow >= (int) ttyDisplay->rows
2525             || !iflags.menu_overlay)
2526 #else
2527         if (cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows
2528             || !iflags.menu_overlay)
2529 #endif
2530         {
2531             cw->offx = 0;
2532             if (cw->offy || iflags.menu_overlay) {
2533                 tty_curs(window, 1, 0);
2534                 cl_eos();
2535             } else
2536                 clear_screen();
2537             ttyDisplay->toplin = 0;
2538         } else {
2539             if (WIN_MESSAGE != WIN_ERR)
2540                 tty_clear_nhwindow(WIN_MESSAGE);
2541         }
2542
2543         if (cw->data || !cw->maxrow)
2544             process_text_window(window, cw);
2545         else
2546             process_menu_window(window, cw);
2547         break;
2548     }
2549     cw->active = 1;
2550 }
2551
2552 void
2553 tty_dismiss_nhwindow(window)
2554 winid window;
2555 {
2556     register struct WinDesc *cw = 0;
2557
2558     HUPSKIP();
2559     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2560         panic(winpanicstr, window);
2561
2562     print_vt_code2(AVTC_SELECT_WINDOW, window);
2563
2564     switch (cw->type) {
2565     case NHW_MESSAGE:
2566         if (ttyDisplay->toplin)
2567             tty_display_nhwindow(WIN_MESSAGE, TRUE);
2568         /*FALLTHRU*/
2569     case NHW_STATUS:
2570     case NHW_BASE:
2571     case NHW_MAP:
2572         /*
2573          * these should only get dismissed when the game is going away
2574          * or suspending
2575          */
2576         tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->rows - 1);
2577         cw->active = 0;
2578         break;
2579     case NHW_MENU:
2580     case NHW_TEXT:
2581         if (cw->active) {
2582             if (iflags.window_inited) {
2583                 /* otherwise dismissing the text endwin after other windows
2584                  * are dismissed tries to redraw the map and panics.  since
2585                  * the whole reason for dismissing the other windows was to
2586                  * leave the ending window on the screen, we don't want to
2587                  * erase it anyway.
2588                  */
2589                 erase_menu_or_text(window, cw, FALSE);
2590             }
2591             cw->active = 0;
2592         }
2593         break;
2594     }
2595     cw->flags = 0;
2596 }
2597
2598 void
2599 tty_destroy_nhwindow(window)
2600 winid window;
2601 {
2602     register struct WinDesc *cw = 0;
2603
2604     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2605         panic(winpanicstr, window);
2606
2607     if (cw->active)
2608         tty_dismiss_nhwindow(window);
2609     if (cw->type == NHW_MESSAGE)
2610         iflags.window_inited = 0;
2611     if (cw->type == NHW_MAP)
2612         clear_screen();
2613
2614     free_window_info(cw, TRUE);
2615     free((genericptr_t) cw);
2616     wins[window] = 0; /* available for re-use */
2617 }
2618
2619 void
2620 tty_curs(window, x, y)
2621 winid window;
2622 register int x, y; /* not xchar: perhaps xchar is unsigned and
2623                       curx-x would be unsigned as well */
2624 {
2625     struct WinDesc *cw = 0;
2626     int cx = ttyDisplay->curx;
2627     int cy = ttyDisplay->cury;
2628
2629     HUPSKIP();
2630     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2631         panic(winpanicstr, window);
2632     ttyDisplay->lastwin = window;
2633
2634     print_vt_code2(AVTC_SELECT_WINDOW, window);
2635
2636 #if defined(USE_TILES) && defined(MSDOS)
2637     adjust_cursor_flags(cw);
2638 #endif
2639     cw->curx = --x; /* column 0 is never used */
2640     cw->cury = y;
2641 #ifdef DEBUG
2642     if (x < 0 || y < 0 || y >= cw->rows || x > cw->cols) {
2643         const char *s = "[unknown type]";
2644
2645         switch (cw->type) {
2646         case NHW_MESSAGE:
2647             s = "[topl window]";
2648             break;
2649         case NHW_STATUS:
2650             s = "[status window]";
2651             break;
2652         case NHW_MAP:
2653             s = "[map window]";
2654             break;
2655         case NHW_MENU:
2656             s = "[corner window]";
2657             break;
2658         case NHW_TEXT:
2659             s = "[text window]";
2660             break;
2661         case NHW_BASE:
2662             s = "[base window]";
2663             break;
2664         }
2665         debugpline4("bad curs positioning win %d %s (%d,%d)", window, s, x, y);
2666         /* This return statement caused a functional difference between
2667            DEBUG and non-DEBUG operation, so it is being commented
2668            out. It caused tty_curs() to fail to move the cursor to the
2669            location it needed to be if the x,y range checks failed,
2670            leaving the next piece of output to be displayed at whatever
2671            random location the cursor happened to be at prior. */
2672
2673         /* return; */
2674     }
2675 #endif
2676     x += cw->offx;
2677     y += cw->offy;
2678
2679 #ifdef CLIPPING
2680     if (clipping && window == WIN_MAP) {
2681         x -= clipx;
2682         y -= clipy;
2683     }
2684 #endif
2685
2686     if (y == cy && x == cx)
2687         return;
2688
2689     if (cw->type == NHW_MAP)
2690         end_glyphout();
2691
2692 #ifndef NO_TERMS
2693     if (!nh_ND && (cx != x || x <= 3)) { /* Extremely primitive */
2694         cmov(x, y);                      /* bunker!wtm */
2695         return;
2696     }
2697 #endif
2698
2699     if ((cy -= y) < 0)
2700         cy = -cy;
2701     if ((cx -= x) < 0)
2702         cx = -cx;
2703     if (cy <= 3 && cx <= 3) {
2704         nocmov(x, y);
2705 #ifndef NO_TERMS
2706     } else if ((x <= 3 && cy <= 3) || (!nh_CM && x < cx)) {
2707 # if 0 /*JP*/
2708         (void) putchar('\r');
2709 # else
2710         (void) cputchar('\r');
2711 # endif
2712         ttyDisplay->curx = 0;
2713         nocmov(x, y);
2714     } else if (!nh_CM) {
2715         nocmov(x, y);
2716 #endif
2717     } else
2718         cmov(x, y);
2719
2720     ttyDisplay->curx = x;
2721     ttyDisplay->cury = y;
2722 }
2723
2724 STATIC_OVL void
2725 tty_putsym(window, x, y, ch)
2726 winid window;
2727 int x, y;
2728 char ch;
2729 {
2730     register struct WinDesc *cw = 0;
2731
2732     HUPSKIP();
2733     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2734         panic(winpanicstr, window);
2735
2736     print_vt_code2(AVTC_SELECT_WINDOW, window);
2737
2738     switch (cw->type) {
2739 #ifndef STATUS_HILITES
2740     case NHW_STATUS:
2741 #endif
2742     case NHW_MAP:
2743     case NHW_BASE:
2744         tty_curs(window, x, y);
2745 #if 0 /*JP*/
2746         (void) putchar(ch);
2747 #else
2748         if(cw->type!=NHW_MAP)
2749           (void) jputchar(ch);
2750         else {
2751           (void) cputchar(ch);
2752         }
2753 #endif
2754         ttyDisplay->curx++;
2755         cw->curx++;
2756         break;
2757     case NHW_MESSAGE:
2758     case NHW_MENU:
2759     case NHW_TEXT:
2760         impossible("Can't putsym to window type %d", cw->type);
2761         break;
2762     }
2763 }
2764
2765 STATIC_OVL const char *
2766 compress_str(str)
2767 const char *str;
2768 {
2769     static char cbuf[BUFSZ];
2770
2771     /* compress out consecutive spaces if line is too long;
2772        topline wrapping converts space at wrap point into newline,
2773        we reverse that here */
2774     if ((int) strlen(str) >= CO || index(str, '\n')) {
2775         const char *in_str = str;
2776         char c, *outstr = cbuf, *outend = &cbuf[sizeof cbuf - 1];
2777         boolean was_space = TRUE; /* True discards all leading spaces;
2778                                      False would retain one if present */
2779
2780         while ((c = *in_str++) != '\0' && outstr < outend) {
2781             if (c == '\n')
2782                 c = ' ';
2783             if (was_space && c == ' ')
2784                 continue;
2785             *outstr++ = c;
2786             was_space = (c == ' ');
2787         }
2788         if ((was_space && outstr > cbuf) || outstr == outend)
2789             --outstr; /* remove trailing space or make room for terminator */
2790         *outstr = '\0';
2791         str = cbuf;
2792     }
2793     return str;
2794 }
2795
2796 void
2797 tty_putstr(window, attr, str)
2798 winid window;
2799 int attr;
2800 const char *str;
2801 {
2802     register struct WinDesc *cw = 0;
2803     register char *ob;
2804     register long i, n0;
2805 #ifndef STATUS_HILITES
2806     register const char *nb;
2807     register long j;
2808 #if 1 /*JP*/
2809     int kchar2 = 0;             /* if 1, kanji 2nd byte */
2810 #endif
2811 #endif
2812
2813     HUPSKIP();
2814     /* Assume there's a real problem if the window is missing --
2815      * probably a panic message
2816      */
2817 #if 1 /*JP*/
2818     jputchar('\0');     /* RESET */
2819 #endif
2820
2821     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) {
2822         tty_raw_print(str);
2823         return;
2824     }
2825
2826     if (str == (const char *) 0
2827         || ((cw->flags & WIN_CANCELLED) && (cw->type != NHW_MESSAGE)))
2828         return;
2829     if (cw->type != NHW_MESSAGE)
2830         str = compress_str(str);
2831
2832     ttyDisplay->lastwin = window;
2833
2834     print_vt_code2(AVTC_SELECT_WINDOW, window);
2835
2836     switch (cw->type) {
2837     case NHW_MESSAGE: {
2838         int suppress_history = (attr & ATR_NOHISTORY);
2839
2840         /* in case we ever support display attributes for topline
2841            messages, clear flag mask leaving only display attr */
2842         /*attr &= ~(ATR_URGENT | ATR_NOHISTORY);*/
2843
2844         /* really do this later */
2845 #if defined(USER_SOUNDS) && defined(WIN32CON)
2846         play_sound_for_message(str);
2847 #endif
2848         if (!suppress_history) {
2849             /* normal output; add to current top line if room, else flush
2850                whatever is there to history and then write this */
2851             update_topl(str);
2852         } else {
2853             /* put anything already on top line into history */
2854             remember_topl();
2855             /* write to top line without remembering what we're writing */
2856             show_topl(str);
2857         }
2858         break;
2859     }
2860 #ifndef STATUS_HILITES
2861     case NHW_STATUS:
2862         ob = &cw->data[cw->cury][j = cw->curx];
2863         if (context.botlx)
2864             *ob = '\0';
2865         if (!cw->cury && (int) strlen(str) >= CO) {
2866             /* the characters before "St:" are unnecessary */
2867             nb = index(str, ':');
2868             if (nb && nb > str + 2)
2869                 str = nb - 2;
2870         }
2871         nb = str;
2872         for (i = cw->curx + 1, n0 = cw->cols; i < n0; i++, nb++) {
2873             if (!*nb) {
2874                 if (*ob || context.botlx) {
2875                     /* last char printed may be in middle of line */
2876                     tty_curs(WIN_STATUS, i, cw->cury);
2877                     cl_end();
2878                 }
2879                 break;
2880             }
2881 #if 0 /*JP*/
2882 #else /* this code updates all status line at any time */
2883 # if 0
2884 /* check 2-bytes character for Japanese */
2885 /* by issei 93/12/2                     */
2886             uc = *((unsigned char *)nb);
2887             if((!(uc & 0x80) && *ob != *nb) || kflg){
2888               tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2889             }
2890             else{
2891               if(*ob != *nb || *(ob+1)!= *(nb+1)){
2892                 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2893                 kflg = 1;
2894               }
2895             }
2896 # endif /* 0 */
2897 #define ismbchar(c)     (((unsigned char)(c)) & 0x80)
2898 #define KANJI2  1
2899 #define KUPDATE 2
2900             if (kchar2)                 /* kanji 2nd byte */
2901             {
2902                 if (kchar2 & KUPDATE)
2903                     tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2904                 kchar2 = 0;
2905             }
2906             else if (ismbchar(*nb))     /* kanji 1st byte */
2907             {
2908                 kchar2 = KANJI2;
2909                 /* Kanji char must be checked as 2-bytes pair. */
2910                 /* check i to prevent putting only kanji 1st byte at last. */
2911                 if ((*nb != *ob || *(nb+1) != *(ob+1)) && i < n0-1)
2912                 {
2913                     tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2914                     kchar2 |= KUPDATE;  /* must do update */
2915                 }
2916                 /* else nb is the same char as old, so need not to update */
2917             }
2918             /* not kanji char */
2919             else
2920 #endif
2921             if (*ob != *nb)
2922                 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2923             if (*ob)
2924                 ob++;
2925         }
2926
2927         (void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1);
2928         cw->data[cw->cury][cw->cols - 1] = '\0'; /* null terminate */
2929         cw->cury = (cw->cury + 1) % cw->maxrow;
2930         cw->curx = 0;
2931         break;
2932 #endif /* STATUS_HILITES */
2933     case NHW_MAP:
2934         tty_curs(window, cw->curx + 1, cw->cury);
2935         term_start_attr(attr);
2936         while (*str && (int) ttyDisplay->curx < (int) ttyDisplay->cols - 1) {
2937 #if 0 /*JP*/
2938             (void) putchar(*str);
2939 #else
2940             (void) cputchar(*str);
2941 #endif
2942             str++;
2943             ttyDisplay->curx++;
2944         }
2945         cw->curx = 0;
2946         cw->cury++;
2947         term_end_attr(attr);
2948         break;
2949     case NHW_BASE:
2950         tty_curs(window, cw->curx + 1, cw->cury);
2951         term_start_attr(attr);
2952         while (*str) {
2953             if ((int) ttyDisplay->curx >= (int) ttyDisplay->cols - 1) {
2954                 cw->curx = 0;
2955                 cw->cury++;
2956                 tty_curs(window, cw->curx + 1, cw->cury);
2957             }
2958 #if 0 /*JP*/
2959             (void) putchar(*str);
2960 #else
2961             (void) jputchar(*str);
2962 #endif
2963             str++;
2964             ttyDisplay->curx++;
2965         }
2966         cw->curx = 0;
2967         cw->cury++;
2968         term_end_attr(attr);
2969         break;
2970     case NHW_MENU:
2971     case NHW_TEXT:
2972 #ifdef H2344_BROKEN
2973         if (cw->type == NHW_TEXT
2974             && (cw->cury + cw->offy) == ttyDisplay->rows - 1)
2975 #else
2976         if (cw->type == NHW_TEXT && cw->cury == ttyDisplay->rows - 1)
2977 #endif
2978         {
2979             /* not a menu, so save memory and output 1 page at a time */
2980             cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2981             tty_display_nhwindow(window, TRUE);
2982             for (i = 0; i < cw->maxrow; i++)
2983                 if (cw->data[i]) {
2984                     free((genericptr_t) cw->data[i]);
2985                     cw->data[i] = 0;
2986                 }
2987             cw->maxrow = cw->cury = 0;
2988         }
2989         /* always grows one at a time, but alloc 12 at a time */
2990         if (cw->cury >= cw->rows) {
2991             char **tmp;
2992
2993             cw->rows += 12;
2994             tmp = (char **) alloc(sizeof(char *) * (unsigned) cw->rows);
2995             for (i = 0; i < cw->maxrow; i++)
2996                 tmp[i] = cw->data[i];
2997             if (cw->data)
2998                 free((genericptr_t) cw->data);
2999             cw->data = tmp;
3000
3001             for (i = cw->maxrow; i < cw->rows; i++)
3002                 cw->data[i] = 0;
3003         }
3004         if (cw->data[cw->cury])
3005             free((genericptr_t) cw->data[cw->cury]);
3006         n0 = (long) strlen(str) + 1L;
3007         ob = cw->data[cw->cury] = (char *) alloc((unsigned) n0 + 1);
3008         *ob++ = (char) (attr + 1); /* avoid nuls, for convenience */
3009         Strcpy(ob, str);
3010
3011         if (n0 > cw->maxcol)
3012             cw->maxcol = n0;
3013         if (++cw->cury > cw->maxrow)
3014             cw->maxrow = cw->cury;
3015         if (n0 > CO) {
3016             /* attempt to break the line */
3017             for (i = CO - 1; i && str[i] != ' ' && str[i] != '\n';)
3018                 i--;
3019             if (i) {
3020                 cw->data[cw->cury - 1][++i] = '\0';
3021                 tty_putstr(window, attr, &str[i]);
3022             }
3023         }
3024         break;
3025     }
3026 #if 1 /*JP*/
3027     jputchar('\0');     /* RESET */
3028 #endif
3029 }
3030
3031 void
3032 tty_display_file(fname, complain)
3033 const char *fname;
3034 boolean complain;
3035 {
3036 #ifdef DEF_PAGER /* this implies that UNIX is defined */
3037     {
3038         /* use external pager; this may give security problems */
3039         register int fd = open(fname, 0);
3040
3041         if (fd < 0) {
3042             if (complain)
3043                 pline("Cannot open %s.", fname);
3044             else
3045                 docrt();
3046             return;
3047         }
3048         if (child(1)) {
3049             /* Now that child() does a setuid(getuid()) and a chdir(),
3050                we may not be able to open file fname anymore, so make
3051                it stdin. */
3052             (void) close(0);
3053             if (dup(fd)) {
3054                 if (complain)
3055                     raw_printf("Cannot open %s as stdin.", fname);
3056             } else {
3057                 (void) execlp(catmore, "page", (char *) 0);
3058                 if (complain)
3059                     raw_printf("Cannot exec %s.", catmore);
3060             }
3061             if (complain)
3062                 sleep(10); /* want to wait_synch() but stdin is gone */
3063             nh_terminate(EXIT_FAILURE);
3064         }
3065         (void) close(fd);
3066 #ifdef notyet
3067         winch_seen = 0;
3068 #endif
3069     }
3070 #else /* DEF_PAGER */
3071     {
3072         dlb *f;
3073         char buf[BUFSZ];
3074         char *cr;
3075
3076         tty_clear_nhwindow(WIN_MESSAGE);
3077         f = dlb_fopen(fname, "r");
3078         if (!f) {
3079             if (complain) {
3080                 home();
3081                 tty_mark_synch();
3082                 tty_raw_print("");
3083                 perror(fname);
3084                 tty_wait_synch();
3085                 pline("Cannot open \"%s\".", fname);
3086             } else if (u.ux)
3087                 docrt();
3088         } else {
3089             winid datawin = tty_create_nhwindow(NHW_TEXT);
3090             boolean empty = TRUE;
3091
3092             if (complain
3093 #ifndef NO_TERMS
3094                 && nh_CD
3095 #endif
3096                 ) {
3097                 /* attempt to scroll text below map window if there's room */
3098                 wins[datawin]->offy = wins[WIN_STATUS]->offy + 3;
3099                 if ((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows)
3100                     wins[datawin]->offy = 0;
3101             }
3102             while (dlb_fgets(buf, BUFSZ, f)) {
3103                 if ((cr = index(buf, '\n')) != 0)
3104                     *cr = 0;
3105 #ifdef MSDOS
3106                 if ((cr = index(buf, '\r')) != 0)
3107                     *cr = 0;
3108 #endif
3109                 if (index(buf, '\t') != 0)
3110                     (void) tabexpand(buf);
3111                 empty = FALSE;
3112                 tty_putstr(datawin, 0, buf);
3113                 if (wins[datawin]->flags & WIN_CANCELLED)
3114                     break;
3115             }
3116             if (!empty)
3117                 tty_display_nhwindow(datawin, FALSE);
3118             tty_destroy_nhwindow(datawin);
3119             (void) dlb_fclose(f);
3120         }
3121     }
3122 #endif /* DEF_PAGER */
3123 }
3124
3125 void
3126 tty_start_menu(window)
3127 winid window;
3128 {
3129     tty_clear_nhwindow(window);
3130     return;
3131 }
3132
3133 /*ARGSUSED*/
3134 /*
3135  * Add a menu item to the beginning of the menu list.  This list is reversed
3136  * later.
3137  */
3138 void
3139 tty_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
3140 winid window;               /* window to use, must be of type NHW_MENU */
3141 int glyph UNUSED;           /* glyph to display with item (not used) */
3142 const anything *identifier; /* what to return if selected */
3143 char ch;                    /* keyboard accelerator (0 = pick our own) */
3144 char gch;                   /* group accelerator (0 = no group) */
3145 int attr;                   /* attribute for string (like tty_putstr()) */
3146 const char *str;            /* menu string */
3147 boolean preselected;        /* item is marked as selected */
3148 {
3149     register struct WinDesc *cw = 0;
3150     tty_menu_item *item;
3151     const char *newstr;
3152     char buf[4 + BUFSZ];
3153
3154     HUPSKIP();
3155     if (str == (const char *) 0)
3156         return;
3157
3158     if (window == WIN_ERR
3159         || (cw = wins[window]) == (struct WinDesc *) 0
3160         || cw->type != NHW_MENU)
3161         panic(winpanicstr, window);
3162
3163     cw->nitems++;
3164     if (identifier->a_void) {
3165         int len = (int) strlen(str);
3166
3167         if (len >= BUFSZ) {
3168             /* We *think* everything's coming in off at most BUFSZ bufs... */
3169             impossible("Menu item too long (%d).", len);
3170             len = BUFSZ - 1;
3171         }
3172         Sprintf(buf, "%c - ", ch ? ch : '?');
3173         (void) strncpy(buf + 4, str, len);
3174         buf[4 + len] = '\0';
3175         newstr = buf;
3176     } else
3177         newstr = str;
3178
3179     item = (tty_menu_item *) alloc(sizeof *item);
3180     item->identifier = *identifier;
3181     item->count = -1L;
3182     item->selected = preselected;
3183     item->selector = ch;
3184     item->gselector = gch;
3185     item->attr = attr;
3186     item->str = dupstr(newstr ? newstr : "");
3187
3188     item->next = cw->mlist;
3189     cw->mlist = item;
3190 }
3191
3192 /* Invert the given list, can handle NULL as an input. */
3193 STATIC_OVL tty_menu_item *
3194 reverse(curr)
3195 tty_menu_item *curr;
3196 {
3197     tty_menu_item *next, *head = 0;
3198
3199     while (curr) {
3200         next = curr->next;
3201         curr->next = head;
3202         head = curr;
3203         curr = next;
3204     }
3205     return head;
3206 }
3207
3208 /*
3209  * End a menu in this window, window must a type NHW_MENU.  This routine
3210  * processes the string list.  We calculate the # of pages, then assign
3211  * keyboard accelerators as needed.  Finally we decide on the width and
3212  * height of the window.
3213  */
3214 void
3215 tty_end_menu(window, prompt)
3216 winid window;       /* menu to use */
3217 const char *prompt; /* prompt to for menu */
3218 {
3219     struct WinDesc *cw = 0;
3220     tty_menu_item *curr;
3221     short len;
3222     int lmax, n;
3223     char menu_ch;
3224
3225     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
3226         || cw->type != NHW_MENU)
3227         panic(winpanicstr, window);
3228
3229     /* Reverse the list so that items are in correct order. */
3230     cw->mlist = reverse(cw->mlist);
3231
3232     /* Put the prompt at the beginning of the menu. */
3233     if (prompt) {
3234         anything any;
3235
3236         any = zeroany; /* not selectable */
3237         tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
3238                      MENU_UNSELECTED);
3239         tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt,
3240                      MENU_UNSELECTED);
3241     }
3242
3243     /* 52: 'a'..'z' and 'A'..'Z'; avoids selector duplication within a page */
3244     lmax = min(52, (int) ttyDisplay->rows - 1);    /* # lines per page */
3245     cw->npages = (cw->nitems + (lmax - 1)) / lmax; /* # of pages */
3246     /*
3247      * TODO?
3248      *  For really tall page, allow 53 if '$' or '#' is present and
3249      *  54 if both are.  [Only for single page menu, otherwise pages
3250      *  without those could try to use too many letters.]
3251      *  Probably not worth bothering with; anyone with a display big
3252      *  for this to matter will likely switch from tty to curses for
3253      *  multi-line message window and/or persistent inventory window.
3254      */
3255
3256     /* make sure page list is large enough */
3257     if (cw->plist_size < cw->npages + 1) { /* +1: need one slot beyond last */
3258         if (cw->plist)
3259             free((genericptr_t) cw->plist);
3260         cw->plist_size = cw->npages + 1;
3261         cw->plist = (tty_menu_item **) alloc(cw->plist_size
3262                                              * sizeof (tty_menu_item *));
3263     }
3264
3265     cw->cols = 0;  /* cols is set when the win is initialized... (why?) */
3266     menu_ch = '?'; /* lint suppression */
3267     for (n = 0, curr = cw->mlist; curr; n++, curr = curr->next) {
3268         /* set page boundaries and character accelerators */
3269         if ((n % lmax) == 0) {
3270             menu_ch = 'a';
3271             cw->plist[n / lmax] = curr;
3272         }
3273         if (curr->identifier.a_void && !curr->selector) {
3274             curr->str[0] = curr->selector = menu_ch;
3275             if (menu_ch++ == 'z')
3276                 menu_ch = 'A';
3277         }
3278
3279         /* cut off any lines that are too long */
3280         len = strlen(curr->str) + 2; /* extra space at beg & end */
3281         if (len > (int) ttyDisplay->cols) {
3282             curr->str[ttyDisplay->cols - 2] = 0;
3283             len = ttyDisplay->cols;
3284         }
3285         if (len > cw->cols)
3286             cw->cols = len;
3287     }
3288     cw->plist[cw->npages] = 0; /* plist terminator */
3289
3290     /*
3291      * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
3292      */
3293     if (cw->npages > 1) {
3294         char buf[QBUFSZ];
3295         /* produce the largest demo string */
3296         Sprintf(buf, "(%ld of %ld) ", cw->npages, cw->npages);
3297         len = strlen(buf);
3298         cw->morestr = dupstr("");
3299     } else {
3300         cw->morestr = dupstr("(end) ");
3301         len = strlen(cw->morestr);
3302     }
3303
3304     if (len > (int) ttyDisplay->cols) {
3305         /* truncate the prompt if it's too long for the screen */
3306         if (cw->npages <= 1) /* only str in single page case */
3307             cw->morestr[ttyDisplay->cols] = 0;
3308         len = ttyDisplay->cols;
3309     }
3310     if (len > cw->cols)
3311         cw->cols = len;
3312
3313     cw->maxcol = cw->cols;
3314
3315     /*
3316      * The number of lines in the first page plus the morestr will be the
3317      * maximum size of the window.
3318      */
3319     if (cw->npages > 1)
3320         cw->maxrow = cw->rows = lmax + 1;
3321     else
3322         cw->maxrow = cw->rows = cw->nitems + 1;
3323 }
3324
3325 int
3326 tty_select_menu(window, how, menu_list)
3327 winid window;
3328 int how;
3329 menu_item **menu_list;
3330 {
3331     register struct WinDesc *cw = 0;
3332     tty_menu_item *curr;
3333     menu_item *mi;
3334     int n, cancelled;
3335
3336     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
3337         || cw->type != NHW_MENU)
3338         panic(winpanicstr, window);
3339
3340     *menu_list = (menu_item *) 0;
3341     cw->how = (short) how;
3342     morc = 0;
3343     tty_display_nhwindow(window, TRUE);
3344     cancelled = !!(cw->flags & WIN_CANCELLED);
3345     tty_dismiss_nhwindow(window); /* does not destroy window data */
3346
3347     if (cancelled) {
3348         n = -1;
3349     } else {
3350         for (n = 0, curr = cw->mlist; curr; curr = curr->next)
3351             if (curr->selected)
3352                 n++;
3353     }
3354
3355     if (n > 0) {
3356         *menu_list = (menu_item *) alloc(n * sizeof(menu_item));
3357         for (mi = *menu_list, curr = cw->mlist; curr; curr = curr->next)
3358             if (curr->selected) {
3359                 mi->item = curr->identifier;
3360                 mi->count = curr->count;
3361                 mi++;
3362             }
3363     }
3364
3365     return n;
3366 }
3367
3368 /* special hack for treating top line --More-- as a one item menu */
3369 char
3370 tty_message_menu(let, how, mesg)
3371 char let;
3372 int how;
3373 const char *mesg;
3374 {
3375     HUPSKIP();
3376     /* "menu" without selection; use ordinary pline, no more() */
3377     if (how == PICK_NONE) {
3378         pline("%s", mesg);
3379         return 0;
3380     }
3381
3382     ttyDisplay->dismiss_more = let;
3383     morc = 0;
3384     /* barebones pline(); since we're only supposed to be called after
3385        response to a prompt, we'll assume that the display is up to date */
3386     tty_putstr(WIN_MESSAGE, 0, mesg);
3387     /* if `mesg' didn't wrap (triggering --More--), force --More-- now */
3388     if (ttyDisplay->toplin == 1) {
3389         more();
3390         ttyDisplay->toplin = 1; /* more resets this */
3391         tty_clear_nhwindow(WIN_MESSAGE);
3392     }
3393     /* normally <ESC> means skip further messages, but in this case
3394        it means cancel the current prompt; any other messages should
3395        continue to be output normally */
3396     wins[WIN_MESSAGE]->flags &= ~WIN_CANCELLED;
3397     ttyDisplay->dismiss_more = 0;
3398
3399     return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0';
3400 }
3401
3402 void
3403 tty_update_inventory()
3404 {
3405     return;
3406 }
3407
3408 void
3409 tty_mark_synch()
3410 {
3411     HUPSKIP();
3412     (void) fflush(stdout);
3413 }
3414
3415 void
3416 tty_wait_synch()
3417 {
3418     HUPSKIP();
3419     /* we just need to make sure all windows are synch'd */
3420     if (!ttyDisplay || ttyDisplay->rawprint) {
3421         getret();
3422         if (ttyDisplay)
3423             ttyDisplay->rawprint = 0;
3424     } else {
3425         tty_display_nhwindow(WIN_MAP, FALSE);
3426         if (ttyDisplay->inmore) {
3427             addtopl("--More--");
3428             (void) fflush(stdout);
3429         } else if (ttyDisplay->inread > program_state.gameover) {
3430             /* this can only happen if we were reading and got interrupted */
3431             ttyDisplay->toplin = 3;
3432             /* do this twice; 1st time gets the Quit? message again */
3433             (void) tty_doprev_message();
3434             (void) tty_doprev_message();
3435             ttyDisplay->intr++;
3436             (void) fflush(stdout);
3437         }
3438     }
3439 }
3440
3441 void
3442 docorner(xmin, ymax)
3443 register int xmin, ymax;
3444 {
3445     register int y;
3446     register struct WinDesc *cw = wins[WIN_MAP];
3447
3448     HUPSKIP();
3449 #if 0   /* this optimization is not valuable enough to justify
3450            abusing core internals... */
3451     if (u.uswallow) { /* Can be done more efficiently */
3452         swallowed(1);
3453         /* without this flush, if we happen to follow --More-- displayed in
3454            leftmost column, the cursor gets left in the wrong place after
3455            <docorner<more<update_topl<tty_putstr calls unwind back to core */
3456         flush_screen(0);
3457         return;
3458     }
3459 #endif /*0*/
3460
3461 #if defined(SIGWINCH) && defined(CLIPPING)
3462     if (ymax > LI)
3463         ymax = LI; /* can happen if window gets smaller */
3464 #endif
3465     for (y = 0; y < ymax; y++) {
3466         tty_curs(BASE_WINDOW, xmin, y); /* move cursor */
3467         cl_end();                       /* clear to end of line */
3468 #ifdef CLIPPING
3469         if (y < (int) cw->offy || y + clipy > ROWNO)
3470             continue; /* only refresh board */
3471 #if defined(USE_TILES) && defined(MSDOS)
3472         if (iflags.tile_view)
3473             row_refresh((xmin / 2) + clipx - ((int) cw->offx / 2), COLNO - 1,
3474                         y + clipy - (int) cw->offy);
3475         else
3476 #endif
3477             row_refresh(xmin + clipx - (int) cw->offx, COLNO - 1,
3478                         y + clipy - (int) cw->offy);
3479 #else
3480         if (y < cw->offy || y > ROWNO)
3481             continue; /* only refresh board  */
3482         row_refresh(xmin - (int) cw->offx, COLNO - 1, y - (int) cw->offy);
3483 #endif
3484     }
3485
3486     end_glyphout();
3487     if (ymax >= (int) wins[WIN_STATUS]->offy) {
3488         /* we have wrecked the bottom line */
3489         context.botlx = 1;
3490         bot();
3491     }
3492 }
3493
3494 void
3495 end_glyphout()
3496 {
3497     HUPSKIP();
3498 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3499     if (GFlag) {
3500         GFlag = FALSE;
3501         graph_off();
3502     }
3503 #endif
3504 #ifdef TEXTCOLOR
3505     if (ttyDisplay->color != NO_COLOR) {
3506         term_end_color();
3507         ttyDisplay->color = NO_COLOR;
3508     }
3509 #endif
3510 }
3511
3512 #ifndef WIN32
3513 void
3514 g_putch(in_ch)
3515 int in_ch;
3516 {
3517     register char ch = (char) in_ch;
3518
3519     HUPSKIP();
3520 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3521     if (SYMHANDLING(H_IBM)
3522         /* for DECgraphics, lower-case letters with high bit set mean
3523            switch character set and render with high bit clear;
3524            user might want 8-bits for other characters */
3525         || (iflags.eight_bit_tty && (!SYMHANDLING(H_DEC)
3526                                      || (in_ch & 0x7f) < 0x60))) {
3527         /* IBM-compatible displays don't need other stuff */
3528 #   if 0 /*JP*/
3529         (void) putchar(ch);
3530 #   else
3531         (void) cputchar(ch);
3532 #   endif
3533     } else if (ch & 0x80) {
3534         if (!GFlag || HE_resets_AS) {
3535             graph_on();
3536             GFlag = TRUE;
3537         }
3538 # if 0 /*JP*/
3539         (void) putchar((ch ^ 0x80)); /* Strip 8th bit */
3540 # else
3541         (void) cputchar((ch ^ 0x80)); /* Strip 8th bit */
3542 # endif
3543     } else {
3544         if (GFlag) {
3545             graph_off();
3546             GFlag = FALSE;
3547         }
3548 # if 0 /*JP*/
3549         (void) putchar(ch);
3550 # else
3551         (void) jputchar(ch);
3552 # endif
3553     }
3554
3555 #else
3556     (void) putchar(ch);
3557
3558 #endif /* ASCIIGRAPH && !NO_TERMS */
3559
3560     return;
3561 }
3562 #endif /* !WIN32 */
3563
3564 #ifdef CLIPPING
3565 void
3566 setclipped()
3567 {
3568     clipping = TRUE;
3569     clipx = clipy = 0;
3570     clipxmax = CO;
3571     clipymax = LI - 1 - iflags.wc2_statuslines;
3572 }
3573
3574 void
3575 tty_cliparound(x, y)
3576 int x, y;
3577 {
3578     int oldx = clipx, oldy = clipy;
3579
3580     HUPSKIP();
3581     if (!clipping)
3582         return;
3583     if (x < clipx + 5) {
3584         clipx = max(0, x - 20);
3585         clipxmax = clipx + CO;
3586     } else if (x > clipxmax - 5) {
3587         clipxmax = min(COLNO, clipxmax + 20);
3588         clipx = clipxmax - CO;
3589     }
3590     if (y < clipy + 2) {
3591         clipy = max(0, y - (clipymax - clipy) / 2);
3592         clipymax = clipy + (LI - 1 - iflags.wc2_statuslines);
3593     } else if (y > clipymax - 2) {
3594         clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2);
3595         clipy = clipymax - (LI - 1 - iflags.wc2_statuslines);
3596     }
3597     if (clipx != oldx || clipy != oldy) {
3598         redraw_map(); /* ask the core to resend the map window's data */
3599     }
3600 }
3601 #endif /* CLIPPING */
3602
3603 /*
3604  *  tty_print_glyph
3605  *
3606  *  Print the glyph to the output device.  Don't flush the output device.
3607  *
3608  *  Since this is only called from show_glyph(), it is assumed that the
3609  *  position and glyph are always correct (checked there)!
3610  */
3611
3612 void
3613 tty_print_glyph(window, x, y, glyph, bkglyph)
3614 winid window;
3615 xchar x, y;
3616 int glyph;
3617 int bkglyph UNUSED;
3618 {
3619     int ch;
3620     boolean reverse_on = FALSE;
3621     int color;
3622     unsigned special;
3623
3624     HUPSKIP();
3625 #ifdef CLIPPING
3626     if (clipping) {
3627         if (x <= clipx || y < clipy || x >= clipxmax || y >= clipymax)
3628             return;
3629     }
3630 #endif
3631     /* map glyph to character and color */
3632     (void) mapglyph(glyph, &ch, &color, &special, x, y, 0);
3633
3634     print_vt_code2(AVTC_SELECT_WINDOW, window);
3635
3636     /* Move the cursor. */
3637     tty_curs(window, x, y);
3638
3639     print_vt_code3(AVTC_GLYPH_START, glyph2tile[glyph], special);
3640
3641 #ifndef NO_TERMS
3642     if (ul_hack && ch == '_') { /* non-destructive underscore */
3643 # if 0 /*JP*/
3644         (void) putchar((char) ' ');
3645 # else
3646         (void) cputchar((char) ' ');
3647 # endif
3648         backsp();
3649     }
3650 #endif
3651
3652 #ifdef TEXTCOLOR
3653     if (color != ttyDisplay->color) {
3654         if (ttyDisplay->color != NO_COLOR)
3655             term_end_color();
3656         ttyDisplay->color = color;
3657         if (color != NO_COLOR)
3658             term_start_color(color);
3659     }
3660 #endif /* TEXTCOLOR */
3661
3662     /* must be after color check; term_end_color may turn off inverse too */
3663     if (((special & MG_PET) && iflags.hilite_pet)
3664         || ((special & MG_OBJPILE) && iflags.hilite_pile)
3665         || ((special & MG_DETECT) && iflags.use_inverse)
3666         || ((special & MG_BW_LAVA) && iflags.use_inverse)) {
3667         term_start_attr(ATR_INVERSE);
3668         reverse_on = TRUE;
3669     }
3670
3671 #if defined(USE_TILES) && defined(MSDOS)
3672     if (iflags.grmode && iflags.tile_view)
3673         xputg(glyph, ch, special);
3674     else
3675 #endif
3676         g_putch(ch); /* print the character */
3677
3678     if (reverse_on) {
3679         term_end_attr(ATR_INVERSE);
3680 #ifdef TEXTCOLOR
3681         /* turn off color as well, ATR_INVERSE may have done this already */
3682         if (ttyDisplay->color != NO_COLOR) {
3683             term_end_color();
3684             ttyDisplay->color = NO_COLOR;
3685         }
3686 #endif
3687     }
3688
3689     print_vt_code1(AVTC_GLYPH_END);
3690
3691     wins[window]->curx++; /* one character over */
3692     ttyDisplay->curx++;   /* the real cursor moved too */
3693 }
3694
3695 void
3696 tty_raw_print(str)
3697 const char *str;
3698 {
3699     HUPSKIP();
3700     if (ttyDisplay)
3701         ttyDisplay->rawprint++;
3702     print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3703 #if defined(MICRO) || defined(WIN32CON)
3704     msmsg("%s\n", str);
3705 #else
3706 # if 0 /*JP*/
3707     puts(str);
3708 # else
3709     jputs(str);
3710 # endif
3711     (void) fflush(stdout);
3712 #endif
3713 }
3714
3715 void
3716 tty_raw_print_bold(str)
3717 const char *str;
3718 {
3719     HUPSKIP();
3720     if (ttyDisplay)
3721         ttyDisplay->rawprint++;
3722     print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3723     term_start_raw_bold();
3724 #if defined(MICRO) || defined(WIN32CON)
3725     msmsg("%s", str);
3726 #else
3727 # if 0 /*JP*/
3728     (void) fputs(str, stdout);
3729 # else
3730     (void) jputs(str);
3731 # endif
3732 #endif
3733     term_end_raw_bold();
3734 #if defined(MICRO) || defined(WIN32CON)
3735     msmsg("\n");
3736 #else
3737 # if 0 /*JP*/
3738     puts("");
3739 # endif
3740     (void) fflush(stdout);
3741 #endif
3742 }
3743
3744 int
3745 tty_nhgetch()
3746 {
3747     int i;
3748 #ifdef UNIX
3749     /* kludge alert: Some Unix variants return funny values if getc()
3750      * is called, interrupted, and then called again.  There
3751      * is non-reentrant code in the internal _filbuf() routine, called by
3752      * getc().
3753      */
3754     static volatile int nesting = 0;
3755     char nestbuf;
3756 #endif
3757
3758     HUPSKIP_RESULT('\033');
3759     print_vt_code1(AVTC_INLINE_SYNC);
3760     (void) fflush(stdout);
3761     /* Note: if raw_print() and wait_synch() get called to report terminal
3762      * initialization problems, then wins[] and ttyDisplay might not be
3763      * available yet.  Such problems will probably be fatal before we get
3764      * here, but validate those pointers just in case...
3765      */
3766     if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3767         wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3768     if (iflags.debug_fuzzer) {
3769         i = randomkey();
3770     } else {
3771 #ifdef UNIX
3772         i = (++nesting == 1)
3773               ? tgetch()
3774               : (read(fileno(stdin), (genericptr_t) &nestbuf, 1) == 1)
3775                   ? (int) nestbuf : EOF;
3776         --nesting;
3777 #else
3778         i = tgetch();
3779 #endif
3780     }
3781     if (!i)
3782         i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
3783     else if (i == EOF)
3784         i = '\033'; /* same for EOF */
3785     if (ttyDisplay && ttyDisplay->toplin == 1)
3786         ttyDisplay->toplin = 2;
3787 #ifdef TTY_TILES_ESCCODES
3788     {
3789         /* hack to force output of the window select code */
3790         int tmp = vt_tile_current_window;
3791
3792         vt_tile_current_window++;
3793         print_vt_code2(AVTC_SELECT_WINDOW, tmp);
3794     }
3795 #endif /* TTY_TILES_ESCCODES */
3796     return i;
3797 }
3798
3799 /*
3800  * return a key, or 0, in which case a mouse button was pressed
3801  * mouse events should be returned as character postitions in the map window.
3802  * Since normal tty's don't have mice, just return a key.
3803  */
3804 /*ARGSUSED*/
3805 int
3806 tty_nh_poskey(x, y, mod)
3807 int *x, *y, *mod;
3808 {
3809     int i;
3810
3811     HUPSKIP_RESULT('\033');
3812 #if defined(WIN32CON)
3813     (void) fflush(stdout);
3814     /* Note: if raw_print() and wait_synch() get called to report terminal
3815      * initialization problems, then wins[] and ttyDisplay might not be
3816      * available yet.  Such problems will probably be fatal before we get
3817      * here, but validate those pointers just in case...
3818      */
3819     if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3820         wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3821     i = ntposkey(x, y, mod);
3822     if (!i && mod && (*mod == 0 || *mod == EOF))
3823         i = '\033'; /* map NUL or EOF to ESC, nethack doesn't expect either */
3824     if (ttyDisplay && ttyDisplay->toplin == 1)
3825         ttyDisplay->toplin = 2;
3826 #else /* !WIN32CON */
3827     nhUse(x);
3828     nhUse(y);
3829     nhUse(mod);
3830
3831     i = tty_nhgetch();
3832 #endif /* ?WIN32CON */
3833     return i;
3834 }
3835
3836 void
3837 win_tty_init(dir)
3838 int dir;
3839 {
3840     if (dir != WININIT)
3841         return;
3842     return;
3843 }
3844
3845 #ifdef POSITIONBAR
3846 void
3847 tty_update_positionbar(posbar)
3848 char *posbar;
3849 {
3850 #ifdef MSDOS
3851     video_update_positionbar(posbar);
3852 #endif
3853 }
3854 #endif /* POSITIONBAR */
3855
3856
3857 /*
3858  * +------------------+
3859  * |  STATUS CODE     |
3860  * +------------------+
3861  */
3862
3863 /*
3864  * ----------------------------------------------------------------
3865  * tty_status_update
3866  *
3867  *      Called from the NetHack core and receives info hand-offs
3868  *      from the core, processes them, storing some information
3869  *      for rendering to the tty.
3870  *          -> make_things_fit()
3871  *          -> render_status()
3872  *
3873  * make_things_fit
3874  *
3875  *      Called from tty_status_update(). It calls check_fields()
3876  *      (described next) to get the required number of columns
3877  *      and tries a few different ways to squish a representation
3878  *      of the status window values onto the 80 column tty display.
3879  *          ->check_fields()
3880  *          ->set_condition_length()  - update the width of conditions
3881  *          ->shrink_enc()      - shrink encumbrance message word
3882  *          ->shrink_dlvl()     - reduce the width of Dlvl:42
3883  *
3884  *  check_fields
3885  *
3886  *      Verifies that all the fields are ready for display, and
3887  *      returns FALSE if called too early in the startup
3888  *      processing sequence. It also figures out where everything
3889  *      needs to go, taking the current shrinking attempts into
3890  *      account. It returns number of columns needed back to
3891  *      make_things_fit(), so make_things_fit() can make attempt
3892  *      to make adjustments.
3893  *
3894  *  render_status
3895  *
3896  *      Goes through each of the status row's fields and
3897  *      calls tty_putstatusfield() to place them on the display.
3898  *          ->tty_putstatusfield()
3899  *      At the end of the for-loop, the NOW values get copied
3900  *      to BEFORE values.
3901  *
3902  *  tty_putstatusfield()
3903  *
3904  *      Move the cursor to the target spot, and output the field
3905  *      asked for by render_status().
3906  *
3907  * ----------------------------------------------------------------
3908  */
3909
3910 /*
3911  * The following data structures come from the genl_ routines in
3912  * src/windows.c and as such are considered to be on the window-port
3913  * "side" of things, rather than the NetHack-core "side" of things.
3914  */
3915 extern const char *status_fieldfmt[MAXBLSTATS];
3916 extern char *status_vals[MAXBLSTATS];
3917 extern boolean status_activefields[MAXBLSTATS];
3918 extern winid WIN_STATUS;
3919
3920 #ifdef STATUS_HILITES
3921 #ifdef TEXTCOLOR
3922 STATIC_DCL int FDECL(condcolor, (long, unsigned long *));
3923 #endif
3924 STATIC_DCL int FDECL(condattr, (long, unsigned long *));
3925 static unsigned long *tty_colormasks;
3926 static long tty_condition_bits;
3927 static struct tty_status_fields tty_status[2][MAXBLSTATS]; /* 2: NOW,BEFORE */
3928 static int hpbar_percent, hpbar_color;
3929 static struct condition_t {
3930     long mask;
3931     const char *text[3]; /* 3: potential display vals, progressively shorter */
3932 } conditions[] = {
3933     /* The sequence order of these matters */
3934     { BL_MASK_STONE,    { "Stone",    "Ston",  "Sto" } },
3935     { BL_MASK_SLIME,    { "Slime",    "Slim",  "Slm" } },
3936     { BL_MASK_STRNGL,   { "Strngl",   "Stngl", "Str" } },
3937     { BL_MASK_FOODPOIS, { "FoodPois", "Fpois", "Poi" } },
3938     { BL_MASK_TERMILL,  { "TermIll" , "Ill",   "Ill" } },
3939     { BL_MASK_BLIND,    { "Blind",    "Blnd",  "Bl"  } },
3940     { BL_MASK_DEAF,     { "Deaf",     "Def",   "Df"  } },
3941     { BL_MASK_STUN,     { "Stun",     "Stun",  "St"  } },
3942     { BL_MASK_CONF,     { "Conf",     "Cnf",   "Cf"  } },
3943     { BL_MASK_HALLU,    { "Hallu",    "Hal",   "Hl"  } },
3944     { BL_MASK_LEV,      { "Lev",      "Lev",   "Lv"  } },
3945     { BL_MASK_FLY,      { "Fly",      "Fly",   "Fl"  } },
3946     { BL_MASK_RIDE,     { "Ride",     "Rid",   "Rd"  } },
3947 };
3948 static const char *encvals[3][6] = {
3949     { "", "Burdened", "Stressed", "Strained", "Overtaxed", "Overloaded" },
3950     { "", "Burden",   "Stress",   "Strain",   "Overtax",   "Overload"   },
3951     { "", "Brd",      "Strs",     "Strn",     "Ovtx",      "Ovld"       }
3952 };
3953 #define blPAD BL_FLUSH
3954 #define MAX_PER_ROW 15
3955 /* 2 or 3 status lines */
3956 static const enum statusfields
3957     twolineorder[3][MAX_PER_ROW] = {
3958     { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN,
3959       BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD },
3960     { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
3961       BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER,
3962       BL_CAP, BL_CONDITION, BL_FLUSH },
3963     /* third row of array isn't used for twolineorder */
3964     { BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD,
3965       blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }
3966 },
3967     /* Align moved from 1 to 2, Leveldesc+Time+Condition moved from 2 to 3 */
3968     threelineorder[3][MAX_PER_ROW] = {
3969     { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3970       BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD },
3971     { BL_ALIGN, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
3972       BL_AC, BL_XP, BL_EXP, BL_HD, BL_HUNGER,
3973       BL_CAP, BL_FLUSH, blPAD, blPAD },
3974     { BL_LEVELDESC, BL_TIME, BL_CONDITION, BL_FLUSH, blPAD, blPAD,
3975       blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }
3976 };
3977 static const enum statusfields (*fieldorder)[MAX_PER_ROW];
3978
3979 static int finalx[3][2];    /* [rows][NOW or BEFORE] */
3980 static boolean windowdata_init = FALSE;
3981 static int cond_shrinklvl = 0;
3982 static int enclev = 0, enc_shrinklvl = 0;
3983 static int dlvl_shrinklvl = 0;
3984 static boolean truncation_expected = FALSE;
3985 #define FORCE_RESET TRUE
3986 #define NO_RESET FALSE
3987
3988 /* This controls whether to skip fields that aren't
3989  * flagged as requiring updating during the current
3990  * render_status().
3991  *
3992  * Hopefully that can be confirmed as working correctly
3993  * for all platforms eventually and the conditional
3994  * setting below can be removed.
3995  */
3996 static int do_field_opt =
3997 #if defined(DISABLE_TTY_FIELD_OPT)
3998     0;
3999 #else
4000     1;
4001 #endif
4002
4003 #endif  /* STATUS_HILITES */
4004
4005 /*
4006  *  tty_status_init()
4007  *      -- initialize the tty-specific data structures.
4008  *      -- call genl_status_init() to initialize the general data.
4009  */
4010 void
4011 tty_status_init()
4012 {
4013 #ifdef STATUS_HILITES
4014     int i, num_rows;
4015
4016     num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4017     fieldorder = (num_rows != 3) ? twolineorder : threelineorder;
4018
4019     for (i = 0; i < MAXBLSTATS; ++i) {
4020         tty_status[NOW][i].idx = BL_FLUSH;
4021         tty_status[NOW][i].color = NO_COLOR; /* no color */
4022         tty_status[NOW][i].attr = ATR_NONE;
4023         tty_status[NOW][i].x = tty_status[NOW][i].y = 0;
4024         tty_status[NOW][i].valid  = FALSE;
4025         tty_status[NOW][i].dirty  = FALSE;
4026         tty_status[NOW][i].redraw = FALSE;
4027         tty_status[NOW][i].sanitycheck = FALSE;
4028         tty_status[BEFORE][i] = tty_status[NOW][i];
4029     }
4030     tty_condition_bits = 0L;
4031     hpbar_percent = 0, hpbar_color = NO_COLOR;
4032 #endif /* STATUS_HILITES */
4033
4034     /* let genl_status_init do most of the initialization */
4035     genl_status_init();
4036 }
4037
4038 void
4039 tty_status_enablefield(fieldidx, nm, fmt, enable)
4040 int fieldidx;
4041 const char *nm;
4042 const char *fmt;
4043 boolean enable;
4044 {
4045     genl_status_enablefield(fieldidx, nm, fmt, enable);
4046 }
4047
4048 #ifdef STATUS_HILITES
4049
4050 /*
4051  *  *_status_update()
4052  *      -- update the value of a status field.
4053  *      -- the fldindex identifies which field is changing and
4054  *         is an integer index value from botl.h
4055  *      -- fldindex could be any one of the following from botl.h:
4056  *         BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
4057  *         BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
4058  *         BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
4059  *         BL_LEVELDESC, BL_EXP, BL_CONDITION
4060  *      -- fldindex could also be BL_FLUSH (-1), which is not really
4061  *         a field index, but is a special trigger to tell the
4062  *         windowport that it should output all changes received
4063  *         to this point. It marks the end of a bot() cycle.
4064  *      -- fldindex could also be BL_RESET (-3), which is not really
4065  *         a field index, but is a special advisory to to tell the
4066  *         windowport that it should redisplay all its status fields,
4067  *         even if no changes have been presented to it.
4068  *      -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
4069  *         If fldindex is BL_CONDITION, then ptr is a long value with
4070  *         any or none of the following bits set (from botl.h):
4071  *              BL_MASK_STONE           0x00000001L
4072  *              BL_MASK_SLIME           0x00000002L
4073  *              BL_MASK_STRNGL          0x00000004L
4074  *              BL_MASK_FOODPOIS        0x00000008L
4075  *              BL_MASK_TERMILL         0x00000010L
4076  *              BL_MASK_BLIND           0x00000020L
4077  *              BL_MASK_DEAF            0x00000040L
4078  *              BL_MASK_STUN            0x00000080L
4079  *              BL_MASK_CONF            0x00000100L
4080  *              BL_MASK_HALLU           0x00000200L
4081  *              BL_MASK_LEV             0x00000400L
4082  *              BL_MASK_FLY             0x00000800L
4083  *              BL_MASK_RIDE            0x00001000L
4084  *      -- The value passed for BL_GOLD usually includes an encoded leading
4085  *         symbol for GOLD "\GXXXXNNNN:nnn". If the window port needs to use
4086  *         the textual gold amount without the leading "$:" the port will
4087  *         have to skip past ':' in the passed "ptr" for the BL_GOLD case.
4088  *      -- color is an unsigned int.
4089  *               color_index = color & 0x00FF;         CLR_* value
4090  *               attribute   = (color >> 8) & 0x00FF;  HL_ATTCLR_* mask
4091  *         This holds the color and attribute that the field should
4092  *         be displayed in.
4093  *         This is relevant for everything except BL_CONDITION fldindex.
4094  *         If fldindex is BL_CONDITION, this parameter should be ignored,
4095  *         as condition highlighting is done via the next colormasks
4096  *         parameter instead.
4097  *
4098  *      -- colormasks - pointer to cond_hilites[] array of colormasks.
4099  *         Only relevant for BL_CONDITION fldindex. The window port
4100  *         should ignore this parameter for other fldindex values.
4101  *         Each condition bit must only ever appear in one of the
4102  *         CLR_ array members, but can appear in multiple HL_ATTCLR_
4103  *         offsets (because more than one attribute can co-exist).
4104  *         See doc/window.doc for more details.
4105  */
4106
4107 void
4108 tty_status_update(fldidx, ptr, chg, percent, color, colormasks)
4109 int fldidx, chg UNUSED, percent, color;
4110 genericptr_t ptr;
4111 unsigned long *colormasks;
4112 {
4113     int attrmask;
4114     long *condptr = (long *) ptr;
4115     char *text = (char *) ptr;
4116     char goldbuf[40], *lastchar, *p;
4117     const char *fmt;
4118     boolean reset_state = NO_RESET;
4119
4120     if ((fldidx < BL_RESET) || (fldidx >= MAXBLSTATS))
4121         return;
4122
4123     if ((fldidx >= 0 && fldidx < MAXBLSTATS) && !status_activefields[fldidx])
4124         return;
4125
4126     switch (fldidx) {
4127     case BL_RESET:
4128         reset_state = FORCE_RESET;
4129         /*FALLTHRU*/
4130     case BL_FLUSH:
4131         if (make_things_fit(reset_state) || truncation_expected) {
4132             render_status();
4133 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
4134             status_sanity_check();
4135 #endif
4136         }
4137         return;
4138     case BL_CONDITION:
4139         tty_status[NOW][fldidx].idx = fldidx;
4140         tty_condition_bits = *condptr;
4141         tty_colormasks = colormasks;
4142         tty_status[NOW][fldidx].valid = TRUE;
4143         tty_status[NOW][fldidx].dirty = TRUE;
4144         tty_status[NOW][fldidx].sanitycheck = TRUE;
4145         truncation_expected = FALSE;
4146         break;
4147     case BL_GOLD:
4148         text = decode_mixed(goldbuf, text);
4149         /*FALLTHRU*/
4150     default:
4151         attrmask = (color >> 8) & 0x00FF;
4152 #ifndef TEXTCOLOR
4153         color = NO_COLOR;
4154 #endif
4155         fmt = status_fieldfmt[fldidx];
4156         if (!fmt)
4157             fmt = "%s";
4158         /* should be checking for first enabled field here rather than
4159            just first field, but 'fieldorder' doesn't start any rows
4160            with fields which can be disabled so [any_row][0] suffices */
4161         if (*fmt == ' ' && (fldidx == fieldorder[0][0]
4162                             || fldidx == fieldorder[1][0]
4163                             || fldidx == fieldorder[2][0]))
4164             ++fmt; /* skip leading space for first field on line */
4165         Sprintf(status_vals[fldidx], fmt, text);
4166         tty_status[NOW][fldidx].idx = fldidx;
4167         tty_status[NOW][fldidx].color = (color & 0x00FF);
4168         tty_status[NOW][fldidx].attr = term_attr_fixup(attrmask);
4169         tty_status[NOW][fldidx].lth = strlen(status_vals[fldidx]);
4170         tty_status[NOW][fldidx].valid = TRUE;
4171         tty_status[NOW][fldidx].dirty = TRUE;
4172         tty_status[NOW][fldidx].sanitycheck = TRUE;
4173         break;
4174     }
4175
4176     /* The core botl engine sends a single blank to the window port
4177        for carrying-capacity when its unused. Let's suppress that */
4178     if (fldidx >= 0 && fldidx < MAXBLSTATS
4179         && tty_status[NOW][fldidx].lth == 1
4180         && status_vals[fldidx][0] == ' ') {
4181         status_vals[fldidx][0] = '\0';
4182         tty_status[NOW][fldidx].lth = 0;
4183     }
4184
4185     /* default processing above was required before these */
4186     switch (fldidx) {
4187     case BL_HP:
4188         if (iflags.wc2_hitpointbar) {
4189             /* Special additional processing for hitpointbar */
4190             hpbar_percent = percent;
4191             hpbar_color = (color & 0x00FF);
4192             tty_status[NOW][BL_TITLE].color = hpbar_color;
4193             tty_status[NOW][BL_TITLE].dirty = TRUE;
4194         }
4195         break;
4196     case BL_LEVELDESC:
4197         dlvl_shrinklvl = 0; /* caller is passing full length string */
4198         /*FALLTHRU*/
4199     case BL_HUNGER:
4200         /* The core sends trailing blanks for some fields.
4201            Let's suppress the trailing blanks */
4202         if (tty_status[NOW][fldidx].lth > 0) {
4203             p = status_vals[fldidx];
4204             for (lastchar = eos(p); lastchar > p && *--lastchar == ' '; ) {
4205                 *lastchar = '\0';
4206                 tty_status[NOW][fldidx].lth--;
4207             }
4208         }
4209         break;
4210     case BL_TITLE:
4211         /* when hitpointbar is enabled, rendering will enforce a length
4212            of 30 on title, padding with spaces or truncating if necessary */
4213         if (iflags.wc2_hitpointbar)
4214             tty_status[NOW][fldidx].lth = 30 + 2; /* '[' and ']' */
4215         break;
4216     case BL_GOLD:
4217         /* \GXXXXNNNN counts as 1 [moot since we use decode_mixed() above] */
4218         if ((p = index(status_vals[fldidx], '\\')) != 0 && p[1] == 'G')
4219             tty_status[NOW][fldidx].lth -= (10 - 1);
4220         break;
4221     case BL_CAP:
4222         enc_shrinklvl = 0; /* caller is passing full length string */
4223         enclev = stat_cap_indx();
4224         break;
4225     }
4226     /* As of 3.6.2 we only render on BL_FLUSH (or BL_RESET) */
4227     return;
4228 }
4229
4230 STATIC_OVL int
4231 make_things_fit(force_update)
4232 boolean force_update;
4233 {
4234     int trycnt, fitting = 0, requirement;
4235     int rowsz[3], num_rows, condrow, otheroptions = 0;
4236
4237     num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4238     condrow = num_rows - 1; /* always last row, 1 for 0..1 or 2 for 0..2 */
4239     cond_shrinklvl = 0;
4240     if (enc_shrinklvl > 0 && num_rows == 2)
4241         shrink_enc(0);
4242     if (dlvl_shrinklvl > 0)
4243         shrink_dlvl(0);
4244     set_condition_length();
4245     for (trycnt = 0; trycnt < 6 && !fitting; ++trycnt) {
4246         /* FIXME: this remeasures each line every time even though it
4247            is only attempting to shrink one of them and the other one
4248            (or two) remains the same */
4249         if (!check_fields(force_update, rowsz)) {
4250             fitting = 0;
4251             break;
4252         }
4253
4254         requirement = rowsz[condrow] - 1;
4255         if (requirement <= wins[WIN_STATUS]->cols - 1) {
4256             fitting = requirement;
4257             break;  /* we're good */
4258         }
4259         if (trycnt < 2) {
4260             if (cond_shrinklvl < trycnt + 1) {
4261                 cond_shrinklvl = trycnt + 1;
4262                 set_condition_length();
4263             }
4264             continue;
4265         }
4266         if (cond_shrinklvl >= 2) {
4267             /* We've exhausted the condition identifiers shrinkage,
4268              * so let's try shrinking other things...
4269              */
4270             if (otheroptions < 2) {
4271                 /* try shrinking the encumbrance word, but
4272                    only when it's on the same line as conditions */
4273                 if (num_rows == 2)
4274                     shrink_enc(otheroptions + 1);
4275             } else if (otheroptions == 2) {
4276                 shrink_dlvl(1);
4277             } else {
4278                 /* Last resort - turn on trunction */
4279                 truncation_expected = TRUE;
4280                 break;
4281             }
4282             ++otheroptions;
4283         }
4284     }
4285     return fitting;
4286 }
4287
4288 /*
4289  * This is the routine where we figure out where each field
4290  * should be placed, and flag whether the on-screen details
4291  * must be updated because they need to change.
4292  * This is now done at an individual field case-by-case level.
4293  */
4294 STATIC_OVL boolean
4295 check_fields(forcefields, sz)
4296 boolean forcefields;
4297 int sz[3];
4298 {
4299     int c, i, row, col, num_rows, idx;
4300     boolean valid = TRUE, matchprev, update_right;
4301
4302     if (!windowdata_init && !check_windowdata())
4303         return FALSE;
4304
4305     num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4306
4307     for (row = 0; row < num_rows; ++row) {
4308         sz[row] = 0;
4309         col = 1;
4310         update_right = FALSE;
4311         for (i = 0; (idx = fieldorder[row][i]) != BL_FLUSH; ++i) {
4312             if (!status_activefields[idx])
4313                 continue;
4314             if (!tty_status[NOW][idx].valid)
4315                 valid = FALSE;
4316             /* might be called more than once for shrink tests, so need
4317                to reset these (redraw and x at any rate) each time */
4318             tty_status[NOW][idx].redraw = FALSE;
4319             tty_status[NOW][idx].y = row;
4320             tty_status[NOW][idx].x = col;
4321
4322             /* On a change to the field location, everything further
4323                to the right must be updated as well.  (Not necessarily
4324                everything; it's possible for complementary changes across
4325                multiple fields to put stuff further right back in sync.) */
4326             if (tty_status[NOW][idx].x + tty_status[NOW][idx].lth
4327                 != tty_status[BEFORE][idx].x + tty_status[BEFORE][idx].lth)
4328                 update_right = TRUE;
4329             else if (tty_status[NOW][idx].lth != tty_status[BEFORE][idx].lth
4330                      || tty_status[NOW][idx].x != tty_status[BEFORE][idx].x)
4331                 tty_status[NOW][idx].redraw = TRUE;
4332             else /* in case update_right is set, we're back in sync now */
4333                 update_right = FALSE;
4334
4335             matchprev = FALSE; /* assume failure */
4336             if (valid && !update_right && !forcefields
4337                 && !tty_status[NOW][idx].redraw) {
4338                 /*
4339                  * Check values against those already on the display.
4340                  *  - Is the additional processing time for this worth it?
4341                  */
4342                 if (do_field_opt
4343                     /* color/attr checks aren't right for 'condition'
4344                        and neither is examining status_vals[BL_CONDITION]
4345                        so skip same-contents optimization for conditions */
4346                     && idx != BL_CONDITION
4347                     && (tty_status[NOW][idx].color
4348                         == tty_status[BEFORE][idx].color)
4349                     && (tty_status[NOW][idx].attr
4350                         == tty_status[BEFORE][idx].attr)) {
4351                     matchprev = TRUE; /* assume success */
4352                     if (tty_status[NOW][idx].dirty) {
4353                         /* compare values */
4354                         const char *ob, *nb; /* old byte, new byte */
4355                         struct WinDesc *cw = wins[WIN_STATUS];
4356
4357                         c = col - 1;
4358                         ob = &cw->data[row][c];
4359                         nb = status_vals[idx];
4360                         while (*nb && c < cw->cols) {
4361                             if (*nb != *ob)
4362                                 break;
4363                             nb++;
4364                             ob++;
4365                             c++;
4366                         }
4367                         /* if we're not at the end of new string, no match;
4368                            we don't need to worry about whether there might
4369                            be leftover old string; that could only happen
4370                            if they have different lengths, in which case
4371                            'update_right' will be set and we won't get here */
4372                         if (*nb)
4373                             matchprev = FALSE;
4374 #if 0
4375                         if (*nb || (*ob && *ob != ' '
4376                                     && (*ob != '/' || idx != BL_XP)
4377                                     && (*ob != '(' || (idx != BL_HP
4378                                                        && idx != BL_ENE))))
4379                             matchprev = FALSE;
4380 #endif
4381                     }
4382                 }
4383             }
4384
4385             if (forcefields || update_right
4386                 || (tty_status[NOW][idx].dirty && !matchprev))
4387                 tty_status[NOW][idx].redraw = TRUE;
4388
4389             col += tty_status[NOW][idx].lth;
4390         }
4391         sz[row] = col;
4392     }
4393     return valid;
4394 }
4395
4396 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
4397 STATIC_OVL void
4398 status_sanity_check(VOID_ARGS)
4399 {
4400     int i;
4401     static boolean in_sanity_check = FALSE;
4402     static const char *const idxtext[] = {
4403         "BL_TITLE", "BL_STR", "BL_DX", "BL_CO", "BL_IN", "BL_WI", /* 0.. 5   */
4404         "BL_CH","BL_ALIGN", "BL_SCORE", "BL_CAP", "BL_GOLD",     /* 6.. 10  */
4405         "BL_ENE", "BL_ENEMAX", "BL_XP", "BL_AC", "BL_HD",       /* 11.. 15 */
4406         "BL_TIME", "BL_HUNGER", "BL_HP", "BL_HPMAX",           /* 16.. 19 */
4407         "BL_LEVELDESC", "BL_EXP", "BL_CONDITION"              /* 20.. 22 */
4408     };
4409    
4410     if (in_sanity_check)
4411         return;
4412     in_sanity_check = TRUE;
4413     /*
4414      * Make sure that every field made it down to the
4415      * bottom of the render_status() for-loop.
4416      */
4417     for (i = 0; i < MAXBLSTATS; ++i) {
4418         if (tty_status[NOW][i].sanitycheck) {
4419             char panicmsg[BUFSZ];
4420
4421             Sprintf(panicmsg, "failed on tty_status[NOW][%s].", idxtext[i]);
4422             paniclog("status_sanity_check", panicmsg);
4423             tty_status[NOW][i].sanitycheck = FALSE;
4424             /*
4425              * Attention developers: If you encounter the above
4426              * message in paniclog, it almost certainly means that
4427              * a recent code change has caused a failure to reach
4428              * the bottom of render_status(), at least for the BL_
4429              * field identified in the impossible() message.
4430              *
4431              * That could be because of the addition of a continue
4432              * statement within the render_status() for-loop, or a
4433              * premature return from render_status() before it finished
4434              * its housekeeping chores.
4435              */
4436         }
4437     }
4438     in_sanity_check = FALSE;
4439 }
4440 #endif /* NHDEVEL_STATUS */
4441
4442 /*
4443  * This is what places a field on the tty display.
4444  */
4445 STATIC_OVL void
4446 tty_putstatusfield(text, x, y)
4447 const char *text;
4448 int x, y;
4449 {
4450     int i, n, ncols, nrows, lth = 0;
4451     struct WinDesc *cw = 0;
4452
4453     if (WIN_STATUS == WIN_ERR
4454         || (cw = wins[WIN_STATUS]) == (struct WinDesc *) 0)
4455         panic("tty_putstatusfield: Invalid WinDesc\n");
4456
4457     ncols = cw->cols;
4458     nrows = cw->maxrow;
4459     lth = (int) strlen(text);
4460
4461     print_vt_code2(AVTC_SELECT_WINDOW, NHW_STATUS);
4462
4463     if (x < ncols && y < nrows) {
4464         if (x != cw->curx || y != cw->cury)
4465             tty_curs(NHW_STATUS, x, y);
4466         for (i = 0; i < lth; ++i) {
4467             n = i + x;
4468             if (n < ncols && *text) {
4469 #if 0 /*JP*/
4470                 (void) putchar(*text);
4471 #else
4472                 (void) jputchar(*(unsigned char *)text);
4473 #endif
4474                 ttyDisplay->curx++;
4475                 cw->curx++;
4476                 cw->data[y][n - 1] = *text;
4477                 text++;
4478             }
4479         }
4480     }
4481 #if 0
4482     else {
4483         if (truncation_expected) {
4484         /* Now we're truncating */
4485             ; /* but we knew in advance */
4486         }
4487     }
4488 #endif
4489 }
4490
4491 /* caller must set cond_shrinklvl (0..2) before calling us */
4492 STATIC_OVL void
4493 set_condition_length()
4494 {
4495     long mask;
4496     int c, lth = 0;
4497
4498     if (tty_condition_bits) {
4499         for (c = 0; c < SIZE(conditions); ++c) {
4500             mask = conditions[c].mask;
4501             if ((tty_condition_bits & mask) == mask)
4502                 lth += 1 + (int) strlen(conditions[c].text[cond_shrinklvl]);
4503         }
4504     }
4505     tty_status[NOW][BL_CONDITION].lth = lth;
4506 }
4507
4508 STATIC_OVL void
4509 shrink_enc(lvl)
4510 int lvl;
4511 {
4512     /* shrink or restore the encumbrance word */
4513     if (lvl <= 2) {
4514         enc_shrinklvl = lvl;
4515         Sprintf(status_vals[BL_CAP], " %s", encvals[lvl][enclev]);
4516     }
4517     tty_status[NOW][BL_CAP].lth = strlen(status_vals[BL_CAP]);
4518 }
4519
4520 STATIC_OVL void
4521 shrink_dlvl(lvl)
4522 int lvl;
4523 {
4524     /* try changing Dlvl: to Dl: */
4525     char buf[BUFSZ];
4526     char *levval = index(status_vals[BL_LEVELDESC], ':');
4527
4528     if (levval) {
4529         dlvl_shrinklvl = lvl;
4530         Strcpy(buf, (lvl == 0) ? "Dlvl" : "Dl");
4531         Strcat(buf, levval);
4532         Strcpy(status_vals[BL_LEVELDESC], buf);
4533         tty_status[NOW][BL_LEVELDESC].lth = strlen(status_vals[BL_LEVELDESC]);
4534     }
4535 }
4536
4537 /*
4538  * Ensure the underlying status window data start out
4539  * blank and null-terminated.
4540  */
4541 STATIC_OVL boolean
4542 check_windowdata(VOID_ARGS)
4543 {
4544     if (WIN_STATUS == WIN_ERR || wins[WIN_STATUS] == (struct WinDesc *) 0) {
4545         paniclog("check_windowdata", " null status window.");
4546         return FALSE;
4547     } else if (!windowdata_init) {
4548         tty_clear_nhwindow(WIN_STATUS); /* also sets cw->data[] to spaces */
4549         windowdata_init = TRUE;
4550     }
4551     return TRUE;
4552 }
4553
4554 #ifdef TEXTCOLOR
4555 /*
4556  * Return what color this condition should
4557  * be displayed in based on user settings.
4558  */
4559 STATIC_OVL int
4560 condcolor(bm, bmarray)
4561 long bm;
4562 unsigned long *bmarray;
4563 {
4564     int i;
4565
4566     if (bm && bmarray)
4567         for (i = 0; i < CLR_MAX; ++i) {
4568             if ((bm & bmarray[i]) != 0)
4569                 return i;
4570         }
4571     return NO_COLOR;
4572 }
4573 #else
4574 /* might need something more elaborate if some compiler complains that
4575    the condition where this gets used always has the same value */
4576 #define condcolor(bm,bmarray) NO_COLOR
4577 #define term_start_color(color) /*empty*/
4578 #define term_end_color() /*empty*/
4579 #endif /* TEXTCOLOR */
4580
4581 STATIC_OVL int
4582 condattr(bm, bmarray)
4583 long bm;
4584 unsigned long *bmarray;
4585 {
4586     int attr = 0;
4587     int i;
4588
4589     if (bm && bmarray) {
4590         for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) {
4591             if ((bm & bmarray[i]) != 0) {
4592                 switch (i) {
4593                 case HL_ATTCLR_DIM:
4594                     attr |= HL_DIM;
4595                     break;
4596                 case HL_ATTCLR_BLINK:
4597                     attr |= HL_BLINK;
4598                     break;
4599                 case HL_ATTCLR_ULINE:
4600                     attr |= HL_ULINE;
4601                     break;
4602                 case HL_ATTCLR_INVERSE:
4603                     attr |= HL_INVERSE;
4604                     break;
4605                 case HL_ATTCLR_BOLD:
4606                     attr |= HL_BOLD;
4607                     break;
4608                 }
4609             }
4610         }
4611     }
4612     return attr;
4613 }
4614
4615 #define Begin_Attr(m) \
4616     do {                                        \
4617         if (m) {                                \
4618             if ((m) & HL_BOLD)                  \
4619                 term_start_attr(ATR_BOLD);      \
4620             if ((m) & HL_INVERSE)               \
4621                 term_start_attr(ATR_INVERSE);   \
4622             if ((m) & HL_ULINE)                 \
4623                 term_start_attr(ATR_ULINE);     \
4624             if ((m) & HL_BLINK)                 \
4625                 term_start_attr(ATR_BLINK);     \
4626             if ((m) & HL_DIM)                   \
4627                 term_start_attr(ATR_DIM);       \
4628         }                                       \
4629     } while (0)
4630
4631 #define End_Attr(m) \
4632     do {                                        \
4633         if (m) {                                \
4634             if ((m) & HL_DIM)                   \
4635                 term_end_attr(ATR_DIM);         \
4636             if ((m) & HL_BLINK)                 \
4637                 term_end_attr(ATR_BLINK);       \
4638             if ((m) & HL_ULINE)                 \
4639                 term_end_attr(ATR_ULINE);       \
4640             if ((m) & HL_INVERSE)               \
4641                 term_end_attr(ATR_INVERSE);     \
4642             if ((m) & HL_BOLD)                  \
4643                 term_end_attr(ATR_BOLD);        \
4644         }                                       \
4645     } while (0)
4646
4647 STATIC_OVL void
4648 render_status(VOID_ARGS)
4649 {
4650     long mask, bits;
4651     int i, x, y, idx, c, row, tlth, num_rows, coloridx = 0, attrmask = 0;
4652     char *text;
4653     struct WinDesc *cw = 0;
4654
4655     if (WIN_STATUS == WIN_ERR
4656         || (cw = wins[WIN_STATUS]) == (struct WinDesc *) 0) {
4657         paniclog("render_status", "WIN_ERR on status window.");
4658         return;
4659     }
4660
4661     num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4662     for (row = 0; row < num_rows; ++row) {
4663         HUPSKIP();
4664         y = row;
4665         tty_curs(WIN_STATUS, 1, y);
4666         for (i = 0; (idx = fieldorder[row][i]) != BL_FLUSH; ++i) {
4667             if (!status_activefields[idx])
4668                 continue;
4669             x = tty_status[NOW][idx].x;
4670             text = status_vals[idx]; /* always "" for BL_CONDITION */
4671             tlth = (int) tty_status[NOW][idx].lth; /* valid for BL_CONDITION */
4672
4673             if (tty_status[NOW][idx].redraw || !do_field_opt) {
4674                 boolean hitpointbar = (idx == BL_TITLE
4675                                        && iflags.wc2_hitpointbar);
4676
4677                 if (idx == BL_CONDITION) {
4678                     /*
4679                      * +-----------------+
4680                      * | Condition Codes |
4681                      * +-----------------+
4682                      */
4683                     bits = tty_condition_bits;
4684                     /* if no bits are set, we can fall through condition
4685                        rendering code to finalx[] handling (and subsequent
4686                        rest-of-line erasure if line is shorter than before) */
4687                     if (num_rows == 3 && bits != 0L) {
4688                         int k;
4689                         char *dat = &cw->data[y][0];
4690
4691                         /* line up with hunger (or where it would have
4692                            been when currently omitted); if there isn't
4693                            enough room for that, right justify; or place
4694                            as-is if not even enough room for /that/; we
4695                            expect hunger to be on preceding row, in which
4696                            case its current data has been moved to [BEFORE] */
4697                         if (tty_status[BEFORE][BL_HUNGER].y < row
4698                             && x < tty_status[BEFORE][BL_HUNGER].x
4699                             && (tty_status[BEFORE][BL_HUNGER].x + tlth
4700                                 < cw->cols - 1))
4701                             k = tty_status[BEFORE][BL_HUNGER].x;
4702                         else if (x + tlth < cw->cols - 1)
4703                             k = cw->cols - tlth;
4704                         else
4705                             k = x;
4706                         while (x < k) {
4707                             if (dat[x - 1] != ' ')
4708                                 tty_putstatusfield(" ", x, y);
4709                             ++x;
4710                         }
4711                         tty_status[NOW][BL_CONDITION].x = x;
4712                         tty_curs(WIN_STATUS, x, y);
4713                     }
4714                     for (c = 0; c < SIZE(conditions) && bits != 0L; ++c) {
4715                         mask = conditions[c].mask;
4716                         if (bits & mask) {
4717                             const char *condtext;
4718
4719                             tty_putstatusfield(" ", x++, y);
4720                             if (iflags.hilite_delta) {
4721                                 attrmask = condattr(mask, tty_colormasks);
4722                                 Begin_Attr(attrmask);
4723                                 coloridx = condcolor(mask, tty_colormasks);
4724                                 if (coloridx != NO_COLOR)
4725                                     term_start_color(coloridx);
4726                             }
4727                             condtext = conditions[c].text[cond_shrinklvl];
4728                             if (x >= cw->cols && !truncation_expected) {
4729                                 impossible(
4730                          "Unexpected condition placement overflow for \"%s\"",
4731                                            condtext);
4732                                 condtext = "";
4733                                 bits = 0L; /* skip any remaining conditions */
4734                             }
4735                             tty_putstatusfield(condtext, x, y);
4736                             x += (int) strlen(condtext);
4737                             if (iflags.hilite_delta) {
4738                                 if (coloridx != NO_COLOR)
4739                                     term_end_color();
4740                                 End_Attr(attrmask);
4741                             }
4742                             bits &= ~mask;
4743                         }
4744                     }
4745                     /* 'x' is 1-based and 'cols' and 'data' are 0-based,
4746                        so x==cols means we just stored in data[N-2] and
4747                        are now positioned at data[N-1], the terminator;
4748                        that's ok as long as we don't write there */
4749                     if (x > cw->cols) {
4750                         static unsigned once_only = 0;
4751
4752                         if (!truncation_expected && !once_only++)
4753                             paniclog("render_status()",
4754                                      " unexpected truncation.");
4755                         x = cw->cols;
4756                     }
4757                 } else if (hitpointbar) {
4758                     /*
4759                      * +-------------------------+
4760                      * | Title with Hitpoint Bar |
4761                      * +-------------------------+
4762                      */
4763                     /* hitpointbar using hp percent calculation */
4764                     int bar_len, bar_pos = 0;
4765                     char bar[MAXCO], *bar2 = (char *) 0, savedch = '\0';
4766                     boolean twoparts = (hpbar_percent < 100);
4767
4768                     /* force exactly 30 characters, padded with spaces
4769                        if shorter or truncated if longer */
4770                     if (strlen(text) != 30) {
4771                         Sprintf(bar, "%-30.30s", text);
4772                         Strcpy(status_vals[BL_TITLE], bar);
4773                     } else
4774                         Strcpy(bar, text);
4775                     bar_len = (int) strlen(bar); /* always 30 */
4776                     tlth = bar_len + 2;
4777                     /* when at full HP, the whole title will be highlighted;
4778                        when injured or dead, there will be a second portion
4779                        which is not highlighted */
4780                     if (twoparts) {
4781                         /* figure out where to separate the two parts */
4782                         bar_pos = (bar_len * hpbar_percent) / 100;
4783                         if (bar_pos < 1 && hpbar_percent > 0)
4784                             bar_pos = 1;
4785                         if (bar_pos >= bar_len && hpbar_percent < 100)
4786                             bar_pos = bar_len - 1;
4787                         bar2 = &bar[bar_pos];
4788                         savedch = *bar2;
4789                         *bar2 = '\0';
4790                     }
4791                     tty_putstatusfield("[", x++, y);
4792                     if (*bar) { /* always True, unless twoparts+dead (0 HP) */
4793                         term_start_attr(ATR_INVERSE);
4794                         if (iflags.hilite_delta && hpbar_color != NO_COLOR)
4795                             term_start_color(hpbar_color);
4796                         tty_putstatusfield(bar, x, y);
4797                         x += (int) strlen(bar);
4798                         if (iflags.hilite_delta && hpbar_color != NO_COLOR)
4799                             term_end_color();
4800                         term_end_attr(ATR_INVERSE);
4801                     }
4802                     if (twoparts) { /* no highlighting for second part */
4803                         *bar2 = savedch;
4804                         tty_putstatusfield(bar2, x, y);
4805                         x += (int) strlen(bar2);
4806                     }
4807                     tty_putstatusfield("]", x++, y);
4808                 } else {
4809                     /*
4810                      * +-----------------------------+
4811                      * | Everything else that is not |
4812                      * |   in a special case above   |
4813                      * +-----------------------------+
4814                      */
4815                     if (iflags.hilite_delta) {
4816                         while (*text == ' ') {
4817                             tty_putstatusfield(" ", x++, y);
4818                             text++;
4819                         }
4820                         if (*text == '/' && idx == BL_EXP) {
4821                             tty_putstatusfield("/", x++, y);
4822                             text++;
4823                         }
4824                         attrmask = tty_status[NOW][idx].attr;
4825                         Begin_Attr(attrmask);
4826                         coloridx = tty_status[NOW][idx].color;
4827                         if (coloridx != NO_COLOR)
4828                             term_start_color(coloridx);
4829                     }
4830                     tty_putstatusfield(text, x, y);
4831                     x += (int) strlen(text);
4832                     if (iflags.hilite_delta) {
4833                         if (coloridx != NO_COLOR)
4834                             term_end_color();
4835                         End_Attr(attrmask);
4836                     }
4837                 }
4838             } else {
4839                 /* not rendered => same text as before */
4840                 x += tlth;
4841             }
4842             finalx[row][NOW] = x - 1;
4843             /* reset .redraw and .dirty now that field has been rendered */
4844             tty_status[NOW][idx].dirty  = FALSE;
4845             tty_status[NOW][idx].redraw = FALSE;
4846             tty_status[NOW][idx].sanitycheck = FALSE;
4847             /*
4848              * For comparison of current and previous:
4849              * - Copy the entire tty_status struct.
4850              */
4851             tty_status[BEFORE][idx] = tty_status[NOW][idx];
4852         }
4853         x = finalx[row][NOW];
4854         if ((x < finalx[row][BEFORE] || !finalx[row][BEFORE])
4855             && x + 1 < cw->cols) {
4856             tty_curs(WIN_STATUS, x + 1, y);
4857             cl_end();
4858         }
4859         /*
4860          * For comparison of current and previous:
4861          * - Copy the last written column number on the row.
4862          */
4863         finalx[row][BEFORE] = finalx[row][NOW];
4864     }
4865     return;
4866 }
4867
4868 #endif /* STATUS_HILITES */
4869
4870 #endif /* TTY_GRAPHICS */
4871
4872 /*wintty.c*/