Document explicitly what m-prefix does to each command
[aNetHack.git] / win / tty / wintty.c
blob3ac407a5615d1c7ef895d5a902bfadb61fd4ef41
1 /* NetHack 3.6 wintty.c $NHDT-Date: 1463614572 2016/05/18 23:36:12 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.131 $ */
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) || defined(_BULL_SOURCE)
35 #include <signal.h>
36 #endif
37 #endif
39 #ifdef TTY_TILES_ESCCODES
40 extern short glyph2tile[];
41 #define TILE_ANSI_COMMAND 'z'
42 #define AVTC_GLYPH_START 0
43 #define AVTC_GLYPH_END 1
44 #define AVTC_SELECT_WINDOW 2
45 #define AVTC_INLINE_SYNC 3
46 #endif
48 extern char mapped_menu_cmds[]; /* from options.c */
50 /* this is only needed until tty_status_* routines are written */
51 extern NEARDATA winid WIN_STATUS;
53 /* Interface definition, for windows.c */
54 struct window_procs tty_procs = {
55 "tty",
56 #ifdef MSDOS
57 WC_TILED_MAP | WC_ASCII_MAP |
58 #endif
59 #if defined(WIN32CON)
60 WC_MOUSE_SUPPORT |
61 #endif
62 WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN,
63 #if defined(SELECTSAVED)
64 WC2_SELECTSAVED |
65 #endif
66 WC2_DARKGRAY,
67 tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event,
68 tty_exit_nhwindows, tty_suspend_nhwindows, tty_resume_nhwindows,
69 tty_create_nhwindow, tty_clear_nhwindow, tty_display_nhwindow,
70 tty_destroy_nhwindow, tty_curs, tty_putstr, genl_putmixed,
71 tty_display_file, tty_start_menu, tty_add_menu, tty_end_menu,
72 tty_select_menu, tty_message_menu, tty_update_inventory, tty_mark_synch,
73 tty_wait_synch,
74 #ifdef CLIPPING
75 tty_cliparound,
76 #endif
77 #ifdef POSITIONBAR
78 tty_update_positionbar,
79 #endif
80 tty_print_glyph, tty_raw_print, tty_raw_print_bold, tty_nhgetch,
81 tty_nh_poskey, tty_nhbell, tty_doprev_message, tty_yn_function,
82 tty_getlin, tty_get_ext_cmd, tty_number_pad, tty_delay_output,
83 #ifdef CHANGE_COLOR /* the Mac uses a palette device */
84 tty_change_color,
85 #ifdef MAC
86 tty_change_background, set_tty_font_name,
87 #endif
88 tty_get_color_string,
89 #endif
91 /* other defs that really should go away (they're tty specific) */
92 tty_start_screen, tty_end_screen, genl_outrip,
93 #if defined(WIN32)
94 nttty_preference_update,
95 #else
96 genl_preference_update,
97 #endif
98 tty_getmsghistory, tty_putmsghistory,
99 #ifdef STATUS_VIA_WINDOWPORT
100 tty_status_init,
101 genl_status_finish, genl_status_enablefield,
102 tty_status_update,
103 #ifdef STATUS_HILITES
104 tty_status_threshold,
105 #endif
106 #endif
107 genl_can_suspend_yes,
110 static int maxwin = 0; /* number of windows in use */
111 winid BASE_WINDOW;
112 struct WinDesc *wins[MAXWIN];
113 struct DisplayDesc *ttyDisplay; /* the tty display descriptor */
115 extern void FDECL(cmov, (int, int)); /* from termcap.c */
116 extern void FDECL(nocmov, (int, int)); /* from termcap.c */
117 #if defined(UNIX) || defined(VMS)
118 static char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
119 #endif
121 static char winpanicstr[] = "Bad window id %d";
122 char defmorestr[] = "--More--";
124 #ifdef CLIPPING
125 #if defined(USE_TILES) && defined(MSDOS)
126 boolean clipping = FALSE; /* clipping on? */
127 int clipx = 0, clipxmax = 0;
128 #else
129 static boolean clipping = FALSE; /* clipping on? */
130 static int clipx = 0, clipxmax = 0;
131 #endif
132 static int clipy = 0, clipymax = 0;
133 #endif /* CLIPPING */
135 #if defined(USE_TILES) && defined(MSDOS)
136 extern void FDECL(adjust_cursor_flags, (struct WinDesc *));
137 #endif
139 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
140 boolean GFlag = FALSE;
141 boolean HE_resets_AS; /* see termcap.c */
142 #endif
144 #if defined(MICRO) || defined(WIN32CON)
145 static const char to_continue[] = "to continue";
146 #define getret() getreturn(to_continue)
147 #else
148 STATIC_DCL void NDECL(getret);
149 #endif
150 STATIC_DCL void FDECL(erase_menu_or_text,
151 (winid, struct WinDesc *, BOOLEAN_P));
152 STATIC_DCL void FDECL(free_window_info, (struct WinDesc *, BOOLEAN_P));
153 STATIC_DCL void FDECL(dmore, (struct WinDesc *, const char *));
154 STATIC_DCL void FDECL(set_item_state, (winid, int, tty_menu_item *));
155 STATIC_DCL void FDECL(set_all_on_page, (winid, tty_menu_item *,
156 tty_menu_item *));
157 STATIC_DCL void FDECL(unset_all_on_page, (winid, tty_menu_item *,
158 tty_menu_item *));
159 STATIC_DCL void FDECL(invert_all_on_page, (winid, tty_menu_item *,
160 tty_menu_item *, CHAR_P));
161 STATIC_DCL void FDECL(invert_all, (winid, tty_menu_item *,
162 tty_menu_item *, CHAR_P));
163 STATIC_DCL void FDECL(toggle_menu_attr, (BOOLEAN_P, int, int));
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_DCL void FDECL(winch_handler, (int));
232 * This really ought to just set a flag like the hangup handler does,
233 * then check the flag at "safe" times, in case the signal arrives
234 * while something fragile is executing. Better to have a brief period
235 * where display updates don't fit the new size than for tty internals
236 * to become corrupted.
238 * 'winch_seen' has been "notyet" for a long time....
240 /*ARGUSED*/
241 STATIC_OVL void
242 winch_handler(sig_unused) /* signal handler is called with at least 1 arg */
243 int sig_unused UNUSED;
245 int oldLI = LI, oldCO = CO, i;
246 register struct WinDesc *cw;
248 #ifdef WINCHAIN
250 #define WINCH_MESSAGE "(SIGWINCH)"
251 if (wc_tracelogf)
252 (void) write(fileno(wc_tracelogf), WINCH_MESSAGE,
253 strlen(WINCH_MESSAGE));
254 #undef WINCH_MESSAGE
256 #endif
257 getwindowsz();
258 /* For long running events such as multi-page menus and
259 * display_file(), we note the signal's occurance and
260 * hope the code there decides to handle the situation
261 * and reset the flag. There will be race conditions
262 * when handling this - code handlers so it doesn't matter.
264 #ifdef notyet
265 winch_seen = TRUE;
266 #endif
267 if ((oldLI != LI || oldCO != CO) && ttyDisplay) {
268 ttyDisplay->rows = LI;
269 ttyDisplay->cols = CO;
271 cw = wins[BASE_WINDOW];
272 cw->rows = ttyDisplay->rows;
273 cw->cols = ttyDisplay->cols;
275 if (iflags.window_inited) {
276 cw = wins[WIN_MESSAGE];
277 cw->curx = cw->cury = 0;
279 tty_destroy_nhwindow(WIN_STATUS);
280 WIN_STATUS = tty_create_nhwindow(NHW_STATUS);
282 if (u.ux) {
283 #ifdef CLIPPING
284 if (CO < COLNO || LI < ROWNO + 3) {
285 setclipped();
286 tty_cliparound(u.ux, u.uy);
287 } else {
288 clipping = FALSE;
289 clipx = clipy = 0;
291 #endif
292 i = ttyDisplay->toplin;
293 ttyDisplay->toplin = 0;
294 docrt();
295 bot();
296 ttyDisplay->toplin = i;
297 flush_screen(1);
298 if (i) {
299 addtopl(toplines);
300 } else
301 for (i = WIN_INVEN; i < MAXWIN; i++)
302 if (wins[i] && wins[i]->active) {
303 /* cop-out */
304 addtopl("Press Return to continue: ");
305 break;
307 (void) fflush(stdout);
308 if (i < 2)
309 flush_screen(1);
314 #endif
316 /*ARGSUSED*/
317 void
318 tty_init_nhwindows(argcp, argv)
319 int *argcp UNUSED;
320 char **argv UNUSED;
322 int wid, hgt, i;
325 * Remember tty modes, to be restored on exit.
327 * gettty() must be called before tty_startup()
328 * due to ordering of LI/CO settings
329 * tty_startup() must be called before initoptions()
330 * due to ordering of graphics settings
332 #if defined(UNIX) || defined(VMS)
333 setbuf(stdout, obuf);
334 #endif
335 gettty();
337 /* to port dependant tty setup */
338 tty_startup(&wid, &hgt);
339 setftty(); /* calls start_screen */
341 /* set up tty descriptor */
342 ttyDisplay = (struct DisplayDesc *) alloc(sizeof(struct DisplayDesc));
343 ttyDisplay->toplin = 0;
344 ttyDisplay->rows = hgt;
345 ttyDisplay->cols = wid;
346 ttyDisplay->curx = ttyDisplay->cury = 0;
347 ttyDisplay->inmore = ttyDisplay->inread = ttyDisplay->intr = 0;
348 ttyDisplay->dismiss_more = 0;
349 #ifdef TEXTCOLOR
350 ttyDisplay->color = NO_COLOR;
351 #endif
352 ttyDisplay->attrs = 0;
354 /* set up the default windows */
355 BASE_WINDOW = tty_create_nhwindow(NHW_BASE);
356 wins[BASE_WINDOW]->active = 1;
358 ttyDisplay->lastwin = WIN_ERR;
360 #if defined(SIGWINCH) && defined(CLIPPING) && !defined(NO_SIGNAL)
361 (void) signal(SIGWINCH, (SIG_RET_TYPE) winch_handler);
362 #endif
364 tty_clear_nhwindow(BASE_WINDOW);
366 tty_putstr(BASE_WINDOW, 0, "");
367 for (i = 1; i <= 4; ++i)
368 tty_putstr(BASE_WINDOW, 0, copyright_banner_line(i));
369 tty_putstr(BASE_WINDOW, 0, "");
370 tty_display_nhwindow(BASE_WINDOW, FALSE);
373 /* try to reduce clutter in the code below... */
374 #define ROLE flags.initrole
375 #define RACE flags.initrace
376 #define GEND flags.initgend
377 #define ALGN flags.initalign
379 void
380 tty_player_selection()
382 int i, k, n, choice, nextpick;
383 boolean getconfirmation, picksomething;
384 char pick4u = 'n';
385 char pbuf[QBUFSZ], plbuf[QBUFSZ];
386 winid win;
387 anything any;
388 menu_item *selected = 0;
390 /* Used to avoid "Is this ok?" if player has already specified all
391 * four facets of role.
392 * Note that rigid_role_checks might force any unspecified facets to
393 * have a specific value, but that will still require confirmation;
394 * player can specify the forced ones if avoiding that is demanded.
396 picksomething = (ROLE == ROLE_NONE || RACE == ROLE_NONE
397 || GEND == ROLE_NONE || ALGN == ROLE_NONE);
398 /* Used for '-@';
399 * choose randomly without asking for all unspecified facets.
401 if (flags.randomall && picksomething) {
402 if (ROLE == ROLE_NONE)
403 ROLE = ROLE_RANDOM;
404 if (RACE == ROLE_NONE)
405 RACE = ROLE_RANDOM;
406 if (GEND == ROLE_NONE)
407 GEND = ROLE_RANDOM;
408 if (ALGN == ROLE_NONE)
409 ALGN = ROLE_RANDOM;
412 /* prevent unnecessary prompting if role forces race (samurai) or gender
413 (valkyrie) or alignment (rogue), or race forces alignment (orc), &c */
414 rigid_role_checks();
416 /* Should we randomly pick for the player? */
417 if (ROLE == ROLE_NONE || RACE == ROLE_NONE || GEND == ROLE_NONE
418 || ALGN == ROLE_NONE) {
419 int echoline;
420 char *prompt = build_plselection_prompt(pbuf, QBUFSZ,
421 ROLE, RACE, GEND, ALGN);
423 /* this prompt string ends in "[ynaq]?":
424 y - game picks role,&c then asks player to confirm;
425 n - player manually chooses via menu selections;
426 a - like 'y', but skips confirmation and starts game;
427 q - quit
429 tty_putstr(BASE_WINDOW, 0, "");
430 echoline = wins[BASE_WINDOW]->cury;
431 tty_putstr(BASE_WINDOW, 0, prompt);
432 do {
433 pick4u = lowc(readchar());
434 if (index(quitchars, pick4u))
435 pick4u = 'y';
436 } while (!index(ynaqchars, pick4u));
437 if ((int) strlen(prompt) + 1 < CO) {
438 /* Echo choice and move back down line */
439 tty_putsym(BASE_WINDOW, (int) strlen(prompt) + 1, echoline,
440 pick4u);
441 tty_putstr(BASE_WINDOW, 0, "");
442 } else
443 /* Otherwise it's hard to tell where to echo, and things are
444 * wrapping a bit messily anyway, so (try to) make sure the next
445 * question shows up well and doesn't get wrapped at the
446 * bottom of the window.
448 tty_clear_nhwindow(BASE_WINDOW);
450 if (pick4u != 'y' && pick4u != 'a' && pick4u != 'n')
451 goto give_up;
454 makepicks:
455 nextpick = RS_ROLE;
456 do {
457 if (nextpick == RS_ROLE) {
458 nextpick = RS_RACE;
459 /* Select a role, if necessary;
460 we'll try to be compatible with pre-selected
461 race/gender/alignment, but may not succeed. */
462 if (ROLE < 0) {
463 /* Process the choice */
464 if (pick4u == 'y' || pick4u == 'a' || ROLE == ROLE_RANDOM) {
465 /* Pick a random role */
466 k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
467 if (k < 0) {
468 tty_putstr(BASE_WINDOW, 0, "Incompatible role!");
469 k = randrole();
471 } else {
472 /* Prompt for a role */
473 tty_clear_nhwindow(BASE_WINDOW);
474 role_selection_prolog(RS_ROLE, BASE_WINDOW);
475 win = create_nhwindow(NHW_MENU);
476 start_menu(win);
477 /* populate the menu with role choices */
478 setup_rolemenu(win, TRUE, RACE, GEND, ALGN);
479 /* add miscellaneous menu entries */
480 role_menu_extra(ROLE_RANDOM, win, TRUE);
481 any = zeroany; /* separator, not a choice */
482 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
483 MENU_UNSELECTED);
484 role_menu_extra(RS_RACE, win, FALSE);
485 role_menu_extra(RS_GENDER, win, FALSE);
486 role_menu_extra(RS_ALGNMNT, win, FALSE);
487 if (gotrolefilter())
488 role_menu_extra(RS_filter, win, FALSE);
489 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
490 Strcpy(pbuf, "Pick a role or profession");
491 end_menu(win, pbuf);
492 n = select_menu(win, PICK_ONE, &selected);
494 * PICK_ONE with preselected choice behaves strangely:
495 * n == -1 -- <escape>, so use quit choice;
496 * n == 0 -- explicitly chose preselected entry,
497 * toggling it off, so use it;
498 * n == 1 -- implicitly chose preselected entry
499 * with <space> or <return>;
500 * n == 2 -- explicitly chose a different entry, so
501 * both it and preselected one are in list.
503 if (n > 0) {
504 choice = selected[0].item.a_int;
505 if (n > 1 && choice == ROLE_RANDOM)
506 choice = selected[1].item.a_int;
507 } else
508 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
509 if (selected)
510 free((genericptr_t) selected), selected = 0;
511 destroy_nhwindow(win);
513 if (choice == ROLE_NONE) {
514 goto give_up; /* Selected quit */
515 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
516 ALGN = k = ROLE_NONE;
517 nextpick = RS_ALGNMNT;
518 } else if (choice == RS_menu_arg(RS_GENDER)) {
519 GEND = k = ROLE_NONE;
520 nextpick = RS_GENDER;
521 } else if (choice == RS_menu_arg(RS_RACE)) {
522 RACE = k = ROLE_NONE;
523 nextpick = RS_RACE;
524 } else if (choice == RS_menu_arg(RS_filter)) {
525 ROLE = k = ROLE_NONE;
526 (void) reset_role_filtering();
527 nextpick = RS_ROLE;
528 } else if (choice == ROLE_RANDOM) {
529 k = pick_role(RACE, GEND, ALGN, PICK_RANDOM);
530 if (k < 0)
531 k = randrole();
532 } else {
533 k = choice - 1;
536 ROLE = k;
537 } /* needed role */
538 } /* picking role */
540 if (nextpick == RS_RACE) {
541 nextpick = (ROLE < 0) ? RS_ROLE : RS_GENDER;
542 /* Select a race, if necessary;
543 force compatibility with role, try for compatibility
544 with pre-selected gender/alignment. */
545 if (RACE < 0 || !validrace(ROLE, RACE)) {
546 /* no race yet, or pre-selected race not valid */
547 if (pick4u == 'y' || pick4u == 'a' || RACE == ROLE_RANDOM) {
548 k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM);
549 if (k < 0) {
550 tty_putstr(BASE_WINDOW, 0, "Incompatible race!");
551 k = randrace(ROLE);
553 } else { /* pick4u == 'n' */
554 /* Count the number of valid races */
555 n = 0; /* number valid */
556 k = 0; /* valid race */
557 for (i = 0; races[i].noun; i++)
558 if (ok_race(ROLE, i, GEND, ALGN)) {
559 n++;
560 k = i;
562 if (n == 0) {
563 for (i = 0; races[i].noun; i++)
564 if (validrace(ROLE, i)) {
565 n++;
566 k = i;
569 /* Permit the user to pick, if there is more than one */
570 if (n > 1) {
571 tty_clear_nhwindow(BASE_WINDOW);
572 role_selection_prolog(RS_RACE, BASE_WINDOW);
573 win = create_nhwindow(NHW_MENU);
574 start_menu(win);
575 any = zeroany; /* zero out all bits */
576 /* populate the menu with role choices */
577 setup_racemenu(win, TRUE, ROLE, GEND, ALGN);
578 /* add miscellaneous menu entries */
579 role_menu_extra(ROLE_RANDOM, win, TRUE);
580 any.a_int = 0; /* separator, not a choice */
581 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
582 MENU_UNSELECTED);
583 role_menu_extra(RS_ROLE, win, FALSE);
584 role_menu_extra(RS_GENDER, win, FALSE);
585 role_menu_extra(RS_ALGNMNT, win, FALSE);
586 if (gotrolefilter())
587 role_menu_extra(RS_filter, win, FALSE);
588 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
589 Strcpy(pbuf, "Pick a race or species");
590 end_menu(win, pbuf);
591 n = select_menu(win, PICK_ONE, &selected);
592 if (n > 0) {
593 choice = selected[0].item.a_int;
594 if (n > 1 && choice == ROLE_RANDOM)
595 choice = selected[1].item.a_int;
596 } else
597 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
598 if (selected)
599 free((genericptr_t) selected), selected = 0;
600 destroy_nhwindow(win);
602 if (choice == ROLE_NONE) {
603 goto give_up; /* Selected quit */
604 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
605 ALGN = k = ROLE_NONE;
606 nextpick = RS_ALGNMNT;
607 } else if (choice == RS_menu_arg(RS_GENDER)) {
608 GEND = k = ROLE_NONE;
609 nextpick = RS_GENDER;
610 } else if (choice == RS_menu_arg(RS_ROLE)) {
611 ROLE = k = ROLE_NONE;
612 nextpick = RS_ROLE;
613 } else if (choice == RS_menu_arg(RS_filter)) {
614 RACE = k = ROLE_NONE;
615 if (reset_role_filtering())
616 nextpick = RS_ROLE;
617 else
618 nextpick = RS_RACE;
619 } else if (choice == ROLE_RANDOM) {
620 k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM);
621 if (k < 0)
622 k = randrace(ROLE);
623 } else {
624 k = choice - 1;
628 RACE = k;
629 } /* needed race */
630 } /* picking race */
632 if (nextpick == RS_GENDER) {
633 nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE
634 : RS_ALGNMNT;
635 /* Select a gender, if necessary;
636 force compatibility with role/race, try for compatibility
637 with pre-selected alignment. */
638 if (GEND < 0 || !validgend(ROLE, RACE, GEND)) {
639 /* no gender yet, or pre-selected gender not valid */
640 if (pick4u == 'y' || pick4u == 'a' || GEND == ROLE_RANDOM) {
641 k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM);
642 if (k < 0) {
643 tty_putstr(BASE_WINDOW, 0, "Incompatible gender!");
644 k = randgend(ROLE, RACE);
646 } else { /* pick4u == 'n' */
647 /* Count the number of valid genders */
648 n = 0; /* number valid */
649 k = 0; /* valid gender */
650 for (i = 0; i < ROLE_GENDERS; i++)
651 if (ok_gend(ROLE, RACE, i, ALGN)) {
652 n++;
653 k = i;
655 if (n == 0) {
656 for (i = 0; i < ROLE_GENDERS; i++)
657 if (validgend(ROLE, RACE, i)) {
658 n++;
659 k = i;
662 /* Permit the user to pick, if there is more than one */
663 if (n > 1) {
664 tty_clear_nhwindow(BASE_WINDOW);
665 role_selection_prolog(RS_GENDER, BASE_WINDOW);
666 win = create_nhwindow(NHW_MENU);
667 start_menu(win);
668 any = zeroany; /* zero out all bits */
669 /* populate the menu with gender choices */
670 setup_gendmenu(win, TRUE, ROLE, RACE, ALGN);
671 /* add miscellaneous menu entries */
672 role_menu_extra(ROLE_RANDOM, win, TRUE);
673 any.a_int = 0; /* separator, not a choice */
674 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
675 MENU_UNSELECTED);
676 role_menu_extra(RS_ROLE, win, FALSE);
677 role_menu_extra(RS_RACE, win, FALSE);
678 role_menu_extra(RS_ALGNMNT, win, FALSE);
679 if (gotrolefilter())
680 role_menu_extra(RS_filter, win, FALSE);
681 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
682 Strcpy(pbuf, "Pick a gender or sex");
683 end_menu(win, pbuf);
684 n = select_menu(win, PICK_ONE, &selected);
685 if (n > 0) {
686 choice = selected[0].item.a_int;
687 if (n > 1 && choice == ROLE_RANDOM)
688 choice = selected[1].item.a_int;
689 } else
690 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
691 if (selected)
692 free((genericptr_t) selected), selected = 0;
693 destroy_nhwindow(win);
695 if (choice == ROLE_NONE) {
696 goto give_up; /* Selected quit */
697 } else if (choice == RS_menu_arg(RS_ALGNMNT)) {
698 ALGN = k = ROLE_NONE;
699 nextpick = RS_ALGNMNT;
700 } else if (choice == RS_menu_arg(RS_RACE)) {
701 RACE = k = ROLE_NONE;
702 nextpick = RS_RACE;
703 } else if (choice == RS_menu_arg(RS_ROLE)) {
704 ROLE = k = ROLE_NONE;
705 nextpick = RS_ROLE;
706 } else if (choice == RS_menu_arg(RS_filter)) {
707 GEND = k = ROLE_NONE;
708 if (reset_role_filtering())
709 nextpick = RS_ROLE;
710 else
711 nextpick = RS_GENDER;
712 } else if (choice == ROLE_RANDOM) {
713 k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM);
714 if (k < 0)
715 k = randgend(ROLE, RACE);
716 } else {
717 k = choice - 1;
721 GEND = k;
722 } /* needed gender */
723 } /* picking gender */
725 if (nextpick == RS_ALGNMNT) {
726 nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE : RS_GENDER;
727 /* Select an alignment, if necessary;
728 force compatibility with role/race/gender. */
729 if (ALGN < 0 || !validalign(ROLE, RACE, ALGN)) {
730 /* no alignment yet, or pre-selected alignment not valid */
731 if (pick4u == 'y' || pick4u == 'a' || ALGN == ROLE_RANDOM) {
732 k = pick_align(ROLE, RACE, GEND, PICK_RANDOM);
733 if (k < 0) {
734 tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!");
735 k = randalign(ROLE, RACE);
737 } else { /* pick4u == 'n' */
738 /* Count the number of valid alignments */
739 n = 0; /* number valid */
740 k = 0; /* valid alignment */
741 for (i = 0; i < ROLE_ALIGNS; i++)
742 if (ok_align(ROLE, RACE, GEND, i)) {
743 n++;
744 k = i;
746 if (n == 0) {
747 for (i = 0; i < ROLE_ALIGNS; i++)
748 if (validalign(ROLE, RACE, i)) {
749 n++;
750 k = i;
753 /* Permit the user to pick, if there is more than one */
754 if (n > 1) {
755 tty_clear_nhwindow(BASE_WINDOW);
756 role_selection_prolog(RS_ALGNMNT, BASE_WINDOW);
757 win = create_nhwindow(NHW_MENU);
758 start_menu(win);
759 any = zeroany; /* zero out all bits */
760 setup_algnmenu(win, TRUE, ROLE, RACE, GEND);
761 role_menu_extra(ROLE_RANDOM, win, TRUE);
762 any.a_int = 0; /* separator, not a choice */
763 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
764 MENU_UNSELECTED);
765 role_menu_extra(RS_ROLE, win, FALSE);
766 role_menu_extra(RS_RACE, win, FALSE);
767 role_menu_extra(RS_GENDER, win, FALSE);
768 if (gotrolefilter())
769 role_menu_extra(RS_filter, win, FALSE);
770 role_menu_extra(ROLE_NONE, win, FALSE); /* quit */
771 Strcpy(pbuf, "Pick an alignment or creed");
772 end_menu(win, pbuf);
773 n = select_menu(win, PICK_ONE, &selected);
774 if (n > 0) {
775 choice = selected[0].item.a_int;
776 if (n > 1 && choice == ROLE_RANDOM)
777 choice = selected[1].item.a_int;
778 } else
779 choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE;
780 if (selected)
781 free((genericptr_t) selected), selected = 0;
782 destroy_nhwindow(win);
784 if (choice == ROLE_NONE) {
785 goto give_up; /* Selected quit */
786 } else if (choice == RS_menu_arg(RS_GENDER)) {
787 GEND = k = ROLE_NONE;
788 nextpick = RS_GENDER;
789 } else if (choice == RS_menu_arg(RS_RACE)) {
790 RACE = k = ROLE_NONE;
791 nextpick = RS_RACE;
792 } else if (choice == RS_menu_arg(RS_ROLE)) {
793 ROLE = k = ROLE_NONE;
794 nextpick = RS_ROLE;
795 } else if (choice == RS_menu_arg(RS_filter)) {
796 ALGN = k = ROLE_NONE;
797 if (reset_role_filtering())
798 nextpick = RS_ROLE;
799 else
800 nextpick = RS_ALGNMNT;
801 } else if (choice == ROLE_RANDOM) {
802 k = pick_align(ROLE, RACE, GEND, PICK_RANDOM);
803 if (k < 0)
804 k = randalign(ROLE, RACE);
805 } else {
806 k = choice - 1;
810 ALGN = k;
811 } /* needed alignment */
812 } /* picking alignment */
814 } while (ROLE < 0 || RACE < 0 || GEND < 0 || ALGN < 0);
817 * Role, race, &c have now been determined;
818 * ask for confirmation and maybe go back to choose all over again.
820 * Uses ynaq for familiarity, although 'a' is usually a
821 * superset of 'y' but here is an alternate form of 'n'.
822 * Menu layout:
823 * title: Is this ok? [ynaq]
824 * blank:
825 * text: $name, $alignment $gender $race $role
826 * blank:
827 * menu: y + yes; play
828 * n - no; pick again
829 * maybe: a - no; rename hero
830 * q - quit
831 * (end)
833 getconfirmation = (picksomething && pick4u != 'a' && !flags.randomall);
834 while (getconfirmation) {
835 tty_clear_nhwindow(BASE_WINDOW);
836 role_selection_prolog(ROLE_NONE, BASE_WINDOW);
837 win = create_nhwindow(NHW_MENU);
838 start_menu(win);
839 any = zeroany; /* zero out all bits */
840 any.a_int = 0;
841 if (!roles[ROLE].name.f
842 && (roles[ROLE].allow & ROLE_GENDMASK)
843 == (ROLE_MALE | ROLE_FEMALE))
844 Sprintf(plbuf, " %s", genders[GEND].adj);
845 else
846 *plbuf = '\0'; /* omit redundant gender */
847 Sprintf(pbuf, "%s, %s%s %s %s", plname, aligns[ALGN].adj, plbuf,
848 races[RACE].adj,
849 (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f
850 : roles[ROLE].name.m);
851 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, pbuf,
852 MENU_UNSELECTED);
853 /* blank separator */
854 any.a_int = 0;
855 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
856 /* [ynaq] menu choices */
857 any.a_int = 1;
858 add_menu(win, NO_GLYPH, &any, 'y', 0, ATR_NONE, "Yes; start game",
859 MENU_SELECTED);
860 any.a_int = 2;
861 add_menu(win, NO_GLYPH, &any, 'n', 0, ATR_NONE,
862 "No; choose role again", MENU_UNSELECTED);
863 if (iflags.renameallowed) {
864 any.a_int = 3;
865 add_menu(win, NO_GLYPH, &any, 'a', 0, ATR_NONE,
866 "Not yet; choose another name", MENU_UNSELECTED);
868 any.a_int = -1;
869 add_menu(win, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
870 MENU_UNSELECTED);
871 Sprintf(pbuf, "Is this ok? [yn%sq]", iflags.renameallowed ? "a" : "");
872 end_menu(win, pbuf);
873 n = select_menu(win, PICK_ONE, &selected);
874 /* [pick-one menus with a preselected entry behave oddly...] */
875 choice = (n > 0) ? selected[n - 1].item.a_int : (n == 0) ? 1 : -1;
876 if (selected)
877 free((genericptr_t) selected), selected = 0;
878 destroy_nhwindow(win);
880 switch (choice) {
881 default: /* 'q' or ESC */
882 goto give_up; /* quit */
883 break;
884 case 3: { /* 'a' */
886 * TODO: what, if anything, should be done if the name is
887 * changed to or from "wizard" after port-specific startup
888 * code has set flags.debug based on the original name?
890 int saveROLE, saveRACE, saveGEND, saveALGN;
892 iflags.renameinprogress = TRUE;
893 /* plnamesuffix() can change any or all of ROLE, RACE,
894 GEND, ALGN; we'll override that and honor only the name */
895 saveROLE = ROLE, saveRACE = RACE, saveGEND = GEND,
896 saveALGN = ALGN;
897 *plname = '\0';
898 plnamesuffix(); /* calls askname() when plname[] is empty */
899 ROLE = saveROLE, RACE = saveRACE, GEND = saveGEND,
900 ALGN = saveALGN;
901 break; /* getconfirmation is still True */
903 case 2: /* 'n' */
904 /* start fresh, but bypass "shall I pick everything for you?"
905 step; any partial role selection via config file, command
906 line, or name suffix is discarded this time */
907 pick4u = 'n';
908 ROLE = RACE = GEND = ALGN = ROLE_NONE;
909 goto makepicks;
910 break;
911 case 1: /* 'y' or Space or Return/Enter */
912 /* success; drop out through end of function */
913 getconfirmation = FALSE;
914 break;
918 /* Success! */
919 tty_display_nhwindow(BASE_WINDOW, FALSE);
920 return;
922 give_up:
923 /* Quit */
924 if (selected)
925 free((genericptr_t) selected); /* [obsolete] */
926 bail((char *) 0);
927 /*NOTREACHED*/
928 return;
931 STATIC_OVL boolean
932 reset_role_filtering()
934 winid win;
935 anything any;
936 int i, n;
937 menu_item *selected = 0;
939 win = create_nhwindow(NHW_MENU);
940 start_menu(win);
941 any = zeroany;
943 /* no extra blank line preceding this entry; end_menu supplies one */
944 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
945 "Unacceptable roles", MENU_UNSELECTED);
946 setup_rolemenu(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 "Unacceptable races", MENU_UNSELECTED);
951 setup_racemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
953 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
954 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
955 "Unacceptable genders", MENU_UNSELECTED);
956 setup_gendmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
958 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
959 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
960 "Uncceptable alignments", MENU_UNSELECTED);
961 setup_algnmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE);
963 end_menu(win, "Pick all that apply");
964 n = select_menu(win, PICK_ANY, &selected);
966 if (n > 0) {
967 clearrolefilter();
968 for (i = 0; i < n; i++)
969 setrolefilter(selected[i].item.a_string);
971 ROLE = RACE = GEND = ALGN = ROLE_NONE;
973 if (selected)
974 free((genericptr_t) selected), selected = 0;
975 destroy_nhwindow(win);
976 return (n > 0) ? TRUE : FALSE;
979 #undef ROLE
980 #undef RACE
981 #undef GEND
982 #undef ALGN
984 /* add entries a-Archeologist, b-Barbarian, &c to menu being built in 'win' */
985 STATIC_OVL void
986 setup_rolemenu(win, filtering, race, gend, algn)
987 winid win;
988 boolean filtering; /* True => exclude filtered roles; False => filter reset */
989 int race, gend, algn; /* all ROLE_NONE for !filtering case */
991 anything any;
992 int i;
993 boolean role_ok;
994 char thisch, lastch = '\0', rolenamebuf[50];
996 any = zeroany; /* zero out all bits */
997 for (i = 0; roles[i].name.m; i++) {
998 role_ok = ok_role(i, race, gend, algn);
999 if (filtering && !role_ok)
1000 continue;
1001 if (filtering)
1002 any.a_int = i + 1;
1003 else
1004 any.a_string = roles[i].name.m;
1005 thisch = lowc(*roles[i].name.m);
1006 if (thisch == lastch)
1007 thisch = highc(thisch);
1008 Strcpy(rolenamebuf, roles[i].name.m);
1009 if (roles[i].name.f) {
1010 /* role has distinct name for female (C,P) */
1011 if (gend == 1) {
1012 /* female already chosen; replace male name */
1013 Strcpy(rolenamebuf, roles[i].name.f);
1014 } else if (gend < 0) {
1015 /* not chosen yet; append slash+female name */
1016 Strcat(rolenamebuf, "/");
1017 Strcat(rolenamebuf, roles[i].name.f);
1020 /* !filtering implies reset_role_filtering() where we want to
1021 mark this role as preseleted if current filter excludes it */
1022 add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE, an(rolenamebuf),
1023 (!filtering && !role_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1024 lastch = thisch;
1028 STATIC_OVL void
1029 setup_racemenu(win, filtering, role, gend, algn)
1030 winid win;
1031 boolean filtering;
1032 int role, gend, algn;
1034 anything any;
1035 boolean race_ok;
1036 int i;
1037 char this_ch;
1039 any = zeroany;
1040 for (i = 0; races[i].noun; i++) {
1041 race_ok = ok_race(role, i, gend, algn);
1042 if (filtering && !race_ok)
1043 continue;
1044 if (filtering)
1045 any.a_int = i + 1;
1046 else
1047 any.a_string = races[i].noun;
1048 this_ch = *races[i].noun;
1049 /* filtering: picking race, so choose by first letter, with
1050 capital letter as unseen accelerator;
1051 !filtering: resetting filter rather than picking, choose by
1052 capital letter since lowercase role letters will be present */
1053 add_menu(win, NO_GLYPH, &any,
1054 filtering ? this_ch : highc(this_ch),
1055 filtering ? highc(this_ch) : 0,
1056 ATR_NONE, races[i].noun,
1057 (!filtering && !race_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1061 STATIC_DCL void
1062 setup_gendmenu(win, filtering, role, race, algn)
1063 winid win;
1064 boolean filtering;
1065 int role, race, algn;
1067 anything any;
1068 boolean gend_ok;
1069 int i;
1070 char this_ch;
1072 any = zeroany;
1073 for (i = 0; i < ROLE_GENDERS; i++) {
1074 gend_ok = ok_gend(role, race, i, algn);
1075 if (filtering && !gend_ok)
1076 continue;
1077 if (filtering)
1078 any.a_int = i + 1;
1079 else
1080 any.a_string = genders[i].adj;
1081 this_ch = *genders[i].adj;
1082 /* (see setup_racemenu for explanation of selector letters
1083 and setup_rolemenu for preselection) */
1084 add_menu(win, NO_GLYPH, &any,
1085 filtering ? this_ch : highc(this_ch),
1086 filtering ? highc(this_ch) : 0,
1087 ATR_NONE, genders[i].adj,
1088 (!filtering && !gend_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1092 STATIC_DCL void
1093 setup_algnmenu(win, filtering, role, race, gend)
1094 winid win;
1095 boolean filtering;
1096 int role, race, gend;
1098 anything any;
1099 boolean algn_ok;
1100 int i;
1101 char this_ch;
1103 any = zeroany;
1104 for (i = 0; i < ROLE_ALIGNS; i++) {
1105 algn_ok = ok_align(role, race, gend, i);
1106 if (filtering && !algn_ok)
1107 continue;
1108 if (filtering)
1109 any.a_int = i + 1;
1110 else
1111 any.a_string = aligns[i].adj;
1112 this_ch = *aligns[i].adj;
1113 /* (see setup_racemenu for explanation of selector letters
1114 and setup_rolemenu for preselection) */
1115 add_menu(win, NO_GLYPH, &any,
1116 filtering ? this_ch : highc(this_ch),
1117 filtering ? highc(this_ch) : 0,
1118 ATR_NONE, aligns[i].adj,
1119 (!filtering && !algn_ok) ? MENU_SELECTED : MENU_UNSELECTED);
1124 * plname is filled either by an option (-u Player or -uPlayer) or
1125 * explicitly (by being the wizard) or by askname.
1126 * It may still contain a suffix denoting the role, etc.
1127 * Always called after init_nhwindows() and before display_gamewindows().
1129 void
1130 tty_askname()
1132 static const char who_are_you[] = "Who are you? ";
1133 register int c, ct, tryct = 0;
1135 #ifdef SELECTSAVED
1136 if (iflags.wc2_selectsaved && !iflags.renameinprogress)
1137 switch (restore_menu(BASE_WINDOW)) {
1138 case -1:
1139 bail("Until next time then..."); /* quit */
1140 /*NOTREACHED*/
1141 case 0:
1142 break; /* no game chosen; start new game */
1143 case 1:
1144 return; /* plname[] has been set */
1146 #endif /* SELECTSAVED */
1148 tty_putstr(BASE_WINDOW, 0, "");
1149 do {
1150 if (++tryct > 1) {
1151 if (tryct > 10)
1152 bail("Giving up after 10 tries.\n");
1153 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury - 1);
1154 tty_putstr(BASE_WINDOW, 0, "Enter a name for your character...");
1155 /* erase previous prompt (in case of ESC after partial response)
1157 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury), cl_end();
1159 tty_putstr(BASE_WINDOW, 0, who_are_you);
1160 tty_curs(BASE_WINDOW, (int) (sizeof who_are_you),
1161 wins[BASE_WINDOW]->cury - 1);
1162 ct = 0;
1163 while ((c = tty_nhgetch()) != '\n') {
1164 if (c == EOF)
1165 c = '\033';
1166 if (c == '\033') {
1167 ct = 0;
1168 break;
1169 } /* continue outer loop */
1170 #if defined(WIN32CON)
1171 if (c == '\003')
1172 bail("^C abort.\n");
1173 #endif
1174 /* some people get confused when their erase char is not ^H */
1175 if (c == '\b' || c == '\177') {
1176 if (ct) {
1177 ct--;
1178 #ifdef WIN32CON
1179 ttyDisplay->curx--;
1180 #endif
1181 #if defined(MICRO) || defined(WIN32CON)
1182 #if defined(WIN32CON) || defined(MSDOS)
1183 backsp(); /* \b is visible on NT */
1184 (void) putchar(' ');
1185 backsp();
1186 #else
1187 msmsg("\b \b");
1188 #endif
1189 #else
1190 (void) putchar('\b');
1191 (void) putchar(' ');
1192 (void) putchar('\b');
1193 #endif
1195 continue;
1197 #if defined(UNIX) || defined(VMS)
1198 if (c != '-' && c != '@')
1199 if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') &&
1200 /* reject leading digit but allow digits elsewhere
1201 (avoids ambiguity when character name gets
1202 appended to uid to construct save file name) */
1203 !(c >= '0' && c <= '9' && ct > 0))
1204 c = '_';
1205 #endif
1206 if (ct < (int) (sizeof plname) - 1) {
1207 #if defined(MICRO)
1208 #if defined(MSDOS)
1209 if (iflags.grmode) {
1210 (void) putchar(c);
1211 } else
1212 #endif
1213 msmsg("%c", c);
1214 #else
1215 (void) putchar(c);
1216 #endif
1217 plname[ct++] = c;
1218 #ifdef WIN32CON
1219 ttyDisplay->curx++;
1220 #endif
1223 plname[ct] = 0;
1224 } while (ct == 0);
1226 /* move to next line to simulate echo of user's <return> */
1227 tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury + 1);
1229 /* since we let user pick an arbitrary name now, he/she can pick
1230 another one during role selection */
1231 iflags.renameallowed = TRUE;
1234 void
1235 tty_get_nh_event()
1237 return;
1240 #if !defined(MICRO) && !defined(WIN32CON)
1241 STATIC_OVL void
1242 getret()
1244 xputs("\n");
1245 if (flags.standout)
1246 standoutbeg();
1247 xputs("Hit ");
1248 xputs(iflags.cbreak ? "space" : "return");
1249 xputs(" to continue: ");
1250 if (flags.standout)
1251 standoutend();
1252 xwaitforspace(" ");
1254 #endif
1256 void
1257 tty_suspend_nhwindows(str)
1258 const char *str;
1260 settty(str); /* calls end_screen, perhaps raw_print */
1261 if (!str)
1262 tty_raw_print(""); /* calls fflush(stdout) */
1265 void
1266 tty_resume_nhwindows()
1268 gettty();
1269 setftty(); /* calls start_screen */
1270 docrt();
1273 void
1274 tty_exit_nhwindows(str)
1275 const char *str;
1277 winid i;
1279 tty_suspend_nhwindows(str);
1281 * Disable windows to avoid calls to window routines.
1283 free_pickinv_cache(); /* reset its state as well as tear down window */
1284 for (i = 0; i < MAXWIN; i++) {
1285 if (i == BASE_WINDOW)
1286 continue; /* handle wins[BASE_WINDOW] last */
1287 if (wins[i]) {
1288 #ifdef FREE_ALL_MEMORY
1289 free_window_info(wins[i], TRUE);
1290 free((genericptr_t) wins[i]);
1291 #endif
1292 wins[i] = (struct WinDesc *) 0;
1295 WIN_MAP = WIN_MESSAGE = WIN_INVEN = WIN_ERR; /* these are all gone now */
1296 #ifndef STATUS_VIA_WINDOWPORT
1297 WIN_STATUS = WIN_ERR;
1298 #endif
1299 #ifdef FREE_ALL_MEMORY
1300 if (BASE_WINDOW != WIN_ERR && wins[BASE_WINDOW]) {
1301 free_window_info(wins[BASE_WINDOW], TRUE);
1302 free((genericptr_t) wins[BASE_WINDOW]);
1303 wins[BASE_WINDOW] = (struct WinDesc *) 0;
1304 BASE_WINDOW = WIN_ERR;
1306 free((genericptr_t) ttyDisplay);
1307 ttyDisplay = (struct DisplayDesc *) 0;
1308 #endif
1310 #ifndef NO_TERMS /*(until this gets added to the window interface)*/
1311 tty_shutdown(); /* cleanup termcap/terminfo/whatever */
1312 #endif
1313 iflags.window_inited = 0;
1316 winid
1317 tty_create_nhwindow(type)
1318 int type;
1320 struct WinDesc *newwin;
1321 int i;
1322 int newid;
1324 if (maxwin == MAXWIN)
1325 return WIN_ERR;
1327 newwin = (struct WinDesc *) alloc(sizeof(struct WinDesc));
1328 newwin->type = type;
1329 newwin->flags = 0;
1330 newwin->active = FALSE;
1331 newwin->curx = newwin->cury = 0;
1332 newwin->morestr = 0;
1333 newwin->mlist = (tty_menu_item *) 0;
1334 newwin->plist = (tty_menu_item **) 0;
1335 newwin->npages = newwin->plist_size = newwin->nitems = newwin->how = 0;
1336 switch (type) {
1337 case NHW_BASE:
1338 /* base window, used for absolute movement on the screen */
1339 newwin->offx = newwin->offy = 0;
1340 newwin->rows = ttyDisplay->rows;
1341 newwin->cols = ttyDisplay->cols;
1342 newwin->maxrow = newwin->maxcol = 0;
1343 break;
1344 case NHW_MESSAGE:
1345 /* message window, 1 line long, very wide, top of screen */
1346 newwin->offx = newwin->offy = 0;
1347 /* sanity check */
1348 if (iflags.msg_history < 20)
1349 iflags.msg_history = 20;
1350 else if (iflags.msg_history > 60)
1351 iflags.msg_history = 60;
1352 newwin->maxrow = newwin->rows = iflags.msg_history;
1353 newwin->maxcol = newwin->cols = 0;
1354 break;
1355 case NHW_STATUS:
1356 /* status window, 2 lines long, full width, bottom of screen */
1357 newwin->offx = 0;
1358 #if defined(USE_TILES) && defined(MSDOS)
1359 if (iflags.grmode) {
1360 newwin->offy = ttyDisplay->rows - 2;
1361 } else
1362 #endif
1363 newwin->offy = min((int) ttyDisplay->rows - 2, ROWNO + 1);
1364 newwin->rows = newwin->maxrow = 2;
1365 newwin->cols = newwin->maxcol = ttyDisplay->cols;
1366 break;
1367 case NHW_MAP:
1368 /* map window, ROWNO lines long, full width, below message window */
1369 newwin->offx = 0;
1370 newwin->offy = 1;
1371 newwin->rows = ROWNO;
1372 newwin->cols = COLNO;
1373 newwin->maxrow = 0; /* no buffering done -- let gbuf do it */
1374 newwin->maxcol = 0;
1375 break;
1376 case NHW_MENU:
1377 case NHW_TEXT:
1378 /* inventory/menu window, variable length, full width, top of screen
1380 /* help window, the same, different semantics for display, etc */
1381 newwin->offx = newwin->offy = 0;
1382 newwin->rows = 0;
1383 newwin->cols = ttyDisplay->cols;
1384 newwin->maxrow = newwin->maxcol = 0;
1385 break;
1386 default:
1387 panic("Tried to create window type %d\n", (int) type);
1388 return WIN_ERR;
1391 for (newid = 0; newid < MAXWIN; newid++) {
1392 if (wins[newid] == 0) {
1393 wins[newid] = newwin;
1394 break;
1397 if (newid == MAXWIN) {
1398 panic("No window slots!");
1399 return WIN_ERR;
1402 if (newwin->maxrow) {
1403 newwin->data =
1404 (char **) alloc(sizeof(char *) * (unsigned) newwin->maxrow);
1405 newwin->datlen =
1406 (short *) alloc(sizeof(short) * (unsigned) newwin->maxrow);
1407 if (newwin->maxcol) {
1408 /* WIN_STATUS */
1409 for (i = 0; i < newwin->maxrow; i++) {
1410 newwin->data[i] = (char *) alloc((unsigned) newwin->maxcol);
1411 newwin->datlen[i] = (short) newwin->maxcol;
1413 } else {
1414 for (i = 0; i < newwin->maxrow; i++) {
1415 newwin->data[i] = (char *) 0;
1416 newwin->datlen[i] = 0;
1419 if (newwin->type == NHW_MESSAGE)
1420 newwin->maxrow = 0;
1421 } else {
1422 newwin->data = (char **) 0;
1423 newwin->datlen = (short *) 0;
1426 return newid;
1429 STATIC_OVL void
1430 erase_menu_or_text(window, cw, clear)
1431 winid window;
1432 struct WinDesc *cw;
1433 boolean clear;
1435 if (cw->offx == 0)
1436 if (cw->offy) {
1437 tty_curs(window, 1, 0);
1438 cl_eos();
1439 } else if (clear)
1440 clear_screen();
1441 else
1442 docrt();
1443 else
1444 docorner((int) cw->offx, cw->maxrow + 1);
1447 STATIC_OVL void
1448 free_window_info(cw, free_data)
1449 struct WinDesc *cw;
1450 boolean free_data;
1452 int i;
1454 if (cw->data) {
1455 if (cw == wins[WIN_MESSAGE] && cw->rows > cw->maxrow)
1456 cw->maxrow = cw->rows; /* topl data */
1457 for (i = 0; i < cw->maxrow; i++)
1458 if (cw->data[i]) {
1459 free((genericptr_t) cw->data[i]);
1460 cw->data[i] = (char *) 0;
1461 if (cw->datlen)
1462 cw->datlen[i] = 0;
1464 if (free_data) {
1465 free((genericptr_t) cw->data);
1466 cw->data = (char **) 0;
1467 if (cw->datlen)
1468 free((genericptr_t) cw->datlen);
1469 cw->datlen = (short *) 0;
1470 cw->rows = 0;
1473 cw->maxrow = cw->maxcol = 0;
1474 if (cw->mlist) {
1475 tty_menu_item *temp;
1477 while ((temp = cw->mlist) != 0) {
1478 cw->mlist = cw->mlist->next;
1479 if (temp->str)
1480 free((genericptr_t) temp->str);
1481 free((genericptr_t) temp);
1484 if (cw->plist) {
1485 free((genericptr_t) cw->plist);
1486 cw->plist = 0;
1488 cw->plist_size = cw->npages = cw->nitems = cw->how = 0;
1489 if (cw->morestr) {
1490 free((genericptr_t) cw->morestr);
1491 cw->morestr = 0;
1495 void
1496 tty_clear_nhwindow(window)
1497 winid window;
1499 register struct WinDesc *cw = 0;
1501 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
1502 panic(winpanicstr, window);
1503 ttyDisplay->lastwin = window;
1505 print_vt_code2(AVTC_SELECT_WINDOW, window);
1507 switch (cw->type) {
1508 case NHW_MESSAGE:
1509 if (ttyDisplay->toplin) {
1510 home();
1511 cl_end();
1512 if (cw->cury)
1513 docorner(1, cw->cury + 1);
1514 ttyDisplay->toplin = 0;
1516 break;
1517 case NHW_STATUS:
1518 tty_curs(window, 1, 0);
1519 cl_end();
1520 tty_curs(window, 1, 1);
1521 cl_end();
1522 break;
1523 case NHW_MAP:
1524 /* cheap -- clear the whole thing and tell nethack to redraw botl */
1525 context.botlx = 1;
1526 /* fall into ... */
1527 case NHW_BASE:
1528 clear_screen();
1529 break;
1530 case NHW_MENU:
1531 case NHW_TEXT:
1532 if (cw->active)
1533 erase_menu_or_text(window, cw, TRUE);
1534 free_window_info(cw, FALSE);
1535 break;
1537 cw->curx = cw->cury = 0;
1540 boolean
1541 toggle_menu_curr(window, curr, lineno, in_view, counting, count)
1542 winid window;
1543 tty_menu_item *curr;
1544 int lineno;
1545 boolean in_view, counting;
1546 long count;
1548 if (curr->selected) {
1549 if (counting && count > 0) {
1550 curr->count = count;
1551 if (in_view)
1552 set_item_state(window, lineno, curr);
1553 return TRUE;
1554 } else { /* change state */
1555 curr->selected = FALSE;
1556 curr->count = -1L;
1557 if (in_view)
1558 set_item_state(window, lineno, curr);
1559 return TRUE;
1561 } else { /* !selected */
1562 if (counting && count > 0) {
1563 curr->count = count;
1564 curr->selected = TRUE;
1565 if (in_view)
1566 set_item_state(window, lineno, curr);
1567 return TRUE;
1568 } else if (!counting) {
1569 curr->selected = TRUE;
1570 if (in_view)
1571 set_item_state(window, lineno, curr);
1572 return TRUE;
1574 /* do nothing counting&&count==0 */
1576 return FALSE;
1579 STATIC_OVL void
1580 dmore(cw, s)
1581 register struct WinDesc *cw;
1582 const char *s; /* valid responses */
1584 const char *prompt = cw->morestr ? cw->morestr : defmorestr;
1585 int offset = (cw->type == NHW_TEXT) ? 1 : 2;
1587 tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + offset,
1588 (int) ttyDisplay->cury);
1589 if (flags.standout)
1590 standoutbeg();
1591 xputs(prompt);
1592 ttyDisplay->curx += strlen(prompt);
1593 if (flags.standout)
1594 standoutend();
1596 xwaitforspace(s);
1599 STATIC_OVL void
1600 set_item_state(window, lineno, item)
1601 winid window;
1602 int lineno;
1603 tty_menu_item *item;
1605 char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-';
1607 tty_curs(window, 4, lineno);
1608 term_start_attr(item->attr);
1609 (void) putchar(ch);
1610 ttyDisplay->curx++;
1611 term_end_attr(item->attr);
1614 STATIC_OVL void
1615 set_all_on_page(window, page_start, page_end)
1616 winid window;
1617 tty_menu_item *page_start, *page_end;
1619 tty_menu_item *curr;
1620 int n;
1622 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1623 if (curr->identifier.a_void && !curr->selected) {
1624 curr->selected = TRUE;
1625 set_item_state(window, n, curr);
1629 STATIC_OVL void
1630 unset_all_on_page(window, page_start, page_end)
1631 winid window;
1632 tty_menu_item *page_start, *page_end;
1634 tty_menu_item *curr;
1635 int n;
1637 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1638 if (curr->identifier.a_void && curr->selected) {
1639 curr->selected = FALSE;
1640 curr->count = -1L;
1641 set_item_state(window, n, curr);
1645 STATIC_OVL void
1646 invert_all_on_page(window, page_start, page_end, acc)
1647 winid window;
1648 tty_menu_item *page_start, *page_end;
1649 char acc; /* group accelerator, 0 => all */
1651 tty_menu_item *curr;
1652 int n;
1654 for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next)
1655 if (curr->identifier.a_void && (acc == 0 || curr->gselector == acc)) {
1656 if (curr->selected) {
1657 curr->selected = FALSE;
1658 curr->count = -1L;
1659 } else
1660 curr->selected = TRUE;
1661 set_item_state(window, n, curr);
1666 * Invert all entries that match the give group accelerator (or all if zero).
1668 STATIC_OVL void
1669 invert_all(window, page_start, page_end, acc)
1670 winid window;
1671 tty_menu_item *page_start, *page_end;
1672 char acc; /* group accelerator, 0 => all */
1674 tty_menu_item *curr;
1675 boolean on_curr_page;
1676 struct WinDesc *cw = wins[window];
1678 invert_all_on_page(window, page_start, page_end, acc);
1680 /* invert the rest */
1681 for (on_curr_page = FALSE, curr = cw->mlist; curr; curr = curr->next) {
1682 if (curr == page_start)
1683 on_curr_page = TRUE;
1684 else if (curr == page_end)
1685 on_curr_page = FALSE;
1687 if (!on_curr_page && curr->identifier.a_void
1688 && (acc == 0 || curr->gselector == acc)) {
1689 if (curr->selected) {
1690 curr->selected = FALSE;
1691 curr->count = -1;
1692 } else
1693 curr->selected = TRUE;
1698 /* support menucolor in addition to caller-supplied attribute */
1699 STATIC_OVL void
1700 toggle_menu_attr(on, color, attr)
1701 boolean on;
1702 int color, attr;
1704 if (on) {
1705 term_start_attr(attr);
1706 #ifdef TEXTCOLOR
1707 if (color != NO_COLOR)
1708 term_start_color(color);
1709 #endif
1710 } else {
1711 #ifdef TEXTCOLOR
1712 if (color != NO_COLOR)
1713 term_end_color();
1714 #endif
1715 term_end_attr(attr);
1718 #ifndef TEXTCOLOR
1719 nhUse(color);
1720 #endif
1723 STATIC_OVL void
1724 process_menu_window(window, cw)
1725 winid window;
1726 struct WinDesc *cw;
1728 tty_menu_item *page_start, *page_end, *curr;
1729 long count;
1730 int n, attr_n, curr_page, page_lines, resp_len;
1731 boolean finished, counting, reset_count;
1732 char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ], *msave, *morestr, really_morc;
1733 #define MENU_EXPLICIT_CHOICE 0x7f /* pseudo menu manipulation char */
1735 curr_page = page_lines = 0;
1736 page_start = page_end = 0;
1737 msave = cw->morestr; /* save the morestr */
1738 cw->morestr = morestr = (char *) alloc((unsigned) QBUFSZ);
1739 counting = FALSE;
1740 count = 0L;
1741 reset_count = TRUE;
1742 finished = FALSE;
1744 /* collect group accelerators; for PICK_NONE, they're ignored;
1745 for PICK_ONE, only those which match exactly one entry will be
1746 accepted; for PICK_ANY, those which match any entry are okay */
1747 gacc[0] = '\0';
1748 if (cw->how != PICK_NONE) {
1749 int i, gcnt[128];
1750 #define GSELIDX(c) (c & 127) /* guard against `signed char' */
1752 for (i = 0; i < SIZE(gcnt); i++)
1753 gcnt[i] = 0;
1754 for (n = 0, curr = cw->mlist; curr; curr = curr->next)
1755 if (curr->gselector && curr->gselector != curr->selector) {
1756 ++n;
1757 ++gcnt[GSELIDX(curr->gselector)];
1760 if (n > 0) /* at least one group accelerator found */
1761 for (rp = gacc, curr = cw->mlist; curr; curr = curr->next)
1762 if (curr->gselector && curr->gselector != curr->selector
1763 && !index(gacc, curr->gselector)
1764 && (cw->how == PICK_ANY
1765 || gcnt[GSELIDX(curr->gselector)] == 1)) {
1766 *rp++ = curr->gselector;
1767 *rp = '\0'; /* re-terminate for index() */
1770 resp_len = 0; /* lint suppression */
1772 /* loop until finished */
1773 while (!finished) {
1774 if (reset_count) {
1775 counting = FALSE;
1776 count = 0;
1777 } else
1778 reset_count = TRUE;
1780 if (!page_start) {
1781 /* new page to be displayed */
1782 if (curr_page < 0 || (cw->npages > 0 && curr_page >= cw->npages))
1783 panic("bad menu screen page #%d", curr_page);
1785 /* clear screen */
1786 if (!cw->offx) { /* if not corner, do clearscreen */
1787 if (cw->offy) {
1788 tty_curs(window, 1, 0);
1789 cl_eos();
1790 } else
1791 clear_screen();
1794 rp = resp;
1795 if (cw->npages > 0) {
1796 /* collect accelerators */
1797 page_start = cw->plist[curr_page];
1798 page_end = cw->plist[curr_page + 1];
1799 for (page_lines = 0, curr = page_start; curr != page_end;
1800 page_lines++, curr = curr->next) {
1801 int attr, color = NO_COLOR;
1803 if (curr->selector)
1804 *rp++ = curr->selector;
1806 tty_curs(window, 1, page_lines);
1807 if (cw->offx)
1808 cl_end();
1810 (void) putchar(' ');
1811 ++ttyDisplay->curx;
1813 if (!iflags.use_menu_color
1814 || !get_menu_coloring(curr->str, &color, &attr))
1815 attr = curr->attr;
1817 /* which character to start attribute highlighting;
1818 whole line for headers and such, after the selector
1819 character and space and selection indicator for menu
1820 lines (including fake ones that simulate grayed-out
1821 entries, so we don't rely on curr->identifier here) */
1822 attr_n = 0; /* whole line */
1823 if (curr->str[0] && curr->str[1] == ' '
1824 && curr->str[2] && index("-+#", curr->str[2])
1825 && curr->str[3] == ' ')
1826 /* [0]=letter, [1]==space, [2]=[-+#], [3]=space */
1827 attr_n = 4; /* [4:N]=entry description */
1830 * Don't use xputs() because (1) under unix it calls
1831 * tputstr() which will interpret a '*' as some kind
1832 * of padding information and (2) it calls xputc to
1833 * actually output the character. We're faster doing
1834 * this.
1836 for (n = 0, cp = curr->str;
1837 *cp &&
1838 #ifndef WIN32CON
1839 (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
1840 #else
1841 (int) ttyDisplay->curx < (int) ttyDisplay->cols;
1842 ttyDisplay->curx++,
1843 #endif
1844 cp++, n++) {
1845 if (n == attr_n && (color != NO_COLOR
1846 || attr != ATR_NONE))
1847 toggle_menu_attr(TRUE, color, attr);
1848 if (n == 2
1849 && curr->identifier.a_void != 0
1850 && curr->selected) {
1851 if (curr->count == -1L)
1852 (void) putchar('+'); /* all selected */
1853 else
1854 (void) putchar('#'); /* count selected */
1855 } else
1856 (void) putchar(*cp);
1857 } /* for *cp */
1858 if (n > attr_n && (color != NO_COLOR || attr != ATR_NONE))
1859 toggle_menu_attr(FALSE, color, attr);
1860 } /* if npages > 0 */
1861 } else {
1862 page_start = 0;
1863 page_end = 0;
1864 page_lines = 0;
1866 *rp = 0;
1867 /* remember how many explicit menu choices there are */
1868 resp_len = (int) strlen(resp);
1870 /* corner window - clear extra lines from last page */
1871 if (cw->offx) {
1872 for (n = page_lines + 1; n < cw->maxrow; n++) {
1873 tty_curs(window, 1, n);
1874 cl_end();
1878 /* set extra chars.. */
1879 Strcat(resp, default_menu_cmds);
1880 Strcat(resp, " "); /* next page or end */
1881 Strcat(resp, "0123456789\033\n\r"); /* counts, quit */
1882 Strcat(resp, gacc); /* group accelerators */
1883 Strcat(resp, mapped_menu_cmds);
1885 if (cw->npages > 1)
1886 Sprintf(cw->morestr, "(%d of %d)", curr_page + 1,
1887 (int) cw->npages);
1888 else if (msave)
1889 Strcpy(cw->morestr, msave);
1890 else
1891 Strcpy(cw->morestr, defmorestr);
1893 tty_curs(window, 1, page_lines);
1894 cl_end();
1895 dmore(cw, resp);
1896 } else {
1897 /* just put the cursor back... */
1898 tty_curs(window, (int) strlen(cw->morestr) + 2, page_lines);
1899 xwaitforspace(resp);
1902 really_morc = morc; /* (only used with MENU_EXPLICIT_CHOICE */
1903 if ((rp = index(resp, morc)) != 0 && rp < resp + resp_len)
1904 /* explicit menu selection; don't override it if it also
1905 happens to match a mapped menu command (such as ':' to
1906 look inside a container vs ':' to search) */
1907 morc = MENU_EXPLICIT_CHOICE;
1908 else
1909 morc = map_menu_cmd(morc);
1911 switch (morc) {
1912 case '0':
1913 /* special case: '0' is also the default ball class */
1914 if (!counting && index(gacc, morc))
1915 goto group_accel;
1916 /* fall through to count the zero */
1917 case '1':
1918 case '2':
1919 case '3':
1920 case '4':
1921 case '5':
1922 case '6':
1923 case '7':
1924 case '8':
1925 case '9':
1926 count = (count * 10L) + (long) (morc - '0');
1928 * It is debatable whether we should allow 0 to
1929 * start a count. There is no difference if the
1930 * item is selected. If not selected, then
1931 * "0b" could mean:
1933 * count starting zero: "zero b's"
1934 * ignore starting zero: "select b"
1936 * At present I don't know which is better.
1938 if (count != 0L) { /* ignore leading zeros */
1939 counting = TRUE;
1940 reset_count = FALSE;
1942 break;
1943 case '\033': /* cancel - from counting or loop */
1944 if (!counting) {
1945 /* deselect everything */
1946 for (curr = cw->mlist; curr; curr = curr->next) {
1947 curr->selected = FALSE;
1948 curr->count = -1L;
1950 cw->flags |= WIN_CANCELLED;
1951 finished = TRUE;
1953 /* else only stop count */
1954 break;
1955 case '\0': /* finished (commit) */
1956 case '\n':
1957 case '\r':
1958 /* only finished if we are actually picking something */
1959 if (cw->how != PICK_NONE) {
1960 finished = TRUE;
1961 break;
1963 /* else fall through */
1964 case ' ':
1965 case MENU_NEXT_PAGE:
1966 if (cw->npages > 0 && curr_page != cw->npages - 1) {
1967 curr_page++;
1968 page_start = 0;
1969 } else if (morc == ' ') {
1970 /* ' ' finishes menus here, but stop '>' doing the same. */
1971 finished = TRUE;
1973 break;
1974 case MENU_PREVIOUS_PAGE:
1975 if (cw->npages > 0 && curr_page != 0) {
1976 --curr_page;
1977 page_start = 0;
1979 break;
1980 case MENU_FIRST_PAGE:
1981 if (cw->npages > 0 && curr_page != 0) {
1982 page_start = 0;
1983 curr_page = 0;
1985 break;
1986 case MENU_LAST_PAGE:
1987 if (cw->npages > 0 && curr_page != cw->npages - 1) {
1988 page_start = 0;
1989 curr_page = cw->npages - 1;
1991 break;
1992 case MENU_SELECT_PAGE:
1993 if (cw->how == PICK_ANY)
1994 set_all_on_page(window, page_start, page_end);
1995 break;
1996 case MENU_UNSELECT_PAGE:
1997 unset_all_on_page(window, page_start, page_end);
1998 break;
1999 case MENU_INVERT_PAGE:
2000 if (cw->how == PICK_ANY)
2001 invert_all_on_page(window, page_start, page_end, 0);
2002 break;
2003 case MENU_SELECT_ALL:
2004 if (cw->how == PICK_ANY) {
2005 set_all_on_page(window, page_start, page_end);
2006 /* set the rest */
2007 for (curr = cw->mlist; curr; curr = curr->next)
2008 if (curr->identifier.a_void && !curr->selected)
2009 curr->selected = TRUE;
2011 break;
2012 case MENU_UNSELECT_ALL:
2013 unset_all_on_page(window, page_start, page_end);
2014 /* unset the rest */
2015 for (curr = cw->mlist; curr; curr = curr->next)
2016 if (curr->identifier.a_void && curr->selected) {
2017 curr->selected = FALSE;
2018 curr->count = -1;
2020 break;
2021 case MENU_INVERT_ALL:
2022 if (cw->how == PICK_ANY)
2023 invert_all(window, page_start, page_end, 0);
2024 break;
2025 case MENU_SEARCH:
2026 if (cw->how == PICK_NONE) {
2027 tty_nhbell();
2028 break;
2029 } else {
2030 char searchbuf[BUFSZ + 2], tmpbuf[BUFSZ];
2031 boolean on_curr_page = FALSE;
2032 int lineno = 0;
2034 tty_getlin("Search for:", tmpbuf);
2035 if (!tmpbuf[0] || tmpbuf[0] == '\033')
2036 break;
2037 Sprintf(searchbuf, "*%s*", tmpbuf);
2039 for (curr = cw->mlist; curr; curr = curr->next) {
2040 if (on_curr_page)
2041 lineno++;
2042 if (curr == page_start)
2043 on_curr_page = TRUE;
2044 else if (curr == page_end)
2045 on_curr_page = FALSE;
2046 if (curr->identifier.a_void
2047 && pmatchi(searchbuf, curr->str)) {
2048 toggle_menu_curr(window, curr, lineno, on_curr_page,
2049 counting, count);
2050 if (cw->how == PICK_ONE) {
2051 finished = TRUE;
2052 break;
2057 break;
2058 case MENU_EXPLICIT_CHOICE:
2059 morc = really_morc;
2060 /*FALLTHRU*/
2061 default:
2062 if (cw->how == PICK_NONE || !index(resp, morc)) {
2063 /* unacceptable input received */
2064 tty_nhbell();
2065 break;
2066 } else if (index(gacc, morc)) {
2067 group_accel:
2068 /* group accelerator; for the PICK_ONE case, we know that
2069 it matches exactly one item in order to be in gacc[] */
2070 invert_all(window, page_start, page_end, morc);
2071 if (cw->how == PICK_ONE)
2072 finished = TRUE;
2073 break;
2075 /* find, toggle, and possibly update */
2076 for (n = 0, curr = page_start; curr != page_end;
2077 n++, curr = curr->next)
2078 if (morc == curr->selector) {
2079 toggle_menu_curr(window, curr, n, TRUE, counting, count);
2080 if (cw->how == PICK_ONE)
2081 finished = TRUE;
2082 break; /* from `for' loop */
2084 break;
2087 } /* while */
2088 cw->morestr = msave;
2089 free((genericptr_t) morestr);
2092 STATIC_OVL void
2093 process_text_window(window, cw)
2094 winid window;
2095 struct WinDesc *cw;
2097 int i, n, attr;
2098 register char *cp;
2100 for (n = 0, i = 0; i < cw->maxrow; i++) {
2101 if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) {
2102 tty_curs(window, 1, n);
2103 cl_end();
2104 dmore(cw, quitchars);
2105 if (morc == '\033') {
2106 cw->flags |= WIN_CANCELLED;
2107 break;
2109 if (cw->offy) {
2110 tty_curs(window, 1, 0);
2111 cl_eos();
2112 } else
2113 clear_screen();
2114 n = 0;
2116 tty_curs(window, 1, n++);
2117 #ifdef H2344_BROKEN
2118 cl_end();
2119 #else
2120 if (cw->offx)
2121 cl_end();
2122 #endif
2123 if (cw->data[i]) {
2124 attr = cw->data[i][0] - 1;
2125 if (cw->offx) {
2126 (void) putchar(' ');
2127 ++ttyDisplay->curx;
2129 term_start_attr(attr);
2130 for (cp = &cw->data[i][1];
2131 #ifndef WIN32CON
2132 *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols;
2133 cp++)
2134 #else
2135 *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols;
2136 cp++, ttyDisplay->curx++)
2137 #endif
2138 (void) putchar(*cp);
2139 term_end_attr(attr);
2142 if (i == cw->maxrow) {
2143 #ifdef H2344_BROKEN
2144 if (cw->type == NHW_TEXT) {
2145 tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->cury + 1);
2146 cl_eos();
2148 #endif
2149 tty_curs(BASE_WINDOW, (int) cw->offx + 1,
2150 (cw->type == NHW_TEXT) ? (int) ttyDisplay->rows - 1 : n);
2151 cl_end();
2152 dmore(cw, quitchars);
2153 if (morc == '\033')
2154 cw->flags |= WIN_CANCELLED;
2158 /*ARGSUSED*/
2159 void
2160 tty_display_nhwindow(window, blocking)
2161 winid window;
2162 boolean blocking; /* with ttys, all windows are blocking */
2164 register struct WinDesc *cw = 0;
2165 short s_maxcol;
2167 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2168 panic(winpanicstr, window);
2169 if (cw->flags & WIN_CANCELLED)
2170 return;
2171 ttyDisplay->lastwin = window;
2172 ttyDisplay->rawprint = 0;
2174 print_vt_code2(AVTC_SELECT_WINDOW, window);
2176 switch (cw->type) {
2177 case NHW_MESSAGE:
2178 if (ttyDisplay->toplin == 1) {
2179 more();
2180 ttyDisplay->toplin = 1; /* more resets this */
2181 tty_clear_nhwindow(window);
2182 } else
2183 ttyDisplay->toplin = 0;
2184 cw->curx = cw->cury = 0;
2185 if (!cw->active)
2186 iflags.window_inited = TRUE;
2187 break;
2188 case NHW_MAP:
2189 end_glyphout();
2190 if (blocking) {
2191 if (!ttyDisplay->toplin)
2192 ttyDisplay->toplin = 1;
2193 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2194 return;
2196 case NHW_BASE:
2197 (void) fflush(stdout);
2198 break;
2199 case NHW_TEXT:
2200 cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2201 /*FALLTHRU*/
2202 case NHW_MENU:
2203 cw->active = 1;
2204 /* cw->maxcol is a long, but its value is constrained to
2205 be <= ttyDisplay->cols, so is sure to fit within a short */
2206 s_maxcol = (short) cw->maxcol;
2207 #ifdef H2344_BROKEN
2208 cw->offx = (cw->type == NHW_TEXT)
2210 : min(min(82, ttyDisplay->cols / 2),
2211 ttyDisplay->cols - s_maxcol - 1);
2212 #else
2213 /* avoid converting to uchar before calculations are finished */
2214 cw->offx = (uchar) max((int) 10,
2215 (int) (ttyDisplay->cols - s_maxcol - 1));
2216 #endif
2217 if (cw->offx < 0)
2218 cw->offx = 0;
2219 if (cw->type == NHW_MENU)
2220 cw->offy = 0;
2221 if (ttyDisplay->toplin == 1)
2222 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2223 #ifdef H2344_BROKEN
2224 if (cw->maxrow >= (int) ttyDisplay->rows
2225 || !iflags.menu_overlay)
2226 #else
2227 if (cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows
2228 || !iflags.menu_overlay)
2229 #endif
2231 cw->offx = 0;
2232 if (cw->offy || iflags.menu_overlay) {
2233 tty_curs(window, 1, 0);
2234 cl_eos();
2235 } else
2236 clear_screen();
2237 ttyDisplay->toplin = 0;
2238 } else {
2239 if (WIN_MESSAGE != WIN_ERR)
2240 tty_clear_nhwindow(WIN_MESSAGE);
2243 if (cw->data || !cw->maxrow)
2244 process_text_window(window, cw);
2245 else
2246 process_menu_window(window, cw);
2247 break;
2249 cw->active = 1;
2252 void
2253 tty_dismiss_nhwindow(window)
2254 winid window;
2256 register struct WinDesc *cw = 0;
2258 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2259 panic(winpanicstr, window);
2261 print_vt_code2(AVTC_SELECT_WINDOW, window);
2263 switch (cw->type) {
2264 case NHW_MESSAGE:
2265 if (ttyDisplay->toplin)
2266 tty_display_nhwindow(WIN_MESSAGE, TRUE);
2267 /*FALLTHRU*/
2268 case NHW_STATUS:
2269 case NHW_BASE:
2270 case NHW_MAP:
2272 * these should only get dismissed when the game is going away
2273 * or suspending
2275 tty_curs(BASE_WINDOW, 1, (int) ttyDisplay->rows - 1);
2276 cw->active = 0;
2277 break;
2278 case NHW_MENU:
2279 case NHW_TEXT:
2280 if (cw->active) {
2281 if (iflags.window_inited) {
2282 /* otherwise dismissing the text endwin after other windows
2283 * are dismissed tries to redraw the map and panics. since
2284 * the whole reason for dismissing the other windows was to
2285 * leave the ending window on the screen, we don't want to
2286 * erase it anyway.
2288 erase_menu_or_text(window, cw, FALSE);
2290 cw->active = 0;
2292 break;
2294 cw->flags = 0;
2297 void
2298 tty_destroy_nhwindow(window)
2299 winid window;
2301 register struct WinDesc *cw = 0;
2303 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2304 panic(winpanicstr, window);
2306 if (cw->active)
2307 tty_dismiss_nhwindow(window);
2308 if (cw->type == NHW_MESSAGE)
2309 iflags.window_inited = 0;
2310 if (cw->type == NHW_MAP)
2311 clear_screen();
2313 free_window_info(cw, TRUE);
2314 free((genericptr_t) cw);
2315 wins[window] = 0;
2318 void
2319 tty_curs(window, x, y)
2320 winid window;
2321 register int x, y; /* not xchar: perhaps xchar is unsigned and
2322 curx-x would be unsigned as well */
2324 struct WinDesc *cw = 0;
2325 int cx = ttyDisplay->curx;
2326 int cy = ttyDisplay->cury;
2328 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2329 panic(winpanicstr, window);
2330 ttyDisplay->lastwin = window;
2332 print_vt_code2(AVTC_SELECT_WINDOW, window);
2334 #if defined(USE_TILES) && defined(MSDOS)
2335 adjust_cursor_flags(cw);
2336 #endif
2337 cw->curx = --x; /* column 0 is never used */
2338 cw->cury = y;
2339 #ifdef DEBUG
2340 if (x < 0 || y < 0 || y >= cw->rows || x > cw->cols) {
2341 const char *s = "[unknown type]";
2342 switch (cw->type) {
2343 case NHW_MESSAGE:
2344 s = "[topl window]";
2345 break;
2346 case NHW_STATUS:
2347 s = "[status window]";
2348 break;
2349 case NHW_MAP:
2350 s = "[map window]";
2351 break;
2352 case NHW_MENU:
2353 s = "[corner window]";
2354 break;
2355 case NHW_TEXT:
2356 s = "[text window]";
2357 break;
2358 case NHW_BASE:
2359 s = "[base window]";
2360 break;
2362 debugpline4("bad curs positioning win %d %s (%d,%d)", window, s, x,
2364 return;
2366 #endif
2367 x += cw->offx;
2368 y += cw->offy;
2370 #ifdef CLIPPING
2371 if (clipping && window == WIN_MAP) {
2372 x -= clipx;
2373 y -= clipy;
2375 #endif
2377 if (y == cy && x == cx)
2378 return;
2380 if (cw->type == NHW_MAP)
2381 end_glyphout();
2383 #ifndef NO_TERMS
2384 if (!nh_ND && (cx != x || x <= 3)) { /* Extremely primitive */
2385 cmov(x, y); /* bunker!wtm */
2386 return;
2388 #endif
2390 if ((cy -= y) < 0)
2391 cy = -cy;
2392 if ((cx -= x) < 0)
2393 cx = -cx;
2394 if (cy <= 3 && cx <= 3) {
2395 nocmov(x, y);
2396 #ifndef NO_TERMS
2397 } else if ((x <= 3 && cy <= 3) || (!nh_CM && x < cx)) {
2398 (void) putchar('\r');
2399 ttyDisplay->curx = 0;
2400 nocmov(x, y);
2401 } else if (!nh_CM) {
2402 nocmov(x, y);
2403 #endif
2404 } else
2405 cmov(x, y);
2407 ttyDisplay->curx = x;
2408 ttyDisplay->cury = y;
2411 STATIC_OVL void
2412 tty_putsym(window, x, y, ch)
2413 winid window;
2414 int x, y;
2415 char ch;
2417 register struct WinDesc *cw = 0;
2419 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0)
2420 panic(winpanicstr, window);
2422 print_vt_code2(AVTC_SELECT_WINDOW, window);
2424 switch (cw->type) {
2425 case NHW_STATUS:
2426 case NHW_MAP:
2427 case NHW_BASE:
2428 tty_curs(window, x, y);
2429 (void) putchar(ch);
2430 ttyDisplay->curx++;
2431 cw->curx++;
2432 break;
2433 case NHW_MESSAGE:
2434 case NHW_MENU:
2435 case NHW_TEXT:
2436 impossible("Can't putsym to window type %d", cw->type);
2437 break;
2441 STATIC_OVL const char *
2442 compress_str(str)
2443 const char *str;
2445 static char cbuf[BUFSZ];
2447 /* compress out consecutive spaces if line is too long;
2448 topline wrapping converts space at wrap point into newline,
2449 we reverse that here */
2450 if ((int) strlen(str) >= CO || index(str, '\n')) {
2451 const char *in_str = str;
2452 char c, *outstr = cbuf, *outend = &cbuf[sizeof cbuf - 1];
2453 boolean was_space = TRUE; /* True discards all leading spaces;
2454 False would retain one if present */
2456 while ((c = *in_str++) != '\0' && outstr < outend) {
2457 if (c == '\n')
2458 c = ' ';
2459 if (was_space && c == ' ')
2460 continue;
2461 *outstr++ = c;
2462 was_space = (c == ' ');
2464 if ((was_space && outstr > cbuf) || outstr == outend)
2465 --outstr; /* remove trailing space or make room for terminator */
2466 *outstr = '\0';
2467 str = cbuf;
2469 return str;
2472 void
2473 tty_putstr(window, attr, str)
2474 winid window;
2475 int attr;
2476 const char *str;
2478 register struct WinDesc *cw = 0;
2479 register char *ob;
2480 register const char *nb;
2481 register long i, j, n0;
2483 /* Assume there's a real problem if the window is missing --
2484 * probably a panic message
2486 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) {
2487 tty_raw_print(str);
2488 return;
2491 if (str == (const char *) 0
2492 || ((cw->flags & WIN_CANCELLED) && (cw->type != NHW_MESSAGE)))
2493 return;
2494 if (cw->type != NHW_MESSAGE)
2495 str = compress_str(str);
2497 ttyDisplay->lastwin = window;
2499 print_vt_code2(AVTC_SELECT_WINDOW, window);
2501 switch (cw->type) {
2502 case NHW_MESSAGE:
2503 /* really do this later */
2504 #if defined(USER_SOUNDS) && defined(WIN32CON)
2505 play_sound_for_message(str);
2506 #endif
2507 update_topl(str);
2508 break;
2510 case NHW_STATUS:
2511 ob = &cw->data[cw->cury][j = cw->curx];
2512 if (context.botlx)
2513 *ob = 0;
2514 if (!cw->cury && (int) strlen(str) >= CO) {
2515 /* the characters before "St:" are unnecessary */
2516 nb = index(str, ':');
2517 if (nb && nb > str + 2)
2518 str = nb - 2;
2520 nb = str;
2521 for (i = cw->curx + 1, n0 = cw->cols; i < n0; i++, nb++) {
2522 if (!*nb) {
2523 if (*ob || context.botlx) {
2524 /* last char printed may be in middle of line */
2525 tty_curs(WIN_STATUS, i, cw->cury);
2526 cl_end();
2528 break;
2530 if (*ob != *nb)
2531 tty_putsym(WIN_STATUS, i, cw->cury, *nb);
2532 if (*ob)
2533 ob++;
2536 (void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1);
2537 cw->data[cw->cury][cw->cols - 1] = '\0'; /* null terminate */
2538 #ifdef STATUS_VIA_WINDOWPORT
2539 if (!iflags.use_status_hilites) {
2540 #endif
2541 cw->cury = (cw->cury + 1) % 2;
2542 cw->curx = 0;
2543 #ifdef STATUS_VIA_WINDOWPORT
2545 #endif
2546 break;
2547 case NHW_MAP:
2548 tty_curs(window, cw->curx + 1, cw->cury);
2549 term_start_attr(attr);
2550 while (*str && (int) ttyDisplay->curx < (int) ttyDisplay->cols - 1) {
2551 (void) putchar(*str);
2552 str++;
2553 ttyDisplay->curx++;
2555 cw->curx = 0;
2556 cw->cury++;
2557 term_end_attr(attr);
2558 break;
2559 case NHW_BASE:
2560 tty_curs(window, cw->curx + 1, cw->cury);
2561 term_start_attr(attr);
2562 while (*str) {
2563 if ((int) ttyDisplay->curx >= (int) ttyDisplay->cols - 1) {
2564 cw->curx = 0;
2565 cw->cury++;
2566 tty_curs(window, cw->curx + 1, cw->cury);
2568 (void) putchar(*str);
2569 str++;
2570 ttyDisplay->curx++;
2572 cw->curx = 0;
2573 cw->cury++;
2574 term_end_attr(attr);
2575 break;
2576 case NHW_MENU:
2577 case NHW_TEXT:
2578 #ifdef H2344_BROKEN
2579 if (cw->type == NHW_TEXT
2580 && (cw->cury + cw->offy) == ttyDisplay->rows - 1)
2581 #else
2582 if (cw->type == NHW_TEXT && cw->cury == ttyDisplay->rows - 1)
2583 #endif
2585 /* not a menu, so save memory and output 1 page at a time */
2586 cw->maxcol = ttyDisplay->cols; /* force full-screen mode */
2587 tty_display_nhwindow(window, TRUE);
2588 for (i = 0; i < cw->maxrow; i++)
2589 if (cw->data[i]) {
2590 free((genericptr_t) cw->data[i]);
2591 cw->data[i] = 0;
2593 cw->maxrow = cw->cury = 0;
2595 /* always grows one at a time, but alloc 12 at a time */
2596 if (cw->cury >= cw->rows) {
2597 char **tmp;
2599 cw->rows += 12;
2600 tmp = (char **) alloc(sizeof(char *) * (unsigned) cw->rows);
2601 for (i = 0; i < cw->maxrow; i++)
2602 tmp[i] = cw->data[i];
2603 if (cw->data)
2604 free((genericptr_t) cw->data);
2605 cw->data = tmp;
2607 for (i = cw->maxrow; i < cw->rows; i++)
2608 cw->data[i] = 0;
2610 if (cw->data[cw->cury])
2611 free((genericptr_t) cw->data[cw->cury]);
2612 n0 = (long) strlen(str) + 1L;
2613 ob = cw->data[cw->cury] = (char *) alloc((unsigned) n0 + 1);
2614 *ob++ = (char) (attr + 1); /* avoid nuls, for convenience */
2615 Strcpy(ob, str);
2617 if (n0 > cw->maxcol)
2618 cw->maxcol = n0;
2619 if (++cw->cury > cw->maxrow)
2620 cw->maxrow = cw->cury;
2621 if (n0 > CO) {
2622 /* attempt to break the line */
2623 for (i = CO - 1; i && str[i] != ' ' && str[i] != '\n';)
2624 i--;
2625 if (i) {
2626 cw->data[cw->cury - 1][++i] = '\0';
2627 tty_putstr(window, attr, &str[i]);
2630 break;
2634 void
2635 tty_display_file(fname, complain)
2636 const char *fname;
2637 boolean complain;
2639 #ifdef DEF_PAGER /* this implies that UNIX is defined */
2641 /* use external pager; this may give security problems */
2642 register int fd = open(fname, 0);
2644 if (fd < 0) {
2645 if (complain)
2646 pline("Cannot open %s.", fname);
2647 else
2648 docrt();
2649 return;
2651 if (child(1)) {
2652 /* Now that child() does a setuid(getuid()) and a chdir(),
2653 we may not be able to open file fname anymore, so make
2654 it stdin. */
2655 (void) close(0);
2656 if (dup(fd)) {
2657 if (complain)
2658 raw_printf("Cannot open %s as stdin.", fname);
2659 } else {
2660 (void) execlp(catmore, "page", (char *) 0);
2661 if (complain)
2662 raw_printf("Cannot exec %s.", catmore);
2664 if (complain)
2665 sleep(10); /* want to wait_synch() but stdin is gone */
2666 terminate(EXIT_FAILURE);
2668 (void) close(fd);
2669 #ifdef notyet
2670 winch_seen = 0;
2671 #endif
2673 #else /* DEF_PAGER */
2675 dlb *f;
2676 char buf[BUFSZ];
2677 char *cr;
2679 tty_clear_nhwindow(WIN_MESSAGE);
2680 f = dlb_fopen(fname, "r");
2681 if (!f) {
2682 if (complain) {
2683 home();
2684 tty_mark_synch();
2685 tty_raw_print("");
2686 perror(fname);
2687 tty_wait_synch();
2688 pline("Cannot open \"%s\".", fname);
2689 } else if (u.ux)
2690 docrt();
2691 } else {
2692 winid datawin = tty_create_nhwindow(NHW_TEXT);
2693 boolean empty = TRUE;
2695 if (complain
2696 #ifndef NO_TERMS
2697 && nh_CD
2698 #endif
2700 /* attempt to scroll text below map window if there's room */
2701 wins[datawin]->offy = wins[WIN_STATUS]->offy + 3;
2702 if ((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows)
2703 wins[datawin]->offy = 0;
2705 while (dlb_fgets(buf, BUFSZ, f)) {
2706 if ((cr = index(buf, '\n')) != 0)
2707 *cr = 0;
2708 #ifdef MSDOS
2709 if ((cr = index(buf, '\r')) != 0)
2710 *cr = 0;
2711 #endif
2712 if (index(buf, '\t') != 0)
2713 (void) tabexpand(buf);
2714 empty = FALSE;
2715 tty_putstr(datawin, 0, buf);
2716 if (wins[datawin]->flags & WIN_CANCELLED)
2717 break;
2719 if (!empty)
2720 tty_display_nhwindow(datawin, FALSE);
2721 tty_destroy_nhwindow(datawin);
2722 (void) dlb_fclose(f);
2725 #endif /* DEF_PAGER */
2728 void
2729 tty_start_menu(window)
2730 winid window;
2732 tty_clear_nhwindow(window);
2733 return;
2736 /*ARGSUSED*/
2738 * Add a menu item to the beginning of the menu list. This list is reversed
2739 * later.
2741 void
2742 tty_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
2743 winid window; /* window to use, must be of type NHW_MENU */
2744 int glyph UNUSED; /* glyph to display with item (not used) */
2745 const anything *identifier; /* what to return if selected */
2746 char ch; /* keyboard accelerator (0 = pick our own) */
2747 char gch; /* group accelerator (0 = no group) */
2748 int attr; /* attribute for string (like tty_putstr()) */
2749 const char *str; /* menu string */
2750 boolean preselected; /* item is marked as selected */
2752 register struct WinDesc *cw = 0;
2753 tty_menu_item *item;
2754 const char *newstr;
2755 char buf[4 + BUFSZ];
2757 if (str == (const char *) 0)
2758 return;
2760 if (window == WIN_ERR
2761 || (cw = wins[window]) == (struct WinDesc *) 0
2762 || cw->type != NHW_MENU)
2763 panic(winpanicstr, window);
2765 cw->nitems++;
2766 if (identifier->a_void) {
2767 int len = strlen(str);
2769 if (len >= BUFSZ) {
2770 /* We *think* everything's coming in off at most BUFSZ bufs... */
2771 impossible("Menu item too long (%d).", len);
2772 len = BUFSZ - 1;
2774 Sprintf(buf, "%c - ", ch ? ch : '?');
2775 (void) strncpy(buf + 4, str, len);
2776 buf[4 + len] = '\0';
2777 newstr = buf;
2778 } else
2779 newstr = str;
2781 item = (tty_menu_item *) alloc(sizeof(tty_menu_item));
2782 item->identifier = *identifier;
2783 item->count = -1L;
2784 item->selected = preselected;
2785 item->selector = ch;
2786 item->gselector = gch;
2787 item->attr = attr;
2788 item->str = dupstr(newstr ? newstr : "");
2790 item->next = cw->mlist;
2791 cw->mlist = item;
2794 /* Invert the given list, can handle NULL as an input. */
2795 STATIC_OVL tty_menu_item *
2796 reverse(curr)
2797 tty_menu_item *curr;
2799 tty_menu_item *next, *head = 0;
2801 while (curr) {
2802 next = curr->next;
2803 curr->next = head;
2804 head = curr;
2805 curr = next;
2807 return head;
2811 * End a menu in this window, window must a type NHW_MENU. This routine
2812 * processes the string list. We calculate the # of pages, then assign
2813 * keyboard accelerators as needed. Finally we decide on the width and
2814 * height of the window.
2816 void
2817 tty_end_menu(window, prompt)
2818 winid window; /* menu to use */
2819 const char *prompt; /* prompt to for menu */
2821 struct WinDesc *cw = 0;
2822 tty_menu_item *curr;
2823 short len;
2824 int lmax, n;
2825 char menu_ch;
2827 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
2828 || cw->type != NHW_MENU)
2829 panic(winpanicstr, window);
2831 /* Reverse the list so that items are in correct order. */
2832 cw->mlist = reverse(cw->mlist);
2834 /* Put the prompt at the beginning of the menu. */
2835 if (prompt) {
2836 anything any;
2838 any = zeroany; /* not selectable */
2839 tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
2840 MENU_UNSELECTED);
2841 tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt,
2842 MENU_UNSELECTED);
2845 /* XXX another magic number? 52 */
2846 lmax = min(52, (int) ttyDisplay->rows - 1); /* # lines per page */
2847 cw->npages = (cw->nitems + (lmax - 1)) / lmax; /* # of pages */
2849 /* make sure page list is large enough */
2850 if (cw->plist_size < cw->npages + 1 /*need 1 slot beyond last*/) {
2851 if (cw->plist)
2852 free((genericptr_t) cw->plist);
2853 cw->plist_size = cw->npages + 1;
2854 cw->plist = (tty_menu_item **) alloc(cw->plist_size
2855 * sizeof(tty_menu_item *));
2858 cw->cols = 0; /* cols is set when the win is initialized... (why?) */
2859 menu_ch = '?'; /* lint suppression */
2860 for (n = 0, curr = cw->mlist; curr; n++, curr = curr->next) {
2861 /* set page boundaries and character accelerators */
2862 if ((n % lmax) == 0) {
2863 menu_ch = 'a';
2864 cw->plist[n / lmax] = curr;
2866 if (curr->identifier.a_void && !curr->selector) {
2867 curr->str[0] = curr->selector = menu_ch;
2868 if (menu_ch++ == 'z')
2869 menu_ch = 'A';
2872 /* cut off any lines that are too long */
2873 len = strlen(curr->str) + 2; /* extra space at beg & end */
2874 if (len > (int) ttyDisplay->cols) {
2875 curr->str[ttyDisplay->cols - 2] = 0;
2876 len = ttyDisplay->cols;
2878 if (len > cw->cols)
2879 cw->cols = len;
2881 cw->plist[cw->npages] = 0; /* plist terminator */
2884 * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) "
2886 if (cw->npages > 1) {
2887 char buf[QBUFSZ];
2888 /* produce the largest demo string */
2889 Sprintf(buf, "(%ld of %ld) ", cw->npages, cw->npages);
2890 len = strlen(buf);
2891 cw->morestr = dupstr("");
2892 } else {
2893 cw->morestr = dupstr("(end) ");
2894 len = strlen(cw->morestr);
2897 if (len > (int) ttyDisplay->cols) {
2898 /* truncate the prompt if it's too long for the screen */
2899 if (cw->npages <= 1) /* only str in single page case */
2900 cw->morestr[ttyDisplay->cols] = 0;
2901 len = ttyDisplay->cols;
2903 if (len > cw->cols)
2904 cw->cols = len;
2906 cw->maxcol = cw->cols;
2909 * The number of lines in the first page plus the morestr will be the
2910 * maximum size of the window.
2912 if (cw->npages > 1)
2913 cw->maxrow = cw->rows = lmax + 1;
2914 else
2915 cw->maxrow = cw->rows = cw->nitems + 1;
2919 tty_select_menu(window, how, menu_list)
2920 winid window;
2921 int how;
2922 menu_item **menu_list;
2924 register struct WinDesc *cw = 0;
2925 tty_menu_item *curr;
2926 menu_item *mi;
2927 int n, cancelled;
2929 if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0
2930 || cw->type != NHW_MENU)
2931 panic(winpanicstr, window);
2933 *menu_list = (menu_item *) 0;
2934 cw->how = (short) how;
2935 morc = 0;
2936 tty_display_nhwindow(window, TRUE);
2937 cancelled = !!(cw->flags & WIN_CANCELLED);
2938 tty_dismiss_nhwindow(window); /* does not destroy window data */
2940 if (cancelled) {
2941 n = -1;
2942 } else {
2943 for (n = 0, curr = cw->mlist; curr; curr = curr->next)
2944 if (curr->selected)
2945 n++;
2948 if (n > 0) {
2949 *menu_list = (menu_item *) alloc(n * sizeof(menu_item));
2950 for (mi = *menu_list, curr = cw->mlist; curr; curr = curr->next)
2951 if (curr->selected) {
2952 mi->item = curr->identifier;
2953 mi->count = curr->count;
2954 mi++;
2958 return n;
2961 /* special hack for treating top line --More-- as a one item menu */
2962 char
2963 tty_message_menu(let, how, mesg)
2964 char let;
2965 int how;
2966 const char *mesg;
2968 /* "menu" without selection; use ordinary pline, no more() */
2969 if (how == PICK_NONE) {
2970 pline("%s", mesg);
2971 return 0;
2974 ttyDisplay->dismiss_more = let;
2975 morc = 0;
2976 /* barebones pline(); since we're only supposed to be called after
2977 response to a prompt, we'll assume that the display is up to date */
2978 tty_putstr(WIN_MESSAGE, 0, mesg);
2979 /* if `mesg' didn't wrap (triggering --More--), force --More-- now */
2980 if (ttyDisplay->toplin == 1) {
2981 more();
2982 ttyDisplay->toplin = 1; /* more resets this */
2983 tty_clear_nhwindow(WIN_MESSAGE);
2985 /* normally <ESC> means skip further messages, but in this case
2986 it means cancel the current prompt; any other messages should
2987 continue to be output normally */
2988 wins[WIN_MESSAGE]->flags &= ~WIN_CANCELLED;
2989 ttyDisplay->dismiss_more = 0;
2991 return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0';
2994 void
2995 tty_update_inventory()
2997 return;
3000 void
3001 tty_mark_synch()
3003 (void) fflush(stdout);
3006 void
3007 tty_wait_synch()
3009 /* we just need to make sure all windows are synch'd */
3010 if (!ttyDisplay || ttyDisplay->rawprint) {
3011 getret();
3012 if (ttyDisplay)
3013 ttyDisplay->rawprint = 0;
3014 } else {
3015 tty_display_nhwindow(WIN_MAP, FALSE);
3016 if (ttyDisplay->inmore) {
3017 addtopl("--More--");
3018 (void) fflush(stdout);
3019 } else if (ttyDisplay->inread > program_state.gameover) {
3020 /* this can only happen if we were reading and got interrupted */
3021 ttyDisplay->toplin = 3;
3022 /* do this twice; 1st time gets the Quit? message again */
3023 (void) tty_doprev_message();
3024 (void) tty_doprev_message();
3025 ttyDisplay->intr++;
3026 (void) fflush(stdout);
3031 void
3032 docorner(xmin, ymax)
3033 register int xmin, ymax;
3035 register int y;
3036 register struct WinDesc *cw = wins[WIN_MAP];
3038 #if 0 /* this optimization is not valuable enough to justify
3039 abusing core internals... */
3040 if (u.uswallow) { /* Can be done more efficiently */
3041 swallowed(1);
3042 /* without this flush, if we happen to follow --More-- displayed in
3043 leftmost column, the cursor gets left in the wrong place after
3044 <docorner<more<update_topl<tty_putstr calls unwind back to core */
3045 flush_screen(0);
3046 return;
3048 #endif /*0*/
3050 #if defined(SIGWINCH) && defined(CLIPPING)
3051 if (ymax > LI)
3052 ymax = LI; /* can happen if window gets smaller */
3053 #endif
3054 for (y = 0; y < ymax; y++) {
3055 tty_curs(BASE_WINDOW, xmin, y); /* move cursor */
3056 cl_end(); /* clear to end of line */
3057 #ifdef CLIPPING
3058 if (y < (int) cw->offy || y + clipy > ROWNO)
3059 continue; /* only refresh board */
3060 #if defined(USE_TILES) && defined(MSDOS)
3061 if (iflags.tile_view)
3062 row_refresh((xmin / 2) + clipx - ((int) cw->offx / 2), COLNO - 1,
3063 y + clipy - (int) cw->offy);
3064 else
3065 #endif
3066 row_refresh(xmin + clipx - (int) cw->offx, COLNO - 1,
3067 y + clipy - (int) cw->offy);
3068 #else
3069 if (y < cw->offy || y > ROWNO)
3070 continue; /* only refresh board */
3071 row_refresh(xmin - (int) cw->offx, COLNO - 1, y - (int) cw->offy);
3072 #endif
3075 end_glyphout();
3076 if (ymax >= (int) wins[WIN_STATUS]->offy) {
3077 /* we have wrecked the bottom line */
3078 context.botlx = 1;
3079 bot();
3083 void
3084 end_glyphout()
3086 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3087 if (GFlag) {
3088 GFlag = FALSE;
3089 graph_off();
3091 #endif
3092 #ifdef TEXTCOLOR
3093 if (ttyDisplay->color != NO_COLOR) {
3094 term_end_color();
3095 ttyDisplay->color = NO_COLOR;
3097 #endif
3100 #ifndef WIN32
3101 void
3102 g_putch(in_ch)
3103 int in_ch;
3105 register char ch = (char) in_ch;
3107 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
3108 if (SYMHANDLING(H_IBM) || iflags.eight_bit_tty) {
3109 /* IBM-compatible displays don't need other stuff */
3110 (void) putchar(ch);
3111 } else if (ch & 0x80) {
3112 if (!GFlag || HE_resets_AS) {
3113 graph_on();
3114 GFlag = TRUE;
3116 (void) putchar((ch ^ 0x80)); /* Strip 8th bit */
3117 } else {
3118 if (GFlag) {
3119 graph_off();
3120 GFlag = FALSE;
3122 (void) putchar(ch);
3125 #else
3126 (void) putchar(ch);
3128 #endif /* ASCIIGRAPH && !NO_TERMS */
3130 return;
3132 #endif /* !WIN32 */
3134 #ifdef CLIPPING
3135 void
3136 setclipped()
3138 clipping = TRUE;
3139 clipx = clipy = 0;
3140 clipxmax = CO;
3141 clipymax = LI - 3;
3144 void
3145 tty_cliparound(x, y)
3146 int x, y;
3148 extern boolean restoring;
3149 int oldx = clipx, oldy = clipy;
3151 if (!clipping)
3152 return;
3153 if (x < clipx + 5) {
3154 clipx = max(0, x - 20);
3155 clipxmax = clipx + CO;
3156 } else if (x > clipxmax - 5) {
3157 clipxmax = min(COLNO, clipxmax + 20);
3158 clipx = clipxmax - CO;
3160 if (y < clipy + 2) {
3161 clipy = max(0, y - (clipymax - clipy) / 2);
3162 clipymax = clipy + (LI - 3);
3163 } else if (y > clipymax - 2) {
3164 clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2);
3165 clipy = clipymax - (LI - 3);
3167 if (clipx != oldx || clipy != oldy) {
3168 if (on_level(&u.uz0, &u.uz) && !restoring)
3169 (void) doredraw();
3172 #endif /* CLIPPING */
3175 * tty_print_glyph
3177 * Print the glyph to the output device. Don't flush the output device.
3179 * Since this is only called from show_glyph(), it is assumed that the
3180 * position and glyph are always correct (checked there)!
3183 void
3184 tty_print_glyph(window, x, y, glyph, bkglyph)
3185 winid window;
3186 xchar x, y;
3187 int glyph;
3188 int bkglyph UNUSED;
3190 int ch;
3191 boolean reverse_on = FALSE;
3192 int color;
3193 unsigned special;
3195 #ifdef CLIPPING
3196 if (clipping) {
3197 if (x <= clipx || y < clipy || x >= clipxmax || y >= clipymax)
3198 return;
3200 #endif
3201 /* map glyph to character and color */
3202 (void) mapglyph(glyph, &ch, &color, &special, x, y);
3204 print_vt_code2(AVTC_SELECT_WINDOW, window);
3206 /* Move the cursor. */
3207 tty_curs(window, x, y);
3209 print_vt_code3(AVTC_GLYPH_START, glyph2tile[glyph], special);
3211 #ifndef NO_TERMS
3212 if (ul_hack && ch == '_') { /* non-destructive underscore */
3213 (void) putchar((char) ' ');
3214 backsp();
3216 #endif
3218 #ifdef TEXTCOLOR
3219 if (color != ttyDisplay->color) {
3220 if (ttyDisplay->color != NO_COLOR)
3221 term_end_color();
3222 ttyDisplay->color = color;
3223 if (color != NO_COLOR)
3224 term_start_color(color);
3226 #endif /* TEXTCOLOR */
3228 /* must be after color check; term_end_color may turn off inverse too */
3229 if (((special & MG_PET) && iflags.hilite_pet)
3230 || ((special & MG_OBJPILE) && iflags.hilite_pile)
3231 || ((special & MG_DETECT) && iflags.use_inverse)
3232 || ((special & MG_BW_LAVA) && iflags.use_inverse)) {
3233 term_start_attr(ATR_INVERSE);
3234 reverse_on = TRUE;
3237 #if defined(USE_TILES) && defined(MSDOS)
3238 if (iflags.grmode && iflags.tile_view)
3239 xputg(glyph, ch, special);
3240 else
3241 #endif
3242 g_putch(ch); /* print the character */
3244 if (reverse_on) {
3245 term_end_attr(ATR_INVERSE);
3246 #ifdef TEXTCOLOR
3247 /* turn off color as well, ATR_INVERSE may have done this already */
3248 if (ttyDisplay->color != NO_COLOR) {
3249 term_end_color();
3250 ttyDisplay->color = NO_COLOR;
3252 #endif
3255 print_vt_code1(AVTC_GLYPH_END);
3257 wins[window]->curx++; /* one character over */
3258 ttyDisplay->curx++; /* the real cursor moved too */
3261 void
3262 tty_raw_print(str)
3263 const char *str;
3265 if (ttyDisplay)
3266 ttyDisplay->rawprint++;
3267 print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3268 #if defined(MICRO) || defined(WIN32CON)
3269 msmsg("%s\n", str);
3270 #else
3271 puts(str);
3272 (void) fflush(stdout);
3273 #endif
3276 void
3277 tty_raw_print_bold(str)
3278 const char *str;
3280 if (ttyDisplay)
3281 ttyDisplay->rawprint++;
3282 print_vt_code2(AVTC_SELECT_WINDOW, NHW_BASE);
3283 term_start_raw_bold();
3284 #if defined(MICRO) || defined(WIN32CON)
3285 msmsg("%s", str);
3286 #else
3287 (void) fputs(str, stdout);
3288 #endif
3289 term_end_raw_bold();
3290 #if defined(MICRO) || defined(WIN32CON)
3291 msmsg("\n");
3292 #else
3293 puts("");
3294 (void) fflush(stdout);
3295 #endif
3299 tty_nhgetch()
3301 int i;
3302 #ifdef UNIX
3303 /* kludge alert: Some Unix variants return funny values if getc()
3304 * is called, interrupted, and then called again. There
3305 * is non-reentrant code in the internal _filbuf() routine, called by
3306 * getc().
3308 static volatile int nesting = 0;
3309 char nestbuf;
3310 #endif
3312 print_vt_code1(AVTC_INLINE_SYNC);
3313 (void) fflush(stdout);
3314 /* Note: if raw_print() and wait_synch() get called to report terminal
3315 * initialization problems, then wins[] and ttyDisplay might not be
3316 * available yet. Such problems will probably be fatal before we get
3317 * here, but validate those pointers just in case...
3319 if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3320 wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3321 #ifdef UNIX
3322 i = (++nesting == 1) ? tgetch()
3323 : (read(fileno(stdin), (genericptr_t) &nestbuf, 1)
3324 == 1) ? (int) nestbuf : EOF;
3325 --nesting;
3326 #else
3327 i = tgetch();
3328 #endif
3329 if (!i)
3330 i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */
3331 else if (i == EOF)
3332 i = '\033'; /* same for EOF */
3333 if (ttyDisplay && ttyDisplay->toplin == 1)
3334 ttyDisplay->toplin = 2;
3335 #ifdef TTY_TILES_ESCCODES
3337 /* hack to force output of the window select code */
3338 int tmp = vt_tile_current_window;
3339 vt_tile_current_window++;
3340 print_vt_code2(AVTC_SELECT_WINDOW, tmp);
3342 #endif /* TTY_TILES_ESCCODES */
3343 return i;
3347 * return a key, or 0, in which case a mouse button was pressed
3348 * mouse events should be returned as character postitions in the map window.
3349 * Since normal tty's don't have mice, just return a key.
3351 /*ARGSUSED*/
3353 tty_nh_poskey(x, y, mod)
3354 int *x, *y, *mod;
3356 #if defined(WIN32CON)
3357 int i;
3358 (void) fflush(stdout);
3359 /* Note: if raw_print() and wait_synch() get called to report terminal
3360 * initialization problems, then wins[] and ttyDisplay might not be
3361 * available yet. Such problems will probably be fatal before we get
3362 * here, but validate those pointers just in case...
3364 if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE])
3365 wins[WIN_MESSAGE]->flags &= ~WIN_STOP;
3366 i = ntposkey(x, y, mod);
3367 if (!i && mod && (*mod == 0 || *mod == EOF))
3368 i = '\033'; /* map NUL or EOF to ESC, nethack doesn't expect either */
3369 if (ttyDisplay && ttyDisplay->toplin == 1)
3370 ttyDisplay->toplin = 2;
3371 return i;
3372 #else /* !WIN32CON */
3373 nhUse(x);
3374 nhUse(y);
3375 nhUse(mod);
3377 return tty_nhgetch();
3378 #endif /* ?WIN32CON */
3381 void
3382 win_tty_init(dir)
3383 int dir;
3385 if (dir != WININIT)
3386 return;
3387 #if defined(WIN32CON)
3388 if (!strncmpi(windowprocs.name, "tty", 3))
3389 nttty_open(0);
3390 #endif
3391 return;
3394 #ifdef POSITIONBAR
3395 void
3396 tty_update_positionbar(posbar)
3397 char *posbar;
3399 #ifdef MSDOS
3400 video_update_positionbar(posbar);
3401 #endif
3403 #endif
3405 #ifdef STATUS_VIA_WINDOWPORT
3407 * The following data structures come from the genl_ routines in
3408 * src/windows.c and as such are considered to be on the window-port
3409 * "side" of things, rather than the NetHack-core "side" of things.
3412 extern const char *status_fieldnm[MAXBLSTATS];
3413 extern const char *status_fieldfmt[MAXBLSTATS];
3414 extern char *status_vals[MAXBLSTATS];
3415 extern boolean status_activefields[MAXBLSTATS];
3416 extern winid WIN_STATUS;
3418 #ifdef STATUS_HILITES
3419 typedef struct hilite_data_struct {
3420 int thresholdtype;
3421 anything threshold;
3422 int behavior;
3423 int under;
3424 int over;
3425 } hilite_data_t;
3426 static hilite_data_t tty_status_hilites[MAXBLSTATS];
3427 static int tty_status_colors[MAXBLSTATS];
3429 struct color_option {
3430 int color;
3431 int attr_bits;
3434 static void FDECL(start_color_option, (struct color_option));
3435 static void FDECL(end_color_option, (struct color_option));
3436 static void FDECL(apply_color_option, (struct color_option, const char *));
3437 static void FDECL(add_colored_text, (const char *, char *));
3438 #endif
3440 void
3441 tty_status_init()
3443 int i;
3445 /* let genl_status_init do most of the initialization */
3446 genl_status_init();
3448 for (i = 0; i < MAXBLSTATS; ++i) {
3449 #ifdef STATUS_HILITES
3450 tty_status_colors[i] = NO_COLOR; /* no color */
3451 tty_status_hilites[i].thresholdtype = 0;
3452 tty_status_hilites[i].behavior = BL_TH_NONE;
3453 tty_status_hilites[i].under = BL_HILITE_NONE;
3454 tty_status_hilites[i].over = BL_HILITE_NONE;
3455 #endif /* STATUS_HILITES */
3460 * *_status_update()
3461 * -- update the value of a status field.
3462 * -- the fldindex identifies which field is changing and
3463 * is an integer index value from botl.h
3464 * -- fldindex could be any one of the following from botl.h:
3465 * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3466 * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
3467 * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
3468 * BL_LEVELDESC, BL_EXP, BL_CONDITION
3469 * -- fldindex could also be BL_FLUSH (-1), which is not really
3470 * a field index, but is a special trigger to tell the
3471 * windowport that it should redisplay all its status fields,
3472 * even if no changes have been presented to it.
3473 * -- ptr is usually a "char *", unless fldindex is BL_CONDITION.
3474 * If fldindex is BL_CONDITION, then ptr is a long value with
3475 * any or none of the following bits set (from botl.h):
3476 * BL_MASK_STONE 0x00000001L
3477 * BL_MASK_SLIME 0x00000002L
3478 * BL_MASK_STRNGL 0x00000004L
3479 * BL_MASK_FOODPOIS 0x00000008L
3480 * BL_MASK_TERMILL 0x00000010L
3481 * BL_MASK_BLIND 0x00000020L
3482 * BL_MASK_DEAF 0x00000040L
3483 * BL_MASK_STUN 0x00000080L
3484 * BL_MASK_CONF 0x00000100L
3485 * BL_MASK_HALLU 0x00000200L
3486 * BL_MASK_LEV 0x00000400L
3487 * BL_MASK_FLY 0x00000800L
3488 * BL_MASK_RIDE 0x00001000L
3489 * -- The value passed for BL_GOLD includes an encoded leading
3490 * symbol for GOLD "\GXXXXNNNN:nnn". If the window port needs to use
3491 * the textual gold amount without the leading "$:" the port will
3492 * have to skip past ':' in the passed "ptr" for the BL_GOLD case.
3494 void
3495 tty_status_update(fldidx, ptr, chg, percent)
3496 int fldidx, chg, percent;
3497 genericptr_t ptr;
3499 long cond, *condptr = (long *) ptr;
3500 register int i;
3501 char *text = (char *) ptr;
3502 /* Mapping BL attributes to tty attributes
3503 * BL_HILITE_NONE -1 + 3 = 2 (statusattr[2])
3504 * BL_HILITE_INVERSE -2 + 3 = 1 (statusattr[1])
3505 * BL_HILITE_BOLD -3 + 3 = 0 (statusattr[0])
3507 long value = -1L;
3508 static boolean beenhere = FALSE;
3509 enum statusfields fieldorder[2][15] = {
3510 { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN,
3511 BL_SCORE, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH,
3512 BL_FLUSH },
3513 { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
3514 BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER,
3515 BL_CAP, BL_CONDITION, BL_FLUSH }
3517 #ifdef STATUS_HILITES
3518 static int statusattr[] = { ATR_BOLD, ATR_INVERSE, ATR_NONE };
3519 int attridx = 0;
3520 #else
3521 nhUse(chg);
3522 nhUse(percent);
3523 #endif
3525 if (fldidx != BL_FLUSH) {
3526 if (!status_activefields[fldidx])
3527 return;
3528 switch (fldidx) {
3529 case BL_CONDITION:
3530 cond = *condptr;
3531 *status_vals[fldidx] = '\0';
3532 if (cond & BL_MASK_STONE)
3533 Strcat(status_vals[fldidx], " Stone");
3534 if (cond & BL_MASK_SLIME)
3535 Strcat(status_vals[fldidx], " Slime");
3536 if (cond & BL_MASK_STRNGL)
3537 Strcat(status_vals[fldidx], " Strngl");
3538 if (cond & BL_MASK_FOODPOIS)
3539 Strcat(status_vals[fldidx], " FoodPois");
3540 if (cond & BL_MASK_TERMILL)
3541 Strcat(status_vals[fldidx], " TermIll");
3542 if (cond & BL_MASK_BLIND)
3543 Strcat(status_vals[fldidx], " Blind");
3544 if (cond & BL_MASK_DEAF)
3545 Strcat(status_vals[fldidx], " Deaf");
3546 if (cond & BL_MASK_STUN)
3547 Strcat(status_vals[fldidx], " Stun");
3548 if (cond & BL_MASK_CONF)
3549 Strcat(status_vals[fldidx], " Conf");
3550 if (cond & BL_MASK_HALLU)
3551 Strcat(status_vals[fldidx], " Hallu");
3552 if (cond & BL_MASK_LEV)
3553 Strcat(status_vals[fldidx], " Lev");
3554 if (cond & BL_MASK_FLY)
3555 Strcat(status_vals[fldidx], " Fly");
3556 if (cond & BL_MASK_RIDE)
3557 Strcat(status_vals[fldidx], " Ride");
3558 value = cond;
3559 break;
3560 default:
3561 value = atol(text);
3562 Sprintf(status_vals[fldidx],
3563 status_fieldfmt[fldidx] ? status_fieldfmt[fldidx] : "%s",
3564 text);
3565 break;
3568 #ifdef STATUS_HILITES
3569 switch (tty_status_hilites[fldidx].behavior) {
3570 case BL_TH_NONE:
3571 tty_status_colors[fldidx] = NO_COLOR;
3572 break;
3573 case BL_TH_UPDOWN:
3574 if (chg > 0)
3575 tty_status_colors[fldidx] = tty_status_hilites[fldidx].over;
3576 else if (chg < 0)
3577 tty_status_colors[fldidx] = tty_status_hilites[fldidx].under;
3578 else
3579 tty_status_colors[fldidx] = NO_COLOR;
3580 break;
3581 case BL_TH_VAL_PERCENTAGE: {
3582 int pct_th = 0;
3584 if (tty_status_hilites[fldidx].thresholdtype != ANY_INT) {
3585 impossible(
3586 "tty_status_update: unsupported percentage threshold type %d",
3587 tty_status_hilites[fldidx].thresholdtype);
3588 } else {
3589 pct_th = tty_status_hilites[fldidx].threshold.a_int;
3590 tty_status_colors[fldidx] = (percent >= pct_th)
3591 ? tty_status_hilites[fldidx].over
3592 : tty_status_hilites[fldidx].under;
3594 break;
3596 case BL_TH_VAL_ABSOLUTE: {
3597 int c = NO_COLOR;
3598 int o = tty_status_hilites[fldidx].over;
3599 int u = tty_status_hilites[fldidx].under;
3600 anything *t = &tty_status_hilites[fldidx].threshold;
3602 switch (tty_status_hilites[fldidx].thresholdtype) {
3603 case ANY_LONG:
3604 c = (value >= t->a_long) ? o : u;
3605 break;
3606 case ANY_INT:
3607 c = (value >= t->a_int) ? o : u;
3608 break;
3609 case ANY_UINT:
3610 c = ((unsigned long) value >= t->a_uint) ? o : u;
3611 break;
3612 case ANY_ULONG:
3613 c = ((unsigned long) value >= t->a_ulong) ? o : u;
3614 break;
3615 case ANY_MASK32:
3616 c = (value & t->a_ulong) ? o : u;
3617 break;
3618 default:
3619 impossible(
3620 "tty_status_update: unsupported absolute threshold type %d\n",
3621 tty_status_hilites[fldidx].thresholdtype);
3622 break;
3624 tty_status_colors[fldidx] = c;
3625 break;
3626 } /* case */
3627 } /* switch */
3628 #endif /* STATUS_HILITES */
3631 /* For now, this version copied from the genl_ version currently
3632 * updates everything on the display, everytime
3635 if (!beenhere || !iflags.use_status_hilites) {
3636 char newbot1[MAXCO], newbot2[MAXCO];
3638 newbot1[0] = '\0';
3639 for (i = 0; fieldorder[0][i] >= 0; ++i) {
3640 int idx1 = fieldorder[0][i];
3642 if (status_activefields[idx1])
3643 Strcat(newbot1, status_vals[idx1]);
3645 newbot2[0] = '\0';
3646 for (i = 0; fieldorder[1][i] >= 0; ++i) {
3647 int idx2 = fieldorder[1][i];
3649 if (status_activefields[idx2])
3650 Strcat(newbot2, status_vals[idx2]);
3653 curs(WIN_STATUS, 1, 0);
3654 putstr(WIN_STATUS, 0, newbot1);
3655 curs(WIN_STATUS, 1, 1);
3656 putmixed(WIN_STATUS, 0, newbot2); /* putmixed() due to GOLD glyph */
3657 beenhere = TRUE;
3658 return;
3661 curs(WIN_STATUS, 1, 0);
3662 for (i = 0; fieldorder[0][i] != BL_FLUSH; ++i) {
3663 int fldidx1 = fieldorder[0][i];
3665 if (status_activefields[fldidx1]) {
3666 #ifdef STATUS_HILITES
3667 if (tty_status_colors[fldidx1] < 0
3668 && tty_status_colors[fldidx1] >= -3) {
3669 /* attribute, not a color */
3670 attridx = tty_status_colors[fldidx1] + 3;
3671 term_start_attr(statusattr[attridx]);
3672 putstr(WIN_STATUS, 0, status_vals[fldidx1]);
3673 term_end_attr(statusattr[attridx]);
3674 } else
3675 #ifdef TEXTCOLOR
3676 if (tty_status_colors[fldidx1] != CLR_MAX) {
3677 if (tty_status_colors[fldidx1] != NO_COLOR)
3678 term_start_color(tty_status_colors[fldidx1]);
3679 putstr(WIN_STATUS, 0, status_vals[fldidx1]);
3680 if (tty_status_colors[fldidx1] != NO_COLOR)
3681 term_end_color();
3682 } else
3683 #endif
3684 #endif /* STATUS_HILITES */
3685 putstr(WIN_STATUS, 0, status_vals[fldidx1]);
3688 curs(WIN_STATUS, 1, 1);
3689 for (i = 0; fieldorder[1][i] != BL_FLUSH; ++i) {
3690 int fldidx2 = fieldorder[1][i];
3692 if (status_activefields[fldidx2]) {
3693 #ifdef STATUS_HILITES
3694 if (tty_status_colors[fldidx2] < 0
3695 && tty_status_colors[fldidx2] >= -3) {
3696 /* attribute, not a color */
3697 attridx = tty_status_colors[fldidx2] + 3;
3698 term_start_attr(statusattr[attridx]);
3699 putstr(WIN_STATUS, 0, status_vals[fldidx2]);
3700 term_end_attr(statusattr[attridx]);
3701 } else
3702 #ifdef TEXTCOLOR
3703 if (tty_status_colors[fldidx2] != CLR_MAX) {
3704 if (tty_status_colors[fldidx2] != NO_COLOR)
3705 term_start_color(tty_status_colors[fldidx2]);
3706 if (fldidx2 == BL_GOLD) {
3707 /* putmixed() due to GOLD glyph */
3708 putmixed(WIN_STATUS, 0, status_vals[fldidx2]);
3709 } else {
3710 putstr(WIN_STATUS, 0, status_vals[fldidx2]);
3712 if (tty_status_colors[fldidx2] != NO_COLOR)
3713 term_end_color();
3714 } else
3715 #endif
3716 #endif /* STATUS_HILITES */
3717 putstr(WIN_STATUS, 0, status_vals[fldidx2]);
3720 return;
3723 #ifdef STATUS_HILITES
3725 * status_threshold(int fldidx, int threshholdtype, anything threshold,
3726 * int behavior, int under, int over)
3728 * -- called when a hiliting preference is added, changed, or
3729 * removed.
3730 * -- the fldindex identifies which field is having its hiliting
3731 * preference set. It is an integer index value from botl.h
3732 * -- fldindex could be any one of the following from botl.h:
3733 * BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH,
3734 * BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX,
3735 * BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX,
3736 * BL_LEVELDESC, BL_EXP, BL_CONDITION
3737 * -- datatype is P_INT, P_UINT, P_LONG, or P_MASK.
3738 * -- threshold is an "anything" union which can contain the
3739 * datatype value.
3740 * -- behavior is used to define how threshold is used and can
3741 * be BL_TH_NONE, BL_TH_VAL_PERCENTAGE, BL_TH_VAL_ABSOLUTE,
3742 * or BL_TH_UPDOWN. BL_TH_NONE means don't do anything above
3743 * or below the threshold. BL_TH_VAL_PERCENTAGE treats the
3744 * threshold value as a precentage of the maximum possible
3745 * value. BL_TH_VAL_ABSOLUTE means that the threshold is an
3746 * actual value. BL_TH_UPDOWN means that threshold is not
3747 * used, and the two below/above hilite values indicate how
3748 * to display something going down (under) or rising (over).
3749 * -- under is the hilite attribute used if value is below the
3750 * threshold. The attribute can be BL_HILITE_NONE,
3751 * BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one
3752 * of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN,
3753 * CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY,
3754 * CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE,
3755 * CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15).
3756 * -- over is the hilite attribute used if value is at or above
3757 * the threshold. The attribute can be BL_HILITE_NONE,
3758 * BL_HILITE_INVERSE, BL_HILITE_BOLD (-1, -2, or -3), or one
3759 * of the color indexes of CLR_BLACK, CLR_RED, CLR_GREEN,
3760 * CLR_BROWN, CLR_BLUE, CLR_MAGENTA, CLR_CYAN, CLR_GRAY,
3761 * CLR_ORANGE, CLR_BRIGHT_GREEN, CLR_YELLOW, CLR_BRIGHT_BLUE,
3762 * CLR_BRIGHT_MAGENTA, CLR_BRIGHT_CYAN, or CLR_WHITE (0 - 15).
3764 void
3765 tty_status_threshold(fldidx, thresholdtype, threshold, behavior, under, over)
3766 int fldidx, thresholdtype;
3767 int behavior, under, over;
3768 anything threshold;
3770 tty_status_hilites[fldidx].thresholdtype = thresholdtype;
3771 tty_status_hilites[fldidx].threshold = threshold;
3772 tty_status_hilites[fldidx].behavior = behavior;
3773 tty_status_hilites[fldidx].under = under;
3774 tty_status_hilites[fldidx].over = over;
3775 return;
3778 #endif /* STATUS_HILITES */
3779 #endif /*STATUS_VIA_WINDOWPORT*/
3781 #endif /* TTY_GRAPHICS */
3783 /*wintty.c*/