tty role selection - 'random' default
[aNetHack.git] / win / tty / wintty.c
blob1e98c032bcaccf1c68c97488d9a8eee6fdb3fbcb
1 /* NetHack 3.6 wintty.c $NHDT-Date: 1453514601 2016/01/23 02:03:21 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.124 $ */
2 /* Copyright (c) David Cohrs, 1991 */
3 /* NetHack may be freely redistributed. See license for details. */
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 */
11 /* It's still not clear I've caught all the cases for H2344. #undef this
12 * to back out the changes. */
13 #define H2344_BROKEN
15 #include "hack.h"
17 #ifdef TTY_GRAPHICS
18 #include "dlb.h"
20 #ifdef MAC
21 #define MICRO /* The Mac is a MICRO only for this file, not in general! */
22 #ifdef THINK_C
23 extern void msmsg(const char *, ...);
24 #endif
25 #endif
27 #ifndef NO_TERMS
28 #include "tcap.h"
29 #endif
31 #include "wintty.h"
33 #ifdef CLIPPING /* might want SIGWINCH */
34 #if defined(BSD) || defined(ULTRIX) || defined(AIX_31) \
35 || defined(_BULL_SOURCE)
36 #include <signal.h>
37 #endif
38 #endif
40 #ifdef TTY_TILES_ESCCODES
41 extern short glyph2tile[];
42 #define TILE_ANSI_COMMAND 'z'
43 #define AVTC_GLYPH_START 0
44 #define AVTC_GLYPH_END 1
45 #define AVTC_SELECT_WINDOW 2
46 #define AVTC_INLINE_SYNC 3
47 #endif
49 extern char mapped_menu_cmds[]; /* from options.c */
51 /* this is only needed until tty_status_* routines are written */
52 extern NEARDATA winid WIN_STATUS;
54 /* Interface definition, for windows.c */
55 struct window_procs tty_procs = {
56 "tty",
57 #ifdef MSDOS
58 WC_TILED_MAP | WC_ASCII_MAP |
59 #endif
60 #if defined(WIN32CON)
61 WC_MOUSE_SUPPORT |
62 #endif
63 WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN,
64 #if defined(SELECTSAVED)
65 WC2_SELECTSAVED |
66 #endif
67 WC2_DARKGRAY,
68 tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event,
69 tty_exit_nhwindows, tty_suspend_nhwindows, tty_resume_nhwindows,
70 tty_create_nhwindow, tty_clear_nhwindow, tty_display_nhwindow,
71 tty_destroy_nhwindow, tty_curs, tty_putstr, genl_putmixed,
72 tty_display_file, tty_start_menu, tty_add_menu, tty_end_menu,
73 tty_select_menu, tty_message_menu, tty_update_inventory, tty_mark_synch,
74 tty_wait_synch,
75 #ifdef CLIPPING
76 tty_cliparound,
77 #endif
78 #ifdef POSITIONBAR
79 tty_update_positionbar,
80 #endif
81 tty_print_glyph, tty_raw_print, tty_raw_print_bold, tty_nhgetch,
82 tty_nh_poskey, tty_nhbell, tty_doprev_message, tty_yn_function,
83 tty_getlin, tty_get_ext_cmd, tty_number_pad, tty_delay_output,
84 #ifdef CHANGE_COLOR /* the Mac uses a palette device */
85 tty_change_color,
86 #ifdef MAC
87 tty_change_background, set_tty_font_name,
88 #endif
89 tty_get_color_string,
90 #endif
92 /* other defs that really should go away (they're tty specific) */
93 tty_start_screen, tty_end_screen, genl_outrip,
94 #if defined(WIN32)
95 nttty_preference_update,
96 #else
97 genl_preference_update,
98 #endif
99 tty_getmsghistory, tty_putmsghistory,
100 #ifdef STATUS_VIA_WINDOWPORT
101 tty_status_init,
102 genl_status_finish, genl_status_enablefield,
103 tty_status_update,
104 #ifdef STATUS_HILITES
105 tty_status_threshold,
106 #endif
107 #endif
108 genl_can_suspend_yes,
111 static int maxwin = 0; /* number of windows in use */
112 winid BASE_WINDOW;
113 struct WinDesc *wins[MAXWIN];
114 struct DisplayDesc *ttyDisplay; /* the tty display descriptor */
116 extern void FDECL(cmov, (int, int)); /* from termcap.c */
117 extern void FDECL(nocmov, (int, int)); /* from termcap.c */
118 #if defined(UNIX) || defined(VMS)
119 static char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
120 #endif
122 static char winpanicstr[] = "Bad window id %d";
123 char defmorestr[] = "--More--";
125 #ifdef CLIPPING
126 #if defined(USE_TILES) && defined(MSDOS)
127 boolean clipping = FALSE; /* clipping on? */
128 int clipx = 0, clipxmax = 0;
129 #else
130 static boolean clipping = FALSE; /* clipping on? */
131 static int clipx = 0, clipxmax = 0;
132 #endif
133 static int clipy = 0, clipymax = 0;
134 #endif /* CLIPPING */
136 #if defined(USE_TILES) && defined(MSDOS)
137 extern void FDECL(adjust_cursor_flags, (struct WinDesc *));
138 #endif
140 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
141 boolean GFlag = FALSE;
142 boolean HE_resets_AS; /* see termcap.c */
143 #endif
145 #if defined(MICRO) || defined(WIN32CON)
146 static const char to_continue[] = "to continue";
147 #define getret() getreturn(to_continue)
148 #else
149 STATIC_DCL void NDECL(getret);
150 #endif
151 STATIC_DCL void FDECL(erase_menu_or_text,
152 (winid, struct WinDesc *, BOOLEAN_P));
153 STATIC_DCL void FDECL(free_window_info, (struct WinDesc *, BOOLEAN_P));
154 STATIC_DCL void FDECL(dmore, (struct WinDesc *, const char *));
155 STATIC_DCL void FDECL(set_item_state, (winid, int, tty_menu_item *));
156 STATIC_DCL void FDECL(set_all_on_page,
157 (winid, tty_menu_item *, tty_menu_item *));
158 STATIC_DCL void FDECL(unset_all_on_page,
159 (winid, tty_menu_item *, tty_menu_item *));
160 STATIC_DCL void FDECL(invert_all_on_page,
161 (winid, tty_menu_item *, tty_menu_item *, CHAR_P));
162 STATIC_DCL void FDECL(invert_all,
163 (winid, tty_menu_item *, tty_menu_item *, CHAR_P));
164 STATIC_DCL void FDECL(process_menu_window, (winid, struct WinDesc *));
165 STATIC_DCL void FDECL(process_text_window, (winid, struct WinDesc *));
166 STATIC_DCL tty_menu_item *FDECL(reverse, (tty_menu_item *));
167 STATIC_DCL const char *FDECL(compress_str, (const char *));
168 STATIC_DCL void FDECL(tty_putsym, (winid, int, int, CHAR_P));
169 STATIC_DCL void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */
170 STATIC_DCL void FDECL(setup_rolemenu, (winid, BOOLEAN_P, int, int, int));
171 STATIC_DCL void FDECL(setup_racemenu, (winid, BOOLEAN_P, int, int, int));
172 STATIC_DCL void FDECL(setup_gendmenu, (winid, BOOLEAN_P, int, int, int));
173 STATIC_DCL void FDECL(setup_algnmenu, (winid, BOOLEAN_P, int, int, int));
174 STATIC_DCL boolean NDECL(reset_role_filtering);
177 * A string containing all the default commands -- to add to a list
178 * of acceptable inputs.
180 static const char default_menu_cmds[] = {
181 MENU_FIRST_PAGE, MENU_LAST_PAGE, MENU_NEXT_PAGE,
182 MENU_PREVIOUS_PAGE, MENU_SELECT_ALL, MENU_UNSELECT_ALL,
183 MENU_INVERT_ALL, MENU_SELECT_PAGE, MENU_UNSELECT_PAGE,
184 MENU_INVERT_PAGE, MENU_SEARCH, 0 /* null terminator */
187 #ifdef TTY_TILES_ESCCODES
188 static int vt_tile_current_window = -2;
190 void
191 print_vt_code(i, c, d)
192 int i, c, d;
194 if (iflags.vt_tiledata) {
195 if (c >= 0) {
196 if (i == AVTC_SELECT_WINDOW) {
197 if (c == vt_tile_current_window) return;
198 vt_tile_current_window = c;
200 if (d >= 0)
201 printf("\033[1;%d;%d;%d%c", i, c, d, TILE_ANSI_COMMAND);
202 else
203 printf("\033[1;%d;%d%c", i, c, TILE_ANSI_COMMAND);
204 } else {
205 printf("\033[1;%d%c", i, TILE_ANSI_COMMAND);
209 #else
210 # define print_vt_code(i, c, d) ;
211 #endif /* !TTY_TILES_ESCCODES */
212 #define print_vt_code1(i) print_vt_code((i), -1, -1)
213 #define print_vt_code2(i,c) print_vt_code((i), (c), -1)
214 #define print_vt_code3(i,c,d) print_vt_code((i), (c), (d))
217 /* clean up and quit */
218 STATIC_OVL void
219 bail(mesg)
220 const char *mesg;
222 clearlocks();
223 tty_exit_nhwindows(mesg);
224 terminate(EXIT_SUCCESS);
225 /*NOTREACHED*/
228 #if defined(SIGWINCH) && defined(CLIPPING)
229 STATIC_OVL void
230 winch()
232 int oldLI = LI, oldCO = CO, i;
233 register struct WinDesc *cw;
235 #ifdef WINCHAIN
237 #define WINCH_MESSAGE "(SIGWINCH)"
238 if (wc_tracelogf)
239 (void) write(fileno(wc_tracelogf), WINCH_MESSAGE,
240 strlen(WINCH_MESSAGE));
241 #undef WINCH_MESSAGE
243 #endif
244 getwindowsz();
245 /* For long running events such as multi-page menus and
246 * display_file(), we note the signal's occurance and
247 * hope the code there decides to handle the situation
248 * and reset the flag. There will be race conditions
249 * when handling this - code handlers so it doesn't matter.
251 #ifdef notyet
252 winch_seen = TRUE;
253 #endif
254 if ((oldLI != LI || oldCO != CO) && ttyDisplay) {
255 ttyDisplay->rows = LI;
256 ttyDisplay->cols = CO;
258 cw = wins[BASE_WINDOW];
259 cw->rows = ttyDisplay->rows;
260 cw->cols = ttyDisplay->cols;
262 if (iflags.window_inited) {
263 cw = wins[WIN_MESSAGE];
264 cw->curx = cw->cury = 0;
266 tty_destroy_nhwindow(WIN_STATUS);
267 WIN_STATUS = tty_create_nhwindow(NHW_STATUS);
269 if (u.ux) {
270 #ifdef CLIPPING
271 if (CO < COLNO || LI < ROWNO + 3) {
272 setclipped();
273 tty_cliparound(u.ux, u.uy);
274 } else {
275 clipping = FALSE;
276 clipx = clipy = 0;
278 #endif
279 i = ttyDisplay->toplin;
280 ttyDisplay->toplin = 0;
281 docrt();
282 bot();
283 ttyDisplay->toplin = i;
284 flush_screen(1);
285 if (i) {
286 addtopl(toplines);
287 } else
288 for (i = WIN_INVEN; i < MAXWIN; i++)
289 if (wins[i] && wins[i]->active) {
290 /* cop-out */
291 addtopl("Press Return to continue: ");
292 break;
294 (void) fflush(stdout);
295 if (i < 2)
296 flush_screen(1);
301 #endif
303 /*ARGSUSED*/
304 void
305 tty_init_nhwindows(argcp, argv)
306 int *argcp UNUSED;
307 char **argv UNUSED;
309 int wid, hgt, i;
312 * Remember tty modes, to be restored on exit.
314 * gettty() must be called before tty_startup()
315 * due to ordering of LI/CO settings
316 * tty_startup() must be called before initoptions()
317 * due to ordering of graphics settings
319 #if defined(UNIX) || defined(VMS)
320 setbuf(stdout, obuf);
321 #endif
322 gettty();
324 /* to port dependant tty setup */
325 tty_startup(&wid, &hgt);
326 setftty(); /* calls start_screen */
328 /* set up tty descriptor */
329 ttyDisplay = (struct DisplayDesc *) alloc(sizeof(struct DisplayDesc));
330 ttyDisplay->toplin = 0;
331 ttyDisplay->rows = hgt;
332 ttyDisplay->cols = wid;
333 ttyDisplay->curx = ttyDisplay->cury = 0;
334 ttyDisplay->inmore = ttyDisplay->inread = ttyDisplay->intr = 0;
335 ttyDisplay->dismiss_more = 0;
336 #ifdef TEXTCOLOR
337 ttyDisplay->color = NO_COLOR;
338 #endif
339 ttyDisplay->attrs = 0;
341 /* set up the default windows */
342 BASE_WINDOW = tty_create_nhwindow(NHW_BASE);
343 wins[BASE_WINDOW]->active = 1;
345 ttyDisplay->lastwin = WIN_ERR;
347 #if defined(SIGWINCH) && defined(CLIPPING) && !defined(NO_SIGNAL)
348 (void) signal(SIGWINCH, winch);
349 #endif
351 /* add one a space forward menu command alias */
352 add_menu_cmd_alias(' ', MENU_NEXT_PAGE);
354 tty_clear_nhwindow(BASE_WINDOW);
356 tty_putstr(BASE_WINDOW, 0, "");
357 for (i = 1; i <= 4; ++i)
358 tty_putstr(BASE_WINDOW, 0, copyright_banner_line(i));
359 tty_putstr(BASE_WINDOW, 0, "");
360 tty_display_nhwindow(BASE_WINDOW, FALSE);
363 /* try to reduce clutter in the code below... */
364 #define ROLE flags.initrole
365 #define RACE flags.initrace
366 #define GEND flags.initgend
367 #define ALGN flags.initalign
369 void
370 tty_player_selection()
372 int i, k, n, choice, nextpick;
373 boolean getconfirmation, picksomething;
374 char pick4u = 'n';
375 char pbuf[QBUFSZ], plbuf[QBUFSZ];
376 winid win;
377 anything any;
378 menu_item *selected = 0;
380 /* Used to avoid "Is this ok?" if player has already specified all
381 * four facets of role.
382 * Note that rigid_role_checks might force any unspecified facets to
383 * have a specific value, but that will still require confirmation;
384 * player can specify the forced ones if avoiding that is demanded.
386 picksomething = (ROLE == ROLE_NONE || RACE == ROLE_NONE
387 || GEND == ROLE_NONE || ALGN == ROLE_NONE);
388 /* Used for '-@';
389 * choose randomly without asking for all unspecified facets.
391 if (flags.randomall && picksomething) {
392 if (ROLE == ROLE_NONE)
393 ROLE = ROLE_RANDOM;
394 if (RACE == ROLE_NONE)
395 RACE = ROLE_RANDOM;
396 if (GEND == ROLE_NONE)
397 GEND = ROLE_RANDOM;
398 if (ALGN == ROLE_NONE)
399 ALGN = ROLE_RANDOM;
402 /* prevent unnecessary prompting if role forces race (samurai) or gender
403 (valkyrie) or alignment (rogue), or race forces alignment (orc), &c */
404 rigid_role_checks();
406 /* Should we randomly pick for the player? */
407 if (ROLE == ROLE_NONE || RACE == ROLE_NONE || GEND == ROLE_NONE
408 || ALGN == ROLE_NONE) {
409 int echoline;
410 char *prompt = build_plselection_prompt(pbuf, QBUFSZ,
411 ROLE, RACE, GEND, ALGN);
413 /* this prompt string ends in "[ynaq]?":
414 y - game picks role,&c then asks player to confirm;
415 n - player manually chooses via menu selections;
416 a - like 'y', but skips confirmation and starts game;
417 q - quit
419 tty_putstr(BASE_WINDOW, 0, "");
420 echoline = wins[BASE_WINDOW]->cury;
421 tty_putstr(BASE_WINDOW, 0, prompt);
422 do {
423 pick4u = lowc(readchar());
424 if (index(quitchars, pick4u))
425 pick4u = 'y';
426 } while (!index(ynaqchars, pick4u));
427 if ((int) strlen(prompt) + 1 < CO) {
428 /* Echo choice and move back down line */
429 tty_putsym(BASE_WINDOW, (int) strlen(prompt) + 1, echoline,
430 pick4u);
431 tty_putstr(BASE_WINDOW, 0, "");
432 } else
433 /* Otherwise it's hard to tell where to echo, and things are
434 * wrapping a bit messily anyway, so (try to) make sure the next
435 * question shows up well and doesn't get wrapped at the
436 * bottom of the window.
438 tty_clear_nhwindow(BASE_WINDOW);
440 if (pick4u != 'y' && pick4u != 'a' && pick4u != 'n')
441 goto give_up;
444 makepicks:
445 nextpick = RS_ROLE;
446 do {
447 if (nextpick == RS_ROLE) {
448 nextpick = RS_RACE;
449 /* Select a role, if necessary;
450 we'll try to be compatible with pre-selected
451 race/gender/alignment, but may not succeed. */
452 if (ROLE < 0) {
453 /* Process the choice */
454 if (pick4u == 'y' || pick4u == 'a' || ROLE == ROLE_RANDOM) {
455 /* Pick a random role */
456 k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
457 if (k < 0) {
458 tty_putstr(BASE_WINDOW, 0, "Incompatible role!");
459 k = randrole();
461 } else {
462 /* Prompt for a role */
463 tty_clear_nhwindow(BASE_WINDOW);
464 role_selection_prolog(RS_ROLE, BASE_WINDOW);
465 win = create_nhwindow(NHW_MENU);
466 start_menu(win);
467 /* populate the menu with role choices */
468 setup_rolemenu(win, TRUE, RACE, GEND, ALGN);
469 /* add miscellaneous menu entries */
470 role_menu_extra(ROLE_RANDOM, win, TRUE);
471 any.a_int = 0; /* separator, not a choice */
472 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
473 MENU_UNSELECTED);
474 role_menu_extra(RS_RACE, win, FALSE);
475 role_menu_extra(RS_GENDER, win, FALSE);
476 role_menu_extra(RS_ALGNMNT, win, FALSE);
477 if (gotrolefilter())
478 role_menu_extra(RS_filter, win, FALSE);
479 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
480 Strcpy(pbuf, "Pick a role or profession");
481 end_menu(win, pbuf);
482 n = select_menu(win, PICK_ONE, &selected);
484 * PICK_ONE with preselected choice behaves strangely:
485 * n == -1 -- <escape>, so use quit choice;
486 * n == 0 -- explicitly chose preselected entry,
487 * toggling it off, so use it;
488 * n == 1 -- implicitly chose preselected entry
489 * with <space> or <return>;
490 * n == 2 -- explicitly chose a different entry, so
491 * both it and preselected one are in list.
493 if (n > 0) {
494 choice = selected[0].item.a_int;
495 if (n > 1 && choice == ROLE_RANDOM)
496 choice = selected[1].item.a_int;
497 } else
498 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
499 if (selected)
500 free((genericptr_t) selected), selected = 0;
501 destroy_nhwindow(win);
503 if (choice == ROLE_NONE) {
504 goto give_up; /* Selected quit */
505 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
506 ALGN = k = ROLE_NONE;
507 nextpick = RS_ALGNMNT;
508 } else if (choice == RS_menu_arg(RS_GENDER)) {
509 GEND = k = ROLE_NONE;
510 nextpick = RS_GENDER;
511 } else if (choice == RS_menu_arg(RS_RACE)) {
512 RACE = k = ROLE_NONE;
513 nextpick = RS_RACE;
514 } else if (choice == RS_menu_arg(RS_filter)) {
515 ROLE = k = ROLE_NONE;
516 (void) reset_role_filtering();
517 nextpick = RS_ROLE;
518 } else if (choice == ROLE_RANDOM) {
519 k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
520 if (k < 0)
521 k = randrole();
522 } else {
523 k = choice - 1;
526 ROLE = k;
527 } /* needed role */
528 } /* picking role */
530 if (nextpick == RS_RACE) {
531 nextpick = (ROLE < 0) ? RS_ROLE : RS_GENDER;
532 /* Select a race, if necessary;
533 force compatibility with role, try for compatibility
534 with pre-selected gender/alignment. */
535 if (RACE < 0 || !validrace(ROLE, RACE)) {
536 /* no race yet, or pre-selected race not valid */
537 if (pick4u == 'y' || pick4u == 'a' || RACE == ROLE_RANDOM) {
538 k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM);
539 if (k < 0) {
540 tty_putstr(BASE_WINDOW, 0, "Incompatible race!");
541 k = randrace(ROLE);
543 } else { /* pick4u == 'n' */
544 /* Count the number of valid races */
545 n = 0; /* number valid */
546 k = 0; /* valid race */
547 for (i = 0; races[i].noun; i++)
548 if (ok_race(ROLE, i, GEND, ALGN)) {
549 n++;
550 k = i;
552 if (n == 0) {
553 for (i = 0; races[i].noun; i++)
554 if (validrace(ROLE, i)) {
555 n++;
556 k = i;
559 /* Permit the user to pick, if there is more than one */
560 if (n > 1) {
561 tty_clear_nhwindow(BASE_WINDOW);
562 role_selection_prolog(RS_RACE, BASE_WINDOW);
563 win = create_nhwindow(NHW_MENU);
564 start_menu(win);
565 any = zeroany; /* zero out all bits */
566 /* populate the menu with role choices */
567 setup_racemenu(win, TRUE, ROLE, GEND, ALGN);
568 /* add miscellaneous menu entries */
569 role_menu_extra(ROLE_RANDOM, win, TRUE);
570 any.a_int = 0; /* separator, not a choice */
571 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
572 MENU_UNSELECTED);
573 role_menu_extra(RS_ROLE, win, FALSE);
574 role_menu_extra(RS_GENDER, win, FALSE);
575 role_menu_extra(RS_ALGNMNT, win, FALSE);
576 if (gotrolefilter())
577 role_menu_extra(RS_filter, win, FALSE);
578 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
579 Strcpy(pbuf, "Pick a race or species");
580 end_menu(win, pbuf);
581 n = select_menu(win, PICK_ONE, &selected);
582 if (n > 0) {
583 choice = selected[0].item.a_int;
584 if (n > 1 && choice == ROLE_RANDOM)
585 choice = selected[1].item.a_int;
586 } else
587 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
588 if (selected)
589 free((genericptr_t) selected), selected = 0;
590 destroy_nhwindow(win);
592 if (choice == ROLE_NONE) {
593 goto give_up; /* Selected quit */
594 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
595 ALGN = k = ROLE_NONE;
596 nextpick = RS_ALGNMNT;
597 } else if (choice == RS_menu_arg(RS_GENDER)) {
598 GEND = k = ROLE_NONE;
599 nextpick = RS_GENDER;
600 } else if (choice == RS_menu_arg(RS_ROLE)) {
601 ROLE = k = ROLE_NONE;
602 nextpick = RS_ROLE;
603 } else if (choice == RS_menu_arg(RS_filter)) {
604 RACE = k = ROLE_NONE;
605 if (reset_role_filtering())
606 nextpick = RS_ROLE;
607 else
608 nextpick = RS_RACE;
609 } else if (choice == ROLE_RANDOM) {
610 k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM);
611 if (k < 0)
612 k = randrace(ROLE);
613 } else {
614 k = choice - 1;
618 RACE = k;
619 } /* needed race */
620 } /* picking race */
622 if (nextpick == RS_GENDER) {
623 nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE
624 : RS_ALGNMNT;
625 /* Select a gender, if necessary;
626 force compatibility with role/race, try for compatibility
627 with pre-selected alignment. */
628 if (GEND < 0 || !validgend(ROLE, RACE, GEND)) {
629 /* no gender yet, or pre-selected gender not valid */
630 if (pick4u == 'y' || pick4u == 'a' || GEND == ROLE_RANDOM) {
631 k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM);
632 if (k < 0) {
633 tty_putstr(BASE_WINDOW, 0, "Incompatible gender!");
634 k = randgend(ROLE, RACE);
636 } else { /* pick4u == 'n' */
637 /* Count the number of valid genders */
638 n = 0; /* number valid */
639 k = 0; /* valid gender */
640 for (i = 0; i < ROLE_GENDERS; i++)
641 if (ok_gend(ROLE, RACE, i, ALGN)) {
642 n++;
643 k = i;
645 if (n == 0) {
646 for (i = 0; i < ROLE_GENDERS; i++)
647 if (validgend(ROLE, RACE, i)) {
648 n++;
649 k = i;
652 /* Permit the user to pick, if there is more than one */
653 if (n > 1) {
654 tty_clear_nhwindow(BASE_WINDOW);
655 role_selection_prolog(RS_GENDER, BASE_WINDOW);
656 win = create_nhwindow(NHW_MENU);
657 start_menu(win);
658 any = zeroany; /* zero out all bits */
659 /* populate the menu with gender choices */
660 setup_gendmenu(win, TRUE, ROLE, RACE, ALGN);
661 /* add miscellaneous menu entries */
662 role_menu_extra(ROLE_RANDOM, win, TRUE);
663 any.a_int = 0; /* separator, not a choice */
664 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
665 MENU_UNSELECTED);
666 role_menu_extra(RS_ROLE, win, FALSE);
667 role_menu_extra(RS_RACE, win, FALSE);
668 role_menu_extra(RS_ALGNMNT, win, FALSE);
669 if (gotrolefilter())
670 role_menu_extra(RS_filter, win, FALSE);
671 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
672 Strcpy(pbuf, "Pick a gender or sex");
673 end_menu(win, pbuf);
674 n = select_menu(win, PICK_ONE, &selected);
675 if (n > 0) {
676 choice = selected[0].item.a_int;
677 if (n > 1 && choice == ROLE_RANDOM)
678 choice = selected[1].item.a_int;
679 } else
680 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
681 if (selected)
682 free((genericptr_t) selected), selected = 0;
683 destroy_nhwindow(win);
685 if (choice == ROLE_NONE) {
686 goto give_up; /* Selected quit */
687 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
688 ALGN = k = ROLE_NONE;
689 nextpick = RS_ALGNMNT;
690 } else if (choice == RS_menu_arg(RS_RACE)) {
691 RACE = k = ROLE_NONE;
692 nextpick = RS_RACE;
693 } else if (choice == RS_menu_arg(RS_ROLE)) {
694 ROLE = k = ROLE_NONE;
695 nextpick = RS_ROLE;
696 } else if (choice == RS_menu_arg(RS_filter)) {
697 GEND = k = ROLE_NONE;
698 if (reset_role_filtering())
699 nextpick = RS_ROLE;
700 else
701 nextpick = RS_GENDER;
702 } else if (choice == ROLE_RANDOM) {
703 k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM);
704 if (k < 0)
705 k = randgend(ROLE, RACE);
706 } else {
707 k = choice - 1;
711 GEND = k;
712 } /* needed gender */
713 } /* picking gender */
715 if (nextpick == RS_ALGNMNT) {
716 nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE : RS_GENDER;
717 /* Select an alignment, if necessary;
718 force compatibility with role/race/gender. */
719 if (ALGN < 0 || !validalign(ROLE, RACE, ALGN)) {
720 /* no alignment yet, or pre-selected alignment not valid */
721 if (pick4u == 'y' || pick4u == 'a' || ALGN == ROLE_RANDOM) {
722 k = pick_align(ROLE, RACE, GEND, PICK_RANDOM);
723 if (k < 0) {
724 tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!");
725 k = randalign(ROLE, RACE);
727 } else { /* pick4u == 'n' */
728 /* Count the number of valid alignments */
729 n = 0; /* number valid */
730 k = 0; /* valid alignment */
731 for (i = 0; i < ROLE_ALIGNS; i++)
732 if (ok_align(ROLE, RACE, GEND, i)) {
733 n++;
734 k = i;
736 if (n == 0) {
737 for (i = 0; i < ROLE_ALIGNS; i++)
738 if (validalign(ROLE, RACE, i)) {
739 n++;
740 k = i;
743 /* Permit the user to pick, if there is more than one */
744 if (n > 1) {
745 tty_clear_nhwindow(BASE_WINDOW);
746 role_selection_prolog(RS_ALGNMNT, BASE_WINDOW);
747 win = create_nhwindow(NHW_MENU);
748 start_menu(win);
749 any = zeroany; /* zero out all bits */
750 setup_algnmenu(win, TRUE, ROLE, RACE, GEND);
751 role_menu_extra(ROLE_RANDOM, win, TRUE);
752 any.a_int = 0; /* separator, not a choice */
753 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
754 MENU_UNSELECTED);
755 role_menu_extra(RS_ROLE, win, FALSE);
756 role_menu_extra(RS_RACE, win, FALSE);
757 role_menu_extra(RS_GENDER, win, FALSE);
758 if (gotrolefilter())
759 role_menu_extra(RS_filter, win, FALSE);
760 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
761 Strcpy(pbuf, "Pick an alignment or creed");
762 end_menu(win, pbuf);
763 n = select_menu(win, PICK_ONE, &selected);
764 if (n > 0) {
765 choice = selected[0].item.a_int;
766 if (n > 1 && choice == ROLE_RANDOM)
767 choice = selected[1].item.a_int;
768 } else
769 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
770 if (selected)
771 free((genericptr_t) selected), selected = 0;
772 destroy_nhwindow(win);
774 if (choice == ROLE_NONE) {
775 goto give_up; /* Selected quit */
776 } else if (choice == RS_menu_arg(RS_GENDER)) {
777 GEND = k = ROLE_NONE;
778 nextpick = RS_GENDER;
779 } else if (choice == RS_menu_arg(RS_RACE)) {
780 RACE = k = ROLE_NONE;
781 nextpick = RS_RACE;
782 } else if (choice == RS_menu_arg(RS_ROLE)) {
783 ROLE = k = ROLE_NONE;
784 nextpick = RS_ROLE;
785 } else if (choice == RS_menu_arg(RS_filter)) {
786 ALGN = k = ROLE_NONE;
787 if (reset_role_filtering())
788 nextpick = RS_ROLE;
789 else
790 nextpick = RS_ALGNMNT;
791 } else if (choice == ROLE_RANDOM) {
792 k = pick_align(ROLE, RACE, GEND, PICK_RANDOM);
793 if (k < 0)
794 k = randalign(ROLE, RACE);
795 } else {
796 k = choice - 1;
800 ALGN = k;
801 } /* needed alignment */
802 } /* picking alignment */
804 } while (ROLE < 0 || RACE < 0 || GEND < 0 || ALGN < 0);
807 * Role, race, &c have now been determined;
808 * ask for confirmation and maybe go back to choose all over again.
810 * Uses ynaq for familiarity, although 'a' is usually a
811 * superset of 'y' but here is an alternate form of 'n'.
812 * Menu layout:
813 * title: Is this ok? [ynaq]
814 * blank:
815 * text: $name, $alignment $gender $race $role
816 * blank:
817 * menu: y + yes; play
818 * n - no; pick again
819 * maybe: a - no; rename hero
820 * q - quit
821 * (end)
823 getconfirmation = (picksomething && pick4u != 'a' && !flags.randomall);
824 while (getconfirmation) {
825 tty_clear_nhwindow(BASE_WINDOW);
826 role_selection_prolog(ROLE_NONE, BASE_WINDOW);
827 win = create_nhwindow(NHW_MENU);
828 start_menu(win);
829 any = zeroany; /* zero out all bits */
830 any.a_int = 0;
831 if (!roles[ROLE].name.f
832 && (roles[ROLE].allow & ROLE_GENDMASK)
833 == (ROLE_MALE | ROLE_FEMALE))
834 Sprintf(plbuf, " %s", genders[GEND].adj);
835 else
836 *plbuf = '\0'; /* omit redundant gender */
837 Sprintf(pbuf, "%s, %s%s %s %s", plname, aligns[ALGN].adj, plbuf,
838 races[RACE].adj,
839 (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f
840 : roles[ROLE].name.m);
841 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, pbuf,
842 MENU_UNSELECTED);
843 /* blank separator */
844 any.a_int = 0;
845 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
846 /* [ynaq] menu choices */
847 any.a_int = 1;
848 add_menu(win, NO_GLYPH, &any, 'y', 0, ATR_NONE, "Yes; start game",
849 MENU_SELECTED);
850 any.a_int = 2;
851 add_menu(win, NO_GLYPH, &any, 'n', 0, ATR_NONE,
852 "No; choose role again", MENU_UNSELECTED);
853 if (iflags.renameallowed) {
854 any.a_int = 3;
855 add_menu(win, NO_GLYPH, &any, 'a', 0, ATR_NONE,
856 "Not yet; choose another name", MENU_UNSELECTED);
858 any.a_int = -1;
859 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
860 MENU_UNSELECTED);
861 Sprintf(pbuf, "Is this ok? [yn%sq]", iflags.renameallowed ? "a" : "");
862 end_menu(win, pbuf);
863 n = select_menu(win, PICK_ONE, &selected);
864 /* [pick-one menus with a preselected entry behave oddly...] */
865 choice = (n > 0) ? selected[n - 1].item.a_int : (n == 0) ? 1 : -1;
866 if (selected)
867 free((genericptr_t) selected), selected = 0;
868 destroy_nhwindow(win);
870 switch (choice) {
871 default: /* 'q' or ESC */
872 goto give_up; /* quit */
873 break;
874 case 3: { /* 'a' */
876 * TODO: what, if anything, should be done if the name is
877 * changed to or from "wizard" after port-specific startup
878 * code has set flags.debug based on the original name?
880 int saveROLE, saveRACE, saveGEND, saveALGN;
882 iflags.renameinprogress = TRUE;
883 /* plnamesuffix() can change any or all of ROLE, RACE,
884 GEND, ALGN; we'll override that and honor only the name */
885 saveROLE = ROLE, saveRACE = RACE, saveGEND = GEND,
886 saveALGN = ALGN;
887 *plname = '\0';
888 plnamesuffix(); /* calls askname() when plname[] is empty */
889 ROLE = saveROLE, RACE = saveRACE, GEND = saveGEND,
890 ALGN = saveALGN;
891 break; /* getconfirmation is still True */
893 case 2: /* 'n' */
894 /* start fresh, but bypass "shall I pick everything for you?"
895 step; any partial role selection via config file, command
896 line, or name suffix is discarded this time */
897 pick4u = 'n';
898 ROLE = RACE = GEND = ALGN = ROLE_NONE;
899 goto makepicks;
900 break;
901 case 1: /* 'y' or Space or Return/Enter */
902 /* success; drop out through end of function */
903 getconfirmation = FALSE;
904 break;
908 /* Success! */
909 tty_display_nhwindow(BASE_WINDOW, FALSE);
910 return;
912 give_up:
913 /* Quit */
914 if (selected)
915 free((genericptr_t) selected); /* [obsolete] */
916 bail((char *) 0);
917 /*NOTREACHED*/
918 return;
921 STATIC_OVL boolean
922 reset_role_filtering()
924 winid win;
925 anything any;
926 int i, n;
927 menu_item *selected = 0;
929 win = create_nhwindow(NHW_MENU);
930 start_menu(win);
931 any = zeroany;
933 /* no extra blank line preceding this entry; end_menu supplies one */
934 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
935 "Unacceptable roles", MENU_UNSELECTED);
936 setup_rolemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
938 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
939 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
940 "Unacceptable races", MENU_UNSELECTED);
941 setup_racemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
943 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
944 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
945 "Unacceptable genders", MENU_UNSELECTED);
946 setup_gendmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
948 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
949 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
950 "Uncceptable alignments", MENU_UNSELECTED);
951 setup_algnmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
953 end_menu(win, "Pick all that apply");
954 n = select_menu(win, PICK_ANY, &selected);
956 if (n > 0) {
957 clearrolefilter();
958 for (i = 0; i < n; i++)
959 setrolefilter(selected[i].item.a_string);
961 ROLE = RACE = GEND = ALGN = ROLE_NONE;
963 if (selected)
964 free((genericptr_t) selected), selected = 0;
965 destroy_nhwindow(win);
966 return (n > 0) ? TRUE : FALSE;
969 #undef ROLE
970 #undef RACE
971 #undef GEND
972 #undef ALGN
974 /* add entries a-Archeologist, b-Barbarian, &c to menu being built in 'win' */
975 STATIC_OVL void
976 setup_rolemenu(win, filtering, race, gend, algn)
977 winid win;
978 boolean filtering; /* True => exclude filtered roles; False => filter reset */
979 int race, gend, algn; /* all ROLE_NONE for !filtering case */
981 anything any;
982 int i;
983 boolean role_ok;
984 char thisch, lastch = '\0', rolenamebuf[50];
986 any = zeroany; /* zero out all bits */
987 for (i = 0; roles[i].name.m; i++) {
988 role_ok = ok_role(i, race, gend, algn);
989 if (filtering && !role_ok)
990 continue;
991 if (filtering)
992 any.a_int = i + 1;
993 else
994 any.a_string = roles[i].name.m;
995 thisch = lowc(*roles[i].name.m);
996 if (thisch == lastch)
997 thisch = highc(thisch);
998 Strcpy(rolenamebuf, roles[i].name.m);
999 if (roles[i].name.f) {
1000 /* role has distinct name for female (C,P) */
1001 if (gend == 1) {
1002 /* female already chosen; replace male name */
1003 Strcpy(rolenamebuf, roles[i].name.f);
1004 } else if (gend < 0) {
1005 /* not chosen yet; append slash+female name */
1006 Strcat(rolenamebuf, "/");
1007 Strcat(rolenamebuf, roles[i].name.f);
1010 /* !filtering implies reset_role_filtering() where we want to
1011 mark this role as preseleted if current filter excludes it */
1012 add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE, an(rolenamebuf),
1013 (!filtering && !role_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1014 lastch = thisch;
1018 STATIC_OVL void
1019 setup_racemenu(win, filtering, role, gend, algn)
1020 winid win;
1021 boolean filtering;
1022 int role, gend, algn;
1024 anything any;
1025 boolean race_ok;
1026 int i;
1027 char this_ch;
1029 any = zeroany;
1030 for (i = 0; races[i].noun; i++) {
1031 race_ok = ok_race(role, i, gend, algn);
1032 if (filtering && !race_ok)
1033 continue;
1034 if (filtering)
1035 any.a_int = i + 1;
1036 else
1037 any.a_string = races[i].noun;
1038 this_ch = *races[i].noun;
1039 /* filtering: picking race, so choose by first letter, with
1040 capital letter as unseen accelerator;
1041 !filtering: resetting filter rather than picking, choose by
1042 capital letter since lowercase role letters will be present */
1043 add_menu(win, NO_GLYPH, &any,
1044 filtering ? this_ch : highc(this_ch),
1045 filtering ? highc(this_ch) : 0,
1046 ATR_NONE, races[i].noun,
1047 (!filtering && !race_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1051 STATIC_DCL void
1052 setup_gendmenu(win, filtering, role, race, algn)
1053 winid win;
1054 boolean filtering;
1055 int role, race, algn;
1057 anything any;
1058 boolean gend_ok;
1059 int i;
1060 char this_ch;
1062 any = zeroany;
1063 for (i = 0; i < ROLE_GENDERS; i++) {
1064 gend_ok = ok_gend(role, race, i, algn);
1065 if (filtering && !gend_ok)
1066 continue;
1067 if (filtering)
1068 any.a_int = i + 1;
1069 else
1070 any.a_string = genders[i].adj;
1071 this_ch = *genders[i].adj;
1072 /* (see setup_racemenu for explanation of selector letters
1073 and setup_rolemenu for preselection) */
1074 add_menu(win, NO_GLYPH, &any,
1075 filtering ? this_ch : highc(this_ch),
1076 filtering ? highc(this_ch) : 0,
1077 ATR_NONE, genders[i].adj,
1078 (!filtering && !gend_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1082 STATIC_DCL void
1083 setup_algnmenu(win, filtering, role, race, gend)
1084 winid win;
1085 boolean filtering;
1086 int role, race, gend;
1088 anything any;
1089 boolean algn_ok;
1090 int i;
1091 char this_ch;
1093 any = zeroany;
1094 for (i = 0; i < ROLE_ALIGNS; i++) {
1095 algn_ok = ok_align(role, race, gend, i);
1096 if (filtering && !algn_ok)
1097 continue;
1098 if (filtering)
1099 any.a_int = i + 1;
1100 else
1101 any.a_string = aligns[i].adj;
1102 this_ch = *aligns[i].adj;
1103 /* (see setup_racemenu for explanation of selector letters
1104 and setup_rolemenu for preselection) */
1105 add_menu(win, NO_GLYPH, &any,
1106 filtering ? this_ch : highc(this_ch),
1107 filtering ? highc(this_ch) : 0,
1108 ATR_NONE, aligns[i].adj,
1109 (!filtering && !algn_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1114 * plname is filled either by an option (-u Player or -uPlayer) or
1115 * explicitly (by being the wizard) or by askname.
1116 * It may still contain a suffix denoting the role, etc.
1117 * Always called after init_nhwindows() and before display_gamewindows().
1119 void
1120 tty_askname()
1122 static const char who_are_you[] = "Who are you? ";
1123 register int c, ct, tryct = 0;
1125 #ifdef SELECTSAVED
1126 if (iflags.wc2_selectsaved && !iflags.renameinprogress)
1127 switch (restore_menu(BASE_WINDOW)) {
1128 case -1:
1129 bail("Until next time then..."); /* quit */
1130 /*NOTREACHED*/
1131 case 0:
1132 break; /* no game chosen; start new game */
1133 case 1:
1134 return; /* plname[] has been set */
1136 #endif /* SELECTSAVED */
1138 tty_putstr(BASE_WINDOW, 0, "");
1139 do {
1140 if (++tryct > 1) {
1141 if (tryct > 10)
1142 bail("Giving up after 10 tries.\n");
1143 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury - 1);
1144 tty_putstr(BASE_WINDOW, 0, "Enter a name for your character...");
1145 /* erase previous prompt (in case of ESC after partial response)
1147 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury), cl_end();
1149 tty_putstr(BASE_WINDOW, 0, who_are_you);
1150 tty_curs(BASE_WINDOW, (int) (sizeof who_are_you),
1151 wins[BASE_WINDOW]->cury - 1);
1152 ct = 0;
1153 while ((c = tty_nhgetch()) != '\n') {
1154 if (c == EOF)
1155 c = '\033';
1156 if (c == '\033') {
1157 ct = 0;
1158 break;
1159 } /* continue outer loop */
1160 #if defined(WIN32CON)
1161 if (c == '\003')
1162 bail("^C abort.\n");
1163 #endif
1164 /* some people get confused when their erase char is not ^H */
1165 if (c == '\b' || c == '\177') {
1166 if (ct) {
1167 ct--;
1168 #ifdef WIN32CON
1169 ttyDisplay->curx--;
1170 #endif
1171 #if defined(MICRO) || defined(WIN32CON)
1172 #if defined(WIN32CON) || defined(MSDOS)
1173 backsp(); /* \b is visible on NT */
1174 (void) putchar(' ');
1175 backsp();
1176 #else
1177 msmsg("\b \b");
1178 #endif
1179 #else
1180 (void) putchar('\b');
1181 (void) putchar(' ');
1182 (void) putchar('\b');
1183 #endif
1185 continue;
1187 #if defined(UNIX) || defined(VMS)
1188 if (c != '-' && c != '@')
1189 if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') &&
1190 /* reject leading digit but allow digits elsewhere
1191 (avoids ambiguity when character name gets
1192 appended to uid to construct save file name) */
1193 !(c >= '0' && c <= '9' && ct > 0))
1194 c = '_';
1195 #endif
1196 if (ct < (int) (sizeof plname) - 1) {
1197 #if defined(MICRO)
1198 #if defined(MSDOS)
1199 if (iflags.grmode) {
1200 (void) putchar(c);
1201 } else
1202 #endif
1203 msmsg("%c", c);
1204 #else
1205 (void) putchar(c);
1206 #endif
1207 plname[ct++] = c;
1208 #ifdef WIN32CON
1209 ttyDisplay->curx++;
1210 #endif
1213 plname[ct] = 0;
1214 } while (ct == 0);
1216 /* move to next line to simulate echo of user's <return> */
1217 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury + 1);
1219 /* since we let user pick an arbitrary name now, he/she can pick
1220 another one during role selection */
1221 iflags.renameallowed = TRUE;
1224 void
1225 tty_get_nh_event()
1227 return;
1230 #if !defined(MICRO) && !defined(WIN32CON)
1231 STATIC_OVL void
1232 getret()
1234 xputs("\n");
1235 if (flags.standout)
1236 standoutbeg();
1237 xputs("Hit ");
1238 xputs(iflags.cbreak ? "space" : "return");
1239 xputs(" to continue: ");
1240 if (flags.standout)
1241 standoutend();
1242 xwaitforspace(" ");
1244 #endif
1246 void
1247 tty_suspend_nhwindows(str)
1248 const char *str;
1250 settty(str); /* calls end_screen, perhaps raw_print */
1251 if (!str)
1252 tty_raw_print(""); /* calls fflush(stdout) */
1255 void
1256 tty_resume_nhwindows()
1258 gettty();
1259 setftty(); /* calls start_screen */
1260 docrt();
1263 void
1264 tty_exit_nhwindows(str)
1265 const char *str;
1267 winid i;
1269 tty_suspend_nhwindows(str);
1271 * Disable windows to avoid calls to window routines.
1273 free_pickinv_cache(); /* reset its state as well as tear down window */
1274 for (i = 0; i < MAXWIN; i++) {
1275 if (i == BASE_WINDOW)
1276 continue; /* handle wins[BASE_WINDOW] last */
1277 if (wins[i]) {
1278 #ifdef FREE_ALL_MEMORY
1279 free_window_info(wins[i], TRUE);
1280 free((genericptr_t) wins[i]);
1281 #endif
1282 wins[i] = (struct WinDesc *) 0;
1285 WIN_MAP = WIN_MESSAGE = WIN_INVEN = WIN_ERR; /* these are all gone now */
1286 #ifndef STATUS_VIA_WINDOWPORT
1287 WIN_STATUS = WIN_ERR;
1288 #endif
1289 #ifdef FREE_ALL_MEMORY
1290 if (BASE_WINDOW != WIN_ERR && wins[BASE_WINDOW]) {
1291 free_window_info(wins[BASE_WINDOW], TRUE);
1292 free((genericptr_t) wins[BASE_WINDOW]);
1293 wins[BASE_WINDOW] = (struct WinDesc *) 0;
1294 BASE_WINDOW = WIN_ERR;
1296 free((genericptr_t) ttyDisplay);
1297 ttyDisplay = (struct DisplayDesc *) 0;
1298 #endif
1300 #ifndef NO_TERMS /*(until this gets added to the window interface)*/
1301 tty_shutdown(); /* cleanup termcap/terminfo/whatever */
1302 #endif
1303 iflags.window_inited = 0;
1306 winid
1307 tty_create_nhwindow(type)
1308 int type;
1310 struct WinDesc *newwin;
1311 int i;
1312 int newid;
1314 if (maxwin == MAXWIN)
1315 return WIN_ERR;
1317 newwin = (struct WinDesc *) alloc(sizeof(struct WinDesc));
1318 newwin->type = type;
1319 newwin->flags = 0;
1320 newwin->active = FALSE;
1321 newwin->curx = newwin->cury = 0;
1322 newwin->morestr = 0;
1323 newwin->mlist = (tty_menu_item *) 0;
1324 newwin->plist = (tty_menu_item **) 0;
1325 newwin->npages = newwin->plist_size = newwin->nitems = newwin->how = 0;
1326 switch (type) {
1327 case NHW_BASE:
1328 /* base window, used for absolute movement on the screen */
1329 newwin->offx = newwin->offy = 0;
1330 newwin->rows = ttyDisplay->rows;
1331 newwin->cols = ttyDisplay->cols;
1332 newwin->maxrow = newwin->maxcol = 0;
1333 break;
1334 case NHW_MESSAGE:
1335 /* message window, 1 line long, very wide, top of screen */
1336 newwin->offx = newwin->offy = 0;
1337 /* sanity check */
1338 if (iflags.msg_history < 20)
1339 iflags.msg_history = 20;
1340 else if (iflags.msg_history > 60)
1341 iflags.msg_history = 60;
1342 newwin->maxrow = newwin->rows = iflags.msg_history;
1343 newwin->maxcol = newwin->cols = 0;
1344 break;
1345 case NHW_STATUS:
1346 /* status window, 2 lines long, full width, bottom of screen */
1347 newwin->offx = 0;
1348 #if defined(USE_TILES) && defined(MSDOS)
1349 if (iflags.grmode) {
1350 newwin->offy = ttyDisplay->rows - 2;
1351 } else
1352 #endif
1353 newwin->offy = min((int) ttyDisplay->rows - 2, ROWNO + 1);
1354 newwin->rows = newwin->maxrow = 2;
1355 newwin->cols = newwin->maxcol = ttyDisplay->cols;
1356 break;
1357 case NHW_MAP:
1358 /* map window, ROWNO lines long, full width, below message window */
1359 newwin->offx = 0;
1360 newwin->offy = 1;
1361 newwin->rows = ROWNO;
1362 newwin->cols = COLNO;
1363 newwin->maxrow = 0; /* no buffering done -- let gbuf do it */
1364 newwin->maxcol = 0;
1365 break;
1366 case NHW_MENU:
1367 case NHW_TEXT:
1368 /* inventory/menu window, variable length, full width, top of screen
1370 /* help window, the same, different semantics for display, etc */
1371 newwin->offx = newwin->offy = 0;
1372 newwin->rows = 0;
1373 newwin->cols = ttyDisplay->cols;
1374 newwin->maxrow = newwin->maxcol = 0;
1375 break;
1376 default:
1377 panic("Tried to create window type %d\n", (int) type);
1378 return WIN_ERR;
1381 for (newid = 0; newid < MAXWIN; newid++) {
1382 if (wins[newid] == 0) {
1383 wins[newid] = newwin;
1384 break;
1387 if (newid == MAXWIN) {
1388 panic("No window slots!");
1389 return WIN_ERR;
1392 if (newwin->maxrow) {
1393 newwin->data =
1394 (char **) alloc(sizeof(char *) * (unsigned) newwin->maxrow);
1395 newwin->datlen =
1396 (short *) alloc(sizeof(short) * (unsigned) newwin->maxrow);
1397 if (newwin->maxcol) {
1398 /* WIN_STATUS */
1399 for (i = 0; i < newwin->maxrow; i++) {
1400 newwin->data[i] = (char *) alloc((unsigned) newwin->maxcol);
1401 newwin->datlen[i] = (short) newwin->maxcol;
1403 } else {
1404 for (i = 0; i < newwin->maxrow; i++) {
1405 newwin->data[i] = (char *) 0;
1406 newwin->datlen[i] = 0;
1409 if (newwin->type == NHW_MESSAGE)
1410 newwin->maxrow = 0;
1411 } else {
1412 newwin->data = (char **) 0;
1413 newwin->datlen = (short *) 0;
1416 return newid;
1419 STATIC_OVL void
1420 erase_menu_or_text(window, cw, clear)
1421 winid window;
1422 struct WinDesc *cw;
1423 boolean clear;
1425 if (cw->offx == 0)
1426 if (cw->offy) {
1427 tty_curs(window, 1, 0);
1428 cl_eos();
1429 } else if (clear)
1430 clear_screen();
1431 else
1432 docrt();
1433 else
1434 docorner((int) cw->offx, cw->maxrow + 1);
1437 STATIC_OVL void
1438 free_window_info(cw, free_data)
1439 struct WinDesc *cw;
1440 boolean free_data;
1442 int i;
1444 if (cw->data) {
1445 if (cw == wins[WIN_MESSAGE] && cw->rows > cw->maxrow)
1446 cw->maxrow = cw->rows; /* topl data */
1447 for (i = 0; i < cw->maxrow; i++)
1448 if (cw->data[i]) {
1449 free((genericptr_t) cw->data[i]);
1450 cw->data[i] = (char *) 0;
1451 if (cw->datlen)
1452 cw->datlen[i] = 0;
1454 if (free_data) {
1455 free((genericptr_t) cw->data);
1456 cw->data = (char **) 0;
1457 if (cw->datlen)
1458 free((genericptr_t) cw->datlen);
1459 cw->datlen = (short *) 0;
1460 cw->rows = 0;
1463 cw->maxrow = cw->maxcol = 0;
1464 if (cw->mlist) {
1465 tty_menu_item *temp;
1467 while ((temp = cw->mlist) != 0) {
1468 cw->mlist = cw->mlist->next;
1469 if (temp->str)
1470 free((genericptr_t) temp->str);
1471 free((genericptr_t) temp);
1474 if (cw->plist) {
1475 free((genericptr_t) cw->plist);
1476 cw->plist = 0;
1478 cw->plist_size = cw->npages = cw->nitems = cw->how = 0;
1479 if (cw->morestr) {
1480 free((genericptr_t) cw->morestr);
1481 cw->morestr = 0;
1485 void
1486 tty_clear_nhwindow(window)
1487 winid window;
1489 register struct WinDesc *cw = 0;
1491 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1492 panic(winpanicstr, window);
1493 ttyDisplay->lastwin = window;
1495 print_vt_code2(AVTC_SELECT_WINDOW, window);
1497 switch (cw->type) {
1498 case NHW_MESSAGE:
1499 if (ttyDisplay->toplin) {
1500 home();
1501 cl_end();
1502 if (cw->cury)
1503 docorner(1, cw->cury + 1);
1504 ttyDisplay->toplin = 0;
1506 break;
1507 case NHW_STATUS:
1508 tty_curs(window, 1, 0);
1509 cl_end();
1510 tty_curs(window, 1, 1);
1511 cl_end();
1512 break;
1513 case NHW_MAP:
1514 /* cheap -- clear the whole thing and tell nethack to redraw botl */
1515 context.botlx = 1;
1516 /* fall into ... */
1517 case NHW_BASE:
1518 clear_screen();
1519 break;
1520 case NHW_MENU:
1521 case NHW_TEXT:
1522 if (cw->active)
1523 erase_menu_or_text(window, cw, TRUE);
1524 free_window_info(cw, FALSE);
1525 break;
1527 cw->curx = cw->cury = 0;
1530 boolean
1531 toggle_menu_curr(window, curr, lineno, in_view, counting, count)
1532 winid window;
1533 tty_menu_item *curr;
1534 int lineno;
1535 boolean in_view, counting;
1536 long count;
1538 if (curr->selected) {
1539 if (counting && count > 0) {
1540 curr->count = count;
1541 if (in_view)
1542 set_item_state(window, lineno, curr);
1543 return TRUE;
1544 } else { /* change state */
1545 curr->selected = FALSE;
1546 curr->count = -1L;
1547 if (in_view)
1548 set_item_state(window, lineno, curr);
1549 return TRUE;
1551 } else { /* !selected */
1552 if (counting && count > 0) {
1553 curr->count = count;
1554 curr->selected = TRUE;
1555 if (in_view)
1556 set_item_state(window, lineno, curr);
1557 return TRUE;
1558 } else if (!counting) {
1559 curr->selected = TRUE;
1560 if (in_view)
1561 set_item_state(window, lineno, curr);
1562 return TRUE;
1564 /* do nothing counting&&count==0 */
1566 return FALSE;
1569 STATIC_OVL void
1570 dmore(cw, s)
1571 register struct WinDesc *cw;
1572 const char *s; /* valid responses */
1574 const char *prompt = cw->morestr ? cw->morestr : defmorestr;
1575 int offset = (cw->type == NHW_TEXT) ? 1 : 2;
1577 tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + offset,
1578 (int) ttyDisplay->cury);
1579 if (flags.standout)
1580 standoutbeg();
1581 xputs(prompt);
1582 ttyDisplay->curx += strlen(prompt);
1583 if (flags.standout)
1584 standoutend();
1586 xwaitforspace(s);
1589 STATIC_OVL void
1590 set_item_state(window, lineno, item)
1591 winid window;
1592 int lineno;
1593 tty_menu_item *item;
1595 char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-';
1597 tty_curs(window, 4, lineno);
1598 term_start_attr(item->attr);
1599 (void) putchar(ch);
1600 ttyDisplay->curx++;
1601 term_end_attr(item->attr);
1604 STATIC_OVL void
1605 set_all_on_page(window, page_start, page_end)
1606 winid window;
1607 tty_menu_item *page_start, *page_end;
1609 tty_menu_item *curr;
1610 int n;
1612 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1613 if (curr->identifier.a_void && !curr->selected) {
1614 curr->selected = TRUE;
1615 set_item_state(window, n, curr);
1619 STATIC_OVL void
1620 unset_all_on_page(window, page_start, page_end)
1621 winid window;
1622 tty_menu_item *page_start, *page_end;
1624 tty_menu_item *curr;
1625 int n;
1627 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1628 if (curr->identifier.a_void && curr->selected) {
1629 curr->selected = FALSE;
1630 curr->count = -1L;
1631 set_item_state(window, n, curr);
1635 STATIC_OVL void
1636 invert_all_on_page(window, page_start, page_end, acc)
1637 winid window;
1638 tty_menu_item *page_start, *page_end;
1639 char acc; /* group accelerator, 0 => all */
1641 tty_menu_item *curr;
1642 int n;
1644 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1645 if (curr->identifier.a_void && (acc == 0 || curr->gselector == acc)) {
1646 if (curr->selected) {
1647 curr->selected = FALSE;
1648 curr->count = -1L;
1649 } else
1650 curr->selected = TRUE;
1651 set_item_state(window, n, curr);
1656 * Invert all entries that match the give group accelerator (or all if zero).
1658 STATIC_OVL void
1659 invert_all(window, page_start, page_end, acc)
1660 winid window;
1661 tty_menu_item *page_start, *page_end;
1662 char acc; /* group accelerator, 0 => all */
1664 tty_menu_item *curr;
1665 boolean on_curr_page;
1666 struct WinDesc *cw = wins[window];
1668 invert_all_on_page(window, page_start, page_end, acc);
1670 /* invert the rest */
1671 for (on_curr_page = FALSE, curr = cw->mlist; curr; curr = curr->next) {
1672 if (curr == page_start)
1673 on_curr_page = TRUE;
1674 else if (curr == page_end)
1675 on_curr_page = FALSE;
1677 if (!on_curr_page && curr->identifier.a_void
1678 && (acc == 0 || curr->gselector == acc)) {
1679 if (curr->selected) {
1680 curr->selected = FALSE;
1681 curr->count = -1;
1682 } else
1683 curr->selected = TRUE;
1688 STATIC_OVL void
1689 process_menu_window(window, cw)
1690 winid window;
1691 struct WinDesc *cw;
1693 tty_menu_item *page_start, *page_end, *curr;
1694 long count;
1695 int n, curr_page, page_lines, resp_len;
1696 boolean finished, counting, reset_count;
1697 char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ], *msave, *morestr, really_morc;
1698 #define MENU_EXPLICIT_CHOICE 0x7f /* pseudo menu manipulation char */
1700 curr_page = page_lines = 0;
1701 page_start = page_end = 0;
1702 msave = cw->morestr; /* save the morestr */
1703 cw->morestr = morestr = (char *) alloc((unsigned) QBUFSZ);
1704 counting = FALSE;
1705 count = 0L;
1706 reset_count = TRUE;
1707 finished = FALSE;
1709 /* collect group accelerators; for PICK_NONE, they're ignored;
1710 for PICK_ONE, only those which match exactly one entry will be
1711 accepted; for PICK_ANY, those which match any entry are okay */
1712 gacc[0] = '\0';
1713 if (cw->how != PICK_NONE) {
1714 int i, gcnt[128];
1715 #define GSELIDX(c) (c & 127) /* guard against `signed char' */
1717 for (i = 0; i < SIZE(gcnt); i++)
1718 gcnt[i] = 0;
1719 for (n = 0, curr = cw->mlist; curr; curr = curr->next)
1720 if (curr->gselector && curr->gselector != curr->selector) {
1721 ++n;
1722 ++gcnt[GSELIDX(curr->gselector)];
1725 if (n > 0) /* at least one group accelerator found */
1726 for (rp = gacc, curr = cw->mlist; curr; curr = curr->next)
1727 if (curr->gselector && curr->gselector != curr->selector
1728 && !index(gacc, curr->gselector)
1729 && (cw->how == PICK_ANY
1730 || gcnt[GSELIDX(curr->gselector)] == 1)) {
1731 *rp++ = curr->gselector;
1732 *rp = '\0'; /* re-terminate for index() */
1735 resp_len = 0; /* lint suppression */
1737 /* loop until finished */
1738 while (!finished) {
1739 if (reset_count) {
1740 counting = FALSE;
1741 count = 0;
1742 } else
1743 reset_count = TRUE;
1745 if (!page_start) {
1746 /* new page to be displayed */
1747 if (curr_page < 0 || (cw->npages > 0 && curr_page >= cw->npages))
1748 panic("bad menu screen page #%d", curr_page);
1750 /* clear screen */
1751 if (!cw->offx) { /* if not corner, do clearscreen */
1752 if (cw->offy) {
1753 tty_curs(window, 1, 0);
1754 cl_eos();
1755 } else
1756 clear_screen();
1759 rp = resp;
1760 if (cw->npages > 0) {
1761 /* collect accelerators */
1762 page_start = cw->plist[curr_page];
1763 page_end = cw->plist[curr_page + 1];
1764 for (page_lines = 0, curr = page_start; curr != page_end;
1765 page_lines++, curr = curr->next) {
1766 int color = NO_COLOR, attr = ATR_NONE;
1767 boolean menucolr = FALSE;
1768 if (curr->selector)
1769 *rp++ = curr->selector;
1771 tty_curs(window, 1, page_lines);
1772 if (cw->offx)
1773 cl_end();
1775 (void) putchar(' ');
1776 ++ttyDisplay->curx;
1778 * Don't use xputs() because (1) under unix it calls
1779 * tputstr() which will interpret a '*' as some kind
1780 * of padding information and (2) it calls xputc to
1781 * actually output the character. We're faster doing
1782 * this.
1784 if (iflags.use_menu_color
1785 && (menucolr = get_menu_coloring(curr->str, &color,
1786 &attr))) {
1787 term_start_attr(attr);
1788 #ifdef TEXTCOLOR
1789 if (color != NO_COLOR)
1790 term_start_color(color);
1791 #endif
1792 } else
1793 term_start_attr(curr->attr);
1794 for (n = 0, cp = curr->str;
1795 #ifndef WIN32CON
1797 && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
1798 cp++, n++)
1799 #else
1801 && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
1802 cp++, n++, ttyDisplay->curx++)
1803 #endif
1804 if (n == 2 && curr->identifier.a_void != 0
1805 && curr->selected) {
1806 if (curr->count == -1L)
1807 (void) putchar('+'); /* all selected */
1808 else
1809 (void) putchar('#'); /* count selected */
1810 } else
1811 (void) putchar(*cp);
1812 if (iflags.use_menu_color && menucolr) {
1813 #ifdef TEXTCOLOR
1814 if (color != NO_COLOR)
1815 term_end_color();
1816 #endif
1817 term_end_attr(attr);
1818 } else
1819 term_end_attr(curr->attr);
1821 } else {
1822 page_start = 0;
1823 page_end = 0;
1824 page_lines = 0;
1826 *rp = 0;
1827 /* remember how many explicit menu choices there are */
1828 resp_len = (int) strlen(resp);
1830 /* corner window - clear extra lines from last page */
1831 if (cw->offx) {
1832 for (n = page_lines + 1; n < cw->maxrow; n++) {
1833 tty_curs(window, 1, n);
1834 cl_end();
1838 /* set extra chars.. */
1839 Strcat(resp, default_menu_cmds);
1840 Strcat(resp, "0123456789\033\n\r"); /* counts, quit */
1841 Strcat(resp, gacc); /* group accelerators */
1842 Strcat(resp, mapped_menu_cmds);
1844 if (cw->npages > 1)
1845 Sprintf(cw->morestr, "(%d of %d)", curr_page + 1,
1846 (int) cw->npages);
1847 else if (msave)
1848 Strcpy(cw->morestr, msave);
1849 else
1850 Strcpy(cw->morestr, defmorestr);
1852 tty_curs(window, 1, page_lines);
1853 cl_end();
1854 dmore(cw, resp);
1855 } else {
1856 /* just put the cursor back... */
1857 tty_curs(window, (int) strlen(cw->morestr) + 2, page_lines);
1858 xwaitforspace(resp);
1861 really_morc = morc; /* (only used with MENU_EXPLICIT_CHOICE */
1862 if ((rp = index(resp, morc)) != 0 && rp < resp + resp_len)
1863 /* explicit menu selection; don't override it if it also
1864 happens to match a mapped menu command (such as ':' to
1865 look inside a container vs ':' to search) */
1866 morc = MENU_EXPLICIT_CHOICE;
1867 else
1868 morc = map_menu_cmd(morc);
1870 switch (morc) {
1871 case '0':
1872 /* special case: '0' is also the default ball class */
1873 if (!counting && index(gacc, morc))
1874 goto group_accel;
1875 /* fall through to count the zero */
1876 case '1':
1877 case '2':
1878 case '3':
1879 case '4':
1880 case '5':
1881 case '6':
1882 case '7':
1883 case '8':
1884 case '9':
1885 count = (count * 10L) + (long) (morc - '0');
1887 * It is debatable whether we should allow 0 to
1888 * start a count. There is no difference if the
1889 * item is selected. If not selected, then
1890 * "0b" could mean:
1892 * count starting zero: "zero b's"
1893 * ignore starting zero: "select b"
1895 * At present I don't know which is better.
1897 if (count != 0L) { /* ignore leading zeros */
1898 counting = TRUE;
1899 reset_count = FALSE;
1901 break;
1902 case '\033': /* cancel - from counting or loop */
1903 if (!counting) {
1904 /* deselect everything */
1905 for (curr = cw->mlist; curr; curr = curr->next) {
1906 curr->selected = FALSE;
1907 curr->count = -1L;
1909 cw->flags |= WIN_CANCELLED;
1910 finished = TRUE;
1912 /* else only stop count */
1913 break;
1914 case '\0': /* finished (commit) */
1915 case '\n':
1916 case '\r':
1917 /* only finished if we are actually picking something */
1918 if (cw->how != PICK_NONE) {
1919 finished = TRUE;
1920 break;
1922 /* else fall through */
1923 case MENU_NEXT_PAGE:
1924 if (cw->npages > 0 && curr_page != cw->npages - 1) {
1925 curr_page++;
1926 page_start = 0;
1927 } else
1928 finished = TRUE; /* questionable behavior */
1929 break;
1930 case MENU_PREVIOUS_PAGE:
1931 if (cw->npages > 0 && curr_page != 0) {
1932 --curr_page;
1933 page_start = 0;
1935 break;
1936 case MENU_FIRST_PAGE:
1937 if (cw->npages > 0 && curr_page != 0) {
1938 page_start = 0;
1939 curr_page = 0;
1941 break;
1942 case MENU_LAST_PAGE:
1943 if (cw->npages > 0 && curr_page != cw->npages - 1) {
1944 page_start = 0;
1945 curr_page = cw->npages - 1;
1947 break;
1948 case MENU_SELECT_PAGE:
1949 if (cw->how == PICK_ANY)
1950 set_all_on_page(window, page_start, page_end);
1951 break;
1952 case MENU_UNSELECT_PAGE:
1953 unset_all_on_page(window, page_start, page_end);
1954 break;
1955 case MENU_INVERT_PAGE:
1956 if (cw->how == PICK_ANY)
1957 invert_all_on_page(window, page_start, page_end, 0);
1958 break;
1959 case MENU_SELECT_ALL:
1960 if (cw->how == PICK_ANY) {
1961 set_all_on_page(window, page_start, page_end);
1962 /* set the rest */
1963 for (curr = cw->mlist; curr; curr = curr->next)
1964 if (curr->identifier.a_void && !curr->selected)
1965 curr->selected = TRUE;
1967 break;
1968 case MENU_UNSELECT_ALL:
1969 unset_all_on_page(window, page_start, page_end);
1970 /* unset the rest */
1971 for (curr = cw->mlist; curr; curr = curr->next)
1972 if (curr->identifier.a_void && curr->selected) {
1973 curr->selected = FALSE;
1974 curr->count = -1;
1976 break;
1977 case MENU_INVERT_ALL:
1978 if (cw->how == PICK_ANY)
1979 invert_all(window, page_start, page_end, 0);
1980 break;
1981 case MENU_SEARCH:
1982 if (cw->how == PICK_NONE) {
1983 tty_nhbell();
1984 break;
1985 } else {
1986 char searchbuf[BUFSZ + 2], tmpbuf[BUFSZ];
1987 boolean on_curr_page = FALSE;
1988 int lineno = 0;
1990 tty_getlin("Search for:", tmpbuf);
1991 if (!tmpbuf[0] || tmpbuf[0] == '\033')
1992 break;
1993 Sprintf(searchbuf, "*%s*", tmpbuf);
1995 for (curr = cw->mlist; curr; curr = curr->next) {
1996 if (on_curr_page)
1997 lineno++;
1998 if (curr == page_start)
1999 on_curr_page = TRUE;
2000 else if (curr == page_end)
2001 on_curr_page = FALSE;
2002 if (curr->identifier.a_void
2003 && pmatchi(searchbuf, curr->str)) {
2004 toggle_menu_curr(window, curr, lineno, on_curr_page,
2005 counting, count);
2006 if (cw->how == PICK_ONE) {
2007 finished = TRUE;
2008 break;
2013 break;
2014 case MENU_EXPLICIT_CHOICE:
2015 morc = really_morc;
2016 /*FALLTHRU*/
2017 default:
2018 if (cw->how == PICK_NONE || !index(resp, morc)) {
2019 /* unacceptable input received */
2020 tty_nhbell();
2021 break;
2022 } else if (index(gacc, morc)) {
2023 group_accel:
2024 /* group accelerator; for the PICK_ONE case, we know that
2025 it matches exactly one item in order to be in gacc[] */
2026 invert_all(window, page_start, page_end, morc);
2027 if (cw->how == PICK_ONE)
2028 finished = TRUE;
2029 break;
2031 /* find, toggle, and possibly update */
2032 for (n = 0, curr = page_start; curr != page_end;
2033 n++, curr = curr->next)
2034 if (morc == curr->selector) {
2035 toggle_menu_curr(window, curr, n, TRUE, counting, count);
2036 if (cw->how == PICK_ONE)
2037 finished = TRUE;
2038 break; /* from `for' loop */
2040 break;
2043 } /* while */
2044 cw->morestr = msave;
2045 free((genericptr_t) morestr);
2048 STATIC_OVL void
2049 process_text_window(window, cw)
2050 winid window;
2051 struct WinDesc *cw;
2053 int i, n, attr;
2054 register char *cp;
2056 for (n = 0, i = 0; i < cw->maxrow; i++) {
2057 if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) {
2058 tty_curs(window, 1, n);
2059 cl_end();
2060 dmore(cw, quitchars);
2061 if (morc == '\033') {
2062 cw->flags |= WIN_CANCELLED;
2063 break;
2065 if (cw->offy) {
2066 tty_curs(window, 1, 0);
2067 cl_eos();
2068 } else
2069 clear_screen();
2070 n = 0;
2072 tty_curs(window, 1, n++);
2073 #ifdef H2344_BROKEN
2074 cl_end();
2075 #else
2076 if (cw->offx)
2077 cl_end();
2078 #endif
2079 if (cw->data[i]) {
2080 attr = cw->data[i][0] - 1;
2081 if (cw->offx) {
2082 (void) putchar(' ');
2083 ++ttyDisplay->curx;
2085 term_start_attr(attr);
2086 for (cp = &cw->data[i][1];
2087 #ifndef WIN32CON
2088 *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
2089 cp++)
2090 #else
2091 *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
2092 cp++, ttyDisplay->curx++)
2093 #endif
2094 (void) putchar(*cp);
2095 term_end_attr(attr);
2098 if (i == cw->maxrow) {
2099 #ifdef H2344_BROKEN
2100 if (cw->type == NHW_TEXT) {
2101 tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->cury + 1);
2102 cl_eos();
2104 #endif
2105 tty_curs(BASE_WINDOW, (int) cw->offx + 1,
2106 (cw->type == NHW_TEXT) ? (int) ttyDisplay->rows - 1 : n);
2107 cl_end();
2108 dmore(cw, quitchars);
2109 if (morc == '\033')
2110 cw->flags |= WIN_CANCELLED;
2114 /*ARGSUSED*/
2115 void
2116 tty_display_nhwindow(window, blocking)
2117 winid window;
2118 boolean blocking; /* with ttys, all windows are blocking */
2120 register struct WinDesc *cw = 0;
2121 short s_maxcol;
2123 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2124 panic(winpanicstr, window);
2125 if (cw->flags & WIN_CANCELLED)
2126 return;
2127 ttyDisplay->lastwin = window;
2128 ttyDisplay->rawprint = 0;
2130 print_vt_code2(AVTC_SELECT_WINDOW, window);
2132 switch (cw->type) {
2133 case NHW_MESSAGE:
2134 if (ttyDisplay->toplin == 1) {
2135 more();
2136 ttyDisplay->toplin = 1; /* more resets this */
2137 tty_clear_nhwindow(window);
2138 } else
2139 ttyDisplay->toplin = 0;
2140 cw->curx = cw->cury = 0;
2141 if (!cw->active)
2142 iflags.window_inited = TRUE;
2143 break;
2144 case NHW_MAP:
2145 end_glyphout();
2146 if (blocking) {
2147 if (!ttyDisplay->toplin)
2148 ttyDisplay->toplin = 1;
2149 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2150 return;
2152 case NHW_BASE:
2153 (void) fflush(stdout);
2154 break;
2155 case NHW_TEXT:
2156 cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2157 /*FALLTHRU*/
2158 case NHW_MENU:
2159 cw->active = 1;
2160 /* cw->maxcol is a long, but its value is constrained to
2161 be <= ttyDisplay->cols, so is sure to fit within a short */
2162 s_maxcol = (short) cw->maxcol;
2163 #ifdef H2344_BROKEN
2164 cw->offx = (cw->type == NHW_TEXT)
2166 : min(min(82, ttyDisplay->cols / 2),
2167 ttyDisplay->cols - s_maxcol - 1);
2168 #else
2169 /* avoid converting to uchar before calculations are finished */
2170 cw->offx = (uchar) max((int) 10,
2171 (int) (ttyDisplay->cols - s_maxcol - 1));
2172 #endif
2173 if (cw->offx < 0)
2174 cw->offx = 0;
2175 if (cw->type == NHW_MENU)
2176 cw->offy = 0;
2177 if (ttyDisplay->toplin == 1)
2178 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2179 #ifdef H2344_BROKEN
2180 if (cw->maxrow >= (int) ttyDisplay->rows
2181 || !iflags.menu_overlay)
2182 #else
2183 if (cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows
2184 || !iflags.menu_overlay)
2185 #endif
2187 cw->offx = 0;
2188 if (cw->offy || iflags.menu_overlay) {
2189 tty_curs(window, 1, 0);
2190 cl_eos();
2191 } else
2192 clear_screen();
2193 ttyDisplay->toplin = 0;
2194 } else {
2195 if (WIN_MESSAGE != WIN_ERR)
2196 tty_clear_nhwindow(WIN_MESSAGE);
2199 if (cw->data || !cw->maxrow)
2200 process_text_window(window, cw);
2201 else
2202 process_menu_window(window, cw);
2203 break;
2205 cw->active = 1;
2208 void
2209 tty_dismiss_nhwindow(window)
2210 winid window;
2212 register struct WinDesc *cw = 0;
2214 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2215 panic(winpanicstr, window);
2217 print_vt_code2(AVTC_SELECT_WINDOW, window);
2219 switch (cw->type) {
2220 case NHW_MESSAGE:
2221 if (ttyDisplay->toplin)
2222 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2223 /*FALLTHRU*/
2224 case NHW_STATUS:
2225 case NHW_BASE:
2226 case NHW_MAP:
2228 * these should only get dismissed when the game is going away
2229 * or suspending
2231 tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->rows - 1);
2232 cw->active = 0;
2233 break;
2234 case NHW_MENU:
2235 case NHW_TEXT:
2236 if (cw->active) {
2237 if (iflags.window_inited) {
2238 /* otherwise dismissing the text endwin after other windows
2239 * are dismissed tries to redraw the map and panics. since
2240 * the whole reason for dismissing the other windows was to
2241 * leave the ending window on the screen, we don't want to
2242 * erase it anyway.
2244 erase_menu_or_text(window, cw, FALSE);
2246 cw->active = 0;
2248 break;
2250 cw->flags = 0;
2253 void
2254 tty_destroy_nhwindow(window)
2255 winid window;
2257 register struct WinDesc *cw = 0;
2259 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2260 panic(winpanicstr, window);
2262 if (cw->active)
2263 tty_dismiss_nhwindow(window);
2264 if (cw->type == NHW_MESSAGE)
2265 iflags.window_inited = 0;
2266 if (cw->type == NHW_MAP)
2267 clear_screen();
2269 free_window_info(cw, TRUE);
2270 free((genericptr_t) cw);
2271 wins[window] = 0;
2274 void
2275 tty_curs(window, x, y)
2276 winid window;
2277 register int x, y; /* not xchar: perhaps xchar is unsigned and
2278 curx-x would be unsigned as well */
2280 struct WinDesc *cw = 0;
2281 int cx = ttyDisplay->curx;
2282 int cy = ttyDisplay->cury;
2284 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2285 panic(winpanicstr, window);
2286 ttyDisplay->lastwin = window;
2288 print_vt_code2(AVTC_SELECT_WINDOW, window);
2290 #if defined(USE_TILES) && defined(MSDOS)
2291 adjust_cursor_flags(cw);
2292 #endif
2293 cw->curx = --x; /* column 0 is never used */
2294 cw->cury = y;
2295 #ifdef DEBUG
2296 if (x < 0 || y < 0 || y >= cw->rows || x > cw->cols) {
2297 const char *s = "[unknown type]";
2298 switch (cw->type) {
2299 case NHW_MESSAGE:
2300 s = "[topl window]";
2301 break;
2302 case NHW_STATUS:
2303 s = "[status window]";
2304 break;
2305 case NHW_MAP:
2306 s = "[map window]";
2307 break;
2308 case NHW_MENU:
2309 s = "[corner window]";
2310 break;
2311 case NHW_TEXT:
2312 s = "[text window]";
2313 break;
2314 case NHW_BASE:
2315 s = "[base window]";
2316 break;
2318 debugpline4("bad curs positioning win %d %s (%d,%d)", window, s, x,
2320 return;
2322 #endif
2323 x += cw->offx;
2324 y += cw->offy;
2326 #ifdef CLIPPING
2327 if (clipping && window == WIN_MAP) {
2328 x -= clipx;
2329 y -= clipy;
2331 #endif
2333 if (y == cy && x == cx)
2334 return;
2336 if (cw->type == NHW_MAP)
2337 end_glyphout();
2339 #ifndef NO_TERMS
2340 if (!nh_ND && (cx != x || x <= 3)) { /* Extremely primitive */
2341 cmov(x, y); /* bunker!wtm */
2342 return;
2344 #endif
2346 if ((cy -= y) < 0)
2347 cy = -cy;
2348 if ((cx -= x) < 0)
2349 cx = -cx;
2350 if (cy <= 3 && cx <= 3) {
2351 nocmov(x, y);
2352 #ifndef NO_TERMS
2353 } else if ((x <= 3 && cy <= 3) || (!nh_CM && x < cx)) {
2354 (void) putchar('\r');
2355 ttyDisplay->curx = 0;
2356 nocmov(x, y);
2357 } else if (!nh_CM) {
2358 nocmov(x, y);
2359 #endif
2360 } else
2361 cmov(x, y);
2363 ttyDisplay->curx = x;
2364 ttyDisplay->cury = y;
2367 STATIC_OVL void
2368 tty_putsym(window, x, y, ch)
2369 winid window;
2370 int x, y;
2371 char ch;
2373 register struct WinDesc *cw = 0;
2375 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2376 panic(winpanicstr, window);
2378 print_vt_code2(AVTC_SELECT_WINDOW, window);
2380 switch (cw->type) {
2381 case NHW_STATUS:
2382 case NHW_MAP:
2383 case NHW_BASE:
2384 tty_curs(window, x, y);
2385 (void) putchar(ch);
2386 ttyDisplay->curx++;
2387 cw->curx++;
2388 break;
2389 case NHW_MESSAGE:
2390 case NHW_MENU:
2391 case NHW_TEXT:
2392 impossible("Can't putsym to window type %d", cw->type);
2393 break;
2397 STATIC_OVL const char *
2398 compress_str(str)
2399 const char *str;
2401 static char cbuf[BUFSZ];
2402 /* compress in case line too long */
2403 if ((int) strlen(str) >= CO) {
2404 register const char *bp0 = str;
2405 register char *bp1 = cbuf;
2407 do {
2408 #ifdef CLIPPING
2409 if (*bp0 != ' ' || bp0[1] != ' ')
2410 #else
2411 if (*bp0 != ' ' || bp0[1] != ' ' || bp0[2] != ' ')
2412 #endif
2413 *bp1++ = *bp0;
2414 } while (*bp0++);
2415 } else
2416 return str;
2417 return cbuf;
2420 void
2421 tty_putstr(window, attr, str)
2422 winid window;
2423 int attr;
2424 const char *str;
2426 register struct WinDesc *cw = 0;
2427 register char *ob;
2428 register const char *nb;
2429 register long i, j, n0;
2431 /* Assume there's a real problem if the window is missing --
2432 * probably a panic message
2434 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) {
2435 tty_raw_print(str);
2436 return;
2439 if (str == (const char *) 0
2440 || ((cw->flags & WIN_CANCELLED) && (cw->type != NHW_MESSAGE)))
2441 return;
2442 if (cw->type != NHW_MESSAGE)
2443 str = compress_str(str);
2445 ttyDisplay->lastwin = window;
2447 print_vt_code2(AVTC_SELECT_WINDOW, window);
2449 switch (cw->type) {
2450 case NHW_MESSAGE:
2451 /* really do this later */
2452 #if defined(USER_SOUNDS) && defined(WIN32CON)
2453 play_sound_for_message(str);
2454 #endif
2455 update_topl(str);
2456 break;
2458 case NHW_STATUS:
2459 ob = &cw->data[cw->cury][j = cw->curx];
2460 if (context.botlx)
2461 *ob = 0;
2462 if (!cw->cury && (int) strlen(str) >= CO) {
2463 /* the characters before "St:" are unnecessary */
2464 nb = index(str, ':');
2465 if (nb && nb > str + 2)
2466 str = nb - 2;
2468 nb = str;
2469 for (i = cw->curx + 1, n0 = cw->cols; i < n0; i++, nb++) {
2470 if (!*nb) {
2471 if (*ob || context.botlx) {
2472 /* last char printed may be in middle of line */
2473 tty_curs(WIN_STATUS, i, cw->cury);
2474 cl_end();
2476 break;
2478 if (*ob != *nb)
2479 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2480 if (*ob)
2481 ob++;
2484 (void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1);
2485 cw->data[cw->cury][cw->cols - 1] = '\0'; /* null terminate */
2486 #ifdef STATUS_VIA_WINDOWPORT
2487 if (!iflags.use_status_hilites) {
2488 #endif
2489 cw->cury = (cw->cury + 1) % 2;
2490 cw->curx = 0;
2491 #ifdef STATUS_VIA_WINDOWPORT
2493 #endif
2494 break;
2495 case NHW_MAP:
2496 tty_curs(window, cw->curx + 1, cw->cury);
2497 term_start_attr(attr);
2498 while (*str && (int) ttyDisplay->curx < (int) ttyDisplay->cols - 1) {
2499 (void) putchar(*str);
2500 str++;
2501 ttyDisplay->curx++;
2503 cw->curx = 0;
2504 cw->cury++;
2505 term_end_attr(attr);
2506 break;
2507 case NHW_BASE:
2508 tty_curs(window, cw->curx + 1, cw->cury);
2509 term_start_attr(attr);
2510 while (*str) {
2511 if ((int) ttyDisplay->curx >= (int) ttyDisplay->cols - 1) {
2512 cw->curx = 0;
2513 cw->cury++;
2514 tty_curs(window, cw->curx + 1, cw->cury);
2516 (void) putchar(*str);
2517 str++;
2518 ttyDisplay->curx++;
2520 cw->curx = 0;
2521 cw->cury++;
2522 term_end_attr(attr);
2523 break;
2524 case NHW_MENU:
2525 case NHW_TEXT:
2526 #ifdef H2344_BROKEN
2527 if (cw->type == NHW_TEXT
2528 && (cw->cury + cw->offy) == ttyDisplay->rows - 1)
2529 #else
2530 if (cw->type == NHW_TEXT && cw->cury == ttyDisplay->rows - 1)
2531 #endif
2533 /* not a menu, so save memory and output 1 page at a time */
2534 cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2535 tty_display_nhwindow(window, TRUE);
2536 for (i = 0; i < cw->maxrow; i++)
2537 if (cw->data[i]) {
2538 free((genericptr_t) cw->data[i]);
2539 cw->data[i] = 0;
2541 cw->maxrow = cw->cury = 0;
2543 /* always grows one at a time, but alloc 12 at a time */
2544 if (cw->cury >= cw->rows) {
2545 char **tmp;
2547 cw->rows += 12;
2548 tmp = (char **) alloc(sizeof(char *) * (unsigned) cw->rows);
2549 for (i = 0; i < cw->maxrow; i++)
2550 tmp[i] = cw->data[i];
2551 if (cw->data)
2552 free((genericptr_t) cw->data);
2553 cw->data = tmp;
2555 for (i = cw->maxrow; i < cw->rows; i++)
2556 cw->data[i] = 0;
2558 if (cw->data[cw->cury])
2559 free((genericptr_t) cw->data[cw->cury]);
2560 n0 = strlen(str) + 1;
2561 ob = cw->data[cw->cury] = (char *) alloc((unsigned) n0 + 1);
2562 *ob++ = (char) (attr + 1); /* avoid nuls, for convenience */
2563 Strcpy(ob, str);
2565 if (n0 > cw->maxcol)
2566 cw->maxcol = n0;
2567 if (++cw->cury > cw->maxrow)
2568 cw->maxrow = cw->cury;
2569 if (n0 > CO) {
2570 /* attempt to break the line */
2571 for (i = CO - 1; i && str[i] != ' ' && str[i] != '\n';)
2572 i--;
2573 if (i) {
2574 cw->data[cw->cury - 1][++i] = '\0';
2575 tty_putstr(window, attr, &str[i]);
2578 break;
2582 void
2583 tty_display_file(fname, complain)
2584 const char *fname;
2585 boolean complain;
2587 #ifdef DEF_PAGER /* this implies that UNIX is defined */
2589 /* use external pager; this may give security problems */
2590 register int fd = open(fname, 0);
2592 if (fd < 0) {
2593 if (complain)
2594 pline("Cannot open %s.", fname);
2595 else
2596 docrt();
2597 return;
2599 if (child(1)) {
2600 /* Now that child() does a setuid(getuid()) and a chdir(),
2601 we may not be able to open file fname anymore, so make
2602 it stdin. */
2603 (void) close(0);
2604 if (dup(fd)) {
2605 if (complain)
2606 raw_printf("Cannot open %s as stdin.", fname);
2607 } else {
2608 (void) execlp(catmore, "page", (char *) 0);
2609 if (complain)
2610 raw_printf("Cannot exec %s.", catmore);
2612 if (complain)
2613 sleep(10); /* want to wait_synch() but stdin is gone */
2614 terminate(EXIT_FAILURE);
2616 (void) close(fd);
2617 #ifdef notyet
2618 winch_seen = 0;
2619 #endif
2621 #else /* DEF_PAGER */
2623 dlb *f;
2624 char buf[BUFSZ];
2625 char *cr;
2627 tty_clear_nhwindow(WIN_MESSAGE);
2628 f = dlb_fopen(fname, "r");
2629 if (!f) {
2630 if (complain) {
2631 home();
2632 tty_mark_synch();
2633 tty_raw_print("");
2634 perror(fname);
2635 tty_wait_synch();
2636 pline("Cannot open \"%s\".", fname);
2637 } else if (u.ux)
2638 docrt();
2639 } else {
2640 winid datawin = tty_create_nhwindow(NHW_TEXT);
2641 boolean empty = TRUE;
2643 if (complain
2644 #ifndef NO_TERMS
2645 && nh_CD
2646 #endif
2648 /* attempt to scroll text below map window if there's room */
2649 wins[datawin]->offy = wins[WIN_STATUS]->offy + 3;
2650 if ((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows)
2651 wins[datawin]->offy = 0;
2653 while (dlb_fgets(buf, BUFSZ, f)) {
2654 if ((cr = index(buf, '\n')) != 0)
2655 *cr = 0;
2656 #ifdef MSDOS
2657 if ((cr = index(buf, '\r')) != 0)
2658 *cr = 0;
2659 #endif
2660 if (index(buf, '\t') != 0)
2661 (void) tabexpand(buf);
2662 empty = FALSE;
2663 tty_putstr(datawin, 0, buf);
2664 if (wins[datawin]->flags & WIN_CANCELLED)
2665 break;
2667 if (!empty)
2668 tty_display_nhwindow(datawin, FALSE);
2669 tty_destroy_nhwindow(datawin);
2670 (void) dlb_fclose(f);
2673 #endif /* DEF_PAGER */
2676 void
2677 tty_start_menu(window)
2678 winid window;
2680 tty_clear_nhwindow(window);
2681 return;
2684 /*ARGSUSED*/
2686 * Add a menu item to the beginning of the menu list. This list is reversed
2687 * later.
2689 void
2690 tty_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
2691 winid window; /* window to use, must be of type NHW_MENU */
2692 int glyph UNUSED; /* glyph to display with item (not used) */
2693 const anything *identifier; /* what to return if selected */
2694 char ch; /* keyboard accelerator (0 = pick our own) */
2695 char gch; /* group accelerator (0 = no group) */
2696 int attr; /* attribute for string (like tty_putstr()) */
2697 const char *str; /* menu string */
2698 boolean preselected; /* item is marked as selected */
2700 register struct WinDesc *cw = 0;
2701 tty_menu_item *item;
2702 const char *newstr;
2703 char buf[4 + BUFSZ];
2705 if (str == (const char *) 0)
2706 return;
2708 if (window == WIN_ERR
2709 || (cw = wins[window]) == (struct WinDesc *) 0
2710 || cw->type != NHW_MENU)
2711 panic(winpanicstr, window);
2713 cw->nitems++;
2714 if (identifier->a_void) {
2715 int len = strlen(str);
2717 if (len >= BUFSZ) {
2718 /* We *think* everything's coming in off at most BUFSZ bufs... */
2719 impossible("Menu item too long (%d).", len);
2720 len = BUFSZ - 1;
2722 Sprintf(buf, "%c - ", ch ? ch : '?');
2723 (void) strncpy(buf + 4, str, len);
2724 buf[4 + len] = '\0';
2725 newstr = buf;
2726 } else
2727 newstr = str;
2729 item = (tty_menu_item *) alloc(sizeof(tty_menu_item));
2730 item->identifier = *identifier;
2731 item->count = -1L;
2732 item->selected = preselected;
2733 item->selector = ch;
2734 item->gselector = gch;
2735 item->attr = attr;
2736 item->str = dupstr(newstr ? newstr : "");
2738 item->next = cw->mlist;
2739 cw->mlist = item;
2742 /* Invert the given list, can handle NULL as an input. */
2743 STATIC_OVL tty_menu_item *
2744 reverse(curr)
2745 tty_menu_item *curr;
2747 tty_menu_item *next, *head = 0;
2749 while (curr) {
2750 next = curr->next;
2751 curr->next = head;
2752 head = curr;
2753 curr = next;
2755 return head;
2759 * End a menu in this window, window must a type NHW_MENU. This routine
2760 * processes the string list. We calculate the # of pages, then assign
2761 * keyboard accelerators as needed. Finally we decide on the width and
2762 * height of the window.
2764 void
2765 tty_end_menu(window, prompt)
2766 winid window; /* menu to use */
2767 const char *prompt; /* prompt to for menu */
2769 struct WinDesc *cw = 0;
2770 tty_menu_item *curr;
2771 short len;
2772 int lmax, n;
2773 char menu_ch;
2775 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
2776 || cw->type != NHW_MENU)
2777 panic(winpanicstr, window);
2779 /* Reverse the list so that items are in correct order. */
2780 cw->mlist = reverse(cw->mlist);
2782 /* Put the prompt at the beginning of the menu. */
2783 if (prompt) {
2784 anything any;
2786 any = zeroany; /* not selectable */
2787 tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
2788 MENU_UNSELECTED);
2789 tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt,
2790 MENU_UNSELECTED);
2793 /* XXX another magic number? 52 */
2794 lmax = min(52, (int) ttyDisplay->rows - 1); /* # lines per page */
2795 cw->npages = (cw->nitems + (lmax - 1)) / lmax; /* # of pages */
2797 /* make sure page list is large enough */
2798 if (cw->plist_size < cw->npages + 1 /*need 1 slot beyond last*/) {
2799 if (cw->plist)
2800 free((genericptr_t) cw->plist);
2801 cw->plist_size = cw->npages + 1;
2802 cw->plist = (tty_menu_item **) alloc(cw->plist_size
2803 * sizeof(tty_menu_item *));
2806 cw->cols = 0; /* cols is set when the win is initialized... (why?) */
2807 menu_ch = '?'; /* lint suppression */
2808 for (n = 0, curr = cw->mlist; curr; n++, curr = curr->next) {
2809 /* set page boundaries and character accelerators */
2810 if ((n % lmax) == 0) {
2811 menu_ch = 'a';
2812 cw->plist[n / lmax] = curr;
2814 if (curr->identifier.a_void && !curr->selector) {
2815 curr->str[0] = curr->selector = menu_ch;
2816 if (menu_ch++ == 'z')
2817 menu_ch = 'A';
2820 /* cut off any lines that are too long */
2821 len = strlen(curr->str) + 2; /* extra space at beg & end */
2822 if (len > (int) ttyDisplay->cols) {
2823 curr->str[ttyDisplay->cols - 2] = 0;
2824 len = ttyDisplay->cols;
2826 if (len > cw->cols)
2827 cw->cols = len;
2829 cw->plist[cw->npages] = 0; /* plist terminator */
2832 * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
2834 if (cw->npages > 1) {
2835 char buf[QBUFSZ];
2836 /* produce the largest demo string */
2837 Sprintf(buf, "(%ld of %ld) ", cw->npages, cw->npages);
2838 len = strlen(buf);
2839 cw->morestr = dupstr("");
2840 } else {
2841 cw->morestr = dupstr("(end) ");
2842 len = strlen(cw->morestr);
2845 if (len > (int) ttyDisplay->cols) {
2846 /* truncate the prompt if it's too long for the screen */
2847 if (cw->npages <= 1) /* only str in single page case */
2848 cw->morestr[ttyDisplay->cols] = 0;
2849 len = ttyDisplay->cols;
2851 if (len > cw->cols)
2852 cw->cols = len;
2854 cw->maxcol = cw->cols;
2857 * The number of lines in the first page plus the morestr will be the
2858 * maximum size of the window.
2860 if (cw->npages > 1)
2861 cw->maxrow = cw->rows = lmax + 1;
2862 else
2863 cw->maxrow = cw->rows = cw->nitems + 1;
2867 tty_select_menu(window, how, menu_list)
2868 winid window;
2869 int how;
2870 menu_item **menu_list;
2872 register struct WinDesc *cw = 0;
2873 tty_menu_item *curr;
2874 menu_item *mi;
2875 int n, cancelled;
2877 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
2878 || cw->type != NHW_MENU)
2879 panic(winpanicstr, window);
2881 *menu_list = (menu_item *) 0;
2882 cw->how = (short) how;
2883 morc = 0;
2884 tty_display_nhwindow(window, TRUE);
2885 cancelled = !!(cw->flags & WIN_CANCELLED);
2886 tty_dismiss_nhwindow(window); /* does not destroy window data */
2888 if (cancelled) {
2889 n = -1;
2890 } else {
2891 for (n = 0, curr = cw->mlist; curr; curr = curr->next)
2892 if (curr->selected)
2893 n++;
2896 if (n > 0) {
2897 *menu_list = (menu_item *) alloc(n * sizeof(menu_item));
2898 for (mi = *menu_list, curr = cw->mlist; curr; curr = curr->next)
2899 if (curr->selected) {
2900 mi->item = curr->identifier;
2901 mi->count = curr->count;
2902 mi++;
2906 return n;
2909 /* special hack for treating top line --More-- as a one item menu */
2910 char
2911 tty_message_menu(let, how, mesg)
2912 char let;
2913 int how;
2914 const char *mesg;
2916 /* "menu" without selection; use ordinary pline, no more() */
2917 if (how == PICK_NONE) {
2918 pline("%s", mesg);
2919 return 0;
2922 ttyDisplay->dismiss_more = let;
2923 morc = 0;
2924 /* barebones pline(); since we're only supposed to be called after
2925 response to a prompt, we'll assume that the display is up to date */
2926 tty_putstr(WIN_MESSAGE, 0, mesg);
2927 /* if `mesg' didn't wrap (triggering --More--), force --More-- now */
2928 if (ttyDisplay->toplin == 1) {
2929 more();
2930 ttyDisplay->toplin = 1; /* more resets this */
2931 tty_clear_nhwindow(WIN_MESSAGE);
2933 /* normally <ESC> means skip further messages, but in this case
2934 it means cancel the current prompt; any other messages should
2935 continue to be output normally */
2936 wins[WIN_MESSAGE]->flags &= ~WIN_CANCELLED;
2937 ttyDisplay->dismiss_more = 0;
2939 return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0';
2942 void
2943 tty_update_inventory()
2945 return;
2948 void
2949 tty_mark_synch()
2951 (void) fflush(stdout);
2954 void
2955 tty_wait_synch()
2957 /* we just need to make sure all windows are synch'd */
2958 if (!ttyDisplay || ttyDisplay->rawprint) {
2959 getret();
2960 if (ttyDisplay)
2961 ttyDisplay->rawprint = 0;
2962 } else {
2963 tty_display_nhwindow(WIN_MAP, FALSE);
2964 if (ttyDisplay->inmore) {
2965 addtopl("--More--");
2966 (void) fflush(stdout);
2967 } else if (ttyDisplay->inread > program_state.gameover) {
2968 /* this can only happen if we were reading and got interrupted */
2969 ttyDisplay->toplin = 3;
2970 /* do this twice; 1st time gets the Quit? message again */
2971 (void) tty_doprev_message();
2972 (void) tty_doprev_message();
2973 ttyDisplay->intr++;
2974 (void) fflush(stdout);
2979 void
2980 docorner(xmin, ymax)
2981 register int xmin, ymax;
2983 register int y;
2984 register struct WinDesc *cw = wins[WIN_MAP];
2986 if (u.uswallow) { /* Can be done more efficiently */
2987 swallowed(1);
2988 return;
2991 #if defined(SIGWINCH) && defined(CLIPPING)
2992 if (ymax > LI)
2993 ymax = LI; /* can happen if window gets smaller */
2994 #endif
2995 for (y = 0; y < ymax; y++) {
2996 tty_curs(BASE_WINDOW, xmin, y); /* move cursor */
2997 cl_end(); /* clear to end of line */
2998 #ifdef CLIPPING
2999 if (y < (int) cw->offy || y + clipy > ROWNO)
3000 continue; /* only refresh board */
3001 #if defined(USE_TILES) && defined(MSDOS)
3002 if (iflags.tile_view)
3003 row_refresh((xmin / 2) + clipx - ((int) cw->offx / 2), COLNO - 1,
3004 y + clipy - (int) cw->offy);
3005 else
3006 #endif
3007 row_refresh(xmin + clipx - (int) cw->offx, COLNO - 1,
3008 y + clipy - (int) cw->offy);
3009 #else
3010 if (y < cw->offy || y > ROWNO)
3011 continue; /* only refresh board */
3012 row_refresh(xmin - (int) cw->offx, COLNO - 1, y - (int) cw->offy);
3013 #endif
3016 end_glyphout();
3017 if (ymax >= (int) wins[WIN_STATUS]->offy) {
3018 /* we have wrecked the bottom line */
3019 context.botlx = 1;
3020 bot();
3024 void
3025 end_glyphout()
3027 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3028 if (GFlag) {
3029 GFlag = FALSE;
3030 graph_off();
3032 #endif
3033 #ifdef TEXTCOLOR
3034 if (ttyDisplay->color != NO_COLOR) {
3035 term_end_color();
3036 ttyDisplay->color = NO_COLOR;
3038 #endif
3041 #ifndef WIN32
3042 void
3043 g_putch(in_ch)
3044 int in_ch;
3046 register char ch = (char) in_ch;
3048 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3049 if (SYMHANDLING(H_IBM) || iflags.eight_bit_tty) {
3050 /* IBM-compatible displays don't need other stuff */
3051 (void) putchar(ch);
3052 } else if (ch & 0x80) {
3053 if (!GFlag || HE_resets_AS) {
3054 graph_on();
3055 GFlag = TRUE;
3057 (void) putchar((ch ^ 0x80)); /* Strip 8th bit */
3058 } else {
3059 if (GFlag) {
3060 graph_off();
3061 GFlag = FALSE;
3063 (void) putchar(ch);
3066 #else
3067 (void) putchar(ch);
3069 #endif /* ASCIIGRAPH && !NO_TERMS */
3071 return;
3073 #endif /* !WIN32 */
3075 #ifdef CLIPPING
3076 void
3077 setclipped()
3079 clipping = TRUE;
3080 clipx = clipy = 0;
3081 clipxmax = CO;
3082 clipymax = LI - 3;
3085 void
3086 tty_cliparound(x, y)
3087 int x, y;
3089 extern boolean restoring;
3090 int oldx = clipx, oldy = clipy;
3092 if (!clipping)
3093 return;
3094 if (x < clipx + 5) {
3095 clipx = max(0, x - 20);
3096 clipxmax = clipx + CO;
3097 } else if (x > clipxmax - 5) {
3098 clipxmax = min(COLNO, clipxmax + 20);
3099 clipx = clipxmax - CO;
3101 if (y < clipy + 2) {
3102 clipy = max(0, y - (clipymax - clipy) / 2);
3103 clipymax = clipy + (LI - 3);
3104 } else if (y > clipymax - 2) {
3105 clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2);
3106 clipy = clipymax - (LI - 3);
3108 if (clipx != oldx || clipy != oldy) {
3109 if (on_level(&u.uz0, &u.uz) && !restoring)
3110 (void) doredraw();
3113 #endif /* CLIPPING */
3116 * tty_print_glyph
3118 * Print the glyph to the output device. Don't flush the output device.
3120 * Since this is only called from show_glyph(), it is assumed that the
3121 * position and glyph are always correct (checked there)!
3124 void
3125 tty_print_glyph(window, x, y, glyph, bkglyph)
3126 winid window;
3127 xchar x, y;
3128 int glyph;
3129 int bkglyph UNUSED;
3131 int ch;
3132 boolean reverse_on = FALSE;
3133 int color;
3134 unsigned special;
3136 #ifdef CLIPPING
3137 if (clipping) {
3138 if (x <= clipx || y < clipy || x >= clipxmax || y >= clipymax)
3139 return;
3141 #endif
3142 /* map glyph to character and color */
3143 (void) mapglyph(glyph, &ch, &color, &special, x, y);
3145 print_vt_code2(AVTC_SELECT_WINDOW, window);
3147 /* Move the cursor. */
3148 tty_curs(window, x, y);
3150 print_vt_code3(AVTC_GLYPH_START, glyph2tile[glyph], special);
3152 #ifndef NO_TERMS
3153 if (ul_hack && ch == '_') { /* non-destructive underscore */
3154 (void) putchar((char) ' ');
3155 backsp();
3157 #endif
3159 #ifdef TEXTCOLOR
3160 if (color != ttyDisplay->color) {
3161 if (ttyDisplay->color != NO_COLOR)
3162 term_end_color();
3163 ttyDisplay->color = color;
3164 if (color != NO_COLOR)
3165 term_start_color(color);
3167 #endif /* TEXTCOLOR */
3169 /* must be after color check; term_end_color may turn off inverse too */
3170 if (((special & MG_PET) && iflags.hilite_pet)
3171 || ((special & MG_OBJPILE) && iflags.hilite_pile)
3172 || ((special & MG_DETECT) && iflags.use_inverse)) {
3173 term_start_attr(ATR_INVERSE);
3174 reverse_on = TRUE;
3177 #if defined(USE_TILES) && defined(MSDOS)
3178 if (iflags.grmode && iflags.tile_view)
3179 xputg(glyph, ch, special);
3180 else
3181 #endif
3182 g_putch(ch); /* print the character */
3184 if (reverse_on) {
3185 term_end_attr(ATR_INVERSE);
3186 #ifdef TEXTCOLOR
3187 /* turn off color as well, ATR_INVERSE may have done this already */
3188 if (ttyDisplay->color != NO_COLOR) {
3189 term_end_color();
3190 ttyDisplay->color = NO_COLOR;
3192 #endif
3195 print_vt_code1(AVTC_GLYPH_END);
3197 wins[window]->curx++; /* one character over */
3198 ttyDisplay->curx++; /* the real cursor moved too */
3201 void
3202 tty_raw_print(str)
3203 const char *str;
3205 if (ttyDisplay)
3206 ttyDisplay->rawprint++;
3207 print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3208 #if defined(MICRO) || defined(WIN32CON)
3209 msmsg("%s\n", str);
3210 #else
3211 puts(str);
3212 (void) fflush(stdout);
3213 #endif
3216 void
3217 tty_raw_print_bold(str)
3218 const char *str;
3220 if (ttyDisplay)
3221 ttyDisplay->rawprint++;
3222 print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3223 term_start_raw_bold();
3224 #if defined(MICRO) || defined(WIN32CON)
3225 msmsg("%s", str);
3226 #else
3227 (void) fputs(str, stdout);
3228 #endif
3229 term_end_raw_bold();
3230 #if defined(MICRO) || defined(WIN32CON)
3231 msmsg("\n");
3232 #else
3233 puts("");
3234 (void) fflush(stdout);
3235 #endif
3239 tty_nhgetch()
3241 int i;
3242 #ifdef UNIX
3243 /* kludge alert: Some Unix variants return funny values if getc()
3244 * is called, interrupted, and then called again. There
3245 * is non-reentrant code in the internal _filbuf() routine, called by
3246 * getc().
3248 static volatile int nesting = 0;
3249 char nestbuf;
3250 #endif
3252 print_vt_code1(AVTC_INLINE_SYNC);
3253 (void) fflush(stdout);
3254 /* Note: if raw_print() and wait_synch() get called to report terminal
3255 * initialization problems, then wins[] and ttyDisplay might not be
3256 * available yet. Such problems will probably be fatal before we get
3257 * here, but validate those pointers just in case...
3259 if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3260 wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3261 #ifdef UNIX
3262 i = (++nesting == 1) ? tgetch()
3263 : (read(fileno(stdin), (genericptr_t) &nestbuf, 1)
3264 == 1) ? (int) nestbuf : EOF;
3265 --nesting;
3266 #else
3267 i = tgetch();
3268 #endif
3269 if (!i)
3270 i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
3271 else if (i == EOF)
3272 i = '\033'; /* same for EOF */
3273 if (ttyDisplay && ttyDisplay->toplin == 1)
3274 ttyDisplay->toplin = 2;
3275 #ifdef TTY_TILES_ESCCODES
3277 /* hack to force output of the window select code */
3278 int tmp = vt_tile_current_window;
3279 vt_tile_current_window++;
3280 print_vt_code2(AVTC_SELECT_WINDOW, tmp);
3282 #endif /* TTY_TILES_ESCCODES */
3283 return i;
3287 * return a key, or 0, in which case a mouse button was pressed
3288 * mouse events should be returned as character postitions in the map window.
3289 * Since normal tty's don't have mice, just return a key.
3291 /*ARGSUSED*/
3293 tty_nh_poskey(x, y, mod)
3294 int *x, *y, *mod;
3296 #if defined(WIN32CON)
3297 int i;
3298 (void) fflush(stdout);
3299 /* Note: if raw_print() and wait_synch() get called to report terminal
3300 * initialization problems, then wins[] and ttyDisplay might not be
3301 * available yet. Such problems will probably be fatal before we get
3302 * here, but validate those pointers just in case...
3304 if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3305 wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3306 i = ntposkey(x, y, mod);
3307 if (!i && mod && (*mod == 0 || *mod == EOF))
3308 i = '\033'; /* map NUL or EOF to ESC, nethack doesn't expect either */
3309 if (ttyDisplay && ttyDisplay->toplin == 1)
3310 ttyDisplay->toplin = 2;
3311 return i;
3312 #else /* !WIN32CON */
3313 nhUse(x);
3314 nhUse(y);
3315 nhUse(mod);
3317 return tty_nhgetch();
3318 #endif /* ?WIN32CON */
3321 void
3322 win_tty_init(dir)
3323 int dir;
3325 if (dir != WININIT)
3326 return;
3327 #if defined(WIN32CON)
3328 if (!strncmpi(windowprocs.name, "tty", 3))
3329 nttty_open(0);
3330 #endif
3331 return;
3334 #ifdef POSITIONBAR
3335 void
3336 tty_update_positionbar(posbar)
3337 char *posbar;
3339 #ifdef MSDOS
3340 video_update_positionbar(posbar);
3341 #endif
3343 #endif
3345 #ifdef STATUS_VIA_WINDOWPORT
3347 * The following data structures come from the genl_ routines in
3348 * src/windows.c and as such are considered to be on the window-port
3349 * "side" of things, rather than the NetHack-core "side" of things.
3352 extern const char *status_fieldnm[MAXBLSTATS];
3353 extern const char *status_fieldfmt[MAXBLSTATS];
3354 extern char *status_vals[MAXBLSTATS];
3355 extern boolean status_activefields[MAXBLSTATS];
3356 extern winid WIN_STATUS;
3358 #ifdef STATUS_HILITES
3359 typedef struct hilite_data_struct {
3360 int thresholdtype;
3361 anything threshold;
3362 int behavior;
3363 int under;
3364 int over;
3365 } hilite_data_t;
3366 static hilite_data_t tty_status_hilites[MAXBLSTATS];
3367 static int tty_status_colors[MAXBLSTATS];
3369 struct color_option {
3370 int color;
3371 int attr_bits;
3374 static void FDECL(start_color_option, (struct color_option));
3375 static void FDECL(end_color_option, (struct color_option));
3376 static void FDECL(apply_color_option, (struct color_option, const char *));
3377 static void FDECL(add_colored_text, (const char *, char *));
3378 #endif
3380 void
3381 tty_status_init()
3383 int i;
3385 /* let genl_status_init do most of the initialization */
3386 genl_status_init();
3388 for (i = 0; i < MAXBLSTATS; ++i) {
3389 #ifdef STATUS_HILITES
3390 tty_status_colors[i] = NO_COLOR; /* no color */
3391 tty_status_hilites[i].thresholdtype = 0;
3392 tty_status_hilites[i].behavior = BL_TH_NONE;
3393 tty_status_hilites[i].under = BL_HILITE_NONE;
3394 tty_status_hilites[i].over = BL_HILITE_NONE;
3395 #endif /* STATUS_HILITES */
3400 * *_status_update()
3401 * -- update the value of a status field.
3402 * -- the fldindex identifies which field is changing and
3403 * is an integer index value from botl.h
3404 * -- fldindex could be any one of the following from botl.h:
3405 * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3406 * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
3407 * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
3408 * BL_LEVELDESC, BL_EXP, BL_CONDITION
3409 * -- fldindex could also be BL_FLUSH (-1), which is not really
3410 * a field index, but is a special trigger to tell the
3411 * windowport that it should redisplay all its status fields,
3412 * even if no changes have been presented to it.
3413 * -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
3414 * If fldindex is BL_CONDITION, then ptr is a long value with
3415 * any or none of the following bits set (from botl.h):
3416 * BL_MASK_STONE 0x00000001L
3417 * BL_MASK_SLIME 0x00000002L
3418 * BL_MASK_STRNGL 0x00000004L
3419 * BL_MASK_FOODPOIS 0x00000008L
3420 * BL_MASK_TERMILL 0x00000010L
3421 * BL_MASK_BLIND 0x00000020L
3422 * BL_MASK_DEAF 0x00000040L
3423 * BL_MASK_STUN 0x00000080L
3424 * BL_MASK_CONF 0x00000100L
3425 * BL_MASK_HALLU 0x00000200L
3426 * BL_MASK_LEV 0x00000400L
3427 * BL_MASK_FLY 0x00000800L
3428 * BL_MASK_RIDE 0x00001000L
3429 * -- The value passed for BL_GOLD includes an encoded leading
3430 * symbol for GOLD "\GXXXXNNNN:nnn". If the window port needs to use
3431 * the textual gold amount without the leading "$:" the port will
3432 * have to skip past ':' in the passed "ptr" for the BL_GOLD case.
3434 void
3435 tty_status_update(fldidx, ptr, chg, percent)
3436 int fldidx, chg, percent;
3437 genericptr_t ptr;
3439 long cond, *condptr = (long *) ptr;
3440 register int i;
3441 char *text = (char *) ptr;
3442 /* Mapping BL attributes to tty attributes
3443 * BL_HILITE_NONE -1 + 3 = 2 (statusattr[2])
3444 * BL_HILITE_INVERSE -2 + 3 = 1 (statusattr[1])
3445 * BL_HILITE_BOLD -3 + 3 = 0 (statusattr[0])
3447 long value = -1L;
3448 static boolean beenhere = FALSE;
3449 enum statusfields fieldorder[2][15] = {
3450 { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN,
3451 BL_SCORE, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH,
3452 BL_FLUSH },
3453 { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
3454 BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER,
3455 BL_CAP, BL_CONDITION, BL_FLUSH }
3457 #ifdef STATUS_HILITES
3458 static int statusattr[] = { ATR_BOLD, ATR_INVERSE, ATR_NONE };
3459 int attridx = 0;
3460 #else
3461 nhUse(chg);
3462 nhUse(percent);
3463 #endif
3465 if (fldidx != BL_FLUSH) {
3466 if (!status_activefields[fldidx])
3467 return;
3468 switch (fldidx) {
3469 case BL_CONDITION:
3470 cond = *condptr;
3471 *status_vals[fldidx] = '\0';
3472 if (cond & BL_MASK_STONE)
3473 Strcat(status_vals[fldidx], " Stone");
3474 if (cond & BL_MASK_SLIME)
3475 Strcat(status_vals[fldidx], " Slime");
3476 if (cond & BL_MASK_STRNGL)
3477 Strcat(status_vals[fldidx], " Strngl");
3478 if (cond & BL_MASK_FOODPOIS)
3479 Strcat(status_vals[fldidx], " FoodPois");
3480 if (cond & BL_MASK_TERMILL)
3481 Strcat(status_vals[fldidx], " TermIll");
3482 if (cond & BL_MASK_BLIND)
3483 Strcat(status_vals[fldidx], " Blind");
3484 if (cond & BL_MASK_DEAF)
3485 Strcat(status_vals[fldidx], " Deaf");
3486 if (cond & BL_MASK_STUN)
3487 Strcat(status_vals[fldidx], " Stun");
3488 if (cond & BL_MASK_CONF)
3489 Strcat(status_vals[fldidx], " Conf");
3490 if (cond & BL_MASK_HALLU)
3491 Strcat(status_vals[fldidx], " Hallu");
3492 if (cond & BL_MASK_LEV)
3493 Strcat(status_vals[fldidx], " Lev");
3494 if (cond & BL_MASK_FLY)
3495 Strcat(status_vals[fldidx], " Fly");
3496 if (cond & BL_MASK_RIDE)
3497 Strcat(status_vals[fldidx], " Ride");
3498 value = cond;
3499 break;
3500 default:
3501 value = atol(text);
3502 Sprintf(status_vals[fldidx],
3503 status_fieldfmt[fldidx] ? status_fieldfmt[fldidx] : "%s",
3504 text);
3505 break;
3508 #ifdef STATUS_HILITES
3509 switch (tty_status_hilites[fldidx].behavior) {
3510 case BL_TH_NONE:
3511 tty_status_colors[fldidx] = NO_COLOR;
3512 break;
3513 case BL_TH_UPDOWN:
3514 if (chg > 0)
3515 tty_status_colors[fldidx] = tty_status_hilites[fldidx].over;
3516 else if (chg < 0)
3517 tty_status_colors[fldidx] = tty_status_hilites[fldidx].under;
3518 else
3519 tty_status_colors[fldidx] = NO_COLOR;
3520 break;
3521 case BL_TH_VAL_PERCENTAGE: {
3522 int pct_th = 0;
3524 if (tty_status_hilites[fldidx].thresholdtype != ANY_INT) {
3525 impossible(
3526 "tty_status_update: unsupported percentage threshold type %d",
3527 tty_status_hilites[fldidx].thresholdtype);
3528 } else {
3529 pct_th = tty_status_hilites[fldidx].threshold.a_int;
3530 tty_status_colors[fldidx] = (percent >= pct_th)
3531 ? tty_status_hilites[fldidx].over
3532 : tty_status_hilites[fldidx].under;
3534 break;
3536 case BL_TH_VAL_ABSOLUTE: {
3537 int c = NO_COLOR;
3538 int o = tty_status_hilites[fldidx].over;
3539 int u = tty_status_hilites[fldidx].under;
3540 anything *t = &tty_status_hilites[fldidx].threshold;
3542 switch (tty_status_hilites[fldidx].thresholdtype) {
3543 case ANY_LONG:
3544 c = (value >= t->a_long) ? o : u;
3545 break;
3546 case ANY_INT:
3547 c = (value >= t->a_int) ? o : u;
3548 break;
3549 case ANY_UINT:
3550 c = ((unsigned long) value >= t->a_uint) ? o : u;
3551 break;
3552 case ANY_ULONG:
3553 c = ((unsigned long) value >= t->a_ulong) ? o : u;
3554 break;
3555 case ANY_MASK32:
3556 c = (value & t->a_ulong) ? o : u;
3557 break;
3558 default:
3559 impossible(
3560 "tty_status_update: unsupported absolute threshold type %d\n",
3561 tty_status_hilites[fldidx].thresholdtype);
3562 break;
3564 tty_status_colors[fldidx] = c;
3565 break;
3566 } /* case */
3567 } /* switch */
3568 #endif /* STATUS_HILITES */
3571 /* For now, this version copied from the genl_ version currently
3572 * updates everything on the display, everytime
3575 if (!beenhere || !iflags.use_status_hilites) {
3576 char newbot1[MAXCO], newbot2[MAXCO];
3578 newbot1[0] = '\0';
3579 for (i = 0; fieldorder[0][i] >= 0; ++i) {
3580 int idx1 = fieldorder[0][i];
3582 if (status_activefields[idx1])
3583 Strcat(newbot1, status_vals[idx1]);
3585 newbot2[0] = '\0';
3586 for (i = 0; fieldorder[1][i] >= 0; ++i) {
3587 int idx2 = fieldorder[1][i];
3589 if (status_activefields[idx2])
3590 Strcat(newbot2, status_vals[idx2]);
3593 curs(WIN_STATUS, 1, 0);
3594 putstr(WIN_STATUS, 0, newbot1);
3595 curs(WIN_STATUS, 1, 1);
3596 putmixed(WIN_STATUS, 0, newbot2); /* putmixed() due to GOLD glyph */
3597 beenhere = TRUE;
3598 return;
3601 curs(WIN_STATUS, 1, 0);
3602 for (i = 0; fieldorder[0][i] != BL_FLUSH; ++i) {
3603 int fldidx1 = fieldorder[0][i];
3605 if (status_activefields[fldidx1]) {
3606 #ifdef STATUS_HILITES
3607 if (tty_status_colors[fldidx1] < 0
3608 && tty_status_colors[fldidx1] >= -3) {
3609 /* attribute, not a color */
3610 attridx = tty_status_colors[fldidx1] + 3;
3611 term_start_attr(statusattr[attridx]);
3612 putstr(WIN_STATUS, 0, status_vals[fldidx1]);
3613 term_end_attr(statusattr[attridx]);
3614 } else
3615 #ifdef TEXTCOLOR
3616 if (tty_status_colors[fldidx1] != CLR_MAX) {
3617 if (tty_status_colors[fldidx1] != NO_COLOR)
3618 term_start_color(tty_status_colors[fldidx1]);
3619 putstr(WIN_STATUS, 0, status_vals[fldidx1]);
3620 if (tty_status_colors[fldidx1] != NO_COLOR)
3621 term_end_color();
3622 } else
3623 #endif
3624 #endif /* STATUS_HILITES */
3625 putstr(WIN_STATUS, 0, status_vals[fldidx1]);
3628 curs(WIN_STATUS, 1, 1);
3629 for (i = 0; fieldorder[1][i] != BL_FLUSH; ++i) {
3630 int fldidx2 = fieldorder[1][i];
3632 if (status_activefields[fldidx2]) {
3633 #ifdef STATUS_HILITES
3634 if (tty_status_colors[fldidx2] < 0
3635 && tty_status_colors[fldidx2] >= -3) {
3636 /* attribute, not a color */
3637 attridx = tty_status_colors[fldidx2] + 3;
3638 term_start_attr(statusattr[attridx]);
3639 putstr(WIN_STATUS, 0, status_vals[fldidx2]);
3640 term_end_attr(statusattr[attridx]);
3641 } else
3642 #ifdef TEXTCOLOR
3643 if (tty_status_colors[fldidx2] != CLR_MAX) {
3644 if (tty_status_colors[fldidx2] != NO_COLOR)
3645 term_start_color(tty_status_colors[fldidx2]);
3646 if (fldidx2 == BL_GOLD) {
3647 /* putmixed() due to GOLD glyph */
3648 putmixed(WIN_STATUS, 0, status_vals[fldidx2]);
3649 } else {
3650 putstr(WIN_STATUS, 0, status_vals[fldidx2]);
3652 if (tty_status_colors[fldidx2] != NO_COLOR)
3653 term_end_color();
3654 } else
3655 #endif
3656 #endif /* STATUS_HILITES */
3657 putstr(WIN_STATUS, 0, status_vals[fldidx2]);
3660 return;
3663 #ifdef STATUS_HILITES
3665 * status_threshold(int fldidx, int threshholdtype, anything threshold,
3666 * int behavior, int under, int over)
3668 * -- called when a hiliting preference is added, changed, or
3669 * removed.
3670 * -- the fldindex identifies which field is having its hiliting
3671 * preference set. It is an integer index value from botl.h
3672 * -- fldindex could be any one of the following from botl.h:
3673 * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3674 * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
3675 * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
3676 * BL_LEVELDESC, BL_EXP, BL_CONDITION
3677 * -- datatype is P_INT, P_UINT, P_LONG, or P_MASK.
3678 * -- threshold is an "anything" union which can contain the
3679 * datatype value.
3680 * -- behavior is used to define how threshold is used and can
3681 * be BL_TH_NONE, BL_TH_VAL_PERCENTAGE, BL_TH_VAL_ABSOLUTE,
3682 * or BL_TH_UPDOWN. BL_TH_NONE means don't do anything above
3683 * or below the threshold. BL_TH_VAL_PERCENTAGE treats the
3684 * threshold value as a precentage of the maximum possible
3685 * value. BL_TH_VAL_ABSOLUTE means that the threshold is an
3686 * actual value. BL_TH_UPDOWN means that threshold is not
3687 * used, and the two below/above hilite values indicate how
3688 * to display something going down (under) or rising (over).
3689 * -- under is the hilite attribute used if value is below the
3690 * threshold. The attribute can be BL_HILITE_NONE,
3691 * BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one
3692 * of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN,
3693 * CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY,
3694 * CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE,
3695 * CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15).
3696 * -- over is the hilite attribute used if value is at or above
3697 * the threshold. The attribute can be BL_HILITE_NONE,
3698 * BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one
3699 * of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN,
3700 * CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY,
3701 * CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE,
3702 * CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15).
3704 void
3705 tty_status_threshold(fldidx, thresholdtype, threshold, behavior, under, over)
3706 int fldidx, thresholdtype;
3707 int behavior, under, over;
3708 anything threshold;
3710 tty_status_hilites[fldidx].thresholdtype = thresholdtype;
3711 tty_status_hilites[fldidx].threshold = threshold;
3712 tty_status_hilites[fldidx].behavior = behavior;
3713 tty_status_hilites[fldidx].under = under;
3714 tty_status_hilites[fldidx].over = over;
3715 return;
3718 #endif /* STATUS_HILITES */
3719 #endif /*STATUS_VIA_WINDOWPORT*/
3721 #endif /* TTY_GRAPHICS */
3723 /*wintty.c*/