OSDN Git Service

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