OSDN Git Service

add copyright
[nethackexpress/trunk.git] / win / tty / wintty.c
1 /*      SCCS Id: @(#)wintty.c   3.4     2002/09/27      */
2 /* Copyright (c) David Cohrs, 1991                                */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /*
6  * Neither a standard out nor character-based control codes should be
7  * part of the "tty look" windowing implementation.
8  * h+ 930227
9  */
10
11 #include "hack.h"
12 #include "dlb.h"
13 #ifdef SHORT_FILENAMES
14 #include "patchlev.h"
15 #else
16 #include "patchlevel.h"
17 #endif
18
19 #ifdef TTY_GRAPHICS
20
21 #ifdef MAC
22 # define MICRO /* The Mac is a MICRO only for this file, not in general! */
23 # ifdef THINK_C
24 extern void msmsg(const char *,...);
25 # endif
26 #endif
27
28
29 #ifndef NO_TERMS
30 #include "tcap.h"
31 #endif
32
33 #include "wintty.h"
34
35 #ifdef CLIPPING         /* might want SIGWINCH */
36 # if defined(BSD) || defined(ULTRIX) || defined(AIX_31) || defined(_BULL_SOURCE)
37 #include <signal.h>
38 # endif
39 #endif
40
41 extern char mapped_menu_cmds[]; /* from options.c */
42
43 /* Interface definition, for windows.c */
44 struct window_procs tty_procs = {
45     "tty",
46 #ifdef MSDOS
47     WC_TILED_MAP|WC_ASCII_MAP|
48 #endif
49 #if defined(WIN32CON)
50     WC_MOUSE_SUPPORT|
51 #endif
52     WC_COLOR|WC_HILITE_PET|WC_INVERSE|WC_EIGHT_BIT_IN,
53     0L,
54     tty_init_nhwindows,
55     tty_player_selection,
56     tty_askname,
57     tty_get_nh_event,
58     tty_exit_nhwindows,
59     tty_suspend_nhwindows,
60     tty_resume_nhwindows,
61     tty_create_nhwindow,
62     tty_clear_nhwindow,
63     tty_display_nhwindow,
64     tty_destroy_nhwindow,
65     tty_curs,
66     tty_putstr,
67     tty_display_file,
68     tty_start_menu,
69     tty_add_menu,
70     tty_end_menu,
71     tty_select_menu,
72     tty_message_menu,
73     tty_update_inventory,
74     tty_mark_synch,
75     tty_wait_synch,
76 #ifdef CLIPPING
77     tty_cliparound,
78 #endif
79 #ifdef POSITIONBAR
80     tty_update_positionbar,
81 #endif
82     tty_print_glyph,
83     tty_raw_print,
84     tty_raw_print_bold,
85     tty_nhgetch,
86     tty_nh_poskey,
87     tty_nhbell,
88     tty_doprev_message,
89     tty_yn_function,
90     tty_getlin,
91     tty_get_ext_cmd,
92     tty_number_pad,
93     tty_delay_output,
94 #ifdef CHANGE_COLOR     /* the Mac uses a palette device */
95     tty_change_color,
96 #ifdef MAC
97     tty_change_background,
98     set_tty_font_name,
99 #endif
100     tty_get_color_string,
101 #endif
102
103     /* other defs that really should go away (they're tty specific) */
104     tty_start_screen,
105     tty_end_screen,
106     genl_outrip,
107 #if defined(WIN32CON)
108     nttty_preference_update,
109 #else
110     genl_preference_update,
111 #endif
112 };
113
114 static int maxwin = 0;                  /* number of windows in use */
115 winid BASE_WINDOW;
116 struct WinDesc *wins[MAXWIN];
117 struct DisplayDesc *ttyDisplay; /* the tty display descriptor */
118
119 extern void FDECL(cmov, (int,int)); /* from termcap.c */
120 extern void FDECL(nocmov, (int,int)); /* from termcap.c */
121 #if defined(UNIX) || defined(VMS)
122 static char obuf[BUFSIZ];       /* BUFSIZ is defined in stdio.h */
123 #endif
124
125 static char winpanicstr[] = "Bad window id %d";
126 char defmorestr[] = "--More--";
127
128 #ifdef CLIPPING
129 # if defined(USE_TILES) && defined(MSDOS)
130 boolean clipping = FALSE;       /* clipping on? */
131 int clipx = 0, clipxmax = 0;
132 # else
133 static boolean clipping = FALSE;        /* clipping on? */
134 static int clipx = 0, clipxmax = 0;
135 # endif
136 static int clipy = 0, clipymax = 0;
137 #endif /* CLIPPING */
138
139 #if defined(USE_TILES) && defined(MSDOS)
140 extern void FDECL(adjust_cursor_flags, (struct WinDesc *));
141 #endif
142
143 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
144 boolean GFlag = FALSE;
145 boolean HE_resets_AS;   /* see termcap.c */
146 #endif
147
148 #if defined(MICRO) || defined(WIN32CON)
149 static const char to_continue[] = "to continue";
150 #define getret() getreturn(to_continue)
151 #else
152 STATIC_DCL void NDECL(getret);
153 #endif
154 STATIC_DCL void FDECL(erase_menu_or_text, (winid, struct WinDesc *, BOOLEAN_P));
155 STATIC_DCL void FDECL(free_window_info, (struct WinDesc *, BOOLEAN_P));
156 STATIC_DCL void FDECL(dmore,(struct WinDesc *, const char *));
157 STATIC_DCL void FDECL(set_item_state, (winid, int, tty_menu_item *));
158 STATIC_DCL void FDECL(set_all_on_page, (winid,tty_menu_item *,tty_menu_item *));
159 STATIC_DCL void FDECL(unset_all_on_page, (winid,tty_menu_item *,tty_menu_item *));
160 STATIC_DCL void FDECL(invert_all_on_page, (winid,tty_menu_item *,tty_menu_item *, CHAR_P));
161 STATIC_DCL void FDECL(invert_all, (winid,tty_menu_item *,tty_menu_item *, CHAR_P));
162 STATIC_DCL void FDECL(process_menu_window, (winid,struct WinDesc *));
163 STATIC_DCL void FDECL(process_text_window, (winid,struct WinDesc *));
164 STATIC_DCL tty_menu_item *FDECL(reverse, (tty_menu_item *));
165 STATIC_DCL const char * FDECL(compress_str, (const char *));
166 STATIC_DCL void FDECL(tty_putsym, (winid, int, int, CHAR_P));
167 static char *FDECL(copy_of, (const char *));
168 STATIC_DCL void FDECL(bail, (const char *));    /* __attribute__((noreturn)) */
169
170 /*
171  * A string containing all the default commands -- to add to a list
172  * of acceptable inputs.
173  */
174 static const char default_menu_cmds[] = {
175         MENU_FIRST_PAGE,
176         MENU_LAST_PAGE,
177         MENU_NEXT_PAGE,
178         MENU_PREVIOUS_PAGE,
179         MENU_SELECT_ALL,
180         MENU_UNSELECT_ALL,
181         MENU_INVERT_ALL,
182         MENU_SELECT_PAGE,
183         MENU_UNSELECT_PAGE,
184         MENU_INVERT_PAGE,
185         0       /* null terminator */
186 };
187
188
189 /* clean up and quit */
190 STATIC_OVL void
191 bail(mesg)
192 const char *mesg;
193 {
194     clearlocks();
195     tty_exit_nhwindows(mesg);
196     terminate(EXIT_SUCCESS);
197     /*NOTREACHED*/
198 }
199
200 #if defined(SIGWINCH) && defined(CLIPPING)
201 STATIC_OVL void
202 winch()
203 {
204     int oldLI = LI, oldCO = CO, i;
205     register struct WinDesc *cw;
206
207     getwindowsz();
208     if((oldLI != LI || oldCO != CO) && ttyDisplay) {
209         ttyDisplay->rows = LI;
210         ttyDisplay->cols = CO;
211
212         cw = wins[BASE_WINDOW];
213         cw->rows = ttyDisplay->rows;
214         cw->cols = ttyDisplay->cols;
215
216         if(iflags.window_inited) {
217             cw = wins[WIN_MESSAGE];
218             cw->curx = cw->cury = 0;
219
220             tty_destroy_nhwindow(WIN_STATUS);
221             WIN_STATUS = tty_create_nhwindow(NHW_STATUS);
222
223             if(u.ux) {
224 #ifdef CLIPPING
225                 if(CO < COLNO || LI < ROWNO+3) {
226                     setclipped();
227                     tty_cliparound(u.ux, u.uy);
228                 } else {
229                     clipping = FALSE;
230                     clipx = clipy = 0;
231                 }
232 #endif
233                 i = ttyDisplay->toplin;
234                 ttyDisplay->toplin = 0;
235                 docrt();
236                 bot();
237                 ttyDisplay->toplin = i;
238                 flush_screen(1);
239                 if(i) {
240                     addtopl(toplines);
241                 } else
242                     for(i=WIN_INVEN; i < MAXWIN; i++)
243                         if(wins[i] && wins[i]->active) {
244                             /* cop-out */
245                             addtopl("Press Return to continue: ");
246                             break;
247                         }
248                 (void) fflush(stdout);
249                 if(i < 2) flush_screen(1);
250             }
251         }
252     }
253 }
254 #endif
255
256 /*ARGSUSED*/
257 void
258 tty_init_nhwindows(argcp,argv)
259 int* argcp;
260 char** argv;
261 {
262     int wid, hgt;
263
264     /*
265      *  Remember tty modes, to be restored on exit.
266      *
267      *  gettty() must be called before tty_startup()
268      *    due to ordering of LI/CO settings
269      *  tty_startup() must be called before initoptions()
270      *    due to ordering of graphics settings
271      */
272 #if defined(UNIX) || defined(VMS)
273     setbuf(stdout,obuf);
274 #endif
275     gettty();
276
277     /* to port dependant tty setup */
278     tty_startup(&wid, &hgt);
279     setftty();                  /* calls start_screen */
280
281     /* set up tty descriptor */
282     ttyDisplay = (struct DisplayDesc*) alloc(sizeof(struct DisplayDesc));
283     ttyDisplay->toplin = 0;
284     ttyDisplay->rows = hgt;
285     ttyDisplay->cols = wid;
286     ttyDisplay->curx = ttyDisplay->cury = 0;
287     ttyDisplay->inmore = ttyDisplay->inread = ttyDisplay->intr = 0;
288     ttyDisplay->dismiss_more = 0;
289 #ifdef TEXTCOLOR
290     ttyDisplay->color = NO_COLOR;
291 #endif
292     ttyDisplay->attrs = 0;
293
294     /* set up the default windows */
295     BASE_WINDOW = tty_create_nhwindow(NHW_BASE);
296     wins[BASE_WINDOW]->active = 1;
297
298     ttyDisplay->lastwin = WIN_ERR;
299
300 #if defined(SIGWINCH) && defined(CLIPPING)
301     (void) signal(SIGWINCH, winch);
302 #endif
303
304     /* add one a space forward menu command alias */
305     add_menu_cmd_alias(' ', MENU_NEXT_PAGE);
306
307     tty_clear_nhwindow(BASE_WINDOW);
308
309     tty_putstr(BASE_WINDOW, 0, "");
310     tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_A);
311     tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_B);
312     tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_B2);
313     tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_C);
314     tty_putstr(BASE_WINDOW, 0, "");
315     tty_display_nhwindow(BASE_WINDOW, FALSE);
316 }
317
318 void
319 tty_player_selection()
320 {
321         int i, k, n;
322         char pick4u = 'n', thisch, lastch = 0;
323         char pbuf[QBUFSZ], plbuf[QBUFSZ];
324         winid win;
325         anything any;
326         menu_item *selected = 0;
327
328         /* prevent an unnecessary prompt */
329         rigid_role_checks();
330
331         /* Should we randomly pick for the player? */
332         if (!flags.randomall &&
333             (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE ||
334              flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) {
335             int echoline;
336             char *prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole,
337                                 flags.initrace, flags.initgend, flags.initalign);
338
339             tty_putstr(BASE_WINDOW, 0, "");
340             echoline = wins[BASE_WINDOW]->cury;
341             tty_putstr(BASE_WINDOW, 0, prompt);
342             do {
343                 pick4u = lowc(readchar());
344                 if (index(quitchars, pick4u)) pick4u = 'y';
345             } while(!index(ynqchars, pick4u));
346             if ((int)strlen(prompt) + 1 < CO) {
347                 /* Echo choice and move back down line */
348                 tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline, pick4u);
349                 tty_putstr(BASE_WINDOW, 0, "");
350             } else
351                 /* Otherwise it's hard to tell where to echo, and things are
352                  * wrapping a bit messily anyway, so (try to) make sure the next
353                  * question shows up well and doesn't get wrapped at the
354                  * bottom of the window.
355                  */
356                 tty_clear_nhwindow(BASE_WINDOW);
357             
358             if (pick4u != 'y' && pick4u != 'n') {
359 give_up:        /* Quit */
360                 if (selected) free((genericptr_t) selected);
361                 bail((char *)0);
362                 /*NOTREACHED*/
363                 return;
364             }
365         }
366
367         (void)  root_plselection_prompt(plbuf, QBUFSZ - 1,
368                         flags.initrole, flags.initrace, flags.initgend, flags.initalign);
369
370         /* Select a role, if necessary */
371         /* we'll try to be compatible with pre-selected race/gender/alignment,
372          * but may not succeed */
373         if (flags.initrole < 0) {
374             char rolenamebuf[QBUFSZ];
375             /* Process the choice */
376             if (pick4u == 'y' || flags.initrole == ROLE_RANDOM || flags.randomall) {
377                 /* Pick a random role */
378                 flags.initrole = pick_role(flags.initrace, flags.initgend,
379                                                 flags.initalign, PICK_RANDOM);
380                 if (flags.initrole < 0) {
381                     tty_putstr(BASE_WINDOW, 0, "Incompatible role!");
382                     flags.initrole = randrole();
383                 }
384             } else {
385                 tty_clear_nhwindow(BASE_WINDOW);
386                 tty_putstr(BASE_WINDOW, 0, "Choosing Character's Role");
387                 /* Prompt for a role */
388                 win = create_nhwindow(NHW_MENU);
389                 start_menu(win);
390                 any.a_void = 0;         /* zero out all bits */
391                 for (i = 0; roles[i].name.m; i++) {
392                     if (ok_role(i, flags.initrace, flags.initgend,
393                                                         flags.initalign)) {
394                         any.a_int = i+1;        /* must be non-zero */
395                         thisch = lowc(roles[i].name.m[0]);
396                         if (thisch == lastch) thisch = highc(thisch);
397                         if (flags.initgend != ROLE_NONE && flags.initgend != ROLE_RANDOM) {
398                                 if (flags.initgend == 1  && roles[i].name.f)
399                                         Strcpy(rolenamebuf, roles[i].name.f);
400                                 else
401                                         Strcpy(rolenamebuf, roles[i].name.m);
402                         } else {
403                                 if (roles[i].name.f) {
404                                         Strcpy(rolenamebuf, roles[i].name.m);
405                                         Strcat(rolenamebuf, "/");
406                                         Strcat(rolenamebuf, roles[i].name.f);
407                                 } else 
408                                         Strcpy(rolenamebuf, roles[i].name.m);
409                         }       
410                         add_menu(win, NO_GLYPH, &any, thisch,
411                             0, ATR_NONE, an(rolenamebuf), MENU_UNSELECTED);
412                         lastch = thisch;
413                     }
414                 }
415                 any.a_int = pick_role(flags.initrace, flags.initgend,
416                                     flags.initalign, PICK_RANDOM)+1;
417                 if (any.a_int == 0)     /* must be non-zero */
418                     any.a_int = randrole()+1;
419                 add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
420                                 "Random", MENU_UNSELECTED);
421                 any.a_int = i+1;        /* must be non-zero */
422                 add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
423                                 "Quit", MENU_UNSELECTED);
424                 Sprintf(pbuf, "Pick a role for your %s", plbuf);
425                 end_menu(win, pbuf);
426                 n = select_menu(win, PICK_ONE, &selected);
427                 destroy_nhwindow(win);
428
429                 /* Process the choice */
430                 if (n != 1 || selected[0].item.a_int == any.a_int)
431                     goto give_up;               /* Selected quit */
432
433                 flags.initrole = selected[0].item.a_int - 1;
434                 free((genericptr_t) selected),  selected = 0;
435             }
436             (void)  root_plselection_prompt(plbuf, QBUFSZ - 1,
437                         flags.initrole, flags.initrace, flags.initgend, flags.initalign);
438         }
439         
440         /* Select a race, if necessary */
441         /* force compatibility with role, try for compatibility with
442          * pre-selected gender/alignment */
443         if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
444             /* pre-selected race not valid */
445             if (pick4u == 'y' || flags.initrace == ROLE_RANDOM || flags.randomall) {
446                 flags.initrace = pick_race(flags.initrole, flags.initgend,
447                                                         flags.initalign, PICK_RANDOM);
448                 if (flags.initrace < 0) {
449                     tty_putstr(BASE_WINDOW, 0, "Incompatible race!");
450                     flags.initrace = randrace(flags.initrole);
451                 }
452             } else {    /* pick4u == 'n' */
453                 /* Count the number of valid races */
454                 n = 0;  /* number valid */
455                 k = 0;  /* valid race */
456                 for (i = 0; races[i].noun; i++) {
457                     if (ok_race(flags.initrole, i, flags.initgend,
458                                                         flags.initalign)) {
459                         n++;
460                         k = i;
461                     }
462                 }
463                 if (n == 0) {
464                     for (i = 0; races[i].noun; i++) {
465                         if (validrace(flags.initrole, i)) {
466                             n++;
467                             k = i;
468                         }
469                     }
470                 }
471
472                 /* Permit the user to pick, if there is more than one */
473                 if (n > 1) {
474                     tty_clear_nhwindow(BASE_WINDOW);
475                     tty_putstr(BASE_WINDOW, 0, "Choosing Race");
476                     win = create_nhwindow(NHW_MENU);
477                     start_menu(win);
478                     any.a_void = 0;         /* zero out all bits */
479                     for (i = 0; races[i].noun; i++)
480                         if (ok_race(flags.initrole, i, flags.initgend,
481                                                         flags.initalign)) {
482                             any.a_int = i+1;    /* must be non-zero */
483                             add_menu(win, NO_GLYPH, &any, races[i].noun[0],
484                                 0, ATR_NONE, races[i].noun, MENU_UNSELECTED);
485                         }
486                     any.a_int = pick_race(flags.initrole, flags.initgend,
487                                         flags.initalign, PICK_RANDOM)+1;
488                     if (any.a_int == 0) /* must be non-zero */
489                         any.a_int = randrace(flags.initrole)+1;
490                     add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
491                                     "Random", MENU_UNSELECTED);
492                     any.a_int = i+1;    /* must be non-zero */
493                     add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
494                                     "Quit", MENU_UNSELECTED);
495                     Sprintf(pbuf, "Pick the race of your %s", plbuf);
496                     end_menu(win, pbuf);
497                     n = select_menu(win, PICK_ONE, &selected);
498                     destroy_nhwindow(win);
499                     if (n != 1 || selected[0].item.a_int == any.a_int)
500                         goto give_up;           /* Selected quit */
501
502                     k = selected[0].item.a_int - 1;
503                     free((genericptr_t) selected),      selected = 0;
504                 }
505                 flags.initrace = k;
506             }
507             (void)  root_plselection_prompt(plbuf, QBUFSZ - 1,
508                         flags.initrole, flags.initrace, flags.initgend, flags.initalign);
509         }
510
511         /* Select a gender, if necessary */
512         /* force compatibility with role/race, try for compatibility with
513          * pre-selected alignment */
514         if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace,
515                                                 flags.initgend)) {
516             /* pre-selected gender not valid */
517             if (pick4u == 'y' || flags.initgend == ROLE_RANDOM || flags.randomall) {
518                 flags.initgend = pick_gend(flags.initrole, flags.initrace,
519                                                 flags.initalign, PICK_RANDOM);
520                 if (flags.initgend < 0) {
521                     tty_putstr(BASE_WINDOW, 0, "Incompatible gender!");
522                     flags.initgend = randgend(flags.initrole, flags.initrace);
523                 }
524             } else {    /* pick4u == 'n' */
525                 /* Count the number of valid genders */
526                 n = 0;  /* number valid */
527                 k = 0;  /* valid gender */
528                 for (i = 0; i < ROLE_GENDERS; i++) {
529                     if (ok_gend(flags.initrole, flags.initrace, i,
530                                                         flags.initalign)) {
531                         n++;
532                         k = i;
533                     }
534                 }
535                 if (n == 0) {
536                     for (i = 0; i < ROLE_GENDERS; i++) {
537                         if (validgend(flags.initrole, flags.initrace, i)) {
538                             n++;
539                             k = i;
540                         }
541                     }
542                 }
543
544                 /* Permit the user to pick, if there is more than one */
545                 if (n > 1) {
546                     tty_clear_nhwindow(BASE_WINDOW);
547                     tty_putstr(BASE_WINDOW, 0, "Choosing Gender");
548                     win = create_nhwindow(NHW_MENU);
549                     start_menu(win);
550                     any.a_void = 0;         /* zero out all bits */
551                     for (i = 0; i < ROLE_GENDERS; i++)
552                         if (ok_gend(flags.initrole, flags.initrace, i,
553                                                             flags.initalign)) {
554                             any.a_int = i+1;
555                             add_menu(win, NO_GLYPH, &any, genders[i].adj[0],
556                                 0, ATR_NONE, genders[i].adj, MENU_UNSELECTED);
557                         }
558                     any.a_int = pick_gend(flags.initrole, flags.initrace,
559                                             flags.initalign, PICK_RANDOM)+1;
560                     if (any.a_int == 0) /* must be non-zero */
561                         any.a_int = randgend(flags.initrole, flags.initrace)+1;
562                     add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
563                                     "Random", MENU_UNSELECTED);
564                     any.a_int = i+1;    /* must be non-zero */
565                     add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
566                                     "Quit", MENU_UNSELECTED);
567                     Sprintf(pbuf, "Pick the gender of your %s", plbuf);
568                     end_menu(win, pbuf);
569                     n = select_menu(win, PICK_ONE, &selected);
570                     destroy_nhwindow(win);
571                     if (n != 1 || selected[0].item.a_int == any.a_int)
572                         goto give_up;           /* Selected quit */
573
574                     k = selected[0].item.a_int - 1;
575                     free((genericptr_t) selected),      selected = 0;
576                 }
577                 flags.initgend = k;
578             }
579             (void)  root_plselection_prompt(plbuf, QBUFSZ - 1,
580                         flags.initrole, flags.initrace, flags.initgend, flags.initalign);
581         }
582
583         /* Select an alignment, if necessary */
584         /* force compatibility with role/race/gender */
585         if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace,
586                                                         flags.initalign)) {
587             /* pre-selected alignment not valid */
588             if (pick4u == 'y' || flags.initalign == ROLE_RANDOM || flags.randomall) {
589                 flags.initalign = pick_align(flags.initrole, flags.initrace,
590                                                         flags.initgend, PICK_RANDOM);
591                 if (flags.initalign < 0) {
592                     tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!");
593                     flags.initalign = randalign(flags.initrole, flags.initrace);
594                 }
595             } else {    /* pick4u == 'n' */
596                 /* Count the number of valid alignments */
597                 n = 0;  /* number valid */
598                 k = 0;  /* valid alignment */
599                 for (i = 0; i < ROLE_ALIGNS; i++) {
600                     if (ok_align(flags.initrole, flags.initrace, flags.initgend,
601                                                         i)) {
602                         n++;
603                         k = i;
604                     }
605                 }
606                 if (n == 0) {
607                     for (i = 0; i < ROLE_ALIGNS; i++) {
608                         if (validalign(flags.initrole, flags.initrace, i)) {
609                             n++;
610                             k = i;
611                         }
612                     }
613                 }
614
615                 /* Permit the user to pick, if there is more than one */
616                 if (n > 1) {
617                     tty_clear_nhwindow(BASE_WINDOW);
618                     tty_putstr(BASE_WINDOW, 0, "Choosing Alignment");
619                     win = create_nhwindow(NHW_MENU);
620                     start_menu(win);
621                     any.a_void = 0;         /* zero out all bits */
622                     for (i = 0; i < ROLE_ALIGNS; i++)
623                         if (ok_align(flags.initrole, flags.initrace,
624                                                         flags.initgend, i)) {
625                             any.a_int = i+1;
626                             add_menu(win, NO_GLYPH, &any, aligns[i].adj[0],
627                                  0, ATR_NONE, aligns[i].adj, MENU_UNSELECTED);
628                         }
629                     any.a_int = pick_align(flags.initrole, flags.initrace,
630                                             flags.initgend, PICK_RANDOM)+1;
631                     if (any.a_int == 0) /* must be non-zero */
632                         any.a_int = randalign(flags.initrole, flags.initrace)+1;
633                     add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
634                                     "Random", MENU_UNSELECTED);
635                     any.a_int = i+1;    /* must be non-zero */
636                     add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
637                                     "Quit", MENU_UNSELECTED);
638                     Sprintf(pbuf, "Pick the alignment of your %s", plbuf);
639                     end_menu(win, pbuf);
640                     n = select_menu(win, PICK_ONE, &selected);
641                     destroy_nhwindow(win);
642                     if (n != 1 || selected[0].item.a_int == any.a_int)
643                         goto give_up;           /* Selected quit */
644
645                     k = selected[0].item.a_int - 1;
646                     free((genericptr_t) selected),      selected = 0;
647                 }
648                 flags.initalign = k;
649             }
650         }
651         /* Success! */
652         tty_display_nhwindow(BASE_WINDOW, FALSE);
653 }
654
655 /*
656  * plname is filled either by an option (-u Player  or  -uPlayer) or
657  * explicitly (by being the wizard) or by askname.
658  * It may still contain a suffix denoting the role, etc.
659  * Always called after init_nhwindows() and before display_gamewindows().
660  */
661 void
662 tty_askname()
663 {
664     static char who_are_you[] = "Who are you? ";
665     register int c, ct, tryct = 0;
666
667     tty_putstr(BASE_WINDOW, 0, "");
668     do {
669         if (++tryct > 1) {
670             if (tryct > 10) bail("Giving up after 10 tries.\n");
671             tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury - 1);
672             tty_putstr(BASE_WINDOW, 0, "Enter a name for your character...");
673             /* erase previous prompt (in case of ESC after partial response) */
674             tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury),  cl_end();
675         }
676         tty_putstr(BASE_WINDOW, 0, who_are_you);
677         tty_curs(BASE_WINDOW, (int)(sizeof who_are_you),
678                  wins[BASE_WINDOW]->cury - 1);
679         ct = 0;
680         while((c = tty_nhgetch()) != '\n') {
681                 if(c == EOF) error("End of input\n");
682                 if (c == '\033') { ct = 0; break; }  /* continue outer loop */
683 #if defined(WIN32CON)
684                 if (c == '\003') bail("^C abort.\n");
685 #endif
686                 /* some people get confused when their erase char is not ^H */
687                 if (c == '\b' || c == '\177') {
688                         if(ct) {
689                                 ct--;
690 #ifdef WIN32CON
691                                 ttyDisplay->curx--;
692 #endif
693 #if defined(MICRO) || defined(WIN32CON)
694 # if defined(WIN32CON) || defined(MSDOS)
695                                 backsp();       /* \b is visible on NT */
696                                 (void) putchar(' ');
697                                 backsp();
698 # else
699                                 msmsg("\b \b");
700 # endif
701 #else
702                                 (void) putchar('\b');
703                                 (void) putchar(' ');
704                                 (void) putchar('\b');
705 #endif
706                         }
707                         continue;
708                 }
709 #if defined(UNIX) || defined(VMS)
710                 if(c != '-' && c != '@')
711                 if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_';
712 #endif
713                 if (ct < (int)(sizeof plname) - 1) {
714 #if defined(MICRO)
715 # if defined(MSDOS)
716                         if (iflags.grmode) {
717                                 (void) putchar(c);
718                         } else
719 # endif
720                         msmsg("%c", c);
721 #else
722                         (void) putchar(c);
723 #endif
724                         plname[ct++] = c;
725 #ifdef WIN32CON
726                         ttyDisplay->curx++;
727 #endif
728                 }
729         }
730         plname[ct] = 0;
731     } while (ct == 0);
732
733     /* move to next line to simulate echo of user's <return> */
734     tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury + 1);
735 }
736
737 void
738 tty_get_nh_event()
739 {
740     return;
741 }
742
743 #if !defined(MICRO) && !defined(WIN32CON)
744 STATIC_OVL void
745 getret()
746 {
747         xputs("\n");
748         if(flags.standout)
749                 standoutbeg();
750         xputs("Hit ");
751         xputs(iflags.cbreak ? "space" : "return");
752         xputs(" to continue: ");
753         if(flags.standout)
754                 standoutend();
755         xwaitforspace(" ");
756 }
757 #endif
758
759 void
760 tty_suspend_nhwindows(str)
761     const char *str;
762 {
763     settty(str);                /* calls end_screen, perhaps raw_print */
764     if (!str) tty_raw_print("");        /* calls fflush(stdout) */
765 }
766
767 void
768 tty_resume_nhwindows()
769 {
770     gettty();
771     setftty();                  /* calls start_screen */
772     docrt();
773 }
774
775 void
776 tty_exit_nhwindows(str)
777     const char *str;
778 {
779     winid i;
780
781     tty_suspend_nhwindows(str);
782     /* Just forget any windows existed, since we're about to exit anyway.
783      * Disable windows to avoid calls to window routines.
784      */
785     for(i=0; i<MAXWIN; i++)
786         if (wins[i] && (i != BASE_WINDOW)) {
787 #ifdef FREE_ALL_MEMORY
788             free_window_info(wins[i], TRUE);
789             free((genericptr_t) wins[i]);
790 #endif
791             wins[i] = 0;
792         }
793 #ifndef NO_TERMS                /*(until this gets added to the window interface)*/
794     tty_shutdown();             /* cleanup termcap/terminfo/whatever */
795 #endif
796     iflags.window_inited = 0;
797 }
798
799 winid
800 tty_create_nhwindow(type)
801     int type;
802 {
803     struct WinDesc* newwin;
804     int i;
805     int newid;
806
807     if(maxwin == MAXWIN)
808         return WIN_ERR;
809
810     newwin = (struct WinDesc*) alloc(sizeof(struct WinDesc));
811     newwin->type = type;
812     newwin->flags = 0;
813     newwin->active = FALSE;
814     newwin->curx = newwin->cury = 0;
815     newwin->morestr = 0;
816     newwin->mlist = (tty_menu_item *) 0;
817     newwin->plist = (tty_menu_item **) 0;
818     newwin->npages = newwin->plist_size = newwin->nitems = newwin->how = 0;
819     switch(type) {
820     case NHW_BASE:
821         /* base window, used for absolute movement on the screen */
822         newwin->offx = newwin->offy = 0;
823         newwin->rows = ttyDisplay->rows;
824         newwin->cols = ttyDisplay->cols;
825         newwin->maxrow = newwin->maxcol = 0;
826         break;
827     case NHW_MESSAGE:
828         /* message window, 1 line long, very wide, top of screen */
829         newwin->offx = newwin->offy = 0;
830         /* sanity check */
831         if(iflags.msg_history < 20) iflags.msg_history = 20;
832         else if(iflags.msg_history > 60) iflags.msg_history = 60;
833         newwin->maxrow = newwin->rows = iflags.msg_history;
834         newwin->maxcol = newwin->cols = 0;
835         break;
836     case NHW_STATUS:
837         /* status window, 2 lines long, full width, bottom of screen */
838         newwin->offx = 0;
839 #if defined(USE_TILES) && defined(MSDOS)
840         if (iflags.grmode) {
841                 newwin->offy = ttyDisplay->rows-2;
842         } else
843 #endif
844         newwin->offy = min((int)ttyDisplay->rows-2, ROWNO+1);
845         newwin->rows = newwin->maxrow = 2;
846         newwin->cols = newwin->maxcol = min(ttyDisplay->cols, COLNO);
847         break;
848     case NHW_MAP:
849         /* map window, ROWNO lines long, full width, below message window */
850         newwin->offx = 0;
851         newwin->offy = 1;
852         newwin->rows = ROWNO;
853         newwin->cols = COLNO;
854         newwin->maxrow = 0;     /* no buffering done -- let gbuf do it */
855         newwin->maxcol = 0;
856         break;
857     case NHW_MENU:
858     case NHW_TEXT:
859         /* inventory/menu window, variable length, full width, top of screen */
860         /* help window, the same, different semantics for display, etc */
861         newwin->offx = newwin->offy = 0;
862         newwin->rows = 0;
863         newwin->cols = ttyDisplay->cols;
864         newwin->maxrow = newwin->maxcol = 0;
865         break;
866    default:
867         panic("Tried to create window type %d\n", (int) type);
868         return WIN_ERR;
869     }
870
871     for(newid = 0; newid<MAXWIN; newid++) {
872         if(wins[newid] == 0) {
873             wins[newid] = newwin;
874             break;
875         }
876     }
877     if(newid == MAXWIN) {
878         panic("No window slots!");
879         return WIN_ERR;
880     }
881
882     if(newwin->maxrow) {
883         newwin->data =
884                 (char **) alloc(sizeof(char *) * (unsigned)newwin->maxrow);
885         newwin->datlen =
886                 (short *) alloc(sizeof(short) * (unsigned)newwin->maxrow);
887         if(newwin->maxcol) {
888             for (i = 0; i < newwin->maxrow; i++) {
889                 newwin->data[i] = (char *) alloc((unsigned)newwin->maxcol);
890                 newwin->datlen[i] = newwin->maxcol;
891             }
892         } else {
893             for (i = 0; i < newwin->maxrow; i++) {
894                 newwin->data[i] = (char *) 0;
895                 newwin->datlen[i] = 0;
896             }
897         }
898         if(newwin->type == NHW_MESSAGE)
899             newwin->maxrow = 0;
900     } else {
901         newwin->data = (char **)0;
902         newwin->datlen = (short *)0;
903     }
904
905     return newid;
906 }
907
908 STATIC_OVL void
909 erase_menu_or_text(window, cw, clear)
910     winid window;
911     struct WinDesc *cw;
912     boolean clear;
913 {
914     if(cw->offx == 0)
915         if(cw->offy) {
916             tty_curs(window, 1, 0);
917             cl_eos();
918         } else if (clear)
919             clear_screen();
920         else
921             docrt();
922     else
923         docorner((int)cw->offx, cw->maxrow+1);
924 }
925
926 STATIC_OVL void
927 free_window_info(cw, free_data)
928     struct WinDesc *cw;
929     boolean free_data;
930 {
931     int i;
932
933     if (cw->data) {
934         if (cw == wins[WIN_MESSAGE] && cw->rows > cw->maxrow)
935             cw->maxrow = cw->rows;              /* topl data */
936         for(i=0; i<cw->maxrow; i++)
937             if(cw->data[i]) {
938                 free((genericptr_t)cw->data[i]);
939                 cw->data[i] = (char *)0;
940                 if (cw->datlen) cw->datlen[i] = 0;
941             }
942         if (free_data) {
943             free((genericptr_t)cw->data);
944             cw->data = (char **)0;
945             if (cw->datlen) free((genericptr_t)cw->datlen);
946             cw->datlen = (short *)0;
947             cw->rows = 0;
948         }
949     }
950     cw->maxrow = cw->maxcol = 0;
951     if(cw->mlist) {
952         tty_menu_item *temp;
953         while ((temp = cw->mlist) != 0) {
954             cw->mlist = cw->mlist->next;
955             if (temp->str) free((genericptr_t)temp->str);
956             free((genericptr_t)temp);
957         }
958     }
959     if (cw->plist) {
960         free((genericptr_t)cw->plist);
961         cw->plist = 0;
962     }
963     cw->plist_size = cw->npages = cw->nitems = cw->how = 0;
964     if(cw->morestr) {
965         free((genericptr_t)cw->morestr);
966         cw->morestr = 0;
967     }
968 }
969
970 void
971 tty_clear_nhwindow(window)
972     winid window;
973 {
974     register struct WinDesc *cw = 0;
975
976     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
977         panic(winpanicstr,  window);
978     ttyDisplay->lastwin = window;
979
980     switch(cw->type) {
981     case NHW_MESSAGE:
982         if(ttyDisplay->toplin) {
983             home();
984             cl_end();
985             if(cw->cury)
986                 docorner(1, cw->cury+1);
987             ttyDisplay->toplin = 0;
988         }
989         break;
990     case NHW_STATUS:
991         tty_curs(window, 1, 0);
992         cl_end();
993         tty_curs(window, 1, 1);
994         cl_end();
995         break;
996     case NHW_MAP:
997         /* cheap -- clear the whole thing and tell nethack to redraw botl */
998         flags.botlx = 1;
999         /* fall into ... */
1000     case NHW_BASE:
1001         clear_screen();
1002         break;
1003     case NHW_MENU:
1004     case NHW_TEXT:
1005         if(cw->active)
1006             erase_menu_or_text(window, cw, TRUE);
1007         free_window_info(cw, FALSE);
1008         break;
1009     }
1010     cw->curx = cw->cury = 0;
1011 }
1012
1013 STATIC_OVL void
1014 dmore(cw, s)
1015     register struct WinDesc *cw;
1016     const char *s;                      /* valid responses */
1017 {
1018     const char *prompt = cw->morestr ? cw->morestr : defmorestr;
1019     int offset = (cw->type == NHW_TEXT) ? 1 : 2;
1020
1021     tty_curs(BASE_WINDOW,
1022              (int)ttyDisplay->curx + offset, (int)ttyDisplay->cury);
1023     if(flags.standout)
1024         standoutbeg();
1025     xputs(prompt);
1026     ttyDisplay->curx += strlen(prompt);
1027     if(flags.standout)
1028         standoutend();
1029
1030     xwaitforspace(s);
1031 }
1032
1033 STATIC_OVL void
1034 set_item_state(window, lineno, item)
1035     winid window;
1036     int lineno;
1037     tty_menu_item *item;
1038 {
1039     char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-';
1040     tty_curs(window, 4, lineno);
1041     term_start_attr(item->attr);
1042     (void) putchar(ch);
1043     ttyDisplay->curx++;
1044     term_end_attr(item->attr);
1045 }
1046
1047 STATIC_OVL void
1048 set_all_on_page(window, page_start, page_end)
1049     winid window;
1050     tty_menu_item *page_start, *page_end;
1051 {
1052     tty_menu_item *curr;
1053     int n;
1054
1055     for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1056         if (curr->identifier.a_void && !curr->selected) {
1057             curr->selected = TRUE;
1058             set_item_state(window, n, curr);
1059         }
1060 }
1061
1062 STATIC_OVL void
1063 unset_all_on_page(window, page_start, page_end)
1064     winid window;
1065     tty_menu_item *page_start, *page_end;
1066 {
1067     tty_menu_item *curr;
1068     int n;
1069
1070     for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1071         if (curr->identifier.a_void && curr->selected) {
1072             curr->selected = FALSE;
1073             curr->count = -1L;
1074             set_item_state(window, n, curr);
1075         }
1076 }
1077
1078 STATIC_OVL void
1079 invert_all_on_page(window, page_start, page_end, acc)
1080     winid window;
1081     tty_menu_item *page_start, *page_end;
1082     char acc;   /* group accelerator, 0 => all */
1083 {
1084     tty_menu_item *curr;
1085     int n;
1086
1087     for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1088         if (curr->identifier.a_void && (acc == 0 || curr->gselector == acc)) {
1089             if (curr->selected) {
1090                 curr->selected = FALSE;
1091                 curr->count = -1L;
1092             } else
1093                 curr->selected = TRUE;
1094             set_item_state(window, n, curr);
1095         }
1096 }
1097
1098 /*
1099  * Invert all entries that match the give group accelerator (or all if
1100  * zero).
1101  */
1102 STATIC_OVL void
1103 invert_all(window, page_start, page_end, acc)
1104     winid window;
1105     tty_menu_item *page_start, *page_end;
1106     char acc;   /* group accelerator, 0 => all */
1107 {
1108     tty_menu_item *curr;
1109     boolean on_curr_page;
1110     struct WinDesc *cw =  wins[window];
1111
1112     invert_all_on_page(window, page_start, page_end, acc);
1113
1114     /* invert the rest */
1115     for (on_curr_page = FALSE, curr = cw->mlist; curr; curr = curr->next) {
1116         if (curr == page_start)
1117             on_curr_page = TRUE;
1118         else if (curr == page_end)
1119             on_curr_page = FALSE;
1120
1121         if (!on_curr_page && curr->identifier.a_void
1122                                 && (acc == 0 || curr->gselector == acc)) {
1123             if (curr->selected) {
1124                 curr->selected = FALSE;
1125                 curr->count = -1;
1126             } else
1127                 curr->selected = TRUE;
1128         }
1129     }
1130 }
1131
1132 STATIC_OVL void
1133 process_menu_window(window, cw)
1134 winid window;
1135 struct WinDesc *cw;
1136 {
1137     tty_menu_item *page_start, *page_end, *curr;
1138     long count;
1139     int n, curr_page, page_lines;
1140     boolean finished, counting, reset_count;
1141     char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ],
1142          *msave, *morestr;
1143
1144     curr_page = page_lines = 0;
1145     page_start = page_end = 0;
1146     msave = cw->morestr;        /* save the morestr */
1147     cw->morestr = morestr = (char*) alloc((unsigned) QBUFSZ);
1148     counting = FALSE;
1149     count = 0L;
1150     reset_count = TRUE;
1151     finished = FALSE;
1152
1153     /* collect group accelerators; for PICK_NONE, they're ignored;
1154        for PICK_ONE, only those which match exactly one entry will be
1155        accepted; for PICK_ANY, those which match any entry are okay */
1156     gacc[0] = '\0';
1157     if (cw->how != PICK_NONE) {
1158         int i, gcnt[128];
1159 #define GSELIDX(c) (c & 127)    /* guard against `signed char' */
1160
1161         for (i = 0; i < SIZE(gcnt); i++) gcnt[i] = 0;
1162         for (n = 0, curr = cw->mlist; curr; curr = curr->next)
1163             if (curr->gselector && curr->gselector != curr->selector) {
1164                 ++n;
1165                 ++gcnt[GSELIDX(curr->gselector)];
1166             }
1167
1168         if (n > 0)      /* at least one group accelerator found */
1169             for (rp = gacc, curr = cw->mlist; curr; curr = curr->next)
1170                 if (curr->gselector && !index(gacc, curr->gselector) &&
1171                         (cw->how == PICK_ANY ||
1172                             gcnt[GSELIDX(curr->gselector)] == 1)) {
1173                     *rp++ = curr->gselector;
1174                     *rp = '\0'; /* re-terminate for index() */
1175                 }
1176     }
1177
1178     /* loop until finished */
1179     while (!finished) {
1180         if (reset_count) {
1181             counting = FALSE;
1182             count = 0;
1183         } else
1184             reset_count = TRUE;
1185
1186         if (!page_start) {
1187             /* new page to be displayed */
1188             if (curr_page < 0 || (cw->npages > 0 && curr_page >= cw->npages))
1189                 panic("bad menu screen page #%d", curr_page);
1190
1191             /* clear screen */
1192             if (!cw->offx) {    /* if not corner, do clearscreen */
1193                 if(cw->offy) {
1194                     tty_curs(window, 1, 0);
1195                     cl_eos();
1196                 } else
1197                     clear_screen();
1198             }
1199
1200             rp = resp;
1201             if (cw->npages > 0) {
1202                 /* collect accelerators */
1203                 page_start = cw->plist[curr_page];
1204                 page_end = cw->plist[curr_page + 1];
1205                 for (page_lines = 0, curr = page_start;
1206                         curr != page_end;
1207                         page_lines++, curr = curr->next) {
1208                     if (curr->selector)
1209                         *rp++ = curr->selector;
1210
1211                     tty_curs(window, 1, page_lines);
1212                     if (cw->offx) cl_end();
1213
1214                     (void) putchar(' ');
1215                     ++ttyDisplay->curx;
1216                     /*
1217                      * Don't use xputs() because (1) under unix it calls
1218                      * tputstr() which will interpret a '*' as some kind
1219                      * of padding information and (2) it calls xputc to
1220                      * actually output the character.  We're faster doing
1221                      * this.
1222                      */
1223                     term_start_attr(curr->attr);
1224                     for (n = 0, cp = curr->str;
1225 #ifndef WIN32CON
1226                           *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
1227                           cp++, n++)
1228 #else
1229                           *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
1230                           cp++, n++, ttyDisplay->curx++)
1231 #endif
1232                         if (n == 2 && curr->identifier.a_void != 0 &&
1233                                                         curr->selected) {
1234                             if (curr->count == -1L)
1235                                 (void) putchar('+'); /* all selected */
1236                             else
1237                                 (void) putchar('#'); /* count selected */
1238                         } else
1239                             (void) putchar(*cp);
1240                     term_end_attr(curr->attr);
1241                 }
1242             } else {
1243                 page_start = 0;
1244                 page_end = 0;
1245                 page_lines = 0;
1246             }
1247             *rp = 0;
1248
1249             /* corner window - clear extra lines from last page */
1250             if (cw->offx) {
1251                 for (n = page_lines + 1; n < cw->maxrow; n++) {
1252                     tty_curs(window, 1, n);
1253                     cl_end();
1254                 }
1255             }
1256
1257             /* set extra chars.. */
1258             Strcat(resp, default_menu_cmds);
1259             Strcat(resp, "0123456789\033\n\r"); /* counts, quit */
1260             Strcat(resp, gacc);                 /* group accelerators */
1261             Strcat(resp, mapped_menu_cmds);
1262
1263             if (cw->npages > 1)
1264                 Sprintf(cw->morestr, "(%d of %d)",
1265                         curr_page + 1, (int) cw->npages);
1266             else if (msave)
1267                 Strcpy(cw->morestr, msave);
1268             else
1269                 Strcpy(cw->morestr, defmorestr);
1270
1271             tty_curs(window, 1, page_lines);
1272             cl_end();
1273             dmore(cw, resp);
1274         } else {
1275             /* just put the cursor back... */
1276             tty_curs(window, (int) strlen(cw->morestr) + 2, page_lines);
1277             xwaitforspace(resp);
1278         }
1279
1280         morc = map_menu_cmd(morc);
1281         switch (morc) {
1282             case '0':
1283                 /* special case: '0' is also the default ball class */
1284                 if (!counting && index(gacc, morc)) goto group_accel;
1285                 /* fall through to count the zero */
1286             case '1': case '2': case '3': case '4':
1287             case '5': case '6': case '7': case '8': case '9':
1288                 count = (count * 10L) + (long) (morc - '0');
1289                 /*
1290                  * It is debatable whether we should allow 0 to
1291                  * start a count.  There is no difference if the
1292                  * item is selected.  If not selected, then
1293                  * "0b" could mean:
1294                  *
1295                  *      count starting zero:    "zero b's"
1296                  *      ignore starting zero:   "select b"
1297                  *
1298                  * At present I don't know which is better.
1299                  */
1300                 if (count != 0L) {      /* ignore leading zeros */
1301                     counting = TRUE;
1302                     reset_count = FALSE;
1303                 }
1304                 break;
1305             case '\033':        /* cancel - from counting or loop */
1306                 if (!counting) {
1307                     /* deselect everything */
1308                     for (curr = cw->mlist; curr; curr = curr->next) {
1309                         curr->selected = FALSE;
1310                         curr->count = -1L;
1311                     }
1312                     cw->flags |= WIN_CANCELLED;
1313                     finished = TRUE;
1314                 }
1315                 /* else only stop count */
1316                 break;
1317             case '\0':          /* finished (commit) */
1318             case '\n':
1319             case '\r':
1320                 /* only finished if we are actually picking something */
1321                 if (cw->how != PICK_NONE) {
1322                     finished = TRUE;
1323                     break;
1324                 }
1325                 /* else fall through */
1326             case MENU_NEXT_PAGE:
1327                 if (cw->npages > 0 && curr_page != cw->npages - 1) {
1328                     curr_page++;
1329                     page_start = 0;
1330                 } else
1331                     finished = TRUE;    /* questionable behavior */
1332                 break;
1333             case MENU_PREVIOUS_PAGE:
1334                 if (cw->npages > 0 && curr_page != 0) {
1335                     --curr_page;
1336                     page_start = 0;
1337                 }
1338                 break;
1339             case MENU_FIRST_PAGE:
1340                 if (cw->npages > 0 && curr_page != 0) {
1341                     page_start = 0;
1342                     curr_page = 0;
1343                 }
1344                 break;
1345             case MENU_LAST_PAGE:
1346                 if (cw->npages > 0 && curr_page != cw->npages - 1) {
1347                     page_start = 0;
1348                     curr_page = cw->npages - 1;
1349                 }
1350                 break;
1351             case MENU_SELECT_PAGE:
1352                 if (cw->how == PICK_ANY)
1353                     set_all_on_page(window, page_start, page_end);
1354                 break;
1355             case MENU_UNSELECT_PAGE:
1356                 unset_all_on_page(window, page_start, page_end);
1357                 break;
1358             case MENU_INVERT_PAGE:
1359                 if (cw->how == PICK_ANY)
1360                     invert_all_on_page(window, page_start, page_end, 0);
1361                 break;
1362             case MENU_SELECT_ALL:
1363                 if (cw->how == PICK_ANY) {
1364                     set_all_on_page(window, page_start, page_end);
1365                     /* set the rest */
1366                     for (curr = cw->mlist; curr; curr = curr->next)
1367                         if (curr->identifier.a_void && !curr->selected)
1368                             curr->selected = TRUE;
1369                 }
1370                 break;
1371             case MENU_UNSELECT_ALL:
1372                 unset_all_on_page(window, page_start, page_end);
1373                 /* unset the rest */
1374                 for (curr = cw->mlist; curr; curr = curr->next)
1375                     if (curr->identifier.a_void && curr->selected) {
1376                         curr->selected = FALSE;
1377                         curr->count = -1;
1378                     }
1379                 break;
1380             case MENU_INVERT_ALL:
1381                 if (cw->how == PICK_ANY)
1382                     invert_all(window, page_start, page_end, 0);
1383                 break;
1384             default:
1385                 if (cw->how == PICK_NONE || !index(resp, morc)) {
1386                     /* unacceptable input received */
1387                     tty_nhbell();
1388                     break;
1389                 } else if (index(gacc, morc)) {
1390  group_accel:
1391                     /* group accelerator; for the PICK_ONE case, we know that
1392                        it matches exactly one item in order to be in gacc[] */
1393                     invert_all(window, page_start, page_end, morc);
1394                     if (cw->how == PICK_ONE) finished = TRUE;
1395                     break;
1396                 }
1397                 /* find, toggle, and possibly update */
1398                 for (n = 0, curr = page_start;
1399                         curr != page_end;
1400                         n++, curr = curr->next)
1401                     if (morc == curr->selector) {
1402                         if (curr->selected) {
1403                             if (counting && count > 0) {
1404                                 curr->count = count;
1405                                 set_item_state(window, n, curr);
1406                             } else { /* change state */
1407                                 curr->selected = FALSE;
1408                                 curr->count = -1L;
1409                                 set_item_state(window, n, curr);
1410                             }
1411                         } else {        /* !selected */
1412                             if (counting && count > 0) {
1413                                 curr->count = count;
1414                                 curr->selected = TRUE;
1415                                 set_item_state(window, n, curr);
1416                             } else if (!counting) {
1417                                 curr->selected = TRUE;
1418                                 set_item_state(window, n, curr);
1419                             }
1420                             /* do nothing counting&&count==0 */
1421                         }
1422
1423                         if (cw->how == PICK_ONE) finished = TRUE;
1424                         break;  /* from `for' loop */
1425                     }
1426                 break;
1427         }
1428
1429     } /* while */
1430     cw->morestr = msave;
1431     free((genericptr_t)morestr);
1432 }
1433
1434 STATIC_OVL void
1435 process_text_window(window, cw)
1436 winid window;
1437 struct WinDesc *cw;
1438 {
1439     int i, n, attr;
1440     register char *cp;
1441
1442     for (n = 0, i = 0; i < cw->maxrow; i++) {
1443         if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) {
1444             tty_curs(window, 1, n);
1445             cl_end();
1446             dmore(cw, quitchars);
1447             if (morc == '\033') {
1448                 cw->flags |= WIN_CANCELLED;
1449                 break;
1450             }
1451             if (cw->offy) {
1452                 tty_curs(window, 1, 0);
1453                 cl_eos();
1454             } else
1455                 clear_screen();
1456             n = 0;
1457         }
1458         tty_curs(window, 1, n++);
1459         if (cw->offx) cl_end();
1460         if (cw->data[i]) {
1461             attr = cw->data[i][0] - 1;
1462             if (cw->offx) {
1463                 (void) putchar(' '); ++ttyDisplay->curx;
1464             }
1465             term_start_attr(attr);
1466             for (cp = &cw->data[i][1];
1467 #ifndef WIN32CON
1468                     *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
1469                     cp++)
1470 #else
1471                     *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
1472                     cp++, ttyDisplay->curx++)
1473 #endif
1474                 (void) putchar(*cp);
1475             term_end_attr(attr);
1476         }
1477     }
1478     if (i == cw->maxrow) {
1479         tty_curs(BASE_WINDOW, (int)cw->offx + 1,
1480                  (cw->type == NHW_TEXT) ? (int) ttyDisplay->rows - 1 : n);
1481         cl_end();
1482         dmore(cw, quitchars);
1483         if (morc == '\033')
1484             cw->flags |= WIN_CANCELLED;
1485     }
1486 }
1487
1488 /*ARGSUSED*/
1489 void
1490 tty_display_nhwindow(window, blocking)
1491     winid window;
1492     boolean blocking;   /* with ttys, all windows are blocking */
1493 {
1494     register struct WinDesc *cw = 0;
1495
1496     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1497         panic(winpanicstr,  window);
1498     if(cw->flags & WIN_CANCELLED)
1499         return;
1500     ttyDisplay->lastwin = window;
1501     ttyDisplay->rawprint = 0;
1502
1503     switch(cw->type) {
1504     case NHW_MESSAGE:
1505         if(ttyDisplay->toplin == 1) {
1506             more();
1507             ttyDisplay->toplin = 1; /* more resets this */
1508             tty_clear_nhwindow(window);
1509         } else
1510             ttyDisplay->toplin = 0;
1511         cw->curx = cw->cury = 0;
1512         if(!cw->active)
1513             iflags.window_inited = TRUE;
1514         break;
1515     case NHW_MAP:
1516         end_glyphout();
1517         if(blocking) {
1518             if(!ttyDisplay->toplin) ttyDisplay->toplin = 1;
1519             tty_display_nhwindow(WIN_MESSAGE, TRUE);
1520             return;
1521         }
1522     case NHW_BASE:
1523         (void) fflush(stdout);
1524         break;
1525     case NHW_TEXT:
1526         cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
1527         /*FALLTHRU*/
1528     case NHW_MENU:
1529         cw->active = 1;
1530         /* avoid converting to uchar before calculations are finished */
1531         cw->offx = (uchar) (int)
1532             max((int) 10, (int) (ttyDisplay->cols - cw->maxcol - 1));
1533         if(cw->type == NHW_MENU)
1534             cw->offy = 0;
1535         if(ttyDisplay->toplin == 1)
1536             tty_display_nhwindow(WIN_MESSAGE, TRUE);
1537         if(cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows) {
1538             cw->offx = 0;
1539             if(cw->offy) {
1540                 tty_curs(window, 1, 0);
1541                 cl_eos();
1542             } else
1543                 clear_screen();
1544             ttyDisplay->toplin = 0;
1545         } else
1546             tty_clear_nhwindow(WIN_MESSAGE);
1547
1548         if (cw->data || !cw->maxrow)
1549             process_text_window(window, cw);
1550         else
1551             process_menu_window(window, cw);
1552         break;
1553     }
1554     cw->active = 1;
1555 }
1556
1557 void
1558 tty_dismiss_nhwindow(window)
1559     winid window;
1560 {
1561     register struct WinDesc *cw = 0;
1562
1563     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1564         panic(winpanicstr,  window);
1565
1566     switch(cw->type) {
1567     case NHW_MESSAGE:
1568         if (ttyDisplay->toplin)
1569             tty_display_nhwindow(WIN_MESSAGE, TRUE);
1570         /*FALLTHRU*/
1571     case NHW_STATUS:
1572     case NHW_BASE:
1573     case NHW_MAP:
1574         /*
1575          * these should only get dismissed when the game is going away
1576          * or suspending
1577          */
1578         tty_curs(BASE_WINDOW, 1, (int)ttyDisplay->rows-1);
1579         cw->active = 0;
1580         break;
1581     case NHW_MENU:
1582     case NHW_TEXT:
1583         if(cw->active) {
1584             if (iflags.window_inited) {
1585                 /* otherwise dismissing the text endwin after other windows
1586                  * are dismissed tries to redraw the map and panics.  since
1587                  * the whole reason for dismissing the other windows was to
1588                  * leave the ending window on the screen, we don't want to
1589                  * erase it anyway.
1590                  */
1591                 erase_menu_or_text(window, cw, FALSE);
1592             }
1593             cw->active = 0;
1594         }
1595         break;
1596     }
1597     cw->flags = 0;
1598 }
1599
1600 void
1601 tty_destroy_nhwindow(window)
1602     winid window;
1603 {
1604     register struct WinDesc *cw = 0;
1605
1606     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1607         panic(winpanicstr,  window);
1608
1609     if(cw->active)
1610         tty_dismiss_nhwindow(window);
1611     if(cw->type == NHW_MESSAGE)
1612         iflags.window_inited = 0;
1613     if(cw->type == NHW_MAP)
1614         clear_screen();
1615
1616     free_window_info(cw, TRUE);
1617     free((genericptr_t)cw);
1618     wins[window] = 0;
1619 }
1620
1621 void
1622 tty_curs(window, x, y)
1623 winid window;
1624 register int x, y;      /* not xchar: perhaps xchar is unsigned and
1625                            curx-x would be unsigned as well */
1626 {
1627     struct WinDesc *cw = 0;
1628     int cx = ttyDisplay->curx;
1629     int cy = ttyDisplay->cury;
1630
1631     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1632         panic(winpanicstr,  window);
1633     ttyDisplay->lastwin = window;
1634
1635 #if defined(USE_TILES) && defined(MSDOS)
1636     adjust_cursor_flags(cw);
1637 #endif
1638     cw->curx = --x;     /* column 0 is never used */
1639     cw->cury = y;
1640 #ifdef DEBUG
1641     if(x<0 || y<0 || y >= cw->rows || x > cw->cols) {
1642         const char *s = "[unknown type]";
1643         switch(cw->type) {
1644         case NHW_MESSAGE: s = "[topl window]"; break;
1645         case NHW_STATUS: s = "[status window]"; break;
1646         case NHW_MAP: s = "[map window]"; break;
1647         case NHW_MENU: s = "[corner window]"; break;
1648         case NHW_TEXT: s = "[text window]"; break;
1649         case NHW_BASE: s = "[base window]"; break;
1650         }
1651         impossible("bad curs positioning win %d %s (%d,%d)", window, s, x, y);
1652         return;
1653     }
1654 #endif
1655     x += cw->offx;
1656     y += cw->offy;
1657
1658 #ifdef CLIPPING
1659     if(clipping && window == WIN_MAP) {
1660         x -= clipx;
1661         y -= clipy;
1662     }
1663 #endif
1664
1665     if (y == cy && x == cx)
1666         return;
1667
1668     if(cw->type == NHW_MAP)
1669         end_glyphout();
1670
1671 #ifndef NO_TERMS
1672     if(!nh_ND && (cx != x || x <= 3)) { /* Extremely primitive */
1673         cmov(x, y); /* bunker!wtm */
1674         return;
1675     }
1676 #endif
1677
1678     if((cy -= y) < 0) cy = -cy;
1679     if((cx -= x) < 0) cx = -cx;
1680     if(cy <= 3 && cx <= 3) {
1681         nocmov(x, y);
1682 #ifndef NO_TERMS
1683     } else if ((x <= 3 && cy <= 3) || (!nh_CM && x < cx)) {
1684         (void) putchar('\r');
1685         ttyDisplay->curx = 0;
1686         nocmov(x, y);
1687     } else if (!nh_CM) {
1688         nocmov(x, y);
1689 #endif
1690     } else
1691         cmov(x, y);
1692
1693     ttyDisplay->curx = x;
1694     ttyDisplay->cury = y;
1695 }
1696
1697 STATIC_OVL void
1698 tty_putsym(window, x, y, ch)
1699     winid window;
1700     int x, y;
1701     char ch;
1702 {
1703     register struct WinDesc *cw = 0;
1704
1705     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1706         panic(winpanicstr,  window);
1707
1708     switch(cw->type) {
1709     case NHW_STATUS:
1710     case NHW_MAP:
1711     case NHW_BASE:
1712         tty_curs(window, x, y);
1713         (void) putchar(ch);
1714         ttyDisplay->curx++;
1715         cw->curx++;
1716         break;
1717     case NHW_MESSAGE:
1718     case NHW_MENU:
1719     case NHW_TEXT:
1720         impossible("Can't putsym to window type %d", cw->type);
1721         break;
1722     }
1723 }
1724
1725
1726 STATIC_OVL const char*
1727 compress_str(str)
1728 const char *str;
1729 {
1730         static char cbuf[BUFSZ];
1731         /* compress in case line too long */
1732         if((int)strlen(str) >= CO) {
1733                 register const char *bp0 = str;
1734                 register char *bp1 = cbuf;
1735
1736                 do {
1737 #ifdef CLIPPING
1738                         if(*bp0 != ' ' || bp0[1] != ' ')
1739 #else
1740                         if(*bp0 != ' ' || bp0[1] != ' ' || bp0[2] != ' ')
1741 #endif
1742                                 *bp1++ = *bp0;
1743                 } while(*bp0++);
1744         } else
1745             return str;
1746         return cbuf;
1747 }
1748
1749 void
1750 tty_putstr(window, attr, str)
1751     winid window;
1752     int attr;
1753     const char *str;
1754 {
1755     register struct WinDesc *cw = 0;
1756     register char *ob;
1757     register const char *nb;
1758     register int i, j, n0;
1759
1760     /* Assume there's a real problem if the window is missing --
1761      * probably a panic message
1762      */
1763     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) {
1764         tty_raw_print(str);
1765         return;
1766     }
1767
1768     if(str == (const char*)0 ||
1769         ((cw->flags & WIN_CANCELLED) && (cw->type != NHW_MESSAGE)))
1770         return;
1771     if(cw->type != NHW_MESSAGE)
1772         str = compress_str(str);
1773
1774     ttyDisplay->lastwin = window;
1775
1776     switch(cw->type) {
1777     case NHW_MESSAGE:
1778         /* really do this later */
1779 #if defined(USER_SOUNDS) && defined(WIN32CON)
1780         play_sound_for_message(str);
1781 #endif
1782         update_topl(str);
1783         break;
1784
1785     case NHW_STATUS:
1786         ob = &cw->data[cw->cury][j = cw->curx];
1787         if(flags.botlx) *ob = 0;
1788         if(!cw->cury && (int)strlen(str) >= CO) {
1789             /* the characters before "St:" are unnecessary */
1790             nb = index(str, ':');
1791             if(nb && nb > str+2)
1792                 str = nb - 2;
1793         }
1794         nb = str;
1795         for(i = cw->curx+1, n0 = cw->cols; i < n0; i++, nb++) {
1796             if(!*nb) {
1797                 if(*ob || flags.botlx) {
1798                     /* last char printed may be in middle of line */
1799                     tty_curs(WIN_STATUS, i, cw->cury);
1800                     cl_end();
1801                 }
1802                 break;
1803             }
1804             if(*ob != *nb)
1805                 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
1806             if(*ob) ob++;
1807         }
1808
1809         (void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1);
1810         cw->data[cw->cury][cw->cols-1] = '\0'; /* null terminate */
1811         cw->cury = (cw->cury+1) % 2;
1812         cw->curx = 0;
1813         break;
1814     case NHW_MAP:
1815         tty_curs(window, cw->curx+1, cw->cury);
1816         term_start_attr(attr);
1817         while(*str && (int) ttyDisplay->curx < (int) ttyDisplay->cols-1) {
1818             (void) putchar(*str);
1819             str++;
1820             ttyDisplay->curx++;
1821         }
1822         cw->curx = 0;
1823         cw->cury++;
1824         term_end_attr(attr);
1825         break;
1826     case NHW_BASE:
1827         tty_curs(window, cw->curx+1, cw->cury);
1828         term_start_attr(attr);
1829         while (*str) {
1830             if ((int) ttyDisplay->curx >= (int) ttyDisplay->cols-1) {
1831                 cw->curx = 0;
1832                 cw->cury++;
1833                 tty_curs(window, cw->curx+1, cw->cury);
1834             }
1835             (void) putchar(*str);
1836             str++;
1837             ttyDisplay->curx++;
1838         }
1839         cw->curx = 0;
1840         cw->cury++;
1841         term_end_attr(attr);
1842         break;
1843     case NHW_MENU:
1844     case NHW_TEXT:
1845         if(cw->type == NHW_TEXT && cw->cury == ttyDisplay->rows-1) {
1846             /* not a menu, so save memory and output 1 page at a time */
1847             cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
1848             tty_display_nhwindow(window, TRUE);
1849             for(i=0; i<cw->maxrow; i++)
1850                 if(cw->data[i]){
1851                     free((genericptr_t)cw->data[i]);
1852                     cw->data[i] = 0;
1853                 }
1854             cw->maxrow = cw->cury = 0;
1855         }
1856         /* always grows one at a time, but alloc 12 at a time */
1857         if(cw->cury >= cw->rows) {
1858             char **tmp;
1859
1860             cw->rows += 12;
1861             tmp = (char **) alloc(sizeof(char *) * (unsigned)cw->rows);
1862             for(i=0; i<cw->maxrow; i++)
1863                 tmp[i] = cw->data[i];
1864             if(cw->data)
1865                 free((genericptr_t)cw->data);
1866             cw->data = tmp;
1867
1868             for(i=cw->maxrow; i<cw->rows; i++)
1869                 cw->data[i] = 0;
1870         }
1871         if(cw->data[cw->cury])
1872             free((genericptr_t)cw->data[cw->cury]);
1873         n0 = strlen(str) + 1;
1874         ob = cw->data[cw->cury] = (char *)alloc((unsigned)n0 + 1);
1875         *ob++ = (char)(attr + 1);       /* avoid nuls, for convenience */
1876         Strcpy(ob, str);
1877
1878         if(n0 > cw->maxcol)
1879             cw->maxcol = n0;
1880         if(++cw->cury > cw->maxrow)
1881             cw->maxrow = cw->cury;
1882         if(n0 > CO) {
1883             /* attempt to break the line */
1884             for(i = CO-1; i && str[i] != ' ' && str[i] != '\n';)
1885                 i--;
1886             if(i) {
1887                 cw->data[cw->cury-1][++i] = '\0';
1888                 tty_putstr(window, attr, &str[i]);
1889             }
1890
1891         }
1892         break;
1893     }
1894 }
1895
1896 void
1897 tty_display_file(fname, complain)
1898 const char *fname;
1899 boolean complain;
1900 {
1901 #ifdef DEF_PAGER                        /* this implies that UNIX is defined */
1902     {
1903         /* use external pager; this may give security problems */
1904         register int fd = open(fname, 0);
1905
1906         if(fd < 0) {
1907             if(complain) pline("Cannot open %s.", fname);
1908             else docrt();
1909             return;
1910         }
1911         if(child(1)) {
1912             /* Now that child() does a setuid(getuid()) and a chdir(),
1913                we may not be able to open file fname anymore, so make
1914                it stdin. */
1915             (void) close(0);
1916             if(dup(fd)) {
1917                 if(complain) raw_printf("Cannot open %s as stdin.", fname);
1918             } else {
1919                 (void) execlp(catmore, "page", (char *)0);
1920                 if(complain) raw_printf("Cannot exec %s.", catmore);
1921             }
1922             if(complain) sleep(10); /* want to wait_synch() but stdin is gone */
1923             terminate(EXIT_FAILURE);
1924         }
1925         (void) close(fd);
1926     }
1927 #else   /* DEF_PAGER */
1928     {
1929         dlb *f;
1930         char buf[BUFSZ];
1931         char *cr;
1932
1933         tty_clear_nhwindow(WIN_MESSAGE);
1934         f = dlb_fopen(fname, "r");
1935         if (!f) {
1936             if(complain) {
1937                 home();  tty_mark_synch();  tty_raw_print("");
1938                 perror(fname);  tty_wait_synch();
1939                 pline("Cannot open \"%s\".", fname);
1940             } else if(u.ux) docrt();
1941         } else {
1942             winid datawin = tty_create_nhwindow(NHW_TEXT);
1943             boolean empty = TRUE;
1944
1945             if(complain
1946 #ifndef NO_TERMS
1947                 && nh_CD
1948 #endif
1949             ) {
1950                 /* attempt to scroll text below map window if there's room */
1951                 wins[datawin]->offy = wins[WIN_STATUS]->offy+3;
1952                 if((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows)
1953                     wins[datawin]->offy = 0;
1954             }
1955             while (dlb_fgets(buf, BUFSZ, f)) {
1956                 if ((cr = index(buf, '\n')) != 0) *cr = 0;
1957 #ifdef MSDOS
1958                 if ((cr = index(buf, '\r')) != 0) *cr = 0;
1959 #endif
1960                 if (index(buf, '\t') != 0) (void) tabexpand(buf);
1961                 empty = FALSE;
1962                 tty_putstr(datawin, 0, buf);
1963                 if(wins[datawin]->flags & WIN_CANCELLED)
1964                     break;
1965             }
1966             if (!empty) tty_display_nhwindow(datawin, FALSE);
1967             tty_destroy_nhwindow(datawin);
1968             (void) dlb_fclose(f);
1969         }
1970     }
1971 #endif /* DEF_PAGER */
1972 }
1973
1974 void
1975 tty_start_menu(window)
1976     winid window;
1977 {
1978     tty_clear_nhwindow(window);
1979     return;
1980 }
1981
1982 /*ARGSUSED*/
1983 /*
1984  * Add a menu item to the beginning of the menu list.  This list is reversed
1985  * later.
1986  */
1987 void
1988 tty_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
1989     winid window;       /* window to use, must be of type NHW_MENU */
1990     int glyph;          /* glyph to display with item (unused) */
1991     const anything *identifier; /* what to return if selected */
1992     char ch;            /* keyboard accelerator (0 = pick our own) */
1993     char gch;           /* group accelerator (0 = no group) */
1994     int attr;           /* attribute for string (like tty_putstr()) */
1995     const char *str;    /* menu string */
1996     boolean preselected; /* item is marked as selected */
1997 {
1998     register struct WinDesc *cw = 0;
1999     tty_menu_item *item;
2000     const char *newstr;
2001     char buf[4+BUFSZ];
2002
2003     if (str == (const char*) 0)
2004         return;
2005
2006     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
2007                 || cw->type != NHW_MENU)
2008         panic(winpanicstr,  window);
2009
2010     cw->nitems++;
2011     if (identifier->a_void) {
2012         int len = strlen(str);
2013         if (len >= BUFSZ) {
2014             /* We *think* everything's coming in off at most BUFSZ bufs... */
2015             impossible("Menu item too long (%d).", len);
2016             len = BUFSZ - 1;
2017         }
2018         Sprintf(buf, "%c - ", ch ? ch : '?');
2019         (void) strncpy(buf+4, str, len);
2020         buf[4+len] = '\0';
2021         newstr = buf;
2022     } else
2023         newstr = str;
2024
2025     item = (tty_menu_item *) alloc(sizeof(tty_menu_item));
2026     item->identifier = *identifier;
2027     item->count = -1L;
2028     item->selected = preselected;
2029     item->selector = ch;
2030     item->gselector = gch;
2031     item->attr = attr;
2032     item->str = copy_of(newstr);
2033
2034     item->next = cw->mlist;
2035     cw->mlist = item;
2036 }
2037
2038 /* Invert the given list, can handle NULL as an input. */
2039 STATIC_OVL tty_menu_item *
2040 reverse(curr)
2041     tty_menu_item *curr;
2042 {
2043     tty_menu_item *next, *head = 0;
2044
2045     while (curr) {
2046         next = curr->next;
2047         curr->next = head;
2048         head = curr;
2049         curr = next;
2050     }
2051     return head;
2052 }
2053
2054 /*
2055  * End a menu in this window, window must a type NHW_MENU.  This routine
2056  * processes the string list.  We calculate the # of pages, then assign
2057  * keyboard accelerators as needed.  Finally we decide on the width and
2058  * height of the window.
2059  */
2060 void
2061 tty_end_menu(window, prompt)
2062     winid window;       /* menu to use */
2063     const char *prompt; /* prompt to for menu */
2064 {
2065     struct WinDesc *cw = 0;
2066     tty_menu_item *curr;
2067     short len;
2068     int lmax, n;
2069     char menu_ch;
2070
2071     if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0 ||
2072                 cw->type != NHW_MENU)
2073         panic(winpanicstr,  window);
2074
2075     /* Reverse the list so that items are in correct order. */
2076     cw->mlist = reverse(cw->mlist);
2077
2078     /* Put the promt at the beginning of the menu. */
2079     if (prompt) {
2080         anything any;
2081
2082         any.a_void = 0; /* not selectable */
2083         tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
2084         tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt, MENU_UNSELECTED);
2085     }
2086
2087     lmax = min(52, (int)ttyDisplay->rows - 1);          /* # lines per page */
2088     cw->npages = (cw->nitems + (lmax - 1)) / lmax;      /* # of pages */
2089
2090     /* make sure page list is large enough */
2091     if (cw->plist_size < cw->npages+1 /*need 1 slot beyond last*/) {
2092         if (cw->plist) free((genericptr_t)cw->plist);
2093         cw->plist_size = cw->npages + 1;
2094         cw->plist = (tty_menu_item **)
2095                         alloc(cw->plist_size * sizeof(tty_menu_item *));
2096     }
2097
2098     cw->cols = 0; /* cols is set when the win is initialized... (why?) */
2099     menu_ch = '?';      /* lint suppression */
2100     for (n = 0, curr = cw->mlist; curr; n++, curr = curr->next) {
2101         /* set page boundaries and character accelerators */
2102         if ((n % lmax) == 0) {
2103             menu_ch = 'a';
2104             cw->plist[n/lmax] = curr;
2105         }
2106         if (curr->identifier.a_void && !curr->selector) {
2107             curr->str[0] = curr->selector = menu_ch;
2108             if (menu_ch++ == 'z') menu_ch = 'A';
2109         }
2110
2111         /* cut off any lines that are too long */
2112         len = strlen(curr->str) + 2;    /* extra space at beg & end */
2113         if (len > (int)ttyDisplay->cols) {
2114             curr->str[ttyDisplay->cols-2] = 0;
2115             len = ttyDisplay->cols;
2116         }
2117         if (len > cw->cols) cw->cols = len;
2118     }
2119     cw->plist[cw->npages] = 0;  /* plist terminator */
2120
2121     /*
2122      * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
2123      */
2124     if (cw->npages > 1) {
2125         char buf[QBUFSZ];
2126         /* produce the largest demo string */
2127         Sprintf(buf, "(%d of %d) ", cw->npages, cw->npages);
2128         len = strlen(buf);
2129         cw->morestr = copy_of("");
2130     } else {
2131         cw->morestr = copy_of("(end) ");
2132         len = strlen(cw->morestr);
2133     }
2134
2135     if (len > (int)ttyDisplay->cols) {
2136         /* truncate the prompt if its too long for the screen */
2137         if (cw->npages <= 1)    /* only str in single page case */
2138             cw->morestr[ttyDisplay->cols] = 0;
2139         len = ttyDisplay->cols;
2140     }
2141     if (len > cw->cols) cw->cols = len;
2142
2143     cw->maxcol = cw->cols;
2144
2145     /*
2146      * The number of lines in the first page plus the morestr will be the
2147      * maximum size of the window.
2148      */
2149     if (cw->npages > 1)
2150         cw->maxrow = cw->rows = lmax + 1;
2151     else
2152         cw->maxrow = cw->rows = cw->nitems + 1;
2153 }
2154
2155 int
2156 tty_select_menu(window, how, menu_list)
2157     winid window;
2158     int how;
2159     menu_item **menu_list;
2160 {
2161     register struct WinDesc *cw = 0;
2162     tty_menu_item *curr;
2163     menu_item *mi;
2164     int n, cancelled;
2165
2166     if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
2167        || cw->type != NHW_MENU)
2168         panic(winpanicstr,  window);
2169
2170     *menu_list = (menu_item *) 0;
2171     cw->how = (short) how;
2172     morc = 0;
2173     tty_display_nhwindow(window, TRUE);
2174     cancelled = !!(cw->flags & WIN_CANCELLED);
2175     tty_dismiss_nhwindow(window);       /* does not destroy window data */
2176
2177     if (cancelled) {
2178         n = -1;
2179     } else {
2180         for (n = 0, curr = cw->mlist; curr; curr = curr->next)
2181             if (curr->selected) n++;
2182     }
2183
2184     if (n > 0) {
2185         *menu_list = (menu_item *) alloc(n * sizeof(menu_item));
2186         for (mi = *menu_list, curr = cw->mlist; curr; curr = curr->next)
2187             if (curr->selected) {
2188                 mi->item = curr->identifier;
2189                 mi->count = curr->count;
2190                 mi++;
2191             }
2192     }
2193
2194     return n;
2195 }
2196
2197 /* special hack for treating top line --More-- as a one item menu */
2198 char
2199 tty_message_menu(let, how, mesg)
2200 char let;
2201 int how;
2202 const char *mesg;
2203 {
2204     /* "menu" without selection; use ordinary pline, no more() */
2205     if (how == PICK_NONE) {
2206         pline("%s", mesg);
2207         return 0;
2208     }
2209
2210     ttyDisplay->dismiss_more = let;
2211     morc = 0;
2212     /* barebones pline(); since we're only supposed to be called after
2213        response to a prompt, we'll assume that the display is up to date */
2214     tty_putstr(WIN_MESSAGE, 0, mesg);
2215     /* if `mesg' didn't wrap (triggering --More--), force --More-- now */
2216     if (ttyDisplay->toplin == 1) {
2217         more();
2218         ttyDisplay->toplin = 1; /* more resets this */
2219         tty_clear_nhwindow(WIN_MESSAGE);
2220     }
2221     /* normally <ESC> means skip further messages, but in this case
2222        it means cancel the current prompt; any other messages should
2223        continue to be output normally */
2224     wins[WIN_MESSAGE]->flags &= ~WIN_CANCELLED;
2225     ttyDisplay->dismiss_more = 0;
2226
2227     return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0';
2228 }
2229
2230 void
2231 tty_update_inventory()
2232 {
2233     return;
2234 }
2235
2236 void
2237 tty_mark_synch()
2238 {
2239     (void) fflush(stdout);
2240 }
2241
2242 void
2243 tty_wait_synch()
2244 {
2245     /* we just need to make sure all windows are synch'd */
2246     if(!ttyDisplay || ttyDisplay->rawprint) {
2247         getret();
2248         if(ttyDisplay) ttyDisplay->rawprint = 0;
2249     } else {
2250         tty_display_nhwindow(WIN_MAP, FALSE);
2251         if(ttyDisplay->inmore) {
2252             addtopl("--More--");
2253             (void) fflush(stdout);
2254         } else if(ttyDisplay->inread > program_state.gameover) {
2255             /* this can only happen if we were reading and got interrupted */
2256             ttyDisplay->toplin = 3;
2257             /* do this twice; 1st time gets the Quit? message again */
2258             (void) tty_doprev_message();
2259             (void) tty_doprev_message();
2260             ttyDisplay->intr++;
2261             (void) fflush(stdout);
2262         }
2263     }
2264 }
2265
2266 void
2267 docorner(xmin, ymax)
2268     register int xmin, ymax;
2269 {
2270     register int y;
2271     register struct WinDesc *cw = wins[WIN_MAP];
2272
2273     if (u.uswallow) {   /* Can be done more efficiently */
2274         swallowed(1);
2275         return;
2276     }
2277
2278 #if defined(SIGWINCH) && defined(CLIPPING)
2279     if(ymax > LI) ymax = LI;            /* can happen if window gets smaller */
2280 #endif
2281     for (y = 0; y < ymax; y++) {
2282         tty_curs(BASE_WINDOW, xmin,y);  /* move cursor */
2283         cl_end();                       /* clear to end of line */
2284 #ifdef CLIPPING
2285         if (y<(int) cw->offy || y+clipy > ROWNO)
2286                 continue; /* only refresh board */
2287 #if defined(USE_TILES) && defined(MSDOS)
2288         if (iflags.tile_view)
2289                 row_refresh((xmin/2)+clipx-((int)cw->offx/2),COLNO-1,y+clipy-(int)cw->offy);
2290         else
2291 #endif
2292         row_refresh(xmin+clipx-(int)cw->offx,COLNO-1,y+clipy-(int)cw->offy);
2293 #else
2294         if (y<cw->offy || y > ROWNO) continue; /* only refresh board  */
2295         row_refresh(xmin-(int)cw->offx,COLNO-1,y-(int)cw->offy);
2296 #endif
2297     }
2298
2299     end_glyphout();
2300     if (ymax >= (int) wins[WIN_STATUS]->offy) {
2301                                         /* we have wrecked the bottom line */
2302         flags.botlx = 1;
2303         bot();
2304     }
2305 }
2306
2307 void
2308 end_glyphout()
2309 {
2310 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
2311     if (GFlag) {
2312         GFlag = FALSE;
2313         graph_off();
2314     }
2315 #endif
2316 #ifdef TEXTCOLOR
2317     if(ttyDisplay->color != NO_COLOR) {
2318         term_end_color();
2319         ttyDisplay->color = NO_COLOR;
2320     }
2321 #endif
2322 }
2323
2324 #ifndef WIN32
2325 void
2326 g_putch(in_ch)
2327 int in_ch;
2328 {
2329     register char ch = (char)in_ch;
2330
2331 # if defined(ASCIIGRAPH) && !defined(NO_TERMS)
2332     if (iflags.IBMgraphics || iflags.eight_bit_tty) {
2333         /* IBM-compatible displays don't need other stuff */
2334         (void) putchar(ch);
2335     } else if (ch & 0x80) {
2336         if (!GFlag || HE_resets_AS) {
2337             graph_on();
2338             GFlag = TRUE;
2339         }
2340         (void) putchar((ch ^ 0x80)); /* Strip 8th bit */
2341     } else {
2342         if (GFlag) {
2343             graph_off();
2344             GFlag = FALSE;
2345         }
2346         (void) putchar(ch);
2347     }
2348
2349 #else
2350     (void) putchar(ch);
2351
2352 #endif  /* ASCIIGRAPH && !NO_TERMS */
2353
2354     return;
2355 }
2356 #endif /* !WIN32 */
2357
2358 #ifdef CLIPPING
2359 void
2360 setclipped()
2361 {
2362         clipping = TRUE;
2363         clipx = clipy = 0;
2364         clipxmax = CO;
2365         clipymax = LI - 3;
2366 }
2367
2368 void
2369 tty_cliparound(x, y)
2370 int x, y;
2371 {
2372         extern boolean restoring;
2373         int oldx = clipx, oldy = clipy;
2374
2375         if (!clipping) return;
2376         if (x < clipx + 5) {
2377                 clipx = max(0, x - 20);
2378                 clipxmax = clipx + CO;
2379         }
2380         else if (x > clipxmax - 5) {
2381                 clipxmax = min(COLNO, clipxmax + 20);
2382                 clipx = clipxmax - CO;
2383         }
2384         if (y < clipy + 2) {
2385                 clipy = max(0, y - (clipymax - clipy) / 2);
2386                 clipymax = clipy + (LI - 3);
2387         }
2388         else if (y > clipymax - 2) {
2389                 clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2);
2390                 clipy = clipymax - (LI - 3);
2391         }
2392         if (clipx != oldx || clipy != oldy) {
2393             if (on_level(&u.uz0, &u.uz) && !restoring)
2394                 (void) doredraw();
2395         }
2396 }
2397 #endif /* CLIPPING */
2398
2399
2400 /*
2401  *  tty_print_glyph
2402  *
2403  *  Print the glyph to the output device.  Don't flush the output device.
2404  *
2405  *  Since this is only called from show_glyph(), it is assumed that the
2406  *  position and glyph are always correct (checked there)!
2407  */
2408
2409 void
2410 tty_print_glyph(window, x, y, glyph)
2411     winid window;
2412     xchar x, y;
2413     int glyph;
2414 {
2415     int ch;
2416     boolean reverse_on = FALSE;
2417     int     color;
2418     unsigned special;
2419     
2420 #ifdef CLIPPING
2421     if(clipping) {
2422         if(x <= clipx || y < clipy || x >= clipxmax || y >= clipymax)
2423             return;
2424     }
2425 #endif
2426     /* map glyph to character and color */
2427     mapglyph(glyph, &ch, &color, &special, x, y);
2428
2429     /* Move the cursor. */
2430     tty_curs(window, x,y);
2431
2432 #ifndef NO_TERMS
2433     if (ul_hack && ch == '_') {         /* non-destructive underscore */
2434         (void) putchar((char) ' ');
2435         backsp();
2436     }
2437 #endif
2438
2439 #ifdef TEXTCOLOR
2440     if (color != ttyDisplay->color) {
2441         if(ttyDisplay->color != NO_COLOR)
2442             term_end_color();
2443         ttyDisplay->color = color;
2444         if(color != NO_COLOR)
2445             term_start_color(color);
2446     }
2447 #endif /* TEXTCOLOR */
2448
2449     /* must be after color check; term_end_color may turn off inverse too */
2450     if (((special & MG_PET) && iflags.hilite_pet) ||
2451         ((special & MG_DETECT) && iflags.use_inverse)) {
2452         term_start_attr(ATR_INVERSE);
2453         reverse_on = TRUE;
2454     }
2455
2456 #if defined(USE_TILES) && defined(MSDOS)
2457     if (iflags.grmode && iflags.tile_view)
2458       xputg(glyph,ch,special);
2459     else
2460 #endif
2461         g_putch(ch);            /* print the character */
2462
2463     if (reverse_on) {
2464         term_end_attr(ATR_INVERSE);
2465 #ifdef TEXTCOLOR
2466         /* turn off color as well, ATR_INVERSE may have done this already */
2467         if(ttyDisplay->color != NO_COLOR) {
2468             term_end_color();
2469             ttyDisplay->color = NO_COLOR;
2470         }
2471 #endif
2472     }
2473
2474     wins[window]->curx++;       /* one character over */
2475     ttyDisplay->curx++;         /* the real cursor moved too */
2476 }
2477
2478 void
2479 tty_raw_print(str)
2480     const char *str;
2481 {
2482     if(ttyDisplay) ttyDisplay->rawprint++;
2483 #if defined(MICRO) || defined(WIN32CON)
2484     msmsg("%s\n", str);
2485 #else
2486     puts(str); (void) fflush(stdout);
2487 #endif
2488 }
2489
2490 void
2491 tty_raw_print_bold(str)
2492     const char *str;
2493 {
2494     if(ttyDisplay) ttyDisplay->rawprint++;
2495     term_start_raw_bold();
2496 #if defined(MICRO) || defined(WIN32CON)
2497     msmsg("%s", str);
2498 #else
2499     (void) fputs(str, stdout);
2500 #endif
2501     term_end_raw_bold();
2502 #if defined(MICRO) || defined(WIN32CON)
2503     msmsg("\n");
2504 #else
2505     puts("");
2506     (void) fflush(stdout);
2507 #endif
2508 }
2509
2510 int
2511 tty_nhgetch()
2512 {
2513     int i;
2514 #ifdef UNIX
2515     /* kludge alert: Some Unix variants return funny values if getc()
2516      * is called, interrupted, and then called again.  There
2517      * is non-reentrant code in the internal _filbuf() routine, called by
2518      * getc().
2519      */
2520     static volatile int nesting = 0;
2521     char nestbuf;
2522 #endif
2523
2524     (void) fflush(stdout);
2525     /* Note: if raw_print() and wait_synch() get called to report terminal
2526      * initialization problems, then wins[] and ttyDisplay might not be
2527      * available yet.  Such problems will probably be fatal before we get
2528      * here, but validate those pointers just in case...
2529      */
2530     if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
2531             wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
2532 #ifdef UNIX
2533     i = ((++nesting == 1) ? tgetch() :
2534          (read(fileno(stdin), (genericptr_t)&nestbuf,1) == 1 ? (int)nestbuf :
2535                                                                 EOF));
2536     --nesting;
2537 #else
2538     i = tgetch();
2539 #endif
2540     if (!i) i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
2541     if (ttyDisplay && ttyDisplay->toplin == 1)
2542         ttyDisplay->toplin = 2;
2543     return i;
2544 }
2545
2546 /*
2547  * return a key, or 0, in which case a mouse button was pressed
2548  * mouse events should be returned as character postitions in the map window.
2549  * Since normal tty's don't have mice, just return a key.
2550  */
2551 /*ARGSUSED*/
2552 int
2553 tty_nh_poskey(x, y, mod)
2554     int *x, *y, *mod;
2555 {
2556 # if defined(WIN32CON)
2557     int i;
2558     (void) fflush(stdout);
2559     /* Note: if raw_print() and wait_synch() get called to report terminal
2560      * initialization problems, then wins[] and ttyDisplay might not be
2561      * available yet.  Such problems will probably be fatal before we get
2562      * here, but validate those pointers just in case...
2563      */
2564     if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
2565             wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
2566     i = ntposkey(x, y, mod);
2567     if (!i && mod && *mod == 0)
2568         i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
2569     if (ttyDisplay && ttyDisplay->toplin == 1)
2570                 ttyDisplay->toplin = 2;
2571     return i;
2572 # else
2573     return tty_nhgetch();
2574 # endif
2575 }
2576
2577 void
2578 win_tty_init()
2579 {
2580 # if defined(WIN32CON)
2581     nttty_open();
2582 # endif
2583     return;
2584 }
2585
2586 #ifdef POSITIONBAR
2587 void
2588 tty_update_positionbar(posbar)
2589 char *posbar;
2590 {
2591 # ifdef MSDOS
2592         video_update_positionbar(posbar);
2593 # endif
2594 }
2595 #endif
2596
2597 /*
2598  * Allocate a copy of the given string.  If null, return a string of
2599  * zero length.
2600  *
2601  * This is an exact duplicate of copy_of() in X11/winmenu.c.
2602  */
2603 static char *
2604 copy_of(s)
2605     const char *s;
2606 {
2607     if (!s) s = "";
2608     return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s);
2609 }
2610
2611 #endif /* TTY_GRAPHICS */
2612
2613 /*wintty.c*/