OSDN Git Service

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