OSDN Git Service

update lgtm.yml
[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-2019            */
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     boolean linestart;
2371     register char *cp;
2372
2373     for (n = 0, i = 0; i < cw->maxrow; i++) {
2374         HUPSKIP();
2375         if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) {
2376             tty_curs(window, 1, n);
2377             cl_end();
2378             dmore(cw, quitchars);
2379             if (morc == '\033') {
2380                 cw->flags |= WIN_CANCELLED;
2381                 break;
2382             }
2383             if (cw->offy) {
2384                 tty_curs(window, 1, 0);
2385                 cl_eos();
2386             } else
2387                 clear_screen();
2388             n = 0;
2389         }
2390         tty_curs(window, 1, n++);
2391 #ifdef H2344_BROKEN
2392         cl_end();
2393 #else
2394         if (cw->offx)
2395             cl_end();
2396 #endif
2397         if (cw->data[i]) {
2398             attr = cw->data[i][0] - 1;
2399             if (cw->offx) {
2400 #if 0 /*JP*/
2401                 (void) putchar(' ');
2402 #else
2403                 (void) jputchar(' ');
2404 #endif
2405                 ++ttyDisplay->curx;
2406             }
2407             term_start_attr(attr);
2408             for (cp = &cw->data[i][1], linestart = TRUE;
2409 #ifndef WIN32CON
2410                  *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
2411                  cp++
2412 #else
2413                  *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
2414                  cp++, ttyDisplay->curx++
2415 #endif
2416                  ) {
2417                 /* message recall for msg_window:full/combination/reverse
2418                    might have output from '/' in it (see redotoplin()) */
2419 #if 0 /*JP*/
2420                 if (linestart && (*cp & 0x80) != 0) {
2421                     g_putch(*cp);
2422                     end_glyphout();
2423                     linestart = FALSE;
2424                 } else {
2425                     (void) putchar(*cp);
2426                 }
2427 #else
2428                 (void) jputchar(*cp);
2429 #endif
2430             }
2431             term_end_attr(attr);
2432         }
2433     }
2434     if (i == cw->maxrow) {
2435 #ifdef H2344_BROKEN
2436         if (cw->type == NHW_TEXT) {
2437             tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->cury + 1);
2438             cl_eos();
2439         }
2440 #endif
2441         tty_curs(BASE_WINDOW, (int) cw->offx + 1,
2442                  (cw->type == NHW_TEXT) ? (int) ttyDisplay->rows - 1 : n);
2443         cl_end();
2444         dmore(cw, quitchars);
2445         if (morc == '\033')
2446             cw->flags |= WIN_CANCELLED;
2447     }
2448 }
2449
2450 /*ARGSUSED*/
2451 void
2452 tty_display_nhwindow(window, blocking)
2453 winid window;
2454 boolean blocking; /* with ttys, all windows are blocking */
2455 {
2456     register struct WinDesc *cw = 0;
2457     short s_maxcol;
2458
2459     HUPSKIP();
2460     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2461         panic(winpanicstr, window);
2462     if (cw->flags & WIN_CANCELLED)
2463         return;
2464     ttyDisplay->lastwin = window;
2465     ttyDisplay->rawprint = 0;
2466
2467     print_vt_code2(AVTC_SELECT_WINDOW, window);
2468
2469     switch (cw->type) {
2470     case NHW_MESSAGE:
2471         if (ttyDisplay->toplin == 1) {
2472             more();
2473             ttyDisplay->toplin = 1; /* more resets this */
2474             tty_clear_nhwindow(window);
2475         } else
2476             ttyDisplay->toplin = 0;
2477         cw->curx = cw->cury = 0;
2478         if (!cw->active)
2479             iflags.window_inited = TRUE;
2480         break;
2481     case NHW_MAP:
2482         end_glyphout();
2483         if (blocking) {
2484             if (!ttyDisplay->toplin)
2485                 ttyDisplay->toplin = 1;
2486             tty_display_nhwindow(WIN_MESSAGE, TRUE);
2487             return;
2488         }
2489         /*FALLTHRU*/
2490     case NHW_BASE:
2491         (void) fflush(stdout);
2492         break;
2493     case NHW_TEXT:
2494         cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2495         /*FALLTHRU*/
2496     case NHW_MENU:
2497         cw->active = 1;
2498         /* cw->maxcol is a long, but its value is constrained to
2499            be <= ttyDisplay->cols, so is sure to fit within a short */
2500         s_maxcol = (short) cw->maxcol;
2501 #ifdef H2344_BROKEN
2502         cw->offx = (cw->type == NHW_TEXT)
2503                        ? 0
2504                        : min(min(82, ttyDisplay->cols / 2),
2505                              ttyDisplay->cols - s_maxcol - 1);
2506 #else
2507         /* avoid converting to uchar before calculations are finished */
2508         cw->offx = (uchar) max((int) 10,
2509                                (int) (ttyDisplay->cols - s_maxcol - 1));
2510 #endif
2511         if (cw->offx < 0)
2512             cw->offx = 0;
2513         if (cw->type == NHW_MENU)
2514             cw->offy = 0;
2515         if (ttyDisplay->toplin == 1)
2516             tty_display_nhwindow(WIN_MESSAGE, TRUE);
2517 #ifdef H2344_BROKEN
2518         if (cw->maxrow >= (int) ttyDisplay->rows
2519             || !iflags.menu_overlay)
2520 #else
2521         if (cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows
2522             || !iflags.menu_overlay)
2523 #endif
2524         {
2525             cw->offx = 0;
2526             if (cw->offy || iflags.menu_overlay) {
2527                 tty_curs(window, 1, 0);
2528                 cl_eos();
2529             } else
2530                 clear_screen();
2531             ttyDisplay->toplin = 0;
2532         } else {
2533             if (WIN_MESSAGE != WIN_ERR)
2534                 tty_clear_nhwindow(WIN_MESSAGE);
2535         }
2536
2537         if (cw->data || !cw->maxrow)
2538             process_text_window(window, cw);
2539         else
2540             process_menu_window(window, cw);
2541         break;
2542     }
2543     cw->active = 1;
2544 }
2545
2546 void
2547 tty_dismiss_nhwindow(window)
2548 winid window;
2549 {
2550     register struct WinDesc *cw = 0;
2551
2552     HUPSKIP();
2553     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2554         panic(winpanicstr, window);
2555
2556     print_vt_code2(AVTC_SELECT_WINDOW, window);
2557
2558     switch (cw->type) {
2559     case NHW_MESSAGE:
2560         if (ttyDisplay->toplin)
2561             tty_display_nhwindow(WIN_MESSAGE, TRUE);
2562         /*FALLTHRU*/
2563     case NHW_STATUS:
2564     case NHW_BASE:
2565     case NHW_MAP:
2566         /*
2567          * these should only get dismissed when the game is going away
2568          * or suspending
2569          */
2570         tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->rows - 1);
2571         cw->active = 0;
2572         break;
2573     case NHW_MENU:
2574     case NHW_TEXT:
2575         if (cw->active) {
2576             if (iflags.window_inited) {
2577                 /* otherwise dismissing the text endwin after other windows
2578                  * are dismissed tries to redraw the map and panics.  since
2579                  * the whole reason for dismissing the other windows was to
2580                  * leave the ending window on the screen, we don't want to
2581                  * erase it anyway.
2582                  */
2583                 erase_menu_or_text(window, cw, FALSE);
2584             }
2585             cw->active = 0;
2586         }
2587         break;
2588     }
2589     cw->flags = 0;
2590 }
2591
2592 void
2593 tty_destroy_nhwindow(window)
2594 winid window;
2595 {
2596     register struct WinDesc *cw = 0;
2597
2598     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2599         panic(winpanicstr, window);
2600
2601     if (cw->active)
2602         tty_dismiss_nhwindow(window);
2603     if (cw->type == NHW_MESSAGE)
2604         iflags.window_inited = 0;
2605     if (cw->type == NHW_MAP)
2606         clear_screen();
2607
2608     free_window_info(cw, TRUE);
2609     free((genericptr_t) cw);
2610     wins[window] = 0; /* available for re-use */
2611 }
2612
2613 void
2614 tty_curs(window, x, y)
2615 winid window;
2616 register int x, y; /* not xchar: perhaps xchar is unsigned and
2617                       curx-x would be unsigned as well */
2618 {
2619     struct WinDesc *cw = 0;
2620     int cx = ttyDisplay->curx;
2621     int cy = ttyDisplay->cury;
2622
2623     HUPSKIP();
2624     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2625         panic(winpanicstr, window);
2626     ttyDisplay->lastwin = window;
2627
2628     print_vt_code2(AVTC_SELECT_WINDOW, window);
2629
2630 #if defined(USE_TILES) && defined(MSDOS)
2631     adjust_cursor_flags(cw);
2632 #endif
2633     cw->curx = --x; /* column 0 is never used */
2634     cw->cury = y;
2635 #ifdef DEBUG
2636     if (x < 0 || y < 0 || y >= cw->rows || x > cw->cols) {
2637         const char *s = "[unknown type]";
2638
2639         switch (cw->type) {
2640         case NHW_MESSAGE:
2641             s = "[topl window]";
2642             break;
2643         case NHW_STATUS:
2644             s = "[status window]";
2645             break;
2646         case NHW_MAP:
2647             s = "[map window]";
2648             break;
2649         case NHW_MENU:
2650             s = "[corner window]";
2651             break;
2652         case NHW_TEXT:
2653             s = "[text window]";
2654             break;
2655         case NHW_BASE:
2656             s = "[base window]";
2657             break;
2658         }
2659         debugpline4("bad curs positioning win %d %s (%d,%d)", window, s, x, y);
2660         /* This return statement caused a functional difference between
2661            DEBUG and non-DEBUG operation, so it is being commented
2662            out. It caused tty_curs() to fail to move the cursor to the
2663            location it needed to be if the x,y range checks failed,
2664            leaving the next piece of output to be displayed at whatever
2665            random location the cursor happened to be at prior. */
2666
2667         /* return; */
2668     }
2669 #endif
2670     x += cw->offx;
2671     y += cw->offy;
2672
2673 #ifdef CLIPPING
2674     if (clipping && window == WIN_MAP) {
2675         x -= clipx;
2676         y -= clipy;
2677     }
2678 #endif
2679
2680     if (y == cy && x == cx)
2681         return;
2682
2683     if (cw->type == NHW_MAP)
2684         end_glyphout();
2685
2686 #ifndef NO_TERMS
2687     if (!nh_ND && (cx != x || x <= 3)) { /* Extremely primitive */
2688         cmov(x, y);                      /* bunker!wtm */
2689         return;
2690     }
2691 #endif
2692
2693     if ((cy -= y) < 0)
2694         cy = -cy;
2695     if ((cx -= x) < 0)
2696         cx = -cx;
2697     if (cy <= 3 && cx <= 3) {
2698         nocmov(x, y);
2699 #ifndef NO_TERMS
2700     } else if ((x <= 3 && cy <= 3) || (!nh_CM && x < cx)) {
2701 # if 0 /*JP*/
2702         (void) putchar('\r');
2703 # else
2704         (void) cputchar('\r');
2705 # endif
2706         ttyDisplay->curx = 0;
2707         nocmov(x, y);
2708     } else if (!nh_CM) {
2709         nocmov(x, y);
2710 #endif
2711     } else
2712         cmov(x, y);
2713
2714     ttyDisplay->curx = x;
2715     ttyDisplay->cury = y;
2716 }
2717
2718 STATIC_OVL void
2719 tty_putsym(window, x, y, ch)
2720 winid window;
2721 int x, y;
2722 char ch;
2723 {
2724     register struct WinDesc *cw = 0;
2725
2726     HUPSKIP();
2727     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2728         panic(winpanicstr, window);
2729
2730     print_vt_code2(AVTC_SELECT_WINDOW, window);
2731
2732     switch (cw->type) {
2733 #ifndef STATUS_HILITES
2734     case NHW_STATUS:
2735 #endif
2736     case NHW_MAP:
2737     case NHW_BASE:
2738         tty_curs(window, x, y);
2739 #if 0 /*JP*/
2740         (void) putchar(ch);
2741 #else
2742         if(cw->type!=NHW_MAP)
2743           (void) jputchar(ch);
2744         else {
2745           (void) cputchar(ch);
2746         }
2747 #endif
2748         ttyDisplay->curx++;
2749         cw->curx++;
2750         break;
2751     case NHW_MESSAGE:
2752     case NHW_MENU:
2753     case NHW_TEXT:
2754         impossible("Can't putsym to window type %d", cw->type);
2755         break;
2756     }
2757 }
2758
2759 STATIC_OVL const char *
2760 compress_str(str)
2761 const char *str;
2762 {
2763     static char cbuf[BUFSZ];
2764
2765     /* compress out consecutive spaces if line is too long;
2766        topline wrapping converts space at wrap point into newline,
2767        we reverse that here */
2768     if ((int) strlen(str) >= CO || index(str, '\n')) {
2769         const char *in_str = str;
2770         char c, *outstr = cbuf, *outend = &cbuf[sizeof cbuf - 1];
2771         boolean was_space = TRUE; /* True discards all leading spaces;
2772                                      False would retain one if present */
2773
2774         while ((c = *in_str++) != '\0' && outstr < outend) {
2775             if (c == '\n')
2776                 c = ' ';
2777             if (was_space && c == ' ')
2778                 continue;
2779             *outstr++ = c;
2780             was_space = (c == ' ');
2781         }
2782         if ((was_space && outstr > cbuf) || outstr == outend)
2783             --outstr; /* remove trailing space or make room for terminator */
2784         *outstr = '\0';
2785         str = cbuf;
2786     }
2787     return str;
2788 }
2789
2790 void
2791 tty_putstr(window, attr, str)
2792 winid window;
2793 int attr;
2794 const char *str;
2795 {
2796     register struct WinDesc *cw = 0;
2797     register char *ob;
2798     register long i, n0;
2799 #ifndef STATUS_HILITES
2800     register const char *nb;
2801     register long j;
2802 #endif
2803 #if 1 /*JP*/
2804     int kchar2 = 0;             /* if 1, kanji 2nd byte */
2805 #endif
2806
2807     HUPSKIP();
2808     /* Assume there's a real problem if the window is missing --
2809      * probably a panic message
2810      */
2811 #if 1 /*JP*/
2812     jputchar('\0');     /* RESET */
2813 #endif
2814
2815     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) {
2816         tty_raw_print(str);
2817         return;
2818     }
2819
2820     if (str == (const char *) 0
2821         || ((cw->flags & WIN_CANCELLED) && (cw->type != NHW_MESSAGE)))
2822         return;
2823     if (cw->type != NHW_MESSAGE)
2824         str = compress_str(str);
2825
2826     ttyDisplay->lastwin = window;
2827
2828     print_vt_code2(AVTC_SELECT_WINDOW, window);
2829
2830     switch (cw->type) {
2831     case NHW_MESSAGE: {
2832         int suppress_history = (attr & ATR_NOHISTORY);
2833
2834         /* in case we ever support display attributes for topline
2835            messages, clear flag mask leaving only display attr */
2836         /*attr &= ~(ATR_URGENT | ATR_NOHISTORY);*/
2837
2838         /* really do this later */
2839 #if defined(USER_SOUNDS) && defined(WIN32CON)
2840         play_sound_for_message(str);
2841 #endif
2842         if (!suppress_history) {
2843             /* normal output; add to current top line if room, else flush
2844                whatever is there to history and then write this */
2845             update_topl(str);
2846         } else {
2847             /* put anything already on top line into history */
2848             remember_topl();
2849             /* write to top line without remembering what we're writing */
2850             show_topl(str);
2851         }
2852         break;
2853     }
2854 #ifndef STATUS_HILITES
2855     case NHW_STATUS:
2856         ob = &cw->data[cw->cury][j = cw->curx];
2857         if (context.botlx)
2858             *ob = '\0';
2859         if (!cw->cury && (int) strlen(str) >= CO) {
2860             /* the characters before "St:" are unnecessary */
2861             nb = index(str, ':');
2862             if (nb && nb > str + 2)
2863                 str = nb - 2;
2864         }
2865         nb = str;
2866         for (i = cw->curx + 1, n0 = cw->cols; i < n0; i++, nb++) {
2867             if (!*nb) {
2868                 if (*ob || context.botlx) {
2869                     /* last char printed may be in middle of line */
2870                     tty_curs(WIN_STATUS, i, cw->cury);
2871                     cl_end();
2872                 }
2873                 break;
2874             }
2875 #if 0 /*JP*/
2876 #else /* this code updates all status line at any time */
2877 # if 0
2878 /* check 2-bytes character for Japanese */
2879 /* by issei 93/12/2                     */
2880             uc = *((unsigned char *)nb);
2881             if((!(uc & 0x80) && *ob != *nb) || kflg){
2882               tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2883             }
2884             else{
2885               if(*ob != *nb || *(ob+1)!= *(nb+1)){
2886                 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2887                 kflg = 1;
2888               }
2889             }
2890 # endif /* 0 */
2891 #define ismbchar(c)     (((unsigned char)(c)) & 0x80)
2892 #define KANJI2  1
2893 #define KUPDATE 2
2894             if (kchar2)                 /* kanji 2nd byte */
2895             {
2896                 if (kchar2 & KUPDATE)
2897                     tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2898                 kchar2 = 0;
2899             }
2900             else if (ismbchar(*nb))     /* kanji 1st byte */
2901             {
2902                 kchar2 = KANJI2;
2903                 /* Kanji char must be checked as 2-bytes pair. */
2904                 /* check i to prevent putting only kanji 1st byte at last. */
2905                 if ((*nb != *ob || *(nb+1) != *(ob+1)) && i < n0-1)
2906                 {
2907                     tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2908                     kchar2 |= KUPDATE;  /* must do update */
2909                 }
2910                 /* else nb is the same char as old, so need not to update */
2911             }
2912             /* not kanji char */
2913             else
2914 #endif
2915             if (*ob != *nb)
2916                 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2917             if (*ob)
2918                 ob++;
2919         }
2920
2921         (void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1);
2922         cw->data[cw->cury][cw->cols - 1] = '\0'; /* null terminate */
2923         cw->cury = (cw->cury + 1) % cw->maxrow;
2924         cw->curx = 0;
2925         break;
2926 #endif /* STATUS_HILITES */
2927     case NHW_MAP:
2928         tty_curs(window, cw->curx + 1, cw->cury);
2929         term_start_attr(attr);
2930         while (*str && (int) ttyDisplay->curx < (int) ttyDisplay->cols - 1) {
2931 #if 0 /*JP*/
2932             (void) putchar(*str);
2933 #else
2934             (void) cputchar(*str);
2935 #endif
2936             str++;
2937             ttyDisplay->curx++;
2938         }
2939         cw->curx = 0;
2940         cw->cury++;
2941         term_end_attr(attr);
2942         break;
2943     case NHW_BASE:
2944         tty_curs(window, cw->curx + 1, cw->cury);
2945         term_start_attr(attr);
2946         while (*str) {
2947             if ((int) ttyDisplay->curx >= (int) ttyDisplay->cols - 1) {
2948                 cw->curx = 0;
2949                 cw->cury++;
2950                 tty_curs(window, cw->curx + 1, cw->cury);
2951             }
2952 #if 0 /*JP*/
2953             (void) putchar(*str);
2954 #else
2955             (void) jputchar(*str);
2956 #endif
2957             str++;
2958             ttyDisplay->curx++;
2959         }
2960         cw->curx = 0;
2961         cw->cury++;
2962         term_end_attr(attr);
2963         break;
2964     case NHW_MENU:
2965     case NHW_TEXT:
2966 #ifdef H2344_BROKEN
2967         if (cw->type == NHW_TEXT
2968             && (cw->cury + cw->offy) == ttyDisplay->rows - 1)
2969 #else
2970         if (cw->type == NHW_TEXT && cw->cury == ttyDisplay->rows - 1)
2971 #endif
2972         {
2973             /* not a menu, so save memory and output 1 page at a time */
2974             cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2975             tty_display_nhwindow(window, TRUE);
2976             for (i = 0; i < cw->maxrow; i++)
2977                 if (cw->data[i]) {
2978                     free((genericptr_t) cw->data[i]);
2979                     cw->data[i] = 0;
2980                 }
2981             cw->maxrow = cw->cury = 0;
2982         }
2983         /* always grows one at a time, but alloc 12 at a time */
2984         if (cw->cury >= cw->rows) {
2985             char **tmp;
2986
2987             cw->rows += 12;
2988             tmp = (char **) alloc(sizeof(char *) * (unsigned) cw->rows);
2989             for (i = 0; i < cw->maxrow; i++)
2990                 tmp[i] = cw->data[i];
2991             if (cw->data)
2992                 free((genericptr_t) cw->data);
2993             cw->data = tmp;
2994
2995             for (i = cw->maxrow; i < cw->rows; i++)
2996                 cw->data[i] = 0;
2997         }
2998         if (cw->data[cw->cury])
2999             free((genericptr_t) cw->data[cw->cury]);
3000         n0 = (long) strlen(str) + 1L;
3001         ob = cw->data[cw->cury] = (char *) alloc((unsigned) n0 + 1);
3002         *ob++ = (char) (attr + 1); /* avoid nuls, for convenience */
3003         Strcpy(ob, str);
3004
3005         if (n0 > cw->maxcol)
3006             cw->maxcol = n0;
3007         if (++cw->cury > cw->maxrow)
3008             cw->maxrow = cw->cury;
3009         if (n0 > CO) {
3010             /* attempt to break the line */
3011             for (i = CO - 1; i && str[i] != ' ' && str[i] != '\n';)
3012                 i--;
3013             if (i) {
3014                 cw->data[cw->cury - 1][++i] = '\0';
3015                 tty_putstr(window, attr, &str[i]);
3016             }
3017         }
3018         break;
3019     }
3020 #if 1 /*JP*/
3021     jputchar('\0');     /* RESET */
3022 #endif
3023 }
3024
3025 void
3026 tty_display_file(fname, complain)
3027 const char *fname;
3028 boolean complain;
3029 {
3030 #ifdef DEF_PAGER /* this implies that UNIX is defined */
3031     {
3032         /* use external pager; this may give security problems */
3033         register int fd = open(fname, 0);
3034
3035         if (fd < 0) {
3036             if (complain)
3037                 pline("Cannot open %s.", fname);
3038             else
3039                 docrt();
3040             return;
3041         }
3042         if (child(1)) {
3043             /* Now that child() does a setuid(getuid()) and a chdir(),
3044                we may not be able to open file fname anymore, so make
3045                it stdin. */
3046             (void) close(0);
3047             if (dup(fd)) {
3048                 if (complain)
3049                     raw_printf("Cannot open %s as stdin.", fname);
3050             } else {
3051                 (void) execlp(catmore, "page", (char *) 0);
3052                 if (complain)
3053                     raw_printf("Cannot exec %s.", catmore);
3054             }
3055             if (complain)
3056                 sleep(10); /* want to wait_synch() but stdin is gone */
3057             nh_terminate(EXIT_FAILURE);
3058         }
3059         (void) close(fd);
3060 #ifdef notyet
3061         winch_seen = 0;
3062 #endif
3063     }
3064 #else /* DEF_PAGER */
3065     {
3066         dlb *f;
3067         char buf[BUFSZ];
3068         char *cr;
3069
3070         tty_clear_nhwindow(WIN_MESSAGE);
3071         f = dlb_fopen(fname, "r");
3072         if (!f) {
3073             if (complain) {
3074                 home();
3075                 tty_mark_synch();
3076                 tty_raw_print("");
3077                 perror(fname);
3078                 tty_wait_synch();
3079                 pline("Cannot open \"%s\".", fname);
3080             } else if (u.ux)
3081                 docrt();
3082         } else {
3083             winid datawin = tty_create_nhwindow(NHW_TEXT);
3084             boolean empty = TRUE;
3085
3086             if (complain
3087 #ifndef NO_TERMS
3088                 && nh_CD
3089 #endif
3090                 ) {
3091                 /* attempt to scroll text below map window if there's room */
3092                 wins[datawin]->offy = wins[WIN_STATUS]->offy + 3;
3093                 if ((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows)
3094                     wins[datawin]->offy = 0;
3095             }
3096             while (dlb_fgets(buf, BUFSZ, f)) {
3097                 if ((cr = index(buf, '\n')) != 0)
3098                     *cr = 0;
3099 #ifdef MSDOS
3100                 if ((cr = index(buf, '\r')) != 0)
3101                     *cr = 0;
3102 #endif
3103                 if (index(buf, '\t') != 0)
3104                     (void) tabexpand(buf);
3105                 empty = FALSE;
3106                 tty_putstr(datawin, 0, buf);
3107                 if (wins[datawin]->flags & WIN_CANCELLED)
3108                     break;
3109             }
3110             if (!empty)
3111                 tty_display_nhwindow(datawin, FALSE);
3112             tty_destroy_nhwindow(datawin);
3113             (void) dlb_fclose(f);
3114         }
3115     }
3116 #endif /* DEF_PAGER */
3117 }
3118
3119 void
3120 tty_start_menu(window)
3121 winid window;
3122 {
3123     tty_clear_nhwindow(window);
3124     return;
3125 }
3126
3127 /*ARGSUSED*/
3128 /*
3129  * Add a menu item to the beginning of the menu list.  This list is reversed
3130  * later.
3131  */
3132 void
3133 tty_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
3134 winid window;               /* window to use, must be of type NHW_MENU */
3135 int glyph UNUSED;           /* glyph to display with item (not used) */
3136 const anything *identifier; /* what to return if selected */
3137 char ch;                    /* keyboard accelerator (0 = pick our own) */
3138 char gch;                   /* group accelerator (0 = no group) */
3139 int attr;                   /* attribute for string (like tty_putstr()) */
3140 const char *str;            /* menu string */
3141 boolean preselected;        /* item is marked as selected */
3142 {
3143     register struct WinDesc *cw = 0;
3144     tty_menu_item *item;
3145     const char *newstr;
3146     char buf[4 + BUFSZ];
3147
3148     HUPSKIP();
3149     if (str == (const char *) 0)
3150         return;
3151
3152     if (window == WIN_ERR
3153         || (cw = wins[window]) == (struct WinDesc *) 0
3154         || cw->type != NHW_MENU)
3155         panic(winpanicstr, window);
3156
3157     cw->nitems++;
3158     if (identifier->a_void) {
3159         int len = (int) strlen(str);
3160
3161         if (len >= BUFSZ) {
3162             /* We *think* everything's coming in off at most BUFSZ bufs... */
3163             impossible("Menu item too long (%d).", len);
3164             len = BUFSZ - 1;
3165         }
3166         Sprintf(buf, "%c - ", ch ? ch : '?');
3167         (void) strncpy(buf + 4, str, len);
3168         buf[4 + len] = '\0';
3169         newstr = buf;
3170     } else
3171         newstr = str;
3172
3173     item = (tty_menu_item *) alloc(sizeof *item);
3174     item->identifier = *identifier;
3175     item->count = -1L;
3176     item->selected = preselected;
3177     item->selector = ch;
3178     item->gselector = gch;
3179     item->attr = attr;
3180     item->str = dupstr(newstr ? newstr : "");
3181
3182     item->next = cw->mlist;
3183     cw->mlist = item;
3184 }
3185
3186 /* Invert the given list, can handle NULL as an input. */
3187 STATIC_OVL tty_menu_item *
3188 reverse(curr)
3189 tty_menu_item *curr;
3190 {
3191     tty_menu_item *next, *head = 0;
3192
3193     while (curr) {
3194         next = curr->next;
3195         curr->next = head;
3196         head = curr;
3197         curr = next;
3198     }
3199     return head;
3200 }
3201
3202 /*
3203  * End a menu in this window, window must a type NHW_MENU.  This routine
3204  * processes the string list.  We calculate the # of pages, then assign
3205  * keyboard accelerators as needed.  Finally we decide on the width and
3206  * height of the window.
3207  */
3208 void
3209 tty_end_menu(window, prompt)
3210 winid window;       /* menu to use */
3211 const char *prompt; /* prompt to for menu */
3212 {
3213     struct WinDesc *cw = 0;
3214     tty_menu_item *curr;
3215     short len;
3216     int lmax, n;
3217     char menu_ch;
3218
3219     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
3220         || cw->type != NHW_MENU)
3221         panic(winpanicstr, window);
3222
3223     /* Reverse the list so that items are in correct order. */
3224     cw->mlist = reverse(cw->mlist);
3225
3226     /* Put the prompt at the beginning of the menu. */
3227     if (prompt) {
3228         anything any;
3229
3230         any = zeroany; /* not selectable */
3231         tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
3232                      MENU_UNSELECTED);
3233         tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt,
3234                      MENU_UNSELECTED);
3235     }
3236
3237     /* 52: 'a'..'z' and 'A'..'Z'; avoids selector duplication within a page */
3238     lmax = min(52, (int) ttyDisplay->rows - 1);    /* # lines per page */
3239     cw->npages = (cw->nitems + (lmax - 1)) / lmax; /* # of pages */
3240     /*
3241      * TODO?
3242      *  For really tall page, allow 53 if '$' or '#' is present and
3243      *  54 if both are.  [Only for single page menu, otherwise pages
3244      *  without those could try to use too many letters.]
3245      *  Probably not worth bothering with; anyone with a display big
3246      *  for this to matter will likely switch from tty to curses for
3247      *  multi-line message window and/or persistent inventory window.
3248      */
3249
3250     /* make sure page list is large enough */
3251     if (cw->plist_size < cw->npages + 1) { /* +1: need one slot beyond last */
3252         if (cw->plist)
3253             free((genericptr_t) cw->plist);
3254         cw->plist_size = cw->npages + 1;
3255         cw->plist = (tty_menu_item **) alloc(cw->plist_size
3256                                              * sizeof (tty_menu_item *));
3257     }
3258
3259     cw->cols = 0;  /* cols is set when the win is initialized... (why?) */
3260     menu_ch = '?'; /* lint suppression */
3261     for (n = 0, curr = cw->mlist; curr; n++, curr = curr->next) {
3262         /* set page boundaries and character accelerators */
3263         if ((n % lmax) == 0) {
3264             menu_ch = 'a';
3265             cw->plist[n / lmax] = curr;
3266         }
3267         if (curr->identifier.a_void && !curr->selector) {
3268             curr->str[0] = curr->selector = menu_ch;
3269             if (menu_ch++ == 'z')
3270                 menu_ch = 'A';
3271         }
3272
3273         /* cut off any lines that are too long */
3274         len = strlen(curr->str) + 2; /* extra space at beg & end */
3275         if (len > (int) ttyDisplay->cols) {
3276             curr->str[ttyDisplay->cols - 2] = 0;
3277             len = ttyDisplay->cols;
3278         }
3279         if (len > cw->cols)
3280             cw->cols = len;
3281     }
3282     cw->plist[cw->npages] = 0; /* plist terminator */
3283
3284     /*
3285      * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
3286      */
3287     if (cw->npages > 1) {
3288         char buf[QBUFSZ];
3289         /* produce the largest demo string */
3290         Sprintf(buf, "(%ld of %ld) ", cw->npages, cw->npages);
3291         len = strlen(buf);
3292         cw->morestr = dupstr("");
3293     } else {
3294         cw->morestr = dupstr("(end) ");
3295         len = strlen(cw->morestr);
3296     }
3297
3298     if (len > (int) ttyDisplay->cols) {
3299         /* truncate the prompt if it's too long for the screen */
3300         if (cw->npages <= 1) /* only str in single page case */
3301             cw->morestr[ttyDisplay->cols] = 0;
3302         len = ttyDisplay->cols;
3303     }
3304     if (len > cw->cols)
3305         cw->cols = len;
3306
3307     cw->maxcol = cw->cols;
3308
3309     /*
3310      * The number of lines in the first page plus the morestr will be the
3311      * maximum size of the window.
3312      */
3313     if (cw->npages > 1)
3314         cw->maxrow = cw->rows = lmax + 1;
3315     else
3316         cw->maxrow = cw->rows = cw->nitems + 1;
3317 }
3318
3319 int
3320 tty_select_menu(window, how, menu_list)
3321 winid window;
3322 int how;
3323 menu_item **menu_list;
3324 {
3325     register struct WinDesc *cw = 0;
3326     tty_menu_item *curr;
3327     menu_item *mi;
3328     int n, cancelled;
3329
3330     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
3331         || cw->type != NHW_MENU)
3332         panic(winpanicstr, window);
3333
3334     *menu_list = (menu_item *) 0;
3335     cw->how = (short) how;
3336     morc = 0;
3337     tty_display_nhwindow(window, TRUE);
3338     cancelled = !!(cw->flags & WIN_CANCELLED);
3339     tty_dismiss_nhwindow(window); /* does not destroy window data */
3340
3341     if (cancelled) {
3342         n = -1;
3343     } else {
3344         for (n = 0, curr = cw->mlist; curr; curr = curr->next)
3345             if (curr->selected)
3346                 n++;
3347     }
3348
3349     if (n > 0) {
3350         *menu_list = (menu_item *) alloc(n * sizeof(menu_item));
3351         for (mi = *menu_list, curr = cw->mlist; curr; curr = curr->next)
3352             if (curr->selected) {
3353                 mi->item = curr->identifier;
3354                 mi->count = curr->count;
3355                 mi++;
3356             }
3357     }
3358
3359     return n;
3360 }
3361
3362 /* special hack for treating top line --More-- as a one item menu */
3363 char
3364 tty_message_menu(let, how, mesg)
3365 char let;
3366 int how;
3367 const char *mesg;
3368 {
3369     HUPSKIP();
3370     /* "menu" without selection; use ordinary pline, no more() */
3371     if (how == PICK_NONE) {
3372         pline("%s", mesg);
3373         return 0;
3374     }
3375
3376     ttyDisplay->dismiss_more = let;
3377     morc = 0;
3378     /* barebones pline(); since we're only supposed to be called after
3379        response to a prompt, we'll assume that the display is up to date */
3380     tty_putstr(WIN_MESSAGE, 0, mesg);
3381     /* if `mesg' didn't wrap (triggering --More--), force --More-- now */
3382     if (ttyDisplay->toplin == 1) {
3383         more();
3384         ttyDisplay->toplin = 1; /* more resets this */
3385         tty_clear_nhwindow(WIN_MESSAGE);
3386     }
3387     /* normally <ESC> means skip further messages, but in this case
3388        it means cancel the current prompt; any other messages should
3389        continue to be output normally */
3390     wins[WIN_MESSAGE]->flags &= ~WIN_CANCELLED;
3391     ttyDisplay->dismiss_more = 0;
3392
3393     return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0';
3394 }
3395
3396 void
3397 tty_update_inventory()
3398 {
3399     return;
3400 }
3401
3402 void
3403 tty_mark_synch()
3404 {
3405     HUPSKIP();
3406     (void) fflush(stdout);
3407 }
3408
3409 void
3410 tty_wait_synch()
3411 {
3412     HUPSKIP();
3413     /* we just need to make sure all windows are synch'd */
3414     if (!ttyDisplay || ttyDisplay->rawprint) {
3415         getret();
3416         if (ttyDisplay)
3417             ttyDisplay->rawprint = 0;
3418     } else {
3419         tty_display_nhwindow(WIN_MAP, FALSE);
3420         if (ttyDisplay->inmore) {
3421             addtopl("--More--");
3422             (void) fflush(stdout);
3423         } else if (ttyDisplay->inread > program_state.gameover) {
3424             /* this can only happen if we were reading and got interrupted */
3425             ttyDisplay->toplin = 3;
3426             /* do this twice; 1st time gets the Quit? message again */
3427             (void) tty_doprev_message();
3428             (void) tty_doprev_message();
3429             ttyDisplay->intr++;
3430             (void) fflush(stdout);
3431         }
3432     }
3433 }
3434
3435 void
3436 docorner(xmin, ymax)
3437 register int xmin, ymax;
3438 {
3439     register int y;
3440     register struct WinDesc *cw = wins[WIN_MAP];
3441
3442     HUPSKIP();
3443 #if 0   /* this optimization is not valuable enough to justify
3444            abusing core internals... */
3445     if (u.uswallow) { /* Can be done more efficiently */
3446         swallowed(1);
3447         /* without this flush, if we happen to follow --More-- displayed in
3448            leftmost column, the cursor gets left in the wrong place after
3449            <docorner<more<update_topl<tty_putstr calls unwind back to core */
3450         flush_screen(0);
3451         return;
3452     }
3453 #endif /*0*/
3454
3455 #if defined(SIGWINCH) && defined(CLIPPING)
3456     if (ymax > LI)
3457         ymax = LI; /* can happen if window gets smaller */
3458 #endif
3459     for (y = 0; y < ymax; y++) {
3460         tty_curs(BASE_WINDOW, xmin, y); /* move cursor */
3461         cl_end();                       /* clear to end of line */
3462 #ifdef CLIPPING
3463         if (y < (int) cw->offy || y + clipy > ROWNO)
3464             continue; /* only refresh board */
3465 #if defined(USE_TILES) && defined(MSDOS)
3466         if (iflags.tile_view)
3467             row_refresh((xmin / 2) + clipx - ((int) cw->offx / 2), COLNO - 1,
3468                         y + clipy - (int) cw->offy);
3469         else
3470 #endif
3471             row_refresh(xmin + clipx - (int) cw->offx, COLNO - 1,
3472                         y + clipy - (int) cw->offy);
3473 #else
3474         if (y < cw->offy || y > ROWNO)
3475             continue; /* only refresh board  */
3476         row_refresh(xmin - (int) cw->offx, COLNO - 1, y - (int) cw->offy);
3477 #endif
3478     }
3479
3480     end_glyphout();
3481     if (ymax >= (int) wins[WIN_STATUS]->offy) {
3482         /* we have wrecked the bottom line */
3483         context.botlx = 1;
3484         bot();
3485     }
3486 }
3487
3488 void
3489 end_glyphout()
3490 {
3491     HUPSKIP();
3492 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3493     if (GFlag) {
3494         GFlag = FALSE;
3495         graph_off();
3496     }
3497 #endif
3498 #ifdef TEXTCOLOR
3499     if (ttyDisplay->color != NO_COLOR) {
3500         term_end_color();
3501         ttyDisplay->color = NO_COLOR;
3502     }
3503 #endif
3504 }
3505
3506 #ifndef WIN32
3507 void
3508 g_putch(in_ch)
3509 int in_ch;
3510 {
3511     register char ch = (char) in_ch;
3512
3513     HUPSKIP();
3514 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3515     if (SYMHANDLING(H_IBM)
3516         /* for DECgraphics, lower-case letters with high bit set mean
3517            switch character set and render with high bit clear;
3518            user might want 8-bits for other characters */
3519         || (iflags.eight_bit_tty && (!SYMHANDLING(H_DEC)
3520                                      || (in_ch & 0x7f) < 0x60))) {
3521         /* IBM-compatible displays don't need other stuff */
3522 #   if 0 /*JP*/
3523         (void) putchar(ch);
3524 #   else
3525         (void) cputchar(ch);
3526 #   endif
3527     } else if (ch & 0x80) {
3528         if (!GFlag || HE_resets_AS) {
3529             graph_on();
3530             GFlag = TRUE;
3531         }
3532 # if 0 /*JP*/
3533         (void) putchar((ch ^ 0x80)); /* Strip 8th bit */
3534 # else
3535         (void) cputchar((ch ^ 0x80)); /* Strip 8th bit */
3536 # endif
3537     } else {
3538         if (GFlag) {
3539             graph_off();
3540             GFlag = FALSE;
3541         }
3542 # if 0 /*JP*/
3543         (void) putchar(ch);
3544 # else
3545         (void) jputchar(ch);
3546 # endif
3547     }
3548
3549 #else
3550     (void) putchar(ch);
3551
3552 #endif /* ASCIIGRAPH && !NO_TERMS */
3553
3554     return;
3555 }
3556 #endif /* !WIN32 */
3557
3558 #ifdef CLIPPING
3559 void
3560 setclipped()
3561 {
3562     clipping = TRUE;
3563     clipx = clipy = 0;
3564     clipxmax = CO;
3565     clipymax = LI - 1 - iflags.wc2_statuslines;
3566 }
3567
3568 void
3569 tty_cliparound(x, y)
3570 int x, y;
3571 {
3572     int oldx = clipx, oldy = clipy;
3573
3574     HUPSKIP();
3575     if (!clipping)
3576         return;
3577     if (x < clipx + 5) {
3578         clipx = max(0, x - 20);
3579         clipxmax = clipx + CO;
3580     } else if (x > clipxmax - 5) {
3581         clipxmax = min(COLNO, clipxmax + 20);
3582         clipx = clipxmax - CO;
3583     }
3584     if (y < clipy + 2) {
3585         clipy = max(0, y - (clipymax - clipy) / 2);
3586         clipymax = clipy + (LI - 1 - iflags.wc2_statuslines);
3587     } else if (y > clipymax - 2) {
3588         clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2);
3589         clipy = clipymax - (LI - 1 - iflags.wc2_statuslines);
3590     }
3591     if (clipx != oldx || clipy != oldy) {
3592         redraw_map(); /* ask the core to resend the map window's data */
3593     }
3594 }
3595 #endif /* CLIPPING */
3596
3597 /*
3598  *  tty_print_glyph
3599  *
3600  *  Print the glyph to the output device.  Don't flush the output device.
3601  *
3602  *  Since this is only called from show_glyph(), it is assumed that the
3603  *  position and glyph are always correct (checked there)!
3604  */
3605
3606 void
3607 tty_print_glyph(window, x, y, glyph, bkglyph)
3608 winid window;
3609 xchar x, y;
3610 int glyph;
3611 int bkglyph UNUSED;
3612 {
3613     int ch;
3614     boolean reverse_on = FALSE;
3615     int color;
3616     unsigned special;
3617
3618     HUPSKIP();
3619 #ifdef CLIPPING
3620     if (clipping) {
3621         if (x <= clipx || y < clipy || x >= clipxmax || y >= clipymax)
3622             return;
3623     }
3624 #endif
3625     /* map glyph to character and color */
3626     (void) mapglyph(glyph, &ch, &color, &special, x, y, 0);
3627
3628     print_vt_code2(AVTC_SELECT_WINDOW, window);
3629
3630     /* Move the cursor. */
3631     tty_curs(window, x, y);
3632
3633     print_vt_code3(AVTC_GLYPH_START, glyph2tile[glyph], special);
3634
3635 #ifndef NO_TERMS
3636     if (ul_hack && ch == '_') { /* non-destructive underscore */
3637 # if 0 /*JP*/
3638         (void) putchar((char) ' ');
3639 # else
3640         (void) cputchar((char) ' ');
3641 # endif
3642         backsp();
3643     }
3644 #endif
3645
3646 #ifdef TEXTCOLOR
3647     if (color != ttyDisplay->color) {
3648         if (ttyDisplay->color != NO_COLOR)
3649             term_end_color();
3650         ttyDisplay->color = color;
3651         if (color != NO_COLOR)
3652             term_start_color(color);
3653     }
3654 #endif /* TEXTCOLOR */
3655
3656     /* must be after color check; term_end_color may turn off inverse too */
3657     if (((special & MG_PET) && iflags.hilite_pet)
3658         || ((special & MG_OBJPILE) && iflags.hilite_pile)
3659         || ((special & MG_DETECT) && iflags.use_inverse)
3660         || ((special & MG_BW_LAVA) && iflags.use_inverse)) {
3661         term_start_attr(ATR_INVERSE);
3662         reverse_on = TRUE;
3663     }
3664
3665 #if defined(USE_TILES) && defined(MSDOS)
3666     if (iflags.grmode && iflags.tile_view)
3667         xputg(glyph, ch, special);
3668     else
3669 #endif
3670         g_putch(ch); /* print the character */
3671
3672     if (reverse_on) {
3673         term_end_attr(ATR_INVERSE);
3674 #ifdef TEXTCOLOR
3675         /* turn off color as well, ATR_INVERSE may have done this already */
3676         if (ttyDisplay->color != NO_COLOR) {
3677             term_end_color();
3678             ttyDisplay->color = NO_COLOR;
3679         }
3680 #endif
3681     }
3682
3683     print_vt_code1(AVTC_GLYPH_END);
3684
3685     wins[window]->curx++; /* one character over */
3686     ttyDisplay->curx++;   /* the real cursor moved too */
3687 }
3688
3689 void
3690 tty_raw_print(str)
3691 const char *str;
3692 {
3693     HUPSKIP();
3694     if (ttyDisplay)
3695         ttyDisplay->rawprint++;
3696     print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3697 #if defined(MICRO) || defined(WIN32CON)
3698     msmsg("%s\n", str);
3699 #else
3700 # if 0 /*JP*/
3701     puts(str);
3702 # else
3703     jputs(str);
3704 # endif
3705     (void) fflush(stdout);
3706 #endif
3707 }
3708
3709 void
3710 tty_raw_print_bold(str)
3711 const char *str;
3712 {
3713     HUPSKIP();
3714     if (ttyDisplay)
3715         ttyDisplay->rawprint++;
3716     print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3717     term_start_raw_bold();
3718 #if defined(MICRO) || defined(WIN32CON)
3719     msmsg("%s", str);
3720 #else
3721 # if 0 /*JP*/
3722     (void) fputs(str, stdout);
3723 # else
3724     (void) jputs(str);
3725 # endif
3726 #endif
3727     term_end_raw_bold();
3728 #if defined(MICRO) || defined(WIN32CON)
3729     msmsg("\n");
3730 #else
3731 # if 0 /*JP*/
3732     puts("");
3733 # endif
3734     (void) fflush(stdout);
3735 #endif
3736 }
3737
3738 int
3739 tty_nhgetch()
3740 {
3741     int i;
3742 #ifdef UNIX
3743     /* kludge alert: Some Unix variants return funny values if getc()
3744      * is called, interrupted, and then called again.  There
3745      * is non-reentrant code in the internal _filbuf() routine, called by
3746      * getc().
3747      */
3748     static volatile int nesting = 0;
3749     char nestbuf;
3750 #endif
3751
3752     HUPSKIP_RESULT('\033');
3753     print_vt_code1(AVTC_INLINE_SYNC);
3754     (void) fflush(stdout);
3755     /* Note: if raw_print() and wait_synch() get called to report terminal
3756      * initialization problems, then wins[] and ttyDisplay might not be
3757      * available yet.  Such problems will probably be fatal before we get
3758      * here, but validate those pointers just in case...
3759      */
3760     if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3761         wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3762     if (iflags.debug_fuzzer) {
3763         i = randomkey();
3764     } else {
3765 #ifdef UNIX
3766         i = (++nesting == 1)
3767               ? tgetch()
3768               : (read(fileno(stdin), (genericptr_t) &nestbuf, 1) == 1)
3769                   ? (int) nestbuf : EOF;
3770         --nesting;
3771 #else
3772         i = tgetch();
3773 #endif
3774     }
3775     if (!i)
3776         i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
3777     else if (i == EOF)
3778         i = '\033'; /* same for EOF */
3779     if (ttyDisplay && ttyDisplay->toplin == 1)
3780         ttyDisplay->toplin = 2;
3781 #ifdef TTY_TILES_ESCCODES
3782     {
3783         /* hack to force output of the window select code */
3784         int tmp = vt_tile_current_window;
3785
3786         vt_tile_current_window++;
3787         print_vt_code2(AVTC_SELECT_WINDOW, tmp);
3788     }
3789 #endif /* TTY_TILES_ESCCODES */
3790     return i;
3791 }
3792
3793 /*
3794  * return a key, or 0, in which case a mouse button was pressed
3795  * mouse events should be returned as character postitions in the map window.
3796  * Since normal tty's don't have mice, just return a key.
3797  */
3798 /*ARGSUSED*/
3799 int
3800 tty_nh_poskey(x, y, mod)
3801 int *x, *y, *mod;
3802 {
3803     int i;
3804
3805     HUPSKIP_RESULT('\033');
3806 #if defined(WIN32CON)
3807     (void) fflush(stdout);
3808     /* Note: if raw_print() and wait_synch() get called to report terminal
3809      * initialization problems, then wins[] and ttyDisplay might not be
3810      * available yet.  Such problems will probably be fatal before we get
3811      * here, but validate those pointers just in case...
3812      */
3813     if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3814         wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3815     i = ntposkey(x, y, mod);
3816     if (!i && mod && (*mod == 0 || *mod == EOF))
3817         i = '\033'; /* map NUL or EOF to ESC, nethack doesn't expect either */
3818     if (ttyDisplay && ttyDisplay->toplin == 1)
3819         ttyDisplay->toplin = 2;
3820 #else /* !WIN32CON */
3821     nhUse(x);
3822     nhUse(y);
3823     nhUse(mod);
3824
3825     i = tty_nhgetch();
3826 #endif /* ?WIN32CON */
3827     return i;
3828 }
3829
3830 void
3831 win_tty_init(dir)
3832 int dir;
3833 {
3834     if (dir != WININIT)
3835         return;
3836     return;
3837 }
3838
3839 #ifdef POSITIONBAR
3840 void
3841 tty_update_positionbar(posbar)
3842 char *posbar;
3843 {
3844 #ifdef MSDOS
3845     video_update_positionbar(posbar);
3846 #endif
3847 }
3848 #endif /* POSITIONBAR */
3849
3850
3851 /*
3852  * +------------------+
3853  * |  STATUS CODE     |
3854  * +------------------+
3855  */
3856
3857 /*
3858  * ----------------------------------------------------------------
3859  * tty_status_update
3860  *
3861  *      Called from the NetHack core and receives info hand-offs
3862  *      from the core, processes them, storing some information
3863  *      for rendering to the tty.
3864  *          -> make_things_fit()
3865  *          -> render_status()
3866  *
3867  * make_things_fit
3868  *
3869  *      Called from tty_status_update(). It calls check_fields()
3870  *      (described next) to get the required number of columns
3871  *      and tries a few different ways to squish a representation
3872  *      of the status window values onto the 80 column tty display.
3873  *          ->check_fields()
3874  *          ->set_condition_length()  - update the width of conditions
3875  *          ->shrink_enc()      - shrink encumbrance message word
3876  *          ->shrink_dlvl()     - reduce the width of Dlvl:42
3877  *
3878  *  check_fields
3879  *
3880  *      Verifies that all the fields are ready for display, and
3881  *      returns FALSE if called too early in the startup
3882  *      processing sequence. It also figures out where everything
3883  *      needs to go, taking the current shrinking attempts into
3884  *      account. It returns number of columns needed back to
3885  *      make_things_fit(), so make_things_fit() can make attempt
3886  *      to make adjustments.
3887  *
3888  *  render_status
3889  *
3890  *      Goes through each of the status row's fields and
3891  *      calls tty_putstatusfield() to place them on the display.
3892  *          ->tty_putstatusfield()
3893  *      At the end of the for-loop, the NOW values get copied
3894  *      to BEFORE values.
3895  *
3896  *  tty_putstatusfield()
3897  *
3898  *      Move the cursor to the target spot, and output the field
3899  *      asked for by render_status().
3900  *
3901  * ----------------------------------------------------------------
3902  */
3903
3904 /*
3905  * The following data structures come from the genl_ routines in
3906  * src/windows.c and as such are considered to be on the window-port
3907  * "side" of things, rather than the NetHack-core "side" of things.
3908  */
3909 extern const char *status_fieldfmt[MAXBLSTATS];
3910 extern char *status_vals[MAXBLSTATS];
3911 extern boolean status_activefields[MAXBLSTATS];
3912 extern winid WIN_STATUS;
3913
3914 #ifdef STATUS_HILITES
3915 #ifdef TEXTCOLOR
3916 STATIC_DCL int FDECL(condcolor, (long, unsigned long *));
3917 #endif
3918 STATIC_DCL int FDECL(condattr, (long, unsigned long *));
3919 static unsigned long *tty_colormasks;
3920 static long tty_condition_bits;
3921 static struct tty_status_fields tty_status[2][MAXBLSTATS]; /* 2: NOW,BEFORE */
3922 static int hpbar_percent, hpbar_color;
3923 static struct condition_t {
3924     long mask;
3925     const char *text[3]; /* 3: potential display vals, progressively shorter */
3926 } conditions[] = {
3927     /* The sequence order of these matters */
3928     { BL_MASK_STONE,    { "Stone",    "Ston",  "Sto" } },
3929     { BL_MASK_SLIME,    { "Slime",    "Slim",  "Slm" } },
3930     { BL_MASK_STRNGL,   { "Strngl",   "Stngl", "Str" } },
3931     { BL_MASK_FOODPOIS, { "FoodPois", "Fpois", "Poi" } },
3932     { BL_MASK_TERMILL,  { "TermIll" , "Ill",   "Ill" } },
3933     { BL_MASK_BLIND,    { "Blind",    "Blnd",  "Bl"  } },
3934     { BL_MASK_DEAF,     { "Deaf",     "Def",   "Df"  } },
3935     { BL_MASK_STUN,     { "Stun",     "Stun",  "St"  } },
3936     { BL_MASK_CONF,     { "Conf",     "Cnf",   "Cf"  } },
3937     { BL_MASK_HALLU,    { "Hallu",    "Hal",   "Hl"  } },
3938     { BL_MASK_LEV,      { "Lev",      "Lev",   "Lv"  } },
3939     { BL_MASK_FLY,      { "Fly",      "Fly",   "Fl"  } },
3940     { BL_MASK_RIDE,     { "Ride",     "Rid",   "Rd"  } },
3941 };
3942 static const char *encvals[3][6] = {
3943     { "", "Burdened", "Stressed", "Strained", "Overtaxed", "Overloaded" },
3944     { "", "Burden",   "Stress",   "Strain",   "Overtax",   "Overload"   },
3945     { "", "Brd",      "Strs",     "Strn",     "Ovtx",      "Ovld"       }
3946 };
3947 #define blPAD BL_FLUSH
3948 #define MAX_PER_ROW 15
3949 /* 2 or 3 status lines */
3950 static const enum statusfields
3951     twolineorder[3][MAX_PER_ROW] = {
3952     { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN,
3953       BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD },
3954     { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
3955       BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER,
3956       BL_CAP, BL_CONDITION, BL_FLUSH },
3957     /* third row of array isn't used for twolineorder */
3958     { BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD,
3959       blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }
3960 },
3961     /* Align moved from 1 to 2, Leveldesc+Time+Condition moved from 2 to 3 */
3962     threelineorder[3][MAX_PER_ROW] = {
3963     { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3964       BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD },
3965     { BL_ALIGN, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
3966       BL_AC, BL_XP, BL_EXP, BL_HD, BL_HUNGER,
3967       BL_CAP, BL_FLUSH, blPAD, blPAD },
3968     { BL_LEVELDESC, BL_TIME, BL_CONDITION, BL_FLUSH, blPAD, blPAD,
3969       blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }
3970 };
3971 static const enum statusfields (*fieldorder)[MAX_PER_ROW];
3972
3973 static int finalx[3][2];    /* [rows][NOW or BEFORE] */
3974 static boolean windowdata_init = FALSE;
3975 static int cond_shrinklvl = 0;
3976 static int enclev = 0, enc_shrinklvl = 0;
3977 static int dlvl_shrinklvl = 0;
3978 static boolean truncation_expected = FALSE;
3979 #define FORCE_RESET TRUE
3980 #define NO_RESET FALSE
3981
3982 /* This controls whether to skip fields that aren't
3983  * flagged as requiring updating during the current
3984  * render_status().
3985  *
3986  * Hopefully that can be confirmed as working correctly
3987  * for all platforms eventually and the conditional
3988  * setting below can be removed.
3989  */
3990 static int do_field_opt =
3991 #if defined(DISABLE_TTY_FIELD_OPT)
3992     0;
3993 #else
3994     1;
3995 #endif
3996
3997 #endif  /* STATUS_HILITES */
3998
3999 /*
4000  *  tty_status_init()
4001  *      -- initialize the tty-specific data structures.
4002  *      -- call genl_status_init() to initialize the general data.
4003  */
4004 void
4005 tty_status_init()
4006 {
4007 #ifdef STATUS_HILITES
4008     int i, num_rows;
4009
4010     num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4011     fieldorder = (num_rows != 3) ? twolineorder : threelineorder;
4012
4013     for (i = 0; i < MAXBLSTATS; ++i) {
4014         tty_status[NOW][i].idx = BL_FLUSH;
4015         tty_status[NOW][i].color = NO_COLOR; /* no color */
4016         tty_status[NOW][i].attr = ATR_NONE;
4017         tty_status[NOW][i].x = tty_status[NOW][i].y = 0;
4018         tty_status[NOW][i].valid  = FALSE;
4019         tty_status[NOW][i].dirty  = FALSE;
4020         tty_status[NOW][i].redraw = FALSE;
4021         tty_status[NOW][i].sanitycheck = FALSE;
4022         tty_status[BEFORE][i] = tty_status[NOW][i];
4023     }
4024     tty_condition_bits = 0L;
4025     hpbar_percent = 0, hpbar_color = NO_COLOR;
4026 #endif /* STATUS_HILITES */
4027
4028     /* let genl_status_init do most of the initialization */
4029     genl_status_init();
4030 }
4031
4032 void
4033 tty_status_enablefield(fieldidx, nm, fmt, enable)
4034 int fieldidx;
4035 const char *nm;
4036 const char *fmt;
4037 boolean enable;
4038 {
4039     genl_status_enablefield(fieldidx, nm, fmt, enable);
4040 }
4041
4042 #ifdef STATUS_HILITES
4043
4044 /*
4045  *  *_status_update()
4046  *      -- update the value of a status field.
4047  *      -- the fldindex identifies which field is changing and
4048  *         is an integer index value from botl.h
4049  *      -- fldindex could be any one of the following from botl.h:
4050  *         BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
4051  *         BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
4052  *         BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
4053  *         BL_LEVELDESC, BL_EXP, BL_CONDITION
4054  *      -- fldindex could also be BL_FLUSH (-1), which is not really
4055  *         a field index, but is a special trigger to tell the
4056  *         windowport that it should output all changes received
4057  *         to this point. It marks the end of a bot() cycle.
4058  *      -- fldindex could also be BL_RESET (-3), which is not really
4059  *         a field index, but is a special advisory to to tell the
4060  *         windowport that it should redisplay all its status fields,
4061  *         even if no changes have been presented to it.
4062  *      -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
4063  *         If fldindex is BL_CONDITION, then ptr is a long value with
4064  *         any or none of the following bits set (from botl.h):
4065  *              BL_MASK_STONE           0x00000001L
4066  *              BL_MASK_SLIME           0x00000002L
4067  *              BL_MASK_STRNGL          0x00000004L
4068  *              BL_MASK_FOODPOIS        0x00000008L
4069  *              BL_MASK_TERMILL         0x00000010L
4070  *              BL_MASK_BLIND           0x00000020L
4071  *              BL_MASK_DEAF            0x00000040L
4072  *              BL_MASK_STUN            0x00000080L
4073  *              BL_MASK_CONF            0x00000100L
4074  *              BL_MASK_HALLU           0x00000200L
4075  *              BL_MASK_LEV             0x00000400L
4076  *              BL_MASK_FLY             0x00000800L
4077  *              BL_MASK_RIDE            0x00001000L
4078  *      -- The value passed for BL_GOLD usually includes an encoded leading
4079  *         symbol for GOLD "\GXXXXNNNN:nnn". If the window port needs to use
4080  *         the textual gold amount without the leading "$:" the port will
4081  *         have to skip past ':' in the passed "ptr" for the BL_GOLD case.
4082  *      -- color is an unsigned int.
4083  *               color_index = color & 0x00FF;         CLR_* value
4084  *               attribute   = (color >> 8) & 0x00FF;  HL_ATTCLR_* mask
4085  *         This holds the color and attribute that the field should
4086  *         be displayed in.
4087  *         This is relevant for everything except BL_CONDITION fldindex.
4088  *         If fldindex is BL_CONDITION, this parameter should be ignored,
4089  *         as condition highlighting is done via the next colormasks
4090  *         parameter instead.
4091  *
4092  *      -- colormasks - pointer to cond_hilites[] array of colormasks.
4093  *         Only relevant for BL_CONDITION fldindex. The window port
4094  *         should ignore this parameter for other fldindex values.
4095  *         Each condition bit must only ever appear in one of the
4096  *         CLR_ array members, but can appear in multiple HL_ATTCLR_
4097  *         offsets (because more than one attribute can co-exist).
4098  *         See doc/window.doc for more details.
4099  */
4100
4101 void
4102 tty_status_update(fldidx, ptr, chg, percent, color, colormasks)
4103 int fldidx, chg UNUSED, percent, color;
4104 genericptr_t ptr;
4105 unsigned long *colormasks;
4106 {
4107     int attrmask;
4108     long *condptr = (long *) ptr;
4109     char *text = (char *) ptr;
4110     char goldbuf[40], *lastchar, *p;
4111     const char *fmt;
4112     boolean reset_state = NO_RESET;
4113
4114     if ((fldidx < BL_RESET) || (fldidx >= MAXBLSTATS))
4115         return;
4116
4117     if ((fldidx >= 0 && fldidx < MAXBLSTATS) && !status_activefields[fldidx])
4118         return;
4119
4120     switch (fldidx) {
4121     case BL_RESET:
4122         reset_state = FORCE_RESET;
4123         /*FALLTHRU*/
4124     case BL_FLUSH:
4125         if (make_things_fit(reset_state) || truncation_expected) {
4126             render_status();
4127 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
4128             status_sanity_check();
4129 #endif
4130         }
4131         return;
4132     case BL_CONDITION:
4133         tty_status[NOW][fldidx].idx = fldidx;
4134         tty_condition_bits = *condptr;
4135         tty_colormasks = colormasks;
4136         tty_status[NOW][fldidx].valid = TRUE;
4137         tty_status[NOW][fldidx].dirty = TRUE;
4138         tty_status[NOW][fldidx].sanitycheck = TRUE;
4139         truncation_expected = FALSE;
4140         break;
4141     case BL_GOLD:
4142         text = decode_mixed(goldbuf, text);
4143         /*FALLTHRU*/
4144     default:
4145         attrmask = (color >> 8) & 0x00FF;
4146 #ifndef TEXTCOLOR
4147         color = NO_COLOR;
4148 #endif
4149         fmt = status_fieldfmt[fldidx];
4150         if (!fmt)
4151             fmt = "%s";
4152         /* should be checking for first enabled field here rather than
4153            just first field, but 'fieldorder' doesn't start any rows
4154            with fields which can be disabled so [any_row][0] suffices */
4155         if (*fmt == ' ' && (fldidx == fieldorder[0][0]
4156                             || fldidx == fieldorder[1][0]
4157                             || fldidx == fieldorder[2][0]))
4158             ++fmt; /* skip leading space for first field on line */
4159         Sprintf(status_vals[fldidx], fmt, text);
4160         tty_status[NOW][fldidx].idx = fldidx;
4161         tty_status[NOW][fldidx].color = (color & 0x00FF);
4162         tty_status[NOW][fldidx].attr = term_attr_fixup(attrmask);
4163         tty_status[NOW][fldidx].lth = strlen(status_vals[fldidx]);
4164         tty_status[NOW][fldidx].valid = TRUE;
4165         tty_status[NOW][fldidx].dirty = TRUE;
4166         tty_status[NOW][fldidx].sanitycheck = TRUE;
4167         break;
4168     }
4169
4170     /* The core botl engine sends a single blank to the window port
4171        for carrying-capacity when its unused. Let's suppress that */
4172     if (fldidx >= 0 && fldidx < MAXBLSTATS
4173         && tty_status[NOW][fldidx].lth == 1
4174         && status_vals[fldidx][0] == ' ') {
4175         status_vals[fldidx][0] = '\0';
4176         tty_status[NOW][fldidx].lth = 0;
4177     }
4178
4179     /* default processing above was required before these */
4180     switch (fldidx) {
4181     case BL_HP:
4182         if (iflags.wc2_hitpointbar) {
4183             /* Special additional processing for hitpointbar */
4184             hpbar_percent = percent;
4185             hpbar_color = (color & 0x00FF);
4186             tty_status[NOW][BL_TITLE].color = hpbar_color;
4187             tty_status[NOW][BL_TITLE].dirty = TRUE;
4188         }
4189         break;
4190     case BL_LEVELDESC:
4191         dlvl_shrinklvl = 0; /* caller is passing full length string */
4192         /*FALLTHRU*/
4193     case BL_HUNGER:
4194         /* The core sends trailing blanks for some fields.
4195            Let's suppress the trailing blanks */
4196         if (tty_status[NOW][fldidx].lth > 0) {
4197             p = status_vals[fldidx];
4198             for (lastchar = eos(p); lastchar > p && *--lastchar == ' '; ) {
4199                 *lastchar = '\0';
4200                 tty_status[NOW][fldidx].lth--;
4201             }
4202         }
4203         break;
4204     case BL_TITLE:
4205         /* when hitpointbar is enabled, rendering will enforce a length
4206            of 30 on title, padding with spaces or truncating if necessary */
4207         if (iflags.wc2_hitpointbar)
4208             tty_status[NOW][fldidx].lth = 30 + 2; /* '[' and ']' */
4209         break;
4210     case BL_GOLD:
4211         /* \GXXXXNNNN counts as 1 [moot since we use decode_mixed() above] */
4212         if ((p = index(status_vals[fldidx], '\\')) != 0 && p[1] == 'G')
4213             tty_status[NOW][fldidx].lth -= (10 - 1);
4214         break;
4215     case BL_CAP:
4216         enc_shrinklvl = 0; /* caller is passing full length string */
4217         enclev = stat_cap_indx();
4218         break;
4219     }
4220     /* As of 3.6.2 we only render on BL_FLUSH (or BL_RESET) */
4221     return;
4222 }
4223
4224 STATIC_OVL int
4225 make_things_fit(force_update)
4226 boolean force_update;
4227 {
4228     int trycnt, fitting = 0, requirement;
4229     int rowsz[3], num_rows, condrow, otheroptions = 0;
4230
4231     num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4232     condrow = num_rows - 1; /* always last row, 1 for 0..1 or 2 for 0..2 */
4233     cond_shrinklvl = 0;
4234     if (enc_shrinklvl > 0 && num_rows == 2)
4235         shrink_enc(0);
4236     if (dlvl_shrinklvl > 0)
4237         shrink_dlvl(0);
4238     set_condition_length();
4239     for (trycnt = 0; trycnt < 6 && !fitting; ++trycnt) {
4240         /* FIXME: this remeasures each line every time even though it
4241            is only attempting to shrink one of them and the other one
4242            (or two) remains the same */
4243         if (!check_fields(force_update, rowsz)) {
4244             fitting = 0;
4245             break;
4246         }
4247
4248         requirement = rowsz[condrow] - 1;
4249         if (requirement <= wins[WIN_STATUS]->cols - 1) {
4250             fitting = requirement;
4251             break;  /* we're good */
4252         }
4253         if (trycnt < 2) {
4254             if (cond_shrinklvl < trycnt + 1) {
4255                 cond_shrinklvl = trycnt + 1;
4256                 set_condition_length();
4257             }
4258             continue;
4259         }
4260         if (cond_shrinklvl >= 2) {
4261             /* We've exhausted the condition identifiers shrinkage,
4262              * so let's try shrinking other things...
4263              */
4264             if (otheroptions < 2) {
4265                 /* try shrinking the encumbrance word, but
4266                    only when it's on the same line as conditions */
4267                 if (num_rows == 2)
4268                     shrink_enc(otheroptions + 1);
4269             } else if (otheroptions == 2) {
4270                 shrink_dlvl(1);
4271             } else {
4272                 /* Last resort - turn on trunction */
4273                 truncation_expected = TRUE;
4274                 break;
4275             }
4276             ++otheroptions;
4277         }
4278     }
4279     return fitting;
4280 }
4281
4282 /*
4283  * This is the routine where we figure out where each field
4284  * should be placed, and flag whether the on-screen details
4285  * must be updated because they need to change.
4286  * This is now done at an individual field case-by-case level.
4287  */
4288 STATIC_OVL boolean
4289 check_fields(forcefields, sz)
4290 boolean forcefields;
4291 int sz[3];
4292 {
4293     int c, i, row, col, num_rows, idx;
4294     boolean valid = TRUE, matchprev, update_right;
4295
4296     if (!windowdata_init && !check_windowdata())
4297         return FALSE;
4298
4299     num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4300
4301     for (row = 0; row < num_rows; ++row) {
4302         sz[row] = 0;
4303         col = 1;
4304         update_right = FALSE;
4305         for (i = 0; (idx = fieldorder[row][i]) != BL_FLUSH; ++i) {
4306             if (!status_activefields[idx])
4307                 continue;
4308             if (!tty_status[NOW][idx].valid)
4309                 valid = FALSE;
4310             /* might be called more than once for shrink tests, so need
4311                to reset these (redraw and x at any rate) each time */
4312             tty_status[NOW][idx].redraw = FALSE;
4313             tty_status[NOW][idx].y = row;
4314             tty_status[NOW][idx].x = col;
4315
4316             /* On a change to the field location, everything further
4317                to the right must be updated as well.  (Not necessarily
4318                everything; it's possible for complementary changes across
4319                multiple fields to put stuff further right back in sync.) */
4320             if (tty_status[NOW][idx].x + tty_status[NOW][idx].lth
4321                 != tty_status[BEFORE][idx].x + tty_status[BEFORE][idx].lth)
4322                 update_right = TRUE;
4323             else if (tty_status[NOW][idx].lth != tty_status[BEFORE][idx].lth
4324                      || tty_status[NOW][idx].x != tty_status[BEFORE][idx].x)
4325                 tty_status[NOW][idx].redraw = TRUE;
4326             else /* in case update_right is set, we're back in sync now */
4327                 update_right = FALSE;
4328
4329             matchprev = FALSE; /* assume failure */
4330             if (valid && !update_right && !forcefields
4331                 && !tty_status[NOW][idx].redraw) {
4332                 /*
4333                  * Check values against those already on the display.
4334                  *  - Is the additional processing time for this worth it?
4335                  */
4336                 if (do_field_opt
4337                     /* color/attr checks aren't right for 'condition'
4338                        and neither is examining status_vals[BL_CONDITION]
4339                        so skip same-contents optimization for conditions */
4340                     && idx != BL_CONDITION
4341                     && (tty_status[NOW][idx].color
4342                         == tty_status[BEFORE][idx].color)
4343                     && (tty_status[NOW][idx].attr
4344                         == tty_status[BEFORE][idx].attr)) {
4345                     matchprev = TRUE; /* assume success */
4346                     if (tty_status[NOW][idx].dirty) {
4347                         /* compare values */
4348                         const char *ob, *nb; /* old byte, new byte */
4349                         struct WinDesc *cw = wins[WIN_STATUS];
4350
4351                         c = col - 1;
4352                         ob = &cw->data[row][c];
4353                         nb = status_vals[idx];
4354                         while (*nb && c < cw->cols) {
4355                             if (*nb != *ob)
4356                                 break;
4357                             nb++;
4358                             ob++;
4359                             c++;
4360                         }
4361                         /* if we're not at the end of new string, no match;
4362                            we don't need to worry about whether there might
4363                            be leftover old string; that could only happen
4364                            if they have different lengths, in which case
4365                            'update_right' will be set and we won't get here */
4366                         if (*nb)
4367                             matchprev = FALSE;
4368 #if 0
4369                         if (*nb || (*ob && *ob != ' '
4370                                     && (*ob != '/' || idx != BL_XP)
4371                                     && (*ob != '(' || (idx != BL_HP
4372                                                        && idx != BL_ENE))))
4373                             matchprev = FALSE;
4374 #endif
4375                     }
4376                 }
4377             }
4378
4379             if (forcefields || update_right
4380                 || (tty_status[NOW][idx].dirty && !matchprev))
4381                 tty_status[NOW][idx].redraw = TRUE;
4382
4383             col += tty_status[NOW][idx].lth;
4384         }
4385         sz[row] = col;
4386     }
4387     return valid;
4388 }
4389
4390 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
4391 STATIC_OVL void
4392 status_sanity_check(VOID_ARGS)
4393 {
4394     int i;
4395     static boolean in_sanity_check = FALSE;
4396     static const char *const idxtext[] = {
4397         "BL_TITLE", "BL_STR", "BL_DX", "BL_CO", "BL_IN", "BL_WI", /* 0.. 5   */
4398         "BL_CH","BL_ALIGN", "BL_SCORE", "BL_CAP", "BL_GOLD",     /* 6.. 10  */
4399         "BL_ENE", "BL_ENEMAX", "BL_XP", "BL_AC", "BL_HD",       /* 11.. 15 */
4400         "BL_TIME", "BL_HUNGER", "BL_HP", "BL_HPMAX",           /* 16.. 19 */
4401         "BL_LEVELDESC", "BL_EXP", "BL_CONDITION"              /* 20.. 22 */
4402     };
4403    
4404     if (in_sanity_check)
4405         return;
4406     in_sanity_check = TRUE;
4407     /*
4408      * Make sure that every field made it down to the
4409      * bottom of the render_status() for-loop.
4410      */
4411     for (i = 0; i < MAXBLSTATS; ++i) {
4412         if (tty_status[NOW][i].sanitycheck) {
4413             char panicmsg[BUFSZ];
4414
4415             Sprintf(panicmsg, "failed on tty_status[NOW][%s].", idxtext[i]);
4416             paniclog("status_sanity_check", panicmsg);
4417             tty_status[NOW][i].sanitycheck = FALSE;
4418             /*
4419              * Attention developers: If you encounter the above
4420              * message in paniclog, it almost certainly means that
4421              * a recent code change has caused a failure to reach
4422              * the bottom of render_status(), at least for the BL_
4423              * field identified in the impossible() message.
4424              *
4425              * That could be because of the addition of a continue
4426              * statement within the render_status() for-loop, or a
4427              * premature return from render_status() before it finished
4428              * its housekeeping chores.
4429              */
4430         }
4431     }
4432     in_sanity_check = FALSE;
4433 }
4434 #endif /* NHDEVEL_STATUS */
4435
4436 /*
4437  * This is what places a field on the tty display.
4438  */
4439 STATIC_OVL void
4440 tty_putstatusfield(text, x, y)
4441 const char *text;
4442 int x, y;
4443 {
4444     int i, n, ncols, nrows, lth = 0;
4445     struct WinDesc *cw = 0;
4446
4447     if (WIN_STATUS == WIN_ERR
4448         || (cw = wins[WIN_STATUS]) == (struct WinDesc *) 0)
4449         panic("tty_putstatusfield: Invalid WinDesc\n");
4450
4451     ncols = cw->cols;
4452     nrows = cw->maxrow;
4453     lth = (int) strlen(text);
4454
4455     print_vt_code2(AVTC_SELECT_WINDOW, NHW_STATUS);
4456
4457     if (x < ncols && y < nrows) {
4458         if (x != cw->curx || y != cw->cury)
4459             tty_curs(NHW_STATUS, x, y);
4460         for (i = 0; i < lth; ++i) {
4461             n = i + x;
4462             if (n < ncols && *text) {
4463 #if 0 /*JP*/
4464                 (void) putchar(*text);
4465 #else
4466                 (void) jputchar(*(unsigned char *)text);
4467 #endif
4468                 ttyDisplay->curx++;
4469                 cw->curx++;
4470                 cw->data[y][n - 1] = *text;
4471                 text++;
4472             }
4473         }
4474     }
4475 #if 0
4476     else {
4477         if (truncation_expected) {
4478         /* Now we're truncating */
4479             ; /* but we knew in advance */
4480         }
4481     }
4482 #endif
4483 }
4484
4485 /* caller must set cond_shrinklvl (0..2) before calling us */
4486 STATIC_OVL void
4487 set_condition_length()
4488 {
4489     long mask;
4490     int c, lth = 0;
4491
4492     if (tty_condition_bits) {
4493         for (c = 0; c < SIZE(conditions); ++c) {
4494             mask = conditions[c].mask;
4495             if ((tty_condition_bits & mask) == mask)
4496                 lth += 1 + (int) strlen(conditions[c].text[cond_shrinklvl]);
4497         }
4498     }
4499     tty_status[NOW][BL_CONDITION].lth = lth;
4500 }
4501
4502 STATIC_OVL void
4503 shrink_enc(lvl)
4504 int lvl;
4505 {
4506     /* shrink or restore the encumbrance word */
4507     if (lvl <= 2) {
4508         enc_shrinklvl = lvl;
4509         Sprintf(status_vals[BL_CAP], " %s", encvals[lvl][enclev]);
4510     }
4511     tty_status[NOW][BL_CAP].lth = strlen(status_vals[BL_CAP]);
4512 }
4513
4514 STATIC_OVL void
4515 shrink_dlvl(lvl)
4516 int lvl;
4517 {
4518     /* try changing Dlvl: to Dl: */
4519     char buf[BUFSZ];
4520     char *levval = index(status_vals[BL_LEVELDESC], ':');
4521
4522     if (levval) {
4523         dlvl_shrinklvl = lvl;
4524         Strcpy(buf, (lvl == 0) ? "Dlvl" : "Dl");
4525         Strcat(buf, levval);
4526         Strcpy(status_vals[BL_LEVELDESC], buf);
4527         tty_status[NOW][BL_LEVELDESC].lth = strlen(status_vals[BL_LEVELDESC]);
4528     }
4529 }
4530
4531 /*
4532  * Ensure the underlying status window data start out
4533  * blank and null-terminated.
4534  */
4535 STATIC_OVL boolean
4536 check_windowdata(VOID_ARGS)
4537 {
4538     if (WIN_STATUS == WIN_ERR || wins[WIN_STATUS] == (struct WinDesc *) 0) {
4539         paniclog("check_windowdata", " null status window.");
4540         return FALSE;
4541     } else if (!windowdata_init) {
4542         tty_clear_nhwindow(WIN_STATUS); /* also sets cw->data[] to spaces */
4543         windowdata_init = TRUE;
4544     }
4545     return TRUE;
4546 }
4547
4548 #ifdef TEXTCOLOR
4549 /*
4550  * Return what color this condition should
4551  * be displayed in based on user settings.
4552  */
4553 STATIC_OVL int
4554 condcolor(bm, bmarray)
4555 long bm;
4556 unsigned long *bmarray;
4557 {
4558     int i;
4559
4560     if (bm && bmarray)
4561         for (i = 0; i < CLR_MAX; ++i) {
4562             if ((bm & bmarray[i]) != 0)
4563                 return i;
4564         }
4565     return NO_COLOR;
4566 }
4567 #else
4568 /* might need something more elaborate if some compiler complains that
4569    the condition where this gets used always has the same value */
4570 #define condcolor(bm,bmarray) NO_COLOR
4571 #define term_start_color(color) /*empty*/
4572 #define term_end_color() /*empty*/
4573 #endif /* TEXTCOLOR */
4574
4575 STATIC_OVL int
4576 condattr(bm, bmarray)
4577 long bm;
4578 unsigned long *bmarray;
4579 {
4580     int attr = 0;
4581     int i;
4582
4583     if (bm && bmarray) {
4584         for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) {
4585             if ((bm & bmarray[i]) != 0) {
4586                 switch (i) {
4587                 case HL_ATTCLR_DIM:
4588                     attr |= HL_DIM;
4589                     break;
4590                 case HL_ATTCLR_BLINK:
4591                     attr |= HL_BLINK;
4592                     break;
4593                 case HL_ATTCLR_ULINE:
4594                     attr |= HL_ULINE;
4595                     break;
4596                 case HL_ATTCLR_INVERSE:
4597                     attr |= HL_INVERSE;
4598                     break;
4599                 case HL_ATTCLR_BOLD:
4600                     attr |= HL_BOLD;
4601                     break;
4602                 }
4603             }
4604         }
4605     }
4606     return attr;
4607 }
4608
4609 #define Begin_Attr(m) \
4610     do {                                        \
4611         if (m) {                                \
4612             if ((m) & HL_BOLD)                  \
4613                 term_start_attr(ATR_BOLD);      \
4614             if ((m) & HL_INVERSE)               \
4615                 term_start_attr(ATR_INVERSE);   \
4616             if ((m) & HL_ULINE)                 \
4617                 term_start_attr(ATR_ULINE);     \
4618             if ((m) & HL_BLINK)                 \
4619                 term_start_attr(ATR_BLINK);     \
4620             if ((m) & HL_DIM)                   \
4621                 term_start_attr(ATR_DIM);       \
4622         }                                       \
4623     } while (0)
4624
4625 #define End_Attr(m) \
4626     do {                                        \
4627         if (m) {                                \
4628             if ((m) & HL_DIM)                   \
4629                 term_end_attr(ATR_DIM);         \
4630             if ((m) & HL_BLINK)                 \
4631                 term_end_attr(ATR_BLINK);       \
4632             if ((m) & HL_ULINE)                 \
4633                 term_end_attr(ATR_ULINE);       \
4634             if ((m) & HL_INVERSE)               \
4635                 term_end_attr(ATR_INVERSE);     \
4636             if ((m) & HL_BOLD)                  \
4637                 term_end_attr(ATR_BOLD);        \
4638         }                                       \
4639     } while (0)
4640
4641 STATIC_OVL void
4642 render_status(VOID_ARGS)
4643 {
4644     long mask, bits;
4645     int i, x, y, idx, c, row, tlth, num_rows, coloridx = 0, attrmask = 0;
4646     char *text;
4647     struct WinDesc *cw = 0;
4648
4649     if (WIN_STATUS == WIN_ERR
4650         || (cw = wins[WIN_STATUS]) == (struct WinDesc *) 0) {
4651         paniclog("render_status", "WIN_ERR on status window.");
4652         return;
4653     }
4654
4655     num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3;
4656     for (row = 0; row < num_rows; ++row) {
4657         HUPSKIP();
4658         y = row;
4659         tty_curs(WIN_STATUS, 1, y);
4660         for (i = 0; (idx = fieldorder[row][i]) != BL_FLUSH; ++i) {
4661             if (!status_activefields[idx])
4662                 continue;
4663             x = tty_status[NOW][idx].x;
4664             text = status_vals[idx]; /* always "" for BL_CONDITION */
4665             tlth = (int) tty_status[NOW][idx].lth; /* valid for BL_CONDITION */
4666
4667             if (tty_status[NOW][idx].redraw || !do_field_opt) {
4668                 boolean hitpointbar = (idx == BL_TITLE
4669                                        && iflags.wc2_hitpointbar);
4670
4671                 if (idx == BL_CONDITION) {
4672                     /*
4673                      * +-----------------+
4674                      * | Condition Codes |
4675                      * +-----------------+
4676                      */
4677                     bits = tty_condition_bits;
4678                     /* if no bits are set, we can fall through condition
4679                        rendering code to finalx[] handling (and subsequent
4680                        rest-of-line erasure if line is shorter than before) */
4681                     if (num_rows == 3 && bits != 0L) {
4682                         int k;
4683                         char *dat = &cw->data[y][0];
4684
4685                         /* line up with hunger (or where it would have
4686                            been when currently omitted); if there isn't
4687                            enough room for that, right justify; or place
4688                            as-is if not even enough room for /that/; we
4689                            expect hunger to be on preceding row, in which
4690                            case its current data has been moved to [BEFORE] */
4691                         if (tty_status[BEFORE][BL_HUNGER].y < row
4692                             && x < tty_status[BEFORE][BL_HUNGER].x
4693                             && (tty_status[BEFORE][BL_HUNGER].x + tlth
4694                                 < cw->cols - 1))
4695                             k = tty_status[BEFORE][BL_HUNGER].x;
4696                         else if (x + tlth < cw->cols - 1)
4697                             k = cw->cols - tlth;
4698                         else
4699                             k = x;
4700                         while (x < k) {
4701                             if (dat[x - 1] != ' ')
4702                                 tty_putstatusfield(" ", x, y);
4703                             ++x;
4704                         }
4705                         tty_status[NOW][BL_CONDITION].x = x;
4706                         tty_curs(WIN_STATUS, x, y);
4707                     }
4708                     for (c = 0; c < SIZE(conditions) && bits != 0L; ++c) {
4709                         mask = conditions[c].mask;
4710                         if (bits & mask) {
4711                             const char *condtext;
4712
4713                             tty_putstatusfield(" ", x++, y);
4714                             if (iflags.hilite_delta) {
4715                                 attrmask = condattr(mask, tty_colormasks);
4716                                 Begin_Attr(attrmask);
4717                                 coloridx = condcolor(mask, tty_colormasks);
4718                                 if (coloridx != NO_COLOR)
4719                                     term_start_color(coloridx);
4720                             }
4721                             condtext = conditions[c].text[cond_shrinklvl];
4722                             if (x >= cw->cols && !truncation_expected) {
4723                                 impossible(
4724                          "Unexpected condition placement overflow for \"%s\"",
4725                                            condtext);
4726                                 condtext = "";
4727                                 bits = 0L; /* skip any remaining conditions */
4728                             }
4729                             tty_putstatusfield(condtext, x, y);
4730                             x += (int) strlen(condtext);
4731                             if (iflags.hilite_delta) {
4732                                 if (coloridx != NO_COLOR)
4733                                     term_end_color();
4734                                 End_Attr(attrmask);
4735                             }
4736                             bits &= ~mask;
4737                         }
4738                     }
4739                     /* 'x' is 1-based and 'cols' and 'data' are 0-based,
4740                        so x==cols means we just stored in data[N-2] and
4741                        are now positioned at data[N-1], the terminator;
4742                        that's ok as long as we don't write there */
4743                     if (x > cw->cols) {
4744                         static unsigned once_only = 0;
4745
4746                         if (!truncation_expected && !once_only++)
4747                             paniclog("render_status()",
4748                                      " unexpected truncation.");
4749                         x = cw->cols;
4750                     }
4751                 } else if (hitpointbar) {
4752                     /*
4753                      * +-------------------------+
4754                      * | Title with Hitpoint Bar |
4755                      * +-------------------------+
4756                      */
4757                     /* hitpointbar using hp percent calculation */
4758                     int bar_len, bar_pos = 0;
4759                     char bar[MAXCO], *bar2 = (char *) 0, savedch = '\0';
4760                     boolean twoparts = (hpbar_percent < 100);
4761
4762                     /* force exactly 30 characters, padded with spaces
4763                        if shorter or truncated if longer */
4764                     if (strlen(text) != 30) {
4765                         Sprintf(bar, "%-30.30s", text);
4766                         Strcpy(status_vals[BL_TITLE], bar);
4767                     } else
4768                         Strcpy(bar, text);
4769                     bar_len = (int) strlen(bar); /* always 30 */
4770                     tlth = bar_len + 2;
4771                     /* when at full HP, the whole title will be highlighted;
4772                        when injured or dead, there will be a second portion
4773                        which is not highlighted */
4774                     if (twoparts) {
4775                         /* figure out where to separate the two parts */
4776                         bar_pos = (bar_len * hpbar_percent) / 100;
4777                         if (bar_pos < 1 && hpbar_percent > 0)
4778                             bar_pos = 1;
4779                         if (bar_pos >= bar_len && hpbar_percent < 100)
4780                             bar_pos = bar_len - 1;
4781                         bar2 = &bar[bar_pos];
4782                         savedch = *bar2;
4783                         *bar2 = '\0';
4784                     }
4785                     tty_putstatusfield("[", x++, y);
4786                     if (*bar) { /* always True, unless twoparts+dead (0 HP) */
4787                         term_start_attr(ATR_INVERSE);
4788                         if (iflags.hilite_delta && hpbar_color != NO_COLOR)
4789                             term_start_color(hpbar_color);
4790                         tty_putstatusfield(bar, x, y);
4791                         x += (int) strlen(bar);
4792                         if (iflags.hilite_delta && hpbar_color != NO_COLOR)
4793                             term_end_color();
4794                         term_end_attr(ATR_INVERSE);
4795                     }
4796                     if (twoparts) { /* no highlighting for second part */
4797                         *bar2 = savedch;
4798                         tty_putstatusfield(bar2, x, y);
4799                         x += (int) strlen(bar2);
4800                     }
4801                     tty_putstatusfield("]", x++, y);
4802                 } else {
4803                     /*
4804                      * +-----------------------------+
4805                      * | Everything else that is not |
4806                      * |   in a special case above   |
4807                      * +-----------------------------+
4808                      */
4809                     if (iflags.hilite_delta) {
4810                         while (*text == ' ') {
4811                             tty_putstatusfield(" ", x++, y);
4812                             text++;
4813                         }
4814                         if (*text == '/' && idx == BL_EXP) {
4815                             tty_putstatusfield("/", x++, y);
4816                             text++;
4817                         }
4818                         attrmask = tty_status[NOW][idx].attr;
4819                         Begin_Attr(attrmask);
4820                         coloridx = tty_status[NOW][idx].color;
4821                         if (coloridx != NO_COLOR)
4822                             term_start_color(coloridx);
4823                     }
4824                     tty_putstatusfield(text, x, y);
4825                     x += (int) strlen(text);
4826                     if (iflags.hilite_delta) {
4827                         if (coloridx != NO_COLOR)
4828                             term_end_color();
4829                         End_Attr(attrmask);
4830                     }
4831                 }
4832             } else {
4833                 /* not rendered => same text as before */
4834                 x += tlth;
4835             }
4836             finalx[row][NOW] = x - 1;
4837             /* reset .redraw and .dirty now that field has been rendered */
4838             tty_status[NOW][idx].dirty  = FALSE;
4839             tty_status[NOW][idx].redraw = FALSE;
4840             tty_status[NOW][idx].sanitycheck = FALSE;
4841             /*
4842              * For comparison of current and previous:
4843              * - Copy the entire tty_status struct.
4844              */
4845             tty_status[BEFORE][idx] = tty_status[NOW][idx];
4846         }
4847         x = finalx[row][NOW];
4848         if ((x < finalx[row][BEFORE] || !finalx[row][BEFORE])
4849             && x + 1 < cw->cols) {
4850             tty_curs(WIN_STATUS, x + 1, y);
4851             cl_end();
4852         }
4853         /*
4854          * For comparison of current and previous:
4855          * - Copy the last written column number on the row.
4856          */
4857         finalx[row][BEFORE] = finalx[row][NOW];
4858     }
4859     return;
4860 }
4861
4862 #endif /* STATUS_HILITES */
4863
4864 #endif /* TTY_GRAPHICS */
4865
4866 /*wintty.c*/